Merge pull request #282 from cwoffenden/faster-amalgamate

Replace the slow single-file shell script with a faster Python equivalent
diff --git a/.gitignore b/.gitignore
index 6f256e5..149177e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
-bin/
 build/
 
 # VS project and working files
diff --git a/.reuse/dep5 b/.reuse/dep5
index 4b48c6c..0884014 100644
--- a/.reuse/dep5
+++ b/.reuse/dep5
@@ -7,22 +7,11 @@
 Copyright: 2019-2021 Binomial LLC
 License: Apache-2.0
 
-Files: encoder/apg_bmp.*
-Copyright: 2019 Anton Gerdelan
-License: Apache-2.0
-
-Files: encoder/basisu_astc_decomp.*
-Copyright: 2016 The Android Open Source Project
-License: Apache-2.0
-
-Files: encoder/cppspmd_sse.h
-Copyright: Copyright 2016 Nicolas Guillemot
-License: Apache-2.0
-
-Files: encoder/lodepng.*
-Copyright: 2005-2019 Lode Vandevenne
-License: Zlib
-
 Files: zstd/*
 Copyright: 2016-present, Facebook, Inc.
 License: BSD
+
+Files: OpenCL/*
+Copyright: Copyright (c) 2008-2020 The Khronos Group Inc.
+License: Apache-2.0
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 78dd110..492233a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,15 +1,40 @@
 project(basisu)
 
 cmake_minimum_required(VERSION 3.0)
-option(BUILD_X64 "build 64-bit" TRUE)
 option(STATIC "static linking" FALSE)
-option(SSE "SSE 4.1 support" FALSE)
+option(SAN "sanitize" FALSE)
+
+# For MSVC builds default to SSE enabled, and determine if it's a 64-bit (-A x64) vs. 32-bit (-A Win32) build.
+if (MSVC)
+	option(SSE "SSE 4.1 support" TRUE)
+	if ( CMAKE_GENERATOR_PLATFORM STREQUAL Win32 )	
+		set(BUILD_X64 0)
+	else()
+		set(BUILD_X64 1)
+	endif()
+else()
+	option(SSE "SSE 4.1 support" FALSE)
+	option(BUILD_X64 "build 64-bit" TRUE)
+endif()
+
 option(ZSTD "ZSTD support for KTX2 transcoding/encoding" TRUE)
+option(OPENCL "OpenCL support in encoder" FALSE)
 
 message("Initial BUILD_X64=${BUILD_X64}")
 message("Initial CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
 message("Initial SSE=${SSE}")
 message("Initial ZSTD=${ZSTD}")
+message("Initial OPENCL=${OPENCL}")
+message("Initial SAN=${SAN}")
+
+if (NOT MSVC)
+	# With MSVC builds we use the Khronos lib/include files in the project's "OpenCL" directory, to completely avoid requiring fiddly to install vendor SDK's.
+	# Otherwise we use the system's (if any).
+	find_package( OpenCL )
+	message(STATUS "OpenCL found: ${OPENCL_FOUND}")
+	message(STATUS "OpenCL includes: ${OpenCL_INCLUDE_DIRS}")
+	message(STATUS "OpenCL libraries: ${OpenCL_LIBRARIES}")
+endif()
 
 if( NOT CMAKE_BUILD_TYPE )
   set( CMAKE_BUILD_TYPE Release )
@@ -37,10 +62,15 @@
 
 if (NOT MSVC)
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
-   set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
-
+   set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g")
+   
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
-   set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
+   set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
+
+   if (SAN)
+      set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined")
+      set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined")
+   endif()
 
    set(CMAKE_CXX_FLAGS -std=c++11)
    set(GCC_COMPILE_FLAGS "-fvisibility=hidden -fPIC -fno-strict-aliasing -D_LARGEFILE64_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -Wextra -Wno-unused-local-typedefs -Wno-unused-value -Wno-unused-parameter -Wno-unused-variable")
@@ -101,19 +131,17 @@
 	encoder/basisu_enc.cpp
 	encoder/basisu_etc.cpp
 	encoder/basisu_frontend.cpp
-	encoder/basisu_global_selector_palette_helpers.cpp
 	encoder/basisu_gpu_texture.cpp
 	encoder/basisu_pvrtc1_4.cpp
 	encoder/basisu_resampler.cpp
 	encoder/basisu_resample_filters.cpp
 	encoder/basisu_ssim.cpp
-	encoder/basisu_astc_decomp.cpp
 	encoder/basisu_uastc_enc.cpp
 	encoder/basisu_bc7enc.cpp
-	encoder/lodepng.cpp
-	encoder/apg_bmp.c
 	encoder/jpgd.cpp
 	encoder/basisu_kernels_sse.cpp
+	encoder/basisu_opencl.cpp
+	encoder/pvpngreader.cpp
 	transcoder/basisu_transcoder.cpp
 	)
 
@@ -138,7 +166,34 @@
 endif()
 
 if (NOT MSVC)
-   target_link_libraries(basisu m pthread)
+	# For Non-Windows builds, let cmake try and find the system OpenCL headers/libs for us.
+	if (OPENCL_FOUND)
+		set(CMAKE_C_FLAGS  "${CMAKE_C_FLAGS} -DBASISU_SUPPORT_OPENCL=1")
+		set(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} -DBASISU_SUPPORT_OPENCL=1")	
+		
+		target_include_directories( basisu PRIVATE ${OpenCL_INCLUDE_DIRS} )
+		set(BASISU_EXTRA_LIBS ${OpenCL_LIBRARIES})
+	endif()
+
+else()
+	# For Windows builds, we use our local copies of the OpenCL import lib and Khronos headers.
+	if (OPENCL)
+		set(CMAKE_C_FLAGS  "${CMAKE_C_FLAGS} -DBASISU_SUPPORT_OPENCL=1")
+		set(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} -DBASISU_SUPPORT_OPENCL=1")	
+		
+		target_include_directories( basisu PRIVATE "OpenCL" )
+
+		if ( BUILD_X64 )
+			target_link_libraries( basisu PRIVATE "OpenCL/lib/OpenCL64" )
+		else()
+			target_link_libraries( basisu PRIVATE "OpenCL/lib/OpenCL" )
+		endif()
+
+	endif()
+endif()	
+
+if (NOT MSVC)
+   target_link_libraries(basisu m pthread ${BASISU_EXTRA_LIBS})
 endif()
 
 if (NOT EMSCRIPTEN)
diff --git a/OpenCL/CL/cl.h b/OpenCL/CL/cl.h
new file mode 100644
index 0000000..0018a0f
--- /dev/null
+++ b/OpenCL/CL/cl.h
@@ -0,0 +1,1929 @@
+/*******************************************************************************
+ * Copyright (c) 2008-2020 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#ifndef __OPENCL_CL_H
+#define __OPENCL_CL_H
+
+#include <CL/cl_version.h>
+#include <CL/cl_platform.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************/
+
+typedef struct _cl_platform_id *    cl_platform_id;
+typedef struct _cl_device_id *      cl_device_id;
+typedef struct _cl_context *        cl_context;
+typedef struct _cl_command_queue *  cl_command_queue;
+typedef struct _cl_mem *            cl_mem;
+typedef struct _cl_program *        cl_program;
+typedef struct _cl_kernel *         cl_kernel;
+typedef struct _cl_event *          cl_event;
+typedef struct _cl_sampler *        cl_sampler;
+
+typedef cl_uint             cl_bool;                     /* WARNING!  Unlike cl_ types in cl_platform.h, cl_bool is not guaranteed to be the same size as the bool in kernels. */
+typedef cl_ulong            cl_bitfield;
+typedef cl_ulong            cl_properties;
+typedef cl_bitfield         cl_device_type;
+typedef cl_uint             cl_platform_info;
+typedef cl_uint             cl_device_info;
+typedef cl_bitfield         cl_device_fp_config;
+typedef cl_uint             cl_device_mem_cache_type;
+typedef cl_uint             cl_device_local_mem_type;
+typedef cl_bitfield         cl_device_exec_capabilities;
+#ifdef CL_VERSION_2_0
+typedef cl_bitfield         cl_device_svm_capabilities;
+#endif
+typedef cl_bitfield         cl_command_queue_properties;
+#ifdef CL_VERSION_1_2
+typedef intptr_t            cl_device_partition_property;
+typedef cl_bitfield         cl_device_affinity_domain;
+#endif
+
+typedef intptr_t            cl_context_properties;
+typedef cl_uint             cl_context_info;
+#ifdef CL_VERSION_2_0
+typedef cl_properties       cl_queue_properties;
+#endif
+typedef cl_uint             cl_command_queue_info;
+typedef cl_uint             cl_channel_order;
+typedef cl_uint             cl_channel_type;
+typedef cl_bitfield         cl_mem_flags;
+#ifdef CL_VERSION_2_0
+typedef cl_bitfield         cl_svm_mem_flags;
+#endif
+typedef cl_uint             cl_mem_object_type;
+typedef cl_uint             cl_mem_info;
+#ifdef CL_VERSION_1_2
+typedef cl_bitfield         cl_mem_migration_flags;
+#endif
+typedef cl_uint             cl_image_info;
+#ifdef CL_VERSION_1_1
+typedef cl_uint             cl_buffer_create_type;
+#endif
+typedef cl_uint             cl_addressing_mode;
+typedef cl_uint             cl_filter_mode;
+typedef cl_uint             cl_sampler_info;
+typedef cl_bitfield         cl_map_flags;
+#ifdef CL_VERSION_2_0
+typedef intptr_t            cl_pipe_properties;
+typedef cl_uint             cl_pipe_info;
+#endif
+typedef cl_uint             cl_program_info;
+typedef cl_uint             cl_program_build_info;
+#ifdef CL_VERSION_1_2
+typedef cl_uint             cl_program_binary_type;
+#endif
+typedef cl_int              cl_build_status;
+typedef cl_uint             cl_kernel_info;
+#ifdef CL_VERSION_1_2
+typedef cl_uint             cl_kernel_arg_info;
+typedef cl_uint             cl_kernel_arg_address_qualifier;
+typedef cl_uint             cl_kernel_arg_access_qualifier;
+typedef cl_bitfield         cl_kernel_arg_type_qualifier;
+#endif
+typedef cl_uint             cl_kernel_work_group_info;
+#ifdef CL_VERSION_2_1
+typedef cl_uint             cl_kernel_sub_group_info;
+#endif
+typedef cl_uint             cl_event_info;
+typedef cl_uint             cl_command_type;
+typedef cl_uint             cl_profiling_info;
+#ifdef CL_VERSION_2_0
+typedef cl_properties       cl_sampler_properties;
+typedef cl_uint             cl_kernel_exec_info;
+#endif
+#ifdef CL_VERSION_3_0
+typedef cl_bitfield         cl_device_atomic_capabilities;
+typedef cl_bitfield         cl_device_device_enqueue_capabilities;
+typedef cl_uint             cl_khronos_vendor_id;
+typedef cl_properties       cl_mem_properties;
+typedef cl_uint             cl_version;
+#endif
+
+typedef struct _cl_image_format {
+    cl_channel_order        image_channel_order;
+    cl_channel_type         image_channel_data_type;
+} cl_image_format;
+
+#ifdef CL_VERSION_1_2
+
+typedef struct _cl_image_desc {
+    cl_mem_object_type      image_type;
+    size_t                  image_width;
+    size_t                  image_height;
+    size_t                  image_depth;
+    size_t                  image_array_size;
+    size_t                  image_row_pitch;
+    size_t                  image_slice_pitch;
+    cl_uint                 num_mip_levels;
+    cl_uint                 num_samples;
+#ifdef CL_VERSION_2_0
+#if defined(__GNUC__)
+    __extension__                   /* Prevents warnings about anonymous union in -pedantic builds */
+#endif
+#if defined(_MSC_VER) && !defined(__STDC__)
+#pragma warning( push )
+#pragma warning( disable : 4201 )   /* Prevents warning about nameless struct/union in /W4 builds */
+#endif
+#if defined(_MSC_VER) && defined(__STDC__)
+    /* Anonymous unions are not supported in /Za builds */
+#else
+    union {
+#endif
+#endif
+      cl_mem                  buffer;
+#ifdef CL_VERSION_2_0
+#if defined(_MSC_VER) && defined(__STDC__)
+    /* Anonymous unions are not supported in /Za builds */
+#else
+      cl_mem                  mem_object;
+    };
+#endif
+#if defined(_MSC_VER) && !defined(__STDC__)
+#pragma warning( pop )
+#endif
+#endif
+} cl_image_desc;
+
+#endif
+
+#ifdef CL_VERSION_1_1
+
+typedef struct _cl_buffer_region {
+    size_t                  origin;
+    size_t                  size;
+} cl_buffer_region;
+
+#endif
+
+#ifdef CL_VERSION_3_0
+
+#define CL_NAME_VERSION_MAX_NAME_SIZE 64
+
+typedef struct _cl_name_version {
+    cl_version              version;
+    char                    name[CL_NAME_VERSION_MAX_NAME_SIZE];
+} cl_name_version;
+
+#endif
+
+/******************************************************************************/
+
+/* Error Codes */
+#define CL_SUCCESS                                  0
+#define CL_DEVICE_NOT_FOUND                         -1
+#define CL_DEVICE_NOT_AVAILABLE                     -2
+#define CL_COMPILER_NOT_AVAILABLE                   -3
+#define CL_MEM_OBJECT_ALLOCATION_FAILURE            -4
+#define CL_OUT_OF_RESOURCES                         -5
+#define CL_OUT_OF_HOST_MEMORY                       -6
+#define CL_PROFILING_INFO_NOT_AVAILABLE             -7
+#define CL_MEM_COPY_OVERLAP                         -8
+#define CL_IMAGE_FORMAT_MISMATCH                    -9
+#define CL_IMAGE_FORMAT_NOT_SUPPORTED               -10
+#define CL_BUILD_PROGRAM_FAILURE                    -11
+#define CL_MAP_FAILURE                              -12
+#ifdef CL_VERSION_1_1
+#define CL_MISALIGNED_SUB_BUFFER_OFFSET             -13
+#define CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST -14
+#endif
+#ifdef CL_VERSION_1_2
+#define CL_COMPILE_PROGRAM_FAILURE                  -15
+#define CL_LINKER_NOT_AVAILABLE                     -16
+#define CL_LINK_PROGRAM_FAILURE                     -17
+#define CL_DEVICE_PARTITION_FAILED                  -18
+#define CL_KERNEL_ARG_INFO_NOT_AVAILABLE            -19
+#endif
+
+#define CL_INVALID_VALUE                            -30
+#define CL_INVALID_DEVICE_TYPE                      -31
+#define CL_INVALID_PLATFORM                         -32
+#define CL_INVALID_DEVICE                           -33
+#define CL_INVALID_CONTEXT                          -34
+#define CL_INVALID_QUEUE_PROPERTIES                 -35
+#define CL_INVALID_COMMAND_QUEUE                    -36
+#define CL_INVALID_HOST_PTR                         -37
+#define CL_INVALID_MEM_OBJECT                       -38
+#define CL_INVALID_IMAGE_FORMAT_DESCRIPTOR          -39
+#define CL_INVALID_IMAGE_SIZE                       -40
+#define CL_INVALID_SAMPLER                          -41
+#define CL_INVALID_BINARY                           -42
+#define CL_INVALID_BUILD_OPTIONS                    -43
+#define CL_INVALID_PROGRAM                          -44
+#define CL_INVALID_PROGRAM_EXECUTABLE               -45
+#define CL_INVALID_KERNEL_NAME                      -46
+#define CL_INVALID_KERNEL_DEFINITION                -47
+#define CL_INVALID_KERNEL                           -48
+#define CL_INVALID_ARG_INDEX                        -49
+#define CL_INVALID_ARG_VALUE                        -50
+#define CL_INVALID_ARG_SIZE                         -51
+#define CL_INVALID_KERNEL_ARGS                      -52
+#define CL_INVALID_WORK_DIMENSION                   -53
+#define CL_INVALID_WORK_GROUP_SIZE                  -54
+#define CL_INVALID_WORK_ITEM_SIZE                   -55
+#define CL_INVALID_GLOBAL_OFFSET                    -56
+#define CL_INVALID_EVENT_WAIT_LIST                  -57
+#define CL_INVALID_EVENT                            -58
+#define CL_INVALID_OPERATION                        -59
+#define CL_INVALID_GL_OBJECT                        -60
+#define CL_INVALID_BUFFER_SIZE                      -61
+#define CL_INVALID_MIP_LEVEL                        -62
+#define CL_INVALID_GLOBAL_WORK_SIZE                 -63
+#ifdef CL_VERSION_1_1
+#define CL_INVALID_PROPERTY                         -64
+#endif
+#ifdef CL_VERSION_1_2
+#define CL_INVALID_IMAGE_DESCRIPTOR                 -65
+#define CL_INVALID_COMPILER_OPTIONS                 -66
+#define CL_INVALID_LINKER_OPTIONS                   -67
+#define CL_INVALID_DEVICE_PARTITION_COUNT           -68
+#endif
+#ifdef CL_VERSION_2_0
+#define CL_INVALID_PIPE_SIZE                        -69
+#define CL_INVALID_DEVICE_QUEUE                     -70
+#endif
+#ifdef CL_VERSION_2_2
+#define CL_INVALID_SPEC_ID                          -71
+#define CL_MAX_SIZE_RESTRICTION_EXCEEDED            -72
+#endif
+
+
+/* cl_bool */
+#define CL_FALSE                                    0
+#define CL_TRUE                                     1
+#ifdef CL_VERSION_1_2
+#define CL_BLOCKING                                 CL_TRUE
+#define CL_NON_BLOCKING                             CL_FALSE
+#endif
+
+/* cl_platform_info */
+#define CL_PLATFORM_PROFILE                         0x0900
+#define CL_PLATFORM_VERSION                         0x0901
+#define CL_PLATFORM_NAME                            0x0902
+#define CL_PLATFORM_VENDOR                          0x0903
+#define CL_PLATFORM_EXTENSIONS                      0x0904
+#ifdef CL_VERSION_2_1
+#define CL_PLATFORM_HOST_TIMER_RESOLUTION           0x0905
+#endif
+#ifdef CL_VERSION_3_0
+#define CL_PLATFORM_NUMERIC_VERSION                 0x0906
+#define CL_PLATFORM_EXTENSIONS_WITH_VERSION         0x0907
+#endif
+
+/* cl_device_type - bitfield */
+#define CL_DEVICE_TYPE_DEFAULT                      (1 << 0)
+#define CL_DEVICE_TYPE_CPU                          (1 << 1)
+#define CL_DEVICE_TYPE_GPU                          (1 << 2)
+#define CL_DEVICE_TYPE_ACCELERATOR                  (1 << 3)
+#ifdef CL_VERSION_1_2
+#define CL_DEVICE_TYPE_CUSTOM                       (1 << 4)
+#endif
+#define CL_DEVICE_TYPE_ALL                          0xFFFFFFFF
+
+/* cl_device_info */
+#define CL_DEVICE_TYPE                                   0x1000
+#define CL_DEVICE_VENDOR_ID                              0x1001
+#define CL_DEVICE_MAX_COMPUTE_UNITS                      0x1002
+#define CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS               0x1003
+#define CL_DEVICE_MAX_WORK_GROUP_SIZE                    0x1004
+#define CL_DEVICE_MAX_WORK_ITEM_SIZES                    0x1005
+#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR            0x1006
+#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT           0x1007
+#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT             0x1008
+#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG            0x1009
+#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT           0x100A
+#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE          0x100B
+#define CL_DEVICE_MAX_CLOCK_FREQUENCY                    0x100C
+#define CL_DEVICE_ADDRESS_BITS                           0x100D
+#define CL_DEVICE_MAX_READ_IMAGE_ARGS                    0x100E
+#define CL_DEVICE_MAX_WRITE_IMAGE_ARGS                   0x100F
+#define CL_DEVICE_MAX_MEM_ALLOC_SIZE                     0x1010
+#define CL_DEVICE_IMAGE2D_MAX_WIDTH                      0x1011
+#define CL_DEVICE_IMAGE2D_MAX_HEIGHT                     0x1012
+#define CL_DEVICE_IMAGE3D_MAX_WIDTH                      0x1013
+#define CL_DEVICE_IMAGE3D_MAX_HEIGHT                     0x1014
+#define CL_DEVICE_IMAGE3D_MAX_DEPTH                      0x1015
+#define CL_DEVICE_IMAGE_SUPPORT                          0x1016
+#define CL_DEVICE_MAX_PARAMETER_SIZE                     0x1017
+#define CL_DEVICE_MAX_SAMPLERS                           0x1018
+#define CL_DEVICE_MEM_BASE_ADDR_ALIGN                    0x1019
+#define CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE               0x101A
+#define CL_DEVICE_SINGLE_FP_CONFIG                       0x101B
+#define CL_DEVICE_GLOBAL_MEM_CACHE_TYPE                  0x101C
+#define CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE              0x101D
+#define CL_DEVICE_GLOBAL_MEM_CACHE_SIZE                  0x101E
+#define CL_DEVICE_GLOBAL_MEM_SIZE                        0x101F
+#define CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE               0x1020
+#define CL_DEVICE_MAX_CONSTANT_ARGS                      0x1021
+#define CL_DEVICE_LOCAL_MEM_TYPE                         0x1022
+#define CL_DEVICE_LOCAL_MEM_SIZE                         0x1023
+#define CL_DEVICE_ERROR_CORRECTION_SUPPORT               0x1024
+#define CL_DEVICE_PROFILING_TIMER_RESOLUTION             0x1025
+#define CL_DEVICE_ENDIAN_LITTLE                          0x1026
+#define CL_DEVICE_AVAILABLE                              0x1027
+#define CL_DEVICE_COMPILER_AVAILABLE                     0x1028
+#define CL_DEVICE_EXECUTION_CAPABILITIES                 0x1029
+#define CL_DEVICE_QUEUE_PROPERTIES                       0x102A    /* deprecated */
+#ifdef CL_VERSION_2_0
+#define CL_DEVICE_QUEUE_ON_HOST_PROPERTIES               0x102A
+#endif
+#define CL_DEVICE_NAME                                   0x102B
+#define CL_DEVICE_VENDOR                                 0x102C
+#define CL_DRIVER_VERSION                                0x102D
+#define CL_DEVICE_PROFILE                                0x102E
+#define CL_DEVICE_VERSION                                0x102F
+#define CL_DEVICE_EXTENSIONS                             0x1030
+#define CL_DEVICE_PLATFORM                               0x1031
+#ifdef CL_VERSION_1_2
+#define CL_DEVICE_DOUBLE_FP_CONFIG                       0x1032
+#endif
+/* 0x1033 reserved for CL_DEVICE_HALF_FP_CONFIG which is already defined in "cl_ext.h" */
+#ifdef CL_VERSION_1_1
+#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF            0x1034
+#define CL_DEVICE_HOST_UNIFIED_MEMORY                    0x1035   /* deprecated */
+#define CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR               0x1036
+#define CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT              0x1037
+#define CL_DEVICE_NATIVE_VECTOR_WIDTH_INT                0x1038
+#define CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG               0x1039
+#define CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT              0x103A
+#define CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE             0x103B
+#define CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF               0x103C
+#define CL_DEVICE_OPENCL_C_VERSION                       0x103D
+#endif
+#ifdef CL_VERSION_1_2
+#define CL_DEVICE_LINKER_AVAILABLE                       0x103E
+#define CL_DEVICE_BUILT_IN_KERNELS                       0x103F
+#define CL_DEVICE_IMAGE_MAX_BUFFER_SIZE                  0x1040
+#define CL_DEVICE_IMAGE_MAX_ARRAY_SIZE                   0x1041
+#define CL_DEVICE_PARENT_DEVICE                          0x1042
+#define CL_DEVICE_PARTITION_MAX_SUB_DEVICES              0x1043
+#define CL_DEVICE_PARTITION_PROPERTIES                   0x1044
+#define CL_DEVICE_PARTITION_AFFINITY_DOMAIN              0x1045
+#define CL_DEVICE_PARTITION_TYPE                         0x1046
+#define CL_DEVICE_REFERENCE_COUNT                        0x1047
+#define CL_DEVICE_PREFERRED_INTEROP_USER_SYNC            0x1048
+#define CL_DEVICE_PRINTF_BUFFER_SIZE                     0x1049
+#endif
+#ifdef CL_VERSION_2_0
+#define CL_DEVICE_IMAGE_PITCH_ALIGNMENT                  0x104A
+#define CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT           0x104B
+#define CL_DEVICE_MAX_READ_WRITE_IMAGE_ARGS              0x104C
+#define CL_DEVICE_MAX_GLOBAL_VARIABLE_SIZE               0x104D
+#define CL_DEVICE_QUEUE_ON_DEVICE_PROPERTIES             0x104E
+#define CL_DEVICE_QUEUE_ON_DEVICE_PREFERRED_SIZE         0x104F
+#define CL_DEVICE_QUEUE_ON_DEVICE_MAX_SIZE               0x1050
+#define CL_DEVICE_MAX_ON_DEVICE_QUEUES                   0x1051
+#define CL_DEVICE_MAX_ON_DEVICE_EVENTS                   0x1052
+#define CL_DEVICE_SVM_CAPABILITIES                       0x1053
+#define CL_DEVICE_GLOBAL_VARIABLE_PREFERRED_TOTAL_SIZE   0x1054
+#define CL_DEVICE_MAX_PIPE_ARGS                          0x1055
+#define CL_DEVICE_PIPE_MAX_ACTIVE_RESERVATIONS           0x1056
+#define CL_DEVICE_PIPE_MAX_PACKET_SIZE                   0x1057
+#define CL_DEVICE_PREFERRED_PLATFORM_ATOMIC_ALIGNMENT    0x1058
+#define CL_DEVICE_PREFERRED_GLOBAL_ATOMIC_ALIGNMENT      0x1059
+#define CL_DEVICE_PREFERRED_LOCAL_ATOMIC_ALIGNMENT       0x105A
+#endif
+#ifdef CL_VERSION_2_1
+#define CL_DEVICE_IL_VERSION                             0x105B
+#define CL_DEVICE_MAX_NUM_SUB_GROUPS                     0x105C
+#define CL_DEVICE_SUB_GROUP_INDEPENDENT_FORWARD_PROGRESS 0x105D
+#endif
+#ifdef CL_VERSION_3_0
+#define CL_DEVICE_NUMERIC_VERSION                        0x105E
+#define CL_DEVICE_EXTENSIONS_WITH_VERSION                0x1060
+#define CL_DEVICE_ILS_WITH_VERSION                       0x1061
+#define CL_DEVICE_BUILT_IN_KERNELS_WITH_VERSION          0x1062
+#define CL_DEVICE_ATOMIC_MEMORY_CAPABILITIES             0x1063
+#define CL_DEVICE_ATOMIC_FENCE_CAPABILITIES              0x1064
+#define CL_DEVICE_NON_UNIFORM_WORK_GROUP_SUPPORT         0x1065
+#define CL_DEVICE_OPENCL_C_ALL_VERSIONS                  0x1066
+#define CL_DEVICE_PREFERRED_WORK_GROUP_SIZE_MULTIPLE     0x1067
+#define CL_DEVICE_WORK_GROUP_COLLECTIVE_FUNCTIONS_SUPPORT 0x1068
+#define CL_DEVICE_GENERIC_ADDRESS_SPACE_SUPPORT          0x1069
+/* 0x106A to 0x106E - Reserved for upcoming KHR extension */
+#define CL_DEVICE_OPENCL_C_FEATURES                      0x106F
+#define CL_DEVICE_DEVICE_ENQUEUE_CAPABILITIES            0x1070
+#define CL_DEVICE_PIPE_SUPPORT                           0x1071
+#define CL_DEVICE_LATEST_CONFORMANCE_VERSION_PASSED      0x1072
+#endif
+
+/* cl_device_fp_config - bitfield */
+#define CL_FP_DENORM                                (1 << 0)
+#define CL_FP_INF_NAN                               (1 << 1)
+#define CL_FP_ROUND_TO_NEAREST                      (1 << 2)
+#define CL_FP_ROUND_TO_ZERO                         (1 << 3)
+#define CL_FP_ROUND_TO_INF                          (1 << 4)
+#define CL_FP_FMA                                   (1 << 5)
+#ifdef CL_VERSION_1_1
+#define CL_FP_SOFT_FLOAT                            (1 << 6)
+#endif
+#ifdef CL_VERSION_1_2
+#define CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT         (1 << 7)
+#endif
+
+/* cl_device_mem_cache_type */
+#define CL_NONE                                     0x0
+#define CL_READ_ONLY_CACHE                          0x1
+#define CL_READ_WRITE_CACHE                         0x2
+
+/* cl_device_local_mem_type */
+#define CL_LOCAL                                    0x1
+#define CL_GLOBAL                                   0x2
+
+/* cl_device_exec_capabilities - bitfield */
+#define CL_EXEC_KERNEL                              (1 << 0)
+#define CL_EXEC_NATIVE_KERNEL                       (1 << 1)
+
+/* cl_command_queue_properties - bitfield */
+#define CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE      (1 << 0)
+#define CL_QUEUE_PROFILING_ENABLE                   (1 << 1)
+#ifdef CL_VERSION_2_0
+#define CL_QUEUE_ON_DEVICE                          (1 << 2)
+#define CL_QUEUE_ON_DEVICE_DEFAULT                  (1 << 3)
+#endif
+
+/* cl_context_info */
+#define CL_CONTEXT_REFERENCE_COUNT                  0x1080
+#define CL_CONTEXT_DEVICES                          0x1081
+#define CL_CONTEXT_PROPERTIES                       0x1082
+#ifdef CL_VERSION_1_1
+#define CL_CONTEXT_NUM_DEVICES                      0x1083
+#endif
+
+/* cl_context_properties */
+#define CL_CONTEXT_PLATFORM                         0x1084
+#ifdef CL_VERSION_1_2
+#define CL_CONTEXT_INTEROP_USER_SYNC                0x1085
+#endif
+
+#ifdef CL_VERSION_1_2
+
+/* cl_device_partition_property */
+#define CL_DEVICE_PARTITION_EQUALLY                 0x1086
+#define CL_DEVICE_PARTITION_BY_COUNTS               0x1087
+#define CL_DEVICE_PARTITION_BY_COUNTS_LIST_END      0x0
+#define CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN      0x1088
+
+#endif
+
+#ifdef CL_VERSION_1_2
+
+/* cl_device_affinity_domain */
+#define CL_DEVICE_AFFINITY_DOMAIN_NUMA               (1 << 0)
+#define CL_DEVICE_AFFINITY_DOMAIN_L4_CACHE           (1 << 1)
+#define CL_DEVICE_AFFINITY_DOMAIN_L3_CACHE           (1 << 2)
+#define CL_DEVICE_AFFINITY_DOMAIN_L2_CACHE           (1 << 3)
+#define CL_DEVICE_AFFINITY_DOMAIN_L1_CACHE           (1 << 4)
+#define CL_DEVICE_AFFINITY_DOMAIN_NEXT_PARTITIONABLE (1 << 5)
+
+#endif
+
+#ifdef CL_VERSION_2_0
+
+/* cl_device_svm_capabilities */
+#define CL_DEVICE_SVM_COARSE_GRAIN_BUFFER           (1 << 0)
+#define CL_DEVICE_SVM_FINE_GRAIN_BUFFER             (1 << 1)
+#define CL_DEVICE_SVM_FINE_GRAIN_SYSTEM             (1 << 2)
+#define CL_DEVICE_SVM_ATOMICS                       (1 << 3)
+
+#endif
+
+/* cl_command_queue_info */
+#define CL_QUEUE_CONTEXT                            0x1090
+#define CL_QUEUE_DEVICE                             0x1091
+#define CL_QUEUE_REFERENCE_COUNT                    0x1092
+#define CL_QUEUE_PROPERTIES                         0x1093
+#ifdef CL_VERSION_2_0
+#define CL_QUEUE_SIZE                               0x1094
+#endif
+#ifdef CL_VERSION_2_1
+#define CL_QUEUE_DEVICE_DEFAULT                     0x1095
+#endif
+#ifdef CL_VERSION_3_0
+#define CL_QUEUE_PROPERTIES_ARRAY                   0x1098
+#endif
+
+/* cl_mem_flags and cl_svm_mem_flags - bitfield */
+#define CL_MEM_READ_WRITE                           (1 << 0)
+#define CL_MEM_WRITE_ONLY                           (1 << 1)
+#define CL_MEM_READ_ONLY                            (1 << 2)
+#define CL_MEM_USE_HOST_PTR                         (1 << 3)
+#define CL_MEM_ALLOC_HOST_PTR                       (1 << 4)
+#define CL_MEM_COPY_HOST_PTR                        (1 << 5)
+/* reserved                                         (1 << 6)    */
+#ifdef CL_VERSION_1_2
+#define CL_MEM_HOST_WRITE_ONLY                      (1 << 7)
+#define CL_MEM_HOST_READ_ONLY                       (1 << 8)
+#define CL_MEM_HOST_NO_ACCESS                       (1 << 9)
+#endif
+#ifdef CL_VERSION_2_0
+#define CL_MEM_SVM_FINE_GRAIN_BUFFER                (1 << 10)   /* used by cl_svm_mem_flags only */
+#define CL_MEM_SVM_ATOMICS                          (1 << 11)   /* used by cl_svm_mem_flags only */
+#define CL_MEM_KERNEL_READ_AND_WRITE                (1 << 12)
+#endif
+
+#ifdef CL_VERSION_1_2
+
+/* cl_mem_migration_flags - bitfield */
+#define CL_MIGRATE_MEM_OBJECT_HOST                  (1 << 0)
+#define CL_MIGRATE_MEM_OBJECT_CONTENT_UNDEFINED     (1 << 1)
+
+#endif
+
+/* cl_channel_order */
+#define CL_R                                        0x10B0
+#define CL_A                                        0x10B1
+#define CL_RG                                       0x10B2
+#define CL_RA                                       0x10B3
+#define CL_RGB                                      0x10B4
+#define CL_RGBA                                     0x10B5
+#define CL_BGRA                                     0x10B6
+#define CL_ARGB                                     0x10B7
+#define CL_INTENSITY                                0x10B8
+#define CL_LUMINANCE                                0x10B9
+#ifdef CL_VERSION_1_1
+#define CL_Rx                                       0x10BA
+#define CL_RGx                                      0x10BB
+#define CL_RGBx                                     0x10BC
+#endif
+#ifdef CL_VERSION_1_2
+#define CL_DEPTH                                    0x10BD
+#define CL_DEPTH_STENCIL                            0x10BE
+#endif
+#ifdef CL_VERSION_2_0
+#define CL_sRGB                                     0x10BF
+#define CL_sRGBx                                    0x10C0
+#define CL_sRGBA                                    0x10C1
+#define CL_sBGRA                                    0x10C2
+#define CL_ABGR                                     0x10C3
+#endif
+
+/* cl_channel_type */
+#define CL_SNORM_INT8                               0x10D0
+#define CL_SNORM_INT16                              0x10D1
+#define CL_UNORM_INT8                               0x10D2
+#define CL_UNORM_INT16                              0x10D3
+#define CL_UNORM_SHORT_565                          0x10D4
+#define CL_UNORM_SHORT_555                          0x10D5
+#define CL_UNORM_INT_101010                         0x10D6
+#define CL_SIGNED_INT8                              0x10D7
+#define CL_SIGNED_INT16                             0x10D8
+#define CL_SIGNED_INT32                             0x10D9
+#define CL_UNSIGNED_INT8                            0x10DA
+#define CL_UNSIGNED_INT16                           0x10DB
+#define CL_UNSIGNED_INT32                           0x10DC
+#define CL_HALF_FLOAT                               0x10DD
+#define CL_FLOAT                                    0x10DE
+#ifdef CL_VERSION_1_2
+#define CL_UNORM_INT24                              0x10DF
+#endif
+#ifdef CL_VERSION_2_1
+#define CL_UNORM_INT_101010_2                       0x10E0
+#endif
+
+/* cl_mem_object_type */
+#define CL_MEM_OBJECT_BUFFER                        0x10F0
+#define CL_MEM_OBJECT_IMAGE2D                       0x10F1
+#define CL_MEM_OBJECT_IMAGE3D                       0x10F2
+#ifdef CL_VERSION_1_2
+#define CL_MEM_OBJECT_IMAGE2D_ARRAY                 0x10F3
+#define CL_MEM_OBJECT_IMAGE1D                       0x10F4
+#define CL_MEM_OBJECT_IMAGE1D_ARRAY                 0x10F5
+#define CL_MEM_OBJECT_IMAGE1D_BUFFER                0x10F6
+#endif
+#ifdef CL_VERSION_2_0
+#define CL_MEM_OBJECT_PIPE                          0x10F7
+#endif
+
+/* cl_mem_info */
+#define CL_MEM_TYPE                                 0x1100
+#define CL_MEM_FLAGS                                0x1101
+#define CL_MEM_SIZE                                 0x1102
+#define CL_MEM_HOST_PTR                             0x1103
+#define CL_MEM_MAP_COUNT                            0x1104
+#define CL_MEM_REFERENCE_COUNT                      0x1105
+#define CL_MEM_CONTEXT                              0x1106
+#ifdef CL_VERSION_1_1
+#define CL_MEM_ASSOCIATED_MEMOBJECT                 0x1107
+#define CL_MEM_OFFSET                               0x1108
+#endif
+#ifdef CL_VERSION_2_0
+#define CL_MEM_USES_SVM_POINTER                     0x1109
+#endif
+#ifdef CL_VERSION_3_0
+#define CL_MEM_PROPERTIES                           0x110A
+#endif
+
+/* cl_image_info */
+#define CL_IMAGE_FORMAT                             0x1110
+#define CL_IMAGE_ELEMENT_SIZE                       0x1111
+#define CL_IMAGE_ROW_PITCH                          0x1112
+#define CL_IMAGE_SLICE_PITCH                        0x1113
+#define CL_IMAGE_WIDTH                              0x1114
+#define CL_IMAGE_HEIGHT                             0x1115
+#define CL_IMAGE_DEPTH                              0x1116
+#ifdef CL_VERSION_1_2
+#define CL_IMAGE_ARRAY_SIZE                         0x1117
+#define CL_IMAGE_BUFFER                             0x1118
+#define CL_IMAGE_NUM_MIP_LEVELS                     0x1119
+#define CL_IMAGE_NUM_SAMPLES                        0x111A
+#endif
+
+
+/* cl_pipe_info */
+#ifdef CL_VERSION_2_0
+#define CL_PIPE_PACKET_SIZE                         0x1120
+#define CL_PIPE_MAX_PACKETS                         0x1121
+#endif
+#ifdef CL_VERSION_3_0
+#define CL_PIPE_PROPERTIES                          0x1122
+#endif
+
+/* cl_addressing_mode */
+#define CL_ADDRESS_NONE                             0x1130
+#define CL_ADDRESS_CLAMP_TO_EDGE                    0x1131
+#define CL_ADDRESS_CLAMP                            0x1132
+#define CL_ADDRESS_REPEAT                           0x1133
+#ifdef CL_VERSION_1_1
+#define CL_ADDRESS_MIRRORED_REPEAT                  0x1134
+#endif
+
+/* cl_filter_mode */
+#define CL_FILTER_NEAREST                           0x1140
+#define CL_FILTER_LINEAR                            0x1141
+
+/* cl_sampler_info */
+#define CL_SAMPLER_REFERENCE_COUNT                  0x1150
+#define CL_SAMPLER_CONTEXT                          0x1151
+#define CL_SAMPLER_NORMALIZED_COORDS                0x1152
+#define CL_SAMPLER_ADDRESSING_MODE                  0x1153
+#define CL_SAMPLER_FILTER_MODE                      0x1154
+#ifdef CL_VERSION_2_0
+/* These enumerants are for the cl_khr_mipmap_image extension.
+   They have since been added to cl_ext.h with an appropriate
+   KHR suffix, but are left here for backwards compatibility. */
+#define CL_SAMPLER_MIP_FILTER_MODE                  0x1155
+#define CL_SAMPLER_LOD_MIN                          0x1156
+#define CL_SAMPLER_LOD_MAX                          0x1157
+#endif
+#ifdef CL_VERSION_3_0
+#define CL_SAMPLER_PROPERTIES                       0x1158
+#endif
+
+/* cl_map_flags - bitfield */
+#define CL_MAP_READ                                 (1 << 0)
+#define CL_MAP_WRITE                                (1 << 1)
+#ifdef CL_VERSION_1_2
+#define CL_MAP_WRITE_INVALIDATE_REGION              (1 << 2)
+#endif
+
+/* cl_program_info */
+#define CL_PROGRAM_REFERENCE_COUNT                  0x1160
+#define CL_PROGRAM_CONTEXT                          0x1161
+#define CL_PROGRAM_NUM_DEVICES                      0x1162
+#define CL_PROGRAM_DEVICES                          0x1163
+#define CL_PROGRAM_SOURCE                           0x1164
+#define CL_PROGRAM_BINARY_SIZES                     0x1165
+#define CL_PROGRAM_BINARIES                         0x1166
+#ifdef CL_VERSION_1_2
+#define CL_PROGRAM_NUM_KERNELS                      0x1167
+#define CL_PROGRAM_KERNEL_NAMES                     0x1168
+#endif
+#ifdef CL_VERSION_2_1
+#define CL_PROGRAM_IL                               0x1169
+#endif
+#ifdef CL_VERSION_2_2
+#define CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT       0x116A
+#define CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT       0x116B
+#endif
+
+/* cl_program_build_info */
+#define CL_PROGRAM_BUILD_STATUS                     0x1181
+#define CL_PROGRAM_BUILD_OPTIONS                    0x1182
+#define CL_PROGRAM_BUILD_LOG                        0x1183
+#ifdef CL_VERSION_1_2
+#define CL_PROGRAM_BINARY_TYPE                      0x1184
+#endif
+#ifdef CL_VERSION_2_0
+#define CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE 0x1185
+#endif
+
+#ifdef CL_VERSION_1_2
+
+/* cl_program_binary_type */
+#define CL_PROGRAM_BINARY_TYPE_NONE                 0x0
+#define CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT      0x1
+#define CL_PROGRAM_BINARY_TYPE_LIBRARY              0x2
+#define CL_PROGRAM_BINARY_TYPE_EXECUTABLE           0x4
+
+#endif
+
+/* cl_build_status */
+#define CL_BUILD_SUCCESS                            0
+#define CL_BUILD_NONE                               -1
+#define CL_BUILD_ERROR                              -2
+#define CL_BUILD_IN_PROGRESS                        -3
+
+/* cl_kernel_info */
+#define CL_KERNEL_FUNCTION_NAME                     0x1190
+#define CL_KERNEL_NUM_ARGS                          0x1191
+#define CL_KERNEL_REFERENCE_COUNT                   0x1192
+#define CL_KERNEL_CONTEXT                           0x1193
+#define CL_KERNEL_PROGRAM                           0x1194
+#ifdef CL_VERSION_1_2
+#define CL_KERNEL_ATTRIBUTES                        0x1195
+#endif
+
+#ifdef CL_VERSION_1_2
+
+/* cl_kernel_arg_info */
+#define CL_KERNEL_ARG_ADDRESS_QUALIFIER             0x1196
+#define CL_KERNEL_ARG_ACCESS_QUALIFIER              0x1197
+#define CL_KERNEL_ARG_TYPE_NAME                     0x1198
+#define CL_KERNEL_ARG_TYPE_QUALIFIER                0x1199
+#define CL_KERNEL_ARG_NAME                          0x119A
+
+#endif
+
+#ifdef CL_VERSION_1_2
+
+/* cl_kernel_arg_address_qualifier */
+#define CL_KERNEL_ARG_ADDRESS_GLOBAL                0x119B
+#define CL_KERNEL_ARG_ADDRESS_LOCAL                 0x119C
+#define CL_KERNEL_ARG_ADDRESS_CONSTANT              0x119D
+#define CL_KERNEL_ARG_ADDRESS_PRIVATE               0x119E
+
+#endif
+
+#ifdef CL_VERSION_1_2
+
+/* cl_kernel_arg_access_qualifier */
+#define CL_KERNEL_ARG_ACCESS_READ_ONLY              0x11A0
+#define CL_KERNEL_ARG_ACCESS_WRITE_ONLY             0x11A1
+#define CL_KERNEL_ARG_ACCESS_READ_WRITE             0x11A2
+#define CL_KERNEL_ARG_ACCESS_NONE                   0x11A3
+
+#endif
+
+#ifdef CL_VERSION_1_2
+
+/* cl_kernel_arg_type_qualifier */
+#define CL_KERNEL_ARG_TYPE_NONE                     0
+#define CL_KERNEL_ARG_TYPE_CONST                    (1 << 0)
+#define CL_KERNEL_ARG_TYPE_RESTRICT                 (1 << 1)
+#define CL_KERNEL_ARG_TYPE_VOLATILE                 (1 << 2)
+#ifdef CL_VERSION_2_0
+#define CL_KERNEL_ARG_TYPE_PIPE                     (1 << 3)
+#endif
+
+#endif
+
+/* cl_kernel_work_group_info */
+#define CL_KERNEL_WORK_GROUP_SIZE                   0x11B0
+#define CL_KERNEL_COMPILE_WORK_GROUP_SIZE           0x11B1
+#define CL_KERNEL_LOCAL_MEM_SIZE                    0x11B2
+#define CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE 0x11B3
+#define CL_KERNEL_PRIVATE_MEM_SIZE                  0x11B4
+#ifdef CL_VERSION_1_2
+#define CL_KERNEL_GLOBAL_WORK_SIZE                  0x11B5
+#endif
+
+#ifdef CL_VERSION_2_1
+
+/* cl_kernel_sub_group_info */
+#define CL_KERNEL_MAX_SUB_GROUP_SIZE_FOR_NDRANGE    0x2033
+#define CL_KERNEL_SUB_GROUP_COUNT_FOR_NDRANGE       0x2034
+#define CL_KERNEL_LOCAL_SIZE_FOR_SUB_GROUP_COUNT    0x11B8
+#define CL_KERNEL_MAX_NUM_SUB_GROUPS                0x11B9
+#define CL_KERNEL_COMPILE_NUM_SUB_GROUPS            0x11BA
+
+#endif
+
+#ifdef CL_VERSION_2_0
+
+/* cl_kernel_exec_info */
+#define CL_KERNEL_EXEC_INFO_SVM_PTRS                0x11B6
+#define CL_KERNEL_EXEC_INFO_SVM_FINE_GRAIN_SYSTEM   0x11B7
+
+#endif
+
+/* cl_event_info */
+#define CL_EVENT_COMMAND_QUEUE                      0x11D0
+#define CL_EVENT_COMMAND_TYPE                       0x11D1
+#define CL_EVENT_REFERENCE_COUNT                    0x11D2
+#define CL_EVENT_COMMAND_EXECUTION_STATUS           0x11D3
+#ifdef CL_VERSION_1_1
+#define CL_EVENT_CONTEXT                            0x11D4
+#endif
+
+/* cl_command_type */
+#define CL_COMMAND_NDRANGE_KERNEL                   0x11F0
+#define CL_COMMAND_TASK                             0x11F1
+#define CL_COMMAND_NATIVE_KERNEL                    0x11F2
+#define CL_COMMAND_READ_BUFFER                      0x11F3
+#define CL_COMMAND_WRITE_BUFFER                     0x11F4
+#define CL_COMMAND_COPY_BUFFER                      0x11F5
+#define CL_COMMAND_READ_IMAGE                       0x11F6
+#define CL_COMMAND_WRITE_IMAGE                      0x11F7
+#define CL_COMMAND_COPY_IMAGE                       0x11F8
+#define CL_COMMAND_COPY_IMAGE_TO_BUFFER             0x11F9
+#define CL_COMMAND_COPY_BUFFER_TO_IMAGE             0x11FA
+#define CL_COMMAND_MAP_BUFFER                       0x11FB
+#define CL_COMMAND_MAP_IMAGE                        0x11FC
+#define CL_COMMAND_UNMAP_MEM_OBJECT                 0x11FD
+#define CL_COMMAND_MARKER                           0x11FE
+#define CL_COMMAND_ACQUIRE_GL_OBJECTS               0x11FF
+#define CL_COMMAND_RELEASE_GL_OBJECTS               0x1200
+#ifdef CL_VERSION_1_1
+#define CL_COMMAND_READ_BUFFER_RECT                 0x1201
+#define CL_COMMAND_WRITE_BUFFER_RECT                0x1202
+#define CL_COMMAND_COPY_BUFFER_RECT                 0x1203
+#define CL_COMMAND_USER                             0x1204
+#endif
+#ifdef CL_VERSION_1_2
+#define CL_COMMAND_BARRIER                          0x1205
+#define CL_COMMAND_MIGRATE_MEM_OBJECTS              0x1206
+#define CL_COMMAND_FILL_BUFFER                      0x1207
+#define CL_COMMAND_FILL_IMAGE                       0x1208
+#endif
+#ifdef CL_VERSION_2_0
+#define CL_COMMAND_SVM_FREE                         0x1209
+#define CL_COMMAND_SVM_MEMCPY                       0x120A
+#define CL_COMMAND_SVM_MEMFILL                      0x120B
+#define CL_COMMAND_SVM_MAP                          0x120C
+#define CL_COMMAND_SVM_UNMAP                        0x120D
+#endif
+#ifdef CL_VERSION_3_0
+#define CL_COMMAND_SVM_MIGRATE_MEM                  0x120E
+#endif
+
+/* command execution status */
+#define CL_COMPLETE                                 0x0
+#define CL_RUNNING                                  0x1
+#define CL_SUBMITTED                                0x2
+#define CL_QUEUED                                   0x3
+
+/* cl_buffer_create_type */
+#ifdef CL_VERSION_1_1
+#define CL_BUFFER_CREATE_TYPE_REGION                0x1220
+#endif
+
+/* cl_profiling_info */
+#define CL_PROFILING_COMMAND_QUEUED                 0x1280
+#define CL_PROFILING_COMMAND_SUBMIT                 0x1281
+#define CL_PROFILING_COMMAND_START                  0x1282
+#define CL_PROFILING_COMMAND_END                    0x1283
+#ifdef CL_VERSION_2_0
+#define CL_PROFILING_COMMAND_COMPLETE               0x1284
+#endif
+
+/* cl_device_atomic_capabilities - bitfield */
+#ifdef CL_VERSION_3_0
+#define CL_DEVICE_ATOMIC_ORDER_RELAXED          (1 << 0)
+#define CL_DEVICE_ATOMIC_ORDER_ACQ_REL          (1 << 1)
+#define CL_DEVICE_ATOMIC_ORDER_SEQ_CST          (1 << 2)
+#define CL_DEVICE_ATOMIC_SCOPE_WORK_ITEM        (1 << 3)
+#define CL_DEVICE_ATOMIC_SCOPE_WORK_GROUP       (1 << 4)
+#define CL_DEVICE_ATOMIC_SCOPE_DEVICE           (1 << 5)
+#define CL_DEVICE_ATOMIC_SCOPE_ALL_DEVICES      (1 << 6)
+#endif
+
+/* cl_device_device_enqueue_capabilities - bitfield */
+#ifdef CL_VERSION_3_0
+#define CL_DEVICE_QUEUE_SUPPORTED               (1 << 0)
+#define CL_DEVICE_QUEUE_REPLACEABLE_DEFAULT     (1 << 1)
+#endif
+
+/* cl_khronos_vendor_id */
+#define CL_KHRONOS_VENDOR_ID_CODEPLAY               0x10004
+
+#ifdef CL_VERSION_3_0
+
+/* cl_version */
+#define CL_VERSION_MAJOR_BITS (10)
+#define CL_VERSION_MINOR_BITS (10)
+#define CL_VERSION_PATCH_BITS (12)
+
+#define CL_VERSION_MAJOR_MASK ((1 << CL_VERSION_MAJOR_BITS) - 1)
+#define CL_VERSION_MINOR_MASK ((1 << CL_VERSION_MINOR_BITS) - 1)
+#define CL_VERSION_PATCH_MASK ((1 << CL_VERSION_PATCH_BITS) - 1)
+
+#define CL_VERSION_MAJOR(version) \
+  ((version) >> (CL_VERSION_MINOR_BITS + CL_VERSION_PATCH_BITS))
+
+#define CL_VERSION_MINOR(version) \
+  (((version) >> CL_VERSION_PATCH_BITS) & CL_VERSION_MINOR_MASK)
+
+#define CL_VERSION_PATCH(version) ((version) & CL_VERSION_PATCH_MASK)
+
+#define CL_MAKE_VERSION(major, minor, patch)                      \
+  ((((major) & CL_VERSION_MAJOR_MASK)                             \
+       << (CL_VERSION_MINOR_BITS + CL_VERSION_PATCH_BITS)) |      \
+   (((minor) & CL_VERSION_MINOR_MASK) << CL_VERSION_PATCH_BITS) | \
+   ((patch) & CL_VERSION_PATCH_MASK))
+
+#endif
+
+/********************************************************************************************************/
+
+/* Platform API */
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetPlatformIDs(cl_uint          num_entries,
+                 cl_platform_id * platforms,
+                 cl_uint *        num_platforms) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetPlatformInfo(cl_platform_id   platform,
+                  cl_platform_info param_name,
+                  size_t           param_value_size,
+                  void *           param_value,
+                  size_t *         param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+/* Device APIs */
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetDeviceIDs(cl_platform_id   platform,
+               cl_device_type   device_type,
+               cl_uint          num_entries,
+               cl_device_id *   devices,
+               cl_uint *        num_devices) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetDeviceInfo(cl_device_id    device,
+                cl_device_info  param_name,
+                size_t          param_value_size,
+                void *          param_value,
+                size_t *        param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_2
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clCreateSubDevices(cl_device_id                         in_device,
+                   const cl_device_partition_property * properties,
+                   cl_uint                              num_devices,
+                   cl_device_id *                       out_devices,
+                   cl_uint *                            num_devices_ret) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clRetainDevice(cl_device_id device) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clReleaseDevice(cl_device_id device) CL_API_SUFFIX__VERSION_1_2;
+
+#endif
+
+#ifdef CL_VERSION_2_1
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clSetDefaultDeviceCommandQueue(cl_context           context,
+                               cl_device_id         device,
+                               cl_command_queue     command_queue) CL_API_SUFFIX__VERSION_2_1;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetDeviceAndHostTimer(cl_device_id    device,
+                        cl_ulong*       device_timestamp,
+                        cl_ulong*       host_timestamp) CL_API_SUFFIX__VERSION_2_1;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetHostTimer(cl_device_id device,
+               cl_ulong *   host_timestamp) CL_API_SUFFIX__VERSION_2_1;
+
+#endif
+
+/* Context APIs */
+extern CL_API_ENTRY cl_context CL_API_CALL
+clCreateContext(const cl_context_properties * properties,
+                cl_uint              num_devices,
+                const cl_device_id * devices,
+                void (CL_CALLBACK * pfn_notify)(const char * errinfo,
+                                                const void * private_info,
+                                                size_t       cb,
+                                                void *       user_data),
+                void *               user_data,
+                cl_int *             errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_context CL_API_CALL
+clCreateContextFromType(const cl_context_properties * properties,
+                        cl_device_type      device_type,
+                        void (CL_CALLBACK * pfn_notify)(const char * errinfo,
+                                                        const void * private_info,
+                                                        size_t       cb,
+                                                        void *       user_data),
+                        void *              user_data,
+                        cl_int *            errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clRetainContext(cl_context context) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clReleaseContext(cl_context context) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetContextInfo(cl_context         context,
+                 cl_context_info    param_name,
+                 size_t             param_value_size,
+                 void *             param_value,
+                 size_t *           param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_3_0
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clSetContextDestructorCallback(cl_context         context,
+                               void (CL_CALLBACK* pfn_notify)(cl_context context,
+                                                              void* user_data),
+                               void*              user_data) CL_API_SUFFIX__VERSION_3_0;
+
+#endif
+
+/* Command Queue APIs */
+
+#ifdef CL_VERSION_2_0
+
+extern CL_API_ENTRY cl_command_queue CL_API_CALL
+clCreateCommandQueueWithProperties(cl_context               context,
+                                   cl_device_id             device,
+                                   const cl_queue_properties *    properties,
+                                   cl_int *                 errcode_ret) CL_API_SUFFIX__VERSION_2_0;
+
+#endif
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clRetainCommandQueue(cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clReleaseCommandQueue(cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetCommandQueueInfo(cl_command_queue      command_queue,
+                      cl_command_queue_info param_name,
+                      size_t                param_value_size,
+                      void *                param_value,
+                      size_t *              param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+/* Memory Object APIs */
+extern CL_API_ENTRY cl_mem CL_API_CALL
+clCreateBuffer(cl_context   context,
+               cl_mem_flags flags,
+               size_t       size,
+               void *       host_ptr,
+               cl_int *     errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_1
+
+extern CL_API_ENTRY cl_mem CL_API_CALL
+clCreateSubBuffer(cl_mem                   buffer,
+                  cl_mem_flags             flags,
+                  cl_buffer_create_type    buffer_create_type,
+                  const void *             buffer_create_info,
+                  cl_int *                 errcode_ret) CL_API_SUFFIX__VERSION_1_1;
+
+#endif
+
+#ifdef CL_VERSION_1_2
+
+extern CL_API_ENTRY cl_mem CL_API_CALL
+clCreateImage(cl_context              context,
+              cl_mem_flags            flags,
+              const cl_image_format * image_format,
+              const cl_image_desc *   image_desc,
+              void *                  host_ptr,
+              cl_int *                errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+#endif
+
+#ifdef CL_VERSION_2_0
+
+extern CL_API_ENTRY cl_mem CL_API_CALL
+clCreatePipe(cl_context                 context,
+             cl_mem_flags               flags,
+             cl_uint                    pipe_packet_size,
+             cl_uint                    pipe_max_packets,
+             const cl_pipe_properties * properties,
+             cl_int *                   errcode_ret) CL_API_SUFFIX__VERSION_2_0;
+
+#endif
+
+#ifdef CL_VERSION_3_0
+
+extern CL_API_ENTRY cl_mem CL_API_CALL
+clCreateBufferWithProperties(cl_context                context,
+                             const cl_mem_properties * properties,
+                             cl_mem_flags              flags,
+                             size_t                    size,
+                             void *                    host_ptr,
+                             cl_int *                  errcode_ret) CL_API_SUFFIX__VERSION_3_0;
+
+extern CL_API_ENTRY cl_mem CL_API_CALL
+clCreateImageWithProperties(cl_context                context,
+                            const cl_mem_properties * properties,
+                            cl_mem_flags              flags,
+                            const cl_image_format *   image_format,
+                            const cl_image_desc *     image_desc,
+                            void *                    host_ptr,
+                            cl_int *                  errcode_ret) CL_API_SUFFIX__VERSION_3_0;
+
+#endif
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clRetainMemObject(cl_mem memobj) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clReleaseMemObject(cl_mem memobj) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetSupportedImageFormats(cl_context           context,
+                           cl_mem_flags         flags,
+                           cl_mem_object_type   image_type,
+                           cl_uint              num_entries,
+                           cl_image_format *    image_formats,
+                           cl_uint *            num_image_formats) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetMemObjectInfo(cl_mem           memobj,
+                   cl_mem_info      param_name,
+                   size_t           param_value_size,
+                   void *           param_value,
+                   size_t *         param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetImageInfo(cl_mem           image,
+               cl_image_info    param_name,
+               size_t           param_value_size,
+               void *           param_value,
+               size_t *         param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_2_0
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetPipeInfo(cl_mem           pipe,
+              cl_pipe_info     param_name,
+              size_t           param_value_size,
+              void *           param_value,
+              size_t *         param_value_size_ret) CL_API_SUFFIX__VERSION_2_0;
+
+#endif
+
+#ifdef CL_VERSION_1_1
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clSetMemObjectDestructorCallback(cl_mem memobj,
+                                 void (CL_CALLBACK * pfn_notify)(cl_mem memobj,
+                                                                 void * user_data),
+                                 void * user_data) CL_API_SUFFIX__VERSION_1_1;
+
+#endif
+
+/* SVM Allocation APIs */
+
+#ifdef CL_VERSION_2_0
+
+extern CL_API_ENTRY void * CL_API_CALL
+clSVMAlloc(cl_context       context,
+           cl_svm_mem_flags flags,
+           size_t           size,
+           cl_uint          alignment) CL_API_SUFFIX__VERSION_2_0;
+
+extern CL_API_ENTRY void CL_API_CALL
+clSVMFree(cl_context        context,
+          void *            svm_pointer) CL_API_SUFFIX__VERSION_2_0;
+
+#endif
+
+/* Sampler APIs */
+
+#ifdef CL_VERSION_2_0
+
+extern CL_API_ENTRY cl_sampler CL_API_CALL
+clCreateSamplerWithProperties(cl_context                     context,
+                              const cl_sampler_properties *  sampler_properties,
+                              cl_int *                       errcode_ret) CL_API_SUFFIX__VERSION_2_0;
+
+#endif
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clRetainSampler(cl_sampler sampler) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clReleaseSampler(cl_sampler sampler) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetSamplerInfo(cl_sampler         sampler,
+                 cl_sampler_info    param_name,
+                 size_t             param_value_size,
+                 void *             param_value,
+                 size_t *           param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+/* Program Object APIs */
+extern CL_API_ENTRY cl_program CL_API_CALL
+clCreateProgramWithSource(cl_context        context,
+                          cl_uint           count,
+                          const char **     strings,
+                          const size_t *    lengths,
+                          cl_int *          errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_program CL_API_CALL
+clCreateProgramWithBinary(cl_context                     context,
+                          cl_uint                        num_devices,
+                          const cl_device_id *           device_list,
+                          const size_t *                 lengths,
+                          const unsigned char **         binaries,
+                          cl_int *                       binary_status,
+                          cl_int *                       errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_2
+
+extern CL_API_ENTRY cl_program CL_API_CALL
+clCreateProgramWithBuiltInKernels(cl_context            context,
+                                  cl_uint               num_devices,
+                                  const cl_device_id *  device_list,
+                                  const char *          kernel_names,
+                                  cl_int *              errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+#endif
+
+#ifdef CL_VERSION_2_1
+
+extern CL_API_ENTRY cl_program CL_API_CALL
+clCreateProgramWithIL(cl_context    context,
+                     const void*    il,
+                     size_t         length,
+                     cl_int*        errcode_ret) CL_API_SUFFIX__VERSION_2_1;
+
+#endif
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clRetainProgram(cl_program program) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clReleaseProgram(cl_program program) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clBuildProgram(cl_program           program,
+               cl_uint              num_devices,
+               const cl_device_id * device_list,
+               const char *         options,
+               void (CL_CALLBACK *  pfn_notify)(cl_program program,
+                                                void * user_data),
+               void *               user_data) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_2
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clCompileProgram(cl_program           program,
+                 cl_uint              num_devices,
+                 const cl_device_id * device_list,
+                 const char *         options,
+                 cl_uint              num_input_headers,
+                 const cl_program *   input_headers,
+                 const char **        header_include_names,
+                 void (CL_CALLBACK *  pfn_notify)(cl_program program,
+                                                  void * user_data),
+                 void *               user_data) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY cl_program CL_API_CALL
+clLinkProgram(cl_context           context,
+              cl_uint              num_devices,
+              const cl_device_id * device_list,
+              const char *         options,
+              cl_uint              num_input_programs,
+              const cl_program *   input_programs,
+              void (CL_CALLBACK *  pfn_notify)(cl_program program,
+                                               void * user_data),
+              void *               user_data,
+              cl_int *             errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+#endif
+
+#ifdef CL_VERSION_2_2
+
+extern CL_API_ENTRY CL_API_PREFIX__VERSION_2_2_DEPRECATED cl_int CL_API_CALL
+clSetProgramReleaseCallback(cl_program          program,
+                            void (CL_CALLBACK * pfn_notify)(cl_program program,
+                                                            void * user_data),
+                            void *              user_data) CL_API_SUFFIX__VERSION_2_2_DEPRECATED;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clSetProgramSpecializationConstant(cl_program  program,
+                                   cl_uint     spec_id,
+                                   size_t      spec_size,
+                                   const void* spec_value) CL_API_SUFFIX__VERSION_2_2;
+
+#endif
+
+#ifdef CL_VERSION_1_2
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clUnloadPlatformCompiler(cl_platform_id platform) CL_API_SUFFIX__VERSION_1_2;
+
+#endif
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetProgramInfo(cl_program         program,
+                 cl_program_info    param_name,
+                 size_t             param_value_size,
+                 void *             param_value,
+                 size_t *           param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetProgramBuildInfo(cl_program            program,
+                      cl_device_id          device,
+                      cl_program_build_info param_name,
+                      size_t                param_value_size,
+                      void *                param_value,
+                      size_t *              param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+/* Kernel Object APIs */
+extern CL_API_ENTRY cl_kernel CL_API_CALL
+clCreateKernel(cl_program      program,
+               const char *    kernel_name,
+               cl_int *        errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clCreateKernelsInProgram(cl_program     program,
+                         cl_uint        num_kernels,
+                         cl_kernel *    kernels,
+                         cl_uint *      num_kernels_ret) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_2_1
+
+extern CL_API_ENTRY cl_kernel CL_API_CALL
+clCloneKernel(cl_kernel     source_kernel,
+              cl_int*       errcode_ret) CL_API_SUFFIX__VERSION_2_1;
+
+#endif
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clRetainKernel(cl_kernel    kernel) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clReleaseKernel(cl_kernel   kernel) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clSetKernelArg(cl_kernel    kernel,
+               cl_uint      arg_index,
+               size_t       arg_size,
+               const void * arg_value) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_2_0
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clSetKernelArgSVMPointer(cl_kernel    kernel,
+                         cl_uint      arg_index,
+                         const void * arg_value) CL_API_SUFFIX__VERSION_2_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clSetKernelExecInfo(cl_kernel            kernel,
+                    cl_kernel_exec_info  param_name,
+                    size_t               param_value_size,
+                    const void *         param_value) CL_API_SUFFIX__VERSION_2_0;
+
+#endif
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetKernelInfo(cl_kernel       kernel,
+                cl_kernel_info  param_name,
+                size_t          param_value_size,
+                void *          param_value,
+                size_t *        param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_2
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetKernelArgInfo(cl_kernel       kernel,
+                   cl_uint         arg_indx,
+                   cl_kernel_arg_info  param_name,
+                   size_t          param_value_size,
+                   void *          param_value,
+                   size_t *        param_value_size_ret) CL_API_SUFFIX__VERSION_1_2;
+
+#endif
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetKernelWorkGroupInfo(cl_kernel                  kernel,
+                         cl_device_id               device,
+                         cl_kernel_work_group_info  param_name,
+                         size_t                     param_value_size,
+                         void *                     param_value,
+                         size_t *                   param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_2_1
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetKernelSubGroupInfo(cl_kernel                   kernel,
+                        cl_device_id                device,
+                        cl_kernel_sub_group_info    param_name,
+                        size_t                      input_value_size,
+                        const void*                 input_value,
+                        size_t                      param_value_size,
+                        void*                       param_value,
+                        size_t*                     param_value_size_ret) CL_API_SUFFIX__VERSION_2_1;
+
+#endif
+
+/* Event Object APIs */
+extern CL_API_ENTRY cl_int CL_API_CALL
+clWaitForEvents(cl_uint             num_events,
+                const cl_event *    event_list) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetEventInfo(cl_event         event,
+               cl_event_info    param_name,
+               size_t           param_value_size,
+               void *           param_value,
+               size_t *         param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_1
+
+extern CL_API_ENTRY cl_event CL_API_CALL
+clCreateUserEvent(cl_context    context,
+                  cl_int *      errcode_ret) CL_API_SUFFIX__VERSION_1_1;
+
+#endif
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clRetainEvent(cl_event event) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clReleaseEvent(cl_event event) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_1
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clSetUserEventStatus(cl_event   event,
+                     cl_int     execution_status) CL_API_SUFFIX__VERSION_1_1;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clSetEventCallback(cl_event    event,
+                   cl_int      command_exec_callback_type,
+                   void (CL_CALLBACK * pfn_notify)(cl_event event,
+                                                   cl_int   event_command_status,
+                                                   void *   user_data),
+                   void *      user_data) CL_API_SUFFIX__VERSION_1_1;
+
+#endif
+
+/* Profiling APIs */
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetEventProfilingInfo(cl_event            event,
+                        cl_profiling_info   param_name,
+                        size_t              param_value_size,
+                        void *              param_value,
+                        size_t *            param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+/* Flush and Finish APIs */
+extern CL_API_ENTRY cl_int CL_API_CALL
+clFlush(cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clFinish(cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0;
+
+/* Enqueued Commands APIs */
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueReadBuffer(cl_command_queue    command_queue,
+                    cl_mem              buffer,
+                    cl_bool             blocking_read,
+                    size_t              offset,
+                    size_t              size,
+                    void *              ptr,
+                    cl_uint             num_events_in_wait_list,
+                    const cl_event *    event_wait_list,
+                    cl_event *          event) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_1
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueReadBufferRect(cl_command_queue    command_queue,
+                        cl_mem              buffer,
+                        cl_bool             blocking_read,
+                        const size_t *      buffer_origin,
+                        const size_t *      host_origin,
+                        const size_t *      region,
+                        size_t              buffer_row_pitch,
+                        size_t              buffer_slice_pitch,
+                        size_t              host_row_pitch,
+                        size_t              host_slice_pitch,
+                        void *              ptr,
+                        cl_uint             num_events_in_wait_list,
+                        const cl_event *    event_wait_list,
+                        cl_event *          event) CL_API_SUFFIX__VERSION_1_1;
+
+#endif
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueWriteBuffer(cl_command_queue   command_queue,
+                     cl_mem             buffer,
+                     cl_bool            blocking_write,
+                     size_t             offset,
+                     size_t             size,
+                     const void *       ptr,
+                     cl_uint            num_events_in_wait_list,
+                     const cl_event *   event_wait_list,
+                     cl_event *         event) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_1
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueWriteBufferRect(cl_command_queue    command_queue,
+                         cl_mem              buffer,
+                         cl_bool             blocking_write,
+                         const size_t *      buffer_origin,
+                         const size_t *      host_origin,
+                         const size_t *      region,
+                         size_t              buffer_row_pitch,
+                         size_t              buffer_slice_pitch,
+                         size_t              host_row_pitch,
+                         size_t              host_slice_pitch,
+                         const void *        ptr,
+                         cl_uint             num_events_in_wait_list,
+                         const cl_event *    event_wait_list,
+                         cl_event *          event) CL_API_SUFFIX__VERSION_1_1;
+
+#endif
+
+#ifdef CL_VERSION_1_2
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueFillBuffer(cl_command_queue   command_queue,
+                    cl_mem             buffer,
+                    const void *       pattern,
+                    size_t             pattern_size,
+                    size_t             offset,
+                    size_t             size,
+                    cl_uint            num_events_in_wait_list,
+                    const cl_event *   event_wait_list,
+                    cl_event *         event) CL_API_SUFFIX__VERSION_1_2;
+
+#endif
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueCopyBuffer(cl_command_queue    command_queue,
+                    cl_mem              src_buffer,
+                    cl_mem              dst_buffer,
+                    size_t              src_offset,
+                    size_t              dst_offset,
+                    size_t              size,
+                    cl_uint             num_events_in_wait_list,
+                    const cl_event *    event_wait_list,
+                    cl_event *          event) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_1
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueCopyBufferRect(cl_command_queue    command_queue,
+                        cl_mem              src_buffer,
+                        cl_mem              dst_buffer,
+                        const size_t *      src_origin,
+                        const size_t *      dst_origin,
+                        const size_t *      region,
+                        size_t              src_row_pitch,
+                        size_t              src_slice_pitch,
+                        size_t              dst_row_pitch,
+                        size_t              dst_slice_pitch,
+                        cl_uint             num_events_in_wait_list,
+                        const cl_event *    event_wait_list,
+                        cl_event *          event) CL_API_SUFFIX__VERSION_1_1;
+
+#endif
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueReadImage(cl_command_queue     command_queue,
+                   cl_mem               image,
+                   cl_bool              blocking_read,
+                   const size_t *       origin,
+                   const size_t *       region,
+                   size_t               row_pitch,
+                   size_t               slice_pitch,
+                   void *               ptr,
+                   cl_uint              num_events_in_wait_list,
+                   const cl_event *     event_wait_list,
+                   cl_event *           event) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueWriteImage(cl_command_queue    command_queue,
+                    cl_mem              image,
+                    cl_bool             blocking_write,
+                    const size_t *      origin,
+                    const size_t *      region,
+                    size_t              input_row_pitch,
+                    size_t              input_slice_pitch,
+                    const void *        ptr,
+                    cl_uint             num_events_in_wait_list,
+                    const cl_event *    event_wait_list,
+                    cl_event *          event) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_2
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueFillImage(cl_command_queue   command_queue,
+                   cl_mem             image,
+                   const void *       fill_color,
+                   const size_t *     origin,
+                   const size_t *     region,
+                   cl_uint            num_events_in_wait_list,
+                   const cl_event *   event_wait_list,
+                   cl_event *         event) CL_API_SUFFIX__VERSION_1_2;
+
+#endif
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueCopyImage(cl_command_queue     command_queue,
+                   cl_mem               src_image,
+                   cl_mem               dst_image,
+                   const size_t *       src_origin,
+                   const size_t *       dst_origin,
+                   const size_t *       region,
+                   cl_uint              num_events_in_wait_list,
+                   const cl_event *     event_wait_list,
+                   cl_event *           event) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueCopyImageToBuffer(cl_command_queue command_queue,
+                           cl_mem           src_image,
+                           cl_mem           dst_buffer,
+                           const size_t *   src_origin,
+                           const size_t *   region,
+                           size_t           dst_offset,
+                           cl_uint          num_events_in_wait_list,
+                           const cl_event * event_wait_list,
+                           cl_event *       event) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueCopyBufferToImage(cl_command_queue command_queue,
+                           cl_mem           src_buffer,
+                           cl_mem           dst_image,
+                           size_t           src_offset,
+                           const size_t *   dst_origin,
+                           const size_t *   region,
+                           cl_uint          num_events_in_wait_list,
+                           const cl_event * event_wait_list,
+                           cl_event *       event) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY void * CL_API_CALL
+clEnqueueMapBuffer(cl_command_queue command_queue,
+                   cl_mem           buffer,
+                   cl_bool          blocking_map,
+                   cl_map_flags     map_flags,
+                   size_t           offset,
+                   size_t           size,
+                   cl_uint          num_events_in_wait_list,
+                   const cl_event * event_wait_list,
+                   cl_event *       event,
+                   cl_int *         errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY void * CL_API_CALL
+clEnqueueMapImage(cl_command_queue  command_queue,
+                  cl_mem            image,
+                  cl_bool           blocking_map,
+                  cl_map_flags      map_flags,
+                  const size_t *    origin,
+                  const size_t *    region,
+                  size_t *          image_row_pitch,
+                  size_t *          image_slice_pitch,
+                  cl_uint           num_events_in_wait_list,
+                  const cl_event *  event_wait_list,
+                  cl_event *        event,
+                  cl_int *          errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueUnmapMemObject(cl_command_queue command_queue,
+                        cl_mem           memobj,
+                        void *           mapped_ptr,
+                        cl_uint          num_events_in_wait_list,
+                        const cl_event * event_wait_list,
+                        cl_event *       event) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_2
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueMigrateMemObjects(cl_command_queue       command_queue,
+                           cl_uint                num_mem_objects,
+                           const cl_mem *         mem_objects,
+                           cl_mem_migration_flags flags,
+                           cl_uint                num_events_in_wait_list,
+                           const cl_event *       event_wait_list,
+                           cl_event *             event) CL_API_SUFFIX__VERSION_1_2;
+
+#endif
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueNDRangeKernel(cl_command_queue command_queue,
+                       cl_kernel        kernel,
+                       cl_uint          work_dim,
+                       const size_t *   global_work_offset,
+                       const size_t *   global_work_size,
+                       const size_t *   local_work_size,
+                       cl_uint          num_events_in_wait_list,
+                       const cl_event * event_wait_list,
+                       cl_event *       event) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueNativeKernel(cl_command_queue  command_queue,
+                      void (CL_CALLBACK * user_func)(void *),
+                      void *            args,
+                      size_t            cb_args,
+                      cl_uint           num_mem_objects,
+                      const cl_mem *    mem_list,
+                      const void **     args_mem_loc,
+                      cl_uint           num_events_in_wait_list,
+                      const cl_event *  event_wait_list,
+                      cl_event *        event) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_2
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueMarkerWithWaitList(cl_command_queue  command_queue,
+                            cl_uint           num_events_in_wait_list,
+                            const cl_event *  event_wait_list,
+                            cl_event *        event) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueBarrierWithWaitList(cl_command_queue  command_queue,
+                             cl_uint           num_events_in_wait_list,
+                             const cl_event *  event_wait_list,
+                             cl_event *        event) CL_API_SUFFIX__VERSION_1_2;
+
+#endif
+
+#ifdef CL_VERSION_2_0
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueSVMFree(cl_command_queue  command_queue,
+                 cl_uint           num_svm_pointers,
+                 void *            svm_pointers[],
+                 void (CL_CALLBACK * pfn_free_func)(cl_command_queue queue,
+                                                    cl_uint          num_svm_pointers,
+                                                    void *           svm_pointers[],
+                                                    void *           user_data),
+                 void *            user_data,
+                 cl_uint           num_events_in_wait_list,
+                 const cl_event *  event_wait_list,
+                 cl_event *        event) CL_API_SUFFIX__VERSION_2_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueSVMMemcpy(cl_command_queue  command_queue,
+                   cl_bool           blocking_copy,
+                   void *            dst_ptr,
+                   const void *      src_ptr,
+                   size_t            size,
+                   cl_uint           num_events_in_wait_list,
+                   const cl_event *  event_wait_list,
+                   cl_event *        event) CL_API_SUFFIX__VERSION_2_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueSVMMemFill(cl_command_queue  command_queue,
+                    void *            svm_ptr,
+                    const void *      pattern,
+                    size_t            pattern_size,
+                    size_t            size,
+                    cl_uint           num_events_in_wait_list,
+                    const cl_event *  event_wait_list,
+                    cl_event *        event) CL_API_SUFFIX__VERSION_2_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueSVMMap(cl_command_queue  command_queue,
+                cl_bool           blocking_map,
+                cl_map_flags      flags,
+                void *            svm_ptr,
+                size_t            size,
+                cl_uint           num_events_in_wait_list,
+                const cl_event *  event_wait_list,
+                cl_event *        event) CL_API_SUFFIX__VERSION_2_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueSVMUnmap(cl_command_queue  command_queue,
+                  void *            svm_ptr,
+                  cl_uint           num_events_in_wait_list,
+                  const cl_event *  event_wait_list,
+                  cl_event *        event) CL_API_SUFFIX__VERSION_2_0;
+
+#endif
+
+#ifdef CL_VERSION_2_1
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueSVMMigrateMem(cl_command_queue         command_queue,
+                       cl_uint                  num_svm_pointers,
+                       const void **            svm_pointers,
+                       const size_t *           sizes,
+                       cl_mem_migration_flags   flags,
+                       cl_uint                  num_events_in_wait_list,
+                       const cl_event *         event_wait_list,
+                       cl_event *               event) CL_API_SUFFIX__VERSION_2_1;
+
+#endif
+
+#ifdef CL_VERSION_1_2
+
+/* Extension function access
+ *
+ * Returns the extension function address for the given function name,
+ * or NULL if a valid function can not be found.  The client must
+ * check to make sure the address is not NULL, before using or
+ * calling the returned function address.
+ */
+extern CL_API_ENTRY void * CL_API_CALL
+clGetExtensionFunctionAddressForPlatform(cl_platform_id platform,
+                                         const char *   func_name) CL_API_SUFFIX__VERSION_1_2;
+
+#endif
+
+#ifdef CL_USE_DEPRECATED_OPENCL_1_0_APIS
+    /*
+     *  WARNING:
+     *     This API introduces mutable state into the OpenCL implementation. It has been REMOVED
+     *  to better facilitate thread safety.  The 1.0 API is not thread safe. It is not tested by the
+     *  OpenCL 1.1 conformance test, and consequently may not work or may not work dependably.
+     *  It is likely to be non-performant. Use of this API is not advised. Use at your own risk.
+     *
+     *  Software developers previously relying on this API are instructed to set the command queue
+     *  properties when creating the queue, instead.
+     */
+    extern CL_API_ENTRY cl_int CL_API_CALL
+    clSetCommandQueueProperty(cl_command_queue              command_queue,
+                              cl_command_queue_properties   properties,
+                              cl_bool                       enable,
+                              cl_command_queue_properties * old_properties) CL_API_SUFFIX__VERSION_1_0_DEPRECATED;
+#endif /* CL_USE_DEPRECATED_OPENCL_1_0_APIS */
+
+/* Deprecated OpenCL 1.1 APIs */
+extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_mem CL_API_CALL
+clCreateImage2D(cl_context              context,
+                cl_mem_flags            flags,
+                const cl_image_format * image_format,
+                size_t                  image_width,
+                size_t                  image_height,
+                size_t                  image_row_pitch,
+                void *                  host_ptr,
+                cl_int *                errcode_ret) CL_API_SUFFIX__VERSION_1_1_DEPRECATED;
+
+extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_mem CL_API_CALL
+clCreateImage3D(cl_context              context,
+                cl_mem_flags            flags,
+                const cl_image_format * image_format,
+                size_t                  image_width,
+                size_t                  image_height,
+                size_t                  image_depth,
+                size_t                  image_row_pitch,
+                size_t                  image_slice_pitch,
+                void *                  host_ptr,
+                cl_int *                errcode_ret) CL_API_SUFFIX__VERSION_1_1_DEPRECATED;
+
+extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL
+clEnqueueMarker(cl_command_queue    command_queue,
+                cl_event *          event) CL_API_SUFFIX__VERSION_1_1_DEPRECATED;
+
+extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL
+clEnqueueWaitForEvents(cl_command_queue  command_queue,
+                        cl_uint          num_events,
+                        const cl_event * event_list) CL_API_SUFFIX__VERSION_1_1_DEPRECATED;
+
+extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL
+clEnqueueBarrier(cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_1_DEPRECATED;
+
+extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL
+clUnloadCompiler(void) CL_API_SUFFIX__VERSION_1_1_DEPRECATED;
+
+extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_1_DEPRECATED void * CL_API_CALL
+clGetExtensionFunctionAddress(const char * func_name) CL_API_SUFFIX__VERSION_1_1_DEPRECATED;
+
+/* Deprecated OpenCL 2.0 APIs */
+extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_2_DEPRECATED cl_command_queue CL_API_CALL
+clCreateCommandQueue(cl_context                     context,
+                     cl_device_id                   device,
+                     cl_command_queue_properties    properties,
+                     cl_int *                       errcode_ret) CL_API_SUFFIX__VERSION_1_2_DEPRECATED;
+
+extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_2_DEPRECATED cl_sampler CL_API_CALL
+clCreateSampler(cl_context          context,
+                cl_bool             normalized_coords,
+                cl_addressing_mode  addressing_mode,
+                cl_filter_mode      filter_mode,
+                cl_int *            errcode_ret) CL_API_SUFFIX__VERSION_1_2_DEPRECATED;
+
+extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_2_DEPRECATED cl_int CL_API_CALL
+clEnqueueTask(cl_command_queue  command_queue,
+              cl_kernel         kernel,
+              cl_uint           num_events_in_wait_list,
+              const cl_event *  event_wait_list,
+              cl_event *        event) CL_API_SUFFIX__VERSION_1_2_DEPRECATED;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __OPENCL_CL_H */
diff --git a/OpenCL/CL/cl_d3d10.h b/OpenCL/CL/cl_d3d10.h
new file mode 100644
index 0000000..6adedb0
--- /dev/null
+++ b/OpenCL/CL/cl_d3d10.h
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2008-2020 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#ifndef __OPENCL_CL_D3D10_H
+#define __OPENCL_CL_D3D10_H
+
+#if defined(_MSC_VER)
+#if _MSC_VER >=1500
+#pragma warning( push )
+#pragma warning( disable : 4201 )
+#endif
+#endif
+#include <d3d10.h>
+#if defined(_MSC_VER)
+#if _MSC_VER >=1500
+#pragma warning( pop )
+#endif
+#endif
+#include <CL/cl.h>
+#include <CL/cl_platform.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************
+ * cl_khr_d3d10_sharing                                                       */
+#define cl_khr_d3d10_sharing 1
+
+typedef cl_uint cl_d3d10_device_source_khr;
+typedef cl_uint cl_d3d10_device_set_khr;
+
+/******************************************************************************/
+
+/* Error Codes */
+#define CL_INVALID_D3D10_DEVICE_KHR                  -1002
+#define CL_INVALID_D3D10_RESOURCE_KHR                -1003
+#define CL_D3D10_RESOURCE_ALREADY_ACQUIRED_KHR       -1004
+#define CL_D3D10_RESOURCE_NOT_ACQUIRED_KHR           -1005
+
+/* cl_d3d10_device_source_nv */
+#define CL_D3D10_DEVICE_KHR                          0x4010
+#define CL_D3D10_DXGI_ADAPTER_KHR                    0x4011
+
+/* cl_d3d10_device_set_nv */
+#define CL_PREFERRED_DEVICES_FOR_D3D10_KHR           0x4012
+#define CL_ALL_DEVICES_FOR_D3D10_KHR                 0x4013
+
+/* cl_context_info */
+#define CL_CONTEXT_D3D10_DEVICE_KHR                  0x4014
+#define CL_CONTEXT_D3D10_PREFER_SHARED_RESOURCES_KHR 0x402C
+
+/* cl_mem_info */
+#define CL_MEM_D3D10_RESOURCE_KHR                    0x4015
+
+/* cl_image_info */
+#define CL_IMAGE_D3D10_SUBRESOURCE_KHR               0x4016
+
+/* cl_command_type */
+#define CL_COMMAND_ACQUIRE_D3D10_OBJECTS_KHR         0x4017
+#define CL_COMMAND_RELEASE_D3D10_OBJECTS_KHR         0x4018
+
+/******************************************************************************/
+
+typedef cl_int (CL_API_CALL *clGetDeviceIDsFromD3D10KHR_fn)(
+    cl_platform_id             platform,
+    cl_d3d10_device_source_khr d3d_device_source,
+    void *                     d3d_object,
+    cl_d3d10_device_set_khr    d3d_device_set,
+    cl_uint                    num_entries,
+    cl_device_id *             devices,
+    cl_uint *                  num_devices) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_mem (CL_API_CALL *clCreateFromD3D10BufferKHR_fn)(
+    cl_context     context,
+    cl_mem_flags   flags,
+    ID3D10Buffer * resource,
+    cl_int *       errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_mem (CL_API_CALL *clCreateFromD3D10Texture2DKHR_fn)(
+    cl_context        context,
+    cl_mem_flags      flags,
+    ID3D10Texture2D * resource,
+    UINT              subresource,
+    cl_int *          errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_mem (CL_API_CALL *clCreateFromD3D10Texture3DKHR_fn)(
+    cl_context        context,
+    cl_mem_flags      flags,
+    ID3D10Texture3D * resource,
+    UINT              subresource,
+    cl_int *          errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int (CL_API_CALL *clEnqueueAcquireD3D10ObjectsKHR_fn)(
+    cl_command_queue command_queue,
+    cl_uint          num_objects,
+    const cl_mem *   mem_objects,
+    cl_uint          num_events_in_wait_list,
+    const cl_event * event_wait_list,
+    cl_event *       event) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int (CL_API_CALL *clEnqueueReleaseD3D10ObjectsKHR_fn)(
+    cl_command_queue command_queue,
+    cl_uint          num_objects,
+    const cl_mem *   mem_objects,
+    cl_uint          num_events_in_wait_list,
+    const cl_event * event_wait_list,
+    cl_event *       event) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __OPENCL_CL_D3D10_H */
+
diff --git a/OpenCL/CL/cl_d3d11.h b/OpenCL/CL/cl_d3d11.h
new file mode 100644
index 0000000..50ed906
--- /dev/null
+++ b/OpenCL/CL/cl_d3d11.h
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2008-2020 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#ifndef __OPENCL_CL_D3D11_H
+#define __OPENCL_CL_D3D11_H
+
+#if defined(_MSC_VER)
+#if _MSC_VER >=1500
+#pragma warning( push )
+#pragma warning( disable : 4201 )
+#endif
+#endif
+#include <d3d11.h>
+#if defined(_MSC_VER)
+#if _MSC_VER >=1500
+#pragma warning( pop )
+#endif
+#endif
+#include <CL/cl.h>
+#include <CL/cl_platform.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************
+ * cl_khr_d3d11_sharing                                                       */
+#define cl_khr_d3d11_sharing 1
+
+typedef cl_uint cl_d3d11_device_source_khr;
+typedef cl_uint cl_d3d11_device_set_khr;
+
+/******************************************************************************/
+
+/* Error Codes */
+#define CL_INVALID_D3D11_DEVICE_KHR                  -1006
+#define CL_INVALID_D3D11_RESOURCE_KHR                -1007
+#define CL_D3D11_RESOURCE_ALREADY_ACQUIRED_KHR       -1008
+#define CL_D3D11_RESOURCE_NOT_ACQUIRED_KHR           -1009
+
+/* cl_d3d11_device_source */
+#define CL_D3D11_DEVICE_KHR                          0x4019
+#define CL_D3D11_DXGI_ADAPTER_KHR                    0x401A
+
+/* cl_d3d11_device_set */
+#define CL_PREFERRED_DEVICES_FOR_D3D11_KHR           0x401B
+#define CL_ALL_DEVICES_FOR_D3D11_KHR                 0x401C
+
+/* cl_context_info */
+#define CL_CONTEXT_D3D11_DEVICE_KHR                  0x401D
+#define CL_CONTEXT_D3D11_PREFER_SHARED_RESOURCES_KHR 0x402D
+
+/* cl_mem_info */
+#define CL_MEM_D3D11_RESOURCE_KHR                    0x401E
+
+/* cl_image_info */
+#define CL_IMAGE_D3D11_SUBRESOURCE_KHR               0x401F
+
+/* cl_command_type */
+#define CL_COMMAND_ACQUIRE_D3D11_OBJECTS_KHR         0x4020
+#define CL_COMMAND_RELEASE_D3D11_OBJECTS_KHR         0x4021
+
+/******************************************************************************/
+
+typedef cl_int (CL_API_CALL *clGetDeviceIDsFromD3D11KHR_fn)(
+    cl_platform_id             platform,
+    cl_d3d11_device_source_khr d3d_device_source,
+    void *                     d3d_object,
+    cl_d3d11_device_set_khr    d3d_device_set,
+    cl_uint                    num_entries,
+    cl_device_id *             devices,
+    cl_uint *                  num_devices) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_mem (CL_API_CALL *clCreateFromD3D11BufferKHR_fn)(
+    cl_context     context,
+    cl_mem_flags   flags,
+    ID3D11Buffer * resource,
+    cl_int *       errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_mem (CL_API_CALL *clCreateFromD3D11Texture2DKHR_fn)(
+    cl_context        context,
+    cl_mem_flags      flags,
+    ID3D11Texture2D * resource,
+    UINT              subresource,
+    cl_int *          errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_mem (CL_API_CALL *clCreateFromD3D11Texture3DKHR_fn)(
+    cl_context        context,
+    cl_mem_flags      flags,
+    ID3D11Texture3D * resource,
+    UINT              subresource,
+    cl_int *          errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_int (CL_API_CALL *clEnqueueAcquireD3D11ObjectsKHR_fn)(
+    cl_command_queue command_queue,
+    cl_uint          num_objects,
+    const cl_mem *   mem_objects,
+    cl_uint          num_events_in_wait_list,
+    const cl_event * event_wait_list,
+    cl_event *       event) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_int (CL_API_CALL *clEnqueueReleaseD3D11ObjectsKHR_fn)(
+    cl_command_queue command_queue,
+    cl_uint          num_objects,
+    const cl_mem *   mem_objects,
+    cl_uint          num_events_in_wait_list,
+    const cl_event * event_wait_list,
+    cl_event *       event) CL_API_SUFFIX__VERSION_1_2;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __OPENCL_CL_D3D11_H */
+
diff --git a/OpenCL/CL/cl_dx9_media_sharing.h b/OpenCL/CL/cl_dx9_media_sharing.h
new file mode 100644
index 0000000..b0d2b23
--- /dev/null
+++ b/OpenCL/CL/cl_dx9_media_sharing.h
@@ -0,0 +1,229 @@
+/*******************************************************************************
+ * Copyright (c) 2008-2020 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#ifndef __OPENCL_CL_DX9_MEDIA_SHARING_H
+#define __OPENCL_CL_DX9_MEDIA_SHARING_H
+
+#include <CL/cl.h>
+#include <CL/cl_platform.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************/
+/* cl_khr_dx9_media_sharing                                                   */
+#define cl_khr_dx9_media_sharing 1
+
+typedef cl_uint             cl_dx9_media_adapter_type_khr;
+typedef cl_uint             cl_dx9_media_adapter_set_khr;
+    
+#if defined(_WIN32)
+#include <d3d9.h>
+typedef struct _cl_dx9_surface_info_khr
+{
+    IDirect3DSurface9 *resource;
+    HANDLE shared_handle;
+} cl_dx9_surface_info_khr;
+#endif
+
+
+/******************************************************************************/
+
+/* Error Codes */
+#define CL_INVALID_DX9_MEDIA_ADAPTER_KHR                -1010
+#define CL_INVALID_DX9_MEDIA_SURFACE_KHR                -1011
+#define CL_DX9_MEDIA_SURFACE_ALREADY_ACQUIRED_KHR       -1012
+#define CL_DX9_MEDIA_SURFACE_NOT_ACQUIRED_KHR           -1013
+
+/* cl_media_adapter_type_khr */
+#define CL_ADAPTER_D3D9_KHR                              0x2020
+#define CL_ADAPTER_D3D9EX_KHR                            0x2021
+#define CL_ADAPTER_DXVA_KHR                              0x2022
+
+/* cl_media_adapter_set_khr */
+#define CL_PREFERRED_DEVICES_FOR_DX9_MEDIA_ADAPTER_KHR   0x2023
+#define CL_ALL_DEVICES_FOR_DX9_MEDIA_ADAPTER_KHR         0x2024
+
+/* cl_context_info */
+#define CL_CONTEXT_ADAPTER_D3D9_KHR                      0x2025
+#define CL_CONTEXT_ADAPTER_D3D9EX_KHR                    0x2026
+#define CL_CONTEXT_ADAPTER_DXVA_KHR                      0x2027
+
+/* cl_mem_info */
+#define CL_MEM_DX9_MEDIA_ADAPTER_TYPE_KHR                0x2028
+#define CL_MEM_DX9_MEDIA_SURFACE_INFO_KHR                0x2029
+
+/* cl_image_info */
+#define CL_IMAGE_DX9_MEDIA_PLANE_KHR                     0x202A
+
+/* cl_command_type */
+#define CL_COMMAND_ACQUIRE_DX9_MEDIA_SURFACES_KHR        0x202B
+#define CL_COMMAND_RELEASE_DX9_MEDIA_SURFACES_KHR        0x202C
+
+/******************************************************************************/
+
+typedef cl_int (CL_API_CALL *clGetDeviceIDsFromDX9MediaAdapterKHR_fn)(
+    cl_platform_id                   platform,
+    cl_uint                          num_media_adapters,
+    cl_dx9_media_adapter_type_khr *  media_adapter_type,
+    void *                           media_adapters,
+    cl_dx9_media_adapter_set_khr     media_adapter_set,
+    cl_uint                          num_entries,
+    cl_device_id *                   devices,
+    cl_uint *                        num_devices) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_mem (CL_API_CALL *clCreateFromDX9MediaSurfaceKHR_fn)(
+    cl_context                    context,
+    cl_mem_flags                  flags,
+    cl_dx9_media_adapter_type_khr adapter_type,
+    void *                        surface_info,
+    cl_uint                       plane,                                                                          
+    cl_int *                      errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_int (CL_API_CALL *clEnqueueAcquireDX9MediaSurfacesKHR_fn)(
+    cl_command_queue command_queue,
+    cl_uint          num_objects,
+    const cl_mem *   mem_objects,
+    cl_uint          num_events_in_wait_list,
+    const cl_event * event_wait_list,
+    cl_event *       event) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_int (CL_API_CALL *clEnqueueReleaseDX9MediaSurfacesKHR_fn)(
+    cl_command_queue command_queue,
+    cl_uint          num_objects,
+    const cl_mem *   mem_objects,
+    cl_uint          num_events_in_wait_list,
+    const cl_event * event_wait_list,
+    cl_event *       event) CL_API_SUFFIX__VERSION_1_2;
+
+/***************************************
+* cl_intel_dx9_media_sharing extension *
+****************************************/
+
+#define cl_intel_dx9_media_sharing 1
+
+typedef cl_uint cl_dx9_device_source_intel;
+typedef cl_uint cl_dx9_device_set_intel;
+
+/* error codes */
+#define CL_INVALID_DX9_DEVICE_INTEL                   -1010
+#define CL_INVALID_DX9_RESOURCE_INTEL                 -1011
+#define CL_DX9_RESOURCE_ALREADY_ACQUIRED_INTEL        -1012
+#define CL_DX9_RESOURCE_NOT_ACQUIRED_INTEL            -1013
+
+/* cl_dx9_device_source_intel */
+#define CL_D3D9_DEVICE_INTEL                          0x4022
+#define CL_D3D9EX_DEVICE_INTEL                        0x4070
+#define CL_DXVA_DEVICE_INTEL                          0x4071
+
+/* cl_dx9_device_set_intel */
+#define CL_PREFERRED_DEVICES_FOR_DX9_INTEL            0x4024
+#define CL_ALL_DEVICES_FOR_DX9_INTEL                  0x4025
+
+/* cl_context_info */
+#define CL_CONTEXT_D3D9_DEVICE_INTEL                  0x4026
+#define CL_CONTEXT_D3D9EX_DEVICE_INTEL                0x4072
+#define CL_CONTEXT_DXVA_DEVICE_INTEL                  0x4073
+
+/* cl_mem_info */
+#define CL_MEM_DX9_RESOURCE_INTEL                     0x4027
+#define CL_MEM_DX9_SHARED_HANDLE_INTEL                0x4074
+
+/* cl_image_info */
+#define CL_IMAGE_DX9_PLANE_INTEL                      0x4075
+
+/* cl_command_type */
+#define CL_COMMAND_ACQUIRE_DX9_OBJECTS_INTEL          0x402A
+#define CL_COMMAND_RELEASE_DX9_OBJECTS_INTEL          0x402B
+/******************************************************************************/
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetDeviceIDsFromDX9INTEL(
+    cl_platform_id              platform,
+    cl_dx9_device_source_intel  dx9_device_source,
+    void*                       dx9_object,
+    cl_dx9_device_set_intel     dx9_device_set,
+    cl_uint                     num_entries,
+    cl_device_id*               devices,
+    cl_uint*                    num_devices) CL_API_SUFFIX__VERSION_1_1;
+
+typedef cl_int (CL_API_CALL* clGetDeviceIDsFromDX9INTEL_fn)(
+    cl_platform_id              platform,
+    cl_dx9_device_source_intel  dx9_device_source,
+    void*                       dx9_object,
+    cl_dx9_device_set_intel     dx9_device_set,
+    cl_uint                     num_entries,
+    cl_device_id*               devices,
+    cl_uint*                    num_devices) CL_API_SUFFIX__VERSION_1_1;
+
+extern CL_API_ENTRY cl_mem CL_API_CALL
+clCreateFromDX9MediaSurfaceINTEL(
+    cl_context                  context,
+    cl_mem_flags                flags,
+    IDirect3DSurface9*          resource,
+    HANDLE                      sharedHandle,
+    UINT                        plane,
+    cl_int*                     errcode_ret) CL_API_SUFFIX__VERSION_1_1;
+
+typedef cl_mem (CL_API_CALL *clCreateFromDX9MediaSurfaceINTEL_fn)(
+    cl_context                  context,
+    cl_mem_flags                flags,
+    IDirect3DSurface9*          resource,
+    HANDLE                      sharedHandle,
+    UINT                        plane,
+    cl_int*                     errcode_ret) CL_API_SUFFIX__VERSION_1_1;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueAcquireDX9ObjectsINTEL(
+    cl_command_queue            command_queue,
+    cl_uint                     num_objects,
+    const cl_mem*               mem_objects,
+    cl_uint                     num_events_in_wait_list,
+    const cl_event*             event_wait_list,
+    cl_event*                   event) CL_API_SUFFIX__VERSION_1_1;
+
+typedef cl_int (CL_API_CALL *clEnqueueAcquireDX9ObjectsINTEL_fn)(
+    cl_command_queue            command_queue,
+    cl_uint                     num_objects,
+    const cl_mem*               mem_objects,
+    cl_uint                     num_events_in_wait_list,
+    const cl_event*             event_wait_list,
+    cl_event*                   event) CL_API_SUFFIX__VERSION_1_1;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueReleaseDX9ObjectsINTEL(
+    cl_command_queue            command_queue,
+    cl_uint                     num_objects,
+    cl_mem*                     mem_objects,
+    cl_uint                     num_events_in_wait_list,
+    const cl_event*             event_wait_list,
+    cl_event*                   event) CL_API_SUFFIX__VERSION_1_1;
+
+typedef cl_int (CL_API_CALL *clEnqueueReleaseDX9ObjectsINTEL_fn)(
+    cl_command_queue            command_queue,
+    cl_uint                     num_objects,
+    cl_mem*                     mem_objects,
+    cl_uint                     num_events_in_wait_list,
+    const cl_event*             event_wait_list,
+    cl_event*                   event) CL_API_SUFFIX__VERSION_1_1;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __OPENCL_CL_DX9_MEDIA_SHARING_H */
+
diff --git a/OpenCL/CL/cl_dx9_media_sharing_intel.h b/OpenCL/CL/cl_dx9_media_sharing_intel.h
new file mode 100644
index 0000000..f6518d7
--- /dev/null
+++ b/OpenCL/CL/cl_dx9_media_sharing_intel.h
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2008-2020 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#include <CL/cl_dx9_media_sharing.h>
+#pragma message("The Intel DX9 media sharing extensions have been moved into cl_dx9_media_sharing.h.  Please include cl_dx9_media_sharing.h directly.")
diff --git a/OpenCL/CL/cl_egl.h b/OpenCL/CL/cl_egl.h
new file mode 100644
index 0000000..357a37c
--- /dev/null
+++ b/OpenCL/CL/cl_egl.h
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * Copyright (c) 2008-2020 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#ifndef __OPENCL_CL_EGL_H
+#define __OPENCL_CL_EGL_H
+
+#include <CL/cl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Command type for events created with clEnqueueAcquireEGLObjectsKHR */
+#define CL_COMMAND_EGL_FENCE_SYNC_OBJECT_KHR  0x202F
+#define CL_COMMAND_ACQUIRE_EGL_OBJECTS_KHR    0x202D
+#define CL_COMMAND_RELEASE_EGL_OBJECTS_KHR    0x202E
+
+/* Error type for clCreateFromEGLImageKHR */
+#define CL_INVALID_EGL_OBJECT_KHR             -1093
+#define CL_EGL_RESOURCE_NOT_ACQUIRED_KHR      -1092
+
+/* CLeglImageKHR is an opaque handle to an EGLImage */
+typedef void* CLeglImageKHR;
+
+/* CLeglDisplayKHR is an opaque handle to an EGLDisplay */
+typedef void* CLeglDisplayKHR;
+
+/* CLeglSyncKHR is an opaque handle to an EGLSync object */
+typedef void* CLeglSyncKHR;
+
+/* properties passed to clCreateFromEGLImageKHR */
+typedef intptr_t cl_egl_image_properties_khr;
+
+
+#define cl_khr_egl_image 1
+
+extern CL_API_ENTRY cl_mem CL_API_CALL
+clCreateFromEGLImageKHR(cl_context                  context,
+                        CLeglDisplayKHR             egldisplay,
+                        CLeglImageKHR               eglimage,
+                        cl_mem_flags                flags,
+                        const cl_egl_image_properties_khr * properties,
+                        cl_int *                    errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_mem (CL_API_CALL *clCreateFromEGLImageKHR_fn)(
+    cl_context                  context,
+    CLeglDisplayKHR             egldisplay,
+    CLeglImageKHR               eglimage,
+    cl_mem_flags                flags,
+    const cl_egl_image_properties_khr * properties,
+    cl_int *                    errcode_ret);
+
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueAcquireEGLObjectsKHR(cl_command_queue command_queue,
+                              cl_uint          num_objects,
+                              const cl_mem *   mem_objects,
+                              cl_uint          num_events_in_wait_list,
+                              const cl_event * event_wait_list,
+                              cl_event *       event) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int (CL_API_CALL *clEnqueueAcquireEGLObjectsKHR_fn)(
+    cl_command_queue command_queue,
+    cl_uint          num_objects,
+    const cl_mem *   mem_objects,
+    cl_uint          num_events_in_wait_list,
+    const cl_event * event_wait_list,
+    cl_event *       event);
+
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueReleaseEGLObjectsKHR(cl_command_queue command_queue,
+                              cl_uint          num_objects,
+                              const cl_mem *   mem_objects,
+                              cl_uint          num_events_in_wait_list,
+                              const cl_event * event_wait_list,
+                              cl_event *       event) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int (CL_API_CALL *clEnqueueReleaseEGLObjectsKHR_fn)(
+    cl_command_queue command_queue,
+    cl_uint          num_objects,
+    const cl_mem *   mem_objects,
+    cl_uint          num_events_in_wait_list,
+    const cl_event * event_wait_list,
+    cl_event *       event);
+
+
+#define cl_khr_egl_event 1
+
+extern CL_API_ENTRY cl_event CL_API_CALL
+clCreateEventFromEGLSyncKHR(cl_context      context,
+                            CLeglSyncKHR    sync,
+                            CLeglDisplayKHR display,
+                            cl_int *        errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_event (CL_API_CALL *clCreateEventFromEGLSyncKHR_fn)(
+    cl_context      context,
+    CLeglSyncKHR    sync,
+    CLeglDisplayKHR display,
+    cl_int *        errcode_ret);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OPENCL_CL_EGL_H */
diff --git a/OpenCL/CL/cl_ext.h b/OpenCL/CL/cl_ext.h
new file mode 100644
index 0000000..36d0256
--- /dev/null
+++ b/OpenCL/CL/cl_ext.h
@@ -0,0 +1,1723 @@
+/*******************************************************************************
+ * Copyright (c) 2008-2020 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+/* cl_ext.h contains OpenCL extensions which don't have external */
+/* (OpenGL, D3D) dependencies.                                   */
+
+#ifndef __CL_EXT_H
+#define __CL_EXT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <CL/cl.h>
+
+/* cl_khr_fp64 extension - no extension #define since it has no functions  */
+/* CL_DEVICE_DOUBLE_FP_CONFIG is defined in CL.h for OpenCL >= 120 */
+
+#if CL_TARGET_OPENCL_VERSION <= 110
+#define CL_DEVICE_DOUBLE_FP_CONFIG                       0x1032
+#endif
+
+/* cl_khr_fp16 extension - no extension #define since it has no functions  */
+#define CL_DEVICE_HALF_FP_CONFIG                    0x1033
+
+/* Memory object destruction
+ *
+ * Apple extension for use to manage externally allocated buffers used with cl_mem objects with CL_MEM_USE_HOST_PTR
+ *
+ * Registers a user callback function that will be called when the memory object is deleted and its resources
+ * freed. Each call to clSetMemObjectCallbackFn registers the specified user callback function on a callback
+ * stack associated with memobj. The registered user callback functions are called in the reverse order in
+ * which they were registered. The user callback functions are called and then the memory object is deleted
+ * and its resources freed. This provides a mechanism for the application (and libraries) using memobj to be
+ * notified when the memory referenced by host_ptr, specified when the memory object is created and used as
+ * the storage bits for the memory object, can be reused or freed.
+ *
+ * The application may not call CL api's with the cl_mem object passed to the pfn_notify.
+ *
+ * Please check for the "cl_APPLE_SetMemObjectDestructor" extension using clGetDeviceInfo(CL_DEVICE_EXTENSIONS)
+ * before using.
+ */
+#define cl_APPLE_SetMemObjectDestructor 1
+extern CL_API_ENTRY cl_int CL_API_CALL clSetMemObjectDestructorAPPLE(  cl_mem memobj,
+                                        void (* pfn_notify)(cl_mem memobj, void * user_data),
+                                        void * user_data)             CL_API_SUFFIX__VERSION_1_0;
+
+
+/* Context Logging Functions
+ *
+ * The next three convenience functions are intended to be used as the pfn_notify parameter to clCreateContext().
+ * Please check for the "cl_APPLE_ContextLoggingFunctions" extension using clGetDeviceInfo(CL_DEVICE_EXTENSIONS)
+ * before using.
+ *
+ * clLogMessagesToSystemLog forwards on all log messages to the Apple System Logger
+ */
+#define cl_APPLE_ContextLoggingFunctions 1
+extern CL_API_ENTRY void CL_API_CALL clLogMessagesToSystemLogAPPLE(  const char * errstr,
+                                            const void * private_info,
+                                            size_t       cb,
+                                            void *       user_data)  CL_API_SUFFIX__VERSION_1_0;
+
+/* clLogMessagesToStdout sends all log messages to the file descriptor stdout */
+extern CL_API_ENTRY void CL_API_CALL clLogMessagesToStdoutAPPLE(   const char * errstr,
+                                          const void * private_info,
+                                          size_t       cb,
+                                          void *       user_data)    CL_API_SUFFIX__VERSION_1_0;
+
+/* clLogMessagesToStderr sends all log messages to the file descriptor stderr */
+extern CL_API_ENTRY void CL_API_CALL clLogMessagesToStderrAPPLE(   const char * errstr,
+                                          const void * private_info,
+                                          size_t       cb,
+                                          void *       user_data)    CL_API_SUFFIX__VERSION_1_0;
+
+
+/************************
+* cl_khr_icd extension *
+************************/
+#define cl_khr_icd 1
+
+/* cl_platform_info                                                        */
+#define CL_PLATFORM_ICD_SUFFIX_KHR                  0x0920
+
+/* Additional Error Codes                                                  */
+#define CL_PLATFORM_NOT_FOUND_KHR                   -1001
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clIcdGetPlatformIDsKHR(cl_uint          num_entries,
+                       cl_platform_id * platforms,
+                       cl_uint *        num_platforms);
+
+typedef cl_int
+(CL_API_CALL *clIcdGetPlatformIDsKHR_fn)(cl_uint          num_entries,
+                                         cl_platform_id * platforms,
+                                         cl_uint *        num_platforms);
+
+
+/*******************************
+ * cl_khr_il_program extension *
+ *******************************/
+#define cl_khr_il_program 1
+
+/* New property to clGetDeviceInfo for retrieving supported intermediate
+ * languages
+ */
+#define CL_DEVICE_IL_VERSION_KHR                    0x105B
+
+/* New property to clGetProgramInfo for retrieving for retrieving the IL of a
+ * program
+ */
+#define CL_PROGRAM_IL_KHR                           0x1169
+
+extern CL_API_ENTRY cl_program CL_API_CALL
+clCreateProgramWithILKHR(cl_context   context,
+                         const void * il,
+                         size_t       length,
+                         cl_int *     errcode_ret);
+
+typedef cl_program
+(CL_API_CALL *clCreateProgramWithILKHR_fn)(cl_context   context,
+                                           const void * il,
+                                           size_t       length,
+                                           cl_int *     errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+/* Extension: cl_khr_image2d_from_buffer
+ *
+ * This extension allows a 2D image to be created from a cl_mem buffer without
+ * a copy. The type associated with a 2D image created from a buffer in an
+ * OpenCL program is image2d_t. Both the sampler and sampler-less read_image
+ * built-in functions are supported for 2D images and 2D images created from
+ * a buffer.  Similarly, the write_image built-ins are also supported for 2D
+ * images created from a buffer.
+ *
+ * When the 2D image from buffer is created, the client must specify the
+ * width, height, image format (i.e. channel order and channel data type)
+ * and optionally the row pitch.
+ *
+ * The pitch specified must be a multiple of
+ * CL_DEVICE_IMAGE_PITCH_ALIGNMENT_KHR pixels.
+ * The base address of the buffer must be aligned to
+ * CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT_KHR pixels.
+ */
+
+#define CL_DEVICE_IMAGE_PITCH_ALIGNMENT_KHR              0x104A
+#define CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT_KHR       0x104B
+
+
+/**************************************
+ * cl_khr_initialize_memory extension *
+ **************************************/
+
+#define CL_CONTEXT_MEMORY_INITIALIZE_KHR            0x2030
+
+
+/**************************************
+ * cl_khr_terminate_context extension *
+ **************************************/
+
+#define CL_CONTEXT_TERMINATED_KHR                   -1121
+
+#define CL_DEVICE_TERMINATE_CAPABILITY_KHR          0x2031
+#define CL_CONTEXT_TERMINATE_KHR                    0x2032
+
+#define cl_khr_terminate_context 1
+extern CL_API_ENTRY cl_int CL_API_CALL
+clTerminateContextKHR(cl_context context) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_int
+(CL_API_CALL *clTerminateContextKHR_fn)(cl_context context) CL_API_SUFFIX__VERSION_1_2;
+
+
+/*
+ * Extension: cl_khr_spir
+ *
+ * This extension adds support to create an OpenCL program object from a
+ * Standard Portable Intermediate Representation (SPIR) instance
+ */
+
+#define CL_DEVICE_SPIR_VERSIONS                     0x40E0
+#define CL_PROGRAM_BINARY_TYPE_INTERMEDIATE         0x40E1
+
+
+/*****************************************
+ * cl_khr_create_command_queue extension *
+ *****************************************/
+#define cl_khr_create_command_queue 1
+
+typedef cl_properties cl_queue_properties_khr;
+
+extern CL_API_ENTRY cl_command_queue CL_API_CALL
+clCreateCommandQueueWithPropertiesKHR(cl_context context,
+                                      cl_device_id device,
+                                      const cl_queue_properties_khr* properties,
+                                      cl_int* errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_command_queue
+(CL_API_CALL *clCreateCommandQueueWithPropertiesKHR_fn)(cl_context context,
+                                                        cl_device_id device,
+                                                        const cl_queue_properties_khr* properties,
+                                                        cl_int* errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+
+/******************************************
+* cl_nv_device_attribute_query extension *
+******************************************/
+
+/* cl_nv_device_attribute_query extension - no extension #define since it has no functions */
+#define CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV       0x4000
+#define CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV       0x4001
+#define CL_DEVICE_REGISTERS_PER_BLOCK_NV            0x4002
+#define CL_DEVICE_WARP_SIZE_NV                      0x4003
+#define CL_DEVICE_GPU_OVERLAP_NV                    0x4004
+#define CL_DEVICE_KERNEL_EXEC_TIMEOUT_NV            0x4005
+#define CL_DEVICE_INTEGRATED_MEMORY_NV              0x4006
+
+
+/*********************************
+* cl_amd_device_attribute_query *
+*********************************/
+
+#define CL_DEVICE_PROFILING_TIMER_OFFSET_AMD            0x4036
+#define CL_DEVICE_TOPOLOGY_AMD                          0x4037
+#define CL_DEVICE_BOARD_NAME_AMD                        0x4038
+#define CL_DEVICE_GLOBAL_FREE_MEMORY_AMD                0x4039
+#define CL_DEVICE_SIMD_PER_COMPUTE_UNIT_AMD             0x4040
+#define CL_DEVICE_SIMD_WIDTH_AMD                        0x4041
+#define CL_DEVICE_SIMD_INSTRUCTION_WIDTH_AMD            0x4042
+#define CL_DEVICE_WAVEFRONT_WIDTH_AMD                   0x4043
+#define CL_DEVICE_GLOBAL_MEM_CHANNELS_AMD               0x4044
+#define CL_DEVICE_GLOBAL_MEM_CHANNEL_BANKS_AMD          0x4045
+#define CL_DEVICE_GLOBAL_MEM_CHANNEL_BANK_WIDTH_AMD     0x4046
+#define CL_DEVICE_LOCAL_MEM_SIZE_PER_COMPUTE_UNIT_AMD   0x4047
+#define CL_DEVICE_LOCAL_MEM_BANKS_AMD                   0x4048
+#define CL_DEVICE_THREAD_TRACE_SUPPORTED_AMD            0x4049
+#define CL_DEVICE_GFXIP_MAJOR_AMD                       0x404A
+#define CL_DEVICE_GFXIP_MINOR_AMD                       0x404B
+#define CL_DEVICE_AVAILABLE_ASYNC_QUEUES_AMD            0x404C
+#define CL_DEVICE_PREFERRED_WORK_GROUP_SIZE_AMD         0x4030
+#define CL_DEVICE_MAX_WORK_GROUP_SIZE_AMD               0x4031
+#define CL_DEVICE_PREFERRED_CONSTANT_BUFFER_SIZE_AMD    0x4033
+#define CL_DEVICE_PCIE_ID_AMD                           0x4034
+
+
+/*********************************
+* cl_arm_printf extension
+*********************************/
+
+#define CL_PRINTF_CALLBACK_ARM                      0x40B0
+#define CL_PRINTF_BUFFERSIZE_ARM                    0x40B1
+
+
+/***********************************
+* cl_ext_device_fission extension
+***********************************/
+#define cl_ext_device_fission   1
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clReleaseDeviceEXT(cl_device_id device) CL_API_SUFFIX__VERSION_1_1;
+
+typedef cl_int
+(CL_API_CALL *clReleaseDeviceEXT_fn)(cl_device_id device) CL_API_SUFFIX__VERSION_1_1;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clRetainDeviceEXT(cl_device_id device) CL_API_SUFFIX__VERSION_1_1;
+
+typedef cl_int
+(CL_API_CALL *clRetainDeviceEXT_fn)(cl_device_id device) CL_API_SUFFIX__VERSION_1_1;
+
+typedef cl_ulong  cl_device_partition_property_ext;
+extern CL_API_ENTRY cl_int CL_API_CALL
+clCreateSubDevicesEXT(cl_device_id   in_device,
+                      const cl_device_partition_property_ext * properties,
+                      cl_uint        num_entries,
+                      cl_device_id * out_devices,
+                      cl_uint *      num_devices) CL_API_SUFFIX__VERSION_1_1;
+
+typedef cl_int
+(CL_API_CALL * clCreateSubDevicesEXT_fn)(cl_device_id   in_device,
+                                         const cl_device_partition_property_ext * properties,
+                                         cl_uint        num_entries,
+                                         cl_device_id * out_devices,
+                                         cl_uint *      num_devices) CL_API_SUFFIX__VERSION_1_1;
+
+/* cl_device_partition_property_ext */
+#define CL_DEVICE_PARTITION_EQUALLY_EXT             0x4050
+#define CL_DEVICE_PARTITION_BY_COUNTS_EXT           0x4051
+#define CL_DEVICE_PARTITION_BY_NAMES_EXT            0x4052
+#define CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN_EXT  0x4053
+
+/* clDeviceGetInfo selectors */
+#define CL_DEVICE_PARENT_DEVICE_EXT                 0x4054
+#define CL_DEVICE_PARTITION_TYPES_EXT               0x4055
+#define CL_DEVICE_AFFINITY_DOMAINS_EXT              0x4056
+#define CL_DEVICE_REFERENCE_COUNT_EXT               0x4057
+#define CL_DEVICE_PARTITION_STYLE_EXT               0x4058
+
+/* error codes */
+#define CL_DEVICE_PARTITION_FAILED_EXT              -1057
+#define CL_INVALID_PARTITION_COUNT_EXT              -1058
+#define CL_INVALID_PARTITION_NAME_EXT               -1059
+
+/* CL_AFFINITY_DOMAINs */
+#define CL_AFFINITY_DOMAIN_L1_CACHE_EXT             0x1
+#define CL_AFFINITY_DOMAIN_L2_CACHE_EXT             0x2
+#define CL_AFFINITY_DOMAIN_L3_CACHE_EXT             0x3
+#define CL_AFFINITY_DOMAIN_L4_CACHE_EXT             0x4
+#define CL_AFFINITY_DOMAIN_NUMA_EXT                 0x10
+#define CL_AFFINITY_DOMAIN_NEXT_FISSIONABLE_EXT     0x100
+
+/* cl_device_partition_property_ext list terminators */
+#define CL_PROPERTIES_LIST_END_EXT                  ((cl_device_partition_property_ext) 0)
+#define CL_PARTITION_BY_COUNTS_LIST_END_EXT         ((cl_device_partition_property_ext) 0)
+#define CL_PARTITION_BY_NAMES_LIST_END_EXT          ((cl_device_partition_property_ext) 0 - 1)
+
+
+/***********************************
+ * cl_ext_migrate_memobject extension definitions
+ ***********************************/
+#define cl_ext_migrate_memobject 1
+
+typedef cl_bitfield cl_mem_migration_flags_ext;
+
+#define CL_MIGRATE_MEM_OBJECT_HOST_EXT              0x1
+
+#define CL_COMMAND_MIGRATE_MEM_OBJECT_EXT           0x4040
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueMigrateMemObjectEXT(cl_command_queue command_queue,
+                             cl_uint          num_mem_objects,
+                             const cl_mem *   mem_objects,
+                             cl_mem_migration_flags_ext flags,
+                             cl_uint          num_events_in_wait_list,
+                             const cl_event * event_wait_list,
+                             cl_event *       event);
+
+typedef cl_int
+(CL_API_CALL *clEnqueueMigrateMemObjectEXT_fn)(cl_command_queue command_queue,
+                                               cl_uint          num_mem_objects,
+                                               const cl_mem *   mem_objects,
+                                               cl_mem_migration_flags_ext flags,
+                                               cl_uint          num_events_in_wait_list,
+                                               const cl_event * event_wait_list,
+                                               cl_event *       event);
+
+
+/*********************************
+* cl_ext_cxx_for_opencl extension
+*********************************/
+#define cl_ext_cxx_for_opencl 1
+
+#define CL_DEVICE_CXX_FOR_OPENCL_NUMERIC_VERSION_EXT 0x4230
+
+/*********************************
+* cl_qcom_ext_host_ptr extension
+*********************************/
+#define cl_qcom_ext_host_ptr 1
+
+#define CL_MEM_EXT_HOST_PTR_QCOM                  (1 << 29)
+
+#define CL_DEVICE_EXT_MEM_PADDING_IN_BYTES_QCOM   0x40A0
+#define CL_DEVICE_PAGE_SIZE_QCOM                  0x40A1
+#define CL_IMAGE_ROW_ALIGNMENT_QCOM               0x40A2
+#define CL_IMAGE_SLICE_ALIGNMENT_QCOM             0x40A3
+#define CL_MEM_HOST_UNCACHED_QCOM                 0x40A4
+#define CL_MEM_HOST_WRITEBACK_QCOM                0x40A5
+#define CL_MEM_HOST_WRITETHROUGH_QCOM             0x40A6
+#define CL_MEM_HOST_WRITE_COMBINING_QCOM          0x40A7
+
+typedef cl_uint                                   cl_image_pitch_info_qcom;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetDeviceImageInfoQCOM(cl_device_id             device,
+                         size_t                   image_width,
+                         size_t                   image_height,
+                         const cl_image_format   *image_format,
+                         cl_image_pitch_info_qcom param_name,
+                         size_t                   param_value_size,
+                         void                    *param_value,
+                         size_t                  *param_value_size_ret);
+
+typedef struct _cl_mem_ext_host_ptr
+{
+    /* Type of external memory allocation. */
+    /* Legal values will be defined in layered extensions. */
+    cl_uint  allocation_type;
+
+    /* Host cache policy for this external memory allocation. */
+    cl_uint  host_cache_policy;
+
+} cl_mem_ext_host_ptr;
+
+
+/*******************************************
+* cl_qcom_ext_host_ptr_iocoherent extension
+********************************************/
+
+/* Cache policy specifying io-coherence */
+#define CL_MEM_HOST_IOCOHERENT_QCOM               0x40A9
+
+
+/*********************************
+* cl_qcom_ion_host_ptr extension
+*********************************/
+
+#define CL_MEM_ION_HOST_PTR_QCOM                  0x40A8
+
+typedef struct _cl_mem_ion_host_ptr
+{
+    /* Type of external memory allocation. */
+    /* Must be CL_MEM_ION_HOST_PTR_QCOM for ION allocations. */
+    cl_mem_ext_host_ptr  ext_host_ptr;
+
+    /* ION file descriptor */
+    int                  ion_filedesc;
+
+    /* Host pointer to the ION allocated memory */
+    void*                ion_hostptr;
+
+} cl_mem_ion_host_ptr;
+
+
+/*********************************
+* cl_qcom_android_native_buffer_host_ptr extension
+*********************************/
+
+#define CL_MEM_ANDROID_NATIVE_BUFFER_HOST_PTR_QCOM                  0x40C6
+
+typedef struct _cl_mem_android_native_buffer_host_ptr
+{
+    /* Type of external memory allocation. */
+    /* Must be CL_MEM_ANDROID_NATIVE_BUFFER_HOST_PTR_QCOM for Android native buffers. */
+    cl_mem_ext_host_ptr  ext_host_ptr;
+
+    /* Virtual pointer to the android native buffer */
+    void*                anb_ptr;
+
+} cl_mem_android_native_buffer_host_ptr;
+
+
+/******************************************
+ * cl_img_yuv_image extension *
+ ******************************************/
+
+/* Image formats used in clCreateImage */
+#define CL_NV21_IMG                                 0x40D0
+#define CL_YV12_IMG                                 0x40D1
+
+
+/******************************************
+ * cl_img_cached_allocations extension *
+ ******************************************/
+
+/* Flag values used by clCreateBuffer */
+#define CL_MEM_USE_UNCACHED_CPU_MEMORY_IMG          (1 << 26)
+#define CL_MEM_USE_CACHED_CPU_MEMORY_IMG            (1 << 27)
+
+
+/******************************************
+ * cl_img_use_gralloc_ptr extension *
+ ******************************************/
+#define cl_img_use_gralloc_ptr 1
+
+/* Flag values used by clCreateBuffer */
+#define CL_MEM_USE_GRALLOC_PTR_IMG                  (1 << 28)
+
+/* To be used by clGetEventInfo: */
+#define CL_COMMAND_ACQUIRE_GRALLOC_OBJECTS_IMG      0x40D2
+#define CL_COMMAND_RELEASE_GRALLOC_OBJECTS_IMG      0x40D3
+
+/* Error codes from clEnqueueAcquireGrallocObjectsIMG and clEnqueueReleaseGrallocObjectsIMG */
+#define CL_GRALLOC_RESOURCE_NOT_ACQUIRED_IMG        0x40D4
+#define CL_INVALID_GRALLOC_OBJECT_IMG               0x40D5
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueAcquireGrallocObjectsIMG(cl_command_queue      command_queue,
+                                  cl_uint               num_objects,
+                                  const cl_mem *        mem_objects,
+                                  cl_uint               num_events_in_wait_list,
+                                  const cl_event *      event_wait_list,
+                                  cl_event *            event) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueReleaseGrallocObjectsIMG(cl_command_queue      command_queue,
+                                  cl_uint               num_objects,
+                                  const cl_mem *        mem_objects,
+                                  cl_uint               num_events_in_wait_list,
+                                  const cl_event *      event_wait_list,
+                                  cl_event *            event) CL_API_SUFFIX__VERSION_1_2;
+
+/******************************************
+ * cl_img_generate_mipmap extension *
+ ******************************************/
+#define cl_img_generate_mipmap 1
+
+typedef cl_uint cl_mipmap_filter_mode_img;
+
+/* To be used by clEnqueueGenerateMipmapIMG */
+#define CL_MIPMAP_FILTER_ANY_IMG 0x0
+#define CL_MIPMAP_FILTER_BOX_IMG 0x1
+
+/* To be used by clGetEventInfo */
+#define CL_COMMAND_GENERATE_MIPMAP_IMG 0x40D6
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueGenerateMipmapIMG(cl_command_queue          command_queue,
+                           cl_mem                    src_image,
+                           cl_mem                    dst_image,
+                           cl_mipmap_filter_mode_img mipmap_filter_mode,
+                           const size_t              *array_region,
+                           const size_t              *mip_region,
+                           cl_uint                   num_events_in_wait_list,
+                           const cl_event            *event_wait_list,
+                           cl_event *event) CL_API_SUFFIX__VERSION_1_2;
+  
+/******************************************
+ * cl_img_mem_properties extension *
+ ******************************************/
+#define cl_img_mem_properties 1
+
+/* To be used by clCreateBufferWithProperties */
+#define CL_MEM_ALLOC_FLAGS_IMG 0x40D7
+
+/* To be used wiith the CL_MEM_ALLOC_FLAGS_IMG property */
+typedef cl_bitfield cl_mem_alloc_flags_img;
+
+/* To be used with cl_mem_alloc_flags_img */
+#define CL_MEM_ALLOC_RELAX_REQUIREMENTS_IMG (1 << 0)
+
+/*********************************
+* cl_khr_subgroups extension
+*********************************/
+#define cl_khr_subgroups 1
+
+#if !defined(CL_VERSION_2_1)
+/* For OpenCL 2.1 and newer, cl_kernel_sub_group_info is declared in CL.h.
+   In hindsight, there should have been a khr suffix on this type for
+   the extension, but keeping it un-suffixed to maintain backwards
+   compatibility. */
+typedef cl_uint             cl_kernel_sub_group_info;
+#endif
+
+/* cl_kernel_sub_group_info */
+#define CL_KERNEL_MAX_SUB_GROUP_SIZE_FOR_NDRANGE_KHR    0x2033
+#define CL_KERNEL_SUB_GROUP_COUNT_FOR_NDRANGE_KHR       0x2034
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetKernelSubGroupInfoKHR(cl_kernel    in_kernel,
+                           cl_device_id in_device,
+                           cl_kernel_sub_group_info param_name,
+                           size_t       input_value_size,
+                           const void * input_value,
+                           size_t       param_value_size,
+                           void *       param_value,
+                           size_t *     param_value_size_ret) CL_API_SUFFIX__VERSION_2_0_DEPRECATED;
+
+typedef cl_int
+(CL_API_CALL * clGetKernelSubGroupInfoKHR_fn)(cl_kernel    in_kernel,
+                                              cl_device_id in_device,
+                                              cl_kernel_sub_group_info param_name,
+                                              size_t       input_value_size,
+                                              const void * input_value,
+                                              size_t       param_value_size,
+                                              void *       param_value,
+                                              size_t *     param_value_size_ret) CL_API_SUFFIX__VERSION_2_0_DEPRECATED;
+
+
+/*********************************
+* cl_khr_mipmap_image extension
+*********************************/
+
+/* cl_sampler_properties */
+#define CL_SAMPLER_MIP_FILTER_MODE_KHR              0x1155
+#define CL_SAMPLER_LOD_MIN_KHR                      0x1156
+#define CL_SAMPLER_LOD_MAX_KHR                      0x1157
+
+
+/*********************************
+* cl_khr_priority_hints extension
+*********************************/
+/* This extension define is for backwards compatibility.
+   It shouldn't be required since this extension has no new functions. */
+#define cl_khr_priority_hints 1
+
+typedef cl_uint  cl_queue_priority_khr;
+
+/* cl_command_queue_properties */
+#define CL_QUEUE_PRIORITY_KHR 0x1096
+
+/* cl_queue_priority_khr */
+#define CL_QUEUE_PRIORITY_HIGH_KHR (1<<0)
+#define CL_QUEUE_PRIORITY_MED_KHR (1<<1)
+#define CL_QUEUE_PRIORITY_LOW_KHR (1<<2)
+
+
+/*********************************
+* cl_khr_throttle_hints extension
+*********************************/
+/* This extension define is for backwards compatibility.
+   It shouldn't be required since this extension has no new functions. */
+#define cl_khr_throttle_hints 1
+
+typedef cl_uint  cl_queue_throttle_khr;
+
+/* cl_command_queue_properties */
+#define CL_QUEUE_THROTTLE_KHR 0x1097
+
+/* cl_queue_throttle_khr */
+#define CL_QUEUE_THROTTLE_HIGH_KHR (1<<0)
+#define CL_QUEUE_THROTTLE_MED_KHR (1<<1)
+#define CL_QUEUE_THROTTLE_LOW_KHR (1<<2)
+
+
+/*********************************
+* cl_khr_subgroup_named_barrier
+*********************************/
+/* This extension define is for backwards compatibility.
+   It shouldn't be required since this extension has no new functions. */
+#define cl_khr_subgroup_named_barrier 1
+
+/* cl_device_info */
+#define CL_DEVICE_MAX_NAMED_BARRIER_COUNT_KHR       0x2035
+
+
+/*********************************
+* cl_khr_extended_versioning
+*********************************/
+
+#define cl_khr_extended_versioning 1
+
+#define CL_VERSION_MAJOR_BITS_KHR (10)
+#define CL_VERSION_MINOR_BITS_KHR (10)
+#define CL_VERSION_PATCH_BITS_KHR (12)
+
+#define CL_VERSION_MAJOR_MASK_KHR ((1 << CL_VERSION_MAJOR_BITS_KHR) - 1)
+#define CL_VERSION_MINOR_MASK_KHR ((1 << CL_VERSION_MINOR_BITS_KHR) - 1)
+#define CL_VERSION_PATCH_MASK_KHR ((1 << CL_VERSION_PATCH_BITS_KHR) - 1)
+
+#define CL_VERSION_MAJOR_KHR(version) ((version) >> (CL_VERSION_MINOR_BITS_KHR + CL_VERSION_PATCH_BITS_KHR))
+#define CL_VERSION_MINOR_KHR(version) (((version) >> CL_VERSION_PATCH_BITS_KHR) & CL_VERSION_MINOR_MASK_KHR)
+#define CL_VERSION_PATCH_KHR(version) ((version) & CL_VERSION_PATCH_MASK_KHR)
+
+#define CL_MAKE_VERSION_KHR(major, minor, patch) \
+    ((((major) & CL_VERSION_MAJOR_MASK_KHR) << (CL_VERSION_MINOR_BITS_KHR + CL_VERSION_PATCH_BITS_KHR)) | \
+    (((minor) &  CL_VERSION_MINOR_MASK_KHR) << CL_VERSION_PATCH_BITS_KHR) | \
+    ((patch) & CL_VERSION_PATCH_MASK_KHR))
+
+typedef cl_uint cl_version_khr;
+
+#define CL_NAME_VERSION_MAX_NAME_SIZE_KHR 64
+
+typedef struct _cl_name_version_khr
+{
+    cl_version_khr version;
+    char name[CL_NAME_VERSION_MAX_NAME_SIZE_KHR];
+} cl_name_version_khr;
+
+/* cl_platform_info */
+#define CL_PLATFORM_NUMERIC_VERSION_KHR                  0x0906
+#define CL_PLATFORM_EXTENSIONS_WITH_VERSION_KHR          0x0907
+
+/* cl_device_info */
+#define CL_DEVICE_NUMERIC_VERSION_KHR                    0x105E
+#define CL_DEVICE_OPENCL_C_NUMERIC_VERSION_KHR           0x105F
+#define CL_DEVICE_EXTENSIONS_WITH_VERSION_KHR            0x1060
+#define CL_DEVICE_ILS_WITH_VERSION_KHR                   0x1061
+#define CL_DEVICE_BUILT_IN_KERNELS_WITH_VERSION_KHR      0x1062
+
+
+/*********************************
+* cl_khr_device_uuid extension
+*********************************/
+#define cl_khr_device_uuid 1
+
+#define CL_UUID_SIZE_KHR 16
+#define CL_LUID_SIZE_KHR 8
+
+#define CL_DEVICE_UUID_KHR          0x106A
+#define CL_DRIVER_UUID_KHR          0x106B
+#define CL_DEVICE_LUID_VALID_KHR    0x106C
+#define CL_DEVICE_LUID_KHR          0x106D
+#define CL_DEVICE_NODE_MASK_KHR     0x106E
+
+
+/***************************************************************
+* cl_khr_pci_bus_info
+***************************************************************/
+#define cl_khr_pci_bus_info 1
+
+typedef struct _cl_device_pci_bus_info_khr {
+    cl_uint pci_domain;
+    cl_uint pci_bus;
+    cl_uint pci_device;
+    cl_uint pci_function;
+} cl_device_pci_bus_info_khr;
+
+/* cl_device_info */
+#define CL_DEVICE_PCI_BUS_INFO_KHR                          0x410F
+
+
+/***************************************************************
+* cl_khr_suggested_local_work_size
+***************************************************************/
+#define cl_khr_suggested_local_work_size 1
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetKernelSuggestedLocalWorkSizeKHR(
+    cl_command_queue command_queue,
+    cl_kernel kernel,
+    cl_uint work_dim,
+    const size_t* global_work_offset,
+    const size_t* global_work_size,
+    size_t* suggested_local_work_size) CL_API_SUFFIX__VERSION_3_0;
+
+typedef cl_int (CL_API_CALL *
+clGetKernelSuggestedLocalWorkSizeKHR_fn)(
+    cl_command_queue command_queue,
+    cl_kernel kernel,
+    cl_uint work_dim,
+    const size_t* global_work_offset,
+    const size_t* global_work_size,
+    size_t* suggested_local_work_size) CL_API_SUFFIX__VERSION_3_0;
+
+
+/***************************************************************
+* cl_khr_integer_dot_product
+***************************************************************/
+#define cl_khr_integer_dot_product 1
+
+typedef cl_bitfield         cl_device_integer_dot_product_capabilities_khr;
+
+/* cl_device_integer_dot_product_capabilities_khr */
+#define CL_DEVICE_INTEGER_DOT_PRODUCT_INPUT_4x8BIT_PACKED_KHR (1 << 0)
+#define CL_DEVICE_INTEGER_DOT_PRODUCT_INPUT_4x8BIT_KHR      (1 << 1)
+
+/* cl_device_info */
+#define CL_DEVICE_INTEGER_DOT_PRODUCT_CAPABILITIES_KHR      0x1073
+
+
+/**********************************
+ * cl_arm_import_memory extension *
+ **********************************/
+#define cl_arm_import_memory 1
+
+typedef intptr_t cl_import_properties_arm;
+
+/* Default and valid proporties name for cl_arm_import_memory */
+#define CL_IMPORT_TYPE_ARM                        0x40B2
+
+/* Host process memory type default value for CL_IMPORT_TYPE_ARM property */
+#define CL_IMPORT_TYPE_HOST_ARM                   0x40B3
+
+/* DMA BUF memory type value for CL_IMPORT_TYPE_ARM property */
+#define CL_IMPORT_TYPE_DMA_BUF_ARM                0x40B4
+
+/* Protected memory property */
+#define CL_IMPORT_TYPE_PROTECTED_ARM              0x40B5
+
+/* Android hardware buffer type value for CL_IMPORT_TYPE_ARM property */
+#define CL_IMPORT_TYPE_ANDROID_HARDWARE_BUFFER_ARM 0x41E2
+
+/* Data consistency with host property */
+#define CL_IMPORT_DMA_BUF_DATA_CONSISTENCY_WITH_HOST_ARM 0x41E3
+
+/* Index of plane in a multiplanar hardware buffer */
+#define CL_IMPORT_ANDROID_HARDWARE_BUFFER_PLANE_INDEX_ARM 0x41EF
+
+/* Index of layer in a multilayer hardware buffer */
+#define CL_IMPORT_ANDROID_HARDWARE_BUFFER_LAYER_INDEX_ARM 0x41F0
+
+/* Import memory size value to indicate a size for the whole buffer */
+#define CL_IMPORT_MEMORY_WHOLE_ALLOCATION_ARM SIZE_MAX
+
+/* This extension adds a new function that allows for direct memory import into
+ * OpenCL via the clImportMemoryARM function.
+ *
+ * Memory imported through this interface will be mapped into the device's page
+ * tables directly, providing zero copy access. It will never fall back to copy
+ * operations and aliased buffers.
+ *
+ * Types of memory supported for import are specified as additional extension
+ * strings.
+ *
+ * This extension produces cl_mem allocations which are compatible with all other
+ * users of cl_mem in the standard API.
+ *
+ * This extension maps pages with the same properties as the normal buffer creation
+ * function clCreateBuffer.
+ */
+extern CL_API_ENTRY cl_mem CL_API_CALL
+clImportMemoryARM( cl_context context,
+                   cl_mem_flags flags,
+                   const cl_import_properties_arm *properties,
+                   void *memory,
+                   size_t size,
+                   cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+
+/******************************************
+ * cl_arm_shared_virtual_memory extension *
+ ******************************************/
+#define cl_arm_shared_virtual_memory 1
+
+/* Used by clGetDeviceInfo */
+#define CL_DEVICE_SVM_CAPABILITIES_ARM                  0x40B6
+
+/* Used by clGetMemObjectInfo */
+#define CL_MEM_USES_SVM_POINTER_ARM                     0x40B7
+
+/* Used by clSetKernelExecInfoARM: */
+#define CL_KERNEL_EXEC_INFO_SVM_PTRS_ARM                0x40B8
+#define CL_KERNEL_EXEC_INFO_SVM_FINE_GRAIN_SYSTEM_ARM   0x40B9
+
+/* To be used by clGetEventInfo: */
+#define CL_COMMAND_SVM_FREE_ARM                         0x40BA
+#define CL_COMMAND_SVM_MEMCPY_ARM                       0x40BB
+#define CL_COMMAND_SVM_MEMFILL_ARM                      0x40BC
+#define CL_COMMAND_SVM_MAP_ARM                          0x40BD
+#define CL_COMMAND_SVM_UNMAP_ARM                        0x40BE
+
+/* Flag values returned by clGetDeviceInfo with CL_DEVICE_SVM_CAPABILITIES_ARM as the param_name. */
+#define CL_DEVICE_SVM_COARSE_GRAIN_BUFFER_ARM           (1 << 0)
+#define CL_DEVICE_SVM_FINE_GRAIN_BUFFER_ARM             (1 << 1)
+#define CL_DEVICE_SVM_FINE_GRAIN_SYSTEM_ARM             (1 << 2)
+#define CL_DEVICE_SVM_ATOMICS_ARM                       (1 << 3)
+
+/* Flag values used by clSVMAllocARM: */
+#define CL_MEM_SVM_FINE_GRAIN_BUFFER_ARM                (1 << 10)
+#define CL_MEM_SVM_ATOMICS_ARM                          (1 << 11)
+
+typedef cl_bitfield cl_svm_mem_flags_arm;
+typedef cl_uint     cl_kernel_exec_info_arm;
+typedef cl_bitfield cl_device_svm_capabilities_arm;
+
+extern CL_API_ENTRY void * CL_API_CALL
+clSVMAllocARM(cl_context       context,
+              cl_svm_mem_flags_arm flags,
+              size_t           size,
+              cl_uint          alignment) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY void CL_API_CALL
+clSVMFreeARM(cl_context        context,
+             void *            svm_pointer) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueSVMFreeARM(cl_command_queue  command_queue,
+                    cl_uint           num_svm_pointers,
+                    void *            svm_pointers[],
+                    void (CL_CALLBACK * pfn_free_func)(cl_command_queue queue,
+                                                       cl_uint          num_svm_pointers,
+                                                       void *           svm_pointers[],
+                                                       void *           user_data),
+                    void *            user_data,
+                    cl_uint           num_events_in_wait_list,
+                    const cl_event *  event_wait_list,
+                    cl_event *        event) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueSVMMemcpyARM(cl_command_queue  command_queue,
+                      cl_bool           blocking_copy,
+                      void *            dst_ptr,
+                      const void *      src_ptr,
+                      size_t            size,
+                      cl_uint           num_events_in_wait_list,
+                      const cl_event *  event_wait_list,
+                      cl_event *        event) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueSVMMemFillARM(cl_command_queue  command_queue,
+                       void *            svm_ptr,
+                       const void *      pattern,
+                       size_t            pattern_size,
+                       size_t            size,
+                       cl_uint           num_events_in_wait_list,
+                       const cl_event *  event_wait_list,
+                       cl_event *        event) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueSVMMapARM(cl_command_queue  command_queue,
+                   cl_bool           blocking_map,
+                   cl_map_flags      flags,
+                   void *            svm_ptr,
+                   size_t            size,
+                   cl_uint           num_events_in_wait_list,
+                   const cl_event *  event_wait_list,
+                   cl_event *        event) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueSVMUnmapARM(cl_command_queue  command_queue,
+                     void *            svm_ptr,
+                     cl_uint           num_events_in_wait_list,
+                     const cl_event *  event_wait_list,
+                     cl_event *        event) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clSetKernelArgSVMPointerARM(cl_kernel    kernel,
+                            cl_uint      arg_index,
+                            const void * arg_value) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clSetKernelExecInfoARM(cl_kernel            kernel,
+                       cl_kernel_exec_info_arm  param_name,
+                       size_t               param_value_size,
+                       const void *         param_value) CL_API_SUFFIX__VERSION_1_2;
+
+/********************************
+ * cl_arm_get_core_id extension *
+ ********************************/
+
+#ifdef CL_VERSION_1_2
+
+#define cl_arm_get_core_id 1
+
+/* Device info property for bitfield of cores present */
+#define CL_DEVICE_COMPUTE_UNITS_BITFIELD_ARM      0x40BF
+
+#endif  /* CL_VERSION_1_2 */
+
+/*********************************
+* cl_arm_job_slot_selection
+*********************************/
+
+#define cl_arm_job_slot_selection 1
+
+/* cl_device_info */
+#define CL_DEVICE_JOB_SLOTS_ARM                   0x41E0
+
+/* cl_command_queue_properties */
+#define CL_QUEUE_JOB_SLOT_ARM                     0x41E1
+
+/*********************************
+* cl_arm_scheduling_controls
+*********************************/
+
+#define cl_arm_scheduling_controls 1
+
+typedef cl_bitfield cl_device_scheduling_controls_capabilities_arm;
+
+/* cl_device_info */
+#define CL_DEVICE_SCHEDULING_CONTROLS_CAPABILITIES_ARM          0x41E4
+
+#define CL_DEVICE_SCHEDULING_KERNEL_BATCHING_ARM               (1 << 0)
+#define CL_DEVICE_SCHEDULING_WORKGROUP_BATCH_SIZE_ARM          (1 << 1)
+#define CL_DEVICE_SCHEDULING_WORKGROUP_BATCH_SIZE_MODIFIER_ARM (1 << 2)
+#define CL_DEVICE_SCHEDULING_DEFERRED_FLUSH_ARM                (1 << 3)
+#define CL_DEVICE_SCHEDULING_REGISTER_ALLOCATION_ARM           (1 << 4)
+
+#define CL_DEVICE_SUPPORTED_REGISTER_ALLOCATIONS_ARM            0x41EB
+
+/* cl_kernel_info */
+#define CL_KERNEL_EXEC_INFO_WORKGROUP_BATCH_SIZE_ARM            0x41E5
+#define CL_KERNEL_EXEC_INFO_WORKGROUP_BATCH_SIZE_MODIFIER_ARM   0x41E6
+
+/* cl_queue_properties */
+#define CL_QUEUE_KERNEL_BATCHING_ARM                            0x41E7
+#define CL_QUEUE_DEFERRED_FLUSH_ARM                             0x41EC
+
+/**************************************
+* cl_arm_controlled_kernel_termination
+***************************************/
+
+#define cl_arm_controlled_kernel_termination 1
+
+/* Error code to indicate kernel terminated with failure */
+#define CL_COMMAND_TERMINATED_ITSELF_WITH_FAILURE_ARM -1108
+
+/* cl_device_info */
+#define CL_DEVICE_CONTROLLED_TERMINATION_CAPABILITIES_ARM 0x41EE
+
+/* Bit fields for controlled termination feature query */
+typedef cl_bitfield cl_device_controlled_termination_capabilities_arm;
+
+#define CL_DEVICE_CONTROLLED_TERMINATION_SUCCESS_ARM (1 << 0)
+#define CL_DEVICE_CONTROLLED_TERMINATION_FAILURE_ARM (1 << 1)
+#define CL_DEVICE_CONTROLLED_TERMINATION_QUERY_ARM (1 << 2)
+
+/* cl_event_info */
+#define CL_EVENT_COMMAND_TERMINATION_REASON_ARM 0x41ED
+
+/* Values returned for event termination reason query */
+typedef cl_uint cl_command_termination_reason_arm;
+
+#define CL_COMMAND_TERMINATION_COMPLETION_ARM  0
+#define CL_COMMAND_TERMINATION_CONTROLLED_SUCCESS_ARM 1
+#define CL_COMMAND_TERMINATION_CONTROLLED_FAILURE_ARM 2
+#define CL_COMMAND_TERMINATION_ERROR_ARM 3
+
+/***************************************
+* cl_intel_thread_local_exec extension *
+****************************************/
+
+#define cl_intel_thread_local_exec 1
+
+#define CL_QUEUE_THREAD_LOCAL_EXEC_ENABLE_INTEL      (((cl_bitfield)1) << 31)
+
+/***********************************************
+* cl_intel_device_partition_by_names extension *
+************************************************/
+
+#define cl_intel_device_partition_by_names 1
+
+#define CL_DEVICE_PARTITION_BY_NAMES_INTEL          0x4052
+#define CL_PARTITION_BY_NAMES_LIST_END_INTEL        -1
+
+/************************************************
+* cl_intel_accelerator extension                *
+* cl_intel_motion_estimation extension          *
+* cl_intel_advanced_motion_estimation extension *
+*************************************************/
+
+#define cl_intel_accelerator 1
+#define cl_intel_motion_estimation 1
+#define cl_intel_advanced_motion_estimation 1
+
+typedef struct _cl_accelerator_intel* cl_accelerator_intel;
+typedef cl_uint cl_accelerator_type_intel;
+typedef cl_uint cl_accelerator_info_intel;
+
+typedef struct _cl_motion_estimation_desc_intel {
+    cl_uint mb_block_type;
+    cl_uint subpixel_mode;
+    cl_uint sad_adjust_mode;
+    cl_uint search_path_type;
+} cl_motion_estimation_desc_intel;
+
+/* error codes */
+#define CL_INVALID_ACCELERATOR_INTEL                              -1094
+#define CL_INVALID_ACCELERATOR_TYPE_INTEL                         -1095
+#define CL_INVALID_ACCELERATOR_DESCRIPTOR_INTEL                   -1096
+#define CL_ACCELERATOR_TYPE_NOT_SUPPORTED_INTEL                   -1097
+
+/* cl_accelerator_type_intel */
+#define CL_ACCELERATOR_TYPE_MOTION_ESTIMATION_INTEL               0x0
+
+/* cl_accelerator_info_intel */
+#define CL_ACCELERATOR_DESCRIPTOR_INTEL                           0x4090
+#define CL_ACCELERATOR_REFERENCE_COUNT_INTEL                      0x4091
+#define CL_ACCELERATOR_CONTEXT_INTEL                              0x4092
+#define CL_ACCELERATOR_TYPE_INTEL                                 0x4093
+
+/* cl_motion_detect_desc_intel flags */
+#define CL_ME_MB_TYPE_16x16_INTEL                                 0x0
+#define CL_ME_MB_TYPE_8x8_INTEL                                   0x1
+#define CL_ME_MB_TYPE_4x4_INTEL                                   0x2
+
+#define CL_ME_SUBPIXEL_MODE_INTEGER_INTEL                         0x0
+#define CL_ME_SUBPIXEL_MODE_HPEL_INTEL                            0x1
+#define CL_ME_SUBPIXEL_MODE_QPEL_INTEL                            0x2
+
+#define CL_ME_SAD_ADJUST_MODE_NONE_INTEL                          0x0
+#define CL_ME_SAD_ADJUST_MODE_HAAR_INTEL                          0x1
+
+#define CL_ME_SEARCH_PATH_RADIUS_2_2_INTEL                        0x0
+#define CL_ME_SEARCH_PATH_RADIUS_4_4_INTEL                        0x1
+#define CL_ME_SEARCH_PATH_RADIUS_16_12_INTEL                      0x5
+
+#define CL_ME_SKIP_BLOCK_TYPE_16x16_INTEL                         0x0
+#define CL_ME_CHROMA_INTRA_PREDICT_ENABLED_INTEL                  0x1
+#define CL_ME_LUMA_INTRA_PREDICT_ENABLED_INTEL                    0x2
+#define CL_ME_SKIP_BLOCK_TYPE_8x8_INTEL                           0x4
+
+#define CL_ME_FORWARD_INPUT_MODE_INTEL                            0x1
+#define CL_ME_BACKWARD_INPUT_MODE_INTEL                           0x2
+#define CL_ME_BIDIRECTION_INPUT_MODE_INTEL                        0x3
+
+#define CL_ME_BIDIR_WEIGHT_QUARTER_INTEL                          16
+#define CL_ME_BIDIR_WEIGHT_THIRD_INTEL                            21
+#define CL_ME_BIDIR_WEIGHT_HALF_INTEL                             32
+#define CL_ME_BIDIR_WEIGHT_TWO_THIRD_INTEL                        43
+#define CL_ME_BIDIR_WEIGHT_THREE_QUARTER_INTEL                    48
+
+#define CL_ME_COST_PENALTY_NONE_INTEL                             0x0
+#define CL_ME_COST_PENALTY_LOW_INTEL                              0x1
+#define CL_ME_COST_PENALTY_NORMAL_INTEL                           0x2
+#define CL_ME_COST_PENALTY_HIGH_INTEL                             0x3
+
+#define CL_ME_COST_PRECISION_QPEL_INTEL                           0x0
+#define CL_ME_COST_PRECISION_HPEL_INTEL                           0x1
+#define CL_ME_COST_PRECISION_PEL_INTEL                            0x2
+#define CL_ME_COST_PRECISION_DPEL_INTEL                           0x3
+
+#define CL_ME_LUMA_PREDICTOR_MODE_VERTICAL_INTEL                  0x0
+#define CL_ME_LUMA_PREDICTOR_MODE_HORIZONTAL_INTEL                0x1
+#define CL_ME_LUMA_PREDICTOR_MODE_DC_INTEL                        0x2
+#define CL_ME_LUMA_PREDICTOR_MODE_DIAGONAL_DOWN_LEFT_INTEL        0x3
+
+#define CL_ME_LUMA_PREDICTOR_MODE_DIAGONAL_DOWN_RIGHT_INTEL       0x4
+#define CL_ME_LUMA_PREDICTOR_MODE_PLANE_INTEL                     0x4
+#define CL_ME_LUMA_PREDICTOR_MODE_VERTICAL_RIGHT_INTEL            0x5
+#define CL_ME_LUMA_PREDICTOR_MODE_HORIZONTAL_DOWN_INTEL           0x6
+#define CL_ME_LUMA_PREDICTOR_MODE_VERTICAL_LEFT_INTEL             0x7
+#define CL_ME_LUMA_PREDICTOR_MODE_HORIZONTAL_UP_INTEL             0x8
+
+#define CL_ME_CHROMA_PREDICTOR_MODE_DC_INTEL                      0x0
+#define CL_ME_CHROMA_PREDICTOR_MODE_HORIZONTAL_INTEL              0x1
+#define CL_ME_CHROMA_PREDICTOR_MODE_VERTICAL_INTEL                0x2
+#define CL_ME_CHROMA_PREDICTOR_MODE_PLANE_INTEL                   0x3
+
+/* cl_device_info */
+#define CL_DEVICE_ME_VERSION_INTEL                                0x407E
+
+#define CL_ME_VERSION_LEGACY_INTEL                                0x0
+#define CL_ME_VERSION_ADVANCED_VER_1_INTEL                        0x1
+#define CL_ME_VERSION_ADVANCED_VER_2_INTEL                        0x2
+
+extern CL_API_ENTRY cl_accelerator_intel CL_API_CALL
+clCreateAcceleratorINTEL(
+    cl_context                   context,
+    cl_accelerator_type_intel    accelerator_type,
+    size_t                       descriptor_size,
+    const void*                  descriptor,
+    cl_int*                      errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_accelerator_intel (CL_API_CALL *clCreateAcceleratorINTEL_fn)(
+    cl_context                   context,
+    cl_accelerator_type_intel    accelerator_type,
+    size_t                       descriptor_size,
+    const void*                  descriptor,
+    cl_int*                      errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetAcceleratorInfoINTEL(
+    cl_accelerator_intel         accelerator,
+    cl_accelerator_info_intel    param_name,
+    size_t                       param_value_size,
+    void*                        param_value,
+    size_t*                      param_value_size_ret) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_int (CL_API_CALL *clGetAcceleratorInfoINTEL_fn)(
+    cl_accelerator_intel         accelerator,
+    cl_accelerator_info_intel    param_name,
+    size_t                       param_value_size,
+    void*                        param_value,
+    size_t*                      param_value_size_ret) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clRetainAcceleratorINTEL(
+    cl_accelerator_intel         accelerator) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_int (CL_API_CALL *clRetainAcceleratorINTEL_fn)(
+    cl_accelerator_intel         accelerator) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clReleaseAcceleratorINTEL(
+    cl_accelerator_intel         accelerator) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_int (CL_API_CALL *clReleaseAcceleratorINTEL_fn)(
+    cl_accelerator_intel         accelerator) CL_API_SUFFIX__VERSION_1_2;
+
+/******************************************
+* cl_intel_simultaneous_sharing extension *
+*******************************************/
+
+#define cl_intel_simultaneous_sharing 1
+
+#define CL_DEVICE_SIMULTANEOUS_INTEROPS_INTEL            0x4104
+#define CL_DEVICE_NUM_SIMULTANEOUS_INTEROPS_INTEL        0x4105
+
+/***********************************
+* cl_intel_egl_image_yuv extension *
+************************************/
+
+#define cl_intel_egl_image_yuv 1
+
+#define CL_EGL_YUV_PLANE_INTEL                           0x4107
+
+/********************************
+* cl_intel_packed_yuv extension *
+*********************************/
+
+#define cl_intel_packed_yuv 1
+
+#define CL_YUYV_INTEL                                    0x4076
+#define CL_UYVY_INTEL                                    0x4077
+#define CL_YVYU_INTEL                                    0x4078
+#define CL_VYUY_INTEL                                    0x4079
+
+/********************************************
+* cl_intel_required_subgroup_size extension *
+*********************************************/
+
+#define cl_intel_required_subgroup_size 1
+
+#define CL_DEVICE_SUB_GROUP_SIZES_INTEL                  0x4108
+#define CL_KERNEL_SPILL_MEM_SIZE_INTEL                   0x4109
+#define CL_KERNEL_COMPILE_SUB_GROUP_SIZE_INTEL           0x410A
+
+/****************************************
+* cl_intel_driver_diagnostics extension *
+*****************************************/
+
+#define cl_intel_driver_diagnostics 1
+
+typedef cl_uint cl_diagnostics_verbose_level;
+
+#define CL_CONTEXT_SHOW_DIAGNOSTICS_INTEL                0x4106
+
+#define CL_CONTEXT_DIAGNOSTICS_LEVEL_ALL_INTEL           ( 0xff )
+#define CL_CONTEXT_DIAGNOSTICS_LEVEL_GOOD_INTEL          ( 1 )
+#define CL_CONTEXT_DIAGNOSTICS_LEVEL_BAD_INTEL           ( 1 << 1 )
+#define CL_CONTEXT_DIAGNOSTICS_LEVEL_NEUTRAL_INTEL       ( 1 << 2 )
+
+/********************************
+* cl_intel_planar_yuv extension *
+*********************************/
+
+#define CL_NV12_INTEL                                       0x410E
+
+#define CL_MEM_NO_ACCESS_INTEL                              ( 1 << 24 )
+#define CL_MEM_ACCESS_FLAGS_UNRESTRICTED_INTEL              ( 1 << 25 )
+
+#define CL_DEVICE_PLANAR_YUV_MAX_WIDTH_INTEL                0x417E
+#define CL_DEVICE_PLANAR_YUV_MAX_HEIGHT_INTEL               0x417F
+
+/*******************************************************
+* cl_intel_device_side_avc_motion_estimation extension *
+********************************************************/
+
+#define CL_DEVICE_AVC_ME_VERSION_INTEL                      0x410B
+#define CL_DEVICE_AVC_ME_SUPPORTS_TEXTURE_SAMPLER_USE_INTEL 0x410C
+#define CL_DEVICE_AVC_ME_SUPPORTS_PREEMPTION_INTEL          0x410D
+
+#define CL_AVC_ME_VERSION_0_INTEL                           0x0   /* No support. */
+#define CL_AVC_ME_VERSION_1_INTEL                           0x1   /* First supported version. */
+
+#define CL_AVC_ME_MAJOR_16x16_INTEL                         0x0
+#define CL_AVC_ME_MAJOR_16x8_INTEL                          0x1
+#define CL_AVC_ME_MAJOR_8x16_INTEL                          0x2
+#define CL_AVC_ME_MAJOR_8x8_INTEL                           0x3
+
+#define CL_AVC_ME_MINOR_8x8_INTEL                           0x0
+#define CL_AVC_ME_MINOR_8x4_INTEL                           0x1
+#define CL_AVC_ME_MINOR_4x8_INTEL                           0x2
+#define CL_AVC_ME_MINOR_4x4_INTEL                           0x3
+
+#define CL_AVC_ME_MAJOR_FORWARD_INTEL                       0x0
+#define CL_AVC_ME_MAJOR_BACKWARD_INTEL                      0x1
+#define CL_AVC_ME_MAJOR_BIDIRECTIONAL_INTEL                 0x2
+
+#define CL_AVC_ME_PARTITION_MASK_ALL_INTEL                  0x0
+#define CL_AVC_ME_PARTITION_MASK_16x16_INTEL                0x7E
+#define CL_AVC_ME_PARTITION_MASK_16x8_INTEL                 0x7D
+#define CL_AVC_ME_PARTITION_MASK_8x16_INTEL                 0x7B
+#define CL_AVC_ME_PARTITION_MASK_8x8_INTEL                  0x77
+#define CL_AVC_ME_PARTITION_MASK_8x4_INTEL                  0x6F
+#define CL_AVC_ME_PARTITION_MASK_4x8_INTEL                  0x5F
+#define CL_AVC_ME_PARTITION_MASK_4x4_INTEL                  0x3F
+
+#define CL_AVC_ME_SEARCH_WINDOW_EXHAUSTIVE_INTEL            0x0
+#define CL_AVC_ME_SEARCH_WINDOW_SMALL_INTEL                 0x1
+#define CL_AVC_ME_SEARCH_WINDOW_TINY_INTEL                  0x2
+#define CL_AVC_ME_SEARCH_WINDOW_EXTRA_TINY_INTEL            0x3
+#define CL_AVC_ME_SEARCH_WINDOW_DIAMOND_INTEL               0x4
+#define CL_AVC_ME_SEARCH_WINDOW_LARGE_DIAMOND_INTEL         0x5
+#define CL_AVC_ME_SEARCH_WINDOW_RESERVED0_INTEL             0x6
+#define CL_AVC_ME_SEARCH_WINDOW_RESERVED1_INTEL             0x7
+#define CL_AVC_ME_SEARCH_WINDOW_CUSTOM_INTEL                0x8
+#define CL_AVC_ME_SEARCH_WINDOW_16x12_RADIUS_INTEL          0x9
+#define CL_AVC_ME_SEARCH_WINDOW_4x4_RADIUS_INTEL            0x2
+#define CL_AVC_ME_SEARCH_WINDOW_2x2_RADIUS_INTEL            0xa
+
+#define CL_AVC_ME_SAD_ADJUST_MODE_NONE_INTEL                0x0
+#define CL_AVC_ME_SAD_ADJUST_MODE_HAAR_INTEL                0x2
+
+#define CL_AVC_ME_SUBPIXEL_MODE_INTEGER_INTEL               0x0
+#define CL_AVC_ME_SUBPIXEL_MODE_HPEL_INTEL                  0x1
+#define CL_AVC_ME_SUBPIXEL_MODE_QPEL_INTEL                  0x3
+
+#define CL_AVC_ME_COST_PRECISION_QPEL_INTEL                 0x0
+#define CL_AVC_ME_COST_PRECISION_HPEL_INTEL                 0x1
+#define CL_AVC_ME_COST_PRECISION_PEL_INTEL                  0x2
+#define CL_AVC_ME_COST_PRECISION_DPEL_INTEL                 0x3
+
+#define CL_AVC_ME_BIDIR_WEIGHT_QUARTER_INTEL                0x10
+#define CL_AVC_ME_BIDIR_WEIGHT_THIRD_INTEL                  0x15
+#define CL_AVC_ME_BIDIR_WEIGHT_HALF_INTEL                   0x20
+#define CL_AVC_ME_BIDIR_WEIGHT_TWO_THIRD_INTEL              0x2B
+#define CL_AVC_ME_BIDIR_WEIGHT_THREE_QUARTER_INTEL          0x30
+
+#define CL_AVC_ME_BORDER_REACHED_LEFT_INTEL                 0x0
+#define CL_AVC_ME_BORDER_REACHED_RIGHT_INTEL                0x2
+#define CL_AVC_ME_BORDER_REACHED_TOP_INTEL                  0x4
+#define CL_AVC_ME_BORDER_REACHED_BOTTOM_INTEL               0x8
+
+#define CL_AVC_ME_SKIP_BLOCK_PARTITION_16x16_INTEL          0x0
+#define CL_AVC_ME_SKIP_BLOCK_PARTITION_8x8_INTEL            0x4000
+
+#define CL_AVC_ME_SKIP_BLOCK_16x16_FORWARD_ENABLE_INTEL     ( 0x1 << 24 )
+#define CL_AVC_ME_SKIP_BLOCK_16x16_BACKWARD_ENABLE_INTEL    ( 0x2 << 24 )
+#define CL_AVC_ME_SKIP_BLOCK_16x16_DUAL_ENABLE_INTEL        ( 0x3 << 24 )
+#define CL_AVC_ME_SKIP_BLOCK_8x8_FORWARD_ENABLE_INTEL       ( 0x55 << 24 )
+#define CL_AVC_ME_SKIP_BLOCK_8x8_BACKWARD_ENABLE_INTEL      ( 0xAA << 24 )
+#define CL_AVC_ME_SKIP_BLOCK_8x8_DUAL_ENABLE_INTEL          ( 0xFF << 24 )
+#define CL_AVC_ME_SKIP_BLOCK_8x8_0_FORWARD_ENABLE_INTEL     ( 0x1 << 24 )
+#define CL_AVC_ME_SKIP_BLOCK_8x8_0_BACKWARD_ENABLE_INTEL    ( 0x2 << 24 )
+#define CL_AVC_ME_SKIP_BLOCK_8x8_1_FORWARD_ENABLE_INTEL     ( 0x1 << 26 )
+#define CL_AVC_ME_SKIP_BLOCK_8x8_1_BACKWARD_ENABLE_INTEL    ( 0x2 << 26 )
+#define CL_AVC_ME_SKIP_BLOCK_8x8_2_FORWARD_ENABLE_INTEL     ( 0x1 << 28 )
+#define CL_AVC_ME_SKIP_BLOCK_8x8_2_BACKWARD_ENABLE_INTEL    ( 0x2 << 28 )
+#define CL_AVC_ME_SKIP_BLOCK_8x8_3_FORWARD_ENABLE_INTEL     ( 0x1 << 30 )
+#define CL_AVC_ME_SKIP_BLOCK_8x8_3_BACKWARD_ENABLE_INTEL    ( 0x2 << 30 )
+
+#define CL_AVC_ME_BLOCK_BASED_SKIP_4x4_INTEL                0x00
+#define CL_AVC_ME_BLOCK_BASED_SKIP_8x8_INTEL                0x80
+
+#define CL_AVC_ME_INTRA_16x16_INTEL                         0x0
+#define CL_AVC_ME_INTRA_8x8_INTEL                           0x1
+#define CL_AVC_ME_INTRA_4x4_INTEL                           0x2
+
+#define CL_AVC_ME_INTRA_LUMA_PARTITION_MASK_16x16_INTEL     0x6
+#define CL_AVC_ME_INTRA_LUMA_PARTITION_MASK_8x8_INTEL       0x5
+#define CL_AVC_ME_INTRA_LUMA_PARTITION_MASK_4x4_INTEL       0x3
+
+#define CL_AVC_ME_INTRA_NEIGHBOR_LEFT_MASK_ENABLE_INTEL         0x60
+#define CL_AVC_ME_INTRA_NEIGHBOR_UPPER_MASK_ENABLE_INTEL        0x10
+#define CL_AVC_ME_INTRA_NEIGHBOR_UPPER_RIGHT_MASK_ENABLE_INTEL  0x8
+#define CL_AVC_ME_INTRA_NEIGHBOR_UPPER_LEFT_MASK_ENABLE_INTEL   0x4
+
+#define CL_AVC_ME_LUMA_PREDICTOR_MODE_VERTICAL_INTEL            0x0
+#define CL_AVC_ME_LUMA_PREDICTOR_MODE_HORIZONTAL_INTEL          0x1
+#define CL_AVC_ME_LUMA_PREDICTOR_MODE_DC_INTEL                  0x2
+#define CL_AVC_ME_LUMA_PREDICTOR_MODE_DIAGONAL_DOWN_LEFT_INTEL  0x3
+#define CL_AVC_ME_LUMA_PREDICTOR_MODE_DIAGONAL_DOWN_RIGHT_INTEL 0x4
+#define CL_AVC_ME_LUMA_PREDICTOR_MODE_PLANE_INTEL               0x4
+#define CL_AVC_ME_LUMA_PREDICTOR_MODE_VERTICAL_RIGHT_INTEL      0x5
+#define CL_AVC_ME_LUMA_PREDICTOR_MODE_HORIZONTAL_DOWN_INTEL     0x6
+#define CL_AVC_ME_LUMA_PREDICTOR_MODE_VERTICAL_LEFT_INTEL       0x7
+#define CL_AVC_ME_LUMA_PREDICTOR_MODE_HORIZONTAL_UP_INTEL       0x8
+#define CL_AVC_ME_CHROMA_PREDICTOR_MODE_DC_INTEL                0x0
+#define CL_AVC_ME_CHROMA_PREDICTOR_MODE_HORIZONTAL_INTEL        0x1
+#define CL_AVC_ME_CHROMA_PREDICTOR_MODE_VERTICAL_INTEL          0x2
+#define CL_AVC_ME_CHROMA_PREDICTOR_MODE_PLANE_INTEL             0x3
+
+#define CL_AVC_ME_FRAME_FORWARD_INTEL                       0x1
+#define CL_AVC_ME_FRAME_BACKWARD_INTEL                      0x2
+#define CL_AVC_ME_FRAME_DUAL_INTEL                          0x3
+
+#define CL_AVC_ME_SLICE_TYPE_PRED_INTEL                     0x0
+#define CL_AVC_ME_SLICE_TYPE_BPRED_INTEL                    0x1
+#define CL_AVC_ME_SLICE_TYPE_INTRA_INTEL                    0x2
+
+#define CL_AVC_ME_INTERLACED_SCAN_TOP_FIELD_INTEL           0x0
+#define CL_AVC_ME_INTERLACED_SCAN_BOTTOM_FIELD_INTEL        0x1
+
+/*******************************************
+* cl_intel_unified_shared_memory extension *
+********************************************/
+
+/* These APIs are in sync with Revision Q of the cl_intel_unified_shared_memory spec! */
+
+#define cl_intel_unified_shared_memory 1
+
+/* cl_device_info */
+#define CL_DEVICE_HOST_MEM_CAPABILITIES_INTEL                   0x4190
+#define CL_DEVICE_DEVICE_MEM_CAPABILITIES_INTEL                 0x4191
+#define CL_DEVICE_SINGLE_DEVICE_SHARED_MEM_CAPABILITIES_INTEL   0x4192
+#define CL_DEVICE_CROSS_DEVICE_SHARED_MEM_CAPABILITIES_INTEL    0x4193
+#define CL_DEVICE_SHARED_SYSTEM_MEM_CAPABILITIES_INTEL          0x4194
+
+typedef cl_bitfield cl_device_unified_shared_memory_capabilities_intel;
+
+/* cl_device_unified_shared_memory_capabilities_intel - bitfield */
+#define CL_UNIFIED_SHARED_MEMORY_ACCESS_INTEL                   (1 << 0)
+#define CL_UNIFIED_SHARED_MEMORY_ATOMIC_ACCESS_INTEL            (1 << 1)
+#define CL_UNIFIED_SHARED_MEMORY_CONCURRENT_ACCESS_INTEL        (1 << 2)
+#define CL_UNIFIED_SHARED_MEMORY_CONCURRENT_ATOMIC_ACCESS_INTEL (1 << 3)
+
+typedef cl_properties cl_mem_properties_intel;
+
+/* cl_mem_properties_intel */
+#define CL_MEM_ALLOC_FLAGS_INTEL        0x4195
+
+typedef cl_bitfield cl_mem_alloc_flags_intel;
+
+/* cl_mem_alloc_flags_intel - bitfield */
+#define CL_MEM_ALLOC_WRITE_COMBINED_INTEL               (1 << 0)
+
+typedef cl_uint cl_mem_info_intel;
+
+/* cl_mem_alloc_info_intel */
+#define CL_MEM_ALLOC_TYPE_INTEL         0x419A
+#define CL_MEM_ALLOC_BASE_PTR_INTEL     0x419B
+#define CL_MEM_ALLOC_SIZE_INTEL         0x419C
+#define CL_MEM_ALLOC_DEVICE_INTEL       0x419D
+/* Enum values 0x419E-0x419F are reserved for future queries. */
+
+typedef cl_uint cl_unified_shared_memory_type_intel;
+
+/* cl_unified_shared_memory_type_intel */
+#define CL_MEM_TYPE_UNKNOWN_INTEL       0x4196
+#define CL_MEM_TYPE_HOST_INTEL          0x4197
+#define CL_MEM_TYPE_DEVICE_INTEL        0x4198
+#define CL_MEM_TYPE_SHARED_INTEL        0x4199
+
+typedef cl_uint cl_mem_advice_intel;
+
+/* cl_mem_advice_intel */
+/* Enum values 0x4208-0x420F are reserved for future memory advices. */
+
+/* cl_kernel_exec_info */
+#define CL_KERNEL_EXEC_INFO_INDIRECT_HOST_ACCESS_INTEL      0x4200
+#define CL_KERNEL_EXEC_INFO_INDIRECT_DEVICE_ACCESS_INTEL    0x4201
+#define CL_KERNEL_EXEC_INFO_INDIRECT_SHARED_ACCESS_INTEL    0x4202
+#define CL_KERNEL_EXEC_INFO_USM_PTRS_INTEL                  0x4203
+
+/* cl_command_type */
+#define CL_COMMAND_MEMFILL_INTEL        0x4204
+#define CL_COMMAND_MEMCPY_INTEL         0x4205
+#define CL_COMMAND_MIGRATEMEM_INTEL     0x4206
+#define CL_COMMAND_MEMADVISE_INTEL      0x4207
+
+extern CL_API_ENTRY void* CL_API_CALL
+clHostMemAllocINTEL(
+            cl_context context,
+            const cl_mem_properties_intel* properties,
+            size_t size,
+            cl_uint alignment,
+            cl_int* errcode_ret);
+
+typedef void* (CL_API_CALL *
+clHostMemAllocINTEL_fn)(
+            cl_context context,
+            const cl_mem_properties_intel* properties,
+            size_t size,
+            cl_uint alignment,
+            cl_int* errcode_ret);
+
+extern CL_API_ENTRY void* CL_API_CALL
+clDeviceMemAllocINTEL(
+            cl_context context,
+            cl_device_id device,
+            const cl_mem_properties_intel* properties,
+            size_t size,
+            cl_uint alignment,
+            cl_int* errcode_ret);
+
+typedef void* (CL_API_CALL *
+clDeviceMemAllocINTEL_fn)(
+            cl_context context,
+            cl_device_id device,
+            const cl_mem_properties_intel* properties,
+            size_t size,
+            cl_uint alignment,
+            cl_int* errcode_ret);
+
+extern CL_API_ENTRY void* CL_API_CALL
+clSharedMemAllocINTEL(
+            cl_context context,
+            cl_device_id device,
+            const cl_mem_properties_intel* properties,
+            size_t size,
+            cl_uint alignment,
+            cl_int* errcode_ret);
+
+typedef void* (CL_API_CALL *
+clSharedMemAllocINTEL_fn)(
+            cl_context context,
+            cl_device_id device,
+            const cl_mem_properties_intel* properties,
+            size_t size,
+            cl_uint alignment,
+            cl_int* errcode_ret);
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clMemFreeINTEL(
+            cl_context context,
+            void* ptr);
+
+typedef cl_int (CL_API_CALL *
+clMemFreeINTEL_fn)(
+            cl_context context,
+            void* ptr);
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clMemBlockingFreeINTEL(
+            cl_context context,
+            void* ptr);
+
+typedef cl_int (CL_API_CALL *
+clMemBlockingFreeINTEL_fn)(
+            cl_context context,
+            void* ptr);
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetMemAllocInfoINTEL(
+            cl_context context,
+            const void* ptr,
+            cl_mem_info_intel param_name,
+            size_t param_value_size,
+            void* param_value,
+            size_t* param_value_size_ret);
+
+typedef cl_int (CL_API_CALL *
+clGetMemAllocInfoINTEL_fn)(
+            cl_context context,
+            const void* ptr,
+            cl_mem_info_intel param_name,
+            size_t param_value_size,
+            void* param_value,
+            size_t* param_value_size_ret);
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clSetKernelArgMemPointerINTEL(
+            cl_kernel kernel,
+            cl_uint arg_index,
+            const void* arg_value);
+
+typedef cl_int (CL_API_CALL *
+clSetKernelArgMemPointerINTEL_fn)(
+            cl_kernel kernel,
+            cl_uint arg_index,
+            const void* arg_value);
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueMemsetINTEL(       /* Deprecated */
+            cl_command_queue command_queue,
+            void* dst_ptr,
+            cl_int value,
+            size_t size,
+            cl_uint num_events_in_wait_list,
+            const cl_event* event_wait_list,
+            cl_event* event);
+
+typedef cl_int (CL_API_CALL *
+clEnqueueMemsetINTEL_fn)(   /* Deprecated */
+            cl_command_queue command_queue,
+            void* dst_ptr,
+            cl_int value,
+            size_t size,
+            cl_uint num_events_in_wait_list,
+            const cl_event* event_wait_list,
+            cl_event* event);
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueMemFillINTEL(
+            cl_command_queue command_queue,
+            void* dst_ptr,
+            const void* pattern,
+            size_t pattern_size,
+            size_t size,
+            cl_uint num_events_in_wait_list,
+            const cl_event* event_wait_list,
+            cl_event* event);
+
+typedef cl_int (CL_API_CALL *
+clEnqueueMemFillINTEL_fn)(
+            cl_command_queue command_queue,
+            void* dst_ptr,
+            const void* pattern,
+            size_t pattern_size,
+            size_t size,
+            cl_uint num_events_in_wait_list,
+            const cl_event* event_wait_list,
+            cl_event* event);
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueMemcpyINTEL(
+            cl_command_queue command_queue,
+            cl_bool blocking,
+            void* dst_ptr,
+            const void* src_ptr,
+            size_t size,
+            cl_uint num_events_in_wait_list,
+            const cl_event* event_wait_list,
+            cl_event* event);
+
+typedef cl_int (CL_API_CALL *
+clEnqueueMemcpyINTEL_fn)(
+            cl_command_queue command_queue,
+            cl_bool blocking,
+            void* dst_ptr,
+            const void* src_ptr,
+            size_t size,
+            cl_uint num_events_in_wait_list,
+            const cl_event* event_wait_list,
+            cl_event* event);
+
+#ifdef CL_VERSION_1_2
+
+/* Because these APIs use cl_mem_migration_flags, they require
+   OpenCL 1.2: */
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueMigrateMemINTEL(
+            cl_command_queue command_queue,
+            const void* ptr,
+            size_t size,
+            cl_mem_migration_flags flags,
+            cl_uint num_events_in_wait_list,
+            const cl_event* event_wait_list,
+            cl_event* event);
+
+typedef cl_int (CL_API_CALL *
+clEnqueueMigrateMemINTEL_fn)(
+            cl_command_queue command_queue,
+            const void* ptr,
+            size_t size,
+            cl_mem_migration_flags flags,
+            cl_uint num_events_in_wait_list,
+            const cl_event* event_wait_list,
+            cl_event* event);
+
+#endif
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueMemAdviseINTEL(
+            cl_command_queue command_queue,
+            const void* ptr,
+            size_t size,
+            cl_mem_advice_intel advice,
+            cl_uint num_events_in_wait_list,
+            const cl_event* event_wait_list,
+            cl_event* event);
+
+typedef cl_int (CL_API_CALL *
+clEnqueueMemAdviseINTEL_fn)(
+            cl_command_queue command_queue,
+            const void* ptr,
+            size_t size,
+            cl_mem_advice_intel advice,
+            cl_uint num_events_in_wait_list,
+            const cl_event* event_wait_list,
+            cl_event* event);
+
+/***************************************************
+* cl_intel_create_buffer_with_properties extension *
+****************************************************/
+
+#define cl_intel_create_buffer_with_properties 1
+
+extern CL_API_ENTRY cl_mem CL_API_CALL
+clCreateBufferWithPropertiesINTEL(
+    cl_context   context,
+    const cl_mem_properties_intel* properties,
+    cl_mem_flags flags,
+    size_t       size,
+    void *       host_ptr,
+    cl_int *     errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_mem (CL_API_CALL *
+clCreateBufferWithPropertiesINTEL_fn)(
+    cl_context   context,
+    const cl_mem_properties_intel* properties,
+    cl_mem_flags flags,
+    size_t       size,
+    void *       host_ptr,
+    cl_int *     errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+/******************************************
+* cl_intel_mem_channel_property extension *
+*******************************************/
+
+#define CL_MEM_CHANNEL_INTEL            0x4213
+
+/*********************************
+* cl_intel_mem_force_host_memory *
+**********************************/
+
+#define cl_intel_mem_force_host_memory 1
+
+/* cl_mem_flags */
+#define CL_MEM_FORCE_HOST_MEMORY_INTEL                      (1 << 20)
+
+/***************************************************************
+* cl_intel_command_queue_families
+***************************************************************/
+#define cl_intel_command_queue_families 1
+
+typedef cl_bitfield         cl_command_queue_capabilities_intel;
+
+#define CL_QUEUE_FAMILY_MAX_NAME_SIZE_INTEL                 64
+
+typedef struct _cl_queue_family_properties_intel {
+    cl_command_queue_properties properties;
+    cl_command_queue_capabilities_intel capabilities;
+    cl_uint count;
+    char name[CL_QUEUE_FAMILY_MAX_NAME_SIZE_INTEL];
+} cl_queue_family_properties_intel;
+
+/* cl_device_info */
+#define CL_DEVICE_QUEUE_FAMILY_PROPERTIES_INTEL             0x418B
+
+/* cl_queue_properties */
+#define CL_QUEUE_FAMILY_INTEL                               0x418C
+#define CL_QUEUE_INDEX_INTEL                                0x418D
+
+/* cl_command_queue_capabilities_intel */
+#define CL_QUEUE_DEFAULT_CAPABILITIES_INTEL                 0
+#define CL_QUEUE_CAPABILITY_CREATE_SINGLE_QUEUE_EVENTS_INTEL (1 << 0)
+#define CL_QUEUE_CAPABILITY_CREATE_CROSS_QUEUE_EVENTS_INTEL (1 << 1)
+#define CL_QUEUE_CAPABILITY_SINGLE_QUEUE_EVENT_WAIT_LIST_INTEL (1 << 2)
+#define CL_QUEUE_CAPABILITY_CROSS_QUEUE_EVENT_WAIT_LIST_INTEL (1 << 3)
+#define CL_QUEUE_CAPABILITY_TRANSFER_BUFFER_INTEL           (1 << 8)
+#define CL_QUEUE_CAPABILITY_TRANSFER_BUFFER_RECT_INTEL      (1 << 9)
+#define CL_QUEUE_CAPABILITY_MAP_BUFFER_INTEL                (1 << 10)
+#define CL_QUEUE_CAPABILITY_FILL_BUFFER_INTEL               (1 << 11)
+#define CL_QUEUE_CAPABILITY_TRANSFER_IMAGE_INTEL            (1 << 12)
+#define CL_QUEUE_CAPABILITY_MAP_IMAGE_INTEL                 (1 << 13)
+#define CL_QUEUE_CAPABILITY_FILL_IMAGE_INTEL                (1 << 14)
+#define CL_QUEUE_CAPABILITY_TRANSFER_BUFFER_IMAGE_INTEL     (1 << 15)
+#define CL_QUEUE_CAPABILITY_TRANSFER_IMAGE_BUFFER_INTEL     (1 << 16)
+#define CL_QUEUE_CAPABILITY_MARKER_INTEL                    (1 << 24)
+#define CL_QUEUE_CAPABILITY_BARRIER_INTEL                   (1 << 25)
+#define CL_QUEUE_CAPABILITY_KERNEL_INTEL                    (1 << 26)
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* __CL_EXT_H */
diff --git a/OpenCL/CL/cl_ext_intel.h b/OpenCL/CL/cl_ext_intel.h
new file mode 100644
index 0000000..a7ae87a
--- /dev/null
+++ b/OpenCL/CL/cl_ext_intel.h
@@ -0,0 +1,19 @@
+/*******************************************************************************
+ * Copyright (c) 2008-2020 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <CL/cl_ext.h>
+#pragma message("The Intel extensions have been moved into cl_ext.h.  Please include cl_ext.h directly.")
diff --git a/OpenCL/CL/cl_gl.h b/OpenCL/CL/cl_gl.h
new file mode 100644
index 0000000..5ea0fd8
--- /dev/null
+++ b/OpenCL/CL/cl_gl.h
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright (c) 2008-2021 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#ifndef __OPENCL_CL_GL_H
+#define __OPENCL_CL_GL_H
+
+#include <CL/cl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef cl_uint     cl_gl_object_type;
+typedef cl_uint     cl_gl_texture_info;
+typedef cl_uint     cl_gl_platform_info;
+typedef struct __GLsync *cl_GLsync;
+
+/* cl_gl_object_type = 0x2000 - 0x200F enum values are currently taken           */
+#define CL_GL_OBJECT_BUFFER                     0x2000
+#define CL_GL_OBJECT_TEXTURE2D                  0x2001
+#define CL_GL_OBJECT_TEXTURE3D                  0x2002
+#define CL_GL_OBJECT_RENDERBUFFER               0x2003
+#ifdef CL_VERSION_1_2
+#define CL_GL_OBJECT_TEXTURE2D_ARRAY            0x200E
+#define CL_GL_OBJECT_TEXTURE1D                  0x200F
+#define CL_GL_OBJECT_TEXTURE1D_ARRAY            0x2010
+#define CL_GL_OBJECT_TEXTURE_BUFFER             0x2011
+#endif
+
+/* cl_gl_texture_info           */
+#define CL_GL_TEXTURE_TARGET                    0x2004
+#define CL_GL_MIPMAP_LEVEL                      0x2005
+#ifdef CL_VERSION_1_2
+#define CL_GL_NUM_SAMPLES                       0x2012
+#endif
+
+
+extern CL_API_ENTRY cl_mem CL_API_CALL
+clCreateFromGLBuffer(cl_context     context,
+                     cl_mem_flags   flags,
+                     cl_GLuint      bufobj,
+                     cl_int *       errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_2
+
+extern CL_API_ENTRY cl_mem CL_API_CALL
+clCreateFromGLTexture(cl_context      context,
+                      cl_mem_flags    flags,
+                      cl_GLenum       target,
+                      cl_GLint        miplevel,
+                      cl_GLuint       texture,
+                      cl_int *        errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+#endif
+
+extern CL_API_ENTRY cl_mem CL_API_CALL
+clCreateFromGLRenderbuffer(cl_context   context,
+                           cl_mem_flags flags,
+                           cl_GLuint    renderbuffer,
+                           cl_int *     errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetGLObjectInfo(cl_mem                memobj,
+                  cl_gl_object_type *   gl_object_type,
+                  cl_GLuint *           gl_object_name) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetGLTextureInfo(cl_mem               memobj,
+                   cl_gl_texture_info   param_name,
+                   size_t               param_value_size,
+                   void *               param_value,
+                   size_t *             param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueAcquireGLObjects(cl_command_queue      command_queue,
+                          cl_uint               num_objects,
+                          const cl_mem *        mem_objects,
+                          cl_uint               num_events_in_wait_list,
+                          const cl_event *      event_wait_list,
+                          cl_event *            event) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueReleaseGLObjects(cl_command_queue      command_queue,
+                          cl_uint               num_objects,
+                          const cl_mem *        mem_objects,
+                          cl_uint               num_events_in_wait_list,
+                          const cl_event *      event_wait_list,
+                          cl_event *            event) CL_API_SUFFIX__VERSION_1_0;
+
+
+/* Deprecated OpenCL 1.1 APIs */
+extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_mem CL_API_CALL
+clCreateFromGLTexture2D(cl_context      context,
+                        cl_mem_flags    flags,
+                        cl_GLenum       target,
+                        cl_GLint        miplevel,
+                        cl_GLuint       texture,
+                        cl_int *        errcode_ret) CL_API_SUFFIX__VERSION_1_1_DEPRECATED;
+
+extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_mem CL_API_CALL
+clCreateFromGLTexture3D(cl_context      context,
+                        cl_mem_flags    flags,
+                        cl_GLenum       target,
+                        cl_GLint        miplevel,
+                        cl_GLuint       texture,
+                        cl_int *        errcode_ret) CL_API_SUFFIX__VERSION_1_1_DEPRECATED;
+
+/* cl_khr_gl_sharing extension  */
+
+#define cl_khr_gl_sharing 1
+
+typedef cl_uint     cl_gl_context_info;
+
+/* Additional Error Codes  */
+#define CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR  -1000
+
+/* cl_gl_context_info  */
+#define CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KHR    0x2006
+#define CL_DEVICES_FOR_GL_CONTEXT_KHR           0x2007
+
+/* Additional cl_context_properties  */
+#define CL_GL_CONTEXT_KHR                       0x2008
+#define CL_EGL_DISPLAY_KHR                      0x2009
+#define CL_GLX_DISPLAY_KHR                      0x200A
+#define CL_WGL_HDC_KHR                          0x200B
+#define CL_CGL_SHAREGROUP_KHR                   0x200C
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetGLContextInfoKHR(const cl_context_properties * properties,
+                      cl_gl_context_info            param_name,
+                      size_t                        param_value_size,
+                      void *                        param_value,
+                      size_t *                      param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int (CL_API_CALL *clGetGLContextInfoKHR_fn)(
+    const cl_context_properties * properties,
+    cl_gl_context_info            param_name,
+    size_t                        param_value_size,
+    void *                        param_value,
+    size_t *                      param_value_size_ret);
+
+/* 
+ *  cl_khr_gl_event extension
+ */
+#define CL_COMMAND_GL_FENCE_SYNC_OBJECT_KHR     0x200D
+
+extern CL_API_ENTRY cl_event CL_API_CALL
+clCreateEventFromGLsyncKHR(cl_context context,
+                           cl_GLsync  sync,
+                           cl_int *   errcode_ret) CL_API_SUFFIX__VERSION_1_1;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __OPENCL_CL_GL_H */
diff --git a/OpenCL/CL/cl_gl_ext.h b/OpenCL/CL/cl_gl_ext.h
new file mode 100644
index 0000000..8ec8181
--- /dev/null
+++ b/OpenCL/CL/cl_gl_ext.h
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2008-2021 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#include <CL/cl_gl.h>
+#pragma message("All OpenGL-related extensions have been moved into cl_gl.h.  Please include cl_gl.h directly.")
diff --git a/OpenCL/CL/cl_half.h b/OpenCL/CL/cl_half.h
new file mode 100644
index 0000000..ecc4223
--- /dev/null
+++ b/OpenCL/CL/cl_half.h
@@ -0,0 +1,440 @@
+/*******************************************************************************
+ * Copyright (c) 2019-2020 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+/**
+ * This is a header-only utility library that provides OpenCL host code with
+ * routines for converting to/from cl_half values.
+ *
+ * Example usage:
+ *
+ *    #include <CL/cl_half.h>
+ *    ...
+ *    cl_half h = cl_half_from_float(0.5f, CL_HALF_RTE);
+ *    cl_float f = cl_half_to_float(h);
+ */
+
+#ifndef OPENCL_CL_HALF_H
+#define OPENCL_CL_HALF_H
+
+#include <CL/cl_platform.h>
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * Rounding mode used when converting to cl_half.
+ */
+typedef enum
+{
+  CL_HALF_RTE, // round to nearest even
+  CL_HALF_RTZ, // round towards zero
+  CL_HALF_RTP, // round towards positive infinity
+  CL_HALF_RTN, // round towards negative infinity
+} cl_half_rounding_mode;
+
+
+/* Private utility macros. */
+#define CL_HALF_EXP_MASK 0x7C00
+#define CL_HALF_MAX_FINITE_MAG 0x7BFF
+
+
+/*
+ * Utility to deal with values that overflow when converting to half precision.
+ */
+static inline cl_half cl_half_handle_overflow(cl_half_rounding_mode rounding_mode,
+                                              uint16_t sign)
+{
+  if (rounding_mode == CL_HALF_RTZ)
+  {
+    // Round overflow towards zero -> largest finite number (preserving sign)
+    return (sign << 15) | CL_HALF_MAX_FINITE_MAG;
+  }
+  else if (rounding_mode == CL_HALF_RTP && sign)
+  {
+    // Round negative overflow towards positive infinity -> most negative finite number
+    return (1 << 15) | CL_HALF_MAX_FINITE_MAG;
+  }
+  else if (rounding_mode == CL_HALF_RTN && !sign)
+  {
+    // Round positive overflow towards negative infinity -> largest finite number
+    return CL_HALF_MAX_FINITE_MAG;
+  }
+
+  // Overflow to infinity
+  return (sign << 15) | CL_HALF_EXP_MASK;
+}
+
+/*
+ * Utility to deal with values that underflow when converting to half precision.
+ */
+static inline cl_half cl_half_handle_underflow(cl_half_rounding_mode rounding_mode,
+                                               uint16_t sign)
+{
+  if (rounding_mode == CL_HALF_RTP && !sign)
+  {
+    // Round underflow towards positive infinity -> smallest positive value
+    return (sign << 15) | 1;
+  }
+  else if (rounding_mode == CL_HALF_RTN && sign)
+  {
+    // Round underflow towards negative infinity -> largest negative value
+    return (sign << 15) | 1;
+  }
+
+  // Flush to zero
+  return (sign << 15);
+}
+
+
+/**
+ * Convert a cl_float to a cl_half.
+ */
+static inline cl_half cl_half_from_float(cl_float f, cl_half_rounding_mode rounding_mode)
+{
+  // Type-punning to get direct access to underlying bits
+  union
+  {
+    cl_float f;
+    uint32_t i;
+  } f32;
+  f32.f = f;
+
+  // Extract sign bit
+  uint16_t sign = f32.i >> 31;
+
+  // Extract FP32 exponent and mantissa
+  uint32_t f_exp = (f32.i >> (CL_FLT_MANT_DIG - 1)) & 0xFF;
+  uint32_t f_mant = f32.i & ((1 << (CL_FLT_MANT_DIG - 1)) - 1);
+
+  // Remove FP32 exponent bias
+  int32_t exp = f_exp - CL_FLT_MAX_EXP + 1;
+
+  // Add FP16 exponent bias
+  uint16_t h_exp = (uint16_t)(exp + CL_HALF_MAX_EXP - 1);
+
+  // Position of the bit that will become the FP16 mantissa LSB
+  uint32_t lsb_pos = CL_FLT_MANT_DIG - CL_HALF_MANT_DIG;
+
+  // Check for NaN / infinity
+  if (f_exp == 0xFF)
+  {
+    if (f_mant)
+    {
+      // NaN -> propagate mantissa and silence it
+      uint16_t h_mant = (uint16_t)(f_mant >> lsb_pos);
+      h_mant |= 0x200;
+      return (sign << 15) | CL_HALF_EXP_MASK | h_mant;
+    }
+    else
+    {
+      // Infinity -> zero mantissa
+      return (sign << 15) | CL_HALF_EXP_MASK;
+    }
+  }
+
+  // Check for zero
+  if (!f_exp && !f_mant)
+  {
+    return (sign << 15);
+  }
+
+  // Check for overflow
+  if (exp >= CL_HALF_MAX_EXP)
+  {
+    return cl_half_handle_overflow(rounding_mode, sign);
+  }
+
+  // Check for underflow
+  if (exp < (CL_HALF_MIN_EXP - CL_HALF_MANT_DIG - 1))
+  {
+    return cl_half_handle_underflow(rounding_mode, sign);
+  }
+
+  // Check for value that will become denormal
+  if (exp < -14)
+  {
+    // Denormal -> include the implicit 1 from the FP32 mantissa
+    h_exp = 0;
+    f_mant |= 1 << (CL_FLT_MANT_DIG - 1);
+
+    // Mantissa shift amount depends on exponent
+    lsb_pos = -exp + (CL_FLT_MANT_DIG - 25);
+  }
+
+  // Generate FP16 mantissa by shifting FP32 mantissa
+  uint16_t h_mant = (uint16_t)(f_mant >> lsb_pos);
+
+  // Check whether we need to round
+  uint32_t halfway = 1 << (lsb_pos - 1);
+  uint32_t mask = (halfway << 1) - 1;
+  switch (rounding_mode)
+  {
+    case CL_HALF_RTE:
+      if ((f_mant & mask) > halfway)
+      {
+        // More than halfway -> round up
+        h_mant += 1;
+      }
+      else if ((f_mant & mask) == halfway)
+      {
+        // Exactly halfway -> round to nearest even
+        if (h_mant & 0x1)
+          h_mant += 1;
+      }
+      break;
+    case CL_HALF_RTZ:
+      // Mantissa has already been truncated -> do nothing
+      break;
+    case CL_HALF_RTP:
+      if ((f_mant & mask) && !sign)
+      {
+        // Round positive numbers up
+        h_mant += 1;
+      }
+      break;
+    case CL_HALF_RTN:
+      if ((f_mant & mask) && sign)
+      {
+        // Round negative numbers down
+        h_mant += 1;
+      }
+      break;
+  }
+
+  // Check for mantissa overflow
+  if (h_mant & 0x400)
+  {
+    h_exp += 1;
+    h_mant = 0;
+  }
+
+  return (sign << 15) | (h_exp << 10) | h_mant;
+}
+
+
+/**
+ * Convert a cl_double to a cl_half.
+ */
+static inline cl_half cl_half_from_double(cl_double d, cl_half_rounding_mode rounding_mode)
+{
+  // Type-punning to get direct access to underlying bits
+  union
+  {
+    cl_double d;
+    uint64_t i;
+  } f64;
+  f64.d = d;
+
+  // Extract sign bit
+  uint16_t sign = f64.i >> 63;
+
+  // Extract FP64 exponent and mantissa
+  uint64_t d_exp = (f64.i >> (CL_DBL_MANT_DIG - 1)) & 0x7FF;
+  uint64_t d_mant = f64.i & (((uint64_t)1 << (CL_DBL_MANT_DIG - 1)) - 1);
+
+  // Remove FP64 exponent bias
+  int64_t exp = d_exp - CL_DBL_MAX_EXP + 1;
+
+  // Add FP16 exponent bias
+  uint16_t h_exp = (uint16_t)(exp + CL_HALF_MAX_EXP - 1);
+
+  // Position of the bit that will become the FP16 mantissa LSB
+  uint32_t lsb_pos = CL_DBL_MANT_DIG - CL_HALF_MANT_DIG;
+
+  // Check for NaN / infinity
+  if (d_exp == 0x7FF)
+  {
+    if (d_mant)
+    {
+      // NaN -> propagate mantissa and silence it
+      uint16_t h_mant = (uint16_t)(d_mant >> lsb_pos);
+      h_mant |= 0x200;
+      return (sign << 15) | CL_HALF_EXP_MASK | h_mant;
+    }
+    else
+    {
+      // Infinity -> zero mantissa
+      return (sign << 15) | CL_HALF_EXP_MASK;
+    }
+  }
+
+  // Check for zero
+  if (!d_exp && !d_mant)
+  {
+    return (sign << 15);
+  }
+
+  // Check for overflow
+  if (exp >= CL_HALF_MAX_EXP)
+  {
+    return cl_half_handle_overflow(rounding_mode, sign);
+  }
+
+  // Check for underflow
+  if (exp < (CL_HALF_MIN_EXP - CL_HALF_MANT_DIG - 1))
+  {
+    return cl_half_handle_underflow(rounding_mode, sign);
+  }
+
+  // Check for value that will become denormal
+  if (exp < -14)
+  {
+    // Include the implicit 1 from the FP64 mantissa
+    h_exp = 0;
+    d_mant |= (uint64_t)1 << (CL_DBL_MANT_DIG - 1);
+
+    // Mantissa shift amount depends on exponent
+    lsb_pos = (uint32_t)(-exp + (CL_DBL_MANT_DIG - 25));
+  }
+
+  // Generate FP16 mantissa by shifting FP64 mantissa
+  uint16_t h_mant = (uint16_t)(d_mant >> lsb_pos);
+
+  // Check whether we need to round
+  uint64_t halfway = (uint64_t)1 << (lsb_pos - 1);
+  uint64_t mask = (halfway << 1) - 1;
+  switch (rounding_mode)
+  {
+    case CL_HALF_RTE:
+      if ((d_mant & mask) > halfway)
+      {
+        // More than halfway -> round up
+        h_mant += 1;
+      }
+      else if ((d_mant & mask) == halfway)
+      {
+        // Exactly halfway -> round to nearest even
+        if (h_mant & 0x1)
+          h_mant += 1;
+      }
+      break;
+    case CL_HALF_RTZ:
+      // Mantissa has already been truncated -> do nothing
+      break;
+    case CL_HALF_RTP:
+      if ((d_mant & mask) && !sign)
+      {
+        // Round positive numbers up
+        h_mant += 1;
+      }
+      break;
+    case CL_HALF_RTN:
+      if ((d_mant & mask) && sign)
+      {
+        // Round negative numbers down
+        h_mant += 1;
+      }
+      break;
+  }
+
+  // Check for mantissa overflow
+  if (h_mant & 0x400)
+  {
+    h_exp += 1;
+    h_mant = 0;
+  }
+
+  return (sign << 15) | (h_exp << 10) | h_mant;
+}
+
+
+/**
+ * Convert a cl_half to a cl_float.
+ */
+static inline cl_float cl_half_to_float(cl_half h)
+{
+  // Type-punning to get direct access to underlying bits
+  union
+  {
+    cl_float f;
+    uint32_t i;
+  } f32;
+
+  // Extract sign bit
+  uint16_t sign = h >> 15;
+
+  // Extract FP16 exponent and mantissa
+  uint16_t h_exp = (h >> (CL_HALF_MANT_DIG - 1)) & 0x1F;
+  uint16_t h_mant = h & 0x3FF;
+
+  // Remove FP16 exponent bias
+  int32_t exp = h_exp - CL_HALF_MAX_EXP + 1;
+
+  // Add FP32 exponent bias
+  uint32_t f_exp = exp + CL_FLT_MAX_EXP - 1;
+
+  // Check for NaN / infinity
+  if (h_exp == 0x1F)
+  {
+    if (h_mant)
+    {
+      // NaN -> propagate mantissa and silence it
+      uint32_t f_mant = h_mant << (CL_FLT_MANT_DIG - CL_HALF_MANT_DIG);
+      f_mant |= 0x400000;
+      f32.i = (sign << 31) | 0x7F800000 | f_mant;
+      return f32.f;
+    }
+    else
+    {
+      // Infinity -> zero mantissa
+      f32.i = (sign << 31) | 0x7F800000;
+      return f32.f;
+    }
+  }
+
+  // Check for zero / denormal
+  if (h_exp == 0)
+  {
+    if (h_mant == 0)
+    {
+      // Zero -> zero exponent
+      f_exp = 0;
+    }
+    else
+    {
+      // Denormal -> normalize it
+      // - Shift mantissa to make most-significant 1 implicit
+      // - Adjust exponent accordingly
+      uint32_t shift = 0;
+      while ((h_mant & 0x400) == 0)
+      {
+        h_mant <<= 1;
+        shift++;
+      }
+      h_mant &= 0x3FF;
+      f_exp -= shift - 1;
+    }
+  }
+
+  f32.i = (sign << 31) | (f_exp << 23) | (h_mant << 13);
+  return f32.f;
+}
+
+
+#undef CL_HALF_EXP_MASK
+#undef CL_HALF_MAX_FINITE_MAG
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif  /* OPENCL_CL_HALF_H */
diff --git a/OpenCL/CL/cl_icd.h b/OpenCL/CL/cl_icd.h
new file mode 100644
index 0000000..d5ac1de
--- /dev/null
+++ b/OpenCL/CL/cl_icd.h
@@ -0,0 +1,1294 @@
+/*******************************************************************************
+ * Copyright (c) 2019-2020 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#ifndef OPENCL_CL_ICD_H
+#define OPENCL_CL_ICD_H
+
+#include <CL/cl.h>
+#include <CL/cl_egl.h>
+#include <CL/cl_ext.h>
+#include <CL/cl_gl.h>
+
+#if defined(_WIN32)
+#include <CL/cl_d3d11.h>
+#include <CL/cl_d3d10.h>
+#include <CL/cl_dx9_media_sharing.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This file contains pointer type definitions for each of the CL API calls as
+ * well as a type definition for the dispatch table used by the Khronos ICD
+ * loader (see cl_khr_icd extension specification for background).
+ */
+
+/* API function pointer definitions */
+
+// Platform APIs
+typedef cl_int(CL_API_CALL *cl_api_clGetPlatformIDs)(
+    cl_uint num_entries, cl_platform_id *platforms,
+    cl_uint *num_platforms) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clGetPlatformInfo)(
+    cl_platform_id platform, cl_platform_info param_name,
+    size_t param_value_size, void *param_value,
+    size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+// Device APIs
+typedef cl_int(CL_API_CALL *cl_api_clGetDeviceIDs)(
+    cl_platform_id platform, cl_device_type device_type, cl_uint num_entries,
+    cl_device_id *devices, cl_uint *num_devices) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clGetDeviceInfo)(
+    cl_device_id device, cl_device_info param_name, size_t param_value_size,
+    void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_2
+
+typedef cl_int(CL_API_CALL *cl_api_clCreateSubDevices)(
+    cl_device_id in_device,
+    const cl_device_partition_property *partition_properties,
+    cl_uint num_entries, cl_device_id *out_devices, cl_uint *num_devices);
+
+typedef cl_int(CL_API_CALL *cl_api_clRetainDevice)(
+    cl_device_id device) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_int(CL_API_CALL *cl_api_clReleaseDevice)(
+    cl_device_id device) CL_API_SUFFIX__VERSION_1_2;
+
+#else
+
+typedef void *cl_api_clCreateSubDevices;
+typedef void *cl_api_clRetainDevice;
+typedef void *cl_api_clReleaseDevice;
+
+#endif
+
+// Context APIs
+typedef cl_context(CL_API_CALL *cl_api_clCreateContext)(
+    const cl_context_properties *properties, cl_uint num_devices,
+    const cl_device_id *devices,
+    void(CL_CALLBACK *pfn_notify)(const char *, const void *, size_t, void *),
+    void *user_data, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_context(CL_API_CALL *cl_api_clCreateContextFromType)(
+    const cl_context_properties *properties, cl_device_type device_type,
+    void(CL_CALLBACK *pfn_notify)(const char *, const void *, size_t, void *),
+    void *user_data, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clRetainContext)(
+    cl_context context) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clReleaseContext)(
+    cl_context context) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clGetContextInfo)(
+    cl_context context, cl_context_info param_name, size_t param_value_size,
+    void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+// Command Queue APIs
+typedef cl_command_queue(CL_API_CALL *cl_api_clCreateCommandQueue)(
+    cl_context context, cl_device_id device,
+    cl_command_queue_properties properties,
+    cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_2_0
+
+typedef
+cl_command_queue(CL_API_CALL *cl_api_clCreateCommandQueueWithProperties)(
+    cl_context /* context */, cl_device_id /* device */,
+    const cl_queue_properties * /* properties */,
+    cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_2_0;
+
+#else
+
+typedef void *cl_api_clCreateCommandQueueWithProperties;
+
+#endif
+
+typedef cl_int(CL_API_CALL *cl_api_clRetainCommandQueue)(
+    cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clReleaseCommandQueue)(
+    cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clGetCommandQueueInfo)(
+    cl_command_queue command_queue, cl_command_queue_info param_name,
+    size_t param_value_size, void *param_value,
+    size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+// Memory Object APIs
+typedef cl_mem(CL_API_CALL *cl_api_clCreateBuffer)(
+    cl_context context, cl_mem_flags flags, size_t size, void *host_ptr,
+    cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_2
+
+typedef cl_mem(CL_API_CALL *cl_api_clCreateImage)(
+    cl_context context, cl_mem_flags flags, const cl_image_format *image_format,
+    const cl_image_desc *image_desc, void *host_ptr,
+    cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+#else
+
+typedef void *cl_api_clCreateImage;
+
+#endif
+
+#ifdef CL_VERSION_3_0
+
+typedef cl_mem(CL_API_CALL *cl_api_clCreateBufferWithProperties)(
+    cl_context context, const cl_mem_properties *properties, cl_mem_flags flags,
+    size_t size, void *host_ptr,
+    cl_int *errcode_ret) CL_API_SUFFIX__VERSION_3_0;
+
+typedef cl_mem(CL_API_CALL *cl_api_clCreateImageWithProperties)(
+    cl_context context, const cl_mem_properties *properties, cl_mem_flags flags,
+    const cl_image_format *image_format, const cl_image_desc *image_desc,
+    void *host_ptr, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_3_0;
+
+typedef cl_int(CL_API_CALL* cl_api_clSetContextDestructorCallback)(
+    cl_context context,
+    void(CL_CALLBACK* pfn_notify)(cl_context context, void* user_data),
+    void* user_data) CL_API_SUFFIX__VERSION_3_0;
+
+#else
+
+typedef void *cl_api_clCreateBufferWithProperties;
+typedef void *cl_api_clCreateImageWithProperties;
+typedef void *cl_api_clSetContextDestructorCallback;
+
+#endif
+
+typedef cl_int(CL_API_CALL *cl_api_clRetainMemObject)(
+    cl_mem memobj) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clReleaseMemObject)(
+    cl_mem memobj) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clGetSupportedImageFormats)(
+    cl_context context, cl_mem_flags flags, cl_mem_object_type image_type,
+    cl_uint num_entries, cl_image_format *image_formats,
+    cl_uint *num_image_formats) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clGetMemObjectInfo)(
+    cl_mem memobj, cl_mem_info param_name, size_t param_value_size,
+    void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clGetImageInfo)(
+    cl_mem image, cl_image_info param_name, size_t param_value_size,
+    void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_2_0
+
+typedef cl_mem(CL_API_CALL *cl_api_clCreatePipe)(
+    cl_context /* context */, cl_mem_flags /* flags */,
+    cl_uint /* pipe_packet_size */, cl_uint /* pipe_max_packets */,
+    const cl_pipe_properties * /* properties */,
+    cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_2_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clGetPipeInfo)(
+    cl_mem /* pipe */, cl_pipe_info /* param_name */,
+    size_t /* param_value_size */, void * /* param_value */,
+    size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_2_0;
+
+typedef void *(CL_API_CALL *cl_api_clSVMAlloc)(
+    cl_context /* context */, cl_svm_mem_flags /* flags */, size_t /* size */,
+    unsigned int /* alignment */)CL_API_SUFFIX__VERSION_2_0;
+
+typedef void(CL_API_CALL *cl_api_clSVMFree)(
+    cl_context /* context */,
+    void * /* svm_pointer */) CL_API_SUFFIX__VERSION_2_0;
+
+#else
+
+typedef void *cl_api_clCreatePipe;
+typedef void *cl_api_clGetPipeInfo;
+typedef void *cl_api_clSVMAlloc;
+typedef void *cl_api_clSVMFree;
+
+#endif
+
+// Sampler APIs
+typedef cl_sampler(CL_API_CALL *cl_api_clCreateSampler)(
+    cl_context context, cl_bool normalized_coords,
+    cl_addressing_mode addressing_mode, cl_filter_mode filter_mode,
+    cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clRetainSampler)(
+    cl_sampler sampler) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clReleaseSampler)(
+    cl_sampler sampler) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clGetSamplerInfo)(
+    cl_sampler sampler, cl_sampler_info param_name, size_t param_value_size,
+    void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_2_0
+
+typedef
+cl_sampler(CL_API_CALL *cl_api_clCreateSamplerWithProperties)(
+    cl_context /* context */,
+    const cl_sampler_properties * /* sampler_properties */,
+    cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_2_0;
+
+#else
+
+typedef void *cl_api_clCreateSamplerWithProperties;
+
+#endif
+
+// Program Object APIs
+typedef cl_program(CL_API_CALL *cl_api_clCreateProgramWithSource)(
+    cl_context context, cl_uint count, const char **strings,
+    const size_t *lengths, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_program(CL_API_CALL *cl_api_clCreateProgramWithBinary)(
+    cl_context context, cl_uint num_devices, const cl_device_id *device_list,
+    const size_t *lengths, const unsigned char **binaries,
+    cl_int *binary_status, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_2
+
+typedef
+cl_program(CL_API_CALL *cl_api_clCreateProgramWithBuiltInKernels)(
+    cl_context context, cl_uint num_devices, const cl_device_id *device_list,
+    const char *kernel_names, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+#else
+
+typedef void *cl_api_clCreateProgramWithBuiltInKernels;
+
+#endif
+
+typedef cl_int(CL_API_CALL *cl_api_clRetainProgram)(
+    cl_program program) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clReleaseProgram)(
+    cl_program program) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clBuildProgram)(
+    cl_program program, cl_uint num_devices, const cl_device_id *device_list,
+    const char *options,
+    void(CL_CALLBACK *pfn_notify)(cl_program program, void *user_data),
+    void *user_data) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_2
+
+typedef cl_int(CL_API_CALL *cl_api_clCompileProgram)(
+    cl_program program, cl_uint num_devices, const cl_device_id *device_list,
+    const char *options, cl_uint num_input_headers,
+    const cl_program *input_headers, const char **header_include_names,
+    void(CL_CALLBACK *pfn_notify)(cl_program program, void *user_data),
+    void *user_data) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_program(CL_API_CALL *cl_api_clLinkProgram)(
+    cl_context context, cl_uint num_devices, const cl_device_id *device_list,
+    const char *options, cl_uint num_input_programs,
+    const cl_program *input_programs,
+    void(CL_CALLBACK *pfn_notify)(cl_program program, void *user_data),
+    void *user_data, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+#else
+
+typedef void *cl_api_clCompileProgram;
+typedef void *cl_api_clLinkProgram;
+
+#endif
+
+#ifdef CL_VERSION_2_2
+
+typedef
+cl_int(CL_API_CALL *cl_api_clSetProgramSpecializationConstant)(
+    cl_program program, cl_uint spec_id, size_t spec_size,
+    const void *spec_value) CL_API_SUFFIX__VERSION_2_2;
+
+typedef cl_int(CL_API_CALL *cl_api_clSetProgramReleaseCallback)(
+    cl_program program,
+    void(CL_CALLBACK *pfn_notify)(cl_program program, void *user_data),
+    void *user_data) CL_API_SUFFIX__VERSION_2_2;
+
+#else
+
+typedef void *cl_api_clSetProgramSpecializationConstant;
+typedef void *cl_api_clSetProgramReleaseCallback;
+
+#endif
+
+#ifdef CL_VERSION_1_2
+
+typedef cl_int(CL_API_CALL *cl_api_clUnloadPlatformCompiler)(
+    cl_platform_id platform) CL_API_SUFFIX__VERSION_1_2;
+
+#else
+
+typedef void *cl_api_clUnloadPlatformCompiler;
+
+#endif
+
+typedef cl_int(CL_API_CALL *cl_api_clGetProgramInfo)(
+    cl_program program, cl_program_info param_name, size_t param_value_size,
+    void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clGetProgramBuildInfo)(
+    cl_program program, cl_device_id device, cl_program_build_info param_name,
+    size_t param_value_size, void *param_value,
+    size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+// Kernel Object APIs
+typedef cl_kernel(CL_API_CALL *cl_api_clCreateKernel)(
+    cl_program program, const char *kernel_name,
+    cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clCreateKernelsInProgram)(
+    cl_program program, cl_uint num_kernels, cl_kernel *kernels,
+    cl_uint *num_kernels_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clRetainKernel)(
+    cl_kernel kernel) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clReleaseKernel)(
+    cl_kernel kernel) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clSetKernelArg)(
+    cl_kernel kernel, cl_uint arg_index, size_t arg_size,
+    const void *arg_value) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clGetKernelInfo)(
+    cl_kernel kernel, cl_kernel_info param_name, size_t param_value_size,
+    void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_2
+
+typedef cl_int(CL_API_CALL *cl_api_clGetKernelArgInfo)(
+    cl_kernel kernel, cl_uint arg_indx, cl_kernel_arg_info param_name,
+    size_t param_value_size, void *param_value,
+    size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_2;
+
+#else
+
+typedef void *cl_api_clGetKernelArgInfo;
+
+#endif
+
+typedef cl_int(CL_API_CALL *cl_api_clGetKernelWorkGroupInfo)(
+    cl_kernel kernel, cl_device_id device, cl_kernel_work_group_info param_name,
+    size_t param_value_size, void *param_value,
+    size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_2_0
+
+typedef cl_int(CL_API_CALL *cl_api_clSetKernelArgSVMPointer)(
+    cl_kernel /* kernel */, cl_uint /* arg_index */,
+    const void * /* arg_value */) CL_API_SUFFIX__VERSION_2_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clSetKernelExecInfo)(
+    cl_kernel /* kernel */, cl_kernel_exec_info /* param_name */,
+    size_t /* param_value_size */,
+    const void * /* param_value */) CL_API_SUFFIX__VERSION_2_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clGetKernelSubGroupInfoKHR)(
+    cl_kernel /* in_kernel */, cl_device_id /*in_device*/,
+    cl_kernel_sub_group_info /* param_name */, size_t /*input_value_size*/,
+    const void * /*input_value*/, size_t /*param_value_size*/,
+    void * /*param_value*/,
+    size_t * /*param_value_size_ret*/) CL_API_SUFFIX__VERSION_2_0;
+
+#else
+
+typedef void *cl_api_clSetKernelArgSVMPointer;
+typedef void *cl_api_clSetKernelExecInfo;
+typedef void *cl_api_clGetKernelSubGroupInfoKHR;
+
+#endif
+
+// Event Object APIs
+typedef cl_int(CL_API_CALL *cl_api_clWaitForEvents)(
+    cl_uint num_events, const cl_event *event_list) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clGetEventInfo)(
+    cl_event event, cl_event_info param_name, size_t param_value_size,
+    void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clRetainEvent)(cl_event event)
+    CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clReleaseEvent)(cl_event event)
+    CL_API_SUFFIX__VERSION_1_0;
+
+// Profiling APIs
+typedef cl_int(CL_API_CALL *cl_api_clGetEventProfilingInfo)(
+    cl_event event, cl_profiling_info param_name, size_t param_value_size,
+    void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+// Flush and Finish APIs
+typedef cl_int(CL_API_CALL *cl_api_clFlush)(
+    cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clFinish)(
+    cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0;
+
+// Enqueued Commands APIs
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueReadBuffer)(
+    cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_read,
+    size_t offset, size_t cb, void *ptr, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_1
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueReadBufferRect)(
+    cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_read,
+    const size_t *buffer_origin, const size_t *host_origin,
+    const size_t *region, size_t buffer_row_pitch, size_t buffer_slice_pitch,
+    size_t host_row_pitch, size_t host_slice_pitch, void *ptr,
+    cl_uint num_events_in_wait_list, const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_1;
+
+#else
+
+typedef void *cl_api_clEnqueueReadBufferRect;
+
+#endif
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueWriteBuffer)(
+    cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_write,
+    size_t offset, size_t cb, const void *ptr, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_1
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueWriteBufferRect)(
+    cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_read,
+    const size_t *buffer_origin, const size_t *host_origin,
+    const size_t *region, size_t buffer_row_pitch, size_t buffer_slice_pitch,
+    size_t host_row_pitch, size_t host_slice_pitch, const void *ptr,
+    cl_uint num_events_in_wait_list, const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_1;
+
+#else
+
+typedef void *cl_api_clEnqueueWriteBufferRect;
+
+#endif
+
+#ifdef CL_VERSION_1_2
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueFillBuffer)(
+    cl_command_queue command_queue, cl_mem buffer, const void *pattern,
+    size_t pattern_size, size_t offset, size_t cb,
+    cl_uint num_events_in_wait_list, const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_2;
+
+#else
+
+typedef void *cl_api_clEnqueueFillBuffer;
+
+#endif
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueCopyBuffer)(
+    cl_command_queue command_queue, cl_mem src_buffer, cl_mem dst_buffer,
+    size_t src_offset, size_t dst_offset, size_t cb,
+    cl_uint num_events_in_wait_list, const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_1
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueCopyBufferRect)(
+    cl_command_queue command_queue, cl_mem src_buffer, cl_mem dst_buffer,
+    const size_t *src_origin, const size_t *dst_origin, const size_t *region,
+    size_t src_row_pitch, size_t src_slice_pitch, size_t dst_row_pitch,
+    size_t dst_slice_pitch, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_1;
+
+#else
+
+typedef void *cl_api_clEnqueueCopyBufferRect;
+
+#endif
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueReadImage)(
+    cl_command_queue command_queue, cl_mem image, cl_bool blocking_read,
+    const size_t *origin, const size_t *region, size_t row_pitch,
+    size_t slice_pitch, void *ptr, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueWriteImage)(
+    cl_command_queue command_queue, cl_mem image, cl_bool blocking_write,
+    const size_t *origin, const size_t *region, size_t input_row_pitch,
+    size_t input_slice_pitch, const void *ptr, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_2
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueFillImage)(
+    cl_command_queue command_queue, cl_mem image, const void *fill_color,
+    const size_t origin[3], const size_t region[3],
+    cl_uint num_events_in_wait_list, const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_2;
+
+#else
+
+typedef void *cl_api_clEnqueueFillImage;
+
+#endif
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueCopyImage)(
+    cl_command_queue command_queue, cl_mem src_image, cl_mem dst_image,
+    const size_t *src_origin, const size_t *dst_origin, const size_t *region,
+    cl_uint num_events_in_wait_list, const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueCopyImageToBuffer)(
+    cl_command_queue command_queue, cl_mem src_image, cl_mem dst_buffer,
+    const size_t *src_origin, const size_t *region, size_t dst_offset,
+    cl_uint num_events_in_wait_list, const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueCopyBufferToImage)(
+    cl_command_queue command_queue, cl_mem src_buffer, cl_mem dst_image,
+    size_t src_offset, const size_t *dst_origin, const size_t *region,
+    cl_uint num_events_in_wait_list, const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_0;
+
+typedef void *(CL_API_CALL *cl_api_clEnqueueMapBuffer)(
+    cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_map,
+    cl_map_flags map_flags, size_t offset, size_t cb,
+    cl_uint num_events_in_wait_list, const cl_event *event_wait_list,
+    cl_event *event, cl_int *errcode_ret)CL_API_SUFFIX__VERSION_1_0;
+
+typedef void *(CL_API_CALL *cl_api_clEnqueueMapImage)(
+    cl_command_queue command_queue, cl_mem image, cl_bool blocking_map,
+    cl_map_flags map_flags, const size_t *origin, const size_t *region,
+    size_t *image_row_pitch, size_t *image_slice_pitch,
+    cl_uint num_events_in_wait_list, const cl_event *event_wait_list,
+    cl_event *event, cl_int *errcode_ret)CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueUnmapMemObject)(
+    cl_command_queue command_queue, cl_mem memobj, void *mapped_ptr,
+    cl_uint num_events_in_wait_list, const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_2
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueMigrateMemObjects)(
+    cl_command_queue command_queue, cl_uint num_mem_objects,
+    const cl_mem *mem_objects, cl_mem_migration_flags flags,
+    cl_uint num_events_in_wait_list, const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_2;
+
+#else
+
+typedef void *cl_api_clEnqueueMigrateMemObjects;
+
+#endif
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueNDRangeKernel)(
+    cl_command_queue command_queue, cl_kernel kernel, cl_uint work_dim,
+    const size_t *global_work_offset, const size_t *global_work_size,
+    const size_t *local_work_size, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueTask)(
+    cl_command_queue command_queue, cl_kernel kernel,
+    cl_uint num_events_in_wait_list, const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueNativeKernel)(
+    cl_command_queue command_queue, void(CL_CALLBACK *user_func)(void *),
+    void *args, size_t cb_args, cl_uint num_mem_objects, const cl_mem *mem_list,
+    const void **args_mem_loc, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_0;
+
+#ifdef CL_VERSION_1_2
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueMarkerWithWaitList)(
+    cl_command_queue command_queue, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueBarrierWithWaitList)(
+    cl_command_queue command_queue, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_2;
+
+typedef void *(
+    CL_API_CALL *cl_api_clGetExtensionFunctionAddressForPlatform)(
+    cl_platform_id platform,
+    const char *function_name)CL_API_SUFFIX__VERSION_1_2;
+
+#else
+
+typedef void *cl_api_clEnqueueMarkerWithWaitList;
+typedef void *cl_api_clEnqueueBarrierWithWaitList;
+typedef void *cl_api_clGetExtensionFunctionAddressForPlatform;
+
+#endif
+
+// Shared Virtual Memory APIs
+
+#ifdef CL_VERSION_2_0
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueSVMFree)(
+    cl_command_queue /* command_queue */, cl_uint /* num_svm_pointers */,
+    void ** /* svm_pointers */,
+    void(CL_CALLBACK *pfn_free_func)(cl_command_queue /* queue */,
+                                     cl_uint /* num_svm_pointers */,
+                                     void ** /* svm_pointers[] */,
+                                     void * /* user_data */),
+    void * /* user_data */, cl_uint /* num_events_in_wait_list */,
+    const cl_event * /* event_wait_list */,
+    cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueSVMMemcpy)(
+    cl_command_queue /* command_queue */, cl_bool /* blocking_copy */,
+    void * /* dst_ptr */, const void * /* src_ptr */, size_t /* size */,
+    cl_uint /* num_events_in_wait_list */,
+    const cl_event * /* event_wait_list */,
+    cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueSVMMemFill)(
+    cl_command_queue /* command_queue */, void * /* svm_ptr */,
+    const void * /* pattern */, size_t /* pattern_size */, size_t /* size */,
+    cl_uint /* num_events_in_wait_list */,
+    const cl_event * /* event_wait_list */,
+    cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueSVMMap)(
+    cl_command_queue /* command_queue */, cl_bool /* blocking_map */,
+    cl_map_flags /* map_flags */, void * /* svm_ptr */, size_t /* size */,
+    cl_uint /* num_events_in_wait_list */,
+    const cl_event * /* event_wait_list */,
+    cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueSVMUnmap)(
+    cl_command_queue /* command_queue */, void * /* svm_ptr */,
+    cl_uint /* num_events_in_wait_list */,
+    const cl_event * /* event_wait_list */,
+    cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0;
+
+#else
+
+typedef void *cl_api_clEnqueueSVMFree;
+typedef void *cl_api_clEnqueueSVMMemcpy;
+typedef void *cl_api_clEnqueueSVMMemFill;
+typedef void *cl_api_clEnqueueSVMMap;
+typedef void *cl_api_clEnqueueSVMUnmap;
+
+#endif
+
+// Deprecated APIs
+typedef cl_int(CL_API_CALL *cl_api_clSetCommandQueueProperty)(
+    cl_command_queue command_queue, cl_command_queue_properties properties,
+    cl_bool enable, cl_command_queue_properties *old_properties)
+    CL_API_SUFFIX__VERSION_1_0_DEPRECATED;
+
+typedef cl_mem(CL_API_CALL *cl_api_clCreateImage2D)(
+    cl_context context, cl_mem_flags flags, const cl_image_format *image_format,
+    size_t image_width, size_t image_height, size_t image_row_pitch,
+    void *host_ptr, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_1_DEPRECATED;
+
+typedef cl_mem(CL_API_CALL *cl_api_clCreateImage3D)(
+    cl_context context, cl_mem_flags flags, const cl_image_format *image_format,
+    size_t image_width, size_t image_height, size_t image_depth,
+    size_t image_row_pitch, size_t image_slice_pitch, void *host_ptr,
+    cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_1_DEPRECATED;
+
+typedef cl_int(CL_API_CALL *cl_api_clUnloadCompiler)(void)
+    CL_API_SUFFIX__VERSION_1_1_DEPRECATED;
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueMarker)(
+    cl_command_queue command_queue,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_1_DEPRECATED;
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueWaitForEvents)(
+    cl_command_queue command_queue, cl_uint num_events,
+    const cl_event *event_list) CL_API_SUFFIX__VERSION_1_1_DEPRECATED;
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueBarrier)(
+    cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_1_DEPRECATED;
+
+typedef void *(CL_API_CALL *cl_api_clGetExtensionFunctionAddress)(
+    const char *function_name)CL_API_SUFFIX__VERSION_1_1_DEPRECATED;
+
+// GL and other APIs
+typedef cl_mem(CL_API_CALL *cl_api_clCreateFromGLBuffer)(
+    cl_context context, cl_mem_flags flags, cl_GLuint bufobj,
+    int *errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_mem(CL_API_CALL *cl_api_clCreateFromGLTexture)(
+    cl_context context, cl_mem_flags flags, cl_GLenum target, cl_GLint miplevel,
+    cl_GLuint texture, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_mem(CL_API_CALL *cl_api_clCreateFromGLTexture2D)(
+    cl_context context, cl_mem_flags flags, cl_GLenum target, cl_GLint miplevel,
+    cl_GLuint texture, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_mem(CL_API_CALL *cl_api_clCreateFromGLTexture3D)(
+    cl_context context, cl_mem_flags flags, cl_GLenum target, cl_GLint miplevel,
+    cl_GLuint texture, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_mem(CL_API_CALL *cl_api_clCreateFromGLRenderbuffer)(
+    cl_context context, cl_mem_flags flags, cl_GLuint renderbuffer,
+    cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clGetGLObjectInfo)(
+    cl_mem memobj, cl_gl_object_type *gl_object_type,
+    cl_GLuint *gl_object_name) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clGetGLTextureInfo)(
+    cl_mem memobj, cl_gl_texture_info param_name, size_t param_value_size,
+    void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueAcquireGLObjects)(
+    cl_command_queue command_queue, cl_uint num_objects,
+    const cl_mem *mem_objects, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueReleaseGLObjects)(
+    cl_command_queue command_queue, cl_uint num_objects,
+    const cl_mem *mem_objects, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_0;
+
+/* cl_khr_gl_sharing */
+typedef cl_int(CL_API_CALL *cl_api_clGetGLContextInfoKHR)(
+    const cl_context_properties *properties, cl_gl_context_info param_name,
+    size_t param_value_size, void *param_value, size_t *param_value_size_ret);
+
+/* cl_khr_gl_event */
+typedef cl_event(CL_API_CALL *cl_api_clCreateEventFromGLsyncKHR)(
+    cl_context context, cl_GLsync sync, cl_int *errcode_ret);
+
+#if defined(_WIN32)
+
+/* cl_khr_d3d10_sharing */
+
+typedef cl_int(CL_API_CALL *cl_api_clGetDeviceIDsFromD3D10KHR)(
+    cl_platform_id platform, cl_d3d10_device_source_khr d3d_device_source,
+    void *d3d_object, cl_d3d10_device_set_khr d3d_device_set,
+    cl_uint num_entries, cl_device_id *devices,
+    cl_uint *num_devices) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_mem(CL_API_CALL *cl_api_clCreateFromD3D10BufferKHR)(
+    cl_context context, cl_mem_flags flags, ID3D10Buffer *resource,
+    cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_mem(CL_API_CALL *cl_api_clCreateFromD3D10Texture2DKHR)(
+    cl_context context, cl_mem_flags flags, ID3D10Texture2D *resource,
+    UINT subresource, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_mem(CL_API_CALL *cl_api_clCreateFromD3D10Texture3DKHR)(
+    cl_context context, cl_mem_flags flags, ID3D10Texture3D *resource,
+    UINT subresource, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_0;
+
+typedef
+cl_int(CL_API_CALL *cl_api_clEnqueueAcquireD3D10ObjectsKHR)(
+    cl_command_queue command_queue, cl_uint num_objects,
+    const cl_mem *mem_objects, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_0;
+
+typedef
+cl_int(CL_API_CALL *cl_api_clEnqueueReleaseD3D10ObjectsKHR)(
+    cl_command_queue command_queue, cl_uint num_objects,
+    const cl_mem *mem_objects, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_0;
+
+extern CL_API_ENTRY cl_int CL_API_CALL clGetDeviceIDsFromD3D10KHR(
+    cl_platform_id platform, cl_d3d10_device_source_khr d3d_device_source,
+    void *d3d_object, cl_d3d10_device_set_khr d3d_device_set,
+    cl_uint num_entries, cl_device_id *devices, cl_uint *num_devices);
+
+extern CL_API_ENTRY cl_mem CL_API_CALL
+clCreateFromD3D10BufferKHR(cl_context context, cl_mem_flags flags,
+                           ID3D10Buffer *resource, cl_int *errcode_ret);
+
+extern CL_API_ENTRY cl_mem CL_API_CALL clCreateFromD3D10Texture2DKHR(
+    cl_context context, cl_mem_flags flags, ID3D10Texture2D *resource,
+    UINT subresource, cl_int *errcode_ret);
+
+extern CL_API_ENTRY cl_mem CL_API_CALL clCreateFromD3D10Texture3DKHR(
+    cl_context context, cl_mem_flags flags, ID3D10Texture3D *resource,
+    UINT subresource, cl_int *errcode_ret);
+
+extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueAcquireD3D10ObjectsKHR(
+    cl_command_queue command_queue, cl_uint num_objects,
+    const cl_mem *mem_objects, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list, cl_event *event);
+
+extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueReleaseD3D10ObjectsKHR(
+    cl_command_queue command_queue, cl_uint num_objects,
+    const cl_mem *mem_objects, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list, cl_event *event);
+
+/* cl_khr_d3d11_sharing */
+typedef cl_int(CL_API_CALL *cl_api_clGetDeviceIDsFromD3D11KHR)(
+    cl_platform_id platform, cl_d3d11_device_source_khr d3d_device_source,
+    void *d3d_object, cl_d3d11_device_set_khr d3d_device_set,
+    cl_uint num_entries, cl_device_id *devices,
+    cl_uint *num_devices) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_mem(CL_API_CALL *cl_api_clCreateFromD3D11BufferKHR)(
+    cl_context context, cl_mem_flags flags, ID3D11Buffer *resource,
+    cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_mem(CL_API_CALL *cl_api_clCreateFromD3D11Texture2DKHR)(
+    cl_context context, cl_mem_flags flags, ID3D11Texture2D *resource,
+    UINT subresource, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_mem(CL_API_CALL *cl_api_clCreateFromD3D11Texture3DKHR)(
+    cl_context context, cl_mem_flags flags, ID3D11Texture3D *resource,
+    UINT subresource, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+typedef
+cl_int(CL_API_CALL *cl_api_clEnqueueAcquireD3D11ObjectsKHR)(
+    cl_command_queue command_queue, cl_uint num_objects,
+    const cl_mem *mem_objects, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_2;
+
+typedef
+cl_int(CL_API_CALL *cl_api_clEnqueueReleaseD3D11ObjectsKHR)(
+    cl_command_queue command_queue, cl_uint num_objects,
+    const cl_mem *mem_objects, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_2;
+
+/* cl_khr_dx9_media_sharing */
+typedef
+cl_int(CL_API_CALL *cl_api_clGetDeviceIDsFromDX9MediaAdapterKHR)(
+    cl_platform_id platform, cl_uint num_media_adapters,
+    cl_dx9_media_adapter_type_khr *media_adapters_type, void *media_adapters,
+    cl_dx9_media_adapter_set_khr media_adapter_set, cl_uint num_entries,
+    cl_device_id *devices, cl_uint *num_devices) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_mem(CL_API_CALL *cl_api_clCreateFromDX9MediaSurfaceKHR)(
+    cl_context context, cl_mem_flags flags,
+    cl_dx9_media_adapter_type_khr adapter_type, void *surface_info,
+    cl_uint plane, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+typedef
+cl_int(CL_API_CALL *cl_api_clEnqueueAcquireDX9MediaSurfacesKHR)(
+    cl_command_queue command_queue, cl_uint num_objects,
+    const cl_mem *mem_objects, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_2;
+
+typedef
+cl_int(CL_API_CALL *cl_api_clEnqueueReleaseDX9MediaSurfacesKHR)(
+    cl_command_queue command_queue, cl_uint num_objects,
+    const cl_mem *mem_objects, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_1_2;
+
+/* cl_khr_d3d11_sharing */
+extern CL_API_ENTRY cl_int CL_API_CALL clGetDeviceIDsFromD3D11KHR(
+    cl_platform_id platform, cl_d3d11_device_source_khr d3d_device_source,
+    void *d3d_object, cl_d3d11_device_set_khr d3d_device_set,
+    cl_uint num_entries, cl_device_id *devices, cl_uint *num_devices);
+
+extern CL_API_ENTRY cl_mem CL_API_CALL
+clCreateFromD3D11BufferKHR(cl_context context, cl_mem_flags flags,
+                           ID3D11Buffer *resource, cl_int *errcode_ret);
+
+extern CL_API_ENTRY cl_mem CL_API_CALL clCreateFromD3D11Texture2DKHR(
+    cl_context context, cl_mem_flags flags, ID3D11Texture2D *resource,
+    UINT subresource, cl_int *errcode_ret);
+
+extern CL_API_ENTRY cl_mem CL_API_CALL clCreateFromD3D11Texture3DKHR(
+    cl_context context, cl_mem_flags flags, ID3D11Texture3D *resource,
+    UINT subresource, cl_int *errcode_ret);
+
+extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueAcquireD3D11ObjectsKHR(
+    cl_command_queue command_queue, cl_uint num_objects,
+    const cl_mem *mem_objects, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list, cl_event *event);
+
+extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueReleaseD3D11ObjectsKHR(
+    cl_command_queue command_queue, cl_uint num_objects,
+    const cl_mem *mem_objects, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list, cl_event *event);
+
+/* cl_khr_dx9_media_sharing */
+extern CL_API_ENTRY cl_int CL_API_CALL clGetDeviceIDsFromDX9MediaAdapterKHR(
+    cl_platform_id platform, cl_uint num_media_adapters,
+    cl_dx9_media_adapter_type_khr *media_adapter_type, void *media_adapters,
+    cl_dx9_media_adapter_set_khr media_adapter_set, cl_uint num_entries,
+    cl_device_id *devices, cl_uint *num_devices);
+
+extern CL_API_ENTRY cl_mem CL_API_CALL clCreateFromDX9MediaSurfaceKHR(
+    cl_context context, cl_mem_flags flags,
+    cl_dx9_media_adapter_type_khr adapter_type, void *surface_info,
+    cl_uint plane, cl_int *errcode_ret);
+
+extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueAcquireDX9MediaSurfacesKHR(
+    cl_command_queue command_queue, cl_uint num_objects,
+    const cl_mem *mem_objects, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list, cl_event *event);
+
+extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueReleaseDX9MediaSurfacesKHR(
+    cl_command_queue command_queue, cl_uint num_objects,
+    const cl_mem *mem_objects, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list, cl_event *event);
+
+#else
+
+/* cl_khr_d3d10_sharing */
+typedef void *cl_api_clGetDeviceIDsFromD3D10KHR;
+typedef void *cl_api_clCreateFromD3D10BufferKHR;
+typedef void *cl_api_clCreateFromD3D10Texture2DKHR;
+typedef void *cl_api_clCreateFromD3D10Texture3DKHR;
+typedef void *cl_api_clEnqueueAcquireD3D10ObjectsKHR;
+typedef void *cl_api_clEnqueueReleaseD3D10ObjectsKHR;
+
+/* cl_khr_d3d11_sharing */
+typedef void *cl_api_clGetDeviceIDsFromD3D11KHR;
+typedef void *cl_api_clCreateFromD3D11BufferKHR;
+typedef void *cl_api_clCreateFromD3D11Texture2DKHR;
+typedef void *cl_api_clCreateFromD3D11Texture3DKHR;
+typedef void *cl_api_clEnqueueAcquireD3D11ObjectsKHR;
+typedef void *cl_api_clEnqueueReleaseD3D11ObjectsKHR;
+
+/* cl_khr_dx9_media_sharing */
+typedef void *cl_api_clCreateFromDX9MediaSurfaceKHR;
+typedef void *cl_api_clEnqueueAcquireDX9MediaSurfacesKHR;
+typedef void *cl_api_clEnqueueReleaseDX9MediaSurfacesKHR;
+typedef void *cl_api_clGetDeviceIDsFromDX9MediaAdapterKHR;
+
+#endif
+
+/* OpenCL 1.1 */
+
+#ifdef CL_VERSION_1_1
+
+typedef cl_int(CL_API_CALL *cl_api_clSetEventCallback)(
+    cl_event /* event */, cl_int /* command_exec_callback_type */,
+    void(CL_CALLBACK * /* pfn_notify */)(cl_event, cl_int, void *),
+    void * /* user_data */) CL_API_SUFFIX__VERSION_1_1;
+
+typedef cl_mem(CL_API_CALL *cl_api_clCreateSubBuffer)(
+    cl_mem /* buffer */, cl_mem_flags /* flags */,
+    cl_buffer_create_type /* buffer_create_type */,
+    const void * /* buffer_create_info */,
+    cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_1;
+
+typedef
+cl_int(CL_API_CALL *cl_api_clSetMemObjectDestructorCallback)(
+    cl_mem /* memobj */,
+    void(CL_CALLBACK * /*pfn_notify*/)(cl_mem /* memobj */,
+                                       void * /*user_data*/),
+    void * /*user_data */) CL_API_SUFFIX__VERSION_1_1;
+
+typedef cl_event(CL_API_CALL *cl_api_clCreateUserEvent)(
+    cl_context /* context */,
+    cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_1;
+
+typedef cl_int(CL_API_CALL *cl_api_clSetUserEventStatus)(
+    cl_event /* event */,
+    cl_int /* execution_status */) CL_API_SUFFIX__VERSION_1_1;
+
+#else
+
+typedef void *cl_api_clSetEventCallback;
+typedef void *cl_api_clCreateSubBuffer;
+typedef void *cl_api_clSetMemObjectDestructorCallback;
+typedef void *cl_api_clCreateUserEvent;
+typedef void *cl_api_clSetUserEventStatus;
+
+#endif
+
+typedef cl_int(CL_API_CALL *cl_api_clCreateSubDevicesEXT)(
+    cl_device_id in_device,
+    const cl_device_partition_property_ext *partition_properties,
+    cl_uint num_entries, cl_device_id *out_devices, cl_uint *num_devices);
+
+typedef cl_int(CL_API_CALL *cl_api_clRetainDeviceEXT)(
+    cl_device_id device) CL_API_SUFFIX__VERSION_1_0;
+
+typedef cl_int(CL_API_CALL *cl_api_clReleaseDeviceEXT)(
+    cl_device_id device) CL_API_SUFFIX__VERSION_1_0;
+
+/* cl_khr_egl_image */
+typedef cl_mem(CL_API_CALL *cl_api_clCreateFromEGLImageKHR)(
+    cl_context context, CLeglDisplayKHR display, CLeglImageKHR image,
+    cl_mem_flags flags, const cl_egl_image_properties_khr *properties,
+    cl_int *errcode_ret);
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueAcquireEGLObjectsKHR)(
+    cl_command_queue command_queue, cl_uint num_objects,
+    const cl_mem *mem_objects, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list, cl_event *event);
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueReleaseEGLObjectsKHR)(
+    cl_command_queue command_queue, cl_uint num_objects,
+    const cl_mem *mem_objects, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list, cl_event *event);
+
+/* cl_khr_egl_event */
+typedef cl_event(CL_API_CALL *cl_api_clCreateEventFromEGLSyncKHR)(
+    cl_context context, CLeglSyncKHR sync, CLeglDisplayKHR display,
+    cl_int *errcode_ret);
+
+#ifdef CL_VERSION_2_1
+
+typedef cl_int(CL_API_CALL *cl_api_clSetDefaultDeviceCommandQueue)(
+    cl_context context, cl_device_id device,
+    cl_command_queue command_queue) CL_API_SUFFIX__VERSION_2_1;
+
+typedef cl_program(CL_API_CALL *cl_api_clCreateProgramWithIL)(
+    cl_context context, const void *il, size_t length,
+    cl_int *errcode_ret) CL_API_SUFFIX__VERSION_2_1;
+
+typedef cl_int(CL_API_CALL *cl_api_clGetKernelSubGroupInfo)(
+    cl_kernel kernel, cl_device_id device, cl_kernel_sub_group_info param_name,
+    size_t input_value_size, const void *input_value, size_t param_value_size,
+    void *param_value, size_t *param_value_size_ret) CL_API_SUFFIX__VERSION_2_1;
+
+typedef cl_kernel(CL_API_CALL *cl_api_clCloneKernel)(
+    cl_kernel source_kernel, cl_int *errcode_ret) CL_API_SUFFIX__VERSION_2_1;
+
+typedef cl_int(CL_API_CALL *cl_api_clEnqueueSVMMigrateMem)(
+    cl_command_queue command_queue, cl_uint num_svm_pointers,
+    const void **svm_pointers, const size_t *sizes,
+    cl_mem_migration_flags flags, cl_uint num_events_in_wait_list,
+    const cl_event *event_wait_list,
+    cl_event *event) CL_API_SUFFIX__VERSION_2_1;
+
+typedef cl_int(CL_API_CALL *cl_api_clGetDeviceAndHostTimer)(
+    cl_device_id device, cl_ulong *device_timestamp,
+    cl_ulong *host_timestamp) CL_API_SUFFIX__VERSION_2_1;
+
+typedef cl_int(CL_API_CALL *cl_api_clGetHostTimer)(
+    cl_device_id device, cl_ulong *host_timestamp) CL_API_SUFFIX__VERSION_2_1;
+
+#else
+
+typedef void *cl_api_clSetDefaultDeviceCommandQueue;
+typedef void *cl_api_clCreateProgramWithIL;
+typedef void *cl_api_clGetKernelSubGroupInfo;
+typedef void *cl_api_clCloneKernel;
+typedef void *cl_api_clEnqueueSVMMigrateMem;
+typedef void *cl_api_clGetDeviceAndHostTimer;
+typedef void *cl_api_clGetHostTimer;
+
+#endif
+
+/* Vendor dispatch table struture */
+
+typedef struct _cl_icd_dispatch {
+  /* OpenCL 1.0 */
+  cl_api_clGetPlatformIDs clGetPlatformIDs;
+  cl_api_clGetPlatformInfo clGetPlatformInfo;
+  cl_api_clGetDeviceIDs clGetDeviceIDs;
+  cl_api_clGetDeviceInfo clGetDeviceInfo;
+  cl_api_clCreateContext clCreateContext;
+  cl_api_clCreateContextFromType clCreateContextFromType;
+  cl_api_clRetainContext clRetainContext;
+  cl_api_clReleaseContext clReleaseContext;
+  cl_api_clGetContextInfo clGetContextInfo;
+  cl_api_clCreateCommandQueue clCreateCommandQueue;
+  cl_api_clRetainCommandQueue clRetainCommandQueue;
+  cl_api_clReleaseCommandQueue clReleaseCommandQueue;
+  cl_api_clGetCommandQueueInfo clGetCommandQueueInfo;
+  cl_api_clSetCommandQueueProperty clSetCommandQueueProperty;
+  cl_api_clCreateBuffer clCreateBuffer;
+  cl_api_clCreateImage2D clCreateImage2D;
+  cl_api_clCreateImage3D clCreateImage3D;
+  cl_api_clRetainMemObject clRetainMemObject;
+  cl_api_clReleaseMemObject clReleaseMemObject;
+  cl_api_clGetSupportedImageFormats clGetSupportedImageFormats;
+  cl_api_clGetMemObjectInfo clGetMemObjectInfo;
+  cl_api_clGetImageInfo clGetImageInfo;
+  cl_api_clCreateSampler clCreateSampler;
+  cl_api_clRetainSampler clRetainSampler;
+  cl_api_clReleaseSampler clReleaseSampler;
+  cl_api_clGetSamplerInfo clGetSamplerInfo;
+  cl_api_clCreateProgramWithSource clCreateProgramWithSource;
+  cl_api_clCreateProgramWithBinary clCreateProgramWithBinary;
+  cl_api_clRetainProgram clRetainProgram;
+  cl_api_clReleaseProgram clReleaseProgram;
+  cl_api_clBuildProgram clBuildProgram;
+  cl_api_clUnloadCompiler clUnloadCompiler;
+  cl_api_clGetProgramInfo clGetProgramInfo;
+  cl_api_clGetProgramBuildInfo clGetProgramBuildInfo;
+  cl_api_clCreateKernel clCreateKernel;
+  cl_api_clCreateKernelsInProgram clCreateKernelsInProgram;
+  cl_api_clRetainKernel clRetainKernel;
+  cl_api_clReleaseKernel clReleaseKernel;
+  cl_api_clSetKernelArg clSetKernelArg;
+  cl_api_clGetKernelInfo clGetKernelInfo;
+  cl_api_clGetKernelWorkGroupInfo clGetKernelWorkGroupInfo;
+  cl_api_clWaitForEvents clWaitForEvents;
+  cl_api_clGetEventInfo clGetEventInfo;
+  cl_api_clRetainEvent clRetainEvent;
+  cl_api_clReleaseEvent clReleaseEvent;
+  cl_api_clGetEventProfilingInfo clGetEventProfilingInfo;
+  cl_api_clFlush clFlush;
+  cl_api_clFinish clFinish;
+  cl_api_clEnqueueReadBuffer clEnqueueReadBuffer;
+  cl_api_clEnqueueWriteBuffer clEnqueueWriteBuffer;
+  cl_api_clEnqueueCopyBuffer clEnqueueCopyBuffer;
+  cl_api_clEnqueueReadImage clEnqueueReadImage;
+  cl_api_clEnqueueWriteImage clEnqueueWriteImage;
+  cl_api_clEnqueueCopyImage clEnqueueCopyImage;
+  cl_api_clEnqueueCopyImageToBuffer clEnqueueCopyImageToBuffer;
+  cl_api_clEnqueueCopyBufferToImage clEnqueueCopyBufferToImage;
+  cl_api_clEnqueueMapBuffer clEnqueueMapBuffer;
+  cl_api_clEnqueueMapImage clEnqueueMapImage;
+  cl_api_clEnqueueUnmapMemObject clEnqueueUnmapMemObject;
+  cl_api_clEnqueueNDRangeKernel clEnqueueNDRangeKernel;
+  cl_api_clEnqueueTask clEnqueueTask;
+  cl_api_clEnqueueNativeKernel clEnqueueNativeKernel;
+  cl_api_clEnqueueMarker clEnqueueMarker;
+  cl_api_clEnqueueWaitForEvents clEnqueueWaitForEvents;
+  cl_api_clEnqueueBarrier clEnqueueBarrier;
+  cl_api_clGetExtensionFunctionAddress clGetExtensionFunctionAddress;
+  cl_api_clCreateFromGLBuffer clCreateFromGLBuffer;
+  cl_api_clCreateFromGLTexture2D clCreateFromGLTexture2D;
+  cl_api_clCreateFromGLTexture3D clCreateFromGLTexture3D;
+  cl_api_clCreateFromGLRenderbuffer clCreateFromGLRenderbuffer;
+  cl_api_clGetGLObjectInfo clGetGLObjectInfo;
+  cl_api_clGetGLTextureInfo clGetGLTextureInfo;
+  cl_api_clEnqueueAcquireGLObjects clEnqueueAcquireGLObjects;
+  cl_api_clEnqueueReleaseGLObjects clEnqueueReleaseGLObjects;
+  cl_api_clGetGLContextInfoKHR clGetGLContextInfoKHR;
+
+  /* cl_khr_d3d10_sharing */
+  cl_api_clGetDeviceIDsFromD3D10KHR clGetDeviceIDsFromD3D10KHR;
+  cl_api_clCreateFromD3D10BufferKHR clCreateFromD3D10BufferKHR;
+  cl_api_clCreateFromD3D10Texture2DKHR clCreateFromD3D10Texture2DKHR;
+  cl_api_clCreateFromD3D10Texture3DKHR clCreateFromD3D10Texture3DKHR;
+  cl_api_clEnqueueAcquireD3D10ObjectsKHR clEnqueueAcquireD3D10ObjectsKHR;
+  cl_api_clEnqueueReleaseD3D10ObjectsKHR clEnqueueReleaseD3D10ObjectsKHR;
+
+  /* OpenCL 1.1 */
+  cl_api_clSetEventCallback clSetEventCallback;
+  cl_api_clCreateSubBuffer clCreateSubBuffer;
+  cl_api_clSetMemObjectDestructorCallback clSetMemObjectDestructorCallback;
+  cl_api_clCreateUserEvent clCreateUserEvent;
+  cl_api_clSetUserEventStatus clSetUserEventStatus;
+  cl_api_clEnqueueReadBufferRect clEnqueueReadBufferRect;
+  cl_api_clEnqueueWriteBufferRect clEnqueueWriteBufferRect;
+  cl_api_clEnqueueCopyBufferRect clEnqueueCopyBufferRect;
+
+  /* cl_ext_device_fission */
+  cl_api_clCreateSubDevicesEXT clCreateSubDevicesEXT;
+  cl_api_clRetainDeviceEXT clRetainDeviceEXT;
+  cl_api_clReleaseDeviceEXT clReleaseDeviceEXT;
+
+  /* cl_khr_gl_event */
+  cl_api_clCreateEventFromGLsyncKHR clCreateEventFromGLsyncKHR;
+
+  /* OpenCL 1.2 */
+  cl_api_clCreateSubDevices clCreateSubDevices;
+  cl_api_clRetainDevice clRetainDevice;
+  cl_api_clReleaseDevice clReleaseDevice;
+  cl_api_clCreateImage clCreateImage;
+  cl_api_clCreateProgramWithBuiltInKernels clCreateProgramWithBuiltInKernels;
+  cl_api_clCompileProgram clCompileProgram;
+  cl_api_clLinkProgram clLinkProgram;
+  cl_api_clUnloadPlatformCompiler clUnloadPlatformCompiler;
+  cl_api_clGetKernelArgInfo clGetKernelArgInfo;
+  cl_api_clEnqueueFillBuffer clEnqueueFillBuffer;
+  cl_api_clEnqueueFillImage clEnqueueFillImage;
+  cl_api_clEnqueueMigrateMemObjects clEnqueueMigrateMemObjects;
+  cl_api_clEnqueueMarkerWithWaitList clEnqueueMarkerWithWaitList;
+  cl_api_clEnqueueBarrierWithWaitList clEnqueueBarrierWithWaitList;
+  cl_api_clGetExtensionFunctionAddressForPlatform
+      clGetExtensionFunctionAddressForPlatform;
+  cl_api_clCreateFromGLTexture clCreateFromGLTexture;
+
+  /* cl_khr_d3d11_sharing */
+  cl_api_clGetDeviceIDsFromD3D11KHR clGetDeviceIDsFromD3D11KHR;
+  cl_api_clCreateFromD3D11BufferKHR clCreateFromD3D11BufferKHR;
+  cl_api_clCreateFromD3D11Texture2DKHR clCreateFromD3D11Texture2DKHR;
+  cl_api_clCreateFromD3D11Texture3DKHR clCreateFromD3D11Texture3DKHR;
+  cl_api_clCreateFromDX9MediaSurfaceKHR clCreateFromDX9MediaSurfaceKHR;
+  cl_api_clEnqueueAcquireD3D11ObjectsKHR clEnqueueAcquireD3D11ObjectsKHR;
+  cl_api_clEnqueueReleaseD3D11ObjectsKHR clEnqueueReleaseD3D11ObjectsKHR;
+
+  /* cl_khr_dx9_media_sharing */
+  cl_api_clGetDeviceIDsFromDX9MediaAdapterKHR
+      clGetDeviceIDsFromDX9MediaAdapterKHR;
+  cl_api_clEnqueueAcquireDX9MediaSurfacesKHR
+      clEnqueueAcquireDX9MediaSurfacesKHR;
+  cl_api_clEnqueueReleaseDX9MediaSurfacesKHR
+      clEnqueueReleaseDX9MediaSurfacesKHR;
+
+  /* cl_khr_egl_image */
+  cl_api_clCreateFromEGLImageKHR clCreateFromEGLImageKHR;
+  cl_api_clEnqueueAcquireEGLObjectsKHR clEnqueueAcquireEGLObjectsKHR;
+  cl_api_clEnqueueReleaseEGLObjectsKHR clEnqueueReleaseEGLObjectsKHR;
+
+  /* cl_khr_egl_event */
+  cl_api_clCreateEventFromEGLSyncKHR clCreateEventFromEGLSyncKHR;
+
+  /* OpenCL 2.0 */
+  cl_api_clCreateCommandQueueWithProperties clCreateCommandQueueWithProperties;
+  cl_api_clCreatePipe clCreatePipe;
+  cl_api_clGetPipeInfo clGetPipeInfo;
+  cl_api_clSVMAlloc clSVMAlloc;
+  cl_api_clSVMFree clSVMFree;
+  cl_api_clEnqueueSVMFree clEnqueueSVMFree;
+  cl_api_clEnqueueSVMMemcpy clEnqueueSVMMemcpy;
+  cl_api_clEnqueueSVMMemFill clEnqueueSVMMemFill;
+  cl_api_clEnqueueSVMMap clEnqueueSVMMap;
+  cl_api_clEnqueueSVMUnmap clEnqueueSVMUnmap;
+  cl_api_clCreateSamplerWithProperties clCreateSamplerWithProperties;
+  cl_api_clSetKernelArgSVMPointer clSetKernelArgSVMPointer;
+  cl_api_clSetKernelExecInfo clSetKernelExecInfo;
+
+  /* cl_khr_sub_groups */
+  cl_api_clGetKernelSubGroupInfoKHR clGetKernelSubGroupInfoKHR;
+
+  /* OpenCL 2.1 */
+  cl_api_clCloneKernel clCloneKernel;
+  cl_api_clCreateProgramWithIL clCreateProgramWithIL;
+  cl_api_clEnqueueSVMMigrateMem clEnqueueSVMMigrateMem;
+  cl_api_clGetDeviceAndHostTimer clGetDeviceAndHostTimer;
+  cl_api_clGetHostTimer clGetHostTimer;
+  cl_api_clGetKernelSubGroupInfo clGetKernelSubGroupInfo;
+  cl_api_clSetDefaultDeviceCommandQueue clSetDefaultDeviceCommandQueue;
+
+  /* OpenCL 2.2 */
+  cl_api_clSetProgramReleaseCallback clSetProgramReleaseCallback;
+  cl_api_clSetProgramSpecializationConstant clSetProgramSpecializationConstant;
+
+  /* OpenCL 3.0 */
+  cl_api_clCreateBufferWithProperties clCreateBufferWithProperties;
+  cl_api_clCreateImageWithProperties clCreateImageWithProperties;
+  cl_api_clSetContextDestructorCallback clSetContextDestructorCallback;
+
+} cl_icd_dispatch;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef OPENCL_CL_ICD_H */
diff --git a/OpenCL/CL/cl_layer.h b/OpenCL/CL/cl_layer.h
new file mode 100644
index 0000000..59dae75
--- /dev/null
+++ b/OpenCL/CL/cl_layer.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * OpenCL is a trademark of Apple Inc. used under license by Khronos.
+ */
+
+#ifndef OPENCL_CL_LAYER_H
+#define OPENCL_CL_LAYER_H
+
+#include <CL/cl_icd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef cl_uint cl_layer_info;
+typedef cl_uint cl_layer_api_version;
+#define CL_LAYER_API_VERSION 0x4240
+#define CL_LAYER_API_VERSION_100 100
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetLayerInfo(cl_layer_info  param_name,
+               size_t         param_value_size,
+               void          *param_value,
+               size_t        *param_value_size_ret);
+
+typedef cl_int
+(CL_API_CALL *pfn_clGetLayerInfo)(cl_layer_info  param_name,
+                                  size_t         param_value_size,
+                                  void          *param_value,
+                                  size_t        *param_value_size_ret);
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clInitLayer(cl_uint                 num_entries,
+            const cl_icd_dispatch  *target_dispatch,
+            cl_uint                *num_entries_ret,
+            const cl_icd_dispatch **layer_dispatch_ret);
+
+typedef cl_int
+(CL_API_CALL *pfn_clInitLayer)(cl_uint                 num_entries,
+                               const cl_icd_dispatch  *target_dispatch,
+                               cl_uint                *num_entries_ret,
+                               const cl_icd_dispatch **layer_dispatch_ret);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OPENCL_CL_LAYER_H */
diff --git a/OpenCL/CL/cl_platform.h b/OpenCL/CL/cl_platform.h
new file mode 100644
index 0000000..8ae655d
--- /dev/null
+++ b/OpenCL/CL/cl_platform.h
@@ -0,0 +1,1404 @@
+/*******************************************************************************
+ * Copyright (c) 2008-2020 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#ifndef __CL_PLATFORM_H
+#define __CL_PLATFORM_H
+
+#include <CL/cl_version.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(_WIN32)
+    #if !defined(CL_API_ENTRY)
+        #define CL_API_ENTRY
+    #endif
+    #if !defined(CL_API_CALL)
+        #define CL_API_CALL     __stdcall
+    #endif
+    #if !defined(CL_CALLBACK)
+        #define CL_CALLBACK     __stdcall
+    #endif
+#else
+    #if !defined(CL_API_ENTRY)
+        #define CL_API_ENTRY
+    #endif
+    #if !defined(CL_API_CALL)
+        #define CL_API_CALL
+    #endif
+    #if !defined(CL_CALLBACK)
+        #define CL_CALLBACK
+    #endif
+#endif
+
+/*
+ * Deprecation flags refer to the last version of the header in which the
+ * feature was not deprecated.
+ *
+ * E.g. VERSION_1_1_DEPRECATED means the feature is present in 1.1 without
+ * deprecation but is deprecated in versions later than 1.1.
+ */
+
+#ifndef CL_API_SUFFIX_USER
+#define CL_API_SUFFIX_USER
+#endif
+
+#ifndef CL_API_PREFIX_USER
+#define CL_API_PREFIX_USER
+#endif
+
+#define CL_API_SUFFIX_COMMON CL_API_SUFFIX_USER
+#define CL_API_PREFIX_COMMON CL_API_PREFIX_USER
+
+#define CL_API_SUFFIX__VERSION_1_0 CL_API_SUFFIX_COMMON
+#define CL_API_SUFFIX__VERSION_1_1 CL_API_SUFFIX_COMMON
+#define CL_API_SUFFIX__VERSION_1_2 CL_API_SUFFIX_COMMON
+#define CL_API_SUFFIX__VERSION_2_0 CL_API_SUFFIX_COMMON
+#define CL_API_SUFFIX__VERSION_2_1 CL_API_SUFFIX_COMMON
+#define CL_API_SUFFIX__VERSION_2_2 CL_API_SUFFIX_COMMON
+#define CL_API_SUFFIX__VERSION_3_0 CL_API_SUFFIX_COMMON
+#define CL_API_SUFFIX__EXPERIMENTAL CL_API_SUFFIX_COMMON
+
+
+#ifdef __GNUC__
+  #define CL_API_SUFFIX_DEPRECATED __attribute__((deprecated))
+  #define CL_API_PREFIX_DEPRECATED
+#elif defined(_WIN32)
+  #define CL_API_SUFFIX_DEPRECATED
+  #define CL_API_PREFIX_DEPRECATED __declspec(deprecated)
+#else
+  #define CL_API_SUFFIX_DEPRECATED
+  #define CL_API_PREFIX_DEPRECATED
+#endif
+
+#ifdef CL_USE_DEPRECATED_OPENCL_1_0_APIS
+    #define CL_API_SUFFIX__VERSION_1_0_DEPRECATED CL_API_SUFFIX_COMMON
+    #define CL_API_PREFIX__VERSION_1_0_DEPRECATED CL_API_PREFIX_COMMON
+#else
+    #define CL_API_SUFFIX__VERSION_1_0_DEPRECATED CL_API_SUFFIX_COMMON CL_API_SUFFIX_DEPRECATED
+    #define CL_API_PREFIX__VERSION_1_0_DEPRECATED CL_API_PREFIX_COMMON CL_API_PREFIX_DEPRECATED
+#endif
+
+#ifdef CL_USE_DEPRECATED_OPENCL_1_1_APIS
+    #define CL_API_SUFFIX__VERSION_1_1_DEPRECATED CL_API_SUFFIX_COMMON
+    #define CL_API_PREFIX__VERSION_1_1_DEPRECATED CL_API_PREFIX_COMMON
+#else
+    #define CL_API_SUFFIX__VERSION_1_1_DEPRECATED CL_API_SUFFIX_COMMON CL_API_SUFFIX_DEPRECATED
+    #define CL_API_PREFIX__VERSION_1_1_DEPRECATED CL_API_PREFIX_COMMON CL_API_PREFIX_DEPRECATED
+#endif
+
+#ifdef CL_USE_DEPRECATED_OPENCL_1_2_APIS
+    #define CL_API_SUFFIX__VERSION_1_2_DEPRECATED CL_API_SUFFIX_COMMON
+    #define CL_API_PREFIX__VERSION_1_2_DEPRECATED CL_API_PREFIX_COMMON
+#else
+    #define CL_API_SUFFIX__VERSION_1_2_DEPRECATED CL_API_SUFFIX_COMMON CL_API_SUFFIX_DEPRECATED
+    #define CL_API_PREFIX__VERSION_1_2_DEPRECATED CL_API_PREFIX_COMMON CL_API_PREFIX_DEPRECATED
+ #endif
+
+#ifdef CL_USE_DEPRECATED_OPENCL_2_0_APIS
+    #define CL_API_SUFFIX__VERSION_2_0_DEPRECATED CL_API_SUFFIX_COMMON
+    #define CL_API_PREFIX__VERSION_2_0_DEPRECATED CL_API_PREFIX_COMMON
+#else
+    #define CL_API_SUFFIX__VERSION_2_0_DEPRECATED CL_API_SUFFIX_COMMON CL_API_SUFFIX_DEPRECATED
+    #define CL_API_PREFIX__VERSION_2_0_DEPRECATED CL_API_PREFIX_COMMON CL_API_PREFIX_DEPRECATED
+#endif
+
+#ifdef CL_USE_DEPRECATED_OPENCL_2_1_APIS
+    #define CL_API_SUFFIX__VERSION_2_1_DEPRECATED CL_API_SUFFIX_COMMON
+    #define CL_API_PREFIX__VERSION_2_1_DEPRECATED CL_API_PREFIX_COMMON
+#else
+    #define CL_API_SUFFIX__VERSION_2_1_DEPRECATED CL_API_SUFFIX_COMMON CL_API_SUFFIX_DEPRECATED
+    #define CL_API_PREFIX__VERSION_2_1_DEPRECATED CL_API_PREFIX_COMMON CL_API_PREFIX_DEPRECATED
+#endif
+
+#ifdef CL_USE_DEPRECATED_OPENCL_2_2_APIS
+    #define CL_API_SUFFIX__VERSION_2_2_DEPRECATED CL_API_SUFFIX_COMMON
+    #define CL_API_PREFIX__VERSION_2_2_DEPRECATED CL_API_PREFIX_COMMON
+#else
+    #define CL_API_SUFFIX__VERSION_2_2_DEPRECATED CL_API_SUFFIX_COMMON CL_API_SUFFIX_DEPRECATED
+    #define CL_API_PREFIX__VERSION_2_2_DEPRECATED CL_API_PREFIX_COMMON CL_API_PREFIX_DEPRECATED
+#endif
+
+#if (defined (_WIN32) && defined(_MSC_VER))
+
+/* intptr_t is used in cl.h and provided by stddef.h in Visual C++, but not in clang */
+/* stdint.h was missing before Visual Studio 2010, include it for later versions and for clang */
+#if defined(__clang__) || _MSC_VER >= 1600
+    #include <stdint.h>
+#endif
+
+/* scalar types  */
+typedef signed   __int8         cl_char;
+typedef unsigned __int8         cl_uchar;
+typedef signed   __int16        cl_short;
+typedef unsigned __int16        cl_ushort;
+typedef signed   __int32        cl_int;
+typedef unsigned __int32        cl_uint;
+typedef signed   __int64        cl_long;
+typedef unsigned __int64        cl_ulong;
+
+typedef unsigned __int16        cl_half;
+typedef float                   cl_float;
+typedef double                  cl_double;
+
+/* Macro names and corresponding values defined by OpenCL */
+#define CL_CHAR_BIT         8
+#define CL_SCHAR_MAX        127
+#define CL_SCHAR_MIN        (-127-1)
+#define CL_CHAR_MAX         CL_SCHAR_MAX
+#define CL_CHAR_MIN         CL_SCHAR_MIN
+#define CL_UCHAR_MAX        255
+#define CL_SHRT_MAX         32767
+#define CL_SHRT_MIN         (-32767-1)
+#define CL_USHRT_MAX        65535
+#define CL_INT_MAX          2147483647
+#define CL_INT_MIN          (-2147483647-1)
+#define CL_UINT_MAX         0xffffffffU
+#define CL_LONG_MAX         ((cl_long) 0x7FFFFFFFFFFFFFFFLL)
+#define CL_LONG_MIN         ((cl_long) -0x7FFFFFFFFFFFFFFFLL - 1LL)
+#define CL_ULONG_MAX        ((cl_ulong) 0xFFFFFFFFFFFFFFFFULL)
+
+#define CL_FLT_DIG          6
+#define CL_FLT_MANT_DIG     24
+#define CL_FLT_MAX_10_EXP   +38
+#define CL_FLT_MAX_EXP      +128
+#define CL_FLT_MIN_10_EXP   -37
+#define CL_FLT_MIN_EXP      -125
+#define CL_FLT_RADIX        2
+#define CL_FLT_MAX          340282346638528859811704183484516925440.0f
+#define CL_FLT_MIN          1.175494350822287507969e-38f
+#define CL_FLT_EPSILON      1.1920928955078125e-7f
+
+#define CL_HALF_DIG          3
+#define CL_HALF_MANT_DIG     11
+#define CL_HALF_MAX_10_EXP   +4
+#define CL_HALF_MAX_EXP      +16
+#define CL_HALF_MIN_10_EXP   -4
+#define CL_HALF_MIN_EXP      -13
+#define CL_HALF_RADIX        2
+#define CL_HALF_MAX          65504.0f
+#define CL_HALF_MIN          6.103515625e-05f
+#define CL_HALF_EPSILON      9.765625e-04f
+
+#define CL_DBL_DIG          15
+#define CL_DBL_MANT_DIG     53
+#define CL_DBL_MAX_10_EXP   +308
+#define CL_DBL_MAX_EXP      +1024
+#define CL_DBL_MIN_10_EXP   -307
+#define CL_DBL_MIN_EXP      -1021
+#define CL_DBL_RADIX        2
+#define CL_DBL_MAX          1.7976931348623158e+308
+#define CL_DBL_MIN          2.225073858507201383090e-308
+#define CL_DBL_EPSILON      2.220446049250313080847e-16
+
+#define CL_M_E              2.7182818284590452354
+#define CL_M_LOG2E          1.4426950408889634074
+#define CL_M_LOG10E         0.43429448190325182765
+#define CL_M_LN2            0.69314718055994530942
+#define CL_M_LN10           2.30258509299404568402
+#define CL_M_PI             3.14159265358979323846
+#define CL_M_PI_2           1.57079632679489661923
+#define CL_M_PI_4           0.78539816339744830962
+#define CL_M_1_PI           0.31830988618379067154
+#define CL_M_2_PI           0.63661977236758134308
+#define CL_M_2_SQRTPI       1.12837916709551257390
+#define CL_M_SQRT2          1.41421356237309504880
+#define CL_M_SQRT1_2        0.70710678118654752440
+
+#define CL_M_E_F            2.718281828f
+#define CL_M_LOG2E_F        1.442695041f
+#define CL_M_LOG10E_F       0.434294482f
+#define CL_M_LN2_F          0.693147181f
+#define CL_M_LN10_F         2.302585093f
+#define CL_M_PI_F           3.141592654f
+#define CL_M_PI_2_F         1.570796327f
+#define CL_M_PI_4_F         0.785398163f
+#define CL_M_1_PI_F         0.318309886f
+#define CL_M_2_PI_F         0.636619772f
+#define CL_M_2_SQRTPI_F     1.128379167f
+#define CL_M_SQRT2_F        1.414213562f
+#define CL_M_SQRT1_2_F      0.707106781f
+
+#define CL_NAN              (CL_INFINITY - CL_INFINITY)
+#define CL_HUGE_VALF        ((cl_float) 1e50)
+#define CL_HUGE_VAL         ((cl_double) 1e500)
+#define CL_MAXFLOAT         CL_FLT_MAX
+#define CL_INFINITY         CL_HUGE_VALF
+
+#else
+
+#include <stdint.h>
+
+/* scalar types  */
+typedef int8_t          cl_char;
+typedef uint8_t         cl_uchar;
+typedef int16_t         cl_short;
+typedef uint16_t        cl_ushort;
+typedef int32_t         cl_int;
+typedef uint32_t        cl_uint;
+typedef int64_t         cl_long;
+typedef uint64_t        cl_ulong;
+
+typedef uint16_t        cl_half;
+typedef float           cl_float;
+typedef double          cl_double;
+
+/* Macro names and corresponding values defined by OpenCL */
+#define CL_CHAR_BIT         8
+#define CL_SCHAR_MAX        127
+#define CL_SCHAR_MIN        (-127-1)
+#define CL_CHAR_MAX         CL_SCHAR_MAX
+#define CL_CHAR_MIN         CL_SCHAR_MIN
+#define CL_UCHAR_MAX        255
+#define CL_SHRT_MAX         32767
+#define CL_SHRT_MIN         (-32767-1)
+#define CL_USHRT_MAX        65535
+#define CL_INT_MAX          2147483647
+#define CL_INT_MIN          (-2147483647-1)
+#define CL_UINT_MAX         0xffffffffU
+#define CL_LONG_MAX         ((cl_long) 0x7FFFFFFFFFFFFFFFLL)
+#define CL_LONG_MIN         ((cl_long) -0x7FFFFFFFFFFFFFFFLL - 1LL)
+#define CL_ULONG_MAX        ((cl_ulong) 0xFFFFFFFFFFFFFFFFULL)
+
+#define CL_FLT_DIG          6
+#define CL_FLT_MANT_DIG     24
+#define CL_FLT_MAX_10_EXP   +38
+#define CL_FLT_MAX_EXP      +128
+#define CL_FLT_MIN_10_EXP   -37
+#define CL_FLT_MIN_EXP      -125
+#define CL_FLT_RADIX        2
+#define CL_FLT_MAX          340282346638528859811704183484516925440.0f
+#define CL_FLT_MIN          1.175494350822287507969e-38f
+#define CL_FLT_EPSILON      1.1920928955078125e-7f
+
+#define CL_HALF_DIG          3
+#define CL_HALF_MANT_DIG     11
+#define CL_HALF_MAX_10_EXP   +4
+#define CL_HALF_MAX_EXP      +16
+#define CL_HALF_MIN_10_EXP   -4
+#define CL_HALF_MIN_EXP      -13
+#define CL_HALF_RADIX        2
+#define CL_HALF_MAX          65504.0f
+#define CL_HALF_MIN          6.103515625e-05f
+#define CL_HALF_EPSILON      9.765625e-04f
+
+#define CL_DBL_DIG          15
+#define CL_DBL_MANT_DIG     53
+#define CL_DBL_MAX_10_EXP   +308
+#define CL_DBL_MAX_EXP      +1024
+#define CL_DBL_MIN_10_EXP   -307
+#define CL_DBL_MIN_EXP      -1021
+#define CL_DBL_RADIX        2
+#define CL_DBL_MAX          179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0
+#define CL_DBL_MIN          2.225073858507201383090e-308
+#define CL_DBL_EPSILON      2.220446049250313080847e-16
+
+#define CL_M_E              2.7182818284590452354
+#define CL_M_LOG2E          1.4426950408889634074
+#define CL_M_LOG10E         0.43429448190325182765
+#define CL_M_LN2            0.69314718055994530942
+#define CL_M_LN10           2.30258509299404568402
+#define CL_M_PI             3.14159265358979323846
+#define CL_M_PI_2           1.57079632679489661923
+#define CL_M_PI_4           0.78539816339744830962
+#define CL_M_1_PI           0.31830988618379067154
+#define CL_M_2_PI           0.63661977236758134308
+#define CL_M_2_SQRTPI       1.12837916709551257390
+#define CL_M_SQRT2          1.41421356237309504880
+#define CL_M_SQRT1_2        0.70710678118654752440
+
+#define CL_M_E_F            2.718281828f
+#define CL_M_LOG2E_F        1.442695041f
+#define CL_M_LOG10E_F       0.434294482f
+#define CL_M_LN2_F          0.693147181f
+#define CL_M_LN10_F         2.302585093f
+#define CL_M_PI_F           3.141592654f
+#define CL_M_PI_2_F         1.570796327f
+#define CL_M_PI_4_F         0.785398163f
+#define CL_M_1_PI_F         0.318309886f
+#define CL_M_2_PI_F         0.636619772f
+#define CL_M_2_SQRTPI_F     1.128379167f
+#define CL_M_SQRT2_F        1.414213562f
+#define CL_M_SQRT1_2_F      0.707106781f
+
+#if defined( __GNUC__ )
+   #define CL_HUGE_VALF     __builtin_huge_valf()
+   #define CL_HUGE_VAL      __builtin_huge_val()
+   #define CL_NAN           __builtin_nanf( "" )
+#else
+   #define CL_HUGE_VALF     ((cl_float) 1e50)
+   #define CL_HUGE_VAL      ((cl_double) 1e500)
+   float nanf( const char * );
+   #define CL_NAN           nanf( "" )
+#endif
+#define CL_MAXFLOAT         CL_FLT_MAX
+#define CL_INFINITY         CL_HUGE_VALF
+
+#endif
+
+#include <stddef.h>
+
+/* Mirror types to GL types. Mirror types allow us to avoid deciding which 87s to load based on whether we are using GL or GLES here. */
+typedef unsigned int cl_GLuint;
+typedef int          cl_GLint;
+typedef unsigned int cl_GLenum;
+
+/*
+ * Vector types
+ *
+ *  Note:   OpenCL requires that all types be naturally aligned.
+ *          This means that vector types must be naturally aligned.
+ *          For example, a vector of four floats must be aligned to
+ *          a 16 byte boundary (calculated as 4 * the natural 4-byte
+ *          alignment of the float).  The alignment qualifiers here
+ *          will only function properly if your compiler supports them
+ *          and if you don't actively work to defeat them.  For example,
+ *          in order for a cl_float4 to be 16 byte aligned in a struct,
+ *          the start of the struct must itself be 16-byte aligned.
+ *
+ *          Maintaining proper alignment is the user's responsibility.
+ */
+
+/* Define basic vector types */
+#if defined( __VEC__ )
+  #if !defined(__clang__)
+     #include <altivec.h>   /* may be omitted depending on compiler. AltiVec spec provides no way to detect whether the header is required. */
+  #endif
+   typedef __vector unsigned char     __cl_uchar16;
+   typedef __vector signed char       __cl_char16;
+   typedef __vector unsigned short    __cl_ushort8;
+   typedef __vector signed short      __cl_short8;
+   typedef __vector unsigned int      __cl_uint4;
+   typedef __vector signed int        __cl_int4;
+   typedef __vector float             __cl_float4;
+   #define  __CL_UCHAR16__  1
+   #define  __CL_CHAR16__   1
+   #define  __CL_USHORT8__  1
+   #define  __CL_SHORT8__   1
+   #define  __CL_UINT4__    1
+   #define  __CL_INT4__     1
+   #define  __CL_FLOAT4__   1
+#endif
+
+#if defined( __SSE__ )
+    #if defined( __MINGW64__ )
+        #include <intrin.h>
+    #else
+        #include <xmmintrin.h>
+    #endif
+    #if defined( __GNUC__ )
+        typedef float __cl_float4   __attribute__((vector_size(16)));
+    #else
+        typedef __m128 __cl_float4;
+    #endif
+    #define __CL_FLOAT4__   1
+#endif
+
+#if defined( __SSE2__ )
+    #if defined( __MINGW64__ )
+        #include <intrin.h>
+    #else
+        #include <emmintrin.h>
+    #endif
+    #if defined( __GNUC__ )
+        typedef cl_uchar    __cl_uchar16    __attribute__((vector_size(16)));
+        typedef cl_char     __cl_char16     __attribute__((vector_size(16)));
+        typedef cl_ushort   __cl_ushort8    __attribute__((vector_size(16)));
+        typedef cl_short    __cl_short8     __attribute__((vector_size(16)));
+        typedef cl_uint     __cl_uint4      __attribute__((vector_size(16)));
+        typedef cl_int      __cl_int4       __attribute__((vector_size(16)));
+        typedef cl_ulong    __cl_ulong2     __attribute__((vector_size(16)));
+        typedef cl_long     __cl_long2      __attribute__((vector_size(16)));
+        typedef cl_double   __cl_double2    __attribute__((vector_size(16)));
+    #else
+        typedef __m128i __cl_uchar16;
+        typedef __m128i __cl_char16;
+        typedef __m128i __cl_ushort8;
+        typedef __m128i __cl_short8;
+        typedef __m128i __cl_uint4;
+        typedef __m128i __cl_int4;
+        typedef __m128i __cl_ulong2;
+        typedef __m128i __cl_long2;
+        typedef __m128d __cl_double2;
+    #endif
+    #define __CL_UCHAR16__  1
+    #define __CL_CHAR16__   1
+    #define __CL_USHORT8__  1
+    #define __CL_SHORT8__   1
+    #define __CL_INT4__     1
+    #define __CL_UINT4__    1
+    #define __CL_ULONG2__   1
+    #define __CL_LONG2__    1
+    #define __CL_DOUBLE2__  1
+#endif
+
+#if defined( __MMX__ )
+    #include <mmintrin.h>
+    #if defined( __GNUC__ )
+        typedef cl_uchar    __cl_uchar8     __attribute__((vector_size(8)));
+        typedef cl_char     __cl_char8      __attribute__((vector_size(8)));
+        typedef cl_ushort   __cl_ushort4    __attribute__((vector_size(8)));
+        typedef cl_short    __cl_short4     __attribute__((vector_size(8)));
+        typedef cl_uint     __cl_uint2      __attribute__((vector_size(8)));
+        typedef cl_int      __cl_int2       __attribute__((vector_size(8)));
+        typedef cl_ulong    __cl_ulong1     __attribute__((vector_size(8)));
+        typedef cl_long     __cl_long1      __attribute__((vector_size(8)));
+        typedef cl_float    __cl_float2     __attribute__((vector_size(8)));
+    #else
+        typedef __m64       __cl_uchar8;
+        typedef __m64       __cl_char8;
+        typedef __m64       __cl_ushort4;
+        typedef __m64       __cl_short4;
+        typedef __m64       __cl_uint2;
+        typedef __m64       __cl_int2;
+        typedef __m64       __cl_ulong1;
+        typedef __m64       __cl_long1;
+        typedef __m64       __cl_float2;
+    #endif
+    #define __CL_UCHAR8__   1
+    #define __CL_CHAR8__    1
+    #define __CL_USHORT4__  1
+    #define __CL_SHORT4__   1
+    #define __CL_INT2__     1
+    #define __CL_UINT2__    1
+    #define __CL_ULONG1__   1
+    #define __CL_LONG1__    1
+    #define __CL_FLOAT2__   1
+#endif
+
+#if defined( __AVX__ )
+    #if defined( __MINGW64__ )
+        #include <intrin.h>
+    #else
+        #include <immintrin.h>
+    #endif
+    #if defined( __GNUC__ )
+        typedef cl_float    __cl_float8     __attribute__((vector_size(32)));
+        typedef cl_double   __cl_double4    __attribute__((vector_size(32)));
+    #else
+        typedef __m256      __cl_float8;
+        typedef __m256d     __cl_double4;
+    #endif
+    #define __CL_FLOAT8__   1
+    #define __CL_DOUBLE4__  1
+#endif
+
+/* Define capabilities for anonymous struct members. */
+#if !defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+#define  __CL_HAS_ANON_STRUCT__ 1
+#define  __CL_ANON_STRUCT__
+#elif defined( __GNUC__) && ! defined( __STRICT_ANSI__ )
+#define  __CL_HAS_ANON_STRUCT__ 1
+#define  __CL_ANON_STRUCT__ __extension__
+#elif defined( _WIN32) && defined(_MSC_VER) && ! defined(__STDC__)
+    #if _MSC_VER >= 1500
+   /* Microsoft Developer Studio 2008 supports anonymous structs, but
+    * complains by default. */
+    #define  __CL_HAS_ANON_STRUCT__ 1
+    #define  __CL_ANON_STRUCT__
+   /* Disable warning C4201: nonstandard extension used : nameless
+    * struct/union */
+    #pragma warning( push )
+    #pragma warning( disable : 4201 )
+    #endif
+#else
+#define  __CL_HAS_ANON_STRUCT__ 0
+#define  __CL_ANON_STRUCT__
+#endif
+
+/* Define alignment keys */
+#if defined( __GNUC__ ) || defined(__INTEGRITY)
+    #define CL_ALIGNED(_x)          __attribute__ ((aligned(_x)))
+#elif defined( _WIN32) && (_MSC_VER)
+    /* Alignment keys neutered on windows because MSVC can't swallow function arguments with alignment requirements     */
+    /* http://msdn.microsoft.com/en-us/library/373ak2y1%28VS.71%29.aspx                                                 */
+    /* #include <crtdefs.h>                                                                                             */
+    /* #define CL_ALIGNED(_x)          _CRT_ALIGN(_x)                                                                   */
+    #define CL_ALIGNED(_x)
+#else
+   #warning  Need to implement some method to align data here
+   #define  CL_ALIGNED(_x)
+#endif
+
+/* Indicate whether .xyzw, .s0123 and .hi.lo are supported */
+#if __CL_HAS_ANON_STRUCT__
+    /* .xyzw and .s0123...{f|F} are supported */
+    #define CL_HAS_NAMED_VECTOR_FIELDS 1
+    /* .hi and .lo are supported */
+    #define CL_HAS_HI_LO_VECTOR_FIELDS 1
+#endif
+
+/* Define cl_vector types */
+
+/* ---- cl_charn ---- */
+typedef union
+{
+    cl_char  CL_ALIGNED(2) s[2];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_char  x, y; };
+   __CL_ANON_STRUCT__ struct{ cl_char  s0, s1; };
+   __CL_ANON_STRUCT__ struct{ cl_char  lo, hi; };
+#endif
+#if defined( __CL_CHAR2__)
+    __cl_char2     v2;
+#endif
+}cl_char2;
+
+typedef union
+{
+    cl_char  CL_ALIGNED(4) s[4];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_char  x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_char  s0, s1, s2, s3; };
+   __CL_ANON_STRUCT__ struct{ cl_char2 lo, hi; };
+#endif
+#if defined( __CL_CHAR2__)
+    __cl_char2     v2[2];
+#endif
+#if defined( __CL_CHAR4__)
+    __cl_char4     v4;
+#endif
+}cl_char4;
+
+/* cl_char3 is identical in size, alignment and behavior to cl_char4. See section 6.1.5. */
+typedef  cl_char4  cl_char3;
+
+typedef union
+{
+    cl_char   CL_ALIGNED(8) s[8];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_char  x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_char  s0, s1, s2, s3, s4, s5, s6, s7; };
+   __CL_ANON_STRUCT__ struct{ cl_char4 lo, hi; };
+#endif
+#if defined( __CL_CHAR2__)
+    __cl_char2     v2[4];
+#endif
+#if defined( __CL_CHAR4__)
+    __cl_char4     v4[2];
+#endif
+#if defined( __CL_CHAR8__ )
+    __cl_char8     v8;
+#endif
+}cl_char8;
+
+typedef union
+{
+    cl_char  CL_ALIGNED(16) s[16];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_char  x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; };
+   __CL_ANON_STRUCT__ struct{ cl_char  s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; };
+   __CL_ANON_STRUCT__ struct{ cl_char8 lo, hi; };
+#endif
+#if defined( __CL_CHAR2__)
+    __cl_char2     v2[8];
+#endif
+#if defined( __CL_CHAR4__)
+    __cl_char4     v4[4];
+#endif
+#if defined( __CL_CHAR8__ )
+    __cl_char8     v8[2];
+#endif
+#if defined( __CL_CHAR16__ )
+    __cl_char16    v16;
+#endif
+}cl_char16;
+
+
+/* ---- cl_ucharn ---- */
+typedef union
+{
+    cl_uchar  CL_ALIGNED(2) s[2];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_uchar  x, y; };
+   __CL_ANON_STRUCT__ struct{ cl_uchar  s0, s1; };
+   __CL_ANON_STRUCT__ struct{ cl_uchar  lo, hi; };
+#endif
+#if defined( __cl_uchar2__)
+    __cl_uchar2     v2;
+#endif
+}cl_uchar2;
+
+typedef union
+{
+    cl_uchar  CL_ALIGNED(4) s[4];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_uchar  x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_uchar  s0, s1, s2, s3; };
+   __CL_ANON_STRUCT__ struct{ cl_uchar2 lo, hi; };
+#endif
+#if defined( __CL_UCHAR2__)
+    __cl_uchar2     v2[2];
+#endif
+#if defined( __CL_UCHAR4__)
+    __cl_uchar4     v4;
+#endif
+}cl_uchar4;
+
+/* cl_uchar3 is identical in size, alignment and behavior to cl_uchar4. See section 6.1.5. */
+typedef  cl_uchar4  cl_uchar3;
+
+typedef union
+{
+    cl_uchar   CL_ALIGNED(8) s[8];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_uchar  x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_uchar  s0, s1, s2, s3, s4, s5, s6, s7; };
+   __CL_ANON_STRUCT__ struct{ cl_uchar4 lo, hi; };
+#endif
+#if defined( __CL_UCHAR2__)
+    __cl_uchar2     v2[4];
+#endif
+#if defined( __CL_UCHAR4__)
+    __cl_uchar4     v4[2];
+#endif
+#if defined( __CL_UCHAR8__ )
+    __cl_uchar8     v8;
+#endif
+}cl_uchar8;
+
+typedef union
+{
+    cl_uchar  CL_ALIGNED(16) s[16];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_uchar  x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; };
+   __CL_ANON_STRUCT__ struct{ cl_uchar  s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; };
+   __CL_ANON_STRUCT__ struct{ cl_uchar8 lo, hi; };
+#endif
+#if defined( __CL_UCHAR2__)
+    __cl_uchar2     v2[8];
+#endif
+#if defined( __CL_UCHAR4__)
+    __cl_uchar4     v4[4];
+#endif
+#if defined( __CL_UCHAR8__ )
+    __cl_uchar8     v8[2];
+#endif
+#if defined( __CL_UCHAR16__ )
+    __cl_uchar16    v16;
+#endif
+}cl_uchar16;
+
+
+/* ---- cl_shortn ---- */
+typedef union
+{
+    cl_short  CL_ALIGNED(4) s[2];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_short  x, y; };
+   __CL_ANON_STRUCT__ struct{ cl_short  s0, s1; };
+   __CL_ANON_STRUCT__ struct{ cl_short  lo, hi; };
+#endif
+#if defined( __CL_SHORT2__)
+    __cl_short2     v2;
+#endif
+}cl_short2;
+
+typedef union
+{
+    cl_short  CL_ALIGNED(8) s[4];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_short  x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_short  s0, s1, s2, s3; };
+   __CL_ANON_STRUCT__ struct{ cl_short2 lo, hi; };
+#endif
+#if defined( __CL_SHORT2__)
+    __cl_short2     v2[2];
+#endif
+#if defined( __CL_SHORT4__)
+    __cl_short4     v4;
+#endif
+}cl_short4;
+
+/* cl_short3 is identical in size, alignment and behavior to cl_short4. See section 6.1.5. */
+typedef  cl_short4  cl_short3;
+
+typedef union
+{
+    cl_short   CL_ALIGNED(16) s[8];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_short  x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_short  s0, s1, s2, s3, s4, s5, s6, s7; };
+   __CL_ANON_STRUCT__ struct{ cl_short4 lo, hi; };
+#endif
+#if defined( __CL_SHORT2__)
+    __cl_short2     v2[4];
+#endif
+#if defined( __CL_SHORT4__)
+    __cl_short4     v4[2];
+#endif
+#if defined( __CL_SHORT8__ )
+    __cl_short8     v8;
+#endif
+}cl_short8;
+
+typedef union
+{
+    cl_short  CL_ALIGNED(32) s[16];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_short  x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; };
+   __CL_ANON_STRUCT__ struct{ cl_short  s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; };
+   __CL_ANON_STRUCT__ struct{ cl_short8 lo, hi; };
+#endif
+#if defined( __CL_SHORT2__)
+    __cl_short2     v2[8];
+#endif
+#if defined( __CL_SHORT4__)
+    __cl_short4     v4[4];
+#endif
+#if defined( __CL_SHORT8__ )
+    __cl_short8     v8[2];
+#endif
+#if defined( __CL_SHORT16__ )
+    __cl_short16    v16;
+#endif
+}cl_short16;
+
+
+/* ---- cl_ushortn ---- */
+typedef union
+{
+    cl_ushort  CL_ALIGNED(4) s[2];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_ushort  x, y; };
+   __CL_ANON_STRUCT__ struct{ cl_ushort  s0, s1; };
+   __CL_ANON_STRUCT__ struct{ cl_ushort  lo, hi; };
+#endif
+#if defined( __CL_USHORT2__)
+    __cl_ushort2     v2;
+#endif
+}cl_ushort2;
+
+typedef union
+{
+    cl_ushort  CL_ALIGNED(8) s[4];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_ushort  x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_ushort  s0, s1, s2, s3; };
+   __CL_ANON_STRUCT__ struct{ cl_ushort2 lo, hi; };
+#endif
+#if defined( __CL_USHORT2__)
+    __cl_ushort2     v2[2];
+#endif
+#if defined( __CL_USHORT4__)
+    __cl_ushort4     v4;
+#endif
+}cl_ushort4;
+
+/* cl_ushort3 is identical in size, alignment and behavior to cl_ushort4. See section 6.1.5. */
+typedef  cl_ushort4  cl_ushort3;
+
+typedef union
+{
+    cl_ushort   CL_ALIGNED(16) s[8];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_ushort  x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_ushort  s0, s1, s2, s3, s4, s5, s6, s7; };
+   __CL_ANON_STRUCT__ struct{ cl_ushort4 lo, hi; };
+#endif
+#if defined( __CL_USHORT2__)
+    __cl_ushort2     v2[4];
+#endif
+#if defined( __CL_USHORT4__)
+    __cl_ushort4     v4[2];
+#endif
+#if defined( __CL_USHORT8__ )
+    __cl_ushort8     v8;
+#endif
+}cl_ushort8;
+
+typedef union
+{
+    cl_ushort  CL_ALIGNED(32) s[16];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_ushort  x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; };
+   __CL_ANON_STRUCT__ struct{ cl_ushort  s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; };
+   __CL_ANON_STRUCT__ struct{ cl_ushort8 lo, hi; };
+#endif
+#if defined( __CL_USHORT2__)
+    __cl_ushort2     v2[8];
+#endif
+#if defined( __CL_USHORT4__)
+    __cl_ushort4     v4[4];
+#endif
+#if defined( __CL_USHORT8__ )
+    __cl_ushort8     v8[2];
+#endif
+#if defined( __CL_USHORT16__ )
+    __cl_ushort16    v16;
+#endif
+}cl_ushort16;
+
+
+/* ---- cl_halfn ---- */
+typedef union
+{
+    cl_half  CL_ALIGNED(4) s[2];
+#if __CL_HAS_ANON_STRUCT__
+    __CL_ANON_STRUCT__ struct{ cl_half  x, y; };
+    __CL_ANON_STRUCT__ struct{ cl_half  s0, s1; };
+    __CL_ANON_STRUCT__ struct{ cl_half  lo, hi; };
+#endif
+#if defined( __CL_HALF2__)
+    __cl_half2     v2;
+#endif
+}cl_half2;
+
+typedef union
+{
+    cl_half  CL_ALIGNED(8) s[4];
+#if __CL_HAS_ANON_STRUCT__
+    __CL_ANON_STRUCT__ struct{ cl_half  x, y, z, w; };
+    __CL_ANON_STRUCT__ struct{ cl_half  s0, s1, s2, s3; };
+    __CL_ANON_STRUCT__ struct{ cl_half2 lo, hi; };
+#endif
+#if defined( __CL_HALF2__)
+    __cl_half2     v2[2];
+#endif
+#if defined( __CL_HALF4__)
+    __cl_half4     v4;
+#endif
+}cl_half4;
+
+/* cl_half3 is identical in size, alignment and behavior to cl_half4. See section 6.1.5. */
+typedef  cl_half4  cl_half3;
+
+typedef union
+{
+    cl_half   CL_ALIGNED(16) s[8];
+#if __CL_HAS_ANON_STRUCT__
+    __CL_ANON_STRUCT__ struct{ cl_half  x, y, z, w; };
+    __CL_ANON_STRUCT__ struct{ cl_half  s0, s1, s2, s3, s4, s5, s6, s7; };
+    __CL_ANON_STRUCT__ struct{ cl_half4 lo, hi; };
+#endif
+#if defined( __CL_HALF2__)
+    __cl_half2     v2[4];
+#endif
+#if defined( __CL_HALF4__)
+    __cl_half4     v4[2];
+#endif
+#if defined( __CL_HALF8__ )
+    __cl_half8     v8;
+#endif
+}cl_half8;
+
+typedef union
+{
+    cl_half  CL_ALIGNED(32) s[16];
+#if __CL_HAS_ANON_STRUCT__
+    __CL_ANON_STRUCT__ struct{ cl_half  x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; };
+    __CL_ANON_STRUCT__ struct{ cl_half  s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; };
+    __CL_ANON_STRUCT__ struct{ cl_half8 lo, hi; };
+#endif
+#if defined( __CL_HALF2__)
+    __cl_half2     v2[8];
+#endif
+#if defined( __CL_HALF4__)
+    __cl_half4     v4[4];
+#endif
+#if defined( __CL_HALF8__ )
+    __cl_half8     v8[2];
+#endif
+#if defined( __CL_HALF16__ )
+    __cl_half16    v16;
+#endif
+}cl_half16;
+
+/* ---- cl_intn ---- */
+typedef union
+{
+    cl_int  CL_ALIGNED(8) s[2];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_int  x, y; };
+   __CL_ANON_STRUCT__ struct{ cl_int  s0, s1; };
+   __CL_ANON_STRUCT__ struct{ cl_int  lo, hi; };
+#endif
+#if defined( __CL_INT2__)
+    __cl_int2     v2;
+#endif
+}cl_int2;
+
+typedef union
+{
+    cl_int  CL_ALIGNED(16) s[4];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_int  x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_int  s0, s1, s2, s3; };
+   __CL_ANON_STRUCT__ struct{ cl_int2 lo, hi; };
+#endif
+#if defined( __CL_INT2__)
+    __cl_int2     v2[2];
+#endif
+#if defined( __CL_INT4__)
+    __cl_int4     v4;
+#endif
+}cl_int4;
+
+/* cl_int3 is identical in size, alignment and behavior to cl_int4. See section 6.1.5. */
+typedef  cl_int4  cl_int3;
+
+typedef union
+{
+    cl_int   CL_ALIGNED(32) s[8];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_int  x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_int  s0, s1, s2, s3, s4, s5, s6, s7; };
+   __CL_ANON_STRUCT__ struct{ cl_int4 lo, hi; };
+#endif
+#if defined( __CL_INT2__)
+    __cl_int2     v2[4];
+#endif
+#if defined( __CL_INT4__)
+    __cl_int4     v4[2];
+#endif
+#if defined( __CL_INT8__ )
+    __cl_int8     v8;
+#endif
+}cl_int8;
+
+typedef union
+{
+    cl_int  CL_ALIGNED(64) s[16];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_int  x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; };
+   __CL_ANON_STRUCT__ struct{ cl_int  s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; };
+   __CL_ANON_STRUCT__ struct{ cl_int8 lo, hi; };
+#endif
+#if defined( __CL_INT2__)
+    __cl_int2     v2[8];
+#endif
+#if defined( __CL_INT4__)
+    __cl_int4     v4[4];
+#endif
+#if defined( __CL_INT8__ )
+    __cl_int8     v8[2];
+#endif
+#if defined( __CL_INT16__ )
+    __cl_int16    v16;
+#endif
+}cl_int16;
+
+
+/* ---- cl_uintn ---- */
+typedef union
+{
+    cl_uint  CL_ALIGNED(8) s[2];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_uint  x, y; };
+   __CL_ANON_STRUCT__ struct{ cl_uint  s0, s1; };
+   __CL_ANON_STRUCT__ struct{ cl_uint  lo, hi; };
+#endif
+#if defined( __CL_UINT2__)
+    __cl_uint2     v2;
+#endif
+}cl_uint2;
+
+typedef union
+{
+    cl_uint  CL_ALIGNED(16) s[4];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_uint  x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_uint  s0, s1, s2, s3; };
+   __CL_ANON_STRUCT__ struct{ cl_uint2 lo, hi; };
+#endif
+#if defined( __CL_UINT2__)
+    __cl_uint2     v2[2];
+#endif
+#if defined( __CL_UINT4__)
+    __cl_uint4     v4;
+#endif
+}cl_uint4;
+
+/* cl_uint3 is identical in size, alignment and behavior to cl_uint4. See section 6.1.5. */
+typedef  cl_uint4  cl_uint3;
+
+typedef union
+{
+    cl_uint   CL_ALIGNED(32) s[8];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_uint  x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_uint  s0, s1, s2, s3, s4, s5, s6, s7; };
+   __CL_ANON_STRUCT__ struct{ cl_uint4 lo, hi; };
+#endif
+#if defined( __CL_UINT2__)
+    __cl_uint2     v2[4];
+#endif
+#if defined( __CL_UINT4__)
+    __cl_uint4     v4[2];
+#endif
+#if defined( __CL_UINT8__ )
+    __cl_uint8     v8;
+#endif
+}cl_uint8;
+
+typedef union
+{
+    cl_uint  CL_ALIGNED(64) s[16];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_uint  x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; };
+   __CL_ANON_STRUCT__ struct{ cl_uint  s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; };
+   __CL_ANON_STRUCT__ struct{ cl_uint8 lo, hi; };
+#endif
+#if defined( __CL_UINT2__)
+    __cl_uint2     v2[8];
+#endif
+#if defined( __CL_UINT4__)
+    __cl_uint4     v4[4];
+#endif
+#if defined( __CL_UINT8__ )
+    __cl_uint8     v8[2];
+#endif
+#if defined( __CL_UINT16__ )
+    __cl_uint16    v16;
+#endif
+}cl_uint16;
+
+/* ---- cl_longn ---- */
+typedef union
+{
+    cl_long  CL_ALIGNED(16) s[2];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_long  x, y; };
+   __CL_ANON_STRUCT__ struct{ cl_long  s0, s1; };
+   __CL_ANON_STRUCT__ struct{ cl_long  lo, hi; };
+#endif
+#if defined( __CL_LONG2__)
+    __cl_long2     v2;
+#endif
+}cl_long2;
+
+typedef union
+{
+    cl_long  CL_ALIGNED(32) s[4];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_long  x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_long  s0, s1, s2, s3; };
+   __CL_ANON_STRUCT__ struct{ cl_long2 lo, hi; };
+#endif
+#if defined( __CL_LONG2__)
+    __cl_long2     v2[2];
+#endif
+#if defined( __CL_LONG4__)
+    __cl_long4     v4;
+#endif
+}cl_long4;
+
+/* cl_long3 is identical in size, alignment and behavior to cl_long4. See section 6.1.5. */
+typedef  cl_long4  cl_long3;
+
+typedef union
+{
+    cl_long   CL_ALIGNED(64) s[8];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_long  x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_long  s0, s1, s2, s3, s4, s5, s6, s7; };
+   __CL_ANON_STRUCT__ struct{ cl_long4 lo, hi; };
+#endif
+#if defined( __CL_LONG2__)
+    __cl_long2     v2[4];
+#endif
+#if defined( __CL_LONG4__)
+    __cl_long4     v4[2];
+#endif
+#if defined( __CL_LONG8__ )
+    __cl_long8     v8;
+#endif
+}cl_long8;
+
+typedef union
+{
+    cl_long  CL_ALIGNED(128) s[16];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_long  x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; };
+   __CL_ANON_STRUCT__ struct{ cl_long  s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; };
+   __CL_ANON_STRUCT__ struct{ cl_long8 lo, hi; };
+#endif
+#if defined( __CL_LONG2__)
+    __cl_long2     v2[8];
+#endif
+#if defined( __CL_LONG4__)
+    __cl_long4     v4[4];
+#endif
+#if defined( __CL_LONG8__ )
+    __cl_long8     v8[2];
+#endif
+#if defined( __CL_LONG16__ )
+    __cl_long16    v16;
+#endif
+}cl_long16;
+
+
+/* ---- cl_ulongn ---- */
+typedef union
+{
+    cl_ulong  CL_ALIGNED(16) s[2];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_ulong  x, y; };
+   __CL_ANON_STRUCT__ struct{ cl_ulong  s0, s1; };
+   __CL_ANON_STRUCT__ struct{ cl_ulong  lo, hi; };
+#endif
+#if defined( __CL_ULONG2__)
+    __cl_ulong2     v2;
+#endif
+}cl_ulong2;
+
+typedef union
+{
+    cl_ulong  CL_ALIGNED(32) s[4];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_ulong  x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_ulong  s0, s1, s2, s3; };
+   __CL_ANON_STRUCT__ struct{ cl_ulong2 lo, hi; };
+#endif
+#if defined( __CL_ULONG2__)
+    __cl_ulong2     v2[2];
+#endif
+#if defined( __CL_ULONG4__)
+    __cl_ulong4     v4;
+#endif
+}cl_ulong4;
+
+/* cl_ulong3 is identical in size, alignment and behavior to cl_ulong4. See section 6.1.5. */
+typedef  cl_ulong4  cl_ulong3;
+
+typedef union
+{
+    cl_ulong   CL_ALIGNED(64) s[8];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_ulong  x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_ulong  s0, s1, s2, s3, s4, s5, s6, s7; };
+   __CL_ANON_STRUCT__ struct{ cl_ulong4 lo, hi; };
+#endif
+#if defined( __CL_ULONG2__)
+    __cl_ulong2     v2[4];
+#endif
+#if defined( __CL_ULONG4__)
+    __cl_ulong4     v4[2];
+#endif
+#if defined( __CL_ULONG8__ )
+    __cl_ulong8     v8;
+#endif
+}cl_ulong8;
+
+typedef union
+{
+    cl_ulong  CL_ALIGNED(128) s[16];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_ulong  x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; };
+   __CL_ANON_STRUCT__ struct{ cl_ulong  s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; };
+   __CL_ANON_STRUCT__ struct{ cl_ulong8 lo, hi; };
+#endif
+#if defined( __CL_ULONG2__)
+    __cl_ulong2     v2[8];
+#endif
+#if defined( __CL_ULONG4__)
+    __cl_ulong4     v4[4];
+#endif
+#if defined( __CL_ULONG8__ )
+    __cl_ulong8     v8[2];
+#endif
+#if defined( __CL_ULONG16__ )
+    __cl_ulong16    v16;
+#endif
+}cl_ulong16;
+
+
+/* --- cl_floatn ---- */
+
+typedef union
+{
+    cl_float  CL_ALIGNED(8) s[2];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_float  x, y; };
+   __CL_ANON_STRUCT__ struct{ cl_float  s0, s1; };
+   __CL_ANON_STRUCT__ struct{ cl_float  lo, hi; };
+#endif
+#if defined( __CL_FLOAT2__)
+    __cl_float2     v2;
+#endif
+}cl_float2;
+
+typedef union
+{
+    cl_float  CL_ALIGNED(16) s[4];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_float   x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_float   s0, s1, s2, s3; };
+   __CL_ANON_STRUCT__ struct{ cl_float2  lo, hi; };
+#endif
+#if defined( __CL_FLOAT2__)
+    __cl_float2     v2[2];
+#endif
+#if defined( __CL_FLOAT4__)
+    __cl_float4     v4;
+#endif
+}cl_float4;
+
+/* cl_float3 is identical in size, alignment and behavior to cl_float4. See section 6.1.5. */
+typedef  cl_float4  cl_float3;
+
+typedef union
+{
+    cl_float   CL_ALIGNED(32) s[8];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_float   x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_float   s0, s1, s2, s3, s4, s5, s6, s7; };
+   __CL_ANON_STRUCT__ struct{ cl_float4  lo, hi; };
+#endif
+#if defined( __CL_FLOAT2__)
+    __cl_float2     v2[4];
+#endif
+#if defined( __CL_FLOAT4__)
+    __cl_float4     v4[2];
+#endif
+#if defined( __CL_FLOAT8__ )
+    __cl_float8     v8;
+#endif
+}cl_float8;
+
+typedef union
+{
+    cl_float  CL_ALIGNED(64) s[16];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_float  x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; };
+   __CL_ANON_STRUCT__ struct{ cl_float  s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; };
+   __CL_ANON_STRUCT__ struct{ cl_float8 lo, hi; };
+#endif
+#if defined( __CL_FLOAT2__)
+    __cl_float2     v2[8];
+#endif
+#if defined( __CL_FLOAT4__)
+    __cl_float4     v4[4];
+#endif
+#if defined( __CL_FLOAT8__ )
+    __cl_float8     v8[2];
+#endif
+#if defined( __CL_FLOAT16__ )
+    __cl_float16    v16;
+#endif
+}cl_float16;
+
+/* --- cl_doublen ---- */
+
+typedef union
+{
+    cl_double  CL_ALIGNED(16) s[2];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_double  x, y; };
+   __CL_ANON_STRUCT__ struct{ cl_double s0, s1; };
+   __CL_ANON_STRUCT__ struct{ cl_double lo, hi; };
+#endif
+#if defined( __CL_DOUBLE2__)
+    __cl_double2     v2;
+#endif
+}cl_double2;
+
+typedef union
+{
+    cl_double  CL_ALIGNED(32) s[4];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_double  x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_double  s0, s1, s2, s3; };
+   __CL_ANON_STRUCT__ struct{ cl_double2 lo, hi; };
+#endif
+#if defined( __CL_DOUBLE2__)
+    __cl_double2     v2[2];
+#endif
+#if defined( __CL_DOUBLE4__)
+    __cl_double4     v4;
+#endif
+}cl_double4;
+
+/* cl_double3 is identical in size, alignment and behavior to cl_double4. See section 6.1.5. */
+typedef  cl_double4  cl_double3;
+
+typedef union
+{
+    cl_double   CL_ALIGNED(64) s[8];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_double  x, y, z, w; };
+   __CL_ANON_STRUCT__ struct{ cl_double  s0, s1, s2, s3, s4, s5, s6, s7; };
+   __CL_ANON_STRUCT__ struct{ cl_double4 lo, hi; };
+#endif
+#if defined( __CL_DOUBLE2__)
+    __cl_double2     v2[4];
+#endif
+#if defined( __CL_DOUBLE4__)
+    __cl_double4     v4[2];
+#endif
+#if defined( __CL_DOUBLE8__ )
+    __cl_double8     v8;
+#endif
+}cl_double8;
+
+typedef union
+{
+    cl_double  CL_ALIGNED(128) s[16];
+#if __CL_HAS_ANON_STRUCT__
+   __CL_ANON_STRUCT__ struct{ cl_double  x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; };
+   __CL_ANON_STRUCT__ struct{ cl_double  s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; };
+   __CL_ANON_STRUCT__ struct{ cl_double8 lo, hi; };
+#endif
+#if defined( __CL_DOUBLE2__)
+    __cl_double2     v2[8];
+#endif
+#if defined( __CL_DOUBLE4__)
+    __cl_double4     v4[4];
+#endif
+#if defined( __CL_DOUBLE8__ )
+    __cl_double8     v8[2];
+#endif
+#if defined( __CL_DOUBLE16__ )
+    __cl_double16    v16;
+#endif
+}cl_double16;
+
+/* Macro to facilitate debugging
+ * Usage:
+ *   Place CL_PROGRAM_STRING_DEBUG_INFO on the line before the first line of your source.
+ *   The first line ends with:   CL_PROGRAM_STRING_DEBUG_INFO \"
+ *   Each line thereafter of OpenCL C source must end with: \n\
+ *   The last line ends in ";
+ *
+ *   Example:
+ *
+ *   const char *my_program = CL_PROGRAM_STRING_DEBUG_INFO "\
+ *   kernel void foo( int a, float * b )             \n\
+ *   {                                               \n\
+ *      // my comment                                \n\
+ *      *b[ get_global_id(0)] = a;                   \n\
+ *   }                                               \n\
+ *   ";
+ *
+ * This should correctly set up the line, (column) and file information for your source
+ * string so you can do source level debugging.
+ */
+#define  __CL_STRINGIFY( _x )               # _x
+#define  _CL_STRINGIFY( _x )                __CL_STRINGIFY( _x )
+#define  CL_PROGRAM_STRING_DEBUG_INFO       "#line "  _CL_STRINGIFY(__LINE__) " \"" __FILE__ "\" \n\n"
+
+#ifdef __cplusplus
+}
+#endif
+
+#if defined( _WIN32) && defined(_MSC_VER) && ! defined(__STDC__)
+    #if _MSC_VER >=1500
+    #pragma warning( pop )
+    #endif
+#endif
+
+#endif  /* __CL_PLATFORM_H  */
diff --git a/OpenCL/CL/cl_va_api_media_sharing_intel.h b/OpenCL/CL/cl_va_api_media_sharing_intel.h
new file mode 100644
index 0000000..7ba2ec8
--- /dev/null
+++ b/OpenCL/CL/cl_va_api_media_sharing_intel.h
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2008-2020 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#ifndef __OPENCL_CL_VA_API_MEDIA_SHARING_INTEL_H
+#define __OPENCL_CL_VA_API_MEDIA_SHARING_INTEL_H
+
+#include <CL/cl.h>
+#include <CL/cl_platform.h>
+#include <va/va.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************
+* cl_intel_va_api_media_sharing extension *
+*******************************************/
+
+#define cl_intel_va_api_media_sharing 1
+
+/* error codes */
+#define CL_INVALID_VA_API_MEDIA_ADAPTER_INTEL               -1098
+#define CL_INVALID_VA_API_MEDIA_SURFACE_INTEL               -1099
+#define CL_VA_API_MEDIA_SURFACE_ALREADY_ACQUIRED_INTEL      -1100
+#define CL_VA_API_MEDIA_SURFACE_NOT_ACQUIRED_INTEL          -1101
+
+/* cl_va_api_device_source_intel */
+#define CL_VA_API_DISPLAY_INTEL                             0x4094
+
+/* cl_va_api_device_set_intel */
+#define CL_PREFERRED_DEVICES_FOR_VA_API_INTEL               0x4095
+#define CL_ALL_DEVICES_FOR_VA_API_INTEL                     0x4096
+
+/* cl_context_info */
+#define CL_CONTEXT_VA_API_DISPLAY_INTEL                     0x4097
+
+/* cl_mem_info */
+#define CL_MEM_VA_API_MEDIA_SURFACE_INTEL                   0x4098
+
+/* cl_image_info */
+#define CL_IMAGE_VA_API_PLANE_INTEL                         0x4099
+
+/* cl_command_type */
+#define CL_COMMAND_ACQUIRE_VA_API_MEDIA_SURFACES_INTEL      0x409A
+#define CL_COMMAND_RELEASE_VA_API_MEDIA_SURFACES_INTEL      0x409B
+
+typedef cl_uint cl_va_api_device_source_intel;
+typedef cl_uint cl_va_api_device_set_intel;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clGetDeviceIDsFromVA_APIMediaAdapterINTEL(
+    cl_platform_id                platform,
+    cl_va_api_device_source_intel media_adapter_type,
+    void*                         media_adapter,
+    cl_va_api_device_set_intel    media_adapter_set,
+    cl_uint                       num_entries,
+    cl_device_id*                 devices,
+    cl_uint*                      num_devices) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_int (CL_API_CALL * clGetDeviceIDsFromVA_APIMediaAdapterINTEL_fn)(
+    cl_platform_id                platform,
+    cl_va_api_device_source_intel media_adapter_type,
+    void*                         media_adapter,
+    cl_va_api_device_set_intel    media_adapter_set,
+    cl_uint                       num_entries,
+    cl_device_id*                 devices,
+    cl_uint*                      num_devices) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY cl_mem CL_API_CALL
+clCreateFromVA_APIMediaSurfaceINTEL(
+    cl_context                    context,
+    cl_mem_flags                  flags,
+    VASurfaceID*                  surface,
+    cl_uint                       plane,
+    cl_int*                       errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_mem (CL_API_CALL * clCreateFromVA_APIMediaSurfaceINTEL_fn)(
+    cl_context                    context,
+    cl_mem_flags                  flags,
+    VASurfaceID*                  surface,
+    cl_uint                       plane,
+    cl_int*                       errcode_ret) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueAcquireVA_APIMediaSurfacesINTEL(
+    cl_command_queue              command_queue,
+    cl_uint                       num_objects,
+    const cl_mem*                 mem_objects,
+    cl_uint                       num_events_in_wait_list,
+    const cl_event*               event_wait_list,
+    cl_event*                     event) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_int (CL_API_CALL *clEnqueueAcquireVA_APIMediaSurfacesINTEL_fn)(
+    cl_command_queue              command_queue,
+    cl_uint                       num_objects,
+    const cl_mem*                 mem_objects,
+    cl_uint                       num_events_in_wait_list,
+    const cl_event*               event_wait_list,
+    cl_event*                     event) CL_API_SUFFIX__VERSION_1_2;
+
+extern CL_API_ENTRY cl_int CL_API_CALL
+clEnqueueReleaseVA_APIMediaSurfacesINTEL(
+    cl_command_queue              command_queue,
+    cl_uint                       num_objects,
+    const cl_mem*                 mem_objects,
+    cl_uint                       num_events_in_wait_list,
+    const cl_event*               event_wait_list,
+    cl_event*                     event) CL_API_SUFFIX__VERSION_1_2;
+
+typedef cl_int (CL_API_CALL *clEnqueueReleaseVA_APIMediaSurfacesINTEL_fn)(
+    cl_command_queue              command_queue,
+    cl_uint                       num_objects,
+    const cl_mem*                 mem_objects,
+    cl_uint                       num_events_in_wait_list,
+    const cl_event*               event_wait_list,
+    cl_event*                     event) CL_API_SUFFIX__VERSION_1_2;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __OPENCL_CL_VA_API_MEDIA_SHARING_INTEL_H */
+
diff --git a/OpenCL/CL/cl_version.h b/OpenCL/CL/cl_version.h
new file mode 100644
index 0000000..3844938
--- /dev/null
+++ b/OpenCL/CL/cl_version.h
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2018-2020 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#ifndef __CL_VERSION_H
+#define __CL_VERSION_H
+
+/* Detect which version to target */
+#if !defined(CL_TARGET_OPENCL_VERSION)
+#pragma message("cl_version.h: CL_TARGET_OPENCL_VERSION is not defined. Defaulting to 300 (OpenCL 3.0)")
+#define CL_TARGET_OPENCL_VERSION 300
+#endif
+#if CL_TARGET_OPENCL_VERSION != 100 && \
+    CL_TARGET_OPENCL_VERSION != 110 && \
+    CL_TARGET_OPENCL_VERSION != 120 && \
+    CL_TARGET_OPENCL_VERSION != 200 && \
+    CL_TARGET_OPENCL_VERSION != 210 && \
+    CL_TARGET_OPENCL_VERSION != 220 && \
+    CL_TARGET_OPENCL_VERSION != 300
+#pragma message("cl_version: CL_TARGET_OPENCL_VERSION is not a valid value (100, 110, 120, 200, 210, 220, 300). Defaulting to 300 (OpenCL 3.0)")
+#undef CL_TARGET_OPENCL_VERSION
+#define CL_TARGET_OPENCL_VERSION 300
+#endif
+
+
+/* OpenCL Version */
+#if CL_TARGET_OPENCL_VERSION >= 300 && !defined(CL_VERSION_3_0)
+#define CL_VERSION_3_0  1
+#endif
+#if CL_TARGET_OPENCL_VERSION >= 220 && !defined(CL_VERSION_2_2)
+#define CL_VERSION_2_2  1
+#endif
+#if CL_TARGET_OPENCL_VERSION >= 210 && !defined(CL_VERSION_2_1)
+#define CL_VERSION_2_1  1
+#endif
+#if CL_TARGET_OPENCL_VERSION >= 200 && !defined(CL_VERSION_2_0)
+#define CL_VERSION_2_0  1
+#endif
+#if CL_TARGET_OPENCL_VERSION >= 120 && !defined(CL_VERSION_1_2)
+#define CL_VERSION_1_2  1
+#endif
+#if CL_TARGET_OPENCL_VERSION >= 110 && !defined(CL_VERSION_1_1)
+#define CL_VERSION_1_1  1
+#endif
+#if CL_TARGET_OPENCL_VERSION >= 100 && !defined(CL_VERSION_1_0)
+#define CL_VERSION_1_0  1
+#endif
+
+/* Allow deprecated APIs for older OpenCL versions. */
+#if CL_TARGET_OPENCL_VERSION <= 220 && !defined(CL_USE_DEPRECATED_OPENCL_2_2_APIS)
+#define CL_USE_DEPRECATED_OPENCL_2_2_APIS
+#endif
+#if CL_TARGET_OPENCL_VERSION <= 210 && !defined(CL_USE_DEPRECATED_OPENCL_2_1_APIS)
+#define CL_USE_DEPRECATED_OPENCL_2_1_APIS
+#endif
+#if CL_TARGET_OPENCL_VERSION <= 200 && !defined(CL_USE_DEPRECATED_OPENCL_2_0_APIS)
+#define CL_USE_DEPRECATED_OPENCL_2_0_APIS
+#endif
+#if CL_TARGET_OPENCL_VERSION <= 120 && !defined(CL_USE_DEPRECATED_OPENCL_1_2_APIS)
+#define CL_USE_DEPRECATED_OPENCL_1_2_APIS
+#endif
+#if CL_TARGET_OPENCL_VERSION <= 110 && !defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS)
+#define CL_USE_DEPRECATED_OPENCL_1_1_APIS
+#endif
+#if CL_TARGET_OPENCL_VERSION <= 100 && !defined(CL_USE_DEPRECATED_OPENCL_1_0_APIS)
+#define CL_USE_DEPRECATED_OPENCL_1_0_APIS
+#endif
+
+#endif  /* __CL_VERSION_H */
diff --git a/OpenCL/CL/opencl.h b/OpenCL/CL/opencl.h
new file mode 100644
index 0000000..ef8dd1e
--- /dev/null
+++ b/OpenCL/CL/opencl.h
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008-2021 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#ifndef __OPENCL_H
+#define __OPENCL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <CL/cl.h>
+#include <CL/cl_gl.h>
+#include <CL/cl_ext.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __OPENCL_H   */
diff --git a/OpenCL/lib/OpenCL.lib b/OpenCL/lib/OpenCL.lib
new file mode 100644
index 0000000..704194d
--- /dev/null
+++ b/OpenCL/lib/OpenCL.lib
Binary files differ
diff --git a/OpenCL/lib/OpenCL64.lib b/OpenCL/lib/OpenCL64.lib
new file mode 100644
index 0000000..b30ea48
--- /dev/null
+++ b/OpenCL/lib/OpenCL64.lib
Binary files differ
diff --git a/OpenCL/license.txt b/OpenCL/license.txt
new file mode 100644
index 0000000..9d69cfc
--- /dev/null
+++ b/OpenCL/license.txt
@@ -0,0 +1,4 @@
+These optional files (which are only needed when compiling with OpenCL support enabled in the encoder) are from the 
+Khronos Group OpenCL headers github repo. They are Copyright (c) 2008-2020 The Khronos Group Inc.
+https://github.com/KhronosGroup/OpenCL-Headers
+
diff --git a/README.md b/README.md
index 4646995..a96cdd8 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,17 @@
 # basis_universal
 Basis Universal Supercompressed GPU Texture Codec
 
+<sub>(This software is available under a free and permissive license (Apache 2.0), but like many other large open source projects it needs financial support to sustain its continued security, bug fixes and improvements. In addition to maintenance and stability there are many desirable features and new interchange formats we would like to add. If your company is using Basis Universal in a product, please consider reaching out.)</sub>
+
+Businesses: support continued development and maintenance via invoiced technical support, maintenance, or sponsoring contracts:
+<br>&nbsp;&nbsp;_E-mail: info @ binomial dot info_ or contact us on [Twitter](https://twitter.com/_binomial)
+
+Also see the [Sponsors](https://github.com/BinomialLLC/basis_universal/wiki/Sponsors-and-Supporters) wiki page.
+
+----
+
+[![Build status](https://ci.appveyor.com/api/projects/status/87eb0o96pjho4sh0?svg=true)](https://ci.appveyor.com/project/BinomialLLC/basis-universal)
+
 Basis Universal is a ["supercompressed"](http://gamma.cs.unc.edu/GST/gst.pdf) GPU texture data interchange system that supports two highly compressed intermediate file formats (.basis or the [.KTX2 open standard from the Khronos Group](https://github.khronos.org/KTX-Specification/)) that can be quickly transcoded to a [very wide variety](https://github.com/BinomialLLC/basis_universal/wiki/OpenGL-texture-format-enums-table) of GPU compressed and uncompressed pixel formats: ASTC 4x4 L/LA/RGB/RGBA, PVRTC1 4bpp RGB/RGBA, PVRTC2 RGB/RGBA, BC7 mode 6 RGB, BC7 mode 5 RGB/RGBA, BC1-5 RGB/RGBA/X/XY, ETC1 RGB, ETC2 RGBA, ATC RGB/RGBA, ETC2 EAC R11 and RG11, FXT1 RGB, and uncompressed raster image formats 8888/565/4444. 
 
 The system now supports two modes: a high quality mode which is internally based off the [UASTC compressed texture format](https://richg42.blogspot.com/2020/01/uastc-block-format-encoding.html), and the original lower quality mode which is based off a subset of ETC1 called "ETC1S". UASTC is for extremely high quality (similar to BC7 quality) textures, and ETC1S is for very small files. The ETC1S system includes built-in data compression, while the UASTC system includes an optional Rate Distortion Optimization (RDO) post-process stage that conditions the encoded UASTC texture data in the .basis file so it can be more effectively LZ compressed by the end user. More technical details about UASTC integration are [here](https://github.com/BinomialLLC/basis_universal/wiki/UASTC-implementation-details).
@@ -40,17 +51,11 @@
 
 ### License and 3rd party code dependencies
 
-Detailed legal, license, and IP information is [here](https://github.com/BinomialLLC/basis_universal/wiki/Legal-IP-License-Information). Basis Universal itself uses the Apache 2.0 licenses, but it utilizes some zlib and optional BSD code (Zstandard). The supported texture formats are [open Khronos Group standards](https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html).
+Detailed legal, license, and IP information is [here](https://github.com/BinomialLLC/basis_universal/wiki/Legal-IP-License-Information). Basis Universal itself uses the Apache 2.0 licenses, but it also utilizes some optional BSD code (Zstandard). The supported texture formats are [open Khronos Group standards](https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html).
 
 All C/C++ code dependencies are present inside the Basis Universal repo itself to simplify building.
 
-The stand-alone transcoder (in the "transcoder" directory) is a single .cpp source file library which has no 3rd party code dependencies apart from zstd/zstddeclib.c, which is optional. (It's only used for decompressing UASTC KTX2 files that use Zstandard.)
-
-The encoder uses [lodepng](https://lodev.org/lodepng/) for loading and saving PNG images, which is Copyright (c) 2005-2019 Lode Vandevenne. It uses the zlib license. It also uses [apg_bmp](https://github.com/capnramses/apg/tree/master/apg_bmp) for loading BMP images, which is Copyright 2019 Anton Gerdelan. It uses the Apache 2.0 license.
-
-The encoder uses [tcuAstcUtil.cpp](https://chromium.googlesource.com/external/deqp/+/refs/heads/master/framework/common/tcuAstcUtil.cpp), from the [Android drawElements Quality Program (deqp) Testing Suite](https://source.android.com/devices/graphics/deqp-testing), for unpacking the transcoder's ASTC output for testing/validation purposes. This code is Copyright 2016 The Android Open Source Project, and uses the Apache 2.0 license. We have modified the code so it has no external dependencies, and disabled HDR support.
-
-The encoder optionally uses Zstandard's single source file compressor (in zstd/zstd.c) to support compressing supercompressed KTX2 files.
+The encoder optionally uses Zstandard's single source file compressor (in zstd/zstd.c) to support compressing supercompressed KTX2 files. The stand-alone transcoder (in the "transcoder" directory) is a single .cpp source file library which has no 3rd party code dependencies apart from zstd/zstddeclib.c, which is also technically optional. It's only used for decompressing UASTC KTX2 files that use Zstandard.
 
 ### Command Line Compression Tool
 
@@ -311,48 +316,3 @@
 a clean, checked-out repository periodically, or run it during CI tests
 before any build artifacts have been created.
 
-### Special Thanks
-A huge thanks to Google for partnering with us and enabling this system to be open sourced. 
-
-Thank you to [Esri - Environmental Systems Research Institute](https://www.esri.com/) for sponsoring the encoder optimization work in the v1.13 release, and the KTX2 work in the v1.15 release.
-
-Thanks to the Khronos Group for building an open ecosystem that supports ETC1S/UASTC.
-
-Thanks to a number of companies or groups who have supported or helped out Binomial over the years: Intel, SpaceX, Netflix, Forgotten Empires, Microsoft, Polystream, Hothead Games, BioDigital, Magic Leap, Blizzard Entertainment, Insomniac Games, Rockstar Games, Facebook, Activision, the Khronos Group, and the organizers at CppCon.
-
-Thanks to Dave Wilkinson (AMD/Khronos) for supporting us and giving us very valuable feedback while we developed Basis Universal.
-
-Thanks to Chris Wein (Netflix), who showed us the path to Texture Video.
-
-Thanks to Mike Dussault (SpaceX) and Elon Musk for supporting Binomial in the early days.
-
-Thanks to Graeme Devine at Magic Leap.
-
-Thanks to Matt Pritchard, formerly of Valve Software and Microsoft, for helping me with the computer hardware I used while building this system and its predecessor. 
-
-Thanks to John Brooks at Blue Shift, Inc. for inspiring this work by showing me his Dreamcast texture compression system around 2002, and for releasing etc2comp. I first saw the subblock flip estimation approach (used in basisu_etc.cpp) in etc2comp.
-
-Thanks to Colt McAnlis, for advertising one of my earlier open source texture compression libraries at GDC, and Won Chun, who originally suggested making a universal system.
-
-Thanks to Chas Boyd (Microsoft), for inspiring us to work on texture compression full-time. Chas also gave us great feedback about UASTC before it was released.
-
-Thanks to Mark Callow at Edgewise Consulting for his work on glTF and the KTX2 file format.
-
-I first saw using precomputed tables for quickly computing optimal encodings of solid color blocks in ryg_dxt. The method that limits the canonical Huffman codelengths to a maximum codesize was used in Yoshizaki's lharc. The canonical Huffman codelength compression system is similar to Katz's Deflate method.
-
-### Possible improvements
-The codebook generation process is basically a high quality, but slow and brute force reference. It's possible to massively speed up codebook gen in several ways. One way is to not throw away the tree structures constructed during the creation of the initial codebooks. 
-
-The way the -q (quality) option is converted to codebook sizes is very simple (fixed formulas), and could be improved. It has a tendency to plateau on some files.
-
-The various Huffman codes could be divided up into groups (like Zstd), for much faster Huffman decoding in the transcoder. Also, larger slices could be divided up into multiple segments, and each segment transcoded using a different thread. Both of these changes would modify the format.
-
-PVRTC1 modulation values could be determined using multiple threads and/or SIMD code.
-
-PVRTC1 2bpp support wouldn't be hard to add.
-
-The transcoder's BC7 tables are a bit large, and can be reduced, which would allow the transcoder to be downloaded more quickly.
-
-3-bit selectors for alpha would greatly improve the quality of the alpha, but would break the file format and require extensive additions to the compressor/transcoder.
-
-Fast 6x6 ASTC support may be possible.
diff --git a/basisu.vcxproj b/basisu.vcxproj
index cd889b1..182ed30 100644
--- a/basisu.vcxproj
+++ b/basisu.vcxproj
@@ -27,26 +27,26 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
-    <PlatformToolset>v142</PlatformToolset>
+    <PlatformToolset>v143</PlatformToolset>
     <CharacterSet>MultiByte</CharacterSet>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
-    <PlatformToolset>v142</PlatformToolset>
+    <PlatformToolset>v143</PlatformToolset>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <CharacterSet>MultiByte</CharacterSet>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
-    <PlatformToolset>v142</PlatformToolset>
+    <PlatformToolset>v143</PlatformToolset>
     <CharacterSet>MultiByte</CharacterSet>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
-    <PlatformToolset>v142</PlatformToolset>
+    <PlatformToolset>v143</PlatformToolset>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <CharacterSet>MultiByte</CharacterSet>
   </PropertyGroup>
@@ -87,13 +87,14 @@
       <Optimization>Disabled</Optimization>
       <SDLCheck>true</SDLCheck>
       <OpenMPSupport>true</OpenMPSupport>
-      <AdditionalIncludeDirectories>
-      </AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);BASISU_SUPPORT_SSE=1</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>OpenCL</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);BASISU_SUPPORT_SSE=1;BASISU_SUPPORT_OPENCL=1</PreprocessorDefinitions>
       <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
+      <AdditionalLibraryDirectories>OpenCL\lib</AdditionalLibraryDirectories>
+      <AdditionalDependencies>opencl.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -101,13 +102,15 @@
       <WarningLevel>Level4</WarningLevel>
       <Optimization>Disabled</Optimization>
       <SDLCheck>true</SDLCheck>
-      <AdditionalIncludeDirectories>ispc_texcomp</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>OpenCL</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
       <OpenMPSupport>true</OpenMPSupport>
-      <PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);BASISU_SUPPORT_SSE=1;</PreprocessorDefinitions>
+      <PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);BASISU_SUPPORT_SSE=1;BASISU_SUPPORT_OPENCL=1</PreprocessorDefinitions>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
+      <AdditionalLibraryDirectories>OpenCL\lib</AdditionalLibraryDirectories>
+      <AdditionalDependencies>opencl64.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -117,9 +120,8 @@
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <OpenMPSupport>true</OpenMPSupport>
-      <AdditionalIncludeDirectories>
-      </AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>NDEBUG;_HAS_EXCEPTIONS=0;%(PreprocessorDefinitions);BASISU_SUPPORT_SSE=1</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>OpenCL</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>NDEBUG;_HAS_EXCEPTIONS=0;%(PreprocessorDefinitions);BASISU_SUPPORT_SSE=1;BASISU_SUPPORT_OPENCL=1</PreprocessorDefinitions>
       <BufferSecurityCheck>false</BufferSecurityCheck>
       <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
       <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
@@ -131,6 +133,8 @@
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <SubSystem>Console</SubSystem>
+      <AdditionalLibraryDirectories>OpenCL\lib</AdditionalLibraryDirectories>
+      <AdditionalDependencies>opencl.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -139,8 +143,8 @@
       <Optimization>Full</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <AdditionalIncludeDirectories>ispc_texcomp</AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>NDEBUG;_HAS_EXCEPTIONS=0;%(PreprocessorDefinitions);BASISU_SUPPORT_SSE=1;</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>OpenCL</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>NDEBUG;_HAS_EXCEPTIONS=0;%(PreprocessorDefinitions);BASISU_SUPPORT_SSE=1;BASISU_SUPPORT_OPENCL=1</PreprocessorDefinitions>
       <BufferSecurityCheck>false</BufferSecurityCheck>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
       <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
@@ -153,11 +157,11 @@
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <SubSystem>Console</SubSystem>
+      <AdditionalLibraryDirectories>OpenCL\lib</AdditionalLibraryDirectories>
+      <AdditionalDependencies>opencl64.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
-    <ClCompile Include="encoder\apg_bmp.c" />
-    <ClCompile Include="encoder\basisu_astc_decomp.cpp" />
     <ClCompile Include="encoder\basisu_backend.cpp" />
     <ClCompile Include="encoder\basisu_basis_file.cpp" />
     <ClCompile Include="encoder\basisu_bc7enc.cpp" />
@@ -165,21 +169,22 @@
     <ClCompile Include="encoder\basisu_enc.cpp" />
     <ClCompile Include="encoder\basisu_etc.cpp" />
     <ClCompile Include="encoder\basisu_frontend.cpp" />
-    <ClCompile Include="encoder\basisu_global_selector_palette_helpers.cpp" />
     <ClCompile Include="encoder\basisu_gpu_texture.cpp" />
     <ClCompile Include="encoder\basisu_kernels_sse.cpp" />
+    <ClCompile Include="encoder\basisu_opencl.cpp" />
     <ClCompile Include="encoder\basisu_pvrtc1_4.cpp" />
     <ClCompile Include="encoder\basisu_resampler.cpp" />
     <ClCompile Include="encoder\basisu_resample_filters.cpp" />
     <ClCompile Include="encoder\basisu_ssim.cpp" />
     <ClCompile Include="encoder\basisu_uastc_enc.cpp" />
     <ClCompile Include="encoder\jpgd.cpp" />
-    <ClCompile Include="encoder\lodepng.cpp" />
+    <ClCompile Include="encoder\pvpngreader.cpp" />
     <ClCompile Include="zstd\zstd.c" />
+    <ClInclude Include="encoder\basisu_ocl_kernels.h" />
+    <ClInclude Include="encoder\basisu_opencl.h" />
+    <ClInclude Include="encoder\pvpngreader.h" />
     <ClInclude Include="transcoder\basisu_containers_impl.h" />
     <ClCompile Include="transcoder\basisu_transcoder.cpp" />
-    <ClInclude Include="encoder\apg_bmp.h" />
-    <ClInclude Include="encoder\basisu_astc_decomp.h" />
     <ClInclude Include="encoder\basisu_backend.h" />
     <ClInclude Include="encoder\basisu_basis_file.h" />
     <ClInclude Include="encoder\basisu_bc7enc.h" />
@@ -187,7 +192,6 @@
     <ClInclude Include="encoder\basisu_enc.h" />
     <ClInclude Include="encoder\basisu_etc.h" />
     <ClInclude Include="encoder\basisu_frontend.h" />
-    <ClInclude Include="encoder\basisu_global_selector_palette_helpers.h" />
     <ClInclude Include="encoder\basisu_gpu_texture.h" />
     <ClInclude Include="encoder\basisu_kernels_declares.h" />
     <ClInclude Include="encoder\basisu_kernels_imp.h" />
@@ -203,19 +207,18 @@
     <ClInclude Include="encoder\cppspmd_sse.h" />
     <ClInclude Include="encoder\cppspmd_type_aliases.h" />
     <ClInclude Include="encoder\jpgd.h" />
-    <ClInclude Include="encoder\lodepng.h" />
     <ClInclude Include="transcoder\basisu.h" />
     <ClInclude Include="transcoder\basisu_containers.h" />
     <ClInclude Include="transcoder\basisu_file_headers.h" />
     <ClInclude Include="transcoder\basisu_transcoder.h" />
     <ClInclude Include="transcoder\basisu_transcoder_internal.h" />
-    <ClInclude Include="transcoder\basisu_global_selector_palette.h" />
     <ClInclude Include="transcoder\basisu_transcoder_uastc.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="basisu_tool.cpp" />
   </ItemGroup>
   <ItemGroup>
+    <None Include="bin\ocl_kernels.cl" />
     <None Include="transcoder\basisu_transcoder_tables_astc.inc" />
     <None Include="transcoder\basisu_transcoder_tables_astc_0_255.inc" />
     <None Include="transcoder\basisu_transcoder_tables_atc_55.inc" />
diff --git a/basisu.vcxproj.filters b/basisu.vcxproj.filters
index 498c859..ee34dce 100644
--- a/basisu.vcxproj.filters
+++ b/basisu.vcxproj.filters
@@ -5,12 +5,6 @@
     <ClCompile Include="transcoder\basisu_transcoder.cpp">
       <Filter>transcoder</Filter>
     </ClCompile>
-    <ClCompile Include="encoder\apg_bmp.c">
-      <Filter>encoder</Filter>
-    </ClCompile>
-    <ClCompile Include="encoder\basisu_astc_decomp.cpp">
-      <Filter>encoder</Filter>
-    </ClCompile>
     <ClCompile Include="encoder\basisu_backend.cpp">
       <Filter>encoder</Filter>
     </ClCompile>
@@ -32,9 +26,6 @@
     <ClCompile Include="encoder\basisu_frontend.cpp">
       <Filter>encoder</Filter>
     </ClCompile>
-    <ClCompile Include="encoder\basisu_global_selector_palette_helpers.cpp">
-      <Filter>encoder</Filter>
-    </ClCompile>
     <ClCompile Include="encoder\basisu_gpu_texture.cpp">
       <Filter>encoder</Filter>
     </ClCompile>
@@ -50,9 +41,6 @@
     <ClCompile Include="encoder\jpgd.cpp">
       <Filter>encoder</Filter>
     </ClCompile>
-    <ClCompile Include="encoder\lodepng.cpp">
-      <Filter>encoder</Filter>
-    </ClCompile>
     <ClCompile Include="encoder\basisu_resample_filters.cpp">
       <Filter>encoder</Filter>
     </ClCompile>
@@ -65,11 +53,14 @@
     <ClCompile Include="zstd\zstd.c">
       <Filter>encoder</Filter>
     </ClCompile>
+    <ClCompile Include="encoder\basisu_opencl.cpp">
+      <Filter>encoder</Filter>
+    </ClCompile>
+    <ClCompile Include="encoder\pvpngreader.cpp">
+      <Filter>encoder</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
-    <ClInclude Include="transcoder\basisu_global_selector_palette.h">
-      <Filter>transcoder</Filter>
-    </ClInclude>
     <ClInclude Include="transcoder\basisu.h">
       <Filter>transcoder</Filter>
     </ClInclude>
@@ -82,12 +73,6 @@
     <ClInclude Include="transcoder\basisu_transcoder_uastc.h">
       <Filter>transcoder</Filter>
     </ClInclude>
-    <ClInclude Include="encoder\apg_bmp.h">
-      <Filter>encoder</Filter>
-    </ClInclude>
-    <ClInclude Include="encoder\basisu_astc_decomp.h">
-      <Filter>encoder</Filter>
-    </ClInclude>
     <ClInclude Include="encoder\basisu_backend.h">
       <Filter>encoder</Filter>
     </ClInclude>
@@ -109,9 +94,6 @@
     <ClInclude Include="encoder\basisu_frontend.h">
       <Filter>encoder</Filter>
     </ClInclude>
-    <ClInclude Include="encoder\basisu_global_selector_palette_helpers.h">
-      <Filter>encoder</Filter>
-    </ClInclude>
     <ClInclude Include="encoder\basisu_gpu_texture.h">
       <Filter>encoder</Filter>
     </ClInclude>
@@ -130,9 +112,6 @@
     <ClInclude Include="encoder\jpgd.h">
       <Filter>encoder</Filter>
     </ClInclude>
-    <ClInclude Include="encoder\lodepng.h">
-      <Filter>encoder</Filter>
-    </ClInclude>
     <ClInclude Include="encoder\basisu_ssim.h">
       <Filter>encoder</Filter>
     </ClInclude>
@@ -169,6 +148,15 @@
     <ClInclude Include="transcoder\basisu_file_headers.h">
       <Filter>transcoder</Filter>
     </ClInclude>
+    <ClInclude Include="encoder\basisu_opencl.h">
+      <Filter>encoder</Filter>
+    </ClInclude>
+    <ClInclude Include="encoder\basisu_ocl_kernels.h">
+      <Filter>encoder</Filter>
+    </ClInclude>
+    <ClInclude Include="encoder\pvpngreader.h">
+      <Filter>encoder</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="transcoder\basisu_transcoder_tables_dxt1_6.inc">
@@ -195,6 +183,9 @@
     <None Include="transcoder\basisu_transcoder_tables_atc_56.inc">
       <Filter>transcoder</Filter>
     </None>
+    <None Include="bin\ocl_kernels.cl">
+      <Filter>encoder</Filter>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <Filter Include="transcoder">
diff --git a/basisu_tool.cpp b/basisu_tool.cpp
index 2447943..13ed340 100644
--- a/basisu_tool.cpp
+++ b/basisu_tool.cpp
@@ -1,5 +1,5 @@
 // basisu_tool.cpp
-// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2022 Binomial LLC. All Rights Reserved.
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
@@ -23,10 +23,10 @@
 #include "encoder/basisu_gpu_texture.h"
 #include "encoder/basisu_frontend.h"
 #include "encoder/basisu_backend.h"
-#include "transcoder/basisu_global_selector_palette.h"
 #include "encoder/basisu_comp.h"
 #include "transcoder/basisu_transcoder.h"
 #include "encoder/basisu_ssim.h"
+#include "encoder/basisu_opencl.h"
 
 #define MINIZ_HEADER_FILE_ONLY
 #define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
@@ -40,7 +40,7 @@
 using namespace basisu;
 using namespace buminiz;
 
-#define BASISU_TOOL_VERSION "1.15"
+#define BASISU_TOOL_VERSION "1.16.3"
 
 enum tool_mode
 {
@@ -52,7 +52,10 @@
 	cCompare,
 	cVersion,
 	cBench,
-	cCompSize
+	cCompSize,
+	cTest,
+	cSplitImage,
+	cCombineImages
 };
 
 static void print_usage()
@@ -74,14 +77,17 @@
 		"Filenames prefixed with a @ symbol are read as filename listing files. Listing text files specify which actual filenames to process (one filename per line).\n"
 		"\n"
 		"Options:\n"
+		" -opencl: Enable OpenCL usage\n"
+		" -opencl_serialize: Serialize all calls to the OpenCL driver (to work around buggy drivers, only useful with -parallel)\n"
+		" -parallel: Compress multiple textures simumtanously (one per thread), instead of one at a time. Compatible with OpenCL mode. This is much faster, but in OpenCL mode the driver is pushed harder, and the CLI output will be jumbled.\n"
 		" -ktx2: Write .KTX2 ETC1S/UASTC files instead of .basis files. By default, UASTC files will be compressed using Zstandard unless -ktx2_no_zstandard is specified.\n"
 		" -ktx2_no_zstandard: Don't compress UASTC texture data using Zstandard, store it uncompressed instead.\n"
 		" -ktx2_zstandard_level X: Set ZStandard compression level to X (see Zstandard documentation, default level is 6)\n"
 		" -ktx2_animdata_duration X: Set KTX2animData duration field to integer value X (only valid/useful for -tex_type video, default is 1)\n"
 		" -ktx2_animdata_timescale X: Set KTX2animData timescale field to integer value X (only valid/useful for -tex_type video, default is 15)\n"
 		" -ktx2_animdata_loopcount X: Set KTX2animData loopcount field to integer value X (only valid/useful for -tex_type video, default is 0)\n"
-		" -file filename.png/bmp/tga/jpg: Input image filename, multiple images are OK, use -file X for each input filename (prefixing input filenames with -file is optional)\n"
-		" -alpha_file filename.png/bmp/tga/jpg: Input alpha image filename, multiple images are OK, use -file X for each input filename (must be paired with -file), images converted to REC709 grayscale and used as input alpha\n"
+		" -file filename.png/bmp/tga/jpg/qoi: Input image filename, multiple images are OK, use -file X for each input filename (prefixing input filenames with -file is optional)\n"
+		" -alpha_file filename.png/bmp/tga/jpg/qoi: Input alpha image filename, multiple images are OK, use -file X for each input filename (must be paired with -file), images converted to REC709 grayscale and used as input alpha\n"
 		" -multifile_printf: printf() format strint to use to compose multiple filenames\n"
 		" -multifile_first: The index of the first file to process, default is 0 (must specify -multifile_printf and -multifile_num)\n"
 		" -multifile_num: The total number of files to process.\n"
@@ -90,13 +96,15 @@
 		" -output_file filename: Output .basis/.ktx filename\n"
 		" -output_path: Output .basis/.ktx files to specified directory.\n"
 		" -debug: Enable codec debug print to stdout (slightly slower).\n"
+		" -verbose: Same as -debug (debug output to stdout).\n"
 		" -debug_images: Enable codec debug images (much slower).\n"
 		" -stats: Compute and display image quality metrics (slightly slower).\n"
 		" -tex_type <2d, 2darray, 3d, video, cubemap>: Set Basis file header's texture type field. Cubemap arrays require multiples of 6 images, in X+, X-, Y+, Y-, Z+, Z- order, each image must be the same resolutions.\n"
 		"  2d=arbitrary 2D images, 2darray=2D array, 3D=volume texture slices, video=video frames, cubemap=array of faces. For 2darray/3d/cubemaps/video, each source image's dimensions and # of mipmap levels must be the same.\n"
 		" For video, the .basis file will be written with the first frame being an I-Frame, and subsequent frames being P-Frames (using conditional replenishment). Playback must always occur in order from first to last image.\n"
 		" -framerate X: Set framerate in .basis header to X/frames sec.\n"
-		" -individual: Process input images individually and output multiple .basis files (not as a texture array)\n"
+		" -individual: Process input images individually and output multiple .basis/.ktx2 files (not as a texture array - this is now the default as of v1.16)\n"
+		" -tex_array: Process input images as a single texture array and write a single .basis/.ktx2 file (the former default before v1.16)\n"
 		" -comp_level X: Set ETC1S encoding speed vs. quality tradeoff. Range is 0-6, default is 1. Higher values=MUCH slower, but slightly higher quality. Higher levels intended for videos. Use -q first!\n"
 		" -fuzz_testing: Use with -validate: Disables CRC16 validation of file contents before transcoding\n"
 		"\nUASTC options:\n"
@@ -112,6 +120,8 @@
 		" -uastc_rdo_m: Disable RDO multithreading (slightly higher compression, deterministic).\n"
 		"\n"
 		"More options:\n"
+		" -test: Run an automated ETC1S/UASTC encoding and transcoding test. Returns EXIT_FAILURE if any failures\n"
+		" -test_dir: Optional directory of test files. Defaults to \"../test_files\".\n"
 		" -max_endpoints X: Manually set the max number of color endpoint clusters from 1-16128, use instead of -q\n"
 		" -max_selectors X: Manually set the max number of color selector clusters from 1-16128, use instead of -q\n"
 		" -y_flip: Flip input images vertically before compression\n"
@@ -122,7 +132,10 @@
 		" -swizzle rgba: Specify swizzle for the 4 input color channels using r, g, b and a (the -separate_rg_to_color_alpha flag is equivalent to rrrg)\n"
 		" -renorm: Renormalize each input image before any further processing/compression\n"
 		" -no_multithreading: Disable multithreading\n"
-		" -no_ktx: Disable KTX writing when unpacking (faster)\n"
+		" -max_threads X: Use at most X threads total when multithreading is enabled (this includes the main thread)\n"
+		" -no_ktx: Disable KTX writing when unpacking (faster, less output files)\n"
+		" -ktx_only: Only write KTX files when unpacking (faster, less output files)\n"
+		" -write_out: Write 3dfx OUT files when unpacking FXT1 textures\n"
 		" -etc1_only: Only unpack to ETC1, skipping the other texture formats during -unpack\n"
 		" -disable_hierarchical_endpoint_codebooks: Disable hierarchical endpoint codebook usage, slower but higher quality on some compression levels\n"
 		" -compare_ssim: Compute and display SSIM of image comparison (slow)\n"
@@ -157,7 +170,7 @@
 		" -userdata1 X: Set 32-bit userdata1 field in Basis file header to X (X is a signed 32-bit int)\n"
 		"\n"
 		"Various command line examples:\n"
-		" basisu x.png : Compress sRGB image x.png to x.basis using default settings (multiple filenames OK, use -individual if you don't want a tex array)\n"
+		" basisu x.png : Compress sRGB image x.png to x.basis using default settings (multiple filenames OK, use -tex_array if you want a tex array vs. multiple output files)\n"
 		" basisu x.basis : Unpack x.basis to PNG/KTX files (multiple filenames OK)\n"
 		" basisu -file x.png -mipmap -y_flip : Compress a mipmapped x.basis file from an sRGB image named x.png, Y flip each source image\n"
 		" basisu -validate -file x.basis : Validate x.basis (check header, check file CRC's, attempt to transcode all slices)\n"
@@ -268,14 +281,19 @@
 		m_ktx2_animdata_loopcount(0),
 		m_multifile_first(0),
 		m_multifile_num(0),
-		m_individual(false),
+		m_max_threads(1024), // surely this is high enough
+		m_individual(true),
 		m_no_ktx(false),
+		m_ktx_only(false),
+		m_write_out(false),
 		m_etc1_only(false),
 		m_fuzz_testing(false),
 		m_compare_ssim(false),
-		m_bench(false)
+		m_bench(false),
+		m_parallel_compression(false)
 	{
 		m_comp_params.m_compression_level = basisu::maximum<int>(0, BASISU_DEFAULT_COMPRESSION_LEVEL - 1);
+		m_test_file_dir = "../test_files";
 	}
 
 	bool parse(int arg_c, const char **arg_v)
@@ -325,6 +343,10 @@
 				m_mode = cCompress;
 			else if (strcasecmp(pArg, "-compare") == 0)
 				m_mode = cCompare;
+			else if (strcasecmp(pArg, "-split") == 0)
+				m_mode = cSplitImage;
+			else if (strcasecmp(pArg, "-combine") == 0)
+				m_mode = cCombineImages;
 			else if (strcasecmp(pArg, "-unpack") == 0)
 				m_mode = cUnpack;
 			else if (strcasecmp(pArg, "-validate") == 0)
@@ -339,12 +361,24 @@
 				m_mode = cBench;
 			else if (strcasecmp(pArg, "-comp_size") == 0)
 				m_mode = cCompSize;
+			else if (strcasecmp(pArg, "-test") == 0)
+				m_mode = cTest;
+			else if (strcasecmp(pArg, "-test_dir") == 0)
+			{
+				REMAINING_ARGS_CHECK(1);
+				m_test_file_dir = std::string(arg_v[arg_index + 1]);
+				arg_count++;
+			}
 			else if (strcasecmp(pArg, "-no_sse") == 0)
 			{
 #if BASISU_SUPPORT_SSE
 				g_cpu_supports_sse41 = false;
 #endif
 			}
+			else if (strcasecmp(pArg, "-no_status_output") == 0)
+			{
+				m_comp_params.m_status_output = false;
+			}
 			else if (strcasecmp(pArg, "-file") == 0)
 			{
 				REMAINING_ARGS_CHECK(1);
@@ -457,14 +491,18 @@
 				m_output_path = arg_v[arg_index + 1];
 				arg_count++;
 			}
-			else if (strcasecmp(pArg, "-debug") == 0)
+			else if ((strcasecmp(pArg, "-debug") == 0) || (strcasecmp(pArg, "-verbose") == 0))
 			{
 				m_comp_params.m_debug = true;
 				enable_debug_printf(true);
 			}
 			else if (strcasecmp(pArg, "-validate_etc1s") == 0)
 			{
-				m_comp_params.m_validate = true;
+				m_comp_params.m_validate_etc1s = true;
+			}
+			else if (strcasecmp(pArg, "-validate_output") == 0)
+			{
+				m_comp_params.m_validate_output_data = true;
 			}
 			else if (strcasecmp(pArg, "-debug_images") == 0)
 				m_comp_params.m_debug_images = true;
@@ -557,14 +595,35 @@
 			{
 				m_comp_params.m_multithreading = false;
 			}
+			else if (strcasecmp(pArg, "-parallel") == 0)
+			{
+				m_parallel_compression = true;
+			}
+			else if (strcasecmp(pArg, "-max_threads") == 0)
+			{
+				REMAINING_ARGS_CHECK(1);
+				m_max_threads = atoi(arg_v[arg_index + 1]);
+				arg_count++;
+			}
 			else if (strcasecmp(pArg, "-mipmap") == 0)
 				m_comp_params.m_mip_gen = true;
 			else if (strcasecmp(pArg, "-no_ktx") == 0)
 				m_no_ktx = true;
+			else if (strcasecmp(pArg, "-ktx_only") == 0)
+				m_ktx_only = true;
+			else if (strcasecmp(pArg, "-write_out") == 0)
+				m_write_out = true;
 			else if (strcasecmp(pArg, "-etc1_only") == 0)
 				m_etc1_only = true;
 			else if (strcasecmp(pArg, "-disable_hierarchical_endpoint_codebooks") == 0)
 				m_comp_params.m_disable_hierarchical_endpoint_codebooks = true;
+			else if (strcasecmp(pArg, "-opencl") == 0)
+			{
+				m_comp_params.m_use_opencl = true;
+			}
+			else if (strcasecmp(pArg, "-opencl_serialize") == 0)
+			{
+			}
 			else if (strcasecmp(pArg, "-mip_scale") == 0)
 			{
 				REMAINING_ARGS_CHECK(1);
@@ -612,32 +671,6 @@
 				m_comp_params.m_endpoint_rdo_thresh = (float)atof(arg_v[arg_index + 1]);
 				arg_count++;
 			}
-			else if (strcasecmp(pArg, "-global_sel_pal") == 0)
-				m_comp_params.m_global_sel_pal = true;
-			else if (strcasecmp(pArg, "-no_auto_global_sel_pal") == 0)
-				m_comp_params.m_auto_global_sel_pal = false;
-			else if (strcasecmp(pArg, "-auto_global_sel_pal") == 0)
-				m_comp_params.m_auto_global_sel_pal = true;
-			else if (strcasecmp(pArg, "-global_pal_bits") == 0)
-			{
-				REMAINING_ARGS_CHECK(1);
-				m_comp_params.m_global_pal_bits = atoi(arg_v[arg_index + 1]);
-				arg_count++;
-			}
-			else if (strcasecmp(pArg, "-global_mod_bits") == 0)
-			{
-				REMAINING_ARGS_CHECK(1);
-				m_comp_params.m_global_mod_bits = atoi(arg_v[arg_index + 1]);
-				arg_count++;
-			}
-			else if (strcasecmp(pArg, "-no_hybrid_sel_cb") == 0)
-				m_comp_params.m_no_hybrid_sel_cb = true;
-			else if (strcasecmp(pArg, "-hybrid_sel_cb_quality_thresh") == 0)
-			{
-				REMAINING_ARGS_CHECK(1);
-				m_comp_params.m_hybrid_sel_cb_quality_thresh = (float)atof(arg_v[arg_index + 1]);
-				arg_count++;
-			}
 			else if (strcasecmp(pArg, "-userdata0") == 0)
 			{
 				REMAINING_ARGS_CHECK(1);
@@ -664,17 +697,29 @@
 			else if (strcasecmp(pArg, "-tex_type") == 0)
 			{
 				REMAINING_ARGS_CHECK(1);
-				const char *pType = arg_v[arg_index + 1];
+				const char* pType = arg_v[arg_index + 1];
 				if (strcasecmp(pType, "2d") == 0)
 					m_comp_params.m_tex_type = basist::cBASISTexType2D;
 				else if (strcasecmp(pType, "2darray") == 0)
+				{
 					m_comp_params.m_tex_type = basist::cBASISTexType2DArray;
+					m_individual = false;
+				}
 				else if (strcasecmp(pType, "3d") == 0)
+				{
 					m_comp_params.m_tex_type = basist::cBASISTexTypeVolume;
+					m_individual = false;
+				}
 				else if (strcasecmp(pType, "cubemap") == 0)
+				{
 					m_comp_params.m_tex_type = basist::cBASISTexTypeCubemapArray;
+					m_individual = false;
+				}
 				else if (strcasecmp(pType, "video") == 0)
+				{
 					m_comp_params.m_tex_type = basist::cBASISTexTypeVideoFrames;
+					m_individual = false;
+				}
 				else
 				{
 					error_printf("Invalid texture type: %s\n", pType);
@@ -684,6 +729,8 @@
 			}
 			else if (strcasecmp(pArg, "-individual") == 0)
 				m_individual = true;
+			else if (strcasecmp(pArg, "-tex_array") == 0)
+				m_individual = false;
 			else if (strcasecmp(pArg, "-fuzz_testing") == 0)
 				m_fuzz_testing = true;
 			else if (strcasecmp(pArg, "-csv_file") == 0)
@@ -789,12 +836,20 @@
 	std::string m_csv_file;
 
 	std::string m_etc1s_use_global_codebooks_file;
+
+	std::string m_test_file_dir;
+	
+	uint32_t m_max_threads;
+		
 	bool m_individual;
 	bool m_no_ktx;
+	bool m_ktx_only;
+	bool m_write_out;
 	bool m_etc1_only;
 	bool m_fuzz_testing;
 	bool m_compare_ssim;
 	bool m_bench;
+	bool m_parallel_compression;
 };
 
 static bool expand_multifile(command_line_params &opts)
@@ -838,17 +893,17 @@
 
 struct basis_data
 {
-	basis_data(basist::etc1_global_selector_codebook& sel_codebook) : 
-		m_transcoder(&sel_codebook) 
+	basis_data() : 
+		m_transcoder() 
 	{
 	}
 	uint8_vec m_file_data;
 	basist::basisu_transcoder m_transcoder;
 };
 
-static basis_data *load_basis_file(const char *pInput_filename, basist::etc1_global_selector_codebook &sel_codebook, bool force_etc1s)
+static basis_data *load_basis_file(const char *pInput_filename, bool force_etc1s)
 {
-	basis_data* p = new basis_data(sel_codebook);
+	basis_data* p = new basis_data;
 	uint8_vec &basis_data = p->m_file_data;
 	if (!basisu::read_file_to_vec(pInput_filename, basis_data))
 	{
@@ -889,19 +944,21 @@
 
 static bool compress_mode(command_line_params &opts)
 {
-	basist::etc1_global_selector_codebook sel_codebook(basist::g_global_selector_cb_size, basist::g_global_selector_cb);
-
 	uint32_t num_threads = 1;
 
 	if (opts.m_comp_params.m_multithreading)
 	{
+		// We use std::thread::hardware_concurrency() as a hint to determine the default # of threads to put into a pool.
 		num_threads = std::thread::hardware_concurrency();
 		if (num_threads < 1)
 			num_threads = 1;
+		if (num_threads > opts.m_max_threads)
+			num_threads = opts.m_max_threads;
 	}
 
-	job_pool jpool(num_threads);
-	opts.m_comp_params.m_pJob_pool = &jpool;
+	job_pool compressor_jpool(opts.m_parallel_compression ? 1 : num_threads);
+	if (!opts.m_parallel_compression)
+		opts.m_comp_params.m_pJob_pool = &compressor_jpool;
 		
 	if (!expand_multifile(opts))
 	{
@@ -918,7 +975,7 @@
 	basis_data* pGlobal_codebook_data = nullptr;
 	if (opts.m_etc1s_use_global_codebooks_file.size())
 	{
-		pGlobal_codebook_data = load_basis_file(opts.m_etc1s_use_global_codebooks_file.c_str(), sel_codebook, true);
+		pGlobal_codebook_data = load_basis_file(opts.m_etc1s_use_global_codebooks_file.c_str(), true);
 		if (!pGlobal_codebook_data)
 			return false;
 
@@ -998,7 +1055,6 @@
 
 	params.m_read_source_images = true;
 	params.m_write_output_basis_files = true;
-	params.m_pSel_codebook = &sel_codebook;
 	params.m_pGlobal_codebooks = pGlobal_codebook_data ? &pGlobal_codebook_data->m_transcoder.get_lowlevel_etc1s_decoder() : nullptr; 
 	FILE *pCSV_file = nullptr;
 	if (opts.m_csv_file.size())
@@ -1019,7 +1075,21 @@
 	interval_timer all_tm;
 	all_tm.start();
 
+	basisu::vector<basis_compressor_params> comp_params_vec;
+
 	const size_t total_files = (opts.m_individual ? opts.m_input_filenames.size() : 1U);
+	bool result = true;
+
+	if ((opts.m_individual) && (opts.m_output_filename.size()))
+	{
+		if (total_files > 1)
+		{
+			error_printf("-output_file specified in individual mode, but multiple input files have been specified which would cause the output file to be written multiple times.\n");
+			delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
+			return false;
+		}
+	}
+
 	for (size_t file_index = 0; file_index < total_files; file_index++)
 	{
 		if (opts.m_individual)
@@ -1027,18 +1097,20 @@
 			params.m_source_filenames.resize(1);
 			params.m_source_filenames[0] = opts.m_input_filenames[file_index];
 
-			if (file_index < opts.m_input_alpha_filenames.size()) 
+			if (file_index < opts.m_input_alpha_filenames.size())
 			{
 				params.m_source_alpha_filenames.resize(1);
 				params.m_source_alpha_filenames[0] = opts.m_input_alpha_filenames[file_index];
-				
-				printf("Processing source file \"%s\", alpha file \"%s\"\n", params.m_source_filenames[0].c_str(), params.m_source_alpha_filenames[0].c_str());
+
+				if (params.m_status_output)
+					printf("Processing source file \"%s\", alpha file \"%s\"\n", params.m_source_filenames[0].c_str(), params.m_source_alpha_filenames[0].c_str());
 			}
 			else
 			{
 				params.m_source_alpha_filenames.resize(0);
-				
-				printf("Processing source file \"%s\"\n", params.m_source_filenames[0].c_str());
+
+				if (params.m_status_output)
+					printf("Processing source file \"%s\"\n", params.m_source_filenames[0].c_str());
 			}
 		}
 		else
@@ -1047,15 +1119,15 @@
 			params.m_source_alpha_filenames = opts.m_input_alpha_filenames;
 		}
 				
-		if ((opts.m_output_filename.size()) && (!opts.m_individual))
+		if (opts.m_output_filename.size())
 			params.m_out_filename = opts.m_output_filename;
-		else 
+		else
 		{
 			std::string filename;
-		
+
 			string_get_filename(opts.m_input_filenames[file_index].c_str(), filename);
 			string_remove_extension(filename);
-			
+
 			if (opts.m_ktx2_mode)
 				filename += ".ktx2";
 			else
@@ -1063,49 +1135,65 @@
 
 			if (opts.m_output_path.size())
 				string_combine_path(filename, opts.m_output_path.c_str(), filename.c_str());
-		
+
 			params.m_out_filename = filename;
 		}
-		
-		basis_compressor c;
 
-		if (!c.init(opts.m_comp_params))
+		if (opts.m_parallel_compression)
 		{
-			error_printf("basis_compressor::init() failed!\n");
-
-			if (pCSV_file)
-			{
-				fclose(pCSV_file);
-				pCSV_file = nullptr;
-			}
-
-			delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
-			return false;
-		}
-
-		interval_timer tm;
-		tm.start();
-
-		basis_compressor::error_code ec = c.process();
-
-		tm.stop();
-
-		if (ec == basis_compressor::cECSuccess)
-		{
-			printf("Compression succeeded to file \"%s\" size %u bytes in %3.3f secs\n", params.m_out_filename.c_str(), 
-				opts.m_ktx2_mode ? c.get_output_ktx2_file().size() : c.get_output_basis_file().size(), 
-				tm.get_elapsed_secs());
+			comp_params_vec.push_back(params);
 		}
 		else
 		{
-			bool exit_flag = true;
+			basis_compressor c;
 
-			switch (ec)
+			if (!c.init(opts.m_comp_params))
 			{
+				error_printf("basis_compressor::init() failed!\n");
+
+				if (pCSV_file)
+				{
+					fclose(pCSV_file);
+					pCSV_file = nullptr;
+				}
+
+				delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
+				return false;
+			}
+
+			interval_timer tm;
+			tm.start();
+
+			basis_compressor::error_code ec = c.process();
+
+			tm.stop();
+
+			if (ec == basis_compressor::cECSuccess)
+			{
+				if (params.m_status_output)
+				{
+					printf("Compression succeeded to file \"%s\" size %u bytes in %3.3f secs\n", params.m_out_filename.c_str(),
+						opts.m_ktx2_mode ? c.get_output_ktx2_file().size() : c.get_output_basis_file().size(),
+						tm.get_elapsed_secs());
+				}
+			}
+			else
+			{
+				result = false;
+
+				if (!params.m_status_output)
+				{
+					error_printf("Compression failed on file \"%s\"\n", params.m_out_filename.c_str());
+				}
+
+				bool exit_flag = true;
+
+				switch (ec)
+				{
 				case basis_compressor::cECFailedReadingSourceImages:
 				{
 					error_printf("Compressor failed reading a source image!\n");
-					
+
 					if (opts.m_individual)
 						exit_flag = false;
 
@@ -1141,85 +1229,104 @@
 				default:
 					error_printf("basis_compress::process() failed!\n");
 					break;
-			}
-		
-			if (exit_flag)
-			{
-				if (pCSV_file)
-				{
-					fclose(pCSV_file);
-					pCSV_file = nullptr;
 				}
 
-				delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
-				return false;
-			}
-		}
-
-		if ((pCSV_file) && (c.get_stats().size()))
-		{
-#if 0
-			for (size_t slice_index = 0; slice_index < c.get_stats().size(); slice_index++)
-			{
-				fprintf(pCSV_file, "\"%s\", %u, %u, %u, %u, %u, %f, %f, %f, %f, %f, %u, %u, %f\n",
-					params.m_out_filename.c_str(),
-					(uint32_t)slice_index, (uint32_t)c.get_stats().size(),
-					c.get_stats()[slice_index].m_width, c.get_stats()[slice_index].m_height, (uint32_t)c.get_any_source_image_has_alpha(),
-					c.get_basis_bits_per_texel(),
-					c.get_stats()[slice_index].m_basis_rgb_avg_psnr,
-					c.get_stats()[slice_index].m_basis_rgba_avg_psnr,
-					c.get_stats()[slice_index].m_basis_luma_709_psnr,
-					c.get_stats()[slice_index].m_best_etc1s_luma_709_psnr,
-					params.m_quality_level, (int)params.m_compression_level, tm.get_elapsed_secs());
-				fflush(pCSV_file);
-			}
-#else
-			if (c.get_stats().size())
-			{
-				float rgb_avg_psnr_min = 1e+9f, rgb_avg_psnr_avg = 0.0f;
-				float a_avg_psnr_min = 1e+9f, a_avg_psnr_avg = 0.0f;
-				float luma_709_psnr_min = 1e+9f, luma_709_psnr_avg = 0.0f;
-
-				for (size_t slice_index = 0; slice_index < c.get_stats().size(); slice_index++)
+				if (exit_flag)
 				{
-					rgb_avg_psnr_min = basisu::minimum(rgb_avg_psnr_min, c.get_stats()[slice_index].m_basis_rgb_avg_psnr);
-					rgb_avg_psnr_avg += c.get_stats()[slice_index].m_basis_rgb_avg_psnr;
+					if (pCSV_file)
+					{
+						fclose(pCSV_file);
+						pCSV_file = nullptr;
+					}
 
-					a_avg_psnr_min = basisu::minimum(a_avg_psnr_min, c.get_stats()[slice_index].m_basis_a_avg_psnr);
-					a_avg_psnr_avg += c.get_stats()[slice_index].m_basis_a_avg_psnr;
-
-					luma_709_psnr_min = basisu::minimum(luma_709_psnr_min, c.get_stats()[slice_index].m_basis_luma_709_psnr);
-					luma_709_psnr_avg += c.get_stats()[slice_index].m_basis_luma_709_psnr;
+					delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
+					return false;
 				}
-
-				rgb_avg_psnr_avg /= c.get_stats().size();
-				a_avg_psnr_avg /= c.get_stats().size();
-				luma_709_psnr_avg /= c.get_stats().size();
-				
-				fprintf(pCSV_file, "\"%s\", %u, %u, %u, %u, %u, %f, %f, %f, %f, %f, %u, %u, %f, %f, %f, %f, %f, %f, %f\n",
-					params.m_out_filename.c_str(),
-					c.get_basis_file_size(),
-					(uint32_t)c.get_stats().size(),
-					c.get_stats()[0].m_width, c.get_stats()[0].m_height, (uint32_t)c.get_any_source_image_has_alpha(),
-					c.get_basis_bits_per_texel(),
-					c.get_stats()[0].m_basis_rgb_avg_psnr,
-					c.get_stats()[0].m_basis_rgba_avg_psnr,
-					c.get_stats()[0].m_basis_luma_709_psnr,
-					c.get_stats()[0].m_best_etc1s_luma_709_psnr,
-					params.m_quality_level, (int)params.m_compression_level, tm.get_elapsed_secs(),
-					rgb_avg_psnr_min, rgb_avg_psnr_avg,
-					a_avg_psnr_min, a_avg_psnr_avg,
-					luma_709_psnr_min, luma_709_psnr_avg);
-				fflush(pCSV_file);
 			}
-#endif
-		}
-				
-		if (opts.m_individual)
-			printf("\n");
+
+			if ((pCSV_file) && (c.get_stats().size()))
+			{
+				if (c.get_stats().size())
+				{
+					float rgb_avg_psnr_min = 1e+9f, rgb_avg_psnr_avg = 0.0f;
+					float a_avg_psnr_min = 1e+9f, a_avg_psnr_avg = 0.0f;
+					float luma_709_psnr_min = 1e+9f, luma_709_psnr_avg = 0.0f;
+
+					for (size_t slice_index = 0; slice_index < c.get_stats().size(); slice_index++)
+					{
+						rgb_avg_psnr_min = basisu::minimum(rgb_avg_psnr_min, c.get_stats()[slice_index].m_basis_rgb_avg_psnr);
+						rgb_avg_psnr_avg += c.get_stats()[slice_index].m_basis_rgb_avg_psnr;
+
+						a_avg_psnr_min = basisu::minimum(a_avg_psnr_min, c.get_stats()[slice_index].m_basis_a_avg_psnr);
+						a_avg_psnr_avg += c.get_stats()[slice_index].m_basis_a_avg_psnr;
+
+						luma_709_psnr_min = basisu::minimum(luma_709_psnr_min, c.get_stats()[slice_index].m_basis_luma_709_psnr);
+						luma_709_psnr_avg += c.get_stats()[slice_index].m_basis_luma_709_psnr;
+					}
+
+					rgb_avg_psnr_avg /= c.get_stats().size();
+					a_avg_psnr_avg /= c.get_stats().size();
+					luma_709_psnr_avg /= c.get_stats().size();
+
+					fprintf(pCSV_file, "\"%s\", %u, %u, %u, %u, %u, %f, %f, %f, %f, %f, %u, %u, %f, %f, %f, %f, %f, %f, %f\n",
+						params.m_out_filename.c_str(),
+						c.get_basis_file_size(),
+						(uint32_t)c.get_stats().size(),
+						c.get_stats()[0].m_width, c.get_stats()[0].m_height, (uint32_t)c.get_any_source_image_has_alpha(),
+						c.get_basis_bits_per_texel(),
+						c.get_stats()[0].m_basis_rgb_avg_psnr,
+						c.get_stats()[0].m_basis_rgba_avg_psnr,
+						c.get_stats()[0].m_basis_luma_709_psnr,
+						c.get_stats()[0].m_best_etc1s_luma_709_psnr,
+						params.m_quality_level, (int)params.m_compression_level, tm.get_elapsed_secs(),
+						rgb_avg_psnr_min, rgb_avg_psnr_avg,
+						a_avg_psnr_min, a_avg_psnr_avg,
+						luma_709_psnr_min, luma_709_psnr_avg);
+					fflush(pCSV_file);
+				}
+			}
+
+			//if ((opts.m_individual) && (params.m_status_output))
+			//	printf("\n");
+
+		} // if (opts.m_parallel_compression)
 
 	} // file_index
 
+	if (opts.m_parallel_compression)
+	{
+		basisu::vector<parallel_results> results;
+
+		bool any_failed = basis_parallel_compress(
+			num_threads,
+			comp_params_vec,
+			results);
+		BASISU_NOTE_UNUSED(any_failed);
+
+		uint32_t total_successes = 0, total_failures = 0;
+		for (uint32_t i = 0; i < comp_params_vec.size(); i++)
+		{
+			if (results[i].m_error_code != basis_compressor::cECSuccess)
+			{
+				result = false;
+
+				total_failures++;
+
+				error_printf("File %u (first source image: \"%s\", output file: \"%s\") failed with error code %i!\n", i, 
+					comp_params_vec[i].m_source_filenames[0].c_str(), 
+					comp_params_vec[i].m_out_filename.c_str(),
+					(int)results[i].m_error_code);
+			}
+			else
+			{
+				total_successes++;
+			}
+		}
+
+		printf("Total successes: %u failures: %u\n", total_successes, total_failures);
+
+	} // if (opts.m_parallel_compression)
+		
 	all_tm.stop();
 
 	if (total_files > 1)
@@ -1233,7 +1340,7 @@
 	delete pGlobal_codebook_data; 
 	pGlobal_codebook_data = nullptr;
 		
-	return true;
+	return result;
 }
 
 static bool unpack_and_validate_ktx2_file(
@@ -1243,7 +1350,6 @@
 	command_line_params& opts,
 	FILE* pCSV_file,
 	basis_data* pGlobal_codebook_data,
-	basist::etc1_global_selector_codebook& sel_codebook,
 	uint32_t& total_unpack_warnings,
 	uint32_t& total_pvrtc_nonpow2_warnings)
 {
@@ -1253,7 +1359,7 @@
 
 	const bool validate_flag = (opts.m_mode == cValidate);
 
-	basist::ktx2_transcoder dec(&sel_codebook);
+	basist::ktx2_transcoder dec;
 
 	if (!dec.init(ktx2_file_data.data(), ktx2_file_data.size()))
 	{
@@ -1573,20 +1679,30 @@
 						}
 						//u.crop(level_info.m_orig_width, level_info.m_orig_height);
 
-						std::string rgb_filename;
-						if (gi.size() > 1)
-							rgb_filename = base_filename + string_format("_unpacked_rgb_%s_%u_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, face_index, layer_index);
-						else
-							rgb_filename = base_filename + string_format("_unpacked_rgb_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), face_index, layer_index);
-						if (!save_png(rgb_filename, u, cImageSaveIgnoreAlpha))
-						{
-							error_printf("Failed writing to PNG file \"%s\"\n", rgb_filename.c_str());
-							delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
-							return false;
-						}
-						printf("Wrote PNG file \"%s\"\n", rgb_filename.c_str());
+						bool is_astc = (transcoder_tex_fmt == basist::transcoder_texture_format::cTFASTC_4x4_RGBA);
+						bool write_png = true;
+#if !BASISU_USE_ASTC_DECOMPRESS
+						if (is_astc)
+							write_png = false;
+#endif
 
-						if (transcoder_tex_fmt == basist::transcoder_texture_format::cTFFXT1_RGB)
+						if ((!opts.m_ktx_only) && (write_png))
+						{
+							std::string rgb_filename;
+							if (gi.size() > 1)
+								rgb_filename = base_filename + string_format("_unpacked_rgb_%s_%u_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, face_index, layer_index);
+							else
+								rgb_filename = base_filename + string_format("_unpacked_rgb_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), face_index, layer_index);
+							if (!save_png(rgb_filename, u, cImageSaveIgnoreAlpha))
+							{
+								error_printf("Failed writing to PNG file \"%s\"\n", rgb_filename.c_str());
+								delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
+								return false;
+							}
+							printf("Wrote PNG file \"%s\"\n", rgb_filename.c_str());
+						}
+
+						if ((transcoder_tex_fmt == basist::transcoder_texture_format::cTFFXT1_RGB) && (opts.m_write_out))
 						{
 							std::string out_filename;
 							if (gi.size() > 1)
@@ -1601,7 +1717,7 @@
 							printf("Wrote .OUT file \"%s\"\n", out_filename.c_str());
 						}
 
-						if (basis_transcoder_format_has_alpha(transcoder_tex_fmt))
+						if (basis_transcoder_format_has_alpha(transcoder_tex_fmt) && (!opts.m_ktx_only) && (write_png))
 						{
 							std::string a_filename;
 							if (gi.size() > 1)
@@ -1648,13 +1764,12 @@
 	command_line_params& opts, 
 	FILE *pCSV_file,
 	basis_data* pGlobal_codebook_data,
-	basist::etc1_global_selector_codebook &sel_codebook,
 	uint32_t &total_unpack_warnings,
 	uint32_t &total_pvrtc_nonpow2_warnings)
 {
 	const bool validate_flag = (opts.m_mode == cValidate);
 
-	basist::basisu_transcoder dec(&sel_codebook);
+	basist::basisu_transcoder dec;
 
 	if (pGlobal_codebook_data)
 	{
@@ -1788,7 +1903,7 @@
 	printf("start_transcoding time: %3.3f ms\n", start_transcoding_time_ms);
 
 	basisu::vector< gpu_image_vec > gpu_images[(int)basist::transcoder_texture_format::cTFTotalTextureFormats];
-
+	
 	double total_format_transcoding_time_ms[(int)basist::transcoder_texture_format::cTFTotalTextureFormats];
 	clear_obj(total_format_transcoding_time_ms);
 
@@ -1907,6 +2022,86 @@
 
 	} // image_info
 
+	// Upack UASTC files separarely, to validate we can transcode slices to UASTC and unpack them to pixels.
+	// This is a special path because UASTC is not yet a valid transcoder_texture_format, but a lower-level block_format.
+	if (fileinfo.m_tex_format == basist::basis_tex_format::cUASTC4x4)
+	{
+		for (uint32_t image_index = 0; image_index < fileinfo.m_total_images; image_index++)
+		{
+			for (uint32_t level_index = 0; level_index < fileinfo.m_image_mipmap_levels[image_index]; level_index++)
+			{
+				basist::basisu_image_level_info level_info;
+
+				if (!dec.get_image_level_info(&basis_file_data[0], (uint32_t)basis_file_data.size(), level_info, image_index, level_index))
+				{
+					error_printf("Failed retrieving image level information (%u %u)!\n", image_index, level_index);
+					return false;
+				}
+
+				gpu_image gi;
+				gi.init(basisu::texture_format::cUASTC4x4, level_info.m_orig_width, level_info.m_orig_height);
+
+				// Fill the buffer with psuedo-random bytes, to help more visibly detect cases where the transcoder fails to write to part of the output.
+				fill_buffer_with_random_bytes(gi.get_ptr(), gi.get_size_in_bytes());
+
+				//uint32_t decode_flags = 0;
+
+				tm.start();
+
+				if (!dec.transcode_slice(
+					&basis_file_data[0], (uint32_t)basis_file_data.size(), 
+					level_info.m_first_slice_index, gi.get_ptr(), gi.get_total_blocks(), basist::block_format::cUASTC_4x4, gi.get_bytes_per_block()))
+				{
+					error_printf("Failed transcoding image level (%u %u) to UASTC!\n", image_index, level_index);
+					return false;
+				}
+
+				double total_transcode_time = tm.get_elapsed_ms();
+
+				printf("Transcode of image %u level %u res %ux%u format UASTC_4x4 succeeded in %3.3f ms\n", image_index, level_index, level_info.m_orig_width, level_info.m_orig_height, total_transcode_time);
+
+				if ((!validate_flag) && (!opts.m_ktx_only))
+				{
+					image u;
+					if (!gi.unpack(u))
+					{
+						error_printf("Warning: Failed unpacking GPU texture data (%u %u) to UASTC. \n", image_index, level_index);
+						return false;
+					}
+					//u.crop(level_info.m_orig_width, level_info.m_orig_height);
+
+					std::string rgb_filename;
+					if (fileinfo.m_image_mipmap_levels[image_index] > 1)
+						rgb_filename = base_filename + string_format("_unpacked_rgb_UASTC_4x4_%u_%04u.png", level_index, image_index);
+					else
+						rgb_filename = base_filename + string_format("_unpacked_rgb_UASTC_4x4_%04u.png", image_index);
+
+					if (!save_png(rgb_filename, u, cImageSaveIgnoreAlpha))
+					{
+						error_printf("Failed writing to PNG file \"%s\"\n", rgb_filename.c_str());
+						delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
+						return false;
+					}
+					printf("Wrote PNG file \"%s\"\n", rgb_filename.c_str());
+
+					std::string alpha_filename;
+					if (fileinfo.m_image_mipmap_levels[image_index] > 1)
+						alpha_filename = base_filename + string_format("_unpacked_a_UASTC_4x4_%u_%04u.png", level_index, image_index);
+					else
+						alpha_filename = base_filename + string_format("_unpacked_a_UASTC_4x4_%04u.png", image_index);
+					if (!save_png(alpha_filename, u, cImageSaveGrayscale, 3))
+					{
+						error_printf("Failed writing to PNG file \"%s\"\n", rgb_filename.c_str());
+						delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
+						return false;
+					}
+					printf("Wrote PNG file \"%s\"\n", alpha_filename.c_str());
+
+				}
+			}
+		}
+	}
+
 	if (!validate_flag)
 	{
 		// Now write KTX files and unpack them to individual PNG's
@@ -1985,20 +2180,30 @@
 					}
 					//u.crop(level_info.m_orig_width, level_info.m_orig_height);
 
-					std::string rgb_filename;
-					if (gi.size() > 1)
-						rgb_filename = base_filename + string_format("_unpacked_rgb_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index);
-					else
-						rgb_filename = base_filename + string_format("_unpacked_rgb_%s_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), image_index);
-					if (!save_png(rgb_filename, u, cImageSaveIgnoreAlpha))
-					{
-						error_printf("Failed writing to PNG file \"%s\"\n", rgb_filename.c_str());
-						delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
-						return false;
-					}
-					printf("Wrote PNG file \"%s\"\n", rgb_filename.c_str());
+					bool is_astc = (transcoder_tex_fmt == basist::transcoder_texture_format::cTFASTC_4x4_RGBA);
+					bool write_png = true;
+#if !BASISU_USE_ASTC_DECOMPRESS
+					if (is_astc)
+						write_png = false;
+#endif
 
-					if (transcoder_tex_fmt == basist::transcoder_texture_format::cTFFXT1_RGB)
+					if ((!opts.m_ktx_only) && (write_png))
+					{
+						std::string rgb_filename;
+						if (gi.size() > 1)
+							rgb_filename = base_filename + string_format("_unpacked_rgb_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index);
+						else
+							rgb_filename = base_filename + string_format("_unpacked_rgb_%s_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), image_index);
+						if (!save_png(rgb_filename, u, cImageSaveIgnoreAlpha))
+						{
+							error_printf("Failed writing to PNG file \"%s\"\n", rgb_filename.c_str());
+							delete pGlobal_codebook_data; pGlobal_codebook_data = nullptr;
+							return false;
+						}
+						printf("Wrote PNG file \"%s\"\n", rgb_filename.c_str());
+					}
+
+					if ((transcoder_tex_fmt == basist::transcoder_texture_format::cTFFXT1_RGB) && (opts.m_write_out))
 					{
 						std::string out_filename;
 						if (gi.size() > 1)
@@ -2013,7 +2218,7 @@
 						printf("Wrote .OUT file \"%s\"\n", out_filename.c_str());
 					}
 
-					if (basis_transcoder_format_has_alpha(transcoder_tex_fmt))
+					if (basis_transcoder_format_has_alpha(transcoder_tex_fmt) && (!opts.m_ktx_only) && (write_png))
 					{
 						std::string a_filename;
 						if (gi.size() > 1)
@@ -2085,7 +2290,7 @@
 
 				printf("Transcode of image %u level %u res %ux%u format %s succeeded in %3.3f ms\n", image_index, level_index, level_info.m_orig_width, level_info.m_orig_height, basist::basis_get_format_name(transcoder_tex_fmt), total_transcode_time);
 
-				if (!validate_flag)
+				if ((!validate_flag) && (!opts.m_ktx_only))
 				{
 					std::string rgb_filename(base_filename + string_format("_unpacked_rgb_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index));
 					if (!save_png(rgb_filename, img, cImageSaveIgnoreAlpha))
@@ -2154,7 +2359,7 @@
 
 				printf("Transcode of image %u level %u res %ux%u format %s succeeded in %3.3f ms\n", image_index, level_index, level_info.m_orig_width, level_info.m_orig_height, basist::basis_get_format_name(transcoder_tex_fmt), total_transcode_time);
 
-				if (!validate_flag)
+				if ((!validate_flag) && (!opts.m_ktx_only))
 				{
 					std::string rgb_filename(base_filename + string_format("_unpacked_rgb_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index));
 					if (!save_png(rgb_filename, img, cImageSaveIgnoreAlpha))
@@ -2218,7 +2423,7 @@
 
 				printf("Transcode of image %u level %u res %ux%u format %s succeeded in %3.3f ms\n", image_index, level_index, level_info.m_orig_width, level_info.m_orig_height, basist::basis_get_format_name(transcoder_tex_fmt), total_transcode_time);
 
-				if (!validate_flag)
+				if ((!validate_flag) && (!opts.m_ktx_only))
 				{
 					std::string rgb_filename(base_filename + string_format("_unpacked_rgb_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index));
 					if (!save_png(rgb_filename, img, cImageSaveIgnoreAlpha))
@@ -2274,13 +2479,15 @@
 
 static bool unpack_and_validate_mode(command_line_params &opts)
 {
-	const bool validate_flag = (opts.m_mode == cValidate);
-	basist::etc1_global_selector_codebook sel_codebook(basist::g_global_selector_cb_size, basist::g_global_selector_cb);
+	interval_timer tm;
+	tm.start();
 
+	//const bool validate_flag = (opts.m_mode == cValidate);
+		
 	basis_data* pGlobal_codebook_data = nullptr;
 	if (opts.m_etc1s_use_global_codebooks_file.size())
 	{
-		pGlobal_codebook_data = load_basis_file(opts.m_etc1s_use_global_codebooks_file.c_str(), sel_codebook, true);
+		pGlobal_codebook_data = load_basis_file(opts.m_etc1s_use_global_codebooks_file.c_str(), true);
 		if (!pGlobal_codebook_data)
 		{
 			error_printf("Failed loading global codebook data from file \"%s\"\n", opts.m_etc1s_use_global_codebooks_file.c_str());
@@ -2362,7 +2569,6 @@
 				opts,
 				pCSV_file,
 				pGlobal_codebook_data,
-				sel_codebook,
 				total_unpack_warnings,
 				total_pvrtc_nonpow2_warnings);
 		}
@@ -2375,7 +2581,6 @@
 				opts,
 				pCSV_file,
 				pGlobal_codebook_data,
-				sel_codebook,
 				total_unpack_warnings,
 				total_pvrtc_nonpow2_warnings);
 		}
@@ -2401,6 +2606,8 @@
 	else
 		printf("Success\n");
 
+	debug_printf("Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
+
 	if (pCSV_file)
 	{
 		fclose(pCSV_file);
@@ -2520,11 +2727,277 @@
 	
 	save_png("delta_img_a.png", delta_img, cImageSaveGrayscale, 3);
 	printf("Wrote delta_img_a.png\n");
+
+	uint32_t bins[5][512];
+	clear_obj(bins);
+
+	running_stat delta_stats[5];
+	basisu::rand rm;
+
+	double avg[5];
+	clear_obj(avg);
+
+	for (uint32_t y = 0; y < a.get_height(); y++)
+	{
+		for (uint32_t x = 0; x < a.get_width(); x++)
+		{
+			//color_rgba& d = delta_img(x, y);
+
+			for (int c = 0; c < 4; c++)
+			{
+				int delta = a(x, y)[c] - b(x, y)[c];
+				
+				//delta = clamp<int>((int)std::round(rm.gaussian(70.0f, 10.0f)), -255, 255);
+				
+				bins[c][delta + 256]++;
+				delta_stats[c].push(delta);
+
+				avg[c] += delta;
+			}
+			
+			int y_delta = a(x, y).get_709_luma() - b(x, y).get_709_luma();
+			bins[4][y_delta + 256]++;
+			delta_stats[4].push(y_delta);
+			
+			avg[4] += y_delta;
+
+		} // x
+	} // y
+
+	for (uint32_t i = 0; i <= 4; i++)
+		avg[i] /= a.get_total_pixels();
+
+	printf("\n");
+
+	//bins[2][256+-255] = 100000;
+	//bins[2][256-56] = 50000;
+
+	const uint32_t X_SIZE = 128, Y_SIZE = 40;
+
+	for (uint32_t c = 0; c <= 4; c++)
+	{
+		std::vector<uint8_t> plot[Y_SIZE + 1];
+		for (uint32_t i = 0; i < Y_SIZE; i++)
+		{
+			plot[i].resize(X_SIZE + 2);
+			memset(plot[i].data(), ' ', X_SIZE + 1);
+		}
+
+		uint32_t max_val = 0;
+		int max_val_bin_index = 0;
+		int lowest_bin_index = INT_MAX, highest_bin_index = INT_MIN;
+		double avg_val = 0;
+		double total_val = 0;
+		running_stat bin_stats;
+
+		for (int y = -255; y <= 255; y++)
+		{
+			uint32_t val = bins[c][256 + y];
+			if (!val)
+				continue;
+
+			bin_stats.push(y);
+			
+			total_val += (double)val;
+
+			lowest_bin_index = minimum(lowest_bin_index, y);
+			highest_bin_index = maximum(highest_bin_index, y);
+
+			if (val > max_val)
+			{
+				max_val = val;
+				max_val_bin_index = y;
+			}
+			avg_val += y * (double)val;
+		}
+		avg_val /= total_val;
+
+		int lo_limit = -(int)X_SIZE / 2;
+		int hi_limit = X_SIZE / 2;
+		for (int x = lo_limit; x <= hi_limit; x++)
+		{
+			uint32_t total = 0;
+			if (x == lo_limit)
+			{
+				for (int i = -255; i <= lo_limit; i++)
+					total += bins[c][256 + i];
+			}
+			else if (x == hi_limit)
+			{
+				for (int i = hi_limit; i <= 255; i++)
+					total += bins[c][256 + i];
+			}
+			else
+			{
+				total = bins[c][256 + x];
+			}
+
+			uint32_t height = max_val ? (total * Y_SIZE + max_val - 1) / max_val : 0;
+			
+			if (height)
+			{
+				for (uint32_t y = (Y_SIZE - 1) - (height - 1); y <= (Y_SIZE - 1); y++)
+					plot[y][x + X_SIZE / 2] = '*';
+			}
+		}
+
+		printf("%c delta histogram: total samples: %5.0f, max bin value: %u index: %i (%3.3f%% of total), range %i [%i,%i], weighted mean: %f\n", "RGBAY"[c], total_val, max_val, max_val_bin_index, max_val * 100.0f / total_val, highest_bin_index - lowest_bin_index + 1, lowest_bin_index, highest_bin_index, avg_val);
+		printf("bin mean: %f, bin std deviation: %f, non-zero bins: %u\n", bin_stats.get_mean(), bin_stats.get_std_dev(), bin_stats.get_num());
+		printf("delta mean: %f, delta std deviation: %f\n", delta_stats[c].get_mean(), delta_stats[c].get_std_dev());
+		printf("\n");
+
+		for (uint32_t y = 0; y < Y_SIZE; y++)
+			printf("%s\n", (char*)plot[y].data());
+
+		char tics[1024];
+		tics[0] = '\0';
+		char tics2[1024];
+		tics2[0] = '\0';
+
+		for (int x = 0; x <= (int)X_SIZE; x++)
+		{
+			char buf[64];
+			if (x == X_SIZE / 2)
+			{
+				while ((int)strlen(tics) < x)
+					strcat(tics, ".");
+				
+				while ((int)strlen(tics2) < x)
+					strcat(tics2, " ");
+
+				sprintf(buf, "0");
+				strcat(tics, buf);
+			}
+			else if (((x & 7) == 0) || (x == X_SIZE))
+			{
+				while ((int)strlen(tics) < x)
+					strcat(tics, ".");
+				
+				while ((int)strlen(tics2) < x)
+					strcat(tics2, " ");
+
+				int v = (x - (int)X_SIZE / 2);
+				sprintf(buf, "%i", v / 10);
+				strcat(tics, buf);
+				
+				if (v < 0)
+				{
+					if (-v < 10)
+						sprintf(buf, "%i", v % 10);
+					else
+						sprintf(buf, " %i", -v % 10);
+				}
+				else
+					sprintf(buf, "%i", v % 10);
+				strcat(tics2, buf);
+			}
+			else
+			{
+				while ((int)strlen(tics) < x)
+					strcat(tics, ".");
+			}
+		}
+		printf("%s\n", tics);
+		printf("%s\n", tics2);
+
+		printf("\n");
+	}
 	
 	return true;
 }
 
-#include "encoder/basisu_astc_decomp.h"
+static bool split_image_mode(command_line_params& opts)
+{
+	if (opts.m_input_filenames.size() != 1)
+	{
+		error_printf("Must specify one image filename using -file\n");
+		return false;
+	}
+
+	image a;
+	if (!load_image(opts.m_input_filenames[0].c_str(), a))
+	{
+		error_printf("Failed loading image from file \"%s\"!\n", opts.m_input_filenames[0].c_str());
+		return false;
+	}
+
+	printf("Loaded \"%s\", %ux%u, has alpha: %u\n", opts.m_input_filenames[0].c_str(), a.get_width(), a.get_height(), a.has_alpha());
+
+	if (!save_png("split_rgb.png", a, cImageSaveIgnoreAlpha))
+	{
+		fprintf(stderr, "Failed writing file split_rgb.png\n");
+		return false;
+	}
+	printf("Wrote file split_rgb.png\n");
+
+	for (uint32_t i = 0; i < 4; i++)
+	{
+		char buf[256];
+		snprintf(buf, sizeof(buf), "split_%c.png", "RGBA"[i]);
+		if (!save_png(buf, a, cImageSaveGrayscale, i))
+		{
+			fprintf(stderr, "Failed writing file %s\n", buf);
+			return false;
+		}
+		printf("Wrote file %s\n", buf);
+	}
+	
+	return true;
+}
+
+static bool combine_images_mode(command_line_params& opts)
+{
+	if (opts.m_input_filenames.size() != 2)
+	{
+		error_printf("Must specify two image filename using -file\n");
+		return false;
+	}
+
+	image a, b;
+	if (!load_image(opts.m_input_filenames[0].c_str(), a))
+	{
+		error_printf("Failed loading image from file \"%s\"!\n", opts.m_input_filenames[0].c_str());
+		return false;
+	}
+
+	printf("Loaded \"%s\", %ux%u, has alpha: %u\n", opts.m_input_filenames[0].c_str(), a.get_width(), a.get_height(), a.has_alpha());
+
+	if (!load_image(opts.m_input_filenames[1].c_str(), b))
+	{
+		error_printf("Failed loading image from file \"%s\"!\n", opts.m_input_filenames[1].c_str());
+		return false;
+	}
+
+	printf("Loaded \"%s\", %ux%u, has alpha: %u\n", opts.m_input_filenames[1].c_str(), b.get_width(), b.get_height(), b.has_alpha());
+
+	const uint32_t width = minimum(a.get_width(), b.get_width());
+	const uint32_t height = minimum(b.get_height(), b.get_height());
+
+	image combined_img(width, height);
+	for (uint32_t y = 0; y < height; y++)
+	{
+		for (uint32_t x = 0; x < width; x++)
+		{
+			combined_img(x, y) = a(x, y);
+			combined_img(x, y).a = b(x, y).g;
+		}
+	}
+
+	const char* pOutput_filename = "combined.png";
+	if (opts.m_output_filename.size())
+		pOutput_filename = opts.m_output_filename.c_str();
+		
+	if (!save_png(pOutput_filename, combined_img))
+	{
+		fprintf(stderr, "Failed writing file %s\n", pOutput_filename);
+		return false;
+	}
+	printf("Wrote file %s\n", pOutput_filename);
+
+	return true;
+}
+
+//#include "encoder/basisu_astc_decomp.h"
 #include "encoder/basisu_pvrtc1_4.h"
 
 static bool bench_mode(command_line_params& opts)
@@ -3032,7 +3505,7 @@
 						transcode_uastc_to_astc(encoded_uastc_blk, tastc_data);
 
 						color_rgba decoded_tastc_block[4][4];
-						basisu_astc::astc::decompress((uint8_t*)decoded_tastc_block, (uint8_t*)&tastc_data, false, 4, 4);
+						//basisu_astc::astc::decompress((uint8_t*)decoded_tastc_block, (uint8_t*)&tastc_data, false, 4, 4);
 
 						uastc2_img.set_block_clipped(&decoded_tastc_block[0][0], block_x * 4, block_y * 4, 4, 4);
 
@@ -3706,14 +4179,201 @@
 	return true;
 }
 
+const struct test_file
+{
+	const char* m_pFilename;
+	uint32_t m_etc1s_size;
+	float m_etc1s_psnr;
+	float m_uastc_psnr;
+} g_test_files[] = 
+{
+	{ "black_1x1.png", 189, 100.0f, 100.0f },
+	{ "kodim01.png", 30993, 27.40f, 44.14f },
+	{ "kodim02.png", 28529, 32.20f, 41.06f },
+	{ "kodim03.png", 23411, 32.57f, 44.87f },
+	{ "kodim04.png", 28256, 31.76f, 43.02f },
+	{ "kodim05.png", 32646, 25.94f, 40.28f },
+	{ "kodim06.png", 27336, 28.66f, 44.57f },
+	{ "kodim07.png", 26618, 31.51f, 43.94f },
+	{ "kodim08.png", 31133, 25.28f, 41.15f },
+	{ "kodim09.png", 24777, 32.05f, 45.85f },
+	{ "kodim10.png", 27247, 32.20f, 45.77f },
+	{ "kodim11.png", 26579, 29.22f, 43.68f },
+	{ "kodim12.png", 25102, 32.96f, 46.77f },
+	{ "kodim13.png", 31604, 24.25f, 41.25f },
+	{ "kodim14.png", 31162, 27.81f, 39.65f },
+	{ "kodim15.png", 25528, 31.26f, 42.87f },
+	{ "kodim16.png", 26894, 32.21f, 47.78f },
+	{ "kodim17.png", 29334, 31.40f, 45.66f },
+	{ "kodim18.png", 30929, 27.46f, 41.54f },
+	{ "kodim19.png", 27889, 29.69f, 44.95f },
+	{ "kodim20.png", 21104, 31.30f, 45.31f },
+	{ "kodim21.png", 25943, 28.53f, 44.45f },
+	{ "kodim22.png", 29277, 29.85f, 42.63f },
+	{ "kodim23.png", 23550, 31.69f, 45.11f },
+	{ "kodim24.png", 29613, 26.75f, 40.61f },
+	{ "white_1x1.png", 189, 100.0f, 100.0f },
+	{ "wikipedia.png", 38961, 24.10f, 30.47f },
+	{ "alpha0.png", 766, 100.0f, 56.16f }
+};
+const uint32_t TOTAL_TEST_FILES = sizeof(g_test_files) / sizeof(g_test_files[0]);
+
+static bool test_mode(command_line_params& opts)
+{
+	uint32_t total_mismatches = 0;
+
+	// TODO: Record min/max/avgs
+	// TODO: Add another ETC1S quality level
+
+	for (uint32_t i = 0; i < TOTAL_TEST_FILES; i++)
+	{
+		std::string filename(opts.m_test_file_dir);
+		if (filename.size())
+		{
+			filename.push_back('/');
+		}
+		filename += std::string(g_test_files[i].m_pFilename);
+
+		basisu::vector<image> source_images(1);
+
+		image& source_image = source_images[0];
+		if (!load_png(filename.c_str(), source_image))
+		{
+			error_printf("Failed loading test image \"%s\"\n", filename.c_str());
+			return false;
+		}
+
+		printf("Loaded file \"%s\", dimemsions %ux%u has alpha: %u\n", filename.c_str(), source_image.get_width(), source_image.get_height(), source_image.has_alpha());
+
+		image_stats stats;
+
+		uint32_t flags_and_quality;
+		float uastc_rdo_quality = 0.0f;
+		size_t data_size = 0;
+
+		// Test ETC1S
+		flags_and_quality = (opts.m_comp_params.m_multithreading ? cFlagThreaded : 0);
+
+		void* pData = basis_compress(source_images, flags_and_quality, uastc_rdo_quality, &data_size, &stats);
+		if (!pData)
+		{
+			error_printf("basis_compress() failed!\n");
+			return false;
+		}
+		basis_free_data(pData);
+
+		printf("ETC1S Size: %u, PSNR: %f\n", (uint32_t)data_size, stats.m_basis_rgba_avg_psnr);
+
+		float file_size_ratio = fabs((data_size / (float)g_test_files[i].m_etc1s_size) - 1.0f);
+		if (file_size_ratio > .015f)
+		{
+			error_printf("Expected ETC1S file size was %u, but got %u instead!\n", g_test_files[i].m_etc1s_size, (uint32_t)data_size);
+			total_mismatches++;
+		}
+
+		if (fabs(stats.m_basis_rgba_avg_psnr - g_test_files[i].m_etc1s_psnr) > .02f)
+		{
+			error_printf("Expected ETC1S RGBA Avg PSNR was %f, but got %f instead!\n", g_test_files[i].m_etc1s_psnr, stats.m_basis_rgba_avg_psnr);
+			total_mismatches++;
+		}
+
+		if (opencl_is_available())
+		{
+			flags_and_quality = (opts.m_comp_params.m_multithreading ? cFlagThreaded : 0) | cFlagUseOpenCL;
+
+			pData = basis_compress(source_images, flags_and_quality, uastc_rdo_quality, &data_size, &stats);
+			if (!pData)
+			{
+				error_printf("basis_compress() failed!\n");
+				return false;
+			}
+			basis_free_data(pData);
+
+			printf("ETC1S+OpenCL Size: %u, PSNR: %f\n", (uint32_t)data_size, stats.m_basis_rgba_avg_psnr);
+
+			file_size_ratio = fabs((data_size / (float)g_test_files[i].m_etc1s_size) - 1.0f);
+			if (file_size_ratio > .04f)
+			{
+				error_printf("Expected ETC1S+OpenCL file size was %u, but got %u instead!\n", g_test_files[i].m_etc1s_size, (uint32_t)data_size);
+				total_mismatches++;
+			}
+
+			if (g_test_files[i].m_etc1s_psnr == 100.0f)
+			{
+				// TODO
+				if (stats.m_basis_rgba_avg_psnr < 69.0f)
+				{
+					error_printf("Expected ETC1S+OpenCL RGBA Avg PSNR was %f, but got %f instead!\n", g_test_files[i].m_etc1s_psnr, stats.m_basis_rgba_avg_psnr);
+					total_mismatches++;
+				}
+			}
+			else if (fabs(stats.m_basis_rgba_avg_psnr - g_test_files[i].m_etc1s_psnr) > .2f)
+			{
+				error_printf("Expected ETC1S+OpenCL RGBA Avg PSNR was %f, but got %f instead!\n", g_test_files[i].m_etc1s_psnr, stats.m_basis_rgba_avg_psnr);
+				total_mismatches++;
+			}
+		}
+
+		// Test UASTC
+		flags_and_quality = (opts.m_comp_params.m_multithreading ? cFlagThreaded : 0) | cFlagUASTC;
+
+		pData = basis_compress(source_images, flags_and_quality, uastc_rdo_quality, &data_size, &stats);
+		if (!pData)
+		{
+			error_printf("basis_compress() failed!\n");
+			return false;
+		}
+		basis_free_data(pData);
+
+		printf("UASTC Size: %u, PSNR: %f\n", (uint32_t)data_size, stats.m_basis_rgba_avg_psnr);
+
+		if (fabs(stats.m_basis_rgba_avg_psnr - g_test_files[i].m_uastc_psnr) > .005f)
+		{
+			error_printf("Expected UASTC RGBA Avg PSNR was %f, but got %f instead!\n", g_test_files[i].m_etc1s_psnr, stats.m_basis_rgba_avg_psnr);
+			total_mismatches++;
+		}
+	}
+
+	printf("Total mismatches: %u\n", total_mismatches);
+
+	bool result = true;
+	if (total_mismatches)
+	{
+		error_printf("Test FAILED\n");
+		result = false;
+	}
+	else
+		printf("Test succeeded\n");
+
+	return result;
+}
+
 static int main_internal(int argc, const char **argv)
 {
-	printf("Basis Universal GPU Texture Compressor v" BASISU_TOOL_VERSION "\nCopyright (C) 2019-2021 Binomial LLC, All rights reserved\n");
+	printf("Basis Universal GPU Texture Compressor v" BASISU_TOOL_VERSION "\nCopyright (C) 2019-2022 Binomial LLC, All rights reserved\n");
 
 	//interval_timer tm;
 	//tm.start();
 
-	basisu_encoder_init();
+	// See if OpenCL support has been disabled. We don't want to parse the command line until the lib is initialized
+	bool use_opencl = false;
+	bool opencl_force_serialization = false;
+	for (int i = 1; i < argc; i++)
+	{
+		if (strcmp(argv[i], "-opencl") == 0)
+			use_opencl = true;
+		if (strcmp(argv[i], "-opencl_serialize") == 0)
+			opencl_force_serialization = true;
+	}
+
+#ifndef BASISU_SUPPORT_OPENCL
+	if (use_opencl)
+	{
+		fprintf(stderr, "WARNING: -opencl specified, but OpenCL support was not enabled at compile time! With cmake, use -D OPENCL=1. Falling back to CPU compression.\n");
+	}
+#endif
+
+	basisu_encoder_init(use_opencl, opencl_force_serialization);
 		
 	//printf("Encoder and transcoder libraries initialized in %3.3f ms\n", tm.get_elapsed_ms());
 
@@ -3735,9 +4395,9 @@
 	}
 
 #if BASISU_SUPPORT_SSE
-	printf("Using SSE 4.1: %u, Multithreading: %u, Zstandard support: %u\n", g_cpu_supports_sse41, (uint32_t)opts.m_comp_params.m_multithreading, basist::basisu_transcoder_supports_ktx2_zstd());
+	printf("Using SSE 4.1: %u, Multithreading: %u, Zstandard support: %u, OpenCL: %u\n", g_cpu_supports_sse41, (uint32_t)opts.m_comp_params.m_multithreading, basist::basisu_transcoder_supports_ktx2_zstd(), opencl_is_available());
 #else
-	printf("Multithreading: %u, Zstandard support: %u\n", (uint32_t)opts.m_comp_params.m_multithreading, basist::basisu_transcoder_supports_ktx2_zstd());
+	printf("Multithreading: %u, Zstandard support: %u, OpenCL: %u\n", (uint32_t)opts.m_comp_params.m_multithreading, basist::basisu_transcoder_supports_ktx2_zstd(), opencl_is_available());
 #endif
 
 	if (!opts.process_listing_files())
@@ -3782,6 +4442,15 @@
 	case cCompSize:
 		status = compsize_mode(opts);
 		break;
+	case cTest:
+		status = test_mode(opts);
+		break;
+	case cSplitImage:
+		status = split_image_mode(opts);
+		break;
+	case cCombineImages:
+		status = combine_images_mode(opts);
+		break;
 	default:
 		assert(0);
 		break;
@@ -3795,7 +4464,7 @@
 #ifdef _DEBUG
 	printf("DEBUG\n");
 #endif
-
+		
 	int status = EXIT_FAILURE;
 
 #if BASISU_CATCH_EXCEPTIONS
@@ -3817,3 +4486,4 @@
 
 	return status;
 }
+
diff --git a/bin/ocl_kernels.cl b/bin/ocl_kernels.cl
new file mode 100644
index 0000000..6eda24f
--- /dev/null
+++ b/bin/ocl_kernels.cl
@@ -0,0 +1,1290 @@
+//#define _DEBUG
+
+#ifndef NULL
+	#define NULL 0L
+#endif
+
+typedef char int8_t;
+typedef uchar uint8_t;
+
+typedef short int16_t;
+typedef ushort uint16_t;
+
+typedef int int32_t;
+typedef uint uint32_t;
+
+typedef long int64_t;
+typedef ulong uint64_t;
+
+typedef uchar4 color_rgba;
+
+#define UINT32_MAX 0xFFFFFFFFUL
+#define INT64_MAX LONG_MAX
+#define UINT64_MAX ULONG_MAX
+
+int squarei(int a) { return a * a; }
+
+#ifdef _DEBUG
+	inline void internal_assert(bool x, constant char *pMsg, int line)
+	{
+		if (!x)
+			printf("assert() failed on line %i: %s\n", line, pMsg);
+	}
+	#define assert(x) internal_assert(x, #x, __LINE__)
+#else
+	#define assert(x)
+#endif
+
+inline uint8_t clamp255(int x)
+{
+	return clamp(x, 0, 255);
+}
+
+inline uint8_t clamp255_flag(int x, bool *pDid_clamp)
+{
+	if (x < 0)
+	{
+		*pDid_clamp = true;
+		return 0;
+	}
+	else if (x > 255)
+	{
+		*pDid_clamp = true;
+		return 255;
+	}
+
+	return (uint8_t)(x);
+}
+
+typedef struct __attribute__ ((packed)) encode_etc1s_param_struct_tag
+{
+	uint32_t m_total_blocks;
+	int m_perceptual;
+	int m_total_perms;
+} encode_etc1s_param_struct;
+
+typedef struct __attribute__ ((packed)) pixel_block_tag
+{
+	color_rgba m_pixels[16]; // [y*4+x]
+} pixel_block;
+
+uint color_distance(bool perceptual, color_rgba e1, color_rgba e2, bool alpha)
+{
+	if (perceptual)
+	{
+#if 0
+		float3 delta_rgb = (float3)(e1.x - e2.x, e1.y - e2.y, e1.z - e2.z);
+
+		float3 delta_ycbcr;
+		delta_ycbcr.x = dot(delta_rgb, (float3)(.2126f, .7152f, .0722f)); // y
+		delta_ycbcr.y = delta_rgb.x - delta_ycbcr.x; // cr
+		delta_ycbcr.z = delta_rgb.z - delta_ycbcr.x; // cb
+
+		delta_ycbcr *= delta_ycbcr;
+
+		float d = dot(delta_ycbcr, (float3)(1.0f, 0.203125f, 0.0234375f));
+
+		if (alpha)
+		{
+			int delta_a = e1.w - e2.w;
+			d += delta_a * delta_a;
+		}
+
+		d = clamp(d * 256.0f + .5f, 0.0f, (float)UINT32_MAX);
+
+		return (uint)(d);
+#else
+		// This matches the CPU code, which is useful for testing.
+		int dr = e1.x - e2.x;
+		int dg = e1.y - e2.y;
+		int db = e1.z - e2.z;
+
+		int delta_l = dr * 27 + dg * 92 + db * 9;
+		int delta_cr = dr * 128 - delta_l;
+		int delta_cb = db * 128 - delta_l;
+
+		uint id = ((uint)(delta_l * delta_l) >> 7U) +
+			((((uint)(delta_cr * delta_cr) >> 7U) * 26U) >> 7U) +
+			((((uint)(delta_cb * delta_cb) >> 7U) * 3U) >> 7U);
+
+		if (alpha)
+		{
+			int da = (e1.w - e2.w) << 7;
+			id += ((uint)(da * da) >> 7U);
+		}
+		
+		return id;
+#endif
+	}
+	else if (alpha)
+	{
+		int dr = e1.x - e2.x;
+		int dg = e1.y - e2.y;
+		int db = e1.z - e2.z;	
+		int da = e1.w - e2.w;
+		return dr * dr + dg * dg + db * db + da * da;
+	}
+	else
+	{
+		int dr = e1.x - e2.x;
+		int dg = e1.y - e2.y;
+		int db = e1.z - e2.z;	
+		return dr * dr + dg * dg + db * db;
+	}
+}
+
+typedef struct __attribute__ ((packed)) etc_block_tag
+{
+	// big endian uint64:
+	// bit ofs:  56  48  40  32  24  16   8   0
+	// byte ofs: b0, b1, b2, b3, b4, b5, b6, b7 
+	union
+	{
+		uint64_t m_uint64;
+		uint8_t m_bytes[8];
+	};
+
+} etc_block;
+
+enum etc_constants
+{
+	cETC1BytesPerBlock = 8U,
+
+	cETC1SelectorBits = 2U,
+	cETC1SelectorValues = 1U << cETC1SelectorBits,
+	cETC1SelectorMask = cETC1SelectorValues - 1U,
+
+	cETC1BlockShift = 2U,
+	cETC1BlockSize = 1U << cETC1BlockShift,
+
+	cETC1LSBSelectorIndicesBitOffset = 0,
+	cETC1MSBSelectorIndicesBitOffset = 16,
+
+	cETC1FlipBitOffset = 32,
+	cETC1DiffBitOffset = 33,
+
+	cETC1IntenModifierNumBits = 3,
+	cETC1IntenModifierValues = 1 << cETC1IntenModifierNumBits,
+	cETC1RightIntenModifierTableBitOffset = 34,
+	cETC1LeftIntenModifierTableBitOffset = 37,
+
+	// Base+Delta encoding (5 bit bases, 3 bit delta)
+	cETC1BaseColorCompNumBits = 5,
+	cETC1BaseColorCompMax = 1 << cETC1BaseColorCompNumBits,
+
+	cETC1DeltaColorCompNumBits = 3,
+	cETC1DeltaColorComp = 1 << cETC1DeltaColorCompNumBits,
+	cETC1DeltaColorCompMax = 1 << cETC1DeltaColorCompNumBits,
+
+	cETC1BaseColor5RBitOffset = 59,
+	cETC1BaseColor5GBitOffset = 51,
+	cETC1BaseColor5BBitOffset = 43,
+
+	cETC1DeltaColor3RBitOffset = 56,
+	cETC1DeltaColor3GBitOffset = 48,
+	cETC1DeltaColor3BBitOffset = 40,
+
+	// Absolute (non-delta) encoding (two 4-bit per component bases)
+	cETC1AbsColorCompNumBits = 4,
+	cETC1AbsColorCompMax = 1 << cETC1AbsColorCompNumBits,
+
+	cETC1AbsColor4R1BitOffset = 60,
+	cETC1AbsColor4G1BitOffset = 52,
+	cETC1AbsColor4B1BitOffset = 44,
+
+	cETC1AbsColor4R2BitOffset = 56,
+	cETC1AbsColor4G2BitOffset = 48,
+	cETC1AbsColor4B2BitOffset = 40,
+
+	cETC1ColorDeltaMin = -4,
+	cETC1ColorDeltaMax = 3,
+
+	// Delta3:
+	// 0   1   2   3   4   5   6   7
+	// 000 001 010 011 100 101 110 111
+	// 0   1   2   3   -4  -3  -2  -1
+};
+
+#define BASISU_ETC1_CLUSTER_FIT_ORDER_TABLE_SIZE (165)
+constant struct { uint8_t m_v[4]; } g_cluster_fit_order_tab[BASISU_ETC1_CLUSTER_FIT_ORDER_TABLE_SIZE] =
+{
+	{ { 0, 0, 0, 8 } },{ { 0, 5, 2, 1 } },{ { 0, 6, 1, 1 } },{ { 0, 7, 0, 1 } },{ { 0, 7, 1, 0 } },
+	{ { 0, 0, 8, 0 } },{ { 0, 0, 3, 5 } },{ { 0, 1, 7, 0 } },{ { 0, 0, 4, 4 } },{ { 0, 0, 2, 6 } },
+	{ { 0, 0, 7, 1 } },{ { 0, 0, 1, 7 } },{ { 0, 0, 5, 3 } },{ { 1, 6, 0, 1 } },{ { 0, 0, 6, 2 } },
+	{ { 0, 2, 6, 0 } },{ { 2, 4, 2, 0 } },{ { 0, 3, 5, 0 } },{ { 3, 3, 1, 1 } },{ { 4, 2, 0, 2 } },
+	{ { 1, 5, 2, 0 } },{ { 0, 5, 3, 0 } },{ { 0, 6, 2, 0 } },{ { 2, 4, 1, 1 } },{ { 5, 1, 0, 2 } },
+	{ { 6, 1, 1, 0 } },{ { 3, 3, 0, 2 } },{ { 6, 0, 0, 2 } },{ { 0, 8, 0, 0 } },{ { 6, 1, 0, 1 } },
+	{ { 0, 1, 6, 1 } },{ { 1, 6, 1, 0 } },{ { 4, 1, 3, 0 } },{ { 0, 2, 5, 1 } },{ { 5, 0, 3, 0 } },
+	{ { 5, 3, 0, 0 } },{ { 0, 1, 5, 2 } },{ { 0, 3, 4, 1 } },{ { 2, 5, 1, 0 } },{ { 1, 7, 0, 0 } },
+	{ { 0, 1, 4, 3 } },{ { 6, 0, 2, 0 } },{ { 0, 4, 4, 0 } },{ { 2, 6, 0, 0 } },{ { 0, 2, 4, 2 } },
+	{ { 0, 5, 1, 2 } },{ { 0, 6, 0, 2 } },{ { 3, 5, 0, 0 } },{ { 0, 4, 3, 1 } },{ { 3, 4, 1, 0 } },
+	{ { 4, 3, 1, 0 } },{ { 1, 5, 0, 2 } },{ { 0, 3, 3, 2 } },{ { 1, 4, 1, 2 } },{ { 0, 4, 2, 2 } },
+	{ { 2, 3, 3, 0 } },{ { 4, 4, 0, 0 } },{ { 1, 2, 4, 1 } },{ { 0, 5, 0, 3 } },{ { 0, 1, 3, 4 } },
+	{ { 1, 5, 1, 1 } },{ { 1, 4, 2, 1 } },{ { 1, 3, 2, 2 } },{ { 5, 2, 1, 0 } },{ { 1, 3, 3, 1 } },
+	{ { 0, 1, 2, 5 } },{ { 1, 1, 5, 1 } },{ { 0, 3, 2, 3 } },{ { 2, 5, 0, 1 } },{ { 3, 2, 2, 1 } },
+	{ { 2, 3, 0, 3 } },{ { 1, 4, 3, 0 } },{ { 2, 2, 1, 3 } },{ { 6, 2, 0, 0 } },{ { 1, 0, 6, 1 } },
+	{ { 3, 3, 2, 0 } },{ { 7, 1, 0, 0 } },{ { 3, 1, 4, 0 } },{ { 0, 2, 3, 3 } },{ { 0, 4, 1, 3 } },
+	{ { 0, 4, 0, 4 } },{ { 0, 1, 0, 7 } },{ { 2, 0, 5, 1 } },{ { 2, 0, 4, 2 } },{ { 3, 0, 2, 3 } },
+	{ { 2, 2, 4, 0 } },{ { 2, 2, 3, 1 } },{ { 4, 0, 3, 1 } },{ { 3, 2, 3, 0 } },{ { 2, 3, 2, 1 } },
+	{ { 1, 3, 4, 0 } },{ { 7, 0, 1, 0 } },{ { 3, 0, 4, 1 } },{ { 1, 0, 5, 2 } },{ { 8, 0, 0, 0 } },
+	{ { 3, 0, 1, 4 } },{ { 4, 1, 1, 2 } },{ { 4, 0, 2, 2 } },{ { 1, 2, 5, 0 } },{ { 4, 2, 1, 1 } },
+	{ { 3, 4, 0, 1 } },{ { 2, 0, 3, 3 } },{ { 5, 0, 1, 2 } },{ { 5, 0, 0, 3 } },{ { 2, 4, 0, 2 } },
+	{ { 2, 1, 4, 1 } },{ { 4, 0, 1, 3 } },{ { 2, 1, 5, 0 } },{ { 4, 2, 2, 0 } },{ { 4, 0, 4, 0 } },
+	{ { 1, 0, 4, 3 } },{ { 1, 4, 0, 3 } },{ { 3, 0, 3, 2 } },{ { 4, 3, 0, 1 } },{ { 0, 1, 1, 6 } },
+	{ { 1, 3, 1, 3 } },{ { 0, 2, 2, 4 } },{ { 2, 0, 2, 4 } },{ { 5, 1, 1, 1 } },{ { 3, 0, 5, 0 } },
+	{ { 2, 3, 1, 2 } },{ { 3, 0, 0, 5 } },{ { 0, 3, 1, 4 } },{ { 5, 0, 2, 1 } },{ { 2, 1, 3, 2 } },
+	{ { 2, 0, 6, 0 } },{ { 3, 1, 3, 1 } },{ { 5, 1, 2, 0 } },{ { 1, 0, 3, 4 } },{ { 1, 1, 6, 0 } },
+	{ { 4, 0, 0, 4 } },{ { 2, 0, 1, 5 } },{ { 0, 3, 0, 5 } },{ { 1, 3, 0, 4 } },{ { 4, 1, 2, 1 } },
+	{ { 1, 2, 3, 2 } },{ { 3, 1, 0, 4 } },{ { 5, 2, 0, 1 } },{ { 1, 2, 2, 3 } },{ { 3, 2, 1, 2 } },
+	{ { 2, 2, 2, 2 } },{ { 6, 0, 1, 1 } },{ { 1, 2, 1, 4 } },{ { 1, 1, 4, 2 } },{ { 3, 2, 0, 3 } },
+	{ { 1, 2, 0, 5 } },{ { 1, 0, 7, 0 } },{ { 3, 1, 2, 2 } },{ { 1, 0, 2, 5 } },{ { 2, 0, 0, 6 } },
+	{ { 2, 1, 1, 4 } },{ { 2, 2, 0, 4 } },{ { 1, 1, 3, 3 } },{ { 7, 0, 0, 1 } },{ { 1, 0, 0, 7 } },
+	{ { 2, 1, 2, 3 } },{ { 4, 1, 0, 3 } },{ { 3, 1, 1, 3 } },{ { 1, 1, 2, 4 } },{ { 2, 1, 0, 5 } },
+	{ { 1, 0, 1, 6 } },{ { 0, 2, 1, 5 } },{ { 0, 2, 0, 6 } },{ { 1, 1, 1, 5 } },{ { 1, 1, 0, 6 } }
+};
+
+constant int g_etc1_inten_tables[cETC1IntenModifierValues][cETC1SelectorValues] =
+{
+	{ -8,  -2,   2,   8 }, { -17,  -5,  5,  17 }, { -29,  -9,   9,  29 }, {  -42, -13, 13,  42 },
+	{ -60, -18, 18,  60 }, { -80, -24, 24,  80 }, { -106, -33, 33, 106 }, { -183, -47, 47, 183 }
+};
+
+constant uint8_t g_etc1_to_selector_index[cETC1SelectorValues] = { 2, 3, 1, 0 };
+constant uint8_t g_selector_index_to_etc1[cETC1SelectorValues] = { 3, 2, 0, 1 };
+
+uint32_t etc_block_get_byte_bits(const etc_block *p, uint32_t ofs, uint32_t num) 
+{
+	assert((ofs + num) <= 64U);
+	assert(num && (num <= 8U));
+	assert((ofs >> 3) == ((ofs + num - 1) >> 3));
+	const uint32_t byte_ofs = 7 - (ofs >> 3);
+	const uint32_t byte_bit_ofs = ofs & 7;
+	return (p->m_bytes[byte_ofs] >> byte_bit_ofs) & ((1 << num) - 1);
+}
+
+void etc_block_set_byte_bits(etc_block *p, uint32_t ofs, uint32_t num, uint32_t bits)
+{
+	assert((ofs + num) <= 64U);
+	assert(num && (num < 32U));
+	assert((ofs >> 3) == ((ofs + num - 1) >> 3));
+	assert(bits < (1U << num));
+	const uint32_t byte_ofs = 7 - (ofs >> 3);
+	const uint32_t byte_bit_ofs = ofs & 7;
+	const uint32_t mask = (1 << num) - 1;
+	p->m_bytes[byte_ofs] &= ~(mask << byte_bit_ofs);
+	p->m_bytes[byte_ofs] |= (bits << byte_bit_ofs);
+}
+
+bool etc_block_get_flip_bit(const etc_block *p) 
+{
+	return (p->m_bytes[3] & 1) != 0;
+}
+
+void etc_block_set_flip_bit(etc_block *p, bool flip)
+{
+	p->m_bytes[3] &= ~1;
+	p->m_bytes[3] |= (uint8_t)(flip);
+}
+
+bool etc_block_get_diff_bit(const etc_block *p) 
+{
+	return (p->m_bytes[3] & 2) != 0;
+}
+
+void etc_block_set_diff_bit(etc_block *p, bool diff)
+{
+	p->m_bytes[3] &= ~2;
+	p->m_bytes[3] |= ((uint32_t)(diff) << 1);
+}
+
+// Returns intensity modifier table (0-7) used by subblock subblock_id.
+// subblock_id=0 left/top (CW 1), 1=right/bottom (CW 2)
+uint32_t etc_block_get_inten_table(const etc_block *p, uint32_t subblock_id) 
+{
+	assert(subblock_id < 2);
+	const uint32_t ofs = subblock_id ? 2 : 5;
+	return (p->m_bytes[3] >> ofs) & 7;
+}
+
+// Sets intensity modifier table (0-7) used by subblock subblock_id (0 or 1)
+void etc_block_set_inten_table(etc_block *p, uint32_t subblock_id, uint32_t t)
+{
+	assert(subblock_id < 2);
+	assert(t < 8);
+	const uint32_t ofs = subblock_id ? 2 : 5;
+	p->m_bytes[3] &= ~(7 << ofs);
+	p->m_bytes[3] |= (t << ofs);
+}
+
+void etc_block_set_inten_tables_etc1s(etc_block *p, uint32_t t)
+{
+	etc_block_set_inten_table(p, 0, t);
+	etc_block_set_inten_table(p, 1, t);
+}
+
+uint32_t etc_block_get_raw_selector(const etc_block *pBlock, uint32_t x, uint32_t y) 
+{
+	assert((x | y) < 4);
+
+	const uint32_t bit_index = x * 4 + y;
+	const uint32_t byte_bit_ofs = bit_index & 7;
+	const uint8_t *p = &pBlock->m_bytes[7 - (bit_index >> 3)];
+	const uint32_t lsb = (p[0] >> byte_bit_ofs) & 1;
+	const uint32_t msb = (p[-2] >> byte_bit_ofs) & 1;
+	const uint32_t val = lsb | (msb << 1);
+
+	return val;
+}
+
+// Returned selector value ranges from 0-3 and is a direct index into g_etc1_inten_tables.
+uint32_t etc_block_get_selector(const etc_block *pBlock, uint32_t x, uint32_t y) 
+{
+	return g_etc1_to_selector_index[etc_block_get_raw_selector(pBlock, x, y)];
+}
+
+// Selector "val" ranges from 0-3 and is a direct index into g_etc1_inten_tables.
+void etc_block_set_selector(etc_block *pBlock, uint32_t x, uint32_t y, uint32_t val)
+{
+	assert((x | y | val) < 4);
+	const uint32_t bit_index = x * 4 + y;
+
+	uint8_t *p = &pBlock->m_bytes[7 - (bit_index >> 3)];
+
+	const uint32_t byte_bit_ofs = bit_index & 7;
+	const uint32_t mask = 1 << byte_bit_ofs;
+
+	const uint32_t etc1_val = g_selector_index_to_etc1[val];
+	
+	const uint32_t lsb = etc1_val & 1;
+	const uint32_t msb = etc1_val >> 1;
+
+	p[0] &= ~mask;
+	p[0] |= (lsb << byte_bit_ofs);
+
+	p[-2] &= ~mask;
+	p[-2] |= (msb << byte_bit_ofs);
+}
+
+void etc_block_set_base4_color(etc_block *pBlock, uint32_t idx, uint16_t c)
+{
+	if (idx)
+	{
+		etc_block_set_byte_bits(pBlock, cETC1AbsColor4R2BitOffset, 4, (c >> 8) & 15);
+		etc_block_set_byte_bits(pBlock, cETC1AbsColor4G2BitOffset, 4, (c >> 4) & 15);
+		etc_block_set_byte_bits(pBlock, cETC1AbsColor4B2BitOffset, 4, c & 15);
+	}
+	else
+	{
+		etc_block_set_byte_bits(pBlock, cETC1AbsColor4R1BitOffset, 4, (c >> 8) & 15);
+		etc_block_set_byte_bits(pBlock, cETC1AbsColor4G1BitOffset, 4, (c >> 4) & 15);
+		etc_block_set_byte_bits(pBlock, cETC1AbsColor4B1BitOffset, 4, c & 15);
+	}
+}
+
+uint16_t etc_block_get_base4_color(const etc_block *pBlock, uint32_t idx) 
+{
+	uint32_t r, g, b;
+	if (idx)
+	{
+		r = etc_block_get_byte_bits(pBlock, cETC1AbsColor4R2BitOffset, 4);
+		g = etc_block_get_byte_bits(pBlock, cETC1AbsColor4G2BitOffset, 4);
+		b = etc_block_get_byte_bits(pBlock, cETC1AbsColor4B2BitOffset, 4);
+	}
+	else
+	{
+		r = etc_block_get_byte_bits(pBlock, cETC1AbsColor4R1BitOffset, 4);
+		g = etc_block_get_byte_bits(pBlock, cETC1AbsColor4G1BitOffset, 4);
+		b = etc_block_get_byte_bits(pBlock, cETC1AbsColor4B1BitOffset, 4);
+	}
+	return (uint16_t)(b | (g << 4U) | (r << 8U));
+}
+
+void etc_block_set_base5_color(etc_block *pBlock, uint16_t c)
+{
+	etc_block_set_byte_bits(pBlock, cETC1BaseColor5RBitOffset, 5, (c >> 10) & 31);
+	etc_block_set_byte_bits(pBlock, cETC1BaseColor5GBitOffset, 5, (c >> 5) & 31);
+	etc_block_set_byte_bits(pBlock, cETC1BaseColor5BBitOffset, 5, c & 31);
+}
+
+uint16_t etc_block_get_base5_color(const etc_block *pBlock)
+{
+	const uint32_t r = etc_block_get_byte_bits(pBlock, cETC1BaseColor5RBitOffset, 5);
+	const uint32_t g = etc_block_get_byte_bits(pBlock, cETC1BaseColor5GBitOffset, 5);
+	const uint32_t b = etc_block_get_byte_bits(pBlock, cETC1BaseColor5BBitOffset, 5);
+	return (uint16_t)(b | (g << 5U) | (r << 10U));
+}
+
+void etc_block_set_delta3_color(etc_block *pBlock, uint16_t c)
+{
+	etc_block_set_byte_bits(pBlock, cETC1DeltaColor3RBitOffset, 3, (c >> 6) & 7);
+	etc_block_set_byte_bits(pBlock, cETC1DeltaColor3GBitOffset, 3, (c >> 3) & 7);
+	etc_block_set_byte_bits(pBlock, cETC1DeltaColor3BBitOffset, 3, c & 7);
+}
+
+uint16_t etc_block_get_delta3_color(const etc_block *pBlock) 
+{
+	const uint32_t r = etc_block_get_byte_bits(pBlock, cETC1DeltaColor3RBitOffset, 3);
+	const uint32_t g = etc_block_get_byte_bits(pBlock, cETC1DeltaColor3GBitOffset, 3);
+	const uint32_t b = etc_block_get_byte_bits(pBlock, cETC1DeltaColor3BBitOffset, 3);
+	return (uint16_t)(b | (g << 3U) | (r << 6U));
+}
+
+void etc_block_unpack_delta3(int *pR, int *pG, int *pB, uint16_t packed_delta3)
+{
+	int r = (packed_delta3 >> 6) & 7;
+	int g = (packed_delta3 >> 3) & 7;
+	int b = packed_delta3 & 7;
+	if (r >= 4) r -= 8;
+	if (g >= 4) g -= 8;
+	if (b >= 4) b -= 8;
+	*pR = r;
+	*pG = g;
+	*pB = b;
+}
+
+bool etc_block_unpack_color5_delta3(color_rgba *pResult, uint16_t packed_color5, uint16_t packed_delta3, bool scaled, uint32_t alpha)
+{
+	int dr, dg, db;
+	etc_block_unpack_delta3(&dr, &dg, &db, packed_delta3);
+
+	int b = (packed_color5 & 31U) + db;
+	int g = ((packed_color5 >> 5U) & 31U) + dg;
+	int r = ((packed_color5 >> 10U) & 31U) + dr;
+
+	bool success = true;
+	if ((uint32_t)(r | g | b) > 31U)
+	{
+		success = false;
+		r = clamp(r, 0, 31);
+		g = clamp(g, 0, 31);
+		b = clamp(b, 0, 31);
+	}
+
+	if (scaled)
+	{
+		b = (b << 3U) | (b >> 2U);
+		g = (g << 3U) | (g >> 2U);
+		r = (r << 3U) | (r >> 2U);
+	}
+
+	*pResult = (color_rgba)(r, g, b, min(alpha, 255U));
+	return success;
+}
+
+color_rgba etc_block_unpack_color5(uint16_t packed_color5, bool scaled, uint32_t alpha)
+{
+	uint32_t b = packed_color5 & 31U;
+	uint32_t g = (packed_color5 >> 5U) & 31U;
+	uint32_t r = (packed_color5 >> 10U) & 31U;
+
+	if (scaled)
+	{
+		b = (b << 3U) | (b >> 2U);
+		g = (g << 3U) | (g >> 2U);
+		r = (r << 3U) | (r >> 2U);
+	}
+
+	return (color_rgba)(r, g, b, min(alpha, 255U));
+}
+
+color_rgba etc_block_unpack_color4(uint16_t packed_color4, bool scaled, uint32_t alpha)
+{
+	uint32_t b = packed_color4 & 15U;
+	uint32_t g = (packed_color4 >> 4U) & 15U;
+	uint32_t r = (packed_color4 >> 8U) & 15U;
+
+	if (scaled)
+	{
+		b = (b << 4U) | b;
+		g = (g << 4U) | g;
+		r = (r << 4U) | r;
+	}
+
+	return (color_rgba)(r, g, b, min(alpha, 255U));
+}
+
+// false if didn't clamp, true if any component clamped
+bool etc_block_get_block_colors(const etc_block *pBlock, color_rgba* pBlock_colors, uint32_t subblock_index) 
+{
+	color_rgba b;
+
+	if (etc_block_get_diff_bit(pBlock))
+	{
+		if (subblock_index)
+			etc_block_unpack_color5_delta3(&b, etc_block_get_base5_color(pBlock), etc_block_get_delta3_color(pBlock), true, 255);
+		else
+			b = etc_block_unpack_color5(etc_block_get_base5_color(pBlock), true, 255);
+	}
+	else
+	{
+		b = etc_block_unpack_color4(etc_block_get_base4_color(pBlock, subblock_index), true, 255);
+	}
+
+	constant int* pInten_table = g_etc1_inten_tables[etc_block_get_inten_table(pBlock, subblock_index)];
+
+	bool dc = false;
+	pBlock_colors[0] = (color_rgba)(clamp255_flag(b.x + pInten_table[0], &dc), clamp255_flag(b.y + pInten_table[0], &dc), clamp255_flag(b.z + pInten_table[0], &dc), 255);
+	pBlock_colors[1] = (color_rgba)(clamp255_flag(b.x + pInten_table[1], &dc), clamp255_flag(b.y + pInten_table[1], &dc), clamp255_flag(b.z + pInten_table[1], &dc), 255);
+	pBlock_colors[2] = (color_rgba)(clamp255_flag(b.x + pInten_table[2], &dc), clamp255_flag(b.y + pInten_table[2], &dc), clamp255_flag(b.z + pInten_table[2], &dc), 255);
+	pBlock_colors[3] = (color_rgba)(clamp255_flag(b.x + pInten_table[3], &dc), clamp255_flag(b.y + pInten_table[3], &dc), clamp255_flag(b.z + pInten_table[3], &dc), 255);
+	return dc;
+}
+
+void get_block_colors5(color_rgba *pBlock_colors, const color_rgba *pBase_color5, uint32_t inten_table, bool scaled /* false */) 
+{
+	color_rgba b = *pBase_color5;
+
+	if (!scaled)
+	{
+		b.x = (b.x << 3) | (b.x >> 2);
+		b.y = (b.y << 3) | (b.y >> 2);
+		b.z = (b.z << 3) | (b.z >> 2);
+	}
+
+	constant int* pInten_table = g_etc1_inten_tables[inten_table];
+
+	pBlock_colors[0] = (color_rgba)(clamp255(b.x + pInten_table[0]), clamp255(b.y + pInten_table[0]), clamp255(b.z + pInten_table[0]), 255);
+	pBlock_colors[1] = (color_rgba)(clamp255(b.x + pInten_table[1]), clamp255(b.y + pInten_table[1]), clamp255(b.z + pInten_table[1]), 255);
+	pBlock_colors[2] = (color_rgba)(clamp255(b.x + pInten_table[2]), clamp255(b.y + pInten_table[2]), clamp255(b.z + pInten_table[2]), 255);
+	pBlock_colors[3] = (color_rgba)(clamp255(b.x + pInten_table[3]), clamp255(b.y + pInten_table[3]), clamp255(b.z + pInten_table[3]), 255);
+}
+
+uint64_t etc_block_determine_selectors(etc_block *pBlock, const color_rgba* pSource_pixels, bool perceptual, uint32_t begin_subblock /*= 0*/, uint32_t end_subblock /*= 2*/)
+{
+	uint64_t total_error = 0;
+
+	for (uint32_t subblock = begin_subblock; subblock < end_subblock; subblock++)
+	{
+		color_rgba block_colors[4];
+		etc_block_get_block_colors(pBlock, block_colors, subblock);
+
+		if (etc_block_get_flip_bit(pBlock))
+		{
+			for (uint32_t y = 0; y < 2; y++)
+			{
+				for (uint32_t x = 0; x < 4; x++)
+				{
+					uint32_t best_selector = 0;
+					uint64_t best_error = UINT64_MAX;
+
+					for (uint32_t s = 0; s < 4; s++)
+					{
+						uint64_t err = color_distance(perceptual, block_colors[s], pSource_pixels[x + (subblock * 2 + y) * 4], false);
+						if (err < best_error)
+						{
+							best_error = err;
+							best_selector = s;
+						}
+					}
+
+					etc_block_set_selector(pBlock, x, subblock * 2 + y, best_selector);
+
+					total_error += best_error;
+				}
+			}
+		}
+		else
+		{
+			for (uint32_t y = 0; y < 4; y++)
+			{
+				for (uint32_t x = 0; x < 2; x++)
+				{
+					uint32_t best_selector = 0;
+					uint64_t best_error = UINT64_MAX;
+
+					for (uint32_t s = 0; s < 4; s++)
+					{
+						uint64_t err = color_distance(perceptual, block_colors[s], pSource_pixels[(subblock * 2) + x + y * 4], false);
+						if (err < best_error)
+						{
+							best_error = err;
+							best_selector = s;
+						}
+					}
+
+					etc_block_set_selector(pBlock, subblock * 2 + x, y, best_selector);
+
+					total_error += best_error;
+				}
+			}
+		}
+	}
+
+	return total_error;
+}
+
+uint16_t etc_block_pack_color4_rgb(uint32_t r, uint32_t g, uint32_t b, bool scaled)
+{
+	uint32_t bias = 127;
+
+	if (scaled)
+	{
+		r = (r * 15U + bias) / 255U;
+		g = (g * 15U + bias) / 255U;
+		b = (b * 15U + bias) / 255U;
+	}
+
+	r = min(r, 15U);
+	g = min(g, 15U);
+	b = min(b, 15U);
+
+	return (uint16_t)(b | (g << 4U) | (r << 8U));
+}
+
+uint16_t etc_block_pack_color4(color_rgba color, bool scaled)
+{
+	uint32_t bias = 127;
+	return etc_block_pack_color4_rgb(color.x, color.y, color.z, scaled);
+}
+
+uint16_t etc_block_pack_delta3(int r, int g, int b)
+{
+	assert((r >= cETC1ColorDeltaMin) && (r <= cETC1ColorDeltaMax));
+	assert((g >= cETC1ColorDeltaMin) && (g <= cETC1ColorDeltaMax));
+	assert((b >= cETC1ColorDeltaMin) && (b <= cETC1ColorDeltaMax));
+	if (r < 0) r += 8;
+	if (g < 0) g += 8;
+	if (b < 0) b += 8;
+	return (uint16_t)(b | (g << 3) | (r << 6));
+}
+
+void etc_block_set_block_color4(etc_block *pBlock, color_rgba c0_unscaled, color_rgba c1_unscaled)
+{
+	etc_block_set_diff_bit(pBlock, false);
+
+	etc_block_set_base4_color(pBlock, 0, etc_block_pack_color4(c0_unscaled, false));
+	etc_block_set_base4_color(pBlock, 1, etc_block_pack_color4(c1_unscaled, false));
+}
+
+uint16_t etc_block_pack_color5_rgb(uint32_t r, uint32_t g, uint32_t b, bool scaled)
+{
+	uint32_t bias = 127;
+
+	if (scaled)
+	{
+		r = (r * 31U + bias) / 255U;
+		g = (g * 31U + bias) / 255U;
+		b = (b * 31U + bias) / 255U;
+	}
+
+	r = min(r, 31U);
+	g = min(g, 31U);
+	b = min(b, 31U);
+
+	return (uint16_t)(b | (g << 5U) | (r << 10U));
+}
+
+uint16_t etc_block_pack_color5(color_rgba c, bool scaled)
+{
+	return etc_block_pack_color5_rgb(c.x, c.y, c.z, scaled);
+}
+
+void etc_block_set_block_color5(etc_block *pBlock, color_rgba c0_unscaled, color_rgba c1_unscaled)
+{
+	etc_block_set_diff_bit(pBlock, true);
+
+	etc_block_set_base5_color(pBlock, etc_block_pack_color5(c0_unscaled, false));
+
+	int dr = c1_unscaled.x - c0_unscaled.x;
+	int dg = c1_unscaled.y - c0_unscaled.y;
+	int db = c1_unscaled.z - c0_unscaled.z;
+
+	etc_block_set_delta3_color(pBlock, etc_block_pack_delta3(dr, dg, db));
+}
+
+void etc_block_set_block_color5_etc1s(etc_block *pBlock, color_rgba c_unscaled)
+{
+	etc_block_set_diff_bit(pBlock, true);
+			
+	etc_block_set_base5_color(pBlock, etc_block_pack_color5(c_unscaled, false));
+	etc_block_set_delta3_color(pBlock, etc_block_pack_delta3(0, 0, 0));
+}
+
+bool etc_block_set_block_color5_check(etc_block *pBlock, color_rgba c0_unscaled, color_rgba c1_unscaled)
+{
+	etc_block_set_diff_bit(pBlock, true);
+
+	etc_block_set_base5_color(pBlock, etc_block_pack_color5(c0_unscaled, false));
+
+	int dr = c1_unscaled.x - c0_unscaled.x;
+	int dg = c1_unscaled.y - c0_unscaled.y;
+	int db = c1_unscaled.z - c0_unscaled.z;
+
+	if (((dr < cETC1ColorDeltaMin) || (dr > cETC1ColorDeltaMax)) ||
+		((dg < cETC1ColorDeltaMin) || (dg > cETC1ColorDeltaMax)) ||
+		((db < cETC1ColorDeltaMin) || (db > cETC1ColorDeltaMax)))
+		return false;
+
+	etc_block_set_delta3_color(pBlock, etc_block_pack_delta3(dr, dg, db));
+
+	return true;
+}
+
+void etc_block_pack_raw_selectors(etc_block *pBlock, const uint8_t *pSelectors)
+{
+	uint32_t word3 = 0, word2 = 0;
+	for (uint32_t y = 0; y < 4; y++)
+	{
+		for (uint32_t x = 0; x < 4; x++)
+		{
+			const uint32_t bit_index = x * 4 + y;
+			const uint32_t s = pSelectors[x + y * 4];
+		
+			const uint32_t lsb = s & 1, msb = s >> 1;
+		
+			word3 |= (lsb << bit_index);
+			word2 |= (msb << bit_index);
+		}
+	}
+
+	pBlock->m_bytes[7] = (uint8_t)(word3);
+	pBlock->m_bytes[6] = (uint8_t)(word3 >> 8);
+	pBlock->m_bytes[5] = (uint8_t)(word2);
+	pBlock->m_bytes[4] = (uint8_t)(word2 >> 8);
+}
+
+// ---- EC1S block encoding/endpoint optimization
+
+constant uint8_t g_eval_dist_tables[8][256] =
+{
+	// 99% threshold
+	{ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,},
+	{ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,},
+	{ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,},
+	{ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,},
+	{ 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,},
+	{ 1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,1,0,0,0,0,1,0,1,1,0,1,1,1,1,1,0,1,1,1,0,1,1,0,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,},
+	{ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,},
+	{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,}
+};
+
+typedef struct etc1s_optimizer_solution_coordinates_tag
+{
+	color_rgba m_unscaled_color;
+	uint32_t m_inten_table;
+} etc1s_optimizer_solution_coordinates;
+
+color_rgba get_scaled_color(color_rgba unscaled_color) 
+{
+	int br, bg, bb;
+	
+	br = (unscaled_color.x >> 2) | (unscaled_color.x << 3);
+	bg = (unscaled_color.y >> 2) | (unscaled_color.y << 3);
+	bb = (unscaled_color.z >> 2) | (unscaled_color.z << 3);
+	
+	return (color_rgba)((uint8_t)br, (uint8_t)bg, (uint8_t)bb, 255);
+}
+
+typedef struct etc1s_optimizer_potential_solution_tag
+{
+	uint64_t					m_error;
+	etc1s_optimizer_solution_coordinates m_coords;
+		
+	uint8_t						m_selectors[16];
+	bool						m_valid;
+} etc1s_optimizer_potential_solution;
+
+typedef struct etc1s_optimizer_state_tag
+{
+	int m_br, m_bg, m_bb;
+	float3 m_avg_color;
+	int m_max_comp_spread;
+	etc1s_optimizer_potential_solution m_best_solution;
+} etc1s_optimizer_state;
+
+bool etc1s_optimizer_evaluate_solution(
+	etc1s_optimizer_state *pState,
+	const global encode_etc1s_param_struct *pParams,
+	uint64_t num_pixels, const global color_rgba *pPixels, 
+	const global uint32_t *pWeights,
+	etc1s_optimizer_solution_coordinates coords, 
+	etc1s_optimizer_potential_solution* pTrial_solution, 
+	etc1s_optimizer_potential_solution* pBest_solution)
+{
+	uint8_t temp_selectors[16];
+
+	pTrial_solution->m_valid = false;
+		
+	const color_rgba base_color = get_scaled_color(coords.m_unscaled_color);
+	
+	pTrial_solution->m_error = INT64_MAX;
+		
+	for (uint32_t inten_table = 0; inten_table < cETC1IntenModifierValues; inten_table++)
+	{
+		// TODO: This check is equivalent to medium quality in the C++ version.
+		if (!g_eval_dist_tables[inten_table][pState->m_max_comp_spread])
+			continue;
+
+		constant int* pInten_table = g_etc1_inten_tables[inten_table];
+
+		color_rgba block_colors[4];
+		for (uint32_t s = 0; s < 4; s++)
+		{
+			int yd = pInten_table[s];
+			block_colors[s] = (color_rgba)(clamp255(base_color.x + yd), clamp255(base_color.y + yd), clamp255(base_color.z + yd), 255);
+		}
+
+		uint64_t total_error = 0;
+				
+		for (uint64_t c = 0; c < num_pixels; c++)
+		{
+			color_rgba src_pixel = pPixels[c];
+
+			uint32_t best_selector_index = 3;
+			uint32_t best_error = color_distance(pParams->m_perceptual, src_pixel, block_colors[0], false);
+
+			uint32_t trial_error = color_distance(pParams->m_perceptual, src_pixel, block_colors[1], false);
+			if (trial_error < best_error)
+			{
+				best_error = trial_error;
+				best_selector_index = 2;
+			}
+
+			trial_error = color_distance(pParams->m_perceptual, src_pixel, block_colors[2], false);
+			if (trial_error < best_error)
+			{
+				best_error = trial_error;
+				best_selector_index = 0;
+			}
+
+			trial_error = color_distance(pParams->m_perceptual, src_pixel, block_colors[3], false);
+			if (trial_error < best_error)
+			{
+				best_error = trial_error;
+				best_selector_index = 1;
+			}
+
+			if (num_pixels <= 16)
+				temp_selectors[c] = (uint8_t)(best_selector_index);
+
+			total_error += pWeights ? (best_error * (uint64_t)pWeights[c]) : best_error;
+			
+			if (total_error >= pTrial_solution->m_error)
+				break;
+		}
+
+		if (total_error < pTrial_solution->m_error)
+		{
+			pTrial_solution->m_error = total_error;
+			pTrial_solution->m_coords.m_inten_table = inten_table;
+			if (num_pixels <= 16)
+			{
+				for (uint32_t i = 0; i < num_pixels; i++)
+					pTrial_solution->m_selectors[i] = temp_selectors[i];
+			}
+			pTrial_solution->m_valid = true;
+		}
+	}
+	pTrial_solution->m_coords.m_unscaled_color = coords.m_unscaled_color;
+
+	bool success = false;
+	if (pBest_solution)
+	{
+		if (pTrial_solution->m_error < pBest_solution->m_error)
+		{
+			*pBest_solution = *pTrial_solution;
+			success = true;
+		}
+	}
+				
+	return success;
+}
+
+void etc1s_optimizer_init(
+	etc1s_optimizer_state *pState,
+	const global encode_etc1s_param_struct *pParams,
+	uint64_t num_pixels, const global color_rgba *pPixels, 
+	const global uint32_t *pWeights)
+{
+	const int LIMIT = 31;
+		
+	color_rgba min_color = 255;
+	color_rgba max_color = 0;
+	uint64_t total_weight = 0;
+	uint64_t sum_r = 0, sum_g = 0, sum_b = 0;
+				
+	for (uint64_t i = 0; i < num_pixels; i++)
+	{
+		const color_rgba c = pPixels[i];
+
+		min_color = min(min_color, c);
+		max_color = max(max_color, c);
+
+		if (pWeights)
+		{
+			uint64_t weight = pWeights[i];
+
+			sum_r += weight * c.x;
+			sum_g += weight * c.y;
+			sum_b += weight * c.z;
+		
+			total_weight += weight;
+		}
+		else
+		{
+			sum_r += c.x;
+			sum_g += c.y;
+			sum_b += c.z;
+
+			total_weight++;
+		}
+	}
+				
+	float3 avg_color;
+	avg_color.x = (float)sum_r / total_weight;
+	avg_color.y = (float)sum_g / total_weight;
+	avg_color.z = (float)sum_b / total_weight;
+
+	pState->m_avg_color = avg_color;
+	pState->m_max_comp_spread = max(max((int)max_color.x - (int)min_color.x, (int)max_color.y - (int)min_color.y), (int)max_color.z - (int)min_color.z);
+		
+	// TODO: The rounding here could be improved, like with DXT1/BC1.
+	pState->m_br = clamp((int)(avg_color.x * (LIMIT / 255.0f) + .5f), 0, LIMIT);
+	pState->m_bg = clamp((int)(avg_color.y * (LIMIT / 255.0f) + .5f), 0, LIMIT);
+	pState->m_bb = clamp((int)(avg_color.z * (LIMIT / 255.0f) + .5f), 0, LIMIT);
+
+	pState->m_best_solution.m_valid = false;
+	pState->m_best_solution.m_error = UINT64_MAX;
+}
+
+void etc1s_optimizer_internal_cluster_fit(
+	uint32_t total_perms_to_try,
+	etc1s_optimizer_state *pState,
+	const global encode_etc1s_param_struct *pParams,
+	uint64_t num_pixels, const global color_rgba *pPixels,
+	const global uint32_t *pWeights)
+{
+	const int LIMIT = 31;
+
+	etc1s_optimizer_potential_solution trial_solution;
+
+	etc1s_optimizer_solution_coordinates cur_coords;
+	cur_coords.m_unscaled_color = (color_rgba)(pState->m_br, pState->m_bg, pState->m_bb, 255);
+	etc1s_optimizer_evaluate_solution(pState, pParams, num_pixels, pPixels, pWeights, cur_coords, &trial_solution, &pState->m_best_solution);
+			
+	if (pState->m_best_solution.m_error == 0)
+		return;
+
+	for (uint32_t i = 0; i < total_perms_to_try; i++)
+	{
+		int delta_sum_r = 0, delta_sum_g = 0, delta_sum_b = 0;
+
+		constant int *pInten_table = g_etc1_inten_tables[pState->m_best_solution.m_coords.m_inten_table];
+		const color_rgba base_color = get_scaled_color(pState->m_best_solution.m_coords.m_unscaled_color);
+
+		constant uint8_t *pNum_selectors = g_cluster_fit_order_tab[i].m_v;
+
+		for (uint32_t q = 0; q < 4; q++)
+		{
+			const int yd_temp = pInten_table[q];
+
+			delta_sum_r += pNum_selectors[q] * (clamp(base_color.x + yd_temp, 0, 255) - base_color.x);
+			delta_sum_g += pNum_selectors[q] * (clamp(base_color.y + yd_temp, 0, 255) - base_color.y);
+			delta_sum_b += pNum_selectors[q] * (clamp(base_color.z + yd_temp, 0, 255) - base_color.z);
+		}
+
+		if ((!delta_sum_r) && (!delta_sum_g) && (!delta_sum_b))
+			continue;
+
+		const float avg_delta_r_f = (float)(delta_sum_r) / 8;
+		const float avg_delta_g_f = (float)(delta_sum_g) / 8;
+		const float avg_delta_b_f = (float)(delta_sum_b) / 8;
+
+		const int br1 = clamp((int)((pState->m_avg_color.x - avg_delta_r_f) * (LIMIT / 255.0f) + .5f), 0, LIMIT);
+		const int bg1 = clamp((int)((pState->m_avg_color.y - avg_delta_g_f) * (LIMIT / 255.0f) + .5f), 0, LIMIT);
+		const int bb1 = clamp((int)((pState->m_avg_color.z - avg_delta_b_f) * (LIMIT / 255.0f) + .5f), 0, LIMIT);
+		
+		cur_coords.m_unscaled_color = (color_rgba)(br1, bg1, bb1, 255);
+
+		etc1s_optimizer_evaluate_solution(pState, pParams, num_pixels, pPixels, pWeights, cur_coords, &trial_solution, &pState->m_best_solution);
+	
+		if (pState->m_best_solution.m_error == 0)
+			break;
+	}
+}
+
+// Encode an ETC1S block given a 4x4 pixel block.
+kernel void encode_etc1s_blocks(
+    const global encode_etc1s_param_struct *pParams, 
+    const global pixel_block *pInput_blocks,
+    global etc_block *pOutput_blocks)
+{
+	const uint32_t block_index = get_global_id(0);
+	
+	const global pixel_block *pInput_block = &pInput_blocks[block_index];
+
+	etc1s_optimizer_state state;
+	etc1s_optimizer_init(&state, pParams, 16, pInput_block->m_pixels, NULL);
+	etc1s_optimizer_internal_cluster_fit(pParams->m_total_perms, &state, pParams, 16, pInput_block->m_pixels, NULL);
+	
+	etc_block blk;
+	etc_block_set_flip_bit(&blk, true);
+	etc_block_set_block_color5_etc1s(&blk, state.m_best_solution.m_coords.m_unscaled_color);
+	etc_block_set_inten_tables_etc1s(&blk, state.m_best_solution.m_coords.m_inten_table);
+	etc_block_pack_raw_selectors(&blk, state.m_best_solution.m_selectors);
+							
+	pOutput_blocks[block_index] = blk;
+}
+
+typedef struct __attribute__ ((packed)) pixel_cluster_tag
+{
+	uint64_t m_total_pixels;
+	uint64_t m_first_pixel_index;
+} pixel_cluster;
+
+// Determine the optimal ETC1S color5/intensity given an arbitrary large array of 4x4 input pixel blocks.
+kernel void encode_etc1s_from_pixel_cluster(
+    const global encode_etc1s_param_struct *pParams, 
+    const global pixel_cluster *pInput_pixel_clusters,
+	const global color_rgba *pInput_pixels,
+	const global uint32_t *pInput_weights,
+    global etc_block *pOutput_blocks)
+{
+	const uint32_t cluster_index = get_global_id(0);
+	
+	const global pixel_cluster *pInput_cluster = &pInput_pixel_clusters[cluster_index];
+
+	uint64_t total_pixels = pInput_cluster->m_total_pixels;
+	const global color_rgba *pPixels = pInput_pixels + pInput_cluster->m_first_pixel_index;
+	const global uint32_t *pWeights = pInput_weights + pInput_cluster->m_first_pixel_index;
+
+	etc1s_optimizer_state state;
+	etc1s_optimizer_init(&state, pParams, total_pixels, pPixels, pWeights);
+	etc1s_optimizer_internal_cluster_fit(pParams->m_total_perms, &state, pParams, total_pixels, pPixels, pWeights);
+	
+	etc_block blk;
+	etc_block_set_flip_bit(&blk, true);
+	etc_block_set_block_color5_etc1s(&blk, state.m_best_solution.m_coords.m_unscaled_color);
+	etc_block_set_inten_tables_etc1s(&blk, state.m_best_solution.m_coords.m_inten_table);
+								
+	pOutput_blocks[cluster_index] = blk;
+}
+
+// ---- refine_endpoint_clusterization
+typedef struct __attribute__ ((packed)) rec_block_struct_tag
+{
+	uint16_t m_first_cluster_ofs;
+	uint16_t m_num_clusters;
+	uint16_t m_cur_cluster_index;
+	uint8_t m_cur_cluster_etc_inten;
+} rec_block_struct;
+
+typedef struct __attribute__ ((packed)) rec_endpoint_cluster_struct_tag
+{
+	color_rgba m_unscaled_color;
+	uint8_t m_etc_inten;
+	uint16_t m_cluster_index;
+} rec_endpoint_cluster_struct;
+
+typedef struct __attribute__ ((packed)) rec_param_struct_tag
+{
+	uint32_t m_total_blocks;
+	int m_perceptual;
+} rec_param_struct;
+
+// For each input block: find the best endpoint cluster that encodes it.
+kernel void refine_endpoint_clusterization(
+    const rec_param_struct params, 
+    const global pixel_block *pInput_blocks,
+	const global rec_block_struct *pInput_block_info,
+	const global rec_endpoint_cluster_struct *pInput_clusters,
+	const global uint32_t *pSorted_block_indices,
+    global uint32_t *pOutput_indices)
+{
+	const uint32_t sorted_block_index = get_global_id(0);
+	const uint32_t block_index = pSorted_block_indices[sorted_block_index];
+	const int perceptual = params.m_perceptual;
+
+	const global pixel_block *pInput_block = &pInput_blocks[block_index];
+			
+	pixel_block priv_pixel_block;
+	priv_pixel_block = *pInput_block;
+
+	const uint32_t first_cluster_ofs = pInput_block_info[block_index].m_first_cluster_ofs;
+	const uint32_t num_clusters = pInput_block_info[block_index].m_num_clusters;
+	const uint32_t cur_block_cluster_index = pInput_block_info[block_index].m_cur_cluster_index;
+	const uint32_t cur_block_cluster_etc_inten = pInput_block_info[block_index].m_cur_cluster_etc_inten;
+	
+	uint64_t overall_best_err = UINT64_MAX;
+	uint32_t best_cluster_index = 0;
+
+	for (uint32_t i = 0; i < num_clusters; i++)
+	{
+		const uint32_t cluster_index = first_cluster_ofs + i;
+		color_rgba unscaled_color = pInput_clusters[cluster_index].m_unscaled_color;
+		const uint8_t etc_inten = pInput_clusters[cluster_index].m_etc_inten;
+		const uint16_t orig_cluster_index = pInput_clusters[cluster_index].m_cluster_index;
+
+		if (etc_inten > cur_block_cluster_etc_inten)
+			continue;
+
+		color_rgba block_colors[4];
+		get_block_colors5(block_colors, &unscaled_color, etc_inten, false);
+
+		uint64_t total_error = 0;
+				
+		for (uint32_t c = 0; c < 16; c++)
+		{
+			color_rgba src_pixel = priv_pixel_block.m_pixels[c];
+
+			uint32_t best_error = color_distance(perceptual, src_pixel, block_colors[0], false);
+
+			uint32_t trial_error = color_distance(perceptual, src_pixel, block_colors[1], false);
+			if (trial_error < best_error)
+				best_error = trial_error;
+
+			trial_error = color_distance(perceptual, src_pixel, block_colors[2], false);
+			if (trial_error < best_error)
+				best_error = trial_error;
+
+			trial_error = color_distance(perceptual, src_pixel, block_colors[3], false);
+			if (trial_error < best_error)
+				best_error = trial_error;
+							
+			total_error += best_error;
+		}
+
+		if ( (total_error < overall_best_err) ||
+		     ((orig_cluster_index == cur_block_cluster_index) && (total_error == overall_best_err))
+			)
+		{
+			overall_best_err = total_error;
+			best_cluster_index = orig_cluster_index;
+			if (!overall_best_err)
+				break;
+		}
+	}
+
+	pOutput_indices[block_index] = best_cluster_index;
+}
+
+// ---- find_optimal_selector_clusters_for_each_block
+
+typedef struct __attribute__ ((packed)) fosc_selector_struct_tag
+{
+	uint32_t m_packed_selectors;	// 4x4 grid of 2-bit selectors
+} fosc_selector_struct;
+
+typedef struct __attribute__ ((packed)) fosc_block_struct_tag
+{
+	color_rgba m_etc_color5_inten;  // unscaled 5-bit block color in RGB, alpha has block's intensity index
+	uint32_t m_first_selector;		// offset into selector table
+	uint32_t m_num_selectors;		// number of selectors to check
+} fosc_block_struct;
+
+typedef struct __attribute__ ((packed)) fosc_param_struct_tag
+{
+	uint32_t m_total_blocks;
+	int m_perceptual;
+} fosc_param_struct;
+
+// For each input block: Find the quantized selector which results in the lowest error.
+kernel void find_optimal_selector_clusters_for_each_block(
+    const fosc_param_struct params, 
+    const global pixel_block *pInput_blocks,
+	const global fosc_block_struct *pInput_block_info,
+	const global fosc_selector_struct *pInput_selectors,
+	const global uint32_t *pSelector_cluster_indices,
+    global uint32_t *pOutput_selector_cluster_indices)
+{
+	const uint32_t block_index = get_global_id(0);
+	
+	const global color_rgba *pBlock_pixels = pInput_blocks[block_index].m_pixels;
+	const global fosc_block_struct *pBlock_info = &pInput_block_info[block_index];
+	
+	const global fosc_selector_struct *pSelectors = &pInput_selectors[pBlock_info->m_first_selector];
+	const uint32_t num_selectors = pBlock_info->m_num_selectors;
+
+	color_rgba trial_block_colors[4];
+	color_rgba etc_color5_inten = pBlock_info->m_etc_color5_inten;
+	get_block_colors5(trial_block_colors, &etc_color5_inten, etc_color5_inten.w, false);
+
+	uint32_t trial_errors[4][16];
+
+	if (params.m_perceptual)
+	{
+		for (uint32_t sel = 0; sel < 4; ++sel)
+			for (uint32_t i = 0; i < 16; ++i)
+				trial_errors[sel][i] = color_distance(true, pBlock_pixels[i], trial_block_colors[sel], false);
+	}
+	else
+	{
+		for (uint32_t sel = 0; sel < 4; ++sel)
+			for (uint32_t i = 0; i < 16; ++i)
+				trial_errors[sel][i] = color_distance(false, pBlock_pixels[i], trial_block_colors[sel], false);
+	}
+
+	uint64_t best_err = UINT64_MAX;
+	uint32_t best_index = 0;
+
+	for (uint32_t sel_index = 0; sel_index < num_selectors; sel_index++)
+	{
+		uint32_t sels = pSelectors[sel_index].m_packed_selectors;
+		
+		uint64_t total_err = 0;
+		for (uint32_t i = 0; i < 16; i++, sels >>= 2)
+			total_err += trial_errors[sels & 3][i];
+
+		if (total_err < best_err)
+		{
+			best_err = total_err;
+			best_index = sel_index;
+
+			if (!best_err)
+				break;
+		}
+	}
+
+	pOutput_selector_cluster_indices[block_index] = pSelector_cluster_indices[pBlock_info->m_first_selector + best_index];
+}
+
+// determine_selectors
+
+typedef struct __attribute__ ((packed)) ds_param_struct_tag
+{
+	uint32_t m_total_blocks;
+	int m_perceptual;
+} ds_param_struct;
+
+// For each input block: Determine the ETC1S selectors that result in the lowest error, given each block's predetermined ETC1S color5/intensities. 
+kernel void determine_selectors(
+    const ds_param_struct params, 
+    const global pixel_block *pInput_blocks,
+	const global color_rgba *pInput_etc_color5_and_inten,
+    global etc_block *pOutput_blocks)
+{
+	const uint32_t block_index = get_global_id(0);
+	
+	const global color_rgba *pBlock_pixels = pInput_blocks[block_index].m_pixels;
+
+	color_rgba etc_color5_inten = pInput_etc_color5_and_inten[block_index];
+
+	color_rgba block_colors[4];
+	get_block_colors5(block_colors, &etc_color5_inten, etc_color5_inten.w, false);
+
+	etc_block output_block;
+	etc_block_set_flip_bit(&output_block, true);
+	etc_block_set_block_color5_etc1s(&output_block, etc_color5_inten);
+	etc_block_set_inten_tables_etc1s(&output_block, etc_color5_inten.w);
+
+	for (uint32_t i = 0; i < 16; i++)
+	{
+		color_rgba pixel_color = pBlock_pixels[i];
+
+		uint err0 = color_distance(params.m_perceptual, pixel_color, block_colors[0], false);
+		uint err1 = color_distance(params.m_perceptual, pixel_color, block_colors[1], false);
+		uint err2 = color_distance(params.m_perceptual, pixel_color, block_colors[2], false);
+		uint err3 = color_distance(params.m_perceptual, pixel_color, block_colors[3], false);
+
+		uint best_err = min(min(min(err0, err1), err2), err3);
+
+		uint32_t best_sel = (best_err == err2) ? 2 : 3;
+		best_sel = (best_err == err1) ? 1 : best_sel;
+		best_sel = (best_err == err0) ? 0 : best_sel;
+
+		etc_block_set_selector(&output_block, i & 3, i >> 2, best_sel);
+	}
+
+	pOutput_blocks[block_index] = output_block;
+}
+
diff --git a/contrib/previewers/lib/basisu_transcoder.cpp b/contrib/previewers/lib/basisu_transcoder.cpp
index c5652e0..37640ee 100644
--- a/contrib/previewers/lib/basisu_transcoder.cpp
+++ b/contrib/previewers/lib/basisu_transcoder.cpp
@@ -1,17 +1,14 @@
 /**
  * Basis Universal single file library. Generated using:
  * \code
- *	./combine.sh -r ../../transcoder -x basisu_transcoder_tables_bc7_m6.inc -k basisu_transcoder.h -o basisu_transcoder.cpp basisu_transcoder-in.cpp
+ *	./combine.sh -r ../../transcoder -o basisu_transcoder.cpp basisu_transcoder-in.cpp
  * \endcode
- * 
- * \note The script above excludes the BC7 mode 6 tables, a choice reflected in
- * the build options.
  */
 
 /*
  * Transcoder build options for known platforms (iOS has ETC, ASTC and PVRTC;
  * Emscripten adds DXT to iOS's options; Android adds PVRTC2 to Emscripten's
- * options; other platforms build all except BC7 mode 6 and FXT1).
+ * options; other platforms build all except FXT1).
  * 
  * See https://github.com/BinomialLLC/basis_universal#shrinking-the-transcoders-compiled-size
  */
@@ -28,14 +25,17 @@
 	#ifndef __ANDROID__
 		#define BASISD_SUPPORT_PVRTC2 0
 	#endif
-#else
-	#define BASISD_SUPPORT_BC7_MODE6_OPAQUE_ONLY 0
 #endif
 #define BASISD_SUPPORT_FXT1 0
 
+/*
+ * KTX2 support disabled.
+ */
+#define BASISD_SUPPORT_KTX2 0
+
 /**** start inlining basisu_transcoder.cpp ****/
 // basisu_transcoder.cpp
-// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
+// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -49,76 +49,5030 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-/**** *NOT* inlining basisu_transcoder.h ****/
-#include "basisu_transcoder.h"
-#include <limits.h>
-#include <vector>
+/**** start inlining basisu_transcoder.h ****/
+// basisu_transcoder.h
+// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
+// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
 
-#ifndef IS_BIG_ENDIAN
+// By default KTX2 support is enabled to simplify compilation. This implies the need for the Zstandard library (which we distribute as a single source file in the "zstd" directory) by default.
+// Set BASISD_SUPPORT_KTX2 to 0 to completely disable KTX2 support as well as Zstd/miniz usage which is only required for UASTC supercompression in KTX2 files.
+// Also see BASISD_SUPPORT_KTX2_ZSTD in basisu_transcoder.cpp, which individually disables Zstd usage.
+#ifndef BASISD_SUPPORT_KTX2
+	#define BASISD_SUPPORT_KTX2 1
+#endif
+
+// Set BASISD_SUPPORT_KTX2_ZSTD to 0 to disable Zstd usage and KTX2 UASTC Zstd supercompression support 
+#ifndef BASISD_SUPPORT_KTX2_ZSTD
+	#define BASISD_SUPPORT_KTX2_ZSTD 1
+#endif
+
+// Set BASISU_FORCE_DEVEL_MESSAGES to 1 to enable debug printf()'s whenever an error occurs, for easier debugging during development.
+#ifndef BASISU_FORCE_DEVEL_MESSAGES
+	#define BASISU_FORCE_DEVEL_MESSAGES 0
+#endif
+
+/**** start inlining basisu_transcoder_internal.h ****/
+// basisu_transcoder_internal.h - Universal texture format transcoder library.
+// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
+//
+// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifdef _MSC_VER
+#pragma warning (disable: 4127) //  conditional expression is constant
+#endif
+
+#define BASISD_LIB_VERSION 116
+#define BASISD_VERSION_STRING "01.16"
+
+#ifdef _DEBUG
+#define BASISD_BUILD_DEBUG
+#else
+#define BASISD_BUILD_RELEASE
+#endif
+
+/**** start inlining basisu.h ****/
+// basisu.h
+// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
+// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifdef _MSC_VER
+
+	#pragma warning (disable : 4201)
+	#pragma warning (disable : 4127) // warning C4127: conditional expression is constant
+	#pragma warning (disable : 4530) // C++ exception handler used, but unwind semantics are not enabled.
+
+	// Slamming this off always for v1.16 because we've gotten rid of most std containers.
+	#ifndef BASISU_NO_ITERATOR_DEBUG_LEVEL
+		#define BASISU_NO_ITERATOR_DEBUG_LEVEL (1)
+	#endif
+
+	#ifndef BASISU_NO_ITERATOR_DEBUG_LEVEL
+		//#define _HAS_ITERATOR_DEBUGGING 0
+
+		#if defined(_DEBUG) || defined(DEBUG)
+			// This is madness, but we need to disable iterator debugging in debug builds or the encoder is unsable because MSVC's iterator debugging implementation is totally broken.
+			#ifndef _ITERATOR_DEBUG_LEVEL
+			#define _ITERATOR_DEBUG_LEVEL 1
+			#endif
+			#ifndef _SECURE_SCL
+			#define _SECURE_SCL 1
+			#endif
+		#else // defined(_DEBUG) || defined(DEBUG)
+			#ifndef _SECURE_SCL
+			#define _SECURE_SCL 0
+			#endif
+			#ifndef _ITERATOR_DEBUG_LEVEL
+			#define _ITERATOR_DEBUG_LEVEL 0
+			#endif
+		#endif // defined(_DEBUG) || defined(DEBUG)
+
+	#endif // BASISU_NO_ITERATOR_DEBUG_LEVEL
+
+#endif // _MSC_VER
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdarg.h>
+#include <string.h>
+#include <memory.h>
+#include <limits.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <limits>
+#include <functional>
+#include <iterator>
+#include <type_traits>
+#include <assert.h>
+#include <random>
+
+/**** start inlining basisu_containers.h ****/
+// basisu_containers.h
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <assert.h>
+#include <algorithm>
+
+#if defined(__linux__) && !defined(ANDROID)
+// Only for malloc_usable_size() in basisu_containers_impl.h
+#include <malloc.h>
+#define HAS_MALLOC_USABLE_SIZE 1
+#endif
+
+// Set to 1 to always check vector operator[], front(), and back() even in release.
+#define BASISU_VECTOR_FORCE_CHECKING 0
+
+// If 1, the vector container will not query the CRT to get the size of resized memory blocks.
+#define BASISU_VECTOR_DETERMINISTIC 1
+
+#ifdef _MSC_VER
+#define BASISU_FORCE_INLINE __forceinline
+#else
+#define BASISU_FORCE_INLINE inline
+#endif
+
+namespace basisu
+{
+   enum { cInvalidIndex = -1 };
+
+   namespace helpers
+   {
+      inline bool is_power_of_2(uint32_t x) { return x && ((x & (x - 1U)) == 0U); }
+      inline bool is_power_of_2(uint64_t x) { return x && ((x & (x - 1U)) == 0U); }
+      template<class T> const T& minimum(const T& a, const T& b) { return (b < a) ? b : a; }
+      template<class T> const T& maximum(const T& a, const T& b) { return (a < b) ? b : a; }
+
+      inline uint32_t floor_log2i(uint32_t v)
+      {
+         uint32_t l = 0;
+         while (v > 1U)
+         {
+            v >>= 1;
+            l++;
+         }
+         return l;
+      }
+
+      inline uint32_t next_pow2(uint32_t val)
+      {
+         val--;
+         val |= val >> 16;
+         val |= val >> 8;
+         val |= val >> 4;
+         val |= val >> 2;
+         val |= val >> 1;
+         return val + 1;
+      }
+
+      inline uint64_t next_pow2(uint64_t val)
+      {
+         val--;
+         val |= val >> 32;
+         val |= val >> 16;
+         val |= val >> 8;
+         val |= val >> 4;
+         val |= val >> 2;
+         val |= val >> 1;
+         return val + 1;
+      }
+   } // namespace helpers
+
+   template <typename T>
+   inline T* construct(T* p)
+   {
+      return new (static_cast<void*>(p)) T;
+   }
+
+   template <typename T, typename U>
+   inline T* construct(T* p, const U& init)
+   {
+      return new (static_cast<void*>(p)) T(init);
+   }
+
+   template <typename T>
+   inline void construct_array(T* p, size_t n)
+   {
+      T* q = p + n;
+      for (; p != q; ++p)
+         new (static_cast<void*>(p)) T;
+   }
+
+   template <typename T, typename U>
+   inline void construct_array(T* p, size_t n, const U& init)
+   {
+      T* q = p + n;
+      for (; p != q; ++p)
+         new (static_cast<void*>(p)) T(init);
+   }
+
+   template <typename T>
+   inline void destruct(T* p)
+   {
+      (void)p;
+      p->~T();
+   }
+
+   template <typename T> inline void destruct_array(T* p, size_t n)
+   {
+      T* q = p + n;
+      for (; p != q; ++p)
+         p->~T();
+   }
+
+   template<typename T> struct int_traits { enum { cMin = INT32_MIN, cMax = INT32_MAX, cSigned = true }; };
+
+   template<> struct int_traits<int8_t> { enum { cMin = INT8_MIN, cMax = INT8_MAX, cSigned = true }; };
+   template<> struct int_traits<int16_t> { enum { cMin = INT16_MIN, cMax = INT16_MAX, cSigned = true }; };
+   template<> struct int_traits<int32_t> { enum { cMin = INT32_MIN, cMax = INT32_MAX, cSigned = true }; };
+
+   template<> struct int_traits<uint8_t> { enum { cMin = 0, cMax = UINT8_MAX, cSigned = false }; };
+   template<> struct int_traits<uint16_t> { enum { cMin = 0, cMax = UINT16_MAX, cSigned = false }; };
+   template<> struct int_traits<uint32_t> { enum { cMin = 0, cMax = UINT32_MAX, cSigned = false }; };
+
+   template<typename T>
+   struct scalar_type
+   {
+      enum { cFlag = false };
+      static inline void construct(T* p) { basisu::construct(p); }
+      static inline void construct(T* p, const T& init) { basisu::construct(p, init); }
+      static inline void construct_array(T* p, size_t n) { basisu::construct_array(p, n); }
+      static inline void destruct(T* p) { basisu::destruct(p); }
+      static inline void destruct_array(T* p, size_t n) { basisu::destruct_array(p, n); }
+   };
+
+   template<typename T> struct scalar_type<T*>
+   {
+      enum { cFlag = true };
+      static inline void construct(T** p) { memset(p, 0, sizeof(T*)); }
+      static inline void construct(T** p, T* init) { *p = init; }
+      static inline void construct_array(T** p, size_t n) { memset(p, 0, sizeof(T*) * n); }
+      static inline void destruct(T** p) { p; }
+      static inline void destruct_array(T** p, size_t n) { p, n; }
+   };
+
+#define BASISU_DEFINE_BUILT_IN_TYPE(X) \
+   template<> struct scalar_type<X> { \
+   enum { cFlag = true }; \
+   static inline void construct(X* p) { memset(p, 0, sizeof(X)); } \
+   static inline void construct(X* p, const X& init) { memcpy(p, &init, sizeof(X)); } \
+   static inline void construct_array(X* p, size_t n) { memset(p, 0, sizeof(X) * n); } \
+   static inline void destruct(X* p) { p; } \
+   static inline void destruct_array(X* p, size_t n) { p, n; } };
+
+   BASISU_DEFINE_BUILT_IN_TYPE(bool)
+   BASISU_DEFINE_BUILT_IN_TYPE(char)
+   BASISU_DEFINE_BUILT_IN_TYPE(unsigned char)
+   BASISU_DEFINE_BUILT_IN_TYPE(short)
+   BASISU_DEFINE_BUILT_IN_TYPE(unsigned short)
+   BASISU_DEFINE_BUILT_IN_TYPE(int)
+   BASISU_DEFINE_BUILT_IN_TYPE(unsigned int)
+   BASISU_DEFINE_BUILT_IN_TYPE(long)
+   BASISU_DEFINE_BUILT_IN_TYPE(unsigned long)
+#ifdef __GNUC__
+   BASISU_DEFINE_BUILT_IN_TYPE(long long)
+   BASISU_DEFINE_BUILT_IN_TYPE(unsigned long long)
+#else
+   BASISU_DEFINE_BUILT_IN_TYPE(__int64)
+   BASISU_DEFINE_BUILT_IN_TYPE(unsigned __int64)
+#endif
+   BASISU_DEFINE_BUILT_IN_TYPE(float)
+   BASISU_DEFINE_BUILT_IN_TYPE(double)
+   BASISU_DEFINE_BUILT_IN_TYPE(long double)
+
+#undef BASISU_DEFINE_BUILT_IN_TYPE
+
+   template<typename T>
+   struct bitwise_movable { enum { cFlag = false }; };
+
+#define BASISU_DEFINE_BITWISE_MOVABLE(Q) template<> struct bitwise_movable<Q> { enum { cFlag = true }; };
+
+   template<typename T>
+   struct bitwise_copyable { enum { cFlag = false }; };
+
+#define BASISU_DEFINE_BITWISE_COPYABLE(Q) template<> struct bitwise_copyable<Q> { enum { cFlag = true }; };
+
+#define BASISU_IS_POD(T) __is_pod(T)
+
+#define BASISU_IS_SCALAR_TYPE(T) (scalar_type<T>::cFlag)
+
+#if defined(__GNUC__) && __GNUC__<5
+   #define BASISU_IS_TRIVIALLY_COPYABLE(...) __has_trivial_copy(__VA_ARGS__)
+#else
+   #define BASISU_IS_TRIVIALLY_COPYABLE(...) std::is_trivially_copyable<__VA_ARGS__>::value
+#endif
+
+// TODO: clean this up
+#define BASISU_IS_BITWISE_COPYABLE(T) (BASISU_IS_SCALAR_TYPE(T) || BASISU_IS_POD(T) || BASISU_IS_TRIVIALLY_COPYABLE(T) || (bitwise_copyable<T>::cFlag))
+
+#define BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(T) (BASISU_IS_BITWISE_COPYABLE(T) || (bitwise_movable<T>::cFlag))
+
+#define BASISU_HAS_DESTRUCTOR(T) ((!scalar_type<T>::cFlag) && (!__is_pod(T)))
+
+   typedef char(&yes_t)[1];
+   typedef char(&no_t)[2];
+
+   template <class U> yes_t class_test(int U::*);
+   template <class U> no_t class_test(...);
+
+   template <class T> struct is_class
+   {
+      enum { value = (sizeof(class_test<T>(0)) == sizeof(yes_t)) };
+   };
+
+   template <typename T> struct is_pointer
+   {
+      enum { value = false };
+   };
+
+   template <typename T> struct is_pointer<T*>
+   {
+      enum { value = true };
+   };
+
+   struct empty_type { };
+
+   BASISU_DEFINE_BITWISE_COPYABLE(empty_type);
+   BASISU_DEFINE_BITWISE_MOVABLE(empty_type);
+
+   template<typename T> struct rel_ops
+   {
+      friend bool operator!=(const T& x, const T& y) { return (!(x == y)); }
+      friend bool operator> (const T& x, const T& y) { return (y < x); }
+      friend bool operator<=(const T& x, const T& y) { return (!(y < x)); }
+      friend bool operator>=(const T& x, const T& y) { return (!(x < y)); }
+   };
+
+   struct elemental_vector
+   {
+      void* m_p;
+      uint32_t m_size;
+      uint32_t m_capacity;
+
+      typedef void (*object_mover)(void* pDst, void* pSrc, uint32_t num);
+
+      bool increase_capacity(uint32_t min_new_capacity, bool grow_hint, uint32_t element_size, object_mover pRelocate, bool nofail);
+   };
+
+   template<typename T>
+   class vector : public rel_ops< vector<T> >
+   {
+   public:
+      typedef T* iterator;
+      typedef const T* const_iterator;
+      typedef T value_type;
+      typedef T& reference;
+      typedef const T& const_reference;
+      typedef T* pointer;
+      typedef const T* const_pointer;
+
+      inline vector() :
+         m_p(NULL),
+         m_size(0),
+         m_capacity(0)
+      {
+      }
+
+      inline vector(uint32_t n, const T& init) :
+         m_p(NULL),
+         m_size(0),
+         m_capacity(0)
+      {
+         increase_capacity(n, false);
+         construct_array(m_p, n, init);
+         m_size = n;
+      }
+
+      inline vector(const vector& other) :
+         m_p(NULL),
+         m_size(0),
+         m_capacity(0)
+      {
+         increase_capacity(other.m_size, false);
+
+         m_size = other.m_size;
+
+         if (BASISU_IS_BITWISE_COPYABLE(T))
+            memcpy(m_p, other.m_p, m_size * sizeof(T));
+         else
+         {
+            T* pDst = m_p;
+            const T* pSrc = other.m_p;
+            for (uint32_t i = m_size; i > 0; i--)
+               construct(pDst++, *pSrc++);
+         }
+      }
+
+      inline explicit vector(size_t size) :
+         m_p(NULL),
+         m_size(0),
+         m_capacity(0)
+      {
+         resize(size);
+      }
+
+      inline ~vector()
+      {
+         if (m_p)
+         {
+            scalar_type<T>::destruct_array(m_p, m_size);
+            free(m_p);
+         }
+      }
+
+      inline vector& operator= (const vector& other)
+      {
+         if (this == &other)
+            return *this;
+
+         if (m_capacity >= other.m_size)
+            resize(0);
+         else
+         {
+            clear();
+            increase_capacity(other.m_size, false);
+         }
+
+         if (BASISU_IS_BITWISE_COPYABLE(T))
+            memcpy(m_p, other.m_p, other.m_size * sizeof(T));
+         else
+         {
+            T* pDst = m_p;
+            const T* pSrc = other.m_p;
+            for (uint32_t i = other.m_size; i > 0; i--)
+               construct(pDst++, *pSrc++);
+         }
+
+         m_size = other.m_size;
+
+         return *this;
+      }
+
+      BASISU_FORCE_INLINE const T* begin() const { return m_p; }
+      BASISU_FORCE_INLINE T* begin() { return m_p; }
+
+      BASISU_FORCE_INLINE const T* end() const { return m_p + m_size; }
+      BASISU_FORCE_INLINE T* end() { return m_p + m_size; }
+
+      BASISU_FORCE_INLINE bool empty() const { return !m_size; }
+      BASISU_FORCE_INLINE uint32_t size() const { return m_size; }
+      BASISU_FORCE_INLINE uint32_t size_in_bytes() const { return m_size * sizeof(T); }
+      BASISU_FORCE_INLINE uint32_t capacity() const { return m_capacity; }
+
+      // operator[] will assert on out of range indices, but in final builds there is (and will never be) any range checking on this method.
+      //BASISU_FORCE_INLINE const T& operator[] (uint32_t i) const { assert(i < m_size); return m_p[i]; }
+      //BASISU_FORCE_INLINE T& operator[] (uint32_t i) { assert(i < m_size); return m_p[i]; }
+            
+#if !BASISU_VECTOR_FORCE_CHECKING
+      BASISU_FORCE_INLINE const T& operator[] (size_t i) const { assert(i < m_size); return m_p[i]; }
+      BASISU_FORCE_INLINE T& operator[] (size_t i) { assert(i < m_size); return m_p[i]; }
+#else
+      BASISU_FORCE_INLINE const T& operator[] (size_t i) const 
+      { 
+          if (i >= m_size)
+          {
+              fprintf(stderr, "operator[] invalid index: %u, max entries %u, type size %u\n", (uint32_t)i, m_size, (uint32_t)sizeof(T));
+              abort();
+          }
+          return m_p[i]; 
+      }
+      BASISU_FORCE_INLINE T& operator[] (size_t i) 
+      { 
+          if (i >= m_size)
+          {
+              fprintf(stderr, "operator[] invalid index: %u, max entries %u, type size %u\n", (uint32_t)i, m_size, (uint32_t)sizeof(T));
+              abort();
+          }
+          return m_p[i]; 
+      }
+#endif
+
+      // at() always includes range checking, even in final builds, unlike operator [].
+      // The first element is returned if the index is out of range.
+      BASISU_FORCE_INLINE const T& at(size_t i) const { assert(i < m_size); return (i >= m_size) ? m_p[0] : m_p[i]; }
+      BASISU_FORCE_INLINE T& at(size_t i) { assert(i < m_size); return (i >= m_size) ? m_p[0] : m_p[i]; }
+            
+#if !BASISU_VECTOR_FORCE_CHECKING
+      BASISU_FORCE_INLINE const T& front() const { assert(m_size); return m_p[0]; }
+      BASISU_FORCE_INLINE T& front() { assert(m_size); return m_p[0]; }
+
+      BASISU_FORCE_INLINE const T& back() const { assert(m_size); return m_p[m_size - 1]; }
+      BASISU_FORCE_INLINE T& back() { assert(m_size); return m_p[m_size - 1]; }
+#else
+      BASISU_FORCE_INLINE const T& front() const 
+      { 
+          if (!m_size)
+          {
+              fprintf(stderr, "front: vector is empty, type size %u\n", (uint32_t)sizeof(T));
+              abort();
+          }
+          return m_p[0]; 
+      }
+      BASISU_FORCE_INLINE T& front() 
+      { 
+          if (!m_size)
+          {
+              fprintf(stderr, "front: vector is empty, type size %u\n", (uint32_t)sizeof(T));
+              abort();
+          }
+          return m_p[0]; 
+      }
+
+      BASISU_FORCE_INLINE const T& back() const 
+      { 
+          if(!m_size)
+          {
+              fprintf(stderr, "back: vector is empty, type size %u\n", (uint32_t)sizeof(T));
+              abort();
+          }
+          return m_p[m_size - 1]; 
+      }
+      BASISU_FORCE_INLINE T& back() 
+      { 
+          if (!m_size)
+          {
+              fprintf(stderr, "back: vector is empty, type size %u\n", (uint32_t)sizeof(T));
+              abort();
+          }
+          return m_p[m_size - 1]; 
+      }
+#endif
+
+      BASISU_FORCE_INLINE const T* get_ptr() const { return m_p; }
+      BASISU_FORCE_INLINE T* get_ptr() { return m_p; }
+
+      BASISU_FORCE_INLINE const T* data() const { return m_p; }
+      BASISU_FORCE_INLINE T* data() { return m_p; }
+
+      // clear() sets the container to empty, then frees the allocated block.
+      inline void clear()
+      {
+         if (m_p)
+         {
+            scalar_type<T>::destruct_array(m_p, m_size);
+            free(m_p);
+            m_p = NULL;
+            m_size = 0;
+            m_capacity = 0;
+         }
+      }
+
+      inline void clear_no_destruction()
+      {
+         if (m_p)
+         {
+            free(m_p);
+            m_p = NULL;
+            m_size = 0;
+            m_capacity = 0;
+         }
+      }
+
+      inline void reserve(size_t new_capacity_size_t)
+      {
+         if (new_capacity_size_t > UINT32_MAX)
+         {
+            assert(0);
+            return;
+         }
+
+         uint32_t new_capacity = (uint32_t)new_capacity_size_t;
+
+         if (new_capacity > m_capacity)
+            increase_capacity(new_capacity, false);
+         else if (new_capacity < m_capacity)
+         {
+            // Must work around the lack of a "decrease_capacity()" method.
+            // This case is rare enough in practice that it's probably not worth implementing an optimized in-place resize.
+            vector tmp;
+            tmp.increase_capacity(helpers::maximum(m_size, new_capacity), false);
+            tmp = *this;
+            swap(tmp);
+         }
+      }
+
+      inline bool try_reserve(size_t new_capacity_size_t)
+      {
+         if (new_capacity_size_t > UINT32_MAX)
+         {
+            assert(0);
+            return false;
+         }
+
+         uint32_t new_capacity = (uint32_t)new_capacity_size_t;
+
+         if (new_capacity > m_capacity)
+         {
+            if (!increase_capacity(new_capacity, false))
+               return false;
+         }
+         else if (new_capacity < m_capacity)
+         {
+            // Must work around the lack of a "decrease_capacity()" method.
+            // This case is rare enough in practice that it's probably not worth implementing an optimized in-place resize.
+            vector tmp;
+            tmp.increase_capacity(helpers::maximum(m_size, new_capacity), false);
+            tmp = *this;
+            swap(tmp);
+         }
+
+         return true;
+      }
+
+      // resize(0) sets the container to empty, but does not free the allocated block.
+      inline void resize(size_t new_size_size_t, bool grow_hint = false)
+      {
+         if (new_size_size_t > UINT32_MAX)
+         {
+            assert(0);
+            return;
+         }
+
+         uint32_t new_size = (uint32_t)new_size_size_t;
+
+         if (m_size != new_size)
+         {
+            if (new_size < m_size)
+               scalar_type<T>::destruct_array(m_p + new_size, m_size - new_size);
+            else
+            {
+               if (new_size > m_capacity)
+                  increase_capacity(new_size, (new_size == (m_size + 1)) || grow_hint);
+
+               scalar_type<T>::construct_array(m_p + m_size, new_size - m_size);
+            }
+
+            m_size = new_size;
+         }
+      }
+
+      inline bool try_resize(size_t new_size_size_t, bool grow_hint = false)
+      {
+         if (new_size_size_t > UINT32_MAX)
+         {
+            assert(0);
+            return false;
+         }
+
+         uint32_t new_size = (uint32_t)new_size_size_t;
+
+         if (m_size != new_size)
+         {
+            if (new_size < m_size)
+               scalar_type<T>::destruct_array(m_p + new_size, m_size - new_size);
+            else
+            {
+               if (new_size > m_capacity)
+               {
+                  if (!increase_capacity(new_size, (new_size == (m_size + 1)) || grow_hint, true))
+                     return false;
+               }
+
+               scalar_type<T>::construct_array(m_p + m_size, new_size - m_size);
+            }
+
+            m_size = new_size;
+         }
+
+         return true;
+      }
+
+      // If size >= capacity/2, reset() sets the container's size to 0 but doesn't free the allocated block (because the container may be similarly loaded in the future).
+      // Otherwise it blows away the allocated block. See http://www.codercorner.com/blog/?p=494
+      inline void reset()
+      {
+         if (m_size >= (m_capacity >> 1))
+            resize(0);
+         else
+            clear();
+      }
+
+      inline T* enlarge(uint32_t i)
+      {
+         uint32_t cur_size = m_size;
+         resize(cur_size + i, true);
+         return get_ptr() + cur_size;
+      }
+
+      inline T* try_enlarge(uint32_t i)
+      {
+         uint32_t cur_size = m_size;
+         if (!try_resize(cur_size + i, true))
+            return NULL;
+         return get_ptr() + cur_size;
+      }
+
+      BASISU_FORCE_INLINE void push_back(const T& obj)
+      {
+         assert(!m_p || (&obj < m_p) || (&obj >= (m_p + m_size)));
+
+         if (m_size >= m_capacity)
+            increase_capacity(m_size + 1, true);
+
+         scalar_type<T>::construct(m_p + m_size, obj);
+         m_size++;
+      }
+
+      inline bool try_push_back(const T& obj)
+      {
+         assert(!m_p || (&obj < m_p) || (&obj >= (m_p + m_size)));
+
+         if (m_size >= m_capacity)
+         {
+            if (!increase_capacity(m_size + 1, true, true))
+               return false;
+         }
+
+         scalar_type<T>::construct(m_p + m_size, obj);
+         m_size++;
+
+         return true;
+      }
+
+      inline void push_back_value(T obj)
+      {
+         if (m_size >= m_capacity)
+            increase_capacity(m_size + 1, true);
+
+         scalar_type<T>::construct(m_p + m_size, obj);
+         m_size++;
+      }
+
+      inline void pop_back()
+      {
+         assert(m_size);
+
+         if (m_size)
+         {
+            m_size--;
+            scalar_type<T>::destruct(&m_p[m_size]);
+         }
+      }
+
+      inline void insert(uint32_t index, const T* p, uint32_t n)
+      {
+         assert(index <= m_size);
+         if (!n)
+            return;
+
+         const uint32_t orig_size = m_size;
+         resize(m_size + n, true);
+
+         const uint32_t num_to_move = orig_size - index;
+
+         if (BASISU_IS_BITWISE_COPYABLE(T))
+         {
+            // This overwrites the destination object bits, but bitwise copyable means we don't need to worry about destruction.
+            memmove(m_p + index + n, m_p + index, sizeof(T) * num_to_move);
+         }
+         else
+         {
+            const T* pSrc = m_p + orig_size - 1;
+            T* pDst = const_cast<T*>(pSrc) + n;
+
+            for (uint32_t i = 0; i < num_to_move; i++)
+            {
+               assert((pDst - m_p) < (int)m_size);
+               *pDst-- = *pSrc--;
+            }
+         }
+
+         T* pDst = m_p + index;
+
+         if (BASISU_IS_BITWISE_COPYABLE(T))
+         {
+            // This copies in the new bits, overwriting the existing objects, which is OK for copyable types that don't need destruction.
+            memcpy(pDst, p, sizeof(T) * n);
+         }
+         else
+         {
+            for (uint32_t i = 0; i < n; i++)
+            {
+               assert((pDst - m_p) < (int)m_size);
+               *pDst++ = *p++;
+            }
+         }
+      }
+
+      inline void insert(T* p, const T& obj)
+      {
+         int64_t ofs = p - begin();
+         if ((ofs < 0) || (ofs > UINT32_MAX))
+         {
+            assert(0);
+            return;
+         }
+
+         insert((uint32_t)ofs, &obj, 1);
+      }
+
+      // push_front() isn't going to be very fast - it's only here for usability.
+      inline void push_front(const T& obj)
+      {
+         insert(0, &obj, 1);
+      }
+
+      vector& append(const vector& other)
+      {
+         if (other.m_size)
+            insert(m_size, &other[0], other.m_size);
+         return *this;
+      }
+
+      vector& append(const T* p, uint32_t n)
+      {
+         if (n)
+            insert(m_size, p, n);
+         return *this;
+      }
+            
+      inline void erase(uint32_t start, uint32_t n)
+      {
+         assert((start + n) <= m_size);
+         if ((start + n) > m_size)
+            return;
+
+         if (!n)
+            return;
+
+         const uint32_t num_to_move = m_size - (start + n);
+
+         T* pDst = m_p + start;
+
+         const T* pSrc = m_p + start + n;
+
+         if (BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(T))
+         {
+            // This test is overly cautious.
+            if ((!BASISU_IS_BITWISE_COPYABLE(T)) || (BASISU_HAS_DESTRUCTOR(T)))
+            {
+               // Type has been marked explictly as bitwise movable, which means we can move them around but they may need to be destructed.
+               // First destroy the erased objects.
+               scalar_type<T>::destruct_array(pDst, n);
+            }
+
+            // Copy "down" the objects to preserve, filling in the empty slots.
+            memmove(pDst, pSrc, num_to_move * sizeof(T));
+         }
+         else
+         {
+            // Type is not bitwise copyable or movable. 
+            // Move them down one at a time by using the equals operator, and destroying anything that's left over at the end.
+            T* pDst_end = pDst + num_to_move;
+            while (pDst != pDst_end)
+               *pDst++ = *pSrc++;
+
+            scalar_type<T>::destruct_array(pDst_end, n);
+         }
+
+         m_size -= n;
+      }
+
+      inline void erase(uint32_t index)
+      {
+         erase(index, 1);
+      }
+
+      inline void erase(T* p)
+      {
+         assert((p >= m_p) && (p < (m_p + m_size)));
+         erase(static_cast<uint32_t>(p - m_p));
+      }
+
+      inline void erase(T *pFirst, T *pEnd)
+      {
+         assert(pFirst <= pEnd);
+         assert(pFirst >= begin() && pFirst <= end());
+         assert(pEnd >= begin() && pEnd <= end());
+
+         int64_t ofs = pFirst - begin();
+         if ((ofs < 0) || (ofs > UINT32_MAX))
+         {
+            assert(0);
+            return;
+         }
+
+         int64_t n = pEnd - pFirst;
+         if ((n < 0) || (n > UINT32_MAX))
+         {
+            assert(0);
+            return;
+         }
+
+         erase((uint32_t)ofs, (uint32_t)n);
+      }
+
+      void erase_unordered(uint32_t index)
+      {
+         assert(index < m_size);
+
+         if ((index + 1) < m_size)
+            (*this)[index] = back();
+
+         pop_back();
+      }
+
+      inline bool operator== (const vector& rhs) const
+      {
+         if (m_size != rhs.m_size)
+            return false;
+         else if (m_size)
+         {
+            if (scalar_type<T>::cFlag)
+               return memcmp(m_p, rhs.m_p, sizeof(T) * m_size) == 0;
+            else
+            {
+               const T* pSrc = m_p;
+               const T* pDst = rhs.m_p;
+               for (uint32_t i = m_size; i; i--)
+                  if (!(*pSrc++ == *pDst++))
+                     return false;
+            }
+         }
+
+         return true;
+      }
+
+      inline bool operator< (const vector& rhs) const
+      {
+         const uint32_t min_size = helpers::minimum(m_size, rhs.m_size);
+
+         const T* pSrc = m_p;
+         const T* pSrc_end = m_p + min_size;
+         const T* pDst = rhs.m_p;
+
+         while ((pSrc < pSrc_end) && (*pSrc == *pDst))
+         {
+            pSrc++;
+            pDst++;
+         }
+
+         if (pSrc < pSrc_end)
+            return *pSrc < *pDst;
+
+         return m_size < rhs.m_size;
+      }
+
+      inline void swap(vector& other)
+      {
+         std::swap(m_p, other.m_p);
+         std::swap(m_size, other.m_size);
+         std::swap(m_capacity, other.m_capacity);
+      }
+
+      inline void sort()
+      {
+         std::sort(begin(), end());
+      }
+
+      inline void unique()
+      {
+         if (!empty())
+         {
+            sort();
+
+            resize(std::unique(begin(), end()) - begin());
+         }
+      }
+
+      inline void reverse()
+      {
+         uint32_t j = m_size >> 1;
+         for (uint32_t i = 0; i < j; i++)
+            std::swap(m_p[i], m_p[m_size - 1 - i]);
+      }
+
+      inline int find(const T& key) const
+      {
+         const T* p = m_p;
+         const T* p_end = m_p + m_size;
+
+         uint32_t index = 0;
+
+         while (p != p_end)
+         {
+            if (key == *p)
+               return index;
+
+            p++;
+            index++;
+         }
+
+         return cInvalidIndex;
+      }
+
+      inline int find_sorted(const T& key) const
+      {
+         if (m_size)
+         {
+            // Uniform binary search - Knuth Algorithm 6.2.1 U, unrolled twice.
+            int i = ((m_size + 1) >> 1) - 1;
+            int m = m_size;
+
+            for (; ; )
+            {
+               assert(i >= 0 && i < (int)m_size);
+               const T* pKey_i = m_p + i;
+               int cmp = key < *pKey_i;
+#if defined(_DEBUG) || defined(DEBUG)
+               int cmp2 = *pKey_i < key;
+               assert((cmp != cmp2) || (key == *pKey_i));
+#endif
+               if ((!cmp) && (key == *pKey_i)) return i;
+               m >>= 1;
+               if (!m) break;
+               cmp = -cmp;
+               i += (((m + 1) >> 1) ^ cmp) - cmp;
+               if (i < 0)
+                  break;
+
+               assert(i >= 0 && i < (int)m_size);
+               pKey_i = m_p + i;
+               cmp = key < *pKey_i;
+#if defined(_DEBUG) || defined(DEBUG)
+               cmp2 = *pKey_i < key;
+               assert((cmp != cmp2) || (key == *pKey_i));
+#endif
+               if ((!cmp) && (key == *pKey_i)) return i;
+               m >>= 1;
+               if (!m) break;
+               cmp = -cmp;
+               i += (((m + 1) >> 1) ^ cmp) - cmp;
+               if (i < 0)
+                  break;
+            }
+         }
+
+         return cInvalidIndex;
+      }
+
+      template<typename Q>
+      inline int find_sorted(const T& key, Q less_than) const
+      {
+         if (m_size)
+         {
+            // Uniform binary search - Knuth Algorithm 6.2.1 U, unrolled twice.
+            int i = ((m_size + 1) >> 1) - 1;
+            int m = m_size;
+
+            for (; ; )
+            {
+               assert(i >= 0 && i < (int)m_size);
+               const T* pKey_i = m_p + i;
+               int cmp = less_than(key, *pKey_i);
+               if ((!cmp) && (!less_than(*pKey_i, key))) return i;
+               m >>= 1;
+               if (!m) break;
+               cmp = -cmp;
+               i += (((m + 1) >> 1) ^ cmp) - cmp;
+               if (i < 0)
+                  break;
+
+               assert(i >= 0 && i < (int)m_size);
+               pKey_i = m_p + i;
+               cmp = less_than(key, *pKey_i);
+               if ((!cmp) && (!less_than(*pKey_i, key))) return i;
+               m >>= 1;
+               if (!m) break;
+               cmp = -cmp;
+               i += (((m + 1) >> 1) ^ cmp) - cmp;
+               if (i < 0) 
+                  break;
+            }
+         }
+
+         return cInvalidIndex;
+      }
+
+      inline uint32_t count_occurences(const T& key) const
+      {
+         uint32_t c = 0;
+
+         const T* p = m_p;
+         const T* p_end = m_p + m_size;
+
+         while (p != p_end)
+         {
+            if (key == *p)
+               c++;
+
+            p++;
+         }
+
+         return c;
+      }
+
+      inline void set_all(const T& o)
+      {
+         if ((sizeof(T) == 1) && (scalar_type<T>::cFlag))
+            memset(m_p, *reinterpret_cast<const uint8_t*>(&o), m_size);
+         else
+         {
+            T* pDst = m_p;
+            T* pDst_end = pDst + m_size;
+            while (pDst != pDst_end)
+               *pDst++ = o;
+         }
+      }
+
+      // Caller assumes ownership of the heap block associated with the container. Container is cleared.
+      inline void* assume_ownership()
+      {
+         T* p = m_p;
+         m_p = NULL;
+         m_size = 0;
+         m_capacity = 0;
+         return p;
+      }
+
+      // Caller is granting ownership of the indicated heap block.
+      // Block must have size constructed elements, and have enough room for capacity elements.
+      // The block must have been allocated using malloc().
+      // Important: This method is used in Basis Universal. If you change how this container allocates memory, you'll need to change any users of this method.
+      inline bool grant_ownership(T* p, uint32_t size, uint32_t capacity)
+      {
+         // To to prevent the caller from obviously shooting themselves in the foot.
+         if (((p + capacity) > m_p) && (p < (m_p + m_capacity)))
+         {
+            // Can grant ownership of a block inside the container itself!
+            assert(0);
+            return false;
+         }
+
+         if (size > capacity)
+         {
+            assert(0);
+            return false;
+         }
+
+         if (!p)
+         {
+            if (capacity)
+            {
+               assert(0);
+               return false;
+            }
+         }
+         else if (!capacity)
+         {
+            assert(0);
+            return false;
+         }
+
+         clear();
+         m_p = p;
+         m_size = size;
+         m_capacity = capacity;
+         return true;
+      }
+
+   private:
+      T* m_p;
+      uint32_t m_size;
+      uint32_t m_capacity;
+
+      template<typename Q> struct is_vector { enum { cFlag = false }; };
+      template<typename Q> struct is_vector< vector<Q> > { enum { cFlag = true }; };
+
+      static void object_mover(void* pDst_void, void* pSrc_void, uint32_t num)
+      {
+         T* pSrc = static_cast<T*>(pSrc_void);
+         T* const pSrc_end = pSrc + num;
+         T* pDst = static_cast<T*>(pDst_void);
+
+         while (pSrc != pSrc_end)
+         {
+            // placement new
+            new (static_cast<void*>(pDst)) T(*pSrc);
+            pSrc->~T();
+            ++pSrc;
+            ++pDst;
+         }
+      }
+
+      inline bool increase_capacity(uint32_t min_new_capacity, bool grow_hint, bool nofail = false)
+      {
+         return reinterpret_cast<elemental_vector*>(this)->increase_capacity(
+            min_new_capacity, grow_hint, sizeof(T),
+            (BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(T) || (is_vector<T>::cFlag)) ? NULL : object_mover, nofail);
+      }
+   };
+
+   template<typename T> struct bitwise_movable< vector<T> > { enum { cFlag = true }; };
+
+   // Hash map
+
+   template <typename T>
+   struct hasher
+   {
+      inline size_t operator() (const T& key) const { return static_cast<size_t>(key); }
+   };
+
+   template <typename T>
+   struct equal_to
+   {
+      inline bool operator()(const T& a, const T& b) const { return a == b; }
+   };
+
+   // Important: The Hasher and Equals objects must be bitwise movable!
+   template<typename Key, typename Value = empty_type, typename Hasher = hasher<Key>, typename Equals = equal_to<Key> >
+   class hash_map
+   {
+   public:
+      class iterator;
+      class const_iterator;
+   
+   private:
+      friend class iterator;
+      friend class const_iterator;
+
+      enum state
+      {
+         cStateInvalid = 0,
+         cStateValid = 1
+      };
+
+      enum
+      {
+         cMinHashSize = 4U
+      };
+
+   public:
+      typedef hash_map<Key, Value, Hasher, Equals> hash_map_type;
+      typedef std::pair<Key, Value> value_type;
+      typedef Key                   key_type;
+      typedef Value                 referent_type;
+      typedef Hasher                hasher_type;
+      typedef Equals                equals_type;
+
+      hash_map() :
+         m_hash_shift(32), m_num_valid(0), m_grow_threshold(0)
+      {
+      }
+
+      hash_map(const hash_map& other) :
+         m_values(other.m_values),
+         m_hash_shift(other.m_hash_shift),
+         m_hasher(other.m_hasher),
+         m_equals(other.m_equals),
+         m_num_valid(other.m_num_valid),
+         m_grow_threshold(other.m_grow_threshold)
+      {
+      }
+
+      hash_map& operator= (const hash_map& other)
+      {
+         if (this == &other)
+            return *this;
+
+         clear();
+
+         m_values = other.m_values;
+         m_hash_shift = other.m_hash_shift;
+         m_num_valid = other.m_num_valid;
+         m_grow_threshold = other.m_grow_threshold;
+         m_hasher = other.m_hasher;
+         m_equals = other.m_equals;
+
+         return *this;
+      }
+
+      inline ~hash_map()
+      {
+         clear();
+      }
+
+      const Equals& get_equals() const { return m_equals; }
+      Equals& get_equals() { return m_equals; }
+
+      void set_equals(const Equals& equals) { m_equals = equals; }
+
+      const Hasher& get_hasher() const { return m_hasher; }
+      Hasher& get_hasher() { return m_hasher; }
+
+      void set_hasher(const Hasher& hasher) { m_hasher = hasher; }
+
+      inline void clear()
+      {
+         if (!m_values.empty())
+         {
+            if (BASISU_HAS_DESTRUCTOR(Key) || BASISU_HAS_DESTRUCTOR(Value))
+            {
+               node* p = &get_node(0);
+               node* p_end = p + m_values.size();
+
+               uint32_t num_remaining = m_num_valid;
+               while (p != p_end)
+               {
+                  if (p->state)
+                  {
+                     destruct_value_type(p);
+                     num_remaining--;
+                     if (!num_remaining)
+                        break;
+                  }
+
+                  p++;
+               }
+            }
+
+            m_values.clear_no_destruction();
+
+            m_hash_shift = 32;
+            m_num_valid = 0;
+            m_grow_threshold = 0;
+         }
+      }
+
+      inline void reset()
+      {
+         if (!m_num_valid)
+            return;
+
+         if (BASISU_HAS_DESTRUCTOR(Key) || BASISU_HAS_DESTRUCTOR(Value))
+         {
+            node* p = &get_node(0);
+            node* p_end = p + m_values.size();
+
+            uint32_t num_remaining = m_num_valid;
+            while (p != p_end)
+            {
+               if (p->state)
+               {
+                  destruct_value_type(p);
+                  p->state = cStateInvalid;
+
+                  num_remaining--;
+                  if (!num_remaining)
+                     break;
+               }
+
+               p++;
+            }
+         }
+         else if (sizeof(node) <= 32)
+         {
+            memset(&m_values[0], 0, m_values.size_in_bytes());
+         }
+         else
+         {
+            node* p = &get_node(0);
+            node* p_end = p + m_values.size();
+
+            uint32_t num_remaining = m_num_valid;
+            while (p != p_end)
+            {
+               if (p->state)
+               {
+                  p->state = cStateInvalid;
+
+                  num_remaining--;
+                  if (!num_remaining)
+                     break;
+               }
+
+               p++;
+            }
+         }
+
+         m_num_valid = 0;
+      }
+
+      inline uint32_t size()
+      {
+         return m_num_valid;
+      }
+
+      inline uint32_t get_table_size()
+      {
+         return m_values.size();
+      }
+
+      inline bool empty()
+      {
+         return !m_num_valid;
+      }
+
+      inline void reserve(uint32_t new_capacity)
+      {
+         uint64_t new_hash_size = helpers::maximum(1U, new_capacity);
+
+         new_hash_size = new_hash_size * 2ULL;
+
+         if (!helpers::is_power_of_2(new_hash_size))
+            new_hash_size = helpers::next_pow2(new_hash_size);
+
+         new_hash_size = helpers::maximum<uint64_t>(cMinHashSize, new_hash_size);
+
+         new_hash_size = helpers::minimum<uint64_t>(0x80000000UL, new_hash_size);
+
+         if (new_hash_size > m_values.size())
+            rehash((uint32_t)new_hash_size);
+      }
+            
+      class iterator
+      {
+         friend class hash_map<Key, Value, Hasher, Equals>;
+         friend class hash_map<Key, Value, Hasher, Equals>::const_iterator;
+
+      public:
+         inline iterator() : m_pTable(NULL), m_index(0) { }
+         inline iterator(hash_map_type& table, uint32_t index) : m_pTable(&table), m_index(index) { }
+         inline iterator(const iterator& other) : m_pTable(other.m_pTable), m_index(other.m_index) { }
+
+         inline iterator& operator= (const iterator& other)
+         {
+            m_pTable = other.m_pTable;
+            m_index = other.m_index;
+            return *this;
+         }
+
+         // post-increment
+         inline iterator operator++(int)
+         {
+            iterator result(*this);
+            ++*this;
+            return result;
+         }
+
+         // pre-increment
+         inline iterator& operator++()
+         {
+            probe();
+            return *this;
+         }
+
+         inline value_type& operator*() const { return *get_cur(); }
+         inline value_type* operator->() const { return get_cur(); }
+
+         inline bool operator == (const iterator& b) const { return (m_pTable == b.m_pTable) && (m_index == b.m_index); }
+         inline bool operator != (const iterator& b) const { return !(*this == b); }
+         inline bool operator == (const const_iterator& b) const { return (m_pTable == b.m_pTable) && (m_index == b.m_index); }
+         inline bool operator != (const const_iterator& b) const { return !(*this == b); }
+
+      private:
+         hash_map_type* m_pTable;
+         uint32_t m_index;
+
+         inline value_type* get_cur() const
+         {
+            assert(m_pTable && (m_index < m_pTable->m_values.size()));
+            assert(m_pTable->get_node_state(m_index) == cStateValid);
+
+            return &m_pTable->get_node(m_index);
+         }
+
+         inline void probe()
+         {
+            assert(m_pTable);
+            m_index = m_pTable->find_next(m_index);
+         }
+      };
+
+      class const_iterator
+      {
+         friend class hash_map<Key, Value, Hasher, Equals>;
+         friend class hash_map<Key, Value, Hasher, Equals>::iterator;
+
+      public:
+         inline const_iterator() : m_pTable(NULL), m_index(0) { }
+         inline const_iterator(const hash_map_type& table, uint32_t index) : m_pTable(&table), m_index(index) { }
+         inline const_iterator(const iterator& other) : m_pTable(other.m_pTable), m_index(other.m_index) { }
+         inline const_iterator(const const_iterator& other) : m_pTable(other.m_pTable), m_index(other.m_index) { }
+
+         inline const_iterator& operator= (const const_iterator& other)
+         {
+            m_pTable = other.m_pTable;
+            m_index = other.m_index;
+            return *this;
+         }
+
+         inline const_iterator& operator= (const iterator& other)
+         {
+            m_pTable = other.m_pTable;
+            m_index = other.m_index;
+            return *this;
+         }
+
+         // post-increment
+         inline const_iterator operator++(int)
+         {
+            const_iterator result(*this);
+            ++*this;
+            return result;
+         }
+
+         // pre-increment
+         inline const_iterator& operator++()
+         {
+            probe();
+            return *this;
+         }
+
+         inline const value_type& operator*() const { return *get_cur(); }
+         inline const value_type* operator->() const { return get_cur(); }
+
+         inline bool operator == (const const_iterator& b) const { return (m_pTable == b.m_pTable) && (m_index == b.m_index); }
+         inline bool operator != (const const_iterator& b) const { return !(*this == b); }
+         inline bool operator == (const iterator& b) const { return (m_pTable == b.m_pTable) && (m_index == b.m_index); }
+         inline bool operator != (const iterator& b) const { return !(*this == b); }
+
+      private:
+         const hash_map_type* m_pTable;
+         uint32_t m_index;
+
+         inline const value_type* get_cur() const
+         {
+            assert(m_pTable && (m_index < m_pTable->m_values.size()));
+            assert(m_pTable->get_node_state(m_index) == cStateValid);
+
+            return &m_pTable->get_node(m_index);
+         }
+
+         inline void probe()
+         {
+            assert(m_pTable);
+            m_index = m_pTable->find_next(m_index);
+         }
+      };
+
+      inline const_iterator begin() const
+      {
+         if (!m_num_valid)
+            return end();
+
+         return const_iterator(*this, find_next(UINT32_MAX));
+      }
+
+      inline const_iterator end() const
+      {
+         return const_iterator(*this, m_values.size());
+      }
+
+      inline iterator begin()
+      {
+         if (!m_num_valid)
+            return end();
+
+         return iterator(*this, find_next(UINT32_MAX));
+      }
+
+      inline iterator end()
+      {
+         return iterator(*this, m_values.size());
+      }
+
+      // insert_result.first will always point to inserted key/value (or the already existing key/value).
+      // insert_resutt.second will be true if a new key/value was inserted, or false if the key already existed (in which case first will point to the already existing value).
+      typedef std::pair<iterator, bool> insert_result;
+
+      inline insert_result insert(const Key& k, const Value& v = Value())
+      {
+         insert_result result;
+         if (!insert_no_grow(result, k, v))
+         {
+            grow();
+
+            // This must succeed.
+            if (!insert_no_grow(result, k, v))
+            {
+               fprintf(stderr, "insert() failed");
+               abort();
+            }
+         }
+
+         return result;
+      }
+
+      inline insert_result insert(const value_type& v)
+      {
+         return insert(v.first, v.second);
+      }
+
+      inline const_iterator find(const Key& k) const
+      {
+         return const_iterator(*this, find_index(k));
+      }
+
+      inline iterator find(const Key& k)
+      {
+         return iterator(*this, find_index(k));
+      }
+
+      inline bool erase(const Key& k)
+      {
+         uint32_t i = find_index(k);
+
+         if (i >= m_values.size())
+            return false;
+
+         node* pDst = &get_node(i);
+         destruct_value_type(pDst);
+         pDst->state = cStateInvalid;
+
+         m_num_valid--;
+
+         for (; ; )
+         {
+            uint32_t r, j = i;
+
+            node* pSrc = pDst;
+
+            do
+            {
+               if (!i)
+               {
+                  i = m_values.size() - 1;
+                  pSrc = &get_node(i);
+               }
+               else
+               {
+                  i--;
+                  pSrc--;
+               }
+
+               if (!pSrc->state)
+                  return true;
+
+               r = hash_key(pSrc->first);
+
+            } while ((i <= r && r < j) || (r < j && j < i) || (j < i && i <= r));
+
+            move_node(pDst, pSrc);
+
+            pDst = pSrc;
+         }
+      }
+
+      inline void swap(hash_map_type& other)
+      {
+         m_values.swap(other.m_values);
+         std::swap(m_hash_shift, other.m_hash_shift);
+         std::swap(m_num_valid, other.m_num_valid);
+         std::swap(m_grow_threshold, other.m_grow_threshold);
+         std::swap(m_hasher, other.m_hasher);
+         std::swap(m_equals, other.m_equals);
+      }
+
+   private:
+      struct node : public value_type
+      {
+         uint8_t state;
+      };
+
+      static inline void construct_value_type(value_type* pDst, const Key& k, const Value& v)
+      {
+         if (BASISU_IS_BITWISE_COPYABLE(Key))
+            memcpy(&pDst->first, &k, sizeof(Key));
+         else
+            scalar_type<Key>::construct(&pDst->first, k);
+
+         if (BASISU_IS_BITWISE_COPYABLE(Value))
+            memcpy(&pDst->second, &v, sizeof(Value));
+         else
+            scalar_type<Value>::construct(&pDst->second, v);
+      }
+
+      static inline void construct_value_type(value_type* pDst, const value_type* pSrc)
+      {
+         if ((BASISU_IS_BITWISE_COPYABLE(Key)) && (BASISU_IS_BITWISE_COPYABLE(Value)))
+         {
+            memcpy(pDst, pSrc, sizeof(value_type));
+         }
+         else
+         {
+            if (BASISU_IS_BITWISE_COPYABLE(Key))
+               memcpy(&pDst->first, &pSrc->first, sizeof(Key));
+            else
+               scalar_type<Key>::construct(&pDst->first, pSrc->first);
+
+            if (BASISU_IS_BITWISE_COPYABLE(Value))
+               memcpy(&pDst->second, &pSrc->second, sizeof(Value));
+            else
+               scalar_type<Value>::construct(&pDst->second, pSrc->second);
+         }
+      }
+
+      static inline void destruct_value_type(value_type* p)
+      {
+         scalar_type<Key>::destruct(&p->first);
+         scalar_type<Value>::destruct(&p->second);
+      }
+
+      // Moves *pSrc to *pDst efficiently.
+      // pDst should NOT be constructed on entry.
+      static inline void move_node(node* pDst, node* pSrc, bool update_src_state = true)
+      {
+         assert(!pDst->state);
+
+         if (BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(Key) && BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(Value))
+         {
+            memcpy(pDst, pSrc, sizeof(node));
+         }
+         else
+         {
+            if (BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(Key))
+               memcpy(&pDst->first, &pSrc->first, sizeof(Key));
+            else
+            {
+               scalar_type<Key>::construct(&pDst->first, pSrc->first);
+               scalar_type<Key>::destruct(&pSrc->first);
+            }
+
+            if (BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(Value))
+               memcpy(&pDst->second, &pSrc->second, sizeof(Value));
+            else
+            {
+               scalar_type<Value>::construct(&pDst->second, pSrc->second);
+               scalar_type<Value>::destruct(&pSrc->second);
+            }
+
+            pDst->state = cStateValid;
+         }
+
+         if (update_src_state)
+            pSrc->state = cStateInvalid;
+      }
+
+      struct raw_node
+      {
+         inline raw_node()
+         {
+            node* p = reinterpret_cast<node*>(this);
+            p->state = cStateInvalid;
+         }
+
+         inline ~raw_node()
+         {
+            node* p = reinterpret_cast<node*>(this);
+            if (p->state)
+               hash_map_type::destruct_value_type(p);
+         }
+
+         inline raw_node(const raw_node& other)
+         {
+            node* pDst = reinterpret_cast<node*>(this);
+            const node* pSrc = reinterpret_cast<const node*>(&other);
+
+            if (pSrc->state)
+            {
+               hash_map_type::construct_value_type(pDst, pSrc);
+               pDst->state = cStateValid;
+            }
+            else
+               pDst->state = cStateInvalid;
+         }
+
+         inline raw_node& operator= (const raw_node& rhs)
+         {
+            if (this == &rhs)
+               return *this;
+
+            node* pDst = reinterpret_cast<node*>(this);
+            const node* pSrc = reinterpret_cast<const node*>(&rhs);
+
+            if (pSrc->state)
+            {
+               if (pDst->state)
+               {
+                  pDst->first = pSrc->first;
+                  pDst->second = pSrc->second;
+               }
+               else
+               {
+                  hash_map_type::construct_value_type(pDst, pSrc);
+                  pDst->state = cStateValid;
+               }
+            }
+            else if (pDst->state)
+            {
+               hash_map_type::destruct_value_type(pDst);
+               pDst->state = cStateInvalid;
+            }
+
+            return *this;
+         }
+
+         uint8_t m_bits[sizeof(node)];
+      };
+
+      typedef basisu::vector<raw_node> node_vector;
+
+      node_vector    m_values;
+      uint32_t       m_hash_shift;
+
+      Hasher         m_hasher;
+      Equals         m_equals;
+
+      uint32_t       m_num_valid;
+
+      uint32_t       m_grow_threshold;
+
+      inline uint32_t hash_key(const Key& k) const
+      {
+         assert((1U << (32U - m_hash_shift)) == m_values.size());
+
+         uint32_t hash = static_cast<uint32_t>(m_hasher(k));
+
+         // Fibonacci hashing
+         hash = (2654435769U * hash) >> m_hash_shift;
+
+         assert(hash < m_values.size());
+         return hash;
+      }
+
+      inline const node& get_node(uint32_t index) const
+      {
+         return *reinterpret_cast<const node*>(&m_values[index]);
+      }
+
+      inline node& get_node(uint32_t index)
+      {
+         return *reinterpret_cast<node*>(&m_values[index]);
+      }
+
+      inline state get_node_state(uint32_t index) const
+      {
+         return static_cast<state>(get_node(index).state);
+      }
+
+      inline void set_node_state(uint32_t index, bool valid)
+      {
+         get_node(index).state = valid;
+      }
+
+      inline void grow()
+      {
+         uint64_t n = m_values.size() * 3ULL; // was * 2
+         
+         if (!helpers::is_power_of_2(n))
+            n = helpers::next_pow2(n);
+
+         if (n > 0x80000000UL)
+            n = 0x80000000UL;
+
+         rehash(helpers::maximum<uint32_t>(cMinHashSize, (uint32_t)n));
+      }
+
+      inline void rehash(uint32_t new_hash_size)
+      {
+         assert(new_hash_size >= m_num_valid);
+         assert(helpers::is_power_of_2(new_hash_size));
+
+         if ((new_hash_size < m_num_valid) || (new_hash_size == m_values.size()))
+            return;
+
+         hash_map new_map;
+         new_map.m_values.resize(new_hash_size);
+         new_map.m_hash_shift = 32U - helpers::floor_log2i(new_hash_size);
+         assert(new_hash_size == (1U << (32U - new_map.m_hash_shift)));
+         new_map.m_grow_threshold = UINT_MAX;
+
+         node* pNode = reinterpret_cast<node*>(m_values.begin());
+         node* pNode_end = pNode + m_values.size();
+
+         while (pNode != pNode_end)
+         {
+            if (pNode->state)
+            {
+               new_map.move_into(pNode);
+
+               if (new_map.m_num_valid == m_num_valid)
+                  break;
+            }
+
+            pNode++;
+         }
+
+         new_map.m_grow_threshold = (new_hash_size + 1U) >> 1U;
+
+         m_values.clear_no_destruction();
+         m_hash_shift = 32;
+
+         swap(new_map);
+      }
+
+      inline uint32_t find_next(uint32_t index) const
+      {
+         index++;
+
+         if (index >= m_values.size())
+            return index;
+
+         const node* pNode = &get_node(index);
+
+         for (; ; )
+         {
+            if (pNode->state)
+               break;
+
+            if (++index >= m_values.size())
+               break;
+
+            pNode++;
+         }
+
+         return index;
+      }
+
+      inline uint32_t find_index(const Key& k) const
+      {
+         if (m_num_valid)
+         {
+            uint32_t index = hash_key(k);
+            const node* pNode = &get_node(index);
+
+            if (pNode->state)
+            {
+               if (m_equals(pNode->first, k))
+                  return index;
+
+               const uint32_t orig_index = index;
+
+               for (; ; )
+               {
+                  if (!index)
+                  {
+                     index = m_values.size() - 1;
+                     pNode = &get_node(index);
+                  }
+                  else
+                  {
+                     index--;
+                     pNode--;
+                  }
+
+                  if (index == orig_index)
+                     break;
+
+                  if (!pNode->state)
+                     break;
+
+                  if (m_equals(pNode->first, k))
+                     return index;
+               }
+            }
+         }
+
+         return m_values.size();
+      }
+
+      inline bool insert_no_grow(insert_result& result, const Key& k, const Value& v = Value())
+      {
+         if (!m_values.size())
+            return false;
+
+         uint32_t index = hash_key(k);
+         node* pNode = &get_node(index);
+
+         if (pNode->state)
+         {
+            if (m_equals(pNode->first, k))
+            {
+               result.first = iterator(*this, index);
+               result.second = false;
+               return true;
+            }
+
+            const uint32_t orig_index = index;
+
+            for (; ; )
+            {
+               if (!index)
+               {
+                  index = m_values.size() - 1;
+                  pNode = &get_node(index);
+               }
+               else
+               {
+                  index--;
+                  pNode--;
+               }
+
+               if (orig_index == index)
+                  return false;
+
+               if (!pNode->state)
+                  break;
+
+               if (m_equals(pNode->first, k))
+               {
+                  result.first = iterator(*this, index);
+                  result.second = false;
+                  return true;
+               }
+            }
+         }
+
+         if (m_num_valid >= m_grow_threshold)
+            return false;
+
+         construct_value_type(pNode, k, v);
+
+         pNode->state = cStateValid;
+
+         m_num_valid++;
+         assert(m_num_valid <= m_values.size());
+
+         result.first = iterator(*this, index);
+         result.second = true;
+
+         return true;
+      }
+
+      inline void move_into(node* pNode)
+      {
+         uint32_t index = hash_key(pNode->first);
+         node* pDst_node = &get_node(index);
+
+         if (pDst_node->state)
+         {
+            const uint32_t orig_index = index;
+
+            for (; ; )
+            {
+               if (!index)
+               {
+                  index = m_values.size() - 1;
+                  pDst_node = &get_node(index);
+               }
+               else
+               {
+                  index--;
+                  pDst_node--;
+               }
+
+               if (index == orig_index)
+               {
+                  assert(false);
+                  return;
+               }
+
+               if (!pDst_node->state)
+                  break;
+            }
+         }
+
+         move_node(pDst_node, pNode, false);
+
+         m_num_valid++;
+      }
+   };
+
+   template<typename Key, typename Value, typename Hasher, typename Equals>
+   struct bitwise_movable< hash_map<Key, Value, Hasher, Equals> > { enum { cFlag = true }; };
+   
+#if BASISU_HASHMAP_TEST
+   extern void hash_map_test();
+#endif
+      
+} // namespace basisu
+
+namespace std
+{
+   template<typename T>
+   inline void swap(basisu::vector<T>& a, basisu::vector<T>& b)
+   {
+      a.swap(b);
+   }
+
+   template<typename Key, typename Value, typename Hasher, typename Equals>
+   inline void swap(basisu::hash_map<Key, Value, Hasher, Equals>& a, basisu::hash_map<Key, Value, Hasher, Equals>& b)
+   {
+      a.swap(b);
+   }
+
+} // namespace std
+/**** ended inlining basisu_containers.h ****/
+
+#ifdef max
+#undef max
+#endif
+
+#ifdef min
+#undef min
+#endif
+
+#ifdef _WIN32
+#define strcasecmp _stricmp
+#endif
+
+// Set to one to enable debug printf()'s when any errors occur, for development/debugging. Especially useful for WebGL development.
+#ifndef BASISU_FORCE_DEVEL_MESSAGES
+#define BASISU_FORCE_DEVEL_MESSAGES 0
+#endif
+
+#define BASISU_NOTE_UNUSED(x) (void)(x)
+#define BASISU_ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+#define BASISU_NO_EQUALS_OR_COPY_CONSTRUCT(x) x(const x &) = delete; x& operator= (const x &) = delete;
+#define BASISU_ASSUME(x) static_assert(x, #x);
+#define BASISU_OFFSETOF(s, m) offsetof(s, m)
+#define BASISU_STRINGIZE(x) #x
+#define BASISU_STRINGIZE2(x) BASISU_STRINGIZE(x)
+
+#if BASISU_FORCE_DEVEL_MESSAGES
+	#define BASISU_DEVEL_ERROR(...) do { basisu::debug_printf(__VA_ARGS__); } while(0)
+#else
+	#define BASISU_DEVEL_ERROR(...)
+#endif
+
+namespace basisu
+{
+	// Types/utilities
+
+#ifdef _WIN32
+	const char BASISU_PATH_SEPERATOR_CHAR = '\\';
+#else
+	const char BASISU_PATH_SEPERATOR_CHAR = '/';
+#endif
+
+	typedef basisu::vector<uint8_t> uint8_vec;
+	typedef basisu::vector<int16_t> int16_vec;
+	typedef basisu::vector<uint16_t> uint16_vec;
+	typedef basisu::vector<uint32_t> uint_vec;
+	typedef basisu::vector<uint64_t> uint64_vec;
+	typedef basisu::vector<int> int_vec;
+	typedef basisu::vector<bool> bool_vec;
+
+	void enable_debug_printf(bool enabled);
+	void debug_printf(const char *pFmt, ...);
+		
+
+	template <typename T> inline void clear_obj(T& obj) { memset(&obj, 0, sizeof(obj)); }
+
+	template <typename T0, typename T1> inline T0 lerp(T0 a, T0 b, T1 c) { return a + (b - a) * c; }
+
+	template <typename S> inline S maximum(S a, S b) { return (a > b) ? a : b; }
+	template <typename S> inline S maximum(S a, S b, S c) { return maximum(maximum(a, b), c); }
+	template <typename S> inline S maximum(S a, S b, S c, S d) { return maximum(maximum(maximum(a, b), c), d); }
+	
+	template <typename S> inline S minimum(S a, S b) {	return (a < b) ? a : b; }
+	template <typename S> inline S minimum(S a, S b, S c) {	return minimum(minimum(a, b), c); }
+	template <typename S> inline S minimum(S a, S b, S c, S d) { return minimum(minimum(minimum(a, b), c), d); }
+
+	inline float clampf(float value, float low, float high) { if (value < low) value = low; else if (value > high) value = high;	return value; }
+	inline float saturate(float value) { return clampf(value, 0, 1.0f); }
+	inline uint8_t minimumub(uint8_t a, uint8_t b) { return (a < b) ? a : b; }
+	inline uint32_t minimumu(uint32_t a, uint32_t b) { return (a < b) ? a : b; }
+	inline int32_t minimumi(int32_t a, int32_t b) { return (a < b) ? a : b; }
+	inline float minimumf(float a, float b) { return (a < b) ? a : b; }
+	inline uint8_t maximumub(uint8_t a, uint8_t b) { return (a > b) ? a : b; }
+	inline uint32_t maximumu(uint32_t a, uint32_t b) { return (a > b) ? a : b; }
+	inline int32_t maximumi(int32_t a, int32_t b) { return (a > b) ? a : b; }
+	inline float maximumf(float a, float b) { return (a > b) ? a : b; }
+	inline int squarei(int i) { return i * i; }
+	inline float squaref(float i) { return i * i; }
+	template<typename T> inline T square(T a) { return a * a; }
+
+	template <typename S> inline S clamp(S value, S low, S high) { return (value < low) ? low : ((value > high) ? high : value); }
+
+	inline uint32_t iabs(int32_t i) { return (i < 0) ? static_cast<uint32_t>(-i) : static_cast<uint32_t>(i);	}
+	inline uint64_t iabs64(int64_t i) {	return (i < 0) ? static_cast<uint64_t>(-i) : static_cast<uint64_t>(i); }
+
+	template<typename T> inline void clear_vector(T &vec) { vec.erase(vec.begin(), vec.end()); }		
+	template<typename T> inline typename T::value_type *enlarge_vector(T &vec, size_t n) { size_t cs = vec.size(); vec.resize(cs + n); return &vec[cs]; }
+
+	inline bool is_pow2(uint32_t x) { return x && ((x & (x - 1U)) == 0U); }
+	inline bool is_pow2(uint64_t x) { return x && ((x & (x - 1U)) == 0U); }
+
+	template<typename T> inline T open_range_check(T v, T minv, T maxv) { assert(v >= minv && v < maxv); BASISU_NOTE_UNUSED(minv); BASISU_NOTE_UNUSED(maxv); return v; }
+	template<typename T> inline T open_range_check(T v, T maxv) { assert(v < maxv); BASISU_NOTE_UNUSED(maxv); return v; }
+
+	inline uint32_t total_bits(uint32_t v) { uint32_t l = 0; for ( ; v > 0U; ++l) v >>= 1; return l; }
+
+	template<typename T> inline T saturate(T val) { return clamp(val, 0.0f, 1.0f); }
+
+	template<typename T, typename R> inline void append_vector(T &vec, const R *pObjs, size_t n) 
+	{ 
+		if (n)
+		{
+			if (vec.size())
+			{
+				assert((pObjs + n) <= vec.begin() || (pObjs >= vec.end()));
+			}
+			const size_t cur_s = vec.size();
+			vec.resize(cur_s + n);
+			memcpy(&vec[cur_s], pObjs, sizeof(R) * n);
+		}
+	}
+
+	template<typename T> inline void append_vector(T &vec, const T &other_vec)
+	{
+		assert(&vec != &other_vec);
+		if (other_vec.size())
+			append_vector(vec, &other_vec[0], other_vec.size());
+	}
+
+	template<typename T> inline void vector_ensure_element_is_valid(T &vec, size_t idx)
+	{
+		if (idx >= vec.size())
+			vec.resize(idx + 1);
+	}
+
+	template<typename T> inline void vector_sort(T &vec)
+	{
+		if (vec.size())
+			std::sort(vec.begin(), vec.end());
+	}
+
+	template<typename T, typename U> inline bool unordered_set_contains(T& set, const U&obj)
+	{
+		return set.find(obj) != set.end();
+	}
+
+	template<typename T> int vector_find(const T &vec, const typename T::value_type &obj)
+	{
+		assert(vec.size() <= INT_MAX);
+		for (size_t i = 0; i < vec.size(); i++)
+			if (vec[i] == obj)
+				return static_cast<int>(i);
+		return -1;
+	}
+
+	template<typename T> void vector_set_all(T &vec, const typename T::value_type &obj)
+	{
+		for (size_t i = 0; i < vec.size(); i++)
+			vec[i] = obj;
+	}
+		
+	inline uint64_t read_be64(const void *p)
+	{
+		uint64_t val = 0;
+		for (uint32_t i = 0; i < 8; i++)
+			val |= (static_cast<uint64_t>(static_cast<const uint8_t *>(p)[7 - i]) << (i * 8));
+		return val;
+	}
+
+	inline void write_be64(void *p, uint64_t x)
+	{
+		for (uint32_t i = 0; i < 8; i++)
+			static_cast<uint8_t *>(p)[7 - i] = static_cast<uint8_t>(x >> (i * 8));
+	}
+
+	static inline uint16_t byteswap16(uint16_t x) { return static_cast<uint16_t>((x << 8) | (x >> 8)); }
+	static inline uint32_t byteswap32(uint32_t x) { return ((x << 24) | ((x << 8) & 0x00FF0000) | ((x >> 8) & 0x0000FF00) | (x >> 24)); }
+
+	inline uint32_t floor_log2i(uint32_t v)
+	{
+		uint32_t b = 0;
+		for (; v > 1U; ++b)
+			v >>= 1;
+		return b;
+	}
+
+	inline uint32_t ceil_log2i(uint32_t v)
+	{
+		uint32_t b = floor_log2i(v);
+		if ((b != 32) && (v > (1U << b)))
+			++b;
+		return b;
+	}
+
+	inline int posmod(int x, int y)
+	{
+		if (x >= 0)
+			return (x < y) ? x : (x % y);
+		int m = (-x) % y;
+		return (m != 0) ? (y - m) : m;
+	}
+
+	inline bool do_excl_ranges_overlap(int la, int ha, int lb, int hb)
+	{
+		assert(la < ha && lb < hb);
+		if ((ha <= lb) || (la >= hb)) return false;
+		return true;
+	}
+
+	static inline uint32_t read_le_dword(const uint8_t *pBytes)
+	{
+		return (pBytes[3] << 24U) | (pBytes[2] << 16U) | (pBytes[1] << 8U) | (pBytes[0]);
+	}
+
+	static inline void write_le_dword(uint8_t* pBytes, uint32_t val)
+	{
+		pBytes[0] = (uint8_t)val;
+		pBytes[1] = (uint8_t)(val >> 8U);
+		pBytes[2] = (uint8_t)(val >> 16U);
+		pBytes[3] = (uint8_t)(val >> 24U);
+	}
+		
+	// Always little endian 1-8 byte unsigned int
+	template<uint32_t NumBytes>
+	struct packed_uint
+	{
+		uint8_t m_bytes[NumBytes];
+
+		inline packed_uint() { static_assert(NumBytes <= sizeof(uint64_t), "Invalid NumBytes"); }
+		inline packed_uint(uint64_t v) { *this = v; }
+		inline packed_uint(const packed_uint& other) { *this = other; }
+						
+		inline packed_uint& operator= (uint64_t v) 
+		{ 
+			for (uint32_t i = 0; i < NumBytes; i++) 
+				m_bytes[i] = static_cast<uint8_t>(v >> (i * 8)); 
+			return *this; 
+		}
+
+		inline packed_uint& operator= (const packed_uint& rhs) 
+		{ 
+			memcpy(m_bytes, rhs.m_bytes, sizeof(m_bytes)); 
+			return *this;
+		}
+
+		inline operator uint32_t() const
+		{
+			switch (NumBytes)
+			{
+				case 1:  
+				{
+					return  m_bytes[0];
+				}
+				case 2:  
+				{
+					return (m_bytes[1] << 8U) | m_bytes[0];
+				}
+				case 3:  
+				{
+					return (m_bytes[2] << 16U) | (m_bytes[1] << 8U) | m_bytes[0];
+				}
+				case 4:  
+				{
+					return read_le_dword(m_bytes);
+				}
+				case 5:
+				{
+					uint32_t l = read_le_dword(m_bytes);
+					uint32_t h = m_bytes[4];
+					return static_cast<uint64_t>(l) | (static_cast<uint64_t>(h) << 32U);
+				}
+				case 6:
+				{
+					uint32_t l = read_le_dword(m_bytes);
+					uint32_t h = (m_bytes[5] << 8U) | m_bytes[4];
+					return static_cast<uint64_t>(l) | (static_cast<uint64_t>(h) << 32U);
+				}
+				case 7:
+				{
+					uint32_t l = read_le_dword(m_bytes);
+					uint32_t h = (m_bytes[6] << 16U) | (m_bytes[5] << 8U) | m_bytes[4];
+					return static_cast<uint64_t>(l) | (static_cast<uint64_t>(h) << 32U);
+				}
+				case 8:  
+				{
+					uint32_t l = read_le_dword(m_bytes);
+					uint32_t h = read_le_dword(m_bytes + 4);
+					return static_cast<uint64_t>(l) | (static_cast<uint64_t>(h) << 32U);
+				}
+				default: 
+				{
+					assert(0);
+					return 0;
+				}
+			}
+		}
+	};
+
+	enum eZero { cZero };
+	enum eNoClamp { cNoClamp };
+	
+	// Rice/Huffman entropy coding
+		
+	// This is basically Deflate-style canonical Huffman, except we allow for a lot more symbols.
+	enum
+	{
+		cHuffmanMaxSupportedCodeSize = 16, cHuffmanMaxSupportedInternalCodeSize = 31, 
+		cHuffmanFastLookupBits = 10, 
+		cHuffmanMaxSymsLog2 = 14, cHuffmanMaxSyms = 1 << cHuffmanMaxSymsLog2,
+
+		// Small zero runs
+		cHuffmanSmallZeroRunSizeMin = 3, cHuffmanSmallZeroRunSizeMax = 10, cHuffmanSmallZeroRunExtraBits = 3,
+
+		// Big zero run
+		cHuffmanBigZeroRunSizeMin = 11, cHuffmanBigZeroRunSizeMax = 138, cHuffmanBigZeroRunExtraBits = 7,
+
+		// Small non-zero run
+		cHuffmanSmallRepeatSizeMin = 3, cHuffmanSmallRepeatSizeMax = 6, cHuffmanSmallRepeatExtraBits = 2,
+
+		// Big non-zero run
+		cHuffmanBigRepeatSizeMin = 7, cHuffmanBigRepeatSizeMax = 134, cHuffmanBigRepeatExtraBits = 7,
+
+		cHuffmanTotalCodelengthCodes = 21, cHuffmanSmallZeroRunCode = 17, cHuffmanBigZeroRunCode = 18, cHuffmanSmallRepeatCode = 19, cHuffmanBigRepeatCode = 20
+	};
+
+	static const uint8_t g_huffman_sorted_codelength_codes[] = { cHuffmanSmallZeroRunCode, cHuffmanBigZeroRunCode,	cHuffmanSmallRepeatCode, cHuffmanBigRepeatCode, 0, 8, 7, 9, 6, 0xA, 5, 0xB, 4, 0xC, 3, 0xD, 2, 0xE, 1, 0xF, 0x10 };
+	const uint32_t cHuffmanTotalSortedCodelengthCodes = sizeof(g_huffman_sorted_codelength_codes) / sizeof(g_huffman_sorted_codelength_codes[0]);
+
+	// GPU texture formats
+
+	enum class texture_format
+	{
+		cInvalidTextureFormat = -1,
+		
+		// Block-based formats
+		cETC1,			// ETC1
+		cETC1S,			// ETC1 (subset: diff colors only, no subblocks)
+		cETC2_RGB,		// ETC2 color block (basisu doesn't support ETC2 planar/T/H modes - just basic ETC1)
+		cETC2_RGBA,		// ETC2 EAC alpha block followed by ETC2 color block
+		cETC2_ALPHA,	// ETC2 EAC alpha block 
+		cBC1,				// DXT1
+		cBC3,				// DXT5 (BC4/DXT5A block followed by a BC1/DXT1 block)
+		cBC4,				// DXT5A
+		cBC5,				// 3DC/DXN (two BC4/DXT5A blocks)
+		cBC7,
+		cASTC4x4,		// LDR only
+		cPVRTC1_4_RGB,
+		cPVRTC1_4_RGBA,
+		cATC_RGB,
+		cATC_RGBA_INTERPOLATED_ALPHA,
+		cFXT1_RGB,
+		cPVRTC2_4_RGBA,
+		cETC2_R11_EAC,
+		cETC2_RG11_EAC,
+		cUASTC4x4,		
+		cBC1_NV,
+		cBC1_AMD,
+		
+		// Uncompressed/raw pixels
+		cRGBA32,
+		cRGB565,
+		cBGR565,
+		cRGBA4444,
+		cABGR4444
+	};
+
+	inline uint32_t get_bytes_per_block(texture_format fmt)
+	{
+		switch (fmt)
+		{
+		case texture_format::cETC1:
+		case texture_format::cETC1S:
+		case texture_format::cETC2_RGB:
+		case texture_format::cETC2_ALPHA:
+		case texture_format::cBC1:
+		case texture_format::cBC1_NV:
+		case texture_format::cBC1_AMD:
+		case texture_format::cBC4:
+		case texture_format::cPVRTC1_4_RGB:
+		case texture_format::cPVRTC1_4_RGBA:
+		case texture_format::cATC_RGB:
+		case texture_format::cPVRTC2_4_RGBA:
+		case texture_format::cETC2_R11_EAC:
+			return 8;
+		case texture_format::cRGBA32:
+			return sizeof(uint32_t) * 16;
+		default:
+			break;
+		}
+		return 16;
+	}
+
+	inline uint32_t get_qwords_per_block(texture_format fmt)
+	{
+		return get_bytes_per_block(fmt) >> 3;
+	}
+
+	inline uint32_t get_block_width(texture_format fmt)
+	{
+		BASISU_NOTE_UNUSED(fmt);
+		switch (fmt)
+		{
+		case texture_format::cFXT1_RGB:
+			return 8;
+		default:
+			break;
+		}
+		return 4;
+	}
+
+	inline uint32_t get_block_height(texture_format fmt)
+	{
+		BASISU_NOTE_UNUSED(fmt);
+		return 4;
+	}
+							
+} // namespace basisu
+
+/**** ended inlining basisu.h ****/
+
+#define BASISD_znew (z = 36969 * (z & 65535) + (z >> 16))
+
+namespace basisu
+{
+	extern bool g_debug_printf;
+}
+
+namespace basist
+{
+	// Low-level formats directly supported by the transcoder (other supported texture formats are combinations of these low-level block formats).
+	// You probably don't care about these enum's unless you are going pretty low-level and calling the transcoder to decode individual slices.
+	enum class block_format
+	{
+		cETC1,								// ETC1S RGB 
+		cETC2_RGBA,							// full ETC2 EAC RGBA8 block
+		cBC1,									// DXT1 RGB 
+		cBC3,									// BC4 block followed by a four color BC1 block
+		cBC4,									// DXT5A (alpha block only)
+		cBC5,									// two BC4 blocks
+		cPVRTC1_4_RGB,						// opaque-only PVRTC1 4bpp
+		cPVRTC1_4_RGBA,					// PVRTC1 4bpp RGBA
+		cBC7,									// Full BC7 block, any mode
+		cBC7_M5_COLOR,						// RGB BC7 mode 5 color (writes an opaque mode 5 block)
+		cBC7_M5_ALPHA,						// alpha portion of BC7 mode 5 (cBC7_M5_COLOR output data must have been written to the output buffer first to set the mode/rot fields etc.)
+		cETC2_EAC_A8,						// alpha block of ETC2 EAC (first 8 bytes of the 16-bit ETC2 EAC RGBA format)
+		cASTC_4x4,							// ASTC 4x4 (either color-only or color+alpha). Note that the transcoder always currently assumes sRGB is not enabled when outputting ASTC 
+												// data. If you use a sRGB ASTC format you'll get ~1 LSB of additional error, because of the different way ASTC decoders scale 8-bit endpoints to 16-bits during unpacking.
+		
+		cATC_RGB,
+		cATC_RGBA_INTERPOLATED_ALPHA,
+		cFXT1_RGB,							// Opaque-only, has oddball 8x4 pixel block size
+
+		cPVRTC2_4_RGB,
+		cPVRTC2_4_RGBA,
+
+		cETC2_EAC_R11,
+		cETC2_EAC_RG11,
+												
+		cIndices,							// Used internally: Write 16-bit endpoint and selector indices directly to output (output block must be at least 32-bits)
+
+		cRGB32,								// Writes RGB components to 32bpp output pixels
+		cRGBA32,								// Writes RGB255 components to 32bpp output pixels
+		cA32,									// Writes alpha component to 32bpp output pixels
+				
+		cRGB565,
+		cBGR565,
+		
+		cRGBA4444_COLOR,
+		cRGBA4444_ALPHA,
+		cRGBA4444_COLOR_OPAQUE,
+		cRGBA4444,
+						
+		cTotalBlockFormats
+	};
+
+	const int COLOR5_PAL0_PREV_HI = 9, COLOR5_PAL0_DELTA_LO = -9, COLOR5_PAL0_DELTA_HI = 31;
+	const int COLOR5_PAL1_PREV_HI = 21, COLOR5_PAL1_DELTA_LO = -21, COLOR5_PAL1_DELTA_HI = 21;
+	const int COLOR5_PAL2_PREV_HI = 31, COLOR5_PAL2_DELTA_LO = -31, COLOR5_PAL2_DELTA_HI = 9;
+	const int COLOR5_PAL_MIN_DELTA_B_RUNLEN = 3, COLOR5_PAL_DELTA_5_RUNLEN_VLC_BITS = 3;
+
+	const uint32_t ENDPOINT_PRED_TOTAL_SYMBOLS = (4 * 4 * 4 * 4) + 1;
+	const uint32_t ENDPOINT_PRED_REPEAT_LAST_SYMBOL = ENDPOINT_PRED_TOTAL_SYMBOLS - 1;
+	const uint32_t ENDPOINT_PRED_MIN_REPEAT_COUNT = 3;
+	const uint32_t ENDPOINT_PRED_COUNT_VLC_BITS = 4;
+
+	const uint32_t NUM_ENDPOINT_PREDS = 3;// BASISU_ARRAY_SIZE(g_endpoint_preds);
+	const uint32_t CR_ENDPOINT_PRED_INDEX = NUM_ENDPOINT_PREDS - 1;
+	const uint32_t NO_ENDPOINT_PRED_INDEX = 3;//NUM_ENDPOINT_PREDS;
+	const uint32_t MAX_SELECTOR_HISTORY_BUF_SIZE = 64;
+	const uint32_t SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH = 3;
+	const uint32_t SELECTOR_HISTORY_BUF_RLE_COUNT_BITS = 6;
+	const uint32_t SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL = (1 << SELECTOR_HISTORY_BUF_RLE_COUNT_BITS);
+		
+	uint16_t crc16(const void *r, size_t size, uint16_t crc);
+		
+	class huffman_decoding_table
+	{
+		friend class bitwise_decoder;
+
+	public:
+		huffman_decoding_table()
+		{
+		}
+
+		void clear()
+		{
+			basisu::clear_vector(m_code_sizes);
+			basisu::clear_vector(m_lookup);
+			basisu::clear_vector(m_tree);
+		}
+
+		bool init(uint32_t total_syms, const uint8_t *pCode_sizes, uint32_t fast_lookup_bits = basisu::cHuffmanFastLookupBits)
+		{
+			if (!total_syms)
+			{
+				clear();
+				return true;
+			}
+
+			m_code_sizes.resize(total_syms);
+			memcpy(&m_code_sizes[0], pCode_sizes, total_syms);
+
+			const uint32_t huffman_fast_lookup_size = 1 << fast_lookup_bits;
+
+			m_lookup.resize(0);
+			m_lookup.resize(huffman_fast_lookup_size);
+
+			m_tree.resize(0);
+			m_tree.resize(total_syms * 2);
+
+			uint32_t syms_using_codesize[basisu::cHuffmanMaxSupportedInternalCodeSize + 1];
+			basisu::clear_obj(syms_using_codesize);
+			for (uint32_t i = 0; i < total_syms; i++)
+			{
+				if (pCode_sizes[i] > basisu::cHuffmanMaxSupportedInternalCodeSize)
+					return false;
+				syms_using_codesize[pCode_sizes[i]]++;
+			}
+
+			uint32_t next_code[basisu::cHuffmanMaxSupportedInternalCodeSize + 1];
+			next_code[0] = next_code[1] = 0;
+
+			uint32_t used_syms = 0, total = 0;
+			for (uint32_t i = 1; i < basisu::cHuffmanMaxSupportedInternalCodeSize; i++)
+			{
+				used_syms += syms_using_codesize[i];
+				next_code[i + 1] = (total = ((total + syms_using_codesize[i]) << 1));
+			}
+
+			if (((1U << basisu::cHuffmanMaxSupportedInternalCodeSize) != total) && (used_syms > 1U))
+				return false;
+
+			for (int tree_next = -1, sym_index = 0; sym_index < (int)total_syms; ++sym_index)
+			{
+				uint32_t rev_code = 0, l, cur_code, code_size = pCode_sizes[sym_index];
+				if (!code_size)
+					continue;
+
+				cur_code = next_code[code_size]++;
+
+				for (l = code_size; l > 0; l--, cur_code >>= 1)
+					rev_code = (rev_code << 1) | (cur_code & 1);
+
+				if (code_size <= fast_lookup_bits)
+				{
+					uint32_t k = (code_size << 16) | sym_index;
+					while (rev_code < huffman_fast_lookup_size)
+					{
+						if (m_lookup[rev_code] != 0)
+						{
+							// Supplied codesizes can't create a valid prefix code.
+							return false;
+						}
+
+						m_lookup[rev_code] = k;
+						rev_code += (1 << code_size);
+					}
+					continue;
+				}
+
+				int tree_cur;
+				if (0 == (tree_cur = m_lookup[rev_code & (huffman_fast_lookup_size - 1)]))
+				{
+					const uint32_t idx = rev_code & (huffman_fast_lookup_size - 1);
+					if (m_lookup[idx] != 0)
+					{
+						// Supplied codesizes can't create a valid prefix code.
+						return false;
+					}
+
+					m_lookup[idx] = tree_next;
+					tree_cur = tree_next;
+					tree_next -= 2;
+				}
+
+				if (tree_cur >= 0)
+				{
+					// Supplied codesizes can't create a valid prefix code.
+					return false;
+				}
+
+				rev_code >>= (fast_lookup_bits - 1);
+
+				for (int j = code_size; j > ((int)fast_lookup_bits + 1); j--)
+				{
+					tree_cur -= ((rev_code >>= 1) & 1);
+
+					const int idx = -tree_cur - 1;
+					if (idx < 0)
+						return false;
+					else if (idx >= (int)m_tree.size())
+						m_tree.resize(idx + 1);
+										
+					if (!m_tree[idx])
+					{
+						m_tree[idx] = (int16_t)tree_next;
+						tree_cur = tree_next;
+						tree_next -= 2;
+					}
+					else
+					{
+						tree_cur = m_tree[idx];
+						if (tree_cur >= 0)
+						{
+							// Supplied codesizes can't create a valid prefix code.
+							return false;
+						}
+					}
+				}
+
+				tree_cur -= ((rev_code >>= 1) & 1);
+
+				const int idx = -tree_cur - 1;
+				if (idx < 0)
+					return false;
+				else if (idx >= (int)m_tree.size())
+					m_tree.resize(idx + 1);
+
+				if (m_tree[idx] != 0)
+				{
+					// Supplied codesizes can't create a valid prefix code.
+					return false;
+				}
+
+				m_tree[idx] = (int16_t)sym_index;
+			}
+
+			return true;
+		}
+
+		const basisu::uint8_vec &get_code_sizes() const { return m_code_sizes; }
+		const basisu::int_vec get_lookup() const { return m_lookup; }
+		const basisu::int16_vec get_tree() const { return m_tree; }
+
+		bool is_valid() const { return m_code_sizes.size() > 0; }
+
+	private:
+		basisu::uint8_vec m_code_sizes;
+		basisu::int_vec m_lookup;
+		basisu::int16_vec m_tree;
+	};
+
+	class bitwise_decoder
+	{
+	public:
+		bitwise_decoder() :
+			m_buf_size(0),
+			m_pBuf(nullptr),
+			m_pBuf_start(nullptr),
+			m_pBuf_end(nullptr),
+			m_bit_buf(0),
+			m_bit_buf_size(0)
+		{
+		}
+
+		void clear()
+		{
+			m_buf_size = 0;
+			m_pBuf = nullptr;
+			m_pBuf_start = nullptr;
+			m_pBuf_end = nullptr;
+			m_bit_buf = 0;
+			m_bit_buf_size = 0;
+		}
+
+		bool init(const uint8_t *pBuf, uint32_t buf_size)
+		{
+			if ((!pBuf) && (buf_size))
+				return false;
+
+			m_buf_size = buf_size;
+			m_pBuf = pBuf;
+			m_pBuf_start = pBuf;
+			m_pBuf_end = pBuf + buf_size;
+			m_bit_buf = 0;
+			m_bit_buf_size = 0;
+			return true;
+		}
+
+		void stop()
+		{
+		}
+
+		inline uint32_t peek_bits(uint32_t num_bits)
+		{
+			if (!num_bits)
+				return 0;
+
+			assert(num_bits <= 25);
+
+			while (m_bit_buf_size < num_bits)
+			{
+				uint32_t c = 0;
+				if (m_pBuf < m_pBuf_end)
+					c = *m_pBuf++;
+
+				m_bit_buf |= (c << m_bit_buf_size);
+				m_bit_buf_size += 8;
+				assert(m_bit_buf_size <= 32);
+			}
+
+			return m_bit_buf & ((1 << num_bits) - 1);
+		}
+
+		void remove_bits(uint32_t num_bits)
+		{
+			assert(m_bit_buf_size >= num_bits);
+
+			m_bit_buf >>= num_bits;
+			m_bit_buf_size -= num_bits;
+		}
+
+		uint32_t get_bits(uint32_t num_bits)
+		{
+			if (num_bits > 25)
+			{
+				assert(num_bits <= 32);
+
+				const uint32_t bits0 = peek_bits(25);
+				m_bit_buf >>= 25;
+				m_bit_buf_size -= 25;
+				num_bits -= 25;
+
+				const uint32_t bits = peek_bits(num_bits);
+				m_bit_buf >>= num_bits;
+				m_bit_buf_size -= num_bits;
+
+				return bits0 | (bits << 25);
+			}
+
+			const uint32_t bits = peek_bits(num_bits);
+
+			m_bit_buf >>= num_bits;
+			m_bit_buf_size -= num_bits;
+
+			return bits;
+		}
+
+		uint32_t decode_truncated_binary(uint32_t n)
+		{
+			assert(n >= 2);
+
+			const uint32_t k = basisu::floor_log2i(n);
+			const uint32_t u = (1 << (k + 1)) - n;
+
+			uint32_t result = get_bits(k);
+
+			if (result >= u)
+				result = ((result << 1) | get_bits(1)) - u;
+
+			return result;
+		}
+
+		uint32_t decode_rice(uint32_t m)
+		{
+			assert(m);
+
+			uint32_t q = 0;
+			for (;;)
+			{
+				uint32_t k = peek_bits(16);
+				
+				uint32_t l = 0;
+				while (k & 1)
+				{
+					l++;
+					k >>= 1;
+				}
+				
+				q += l;
+
+				remove_bits(l);
+
+				if (l < 16)
+					break;
+			}
+
+			return (q << m) + (get_bits(m + 1) >> 1);
+		}
+
+		inline uint32_t decode_vlc(uint32_t chunk_bits)
+		{
+			assert(chunk_bits);
+
+			const uint32_t chunk_size = 1 << chunk_bits;
+			const uint32_t chunk_mask = chunk_size - 1;
+					
+			uint32_t v = 0;
+			uint32_t ofs = 0;
+
+			for ( ; ; )
+			{
+				uint32_t s = get_bits(chunk_bits + 1);
+				v |= ((s & chunk_mask) << ofs);
+				ofs += chunk_bits;
+
+				if ((s & chunk_size) == 0)
+					break;
+				
+				if (ofs >= 32)
+				{
+					assert(0);
+					break;
+				}
+			}
+
+			return v;
+		}
+
+		inline uint32_t decode_huffman(const huffman_decoding_table &ct, int fast_lookup_bits = basisu::cHuffmanFastLookupBits)
+		{
+			assert(ct.m_code_sizes.size());
+
+			const uint32_t huffman_fast_lookup_size = 1 << fast_lookup_bits;
+						
+			while (m_bit_buf_size < 16)
+			{
+				uint32_t c = 0;
+				if (m_pBuf < m_pBuf_end)
+					c = *m_pBuf++;
+
+				m_bit_buf |= (c << m_bit_buf_size);
+				m_bit_buf_size += 8;
+				assert(m_bit_buf_size <= 32);
+			}
+						
+			int code_len;
+
+			int sym;
+			if ((sym = ct.m_lookup[m_bit_buf & (huffman_fast_lookup_size - 1)]) >= 0)
+			{
+				code_len = sym >> 16;
+				sym &= 0xFFFF;
+			}
+			else
+			{
+				code_len = fast_lookup_bits;
+				do
+				{
+					sym = ct.m_tree[~sym + ((m_bit_buf >> code_len++) & 1)]; // ~sym = -sym - 1
+				} while (sym < 0);
+			}
+
+			m_bit_buf >>= code_len;
+			m_bit_buf_size -= code_len;
+
+			return sym;
+		}
+
+		bool read_huffman_table(huffman_decoding_table &ct)
+		{
+			ct.clear();
+
+			const uint32_t total_used_syms = get_bits(basisu::cHuffmanMaxSymsLog2);
+
+			if (!total_used_syms)
+				return true;
+			if (total_used_syms > basisu::cHuffmanMaxSyms)
+				return false;
+
+			uint8_t code_length_code_sizes[basisu::cHuffmanTotalCodelengthCodes];
+			basisu::clear_obj(code_length_code_sizes);
+
+			const uint32_t num_codelength_codes = get_bits(5);
+			if ((num_codelength_codes < 1) || (num_codelength_codes > basisu::cHuffmanTotalCodelengthCodes))
+				return false;
+
+			for (uint32_t i = 0; i < num_codelength_codes; i++)
+				code_length_code_sizes[basisu::g_huffman_sorted_codelength_codes[i]] = static_cast<uint8_t>(get_bits(3));
+
+			huffman_decoding_table code_length_table;
+			if (!code_length_table.init(basisu::cHuffmanTotalCodelengthCodes, code_length_code_sizes))
+				return false;
+
+			if (!code_length_table.is_valid())
+				return false;
+
+			basisu::uint8_vec code_sizes(total_used_syms);
+
+			uint32_t cur = 0;
+			while (cur < total_used_syms)
+			{
+				int c = decode_huffman(code_length_table);
+
+				if (c <= 16)
+					code_sizes[cur++] = static_cast<uint8_t>(c);
+				else if (c == basisu::cHuffmanSmallZeroRunCode)
+					cur += get_bits(basisu::cHuffmanSmallZeroRunExtraBits) + basisu::cHuffmanSmallZeroRunSizeMin;
+				else if (c == basisu::cHuffmanBigZeroRunCode)
+					cur += get_bits(basisu::cHuffmanBigZeroRunExtraBits) + basisu::cHuffmanBigZeroRunSizeMin;
+				else
+				{
+					if (!cur)
+						return false;
+
+					uint32_t l;
+					if (c == basisu::cHuffmanSmallRepeatCode)
+						l = get_bits(basisu::cHuffmanSmallRepeatExtraBits) + basisu::cHuffmanSmallRepeatSizeMin;
+					else
+						l = get_bits(basisu::cHuffmanBigRepeatExtraBits) + basisu::cHuffmanBigRepeatSizeMin;
+
+					const uint8_t prev = code_sizes[cur - 1];
+					if (prev == 0)
+						return false;
+					do
+					{
+						if (cur >= total_used_syms)
+							return false;
+						code_sizes[cur++] = prev;
+					} while (--l > 0);
+				}
+			}
+
+			if (cur != total_used_syms)
+				return false;
+
+			return ct.init(total_used_syms, &code_sizes[0]);
+		}
+
+	private:
+		uint32_t m_buf_size;
+		const uint8_t *m_pBuf;
+		const uint8_t *m_pBuf_start;
+		const uint8_t *m_pBuf_end;
+
+		uint32_t m_bit_buf;
+		uint32_t m_bit_buf_size;
+	};
+
+	inline uint32_t basisd_rand(uint32_t seed)
+	{
+		if (!seed)
+			seed++;
+		uint32_t z = seed;
+		BASISD_znew;
+		return z;
+	}
+
+	// Returns random number in [0,limit). Max limit is 0xFFFF.
+	inline uint32_t basisd_urand(uint32_t& seed, uint32_t limit)
+	{
+		seed = basisd_rand(seed);
+		return (((seed ^ (seed >> 16)) & 0xFFFF) * limit) >> 16;
+	}
+
+	class approx_move_to_front
+	{
+	public:
+		approx_move_to_front(uint32_t n)
+		{
+			init(n);
+		}
+
+		void init(uint32_t n)
+		{
+			m_values.resize(n);
+			m_rover = n / 2;
+		}
+
+		const basisu::int_vec& get_values() const { return m_values; }
+		basisu::int_vec& get_values() { return m_values; }
+
+		uint32_t size() const { return (uint32_t)m_values.size(); }
+
+		const int& operator[] (uint32_t index) const { return m_values[index]; }
+		int operator[] (uint32_t index) { return m_values[index]; }
+
+		void add(int new_value)
+		{
+			m_values[m_rover++] = new_value;
+			if (m_rover == m_values.size())
+				m_rover = (uint32_t)m_values.size() / 2;
+		}
+
+		void use(uint32_t index)
+		{
+			if (index)
+			{
+				//std::swap(m_values[index / 2], m_values[index]);
+				int x = m_values[index / 2];
+				int y = m_values[index];
+				m_values[index / 2] = y;
+				m_values[index] = x;
+			}
+		}
+
+		// returns -1 if not found
+		int find(int value) const
+		{
+			for (uint32_t i = 0; i < m_values.size(); i++)
+				if (m_values[i] == value)
+					return i;
+			return -1;
+		}
+
+		void reset()
+		{
+			const uint32_t n = (uint32_t)m_values.size();
+
+			m_values.clear();
+
+			init(n);
+		}
+
+	private:
+		basisu::int_vec m_values;
+		uint32_t m_rover;
+	};
+
+	struct decoder_etc_block;
+	
+	inline uint8_t clamp255(int32_t i)
+	{
+		return (uint8_t)((i & 0xFFFFFF00U) ? (~(i >> 31)) : i);
+	}
+
+	enum eNoClamp
+	{
+		cNoClamp = 0
+	};
+
+	struct color32
+	{
+		union
+		{
+			struct
+			{
+				uint8_t r;
+				uint8_t g;
+				uint8_t b;
+				uint8_t a;
+			};
+
+			uint8_t c[4];
+			
+			uint32_t m;
+		};
+
+		color32() { }
+
+		color32(uint32_t vr, uint32_t vg, uint32_t vb, uint32_t va) { set(vr, vg, vb, va); }
+		color32(eNoClamp unused, uint32_t vr, uint32_t vg, uint32_t vb, uint32_t va) { (void)unused; set_noclamp_rgba(vr, vg, vb, va); }
+
+		void set(uint32_t vr, uint32_t vg, uint32_t vb, uint32_t va) { c[0] = static_cast<uint8_t>(vr); c[1] = static_cast<uint8_t>(vg); c[2] = static_cast<uint8_t>(vb); c[3] = static_cast<uint8_t>(va); }
+
+		void set_noclamp_rgb(uint32_t vr, uint32_t vg, uint32_t vb) { c[0] = static_cast<uint8_t>(vr); c[1] = static_cast<uint8_t>(vg); c[2] = static_cast<uint8_t>(vb); }
+		void set_noclamp_rgba(uint32_t vr, uint32_t vg, uint32_t vb, uint32_t va) { set(vr, vg, vb, va); }
+
+		void set_clamped(int vr, int vg, int vb, int va) { c[0] = clamp255(vr); c[1] = clamp255(vg);	c[2] = clamp255(vb); c[3] = clamp255(va); }
+
+		uint8_t operator[] (uint32_t idx) const { assert(idx < 4); return c[idx]; }
+		uint8_t &operator[] (uint32_t idx) { assert(idx < 4); return c[idx]; }
+
+		bool operator== (const color32&rhs) const { return m == rhs.m; }
+
+		static color32 comp_min(const color32& a, const color32& b) { return color32(cNoClamp, basisu::minimum(a[0], b[0]), basisu::minimum(a[1], b[1]), basisu::minimum(a[2], b[2]), basisu::minimum(a[3], b[3])); }
+		static color32 comp_max(const color32& a, const color32& b) { return color32(cNoClamp, basisu::maximum(a[0], b[0]), basisu::maximum(a[1], b[1]), basisu::maximum(a[2], b[2]), basisu::maximum(a[3], b[3])); }
+	};
+
+	struct endpoint
+	{
+		color32 m_color5;
+		uint8_t m_inten5;
+		bool operator== (const endpoint& rhs) const
+		{
+			return (m_color5.r == rhs.m_color5.r) && (m_color5.g == rhs.m_color5.g) && (m_color5.b == rhs.m_color5.b) && (m_inten5 == rhs.m_inten5);
+		}
+		bool operator!= (const endpoint& rhs) const { return !(*this == rhs); }
+	};
+
+	struct selector
+	{
+		// Plain selectors (2-bits per value)
+		uint8_t m_selectors[4];
+
+		// ETC1 selectors
+		uint8_t m_bytes[4];
+
+		uint8_t m_lo_selector, m_hi_selector;
+		uint8_t m_num_unique_selectors;
+		bool operator== (const selector& rhs) const
+		{
+			return (m_selectors[0] == rhs.m_selectors[0]) &&
+				(m_selectors[1] == rhs.m_selectors[1]) &&
+				(m_selectors[2] == rhs.m_selectors[2]) &&
+				(m_selectors[3] == rhs.m_selectors[3]);
+		}
+		bool operator!= (const selector& rhs) const
+		{
+			return !(*this == rhs);
+		}
+
+		void init_flags()
+		{
+			uint32_t hist[4] = { 0, 0, 0, 0 };
+			for (uint32_t y = 0; y < 4; y++)
+			{
+				for (uint32_t x = 0; x < 4; x++)
+				{
+					uint32_t s = get_selector(x, y);
+					hist[s]++;
+				}
+			}
+
+			m_lo_selector = 3;
+			m_hi_selector = 0;
+			m_num_unique_selectors = 0;
+
+			for (uint32_t i = 0; i < 4; i++)
+			{
+				if (hist[i])
+				{
+					m_num_unique_selectors++;
+					if (i < m_lo_selector) m_lo_selector = static_cast<uint8_t>(i);
+					if (i > m_hi_selector) m_hi_selector = static_cast<uint8_t>(i);
+				}
+			}
+		}
+
+		// Returned selector value ranges from 0-3 and is a direct index into g_etc1_inten_tables.
+		inline uint32_t get_selector(uint32_t x, uint32_t y) const
+		{
+			assert((x < 4) && (y < 4));
+			return (m_selectors[y] >> (x * 2)) & 3;
+		}
+
+		void set_selector(uint32_t x, uint32_t y, uint32_t val)
+		{
+			static const uint8_t s_selector_index_to_etc1[4] = { 3, 2, 0, 1 };
+
+			assert((x | y | val) < 4);
+
+			m_selectors[y] &= ~(3 << (x * 2));
+			m_selectors[y] |= (val << (x * 2));
+
+			const uint32_t etc1_bit_index = x * 4 + y;
+
+			uint8_t *p = &m_bytes[3 - (etc1_bit_index >> 3)];
+
+			const uint32_t byte_bit_ofs = etc1_bit_index & 7;
+			const uint32_t mask = 1 << byte_bit_ofs;
+
+			const uint32_t etc1_val = s_selector_index_to_etc1[val];
+
+			const uint32_t lsb = etc1_val & 1;
+			const uint32_t msb = etc1_val >> 1;
+
+			p[0] &= ~mask;
+			p[0] |= (lsb << byte_bit_ofs);
+
+			p[-2] &= ~mask;
+			p[-2] |= (msb << byte_bit_ofs);
+		}
+	};
+
+	bool basis_block_format_is_uncompressed(block_format tex_type);
+	
+} // namespace basist
+
+
+
+/**** ended inlining basisu_transcoder_internal.h ****/
+/**** start inlining basisu_transcoder_uastc.h ****/
+// basisu_transcoder_uastc.h
+/**** skipping file: basisu_transcoder_internal.h ****/
+
+namespace basist
+{
+	struct color_quad_u8
+	{ 
+		uint8_t m_c[4]; 
+	};
+
+	const uint32_t TOTAL_UASTC_MODES = 19;
+	const uint32_t UASTC_MODE_INDEX_SOLID_COLOR = 8;
+
+	const uint32_t TOTAL_ASTC_BC7_COMMON_PARTITIONS2 = 30;
+	const uint32_t TOTAL_ASTC_BC7_COMMON_PARTITIONS3 = 11;
+	const uint32_t TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS = 19;
+
+	extern const uint8_t g_uastc_mode_weight_bits[TOTAL_UASTC_MODES];
+	extern const uint8_t g_uastc_mode_weight_ranges[TOTAL_UASTC_MODES];
+	extern const uint8_t g_uastc_mode_endpoint_ranges[TOTAL_UASTC_MODES];
+	extern const uint8_t g_uastc_mode_subsets[TOTAL_UASTC_MODES];
+	extern const uint8_t g_uastc_mode_planes[TOTAL_UASTC_MODES];
+	extern const uint8_t g_uastc_mode_comps[TOTAL_UASTC_MODES];
+	extern const uint8_t g_uastc_mode_has_etc1_bias[TOTAL_UASTC_MODES];
+	extern const uint8_t g_uastc_mode_has_bc1_hint0[TOTAL_UASTC_MODES];
+	extern const uint8_t g_uastc_mode_has_bc1_hint1[TOTAL_UASTC_MODES];
+	extern const uint8_t g_uastc_mode_has_alpha[TOTAL_UASTC_MODES];
+	extern const uint8_t g_uastc_mode_is_la[TOTAL_UASTC_MODES];
+
+	struct astc_bc7_common_partition2_desc
+	{
+		uint8_t m_bc7;
+		uint16_t m_astc;
+		bool m_invert;
+	};
+
+	extern const astc_bc7_common_partition2_desc g_astc_bc7_common_partitions2[TOTAL_ASTC_BC7_COMMON_PARTITIONS2];
+
+	struct bc73_astc2_common_partition_desc
+	{
+		uint8_t m_bc73;
+		uint16_t m_astc2;
+		uint8_t k;		// 0-5 - how to modify the BC7 3-subset pattern to match the ASTC pattern (LSB=invert)
+	};
+
+	extern const bc73_astc2_common_partition_desc g_bc7_3_astc2_common_partitions[TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS];
+
+	struct astc_bc7_common_partition3_desc
+	{
+		uint8_t m_bc7;
+		uint16_t m_astc;
+		uint8_t m_astc_to_bc7_perm; // converts ASTC to BC7 partition using g_astc_bc7_partition_index_perm_tables[][]
+	};
+
+	extern const astc_bc7_common_partition3_desc g_astc_bc7_common_partitions3[TOTAL_ASTC_BC7_COMMON_PARTITIONS3];
+
+	extern const uint8_t g_astc_bc7_patterns2[TOTAL_ASTC_BC7_COMMON_PARTITIONS2][16];
+	extern const uint8_t g_astc_bc7_patterns3[TOTAL_ASTC_BC7_COMMON_PARTITIONS3][16];
+	extern const uint8_t g_bc7_3_astc2_patterns2[TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS][16];
+
+	extern const uint8_t g_astc_bc7_pattern2_anchors[TOTAL_ASTC_BC7_COMMON_PARTITIONS2][3];
+	extern const uint8_t g_astc_bc7_pattern3_anchors[TOTAL_ASTC_BC7_COMMON_PARTITIONS3][3];
+	extern const uint8_t g_bc7_3_astc2_patterns2_anchors[TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS][3];
+
+	extern const uint32_t g_uastc_mode_huff_codes[TOTAL_UASTC_MODES + 1][2];
+
+	extern const uint8_t g_astc_to_bc7_partition_index_perm_tables[6][3];
+	extern const uint8_t g_bc7_to_astc_partition_index_perm_tables[6][3]; // inverse of g_astc_to_bc7_partition_index_perm_tables
+
+	extern const uint8_t* s_uastc_to_bc1_weights[6];
+
+	uint32_t bc7_convert_partition_index_3_to_2(uint32_t p, uint32_t k);
+
+	inline uint32_t astc_interpolate(uint32_t l, uint32_t h, uint32_t w, bool srgb)
+	{
+		if (srgb)
+		{
+			l = (l << 8) | 0x80;
+			h = (h << 8) | 0x80;
+		}
+		else
+		{
+			l = (l << 8) | l;
+			h = (h << 8) | h;
+		}
+
+		uint32_t k = (l * (64 - w) + h * w + 32) >> 6;
+
+		return k >> 8;
+	}
+
+	struct astc_block_desc
+	{
+		int m_weight_range;	// weight BISE range
+
+		int m_subsets;			// number of ASTC partitions
+		int m_partition_seed;	// partition pattern seed
+		int m_cem;				// color endpoint mode used by all subsets
+
+		int m_ccs;				// color component selector (dual plane only)
+		bool m_dual_plane;	// true if dual plane
+
+		// Weight and endpoint BISE values. 
+		// Note these values are NOT linear, they must be BISE encoded. See Table 97 and Table 107.
+		uint8_t m_endpoints[18];	// endpoint values, in RR GG BB etc. order 
+		uint8_t m_weights[64];		// weight index values, raster order, in P0 P1, P0 P1, etc. or P0, P0, P0, P0, etc. order
+	};
+
+	const uint32_t BC7ENC_TOTAL_ASTC_RANGES = 21;
+
+	// See tables 81, 93, 18.13.Endpoint Unquantization
+	const uint32_t TOTAL_ASTC_RANGES = 21;
+	extern const int g_astc_bise_range_table[TOTAL_ASTC_RANGES][3];
+
+	struct astc_quant_bin
+	{
+		uint8_t m_unquant; // unquantized value
+		uint8_t m_index; // sorted index
+	};
+
+	extern astc_quant_bin g_astc_unquant[BC7ENC_TOTAL_ASTC_RANGES][256]; // [ASTC encoded endpoint index]
+
+	int astc_get_levels(int range);
+	bool astc_is_valid_endpoint_range(uint32_t range);
+	uint32_t unquant_astc_endpoint(uint32_t packed_bits, uint32_t packed_trits, uint32_t packed_quints, uint32_t range);
+	uint32_t unquant_astc_endpoint_val(uint32_t packed_val, uint32_t range);
+
+	const uint8_t* get_anchor_indices(uint32_t subsets, uint32_t mode, uint32_t common_pattern, const uint8_t*& pPartition_pattern);
+
+	// BC7
+	const uint32_t BC7ENC_BLOCK_SIZE = 16;
+
+	struct bc7_block
+	{
+		uint64_t m_qwords[2];
+	};
+
+	struct bc7_optimization_results
+	{
+		uint32_t m_mode;
+		uint32_t m_partition;
+		uint8_t m_selectors[16];
+		uint8_t m_alpha_selectors[16];
+		color_quad_u8 m_low[3];
+		color_quad_u8 m_high[3];
+		uint32_t m_pbits[3][2];
+		uint32_t m_index_selector;
+		uint32_t m_rotation;
+	};
+
+	extern const uint32_t g_bc7_weights1[2];
+	extern const uint32_t g_bc7_weights2[4];
+	extern const uint32_t g_bc7_weights3[8];
+	extern const uint32_t g_bc7_weights4[16];
+	extern const uint32_t g_astc_weights4[16];
+	extern const uint32_t g_astc_weights5[32];
+	extern const uint32_t g_astc_weights_3levels[3];
+	extern const uint8_t g_bc7_partition1[16];
+	extern const uint8_t g_bc7_partition2[64 * 16];
+	extern const uint8_t g_bc7_partition3[64 * 16];
+	extern const uint8_t g_bc7_table_anchor_index_second_subset[64];
+	extern const uint8_t g_bc7_table_anchor_index_third_subset_1[64];
+	extern const uint8_t g_bc7_table_anchor_index_third_subset_2[64];
+	extern const uint8_t g_bc7_num_subsets[8];
+	extern const uint8_t g_bc7_partition_bits[8];
+	extern const uint8_t g_bc7_color_index_bitcount[8];
+	extern const uint8_t g_bc7_mode_has_p_bits[8];
+	extern const uint8_t g_bc7_mode_has_shared_p_bits[8];
+	extern const uint8_t g_bc7_color_precision_table[8];
+	extern const int8_t g_bc7_alpha_precision_table[8];
+	extern const uint8_t g_bc7_alpha_index_bitcount[8];
+
+	inline bool get_bc7_mode_has_seperate_alpha_selectors(int mode) { return (mode == 4) || (mode == 5); }
+	inline int get_bc7_color_index_size(int mode, int index_selection_bit) { return g_bc7_color_index_bitcount[mode] + index_selection_bit; }
+	inline int get_bc7_alpha_index_size(int mode, int index_selection_bit) { return g_bc7_alpha_index_bitcount[mode] - index_selection_bit; }
+
+	struct endpoint_err
+	{
+		uint16_t m_error; uint8_t m_lo; uint8_t m_hi;
+	};
+
+	extern endpoint_err g_bc7_mode_6_optimal_endpoints[256][2]; // [c][pbit]
+	const uint32_t BC7ENC_MODE_6_OPTIMAL_INDEX = 5;
+
+	extern endpoint_err g_bc7_mode_5_optimal_endpoints[256]; // [c]
+	const uint32_t BC7ENC_MODE_5_OPTIMAL_INDEX = 1;
+
+	// Packs a BC7 block from a high-level description. Handles all BC7 modes.
+	void encode_bc7_block(void* pBlock, const bc7_optimization_results* pResults);
+
+	// Packs an ASTC block
+	// Constraints: Always 4x4, all subset CEM's must be equal, only tested with LDR CEM's.
+	bool pack_astc_block(uint32_t* pDst, const astc_block_desc* pBlock, uint32_t mode);
+
+	void pack_astc_solid_block(void* pDst_block, const color32& color);
+
+#ifdef _DEBUG
+	int astc_compute_texel_partition(int seed, int x, int y, int z, int partitioncount, bool small_block);
+#endif
+		
+	struct uastc_block
+	{
+		union
+		{
+			uint8_t m_bytes[16];
+			uint32_t m_dwords[4];
+
+#ifndef __EMSCRIPTEN__
+			uint64_t m_qwords[2];
+#endif
+		};
+	};
+
+	struct unpacked_uastc_block
+	{
+		astc_block_desc m_astc;
+
+		uint32_t m_mode;
+		uint32_t m_common_pattern;
+
+		color32 m_solid_color;
+
+		bool m_bc1_hint0;
+		bool m_bc1_hint1;
+
+		bool m_etc1_flip;
+		bool m_etc1_diff;
+		uint32_t m_etc1_inten0;
+		uint32_t m_etc1_inten1;
+
+		uint32_t m_etc1_bias;
+
+		uint32_t m_etc2_hints;
+
+		uint32_t m_etc1_selector;
+		uint32_t m_etc1_r, m_etc1_g, m_etc1_b;
+	};
+
+	color32 apply_etc1_bias(const color32 &block_color, uint32_t bias, uint32_t limit, uint32_t subblock);
+	
+	struct decoder_etc_block;
+	struct eac_block;
+		
+	bool unpack_uastc(uint32_t mode, uint32_t common_pattern, const color32& solid_color, const astc_block_desc& astc, color32* pPixels, bool srgb);
+	bool unpack_uastc(const unpacked_uastc_block& unpacked_blk, color32* pPixels, bool srgb);
+
+	bool unpack_uastc(const uastc_block& blk, color32* pPixels, bool srgb);
+	bool unpack_uastc(const uastc_block& blk, unpacked_uastc_block& unpacked, bool undo_blue_contract, bool read_hints = true);
+
+	bool transcode_uastc_to_astc(const uastc_block& src_blk, void* pDst);
+
+	bool transcode_uastc_to_bc7(const unpacked_uastc_block& unpacked_src_blk, bc7_optimization_results& dst_blk);
+	bool transcode_uastc_to_bc7(const uastc_block& src_blk, bc7_optimization_results& dst_blk);
+	bool transcode_uastc_to_bc7(const uastc_block& src_blk, void* pDst);
+
+	void transcode_uastc_to_etc1(unpacked_uastc_block& unpacked_src_blk, color32 block_pixels[4][4], void* pDst);
+	bool transcode_uastc_to_etc1(const uastc_block& src_blk, void* pDst);
+	bool transcode_uastc_to_etc1(const uastc_block& src_blk, void* pDst, uint32_t channel);
+
+	void transcode_uastc_to_etc2_eac_a8(unpacked_uastc_block& unpacked_src_blk, color32 block_pixels[4][4], void* pDst);
+	bool transcode_uastc_to_etc2_rgba(const uastc_block& src_blk, void* pDst);
+
+	// Packs 16 scalar values to BC4. Same PSNR as stb_dxt's BC4 encoder, around 13% faster.
+	void encode_bc4(void* pDst, const uint8_t* pPixels, uint32_t stride);
+	
+	void encode_bc1_solid_block(void* pDst, uint32_t fr, uint32_t fg, uint32_t fb);
+
+	enum
+	{
+		cEncodeBC1HighQuality = 1,
+		cEncodeBC1HigherQuality = 2,
+		cEncodeBC1UseSelectors = 4,
+	};
+	void encode_bc1(void* pDst, const uint8_t* pPixels, uint32_t flags);
+	
+	// Alternate PCA-free encoder, around 15% faster, same (or slightly higher) avg. PSNR
+	void encode_bc1_alt(void* pDst, const uint8_t* pPixels, uint32_t flags);
+
+	void transcode_uastc_to_bc1_hint0(const unpacked_uastc_block& unpacked_src_blk, void* pDst);
+	void transcode_uastc_to_bc1_hint1(const unpacked_uastc_block& unpacked_src_blk, const color32 block_pixels[4][4], void* pDst, bool high_quality);
+
+	bool transcode_uastc_to_bc1(const uastc_block& src_blk, void* pDst, bool high_quality);
+	bool transcode_uastc_to_bc3(const uastc_block& src_blk, void* pDst, bool high_quality);
+	bool transcode_uastc_to_bc4(const uastc_block& src_blk, void* pDst, bool high_quality, uint32_t chan0);
+	bool transcode_uastc_to_bc5(const uastc_block& src_blk, void* pDst, bool high_quality, uint32_t chan0, uint32_t chan1);
+
+	bool transcode_uastc_to_etc2_eac_r11(const uastc_block& src_blk, void* pDst, bool high_quality, uint32_t chan0);
+	bool transcode_uastc_to_etc2_eac_rg11(const uastc_block& src_blk, void* pDst, bool high_quality, uint32_t chan0, uint32_t chan1);
+
+	bool transcode_uastc_to_pvrtc1_4_rgb(const uastc_block* pSrc_blocks, void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, bool high_quality, bool from_alpha);
+	bool transcode_uastc_to_pvrtc1_4_rgba(const uastc_block* pSrc_blocks, void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, bool high_quality);
+		
+	// uastc_init() MUST be called before using this module.
+	void uastc_init();
+
+} // namespace basist
+/**** ended inlining basisu_transcoder_uastc.h ****/
+/**** start inlining basisu_file_headers.h ****/
+// basis_file_headers.h
+// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+/**** skipping file: basisu_transcoder_internal.h ****/
+
+namespace basist
+{
+	// Slice desc header flags
+	enum basis_slice_desc_flags
+	{
+		cSliceDescFlagsHasAlpha = 1,
+		
+		// Video only: Frame doesn't refer to previous frame (no usage of conditional replenishment pred symbols)
+		// Currently the first frame is always an I-Frame, all subsequent frames are P-Frames. This will eventually be changed to periodic I-Frames.
+		cSliceDescFlagsFrameIsIFrame = 2			
+	};
+
+#pragma pack(push)
+#pragma pack(1)
+	struct basis_slice_desc
+	{
+		basisu::packed_uint<3> m_image_index;  // The index of the source image provided to the encoder (will always appear in order from first to last, first image index is 0, no skipping allowed)
+		basisu::packed_uint<1> m_level_index;	// The mipmap level index (mipmaps will always appear from largest to smallest)
+		basisu::packed_uint<1> m_flags;			// enum basis_slice_desc_flags
+
+		basisu::packed_uint<2> m_orig_width;	// The original image width (may not be a multiple of 4 pixels)
+		basisu::packed_uint<2> m_orig_height;  // The original image height (may not be a multiple of 4 pixels)
+
+		basisu::packed_uint<2> m_num_blocks_x;	// The slice's block X dimensions. Each block is 4x4 pixels. The slice's pixel resolution may or may not be a power of 2.
+		basisu::packed_uint<2> m_num_blocks_y;	// The slice's block Y dimensions. 
+
+		basisu::packed_uint<4> m_file_ofs;		// Offset from the start of the file to the start of the slice's data
+		basisu::packed_uint<4> m_file_size;		// The size of the compressed slice data in bytes
+
+		basisu::packed_uint<2> m_slice_data_crc16; // The CRC16 of the compressed slice data, for extra-paranoid use cases
+	};
+
+	// File header files
+	enum basis_header_flags
+	{
+		// Always set for ETC1S files. Not set for UASTC files.
+		cBASISHeaderFlagETC1S = 1,					 
+		
+		// Set if the texture had to be Y flipped before encoding. The actual interpretation of this (is Y up or down?) is up to the user.
+		cBASISHeaderFlagYFlipped = 2,				 
+		
+		// Set if any slices contain alpha (for ETC1S, if the odd slices contain alpha data)
+		cBASISHeaderFlagHasAlphaSlices = 4,		 
+		
+		// For ETC1S files, this will be true if the file utilizes a codebook from another .basis file. 
+		cBASISHeaderFlagUsesGlobalCodebook = 8, 
+		
+		// Set if the texture data is sRGB, otherwise it's linear. 
+		// In reality, we have no idea if the texture data is actually linear or sRGB. This is the m_perceptual parameter passed to the compressor.
+		cBASISHeaderFlagSRGB = 16,					 
+	};
+
+	// The image type field attempts to describe how to interpret the image data in a Basis file.
+	// The encoder library doesn't really do anything special or different with these texture types, this is mostly here for the benefit of the user. 
+	// We do make sure the various constraints are followed (2DArray/cubemap/videoframes/volume implies that each image has the same resolution and # of mipmap levels, etc., cubemap implies that the # of image slices is a multiple of 6)
+	enum basis_texture_type
+	{
+		cBASISTexType2D = 0,					// An arbitrary array of 2D RGB or RGBA images with optional mipmaps, array size = # images, each image may have a different resolution and # of mipmap levels
+		cBASISTexType2DArray = 1,			// An array of 2D RGB or RGBA images with optional mipmaps, array size = # images, each image has the same resolution and mipmap levels
+		cBASISTexTypeCubemapArray = 2,	// an array of cubemap levels, total # of images must be divisable by 6, in X+, X-, Y+, Y-, Z+, Z- order, with optional mipmaps
+		cBASISTexTypeVideoFrames = 3,		// An array of 2D video frames, with optional mipmaps, # frames = # images, each image has the same resolution and # of mipmap levels
+		cBASISTexTypeVolume = 4,			// A 3D texture with optional mipmaps, Z dimension = # images, each image has the same resolution and # of mipmap levels
+
+		cBASISTexTypeTotal
+	};
+
+	enum
+	{
+		cBASISMaxUSPerFrame = 0xFFFFFF
+	};
+
+	enum class basis_tex_format
+	{
+		cETC1S = 0,
+		cUASTC4x4 = 1
+	};
+
+	struct basis_file_header
+	{
+		enum
+		{
+			cBASISSigValue = ('B' << 8) | 's',
+			cBASISFirstVersion = 0x10
+		};
+
+		basisu::packed_uint<2>      m_sig;				// 2 byte file signature
+		basisu::packed_uint<2>      m_ver;				// Baseline file version
+		basisu::packed_uint<2>      m_header_size;	// Header size in bytes, sizeof(basis_file_header)
+		basisu::packed_uint<2>      m_header_crc16;	// CRC16 of the remaining header data
+
+		basisu::packed_uint<4>      m_data_size;		// The total size of all data after the header
+		basisu::packed_uint<2>      m_data_crc16;		// The CRC16 of all data after the header
+
+		basisu::packed_uint<3>      m_total_slices;	// The total # of compressed slices (1 slice per image, or 2 for alpha .basis files)
+
+		basisu::packed_uint<3>      m_total_images;	// The total # of images
+				
+		basisu::packed_uint<1>      m_tex_format;		// enum basis_tex_format
+		basisu::packed_uint<2>      m_flags;			// enum basist::header_flags
+		basisu::packed_uint<1>      m_tex_type;		// enum basist::basis_texture_type
+		basisu::packed_uint<3>      m_us_per_frame;	// Framerate of video, in microseconds per frame
+
+		basisu::packed_uint<4>      m_reserved;		// For future use
+		basisu::packed_uint<4>      m_userdata0;		// For client use
+		basisu::packed_uint<4>      m_userdata1;		// For client use
+
+		basisu::packed_uint<2>      m_total_endpoints;			// The number of endpoints in the endpoint codebook 
+		basisu::packed_uint<4>      m_endpoint_cb_file_ofs;	// The compressed endpoint codebook's file offset relative to the start of the file
+		basisu::packed_uint<3>      m_endpoint_cb_file_size;	// The compressed endpoint codebook's size in bytes
+
+		basisu::packed_uint<2>      m_total_selectors;			// The number of selectors in the endpoint codebook 
+		basisu::packed_uint<4>      m_selector_cb_file_ofs;	// The compressed selectors codebook's file offset relative to the start of the file
+		basisu::packed_uint<3>      m_selector_cb_file_size;	// The compressed selector codebook's size in bytes
+
+		basisu::packed_uint<4>      m_tables_file_ofs;			// The file offset of the compressed Huffman codelength tables, for decompressing slices
+		basisu::packed_uint<4>      m_tables_file_size;			// The file size in bytes of the compressed huffman codelength tables
+
+		basisu::packed_uint<4>      m_slice_desc_file_ofs;		// The file offset to the slice description array, usually follows the header
+		
+		basisu::packed_uint<4>      m_extended_file_ofs;		// The file offset of the "extended" header and compressed data, for future use
+		basisu::packed_uint<4>      m_extended_file_size;		// The file size in bytes of the "extended" header and compressed data, for future use
+	};
+#pragma pack (pop)
+
+} // namespace basist
+/**** ended inlining basisu_file_headers.h ****/
+
+namespace basist
+{
+	// High-level composite texture formats supported by the transcoder.
+	// Each of these texture formats directly correspond to OpenGL/D3D/Vulkan etc. texture formats.
+	// Notes:
+	// - If you specify a texture format that supports alpha, but the .basis file doesn't have alpha, the transcoder will automatically output a 
+	// fully opaque (255) alpha channel.
+	// - The PVRTC1 texture formats only support power of 2 dimension .basis files, but this may be relaxed in a future version.
+	// - The PVRTC1 transcoders are real-time encoders, so don't expect the highest quality. We may add a slower encoder with improved quality.
+	// - These enums must be kept in sync with Javascript code that calls the transcoder.
+	enum class transcoder_texture_format
+	{
+		// Compressed formats
+
+		// ETC1-2
+		cTFETC1_RGB = 0,							// Opaque only, returns RGB or alpha data if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
+		cTFETC2_RGBA = 1,							// Opaque+alpha, ETC2_EAC_A8 block followed by a ETC1 block, alpha channel will be opaque for opaque .basis files
+
+		// BC1-5, BC7 (desktop, some mobile devices)
+		cTFBC1_RGB = 2,							// Opaque only, no punchthrough alpha support yet, transcodes alpha slice if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
+		cTFBC3_RGBA = 3, 							// Opaque+alpha, BC4 followed by a BC1 block, alpha channel will be opaque for opaque .basis files
+		cTFBC4_R = 4,								// Red only, alpha slice is transcoded to output if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
+		cTFBC5_RG = 5,								// XY: Two BC4 blocks, X=R and Y=Alpha, .basis file should have alpha data (if not Y will be all 255's)
+		cTFBC7_RGBA = 6,							// RGB or RGBA, mode 5 for ETC1S, modes (1,2,3,5,6,7) for UASTC
+
+		// PVRTC1 4bpp (mobile, PowerVR devices)
+		cTFPVRTC1_4_RGB = 8,						// Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified, nearly lowest quality of any texture format.
+		cTFPVRTC1_4_RGBA = 9,					// Opaque+alpha, most useful for simple opacity maps. If .basis file doesn't have alpha cTFPVRTC1_4_RGB will be used instead. Lowest quality of any supported texture format.
+
+		// ASTC (mobile, Intel devices, hopefully all desktop GPU's one day)
+		cTFASTC_4x4_RGBA = 10,					// Opaque+alpha, ASTC 4x4, alpha channel will be opaque for opaque .basis files. Transcoder uses RGB/RGBA/L/LA modes, void extent, and up to two ([0,47] and [0,255]) endpoint precisions.
+
+		// ATC (mobile, Adreno devices, this is a niche format)
+		cTFATC_RGB = 11,							// Opaque, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. ATI ATC (GL_ATC_RGB_AMD)
+		cTFATC_RGBA = 12,							// Opaque+alpha, alpha channel will be opaque for opaque .basis files. ATI ATC (GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD) 
+
+		// FXT1 (desktop, Intel devices, this is a super obscure format)
+		cTFFXT1_RGB = 17,							// Opaque only, uses exclusively CC_MIXED blocks. Notable for having a 8x4 block size. GL_3DFX_texture_compression_FXT1 is supported on Intel integrated GPU's (such as HD 630).
+														// Punch-through alpha is relatively easy to support, but full alpha is harder. This format is only here for completeness so opaque-only is fine for now.
+														// See the BASISU_USE_ORIGINAL_3DFX_FXT1_ENCODING macro in basisu_transcoder_internal.h.
+
+		cTFPVRTC2_4_RGB = 18,					// Opaque-only, almost BC1 quality, much faster to transcode and supports arbitrary texture dimensions (unlike PVRTC1 RGB).
+		cTFPVRTC2_4_RGBA = 19,					// Opaque+alpha, slower to encode than cTFPVRTC2_4_RGB. Premultiplied alpha is highly recommended, otherwise the color channel can leak into the alpha channel on transparent blocks.
+
+		cTFETC2_EAC_R11 = 20,					// R only (ETC2 EAC R11 unsigned)
+		cTFETC2_EAC_RG11 = 21,					// RG only (ETC2 EAC RG11 unsigned), R=opaque.r, G=alpha - for tangent space normal maps
+
+		// Uncompressed (raw pixel) formats
+		cTFRGBA32 = 13,							// 32bpp RGBA image stored in raster (not block) order in memory, R is first byte, A is last byte.
+		cTFRGB565 = 14,							// 16bpp RGB image stored in raster (not block) order in memory, R at bit position 11
+		cTFBGR565 = 15,							// 16bpp RGB image stored in raster (not block) order in memory, R at bit position 0
+		cTFRGBA4444 = 16,							// 16bpp RGBA image stored in raster (not block) order in memory, R at bit position 12, A at bit position 0
+
+		cTFTotalTextureFormats = 22,
+
+		// Old enums for compatibility with code compiled against previous versions
+		cTFETC1 = cTFETC1_RGB,
+		cTFETC2 = cTFETC2_RGBA,
+		cTFBC1 = cTFBC1_RGB,
+		cTFBC3 = cTFBC3_RGBA,
+		cTFBC4 = cTFBC4_R,
+		cTFBC5 = cTFBC5_RG,
+
+		// Previously, the caller had some control over which BC7 mode the transcoder output. We've simplified this due to UASTC, which supports numerous modes.
+		cTFBC7_M6_RGB = cTFBC7_RGBA,			// Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. Highest quality of all the non-ETC1 formats.
+		cTFBC7_M5_RGBA = cTFBC7_RGBA,			// Opaque+alpha, alpha channel will be opaque for opaque .basis files
+		cTFBC7_M6_OPAQUE_ONLY = cTFBC7_RGBA,
+		cTFBC7_M5 = cTFBC7_RGBA,
+		cTFBC7_ALT = 7,
+
+		cTFASTC_4x4 = cTFASTC_4x4_RGBA,
+
+		cTFATC_RGBA_INTERPOLATED_ALPHA = cTFATC_RGBA,
+	};
+
+	// For compressed texture formats, this returns the # of bytes per block. For uncompressed, it returns the # of bytes per pixel.
+	// NOTE: Previously, this function was called basis_get_bytes_per_block(), and it always returned 16*bytes_per_pixel for uncompressed formats which was confusing.
+	uint32_t basis_get_bytes_per_block_or_pixel(transcoder_texture_format fmt);
+
+	// Returns format's name in ASCII
+	const char* basis_get_format_name(transcoder_texture_format fmt);
+
+	// Returns block format name in ASCII
+	const char* basis_get_block_format_name(block_format fmt);
+
+	// Returns true if the format supports an alpha channel.
+	bool basis_transcoder_format_has_alpha(transcoder_texture_format fmt);
+
+	// Returns the basisu::texture_format corresponding to the specified transcoder_texture_format.
+	basisu::texture_format basis_get_basisu_texture_format(transcoder_texture_format fmt);
+
+	// Returns the texture type's name in ASCII.
+	const char* basis_get_texture_type_name(basis_texture_type tex_type);
+
+	// Returns true if the transcoder texture type is an uncompressed (raw pixel) format.
+	bool basis_transcoder_format_is_uncompressed(transcoder_texture_format tex_type);
+
+	// Returns the # of bytes per pixel for uncompressed formats, or 0 for block texture formats.
+	uint32_t basis_get_uncompressed_bytes_per_pixel(transcoder_texture_format fmt);
+
+	// Returns the block width for the specified texture format, which is currently either 4 or 8 for FXT1.
+	uint32_t basis_get_block_width(transcoder_texture_format tex_type);
+
+	// Returns the block height for the specified texture format, which is currently always 4.
+	uint32_t basis_get_block_height(transcoder_texture_format tex_type);
+
+	// Returns true if the specified format was enabled at compile time.
+	bool basis_is_format_supported(transcoder_texture_format tex_type, basis_tex_format fmt = basis_tex_format::cETC1S);
+
+	// Validates that the output buffer is large enough to hold the entire transcoded texture.
+	// For uncompressed texture formats, most input parameters are in pixels, not blocks. Blocks are 4x4 pixels.
+	bool basis_validate_output_buffer_size(transcoder_texture_format target_format,
+		uint32_t output_blocks_buf_size_in_blocks_or_pixels,
+		uint32_t orig_width, uint32_t orig_height,
+		uint32_t output_row_pitch_in_blocks_or_pixels,
+		uint32_t output_rows_in_pixels,
+		uint32_t total_slice_blocks);
+
+	class basisu_transcoder;
+
+	// This struct holds all state used during transcoding. For video, it needs to persist between image transcodes (it holds the previous frame).
+	// For threading you can use one state per thread.
+	struct basisu_transcoder_state
+	{
+		struct block_preds
+		{
+			uint16_t m_endpoint_index;
+			uint8_t m_pred_bits;
+		};
+
+		basisu::vector<block_preds> m_block_endpoint_preds[2];
+
+		enum { cMaxPrevFrameLevels = 16 };
+		basisu::vector<uint32_t> m_prev_frame_indices[2][cMaxPrevFrameLevels]; // [alpha_flag][level_index] 
+
+		void clear()
+		{
+			for (uint32_t i = 0; i < 2; i++)
+			{
+				m_block_endpoint_preds[i].clear();
+
+				for (uint32_t j = 0; j < cMaxPrevFrameLevels; j++)
+					m_prev_frame_indices[i][j].clear();
+			}
+		}
+	};
+
+	// Low-level helper class that does the actual transcoding.
+	class basisu_lowlevel_etc1s_transcoder
+	{
+		friend class basisu_transcoder;
+
+	public:
+		basisu_lowlevel_etc1s_transcoder();
+
+		void set_global_codebooks(const basisu_lowlevel_etc1s_transcoder* pGlobal_codebook) { m_pGlobal_codebook = pGlobal_codebook; }
+		const basisu_lowlevel_etc1s_transcoder* get_global_codebooks() const { return m_pGlobal_codebook; }
+
+		bool decode_palettes(
+			uint32_t num_endpoints, const uint8_t* pEndpoints_data, uint32_t endpoints_data_size,
+			uint32_t num_selectors, const uint8_t* pSelectors_data, uint32_t selectors_data_size);
+
+		bool decode_tables(const uint8_t* pTable_data, uint32_t table_data_size);
+
+		bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
+			uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const bool is_video, const bool is_alpha_slice, const uint32_t level_index, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
+			basisu_transcoder_state* pState = nullptr, bool astc_transcode_alpha = false, void* pAlpha_blocks = nullptr, uint32_t output_rows_in_pixels = 0);
+
+		bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
+			uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
+			basisu_transcoder_state* pState = nullptr, bool astc_transcode_alpha = false, void* pAlpha_blocks = nullptr, uint32_t output_rows_in_pixels = 0)
+		{
+			return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt, output_block_or_pixel_stride_in_bytes, bc1_allow_threecolor_blocks,
+				header.m_tex_type == cBASISTexTypeVideoFrames, (slice_desc.m_flags & cSliceDescFlagsHasAlpha) != 0, slice_desc.m_level_index,
+				slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels, pState,
+				astc_transcode_alpha,
+				pAlpha_blocks,
+				output_rows_in_pixels);
+		}
+
+		// Container independent transcoding
+		bool transcode_image(
+			transcoder_texture_format target_format,
+			void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
+			const uint8_t* pCompressed_data, uint32_t compressed_data_length,
+			uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
+			uint32_t rgb_offset, uint32_t rgb_length, uint32_t alpha_offset, uint32_t alpha_length,
+			uint32_t decode_flags = 0,
+			bool basis_file_has_alpha_slices = false,
+			bool is_video = false,
+			uint32_t output_row_pitch_in_blocks_or_pixels = 0,
+			basisu_transcoder_state* pState = nullptr,
+			uint32_t output_rows_in_pixels = 0);
+
+		void clear()
+		{
+			m_local_endpoints.clear();
+			m_local_selectors.clear();
+			m_endpoint_pred_model.clear();
+			m_delta_endpoint_model.clear();
+			m_selector_model.clear();
+			m_selector_history_buf_rle_model.clear();
+			m_selector_history_buf_size = 0;
+		}
+
+		// Low-level methods
+		typedef basisu::vector<endpoint> endpoint_vec;
+		const endpoint_vec& get_endpoints() const { return m_local_endpoints; }
+
+		typedef basisu::vector<selector> selector_vec;
+		const selector_vec& get_selectors() const { return m_local_selectors; }
+				
+	private:
+		const basisu_lowlevel_etc1s_transcoder* m_pGlobal_codebook;
+
+		endpoint_vec m_local_endpoints;
+		selector_vec m_local_selectors;
+				
+		huffman_decoding_table m_endpoint_pred_model, m_delta_endpoint_model, m_selector_model, m_selector_history_buf_rle_model;
+
+		uint32_t m_selector_history_buf_size;
+
+		basisu_transcoder_state m_def_state;
+	};
+
+	enum basisu_decode_flags
+	{
+		// PVRTC1: decode non-pow2 ETC1S texture level to the next larger power of 2 (not implemented yet, but we're going to support it). Ignored if the slice's dimensions are already a power of 2.
+		cDecodeFlagsPVRTCDecodeToNextPow2 = 2,
+
+		// When decoding to an opaque texture format, if the basis file has alpha, decode the alpha slice instead of the color slice to the output texture format.
+		// This is primarily to allow decoding of textures with alpha to multiple ETC1 textures (one for color, another for alpha).
+		cDecodeFlagsTranscodeAlphaDataToOpaqueFormats = 4,
+
+		// Forbid usage of BC1 3 color blocks (we don't support BC1 punchthrough alpha yet).
+		// This flag is used internally when decoding to BC3.
+		cDecodeFlagsBC1ForbidThreeColorBlocks = 8,
+
+		// The output buffer contains alpha endpoint/selector indices. 
+		// Used internally when decoding formats like ASTC that require both color and alpha data to be available when transcoding to the output format.
+		cDecodeFlagsOutputHasAlphaIndices = 16,
+
+		cDecodeFlagsHighQuality = 32
+	};
+
+	class basisu_lowlevel_uastc_transcoder
+	{
+		friend class basisu_transcoder;
+
+	public:
+		basisu_lowlevel_uastc_transcoder();
+
+		bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
+			uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
+			basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);
+
+		bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
+			uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
+			basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)
+		{
+			return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt,
+				output_block_or_pixel_stride_in_bytes, bc1_allow_threecolor_blocks, (header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0, slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels,
+				pState, output_rows_in_pixels, channel0, channel1, decode_flags);
+		}
+
+		// Container independent transcoding
+		bool transcode_image(
+			transcoder_texture_format target_format,
+			void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
+			const uint8_t* pCompressed_data, uint32_t compressed_data_length,
+			uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
+			uint32_t slice_offset, uint32_t slice_length,
+			uint32_t decode_flags = 0,
+			bool has_alpha = false,
+			bool is_video = false,
+			uint32_t output_row_pitch_in_blocks_or_pixels = 0,
+			basisu_transcoder_state* pState = nullptr,
+			uint32_t output_rows_in_pixels = 0,
+			int channel0 = -1, int channel1 = -1);
+	};
+
+	struct basisu_slice_info
+	{
+		uint32_t m_orig_width;
+		uint32_t m_orig_height;
+
+		uint32_t m_width;
+		uint32_t m_height;
+
+		uint32_t m_num_blocks_x;
+		uint32_t m_num_blocks_y;
+		uint32_t m_total_blocks;
+
+		uint32_t m_compressed_size;
+
+		uint32_t m_slice_index;	// the slice index in the .basis file
+		uint32_t m_image_index;	// the source image index originally provided to the encoder
+		uint32_t m_level_index;	// the mipmap level within this image
+
+		uint32_t m_unpacked_slice_crc16;
+
+		bool m_alpha_flag;		// true if the slice has alpha data
+		bool m_iframe_flag;		// true if the slice is an I-Frame
+	};
+
+	typedef basisu::vector<basisu_slice_info> basisu_slice_info_vec;
+
+	struct basisu_image_info
+	{
+		uint32_t m_image_index;
+		uint32_t m_total_levels;
+
+		uint32_t m_orig_width;
+		uint32_t m_orig_height;
+
+		uint32_t m_width;
+		uint32_t m_height;
+
+		uint32_t m_num_blocks_x;
+		uint32_t m_num_blocks_y;
+		uint32_t m_total_blocks;
+
+		uint32_t m_first_slice_index;
+
+		bool m_alpha_flag;		// true if the image has alpha data
+		bool m_iframe_flag;		// true if the image is an I-Frame
+	};
+
+	struct basisu_image_level_info
+	{
+		uint32_t m_image_index;
+		uint32_t m_level_index;
+
+		uint32_t m_orig_width;
+		uint32_t m_orig_height;
+
+		uint32_t m_width;
+		uint32_t m_height;
+
+		uint32_t m_num_blocks_x;
+		uint32_t m_num_blocks_y;
+		uint32_t m_total_blocks;
+
+		uint32_t m_first_slice_index;
+
+		uint32_t m_rgb_file_ofs;
+		uint32_t m_rgb_file_len;
+		uint32_t m_alpha_file_ofs;
+		uint32_t m_alpha_file_len;
+
+		bool m_alpha_flag;		// true if the image has alpha data
+		bool m_iframe_flag;		// true if the image is an I-Frame
+	};
+
+	struct basisu_file_info
+	{
+		uint32_t m_version;
+		uint32_t m_total_header_size;
+
+		uint32_t m_total_selectors;
+		// will be 0 for UASTC or if the file uses global codebooks
+		uint32_t m_selector_codebook_ofs;
+		uint32_t m_selector_codebook_size;
+
+		uint32_t m_total_endpoints;
+		// will be 0 for UASTC or if the file uses global codebooks
+		uint32_t m_endpoint_codebook_ofs;
+		uint32_t m_endpoint_codebook_size;
+
+		uint32_t m_tables_ofs;
+		uint32_t m_tables_size;
+
+		uint32_t m_slices_size;
+
+		basis_texture_type m_tex_type;
+		uint32_t m_us_per_frame;
+
+		// Low-level slice information (1 slice per image for color-only basis files, 2 for alpha basis files)
+		basisu_slice_info_vec m_slice_info;
+
+		uint32_t m_total_images;	 // total # of images
+		basisu::vector<uint32_t> m_image_mipmap_levels; // the # of mipmap levels for each image
+
+		uint32_t m_userdata0;
+		uint32_t m_userdata1;
+
+		basis_tex_format m_tex_format; // ETC1S, UASTC, etc.
+
+		bool m_y_flipped;				// true if the image was Y flipped
+		bool m_etc1s;					// true if the file is ETC1S
+		bool m_has_alpha_slices;	// true if the texture has alpha slices (for ETC1S: even slices RGB, odd slices alpha)
+	};
+
+	// High-level transcoder class which accepts .basis file data and allows the caller to query information about the file and transcode image levels to various texture formats.
+	// If you're just starting out this is the class you care about.
+	class basisu_transcoder
+	{
+		basisu_transcoder(basisu_transcoder&);
+		basisu_transcoder& operator= (const basisu_transcoder&);
+
+	public:
+		basisu_transcoder();
+
+		// Validates the .basis file. This computes a crc16 over the entire file, so it's slow.
+		bool validate_file_checksums(const void* pData, uint32_t data_size, bool full_validation) const;
+
+		// Quick header validation - no crc16 checks.
+		bool validate_header(const void* pData, uint32_t data_size) const;
+
+		basis_texture_type get_texture_type(const void* pData, uint32_t data_size) const;
+		bool get_userdata(const void* pData, uint32_t data_size, uint32_t& userdata0, uint32_t& userdata1) const;
+
+		// Returns the total number of images in the basis file (always 1 or more).
+		// Note that the number of mipmap levels for each image may differ, and that images may have different resolutions.
+		uint32_t get_total_images(const void* pData, uint32_t data_size) const;
+
+		basis_tex_format get_tex_format(const void* pData, uint32_t data_size) const;
+
+		// Returns the number of mipmap levels in an image.
+		uint32_t get_total_image_levels(const void* pData, uint32_t data_size, uint32_t image_index) const;
+
+		// Returns basic information about an image. Note that orig_width/orig_height may not be a multiple of 4.
+		bool get_image_level_desc(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, uint32_t& orig_width, uint32_t& orig_height, uint32_t& total_blocks) const;
+
+		// Returns information about the specified image.
+		bool get_image_info(const void* pData, uint32_t data_size, basisu_image_info& image_info, uint32_t image_index) const;
+
+		// Returns information about the specified image's mipmap level.
+		bool get_image_level_info(const void* pData, uint32_t data_size, basisu_image_level_info& level_info, uint32_t image_index, uint32_t level_index) const;
+
+		// Get a description of the basis file and low-level information about each slice.
+		bool get_file_info(const void* pData, uint32_t data_size, basisu_file_info& file_info) const;
+
+		// start_transcoding() must be called before calling transcode_slice() or transcode_image_level().
+		// For ETC1S files, this call decompresses the selector/endpoint codebooks, so ideally you would only call this once per .basis file (not each image/mipmap level).
+		bool start_transcoding(const void* pData, uint32_t data_size);
+
+		bool stop_transcoding();
+
+		// Returns true if start_transcoding() has been called.
+		bool get_ready_to_transcode() const { return m_ready_to_transcode; }
+
+		// transcode_image_level() decodes a single mipmap level from the .basis file to any of the supported output texture formats.
+		// It'll first find the slice(s) to transcode, then call transcode_slice() one or two times to decode both the color and alpha texture data (or RG texture data from two slices for BC5).
+		// If the .basis file doesn't have alpha slices, the output alpha blocks will be set to fully opaque (all 255's).
+		// Currently, to decode to PVRTC1 the basis texture's dimensions in pixels must be a power of 2, due to PVRTC1 format requirements. 
+		// output_blocks_buf_size_in_blocks_or_pixels should be at least the image level's total_blocks (num_blocks_x * num_blocks_y), or the total number of output pixels if fmt==cTFRGBA32.
+		// output_row_pitch_in_blocks_or_pixels: Number of blocks or pixels per row. If 0, the transcoder uses the slice's num_blocks_x or orig_width (NOT num_blocks_x * 4). Ignored for PVRTC1 (due to texture swizzling).
+		// output_rows_in_pixels: Ignored unless fmt is uncompressed (cRGBA32, etc.). The total number of output rows in the output buffer. If 0, the transcoder assumes the slice's orig_height (NOT num_blocks_y * 4).
+		// Notes: 
+		// - basisu_transcoder_init() must have been called first to initialize the transcoder lookup tables before calling this function.
+		// - This method assumes the output texture buffer is readable. In some cases to handle alpha, the transcoder will write temporary data to the output texture in
+		// a first pass, which will be read in a second pass.
+		bool transcode_image_level(
+			const void* pData, uint32_t data_size,
+			uint32_t image_index, uint32_t level_index,
+			void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
+			transcoder_texture_format fmt,
+			uint32_t decode_flags = 0, uint32_t output_row_pitch_in_blocks_or_pixels = 0, basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0) const;
+
+		// Finds the basis slice corresponding to the specified image/level/alpha params, or -1 if the slice can't be found.
+		int find_slice(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, bool alpha_data) const;
+
+		// transcode_slice() decodes a single slice from the .basis file. It's a low-level API - most likely you want to use transcode_image_level().
+		// This is a low-level API, and will be needed to be called multiple times to decode some texture formats (like BC3, BC5, or ETC2).
+		// output_blocks_buf_size_in_blocks_or_pixels is just used for verification to make sure the output buffer is large enough.
+		// output_blocks_buf_size_in_blocks_or_pixels should be at least the image level's total_blocks (num_blocks_x * num_blocks_y), or the total number of output pixels if fmt==cTFRGBA32.
+		// output_block_stride_in_bytes: Number of bytes between each output block.
+		// output_row_pitch_in_blocks_or_pixels: Number of blocks or pixels per row. If 0, the transcoder uses the slice's num_blocks_x or orig_width (NOT num_blocks_x * 4). Ignored for PVRTC1 (due to texture swizzling).
+		// output_rows_in_pixels: Ignored unless fmt is cRGBA32. The total number of output rows in the output buffer. If 0, the transcoder assumes the slice's orig_height (NOT num_blocks_y * 4).
+		// Notes:
+		// - basisu_transcoder_init() must have been called first to initialize the transcoder lookup tables before calling this function.
+		bool transcode_slice(const void* pData, uint32_t data_size, uint32_t slice_index,
+			void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
+			block_format fmt, uint32_t output_block_stride_in_bytes, uint32_t decode_flags = 0, uint32_t output_row_pitch_in_blocks_or_pixels = 0, basisu_transcoder_state* pState = nullptr, void* pAlpha_blocks = nullptr,
+			uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1) const;
+
+		static void write_opaque_alpha_blocks(
+			uint32_t num_blocks_x, uint32_t num_blocks_y,
+			void* pOutput_blocks, block_format fmt,
+			uint32_t block_stride_in_bytes, uint32_t output_row_pitch_in_blocks_or_pixels);
+
+		void set_global_codebooks(const basisu_lowlevel_etc1s_transcoder* pGlobal_codebook) { m_lowlevel_etc1s_decoder.set_global_codebooks(pGlobal_codebook); }
+		const basisu_lowlevel_etc1s_transcoder* get_global_codebooks() const { return m_lowlevel_etc1s_decoder.get_global_codebooks(); }
+
+		const basisu_lowlevel_etc1s_transcoder& get_lowlevel_etc1s_decoder() const { return m_lowlevel_etc1s_decoder; }
+		basisu_lowlevel_etc1s_transcoder& get_lowlevel_etc1s_decoder() { return m_lowlevel_etc1s_decoder; }
+
+		const basisu_lowlevel_uastc_transcoder& get_lowlevel_uastc_decoder() const { return m_lowlevel_uastc_decoder; }
+		basisu_lowlevel_uastc_transcoder& get_lowlevel_uastc_decoder() { return m_lowlevel_uastc_decoder; }
+
+	private:
+		mutable basisu_lowlevel_etc1s_transcoder m_lowlevel_etc1s_decoder;
+		mutable basisu_lowlevel_uastc_transcoder m_lowlevel_uastc_decoder;
+
+		bool m_ready_to_transcode;
+
+		int find_first_slice_index(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index) const;
+
+		bool validate_header_quick(const void* pData, uint32_t data_size) const;
+	};
+
+	// basisu_transcoder_init() MUST be called before a .basis file can be transcoded.
+	void basisu_transcoder_init();
+		
+	enum debug_flags_t
+	{
+		cDebugFlagVisCRs = 1,
+		cDebugFlagVisBC1Sels = 2,
+		cDebugFlagVisBC1Endpoints = 4
+	};
+	uint32_t get_debug_flags();
+	void set_debug_flags(uint32_t f);
+
+	// ------------------------------------------------------------------------------------------------------ 
+	// Optional .KTX2 file format support
+	// KTX2 reading optionally requires miniz or Zstd decompressors for supercompressed UASTC files.
+	// ------------------------------------------------------------------------------------------------------ 
+#if BASISD_SUPPORT_KTX2
+#pragma pack(push)
+#pragma pack(1)
+	struct ktx2_header
+	{
+		uint8_t m_identifier[12];
+		basisu::packed_uint<4> m_vk_format;
+		basisu::packed_uint<4> m_type_size;
+		basisu::packed_uint<4> m_pixel_width;
+		basisu::packed_uint<4> m_pixel_height;
+		basisu::packed_uint<4> m_pixel_depth;
+		basisu::packed_uint<4> m_layer_count;
+		basisu::packed_uint<4> m_face_count;
+		basisu::packed_uint<4> m_level_count;
+		basisu::packed_uint<4> m_supercompression_scheme;
+		basisu::packed_uint<4> m_dfd_byte_offset;
+		basisu::packed_uint<4> m_dfd_byte_length;
+		basisu::packed_uint<4> m_kvd_byte_offset;
+		basisu::packed_uint<4> m_kvd_byte_length;
+		basisu::packed_uint<8> m_sgd_byte_offset;
+		basisu::packed_uint<8> m_sgd_byte_length;
+	};
+
+	struct ktx2_level_index
+	{
+		basisu::packed_uint<8> m_byte_offset;
+		basisu::packed_uint<8> m_byte_length;
+		basisu::packed_uint<8> m_uncompressed_byte_length;
+	};
+
+	struct ktx2_etc1s_global_data_header
+	{
+		basisu::packed_uint<2> m_endpoint_count;
+		basisu::packed_uint<2> m_selector_count;
+		basisu::packed_uint<4> m_endpoints_byte_length;
+		basisu::packed_uint<4> m_selectors_byte_length;
+		basisu::packed_uint<4> m_tables_byte_length;
+		basisu::packed_uint<4> m_extended_byte_length;
+	};
+
+	struct ktx2_etc1s_image_desc
+	{
+		basisu::packed_uint<4> m_image_flags;
+		basisu::packed_uint<4> m_rgb_slice_byte_offset;
+		basisu::packed_uint<4> m_rgb_slice_byte_length;
+		basisu::packed_uint<4> m_alpha_slice_byte_offset;
+		basisu::packed_uint<4> m_alpha_slice_byte_length;
+	};
+
+	struct ktx2_animdata
+	{
+		basisu::packed_uint<4> m_duration;
+		basisu::packed_uint<4> m_timescale;
+		basisu::packed_uint<4> m_loopcount;
+	};
+#pragma pack(pop)
+
+	const uint32_t KTX2_VK_FORMAT_UNDEFINED = 0;
+	const uint32_t KTX2_KDF_DF_MODEL_UASTC = 166;
+	const uint32_t KTX2_KDF_DF_MODEL_ETC1S = 163;
+	const uint32_t KTX2_IMAGE_IS_P_FRAME = 2;
+	const uint32_t KTX2_UASTC_BLOCK_SIZE = 16;
+	const uint32_t KTX2_MAX_SUPPORTED_LEVEL_COUNT = 16; // this is an implementation specific constraint and can be increased
+
+	// The KTX2 transfer functions supported by KTX2
+	const uint32_t KTX2_KHR_DF_TRANSFER_LINEAR = 1;
+	const uint32_t KTX2_KHR_DF_TRANSFER_SRGB = 2;
+
+	enum ktx2_supercompression
+	{
+		KTX2_SS_NONE = 0,
+		KTX2_SS_BASISLZ = 1,
+		KTX2_SS_ZSTANDARD = 2
+	};
+
+	extern const uint8_t g_ktx2_file_identifier[12];
+
+	enum ktx2_df_channel_id
+	{
+		KTX2_DF_CHANNEL_ETC1S_RGB = 0U,
+		KTX2_DF_CHANNEL_ETC1S_RRR = 3U,
+		KTX2_DF_CHANNEL_ETC1S_GGG = 4U,
+		KTX2_DF_CHANNEL_ETC1S_AAA = 15U,
+
+		KTX2_DF_CHANNEL_UASTC_DATA = 0U,
+		KTX2_DF_CHANNEL_UASTC_RGB = 0U,
+		KTX2_DF_CHANNEL_UASTC_RGBA = 3U,
+		KTX2_DF_CHANNEL_UASTC_RRR = 4U,
+		KTX2_DF_CHANNEL_UASTC_RRRG = 5U,
+		KTX2_DF_CHANNEL_UASTC_RG = 6U,
+	};
+
+	inline const char* ktx2_get_etc1s_df_channel_id_str(ktx2_df_channel_id id)
+	{
+		switch (id)
+		{
+		case KTX2_DF_CHANNEL_ETC1S_RGB: return "RGB";
+		case KTX2_DF_CHANNEL_ETC1S_RRR: return "RRR";
+		case KTX2_DF_CHANNEL_ETC1S_GGG: return "GGG";
+		case KTX2_DF_CHANNEL_ETC1S_AAA: return "AAA";
+		default: break;
+		}
+		return "?";
+	}
+
+	inline const char* ktx2_get_uastc_df_channel_id_str(ktx2_df_channel_id id)
+	{
+		switch (id)
+		{
+		case KTX2_DF_CHANNEL_UASTC_RGB: return "RGB";
+		case KTX2_DF_CHANNEL_UASTC_RGBA: return "RGBA";
+		case KTX2_DF_CHANNEL_UASTC_RRR: return "RRR";
+		case KTX2_DF_CHANNEL_UASTC_RRRG: return "RRRG";
+		case KTX2_DF_CHANNEL_UASTC_RG: return "RG";
+		default: break;
+		}
+		return "?";
+	}
+
+	enum ktx2_df_color_primaries
+	{
+		KTX2_DF_PRIMARIES_UNSPECIFIED = 0,
+		KTX2_DF_PRIMARIES_BT709 = 1,
+		KTX2_DF_PRIMARIES_SRGB = 1,
+		KTX2_DF_PRIMARIES_BT601_EBU = 2,
+		KTX2_DF_PRIMARIES_BT601_SMPTE = 3,
+		KTX2_DF_PRIMARIES_BT2020 = 4,
+		KTX2_DF_PRIMARIES_CIEXYZ = 5,
+		KTX2_DF_PRIMARIES_ACES = 6,
+		KTX2_DF_PRIMARIES_ACESCC = 7,
+		KTX2_DF_PRIMARIES_NTSC1953 = 8,
+		KTX2_DF_PRIMARIES_PAL525 = 9,
+		KTX2_DF_PRIMARIES_DISPLAYP3 = 10,
+		KTX2_DF_PRIMARIES_ADOBERGB = 11
+	};
+
+	inline const char* ktx2_get_df_color_primaries_str(ktx2_df_color_primaries p)
+	{
+		switch (p)
+		{
+		case KTX2_DF_PRIMARIES_UNSPECIFIED: return "UNSPECIFIED";
+		case KTX2_DF_PRIMARIES_BT709: return "BT709";
+		case KTX2_DF_PRIMARIES_BT601_EBU: return "EBU"; 
+		case KTX2_DF_PRIMARIES_BT601_SMPTE: return "SMPTE";
+		case KTX2_DF_PRIMARIES_BT2020: return "BT2020";
+		case KTX2_DF_PRIMARIES_CIEXYZ: return "CIEXYZ";
+		case KTX2_DF_PRIMARIES_ACES: return "ACES";
+		case KTX2_DF_PRIMARIES_ACESCC: return "ACESCC"; 
+		case KTX2_DF_PRIMARIES_NTSC1953: return "NTSC1953";
+		case KTX2_DF_PRIMARIES_PAL525: return "PAL525";
+		case KTX2_DF_PRIMARIES_DISPLAYP3: return "DISPLAYP3";
+		case KTX2_DF_PRIMARIES_ADOBERGB: return "ADOBERGB";
+		default: break;
+		}
+		return "?";
+	}	
+
+	// Information about a single 2D texture "image" in a KTX2 file.
+	struct ktx2_image_level_info
+	{
+		// The mipmap level index (0=largest), texture array layer index, and cubemap face index of the image.
+		uint32_t m_level_index;
+		uint32_t m_layer_index;
+		uint32_t m_face_index;
+
+		// The image's actual (or the original source image's) width/height in pixels, which may not be divisible by 4 pixels.
+		uint32_t m_orig_width;
+		uint32_t m_orig_height;
+
+		// The image's physical width/height, which will always be divisible by 4 pixels.
+		uint32_t m_width;
+		uint32_t m_height;
+
+		// The texture's dimensions in 4x4 texel blocks.
+		uint32_t m_num_blocks_x;
+		uint32_t m_num_blocks_y;
+
+		// The total number of blocks
+		uint32_t m_total_blocks;
+
+		// true if the image has alpha data
+		bool m_alpha_flag;
+
+		// true if the image is an I-Frame. Currently, for ETC1S textures, the first frame will always be an I-Frame, and subsequent frames will always be P-Frames.
+		bool m_iframe_flag;
+	};
+		
+	// Thread-specific ETC1S/supercompressed UASTC transcoder state. (If you're not doing multithreading transcoding you can ignore this.)
+	struct ktx2_transcoder_state
+	{
+		basist::basisu_transcoder_state m_transcoder_state;
+		basisu::uint8_vec m_level_uncomp_data;
+		int m_uncomp_data_level_index;
+
+		void clear()
+		{
+			m_transcoder_state.clear();
+			m_level_uncomp_data.clear();
+			m_uncomp_data_level_index = -1;
+		}
+	};
+
+	// This class is quite similar to basisu_transcoder. It treats KTX2 files as a simple container for ETC1S/UASTC texture data.
+	// It does not support 1D or 3D textures.
+	// It only supports 2D and cubemap textures, with or without mipmaps, texture arrays of 2D/cubemap textures, and texture video files. 
+	// It only supports raw non-supercompressed UASTC, ETC1S, UASTC+Zstd, or UASTC+zlib compressed files.
+	// DFD (Data Format Descriptor) parsing is purposely as simple as possible. 
+	// If you need to know how to interpret the texture channels you'll need to parse the DFD yourself after calling get_dfd().
+	class ktx2_transcoder
+	{
+	public:
+		ktx2_transcoder();
+
+		// Frees all allocations, resets object.
+		void clear();
+
+		// init() parses the KTX2 header, level index array, DFD, and key values, but nothing else.
+		// Importantly, it does not parse or decompress the ETC1S global supercompressed data, so some things (like which frames are I/P-Frames) won't be available until start_transcoding() is called.
+		// This method holds a pointer to the file data until clear() is called.
+		bool init(const void* pData, uint32_t data_size);
+
+		// Returns the data/size passed to init().
+		const uint8_t* get_data() const { return m_pData; }
+		uint32_t get_data_size() const { return m_data_size; }
+
+		// Returns the KTX2 header. Valid after init().
+		const ktx2_header& get_header() const { return m_header; }
+
+		// Returns the KTX2 level index array. There will be one entry for each mipmap level. Valid after init().
+		const basisu::vector<ktx2_level_index>& get_level_index() const { return m_levels; }
+
+		// Returns the texture's width in texels. Always non-zero, might not be divisible by 4. Valid after init().
+		uint32_t get_width() const { return m_header.m_pixel_width; }
+
+		// Returns the texture's height in texels. Always non-zero, might not be divisible by 4. Valid after init().
+		uint32_t get_height() const { return m_header.m_pixel_height; }
+
+		// Returns the texture's number of mipmap levels. Always returns 1 or higher. Valid after init().
+		uint32_t get_levels() const { return m_header.m_level_count; }
+
+		// Returns the number of faces. Returns 1 for 2D textures and or 6 for cubemaps. Valid after init().
+		uint32_t get_faces() const { return m_header.m_face_count; }
+
+		// Returns 0 or the number of layers in the texture array or texture video. Valid after init().
+		uint32_t get_layers() const { return m_header.m_layer_count; }
+
+		// Returns cETC1S or cUASTC4x4. Valid after init().
+		basist::basis_tex_format get_format() const { return m_format; } 
+
+		bool is_etc1s() const { return get_format() == basist::basis_tex_format::cETC1S; }
+
+		bool is_uastc() const { return get_format() == basist::basis_tex_format::cUASTC4x4; }
+
+		// Returns true if the ETC1S file has two planes (typically RGBA, or RRRG), or true if the UASTC file has alpha data. Valid after init().
+		uint32_t get_has_alpha() const { return m_has_alpha; }
+
+		// Returns the entire Data Format Descriptor (DFD) from the KTX2 file. Valid after init().
+		// See https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#_the_khronos_data_format_descriptor_overview
+		const basisu::uint8_vec& get_dfd() const { return m_dfd; }
+
+		// Some basic DFD accessors. Valid after init().
+		uint32_t get_dfd_color_model() const { return m_dfd_color_model; }
+
+		// Returns the DFD color primary.
+		// We do not validate the color primaries, so the returned value may not be in the ktx2_df_color_primaries enum.
+		ktx2_df_color_primaries get_dfd_color_primaries() const { return m_dfd_color_prims; }
+		
+		// Returns KTX2_KHR_DF_TRANSFER_LINEAR or KTX2_KHR_DF_TRANSFER_SRGB.
+		uint32_t get_dfd_transfer_func() const { return m_dfd_transfer_func; }
+
+		uint32_t get_dfd_flags() const { return m_dfd_flags; }
+
+		// Returns 1 (ETC1S/UASTC) or 2 (ETC1S with an internal alpha channel).
+		uint32_t get_dfd_total_samples() const { return m_dfd_samples;	}
+		
+		// Returns the channel mapping for each DFD "sample". UASTC always has 1 sample, ETC1S can have one or two. 
+		// Note the returned value SHOULD be one of the ktx2_df_channel_id enums, but we don't validate that. 
+		// It's up to the caller to decide what to do if the value isn't in the enum.
+		ktx2_df_channel_id get_dfd_channel_id0() const { return m_dfd_chan0; }
+		ktx2_df_channel_id get_dfd_channel_id1() const { return m_dfd_chan1; }
+
+		// Key value field data.
+		struct key_value
+		{
+			// The key field is UTF8 and always zero terminated.
+			basisu::uint8_vec m_key;
+
+			// The value may be empty. It consists of raw bytes which may or may not be zero terminated.
+			basisu::uint8_vec m_value;
+
+			bool operator< (const key_value& rhs) const { return strcmp((const char*)m_key.data(), (const char *)rhs.m_key.data()) < 0; }
+		};
+		typedef basisu::vector<key_value> key_value_vec;
+
+		// Returns the array of key-value entries. This may be empty. Valid after init().
+		// The order of key values fields in this array exactly matches the order they were stored in the file. The keys are supposed to be sorted by their Unicode code points.
+		const key_value_vec& get_key_values() const { return m_key_values; }
+
+		const basisu::uint8_vec *find_key(const std::string& key_name) const;
+
+		// Low-level ETC1S specific accessors
+
+		// Returns the ETC1S global supercompression data header, which is only valid after start_transcoding() is called.
+		const ktx2_etc1s_global_data_header& get_etc1s_header() const { return m_etc1s_header; }
+
+		// Returns the array of ETC1S image descriptors, which is only valid after get_etc1s_image_descs() is called.
+		const basisu::vector<ktx2_etc1s_image_desc>& get_etc1s_image_descs() const { return m_etc1s_image_descs; }
+
+		// Must have called startTranscoding() first
+		uint32_t get_etc1s_image_descs_image_flags(uint32_t level_index, uint32_t layer_index, uint32_t face_index) const;
+
+		// is_video() is only valid after start_transcoding() is called.
+		// For ETC1S data, if this returns true you must currently transcode the file from first to last frame, in order, without skipping any frames.
+		bool is_video() const { return m_is_video; }
+				
+		// start_transcoding() MUST be called before calling transcode_image().
+		// This method decompresses the ETC1S global endpoint/selector codebooks, which is not free, so try to avoid calling it excessively.
+		bool start_transcoding();
+								
+		// get_image_level_info() be called after init(), but the m_iframe_flag's won't be valid until start_transcoding() is called.
+		// You can call this method before calling transcode_image_level() to retrieve basic information about the mipmap level's dimensions, etc.
+		bool get_image_level_info(ktx2_image_level_info& level_info, uint32_t level_index, uint32_t layer_index, uint32_t face_index) const;
+
+		// transcode_image_level() transcodes a single 2D texture or cubemap face from the KTX2 file.
+		// Internally it uses the same low-level transcode API's as basisu_transcoder::transcode_image_level().
+		// If the file is UASTC and is supercompressed with Zstandard, and the file is a texture array or cubemap, it's highly recommended that each mipmap level is 
+		// completely transcoded before switching to another level. Every time the mipmap level is changed all supercompressed level data must be decompressed using Zstandard as a single unit.
+		// Currently ETC1S videos must always be transcoded from first to last frame (or KTX2 "layer"), in order, with no skipping of frames.
+		// By default this method is not thread safe unless you specify a pointer to a user allocated thread-specific transcoder_state struct.
+		bool transcode_image_level(
+			uint32_t level_index, uint32_t layer_index, uint32_t face_index,
+			void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
+			basist::transcoder_texture_format fmt,
+			uint32_t decode_flags = 0, uint32_t output_row_pitch_in_blocks_or_pixels = 0, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1,
+			ktx2_transcoder_state *pState = nullptr);
+				
+	private:
+		const uint8_t* m_pData;
+		uint32_t m_data_size;
+
+		ktx2_header m_header;
+		basisu::vector<ktx2_level_index> m_levels;
+		basisu::uint8_vec m_dfd;
+		key_value_vec m_key_values;
+		
+		ktx2_etc1s_global_data_header m_etc1s_header;
+		basisu::vector<ktx2_etc1s_image_desc> m_etc1s_image_descs;
+
+		basist::basis_tex_format m_format;
+					
+		uint32_t m_dfd_color_model;
+		ktx2_df_color_primaries m_dfd_color_prims;
+		uint32_t m_dfd_transfer_func;
+		uint32_t m_dfd_flags;
+		uint32_t m_dfd_samples;
+		ktx2_df_channel_id m_dfd_chan0, m_dfd_chan1;
+								
+		basist::basisu_lowlevel_etc1s_transcoder m_etc1s_transcoder;
+		basist::basisu_lowlevel_uastc_transcoder m_uastc_transcoder;
+				
+		ktx2_transcoder_state m_def_transcoder_state;
+
+		bool m_has_alpha;
+		bool m_is_video;
+
+		bool decompress_level_data(uint32_t level_index, basisu::uint8_vec& uncomp_data);
+		bool decompress_etc1s_global_data();
+		bool read_key_values();
+	};
+
+#endif // BASISD_SUPPORT_KTX2
+
+	// Returns true if the transcoder was compiled with KTX2 support.
+	bool basisu_transcoder_supports_ktx2();
+
+	// Returns true if the transcoder was compiled with Zstandard support.
+	bool basisu_transcoder_supports_ktx2_zstd();
+
+} // namespace basisu
+
+/**** ended inlining basisu_transcoder.h ****/
+#include <limits.h>
+/**** start inlining basisu_containers_impl.h ****/
+// basisu_containers_impl.h
+// Do not include directly
+
+#ifdef _MSC_VER
+#pragma warning (disable:4127) // warning C4127: conditional expression is constant
+#endif
+
+namespace basisu
+{
+   bool elemental_vector::increase_capacity(uint32_t min_new_capacity, bool grow_hint, uint32_t element_size, object_mover pMover, bool nofail)
+   {
+      assert(m_size <= m_capacity);
+
+      if (sizeof(void *) == sizeof(uint64_t))
+         assert(min_new_capacity < (0x400000000ULL / element_size));
+      else
+         assert(min_new_capacity < (0x7FFF0000U / element_size));
+
+      if (m_capacity >= min_new_capacity)
+         return true;
+
+      size_t new_capacity = min_new_capacity;
+      if ((grow_hint) && (!helpers::is_power_of_2((uint64_t)new_capacity)))
+      {
+         new_capacity = (size_t)helpers::next_pow2((uint64_t)new_capacity);
+
+         assert(new_capacity && (new_capacity > m_capacity));
+
+         if (new_capacity < min_new_capacity)
+         {
+            if (nofail)
+               return false;
+            fprintf(stderr, "vector too large\n");
+            abort();
+         }
+      }
+            
+      const size_t desired_size = element_size * new_capacity;
+      size_t actual_size = 0;
+      if (!pMover)
+      {
+         void* new_p = realloc(m_p, desired_size);
+         if (!new_p)
+         {
+            if (nofail)
+               return false;
+
+            char buf[256];
+#ifdef _MSC_VER
+            sprintf_s(buf, sizeof(buf), "vector: realloc() failed allocating %u bytes", (uint32_t)desired_size);
+#else
+            sprintf(buf, "vector: realloc() failed allocating %u bytes", (uint32_t)desired_size);
+#endif
+            fprintf(stderr, "%s", buf);
+            abort();
+         }
+
+#if BASISU_VECTOR_DETERMINISTIC
+         actual_size = desired_size;
+#elif defined(_MSC_VER)
+         actual_size = _msize(new_p);
+#elif HAS_MALLOC_USABLE_SIZE
+         actual_size = malloc_usable_size(new_p);
+#else
+         actual_size = desired_size;
+#endif
+         m_p = new_p;
+      }
+      else
+      {
+         void* new_p = malloc(desired_size);
+         if (!new_p)
+         {
+            if (nofail)
+               return false;
+
+            char buf[256];
+#ifdef _MSC_VER
+            sprintf_s(buf, sizeof(buf), "vector: malloc() failed allocating %u bytes", (uint32_t)desired_size);
+#else
+            sprintf(buf, "vector: malloc() failed allocating %u bytes", (uint32_t)desired_size);
+#endif
+            fprintf(stderr, "%s", buf);
+            abort();
+         }
+
+#if BASISU_VECTOR_DETERMINISTIC
+         actual_size = desired_size;
+#elif defined(_MSC_VER)
+         actual_size = _msize(new_p);
+#elif HAS_MALLOC_USABLE_SIZE
+         actual_size = malloc_usable_size(new_p);
+#else
+         actual_size = desired_size;
+#endif
+
+         (*pMover)(new_p, m_p, m_size);
+
+         if (m_p)
+            free(m_p);
+         
+         m_p = new_p;
+      }
+
+      if (actual_size > desired_size)
+         m_capacity = static_cast<uint32_t>(actual_size / element_size);
+      else
+         m_capacity = static_cast<uint32_t>(new_capacity);
+
+      return true;
+   }
+
+#if BASISU_HASHMAP_TEST
+
+#define HASHMAP_TEST_VERIFY(c) do { if (!(c)) handle_hashmap_test_verify_failure(__LINE__); } while(0)
+
+   static void handle_hashmap_test_verify_failure(int line)
+   {
+      fprintf(stderr, "HASHMAP_TEST_VERIFY() faild on line %i\n", line);
+      abort();
+   }
+
+   class counted_obj
+   {
+   public:
+      counted_obj(uint32_t v = 0) :
+         m_val(v)
+      {
+         m_count++;
+      }
+
+      counted_obj(const counted_obj& obj) :
+         m_val(obj.m_val)
+      {
+         m_count++;
+      }
+
+      ~counted_obj()
+      {
+         assert(m_count > 0);
+         m_count--;
+      }
+
+      static uint32_t m_count;
+
+      uint32_t m_val;
+
+      operator size_t() const { return m_val; }
+
+      bool operator== (const counted_obj& rhs) const { return m_val == rhs.m_val; }
+      bool operator== (const uint32_t rhs) const { return m_val == rhs; }
+
+   };
+
+   uint32_t counted_obj::m_count;
+
+   static uint32_t urand32()
+   {
+      uint32_t a = rand();
+      uint32_t b = rand() << 15;
+      uint32_t c = rand() << (32 - 15);
+      return a ^ b ^ c;
+   }
+
+   static int irand32(int l, int h)
+   {
+      assert(l < h);
+      if (l >= h)
+         return l;
+
+      uint32_t range = static_cast<uint32_t>(h - l);
+
+      uint32_t rnd = urand32();
+
+      uint32_t rnd_range = static_cast<uint32_t>((((uint64_t)range) * ((uint64_t)rnd)) >> 32U);
+
+      int result = l + rnd_range;
+      assert((result >= l) && (result < h));
+      return result;
+   }
+
+   void hash_map_test()
+   {
+      {
+         basisu::hash_map<uint64_t, uint64_t> k;
+         basisu::hash_map<uint64_t, uint64_t> l;
+         std::swap(k, l);
+
+         k.begin();
+         k.end();
+         k.clear();
+         k.empty();
+         k.erase(0);
+         k.insert(0, 1);
+         k.find(0);
+         k.get_equals();
+         k.get_hasher();
+         k.get_table_size();
+         k.reset();
+         k.reserve(1);
+         k = l;
+         k.set_equals(l.get_equals());
+         k.set_hasher(l.get_hasher());
+         k.get_table_size();
+      }
+
+      uint32_t seed = 0;
+      for (; ; )
+      {
+         seed++;
+
+         typedef basisu::hash_map<counted_obj, counted_obj> my_hash_map;
+         my_hash_map m;
+
+         const uint32_t n = irand32(0, 100000);
+
+         printf("%u\n", n);
+
+         srand(seed); // r1.seed(seed);
+
+         basisu::vector<int> q;
+
+         uint32_t count = 0;
+         for (uint32_t i = 0; i < n; i++)
+         {
+            uint32_t v = urand32() & 0x7FFFFFFF;
+            my_hash_map::insert_result res = m.insert(counted_obj(v), counted_obj(v ^ 0xdeadbeef));
+            if (res.second)
+            {
+               count++;
+               q.push_back(v);
+            }
+         }
+
+         HASHMAP_TEST_VERIFY(m.size() == count);
+
+         srand(seed);
+
+         my_hash_map cm(m);
+         m.clear();
+         m = cm;
+         cm.reset();
+
+         for (uint32_t i = 0; i < n; i++)
+         {
+            uint32_t v = urand32() & 0x7FFFFFFF;
+            my_hash_map::const_iterator it = m.find(counted_obj(v));
+            HASHMAP_TEST_VERIFY(it != m.end());
+            HASHMAP_TEST_VERIFY(it->first == v);
+            HASHMAP_TEST_VERIFY(it->second == (v ^ 0xdeadbeef));
+         }
+
+         for (uint32_t t = 0; t < 2; t++)
+         {
+            const uint32_t nd = irand32(1, q.size() + 1);
+            for (uint32_t i = 0; i < nd; i++)
+            {
+               uint32_t p = irand32(0, q.size());
+
+               int k = q[p];
+               if (k >= 0)
+               {
+                  q[p] = -k - 1;
+
+                  bool s = m.erase(counted_obj(k));
+                  HASHMAP_TEST_VERIFY(s);
+               }
+            }
+
+            typedef basisu::hash_map<uint32_t, empty_type> uint_hash_set;
+            uint_hash_set s;
+
+            for (uint32_t i = 0; i < q.size(); i++)
+            {
+               int v = q[i];
+
+               if (v >= 0)
+               {
+                  my_hash_map::const_iterator it = m.find(counted_obj(v));
+                  HASHMAP_TEST_VERIFY(it != m.end());
+                  HASHMAP_TEST_VERIFY(it->first == (uint32_t)v);
+                  HASHMAP_TEST_VERIFY(it->second == ((uint32_t)v ^ 0xdeadbeef));
+
+                  s.insert(v);
+               }
+               else
+               {
+                  my_hash_map::const_iterator it = m.find(counted_obj(-v - 1));
+                  HASHMAP_TEST_VERIFY(it == m.end());
+               }
+            }
+
+            uint32_t found_count = 0;
+            for (my_hash_map::const_iterator it = m.begin(); it != m.end(); ++it)
+            {
+               HASHMAP_TEST_VERIFY(it->second == ((uint32_t)it->first ^ 0xdeadbeef));
+
+               uint_hash_set::const_iterator fit(s.find((uint32_t)it->first));
+               HASHMAP_TEST_VERIFY(fit != s.end());
+
+               HASHMAP_TEST_VERIFY(fit->first == it->first);
+
+               found_count++;
+            }
+
+            HASHMAP_TEST_VERIFY(found_count == s.size());
+         }
+
+         HASHMAP_TEST_VERIFY(counted_obj::m_count == m.size() * 2);
+      }
+   }
+
+#endif // BASISU_HASHMAP_TEST
+
+} // namespace basisu
+/**** ended inlining basisu_containers_impl.h ****/
+
+#ifndef BASISD_IS_BIG_ENDIAN
 // TODO: This doesn't work on OSX. How can this be so difficult?
 //#if defined(__BIG_ENDIAN__) || defined(_BIG_ENDIAN) || defined(BIG_ENDIAN)
-//	#define IS_BIG_ENDIAN (1)
+//	#define BASISD_IS_BIG_ENDIAN (1)
 //#else
-	#define IS_BIG_ENDIAN (0)
+	#define BASISD_IS_BIG_ENDIAN (0)
 //#endif
 #endif
 
-#ifndef USE_UNALIGNED_WORD_READS
-#define USE_UNALIGNED_WORD_READS (1)
+#ifndef BASISD_USE_UNALIGNED_WORD_READS
+	#ifdef __EMSCRIPTEN__
+		// Can't use unaligned loads/stores with WebAssembly.
+		#define BASISD_USE_UNALIGNED_WORD_READS (0)
+	#elif defined(_M_AMD64) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)
+		#define BASISD_USE_UNALIGNED_WORD_READS (1)
+	#else
+		#define BASISD_USE_UNALIGNED_WORD_READS (0)
+	#endif
 #endif
 
 #define BASISD_SUPPORTED_BASIS_VERSION (0x13)
 
+#ifndef BASISD_SUPPORT_KTX2
+	#error Must have defined BASISD_SUPPORT_KTX2
+#endif
+
+#ifndef BASISD_SUPPORT_KTX2_ZSTD
+#error Must have defined BASISD_SUPPORT_KTX2_ZSTD
+#endif
+
 // Set to 1 for fuzz testing. This will disable all CRC16 checks on headers and compressed data.
 #ifndef BASISU_NO_HEADER_OR_DATA_CRC16_CHECKS
-#define BASISU_NO_HEADER_OR_DATA_CRC16_CHECKS 0
+	#define BASISU_NO_HEADER_OR_DATA_CRC16_CHECKS 0
 #endif
 
 #ifndef BASISD_SUPPORT_DXT1
-#define BASISD_SUPPORT_DXT1 1
+	#define BASISD_SUPPORT_DXT1 1
 #endif
 
 #ifndef BASISD_SUPPORT_DXT5A
-#define BASISD_SUPPORT_DXT5A 1
+	#define BASISD_SUPPORT_DXT5A 1
 #endif
 
 // Disable all BC7 transcoders if necessary (useful when cross compiling to Javascript)
 #if defined(BASISD_SUPPORT_BC7) && !BASISD_SUPPORT_BC7
 	#ifndef BASISD_SUPPORT_BC7_MODE5
-	#define BASISD_SUPPORT_BC7_MODE5 0
+		#define BASISD_SUPPORT_BC7_MODE5 0
 	#endif
 #endif // !BASISD_SUPPORT_BC7
 
 // BC7 mode 5 supports both opaque and opaque+alpha textures, and uses less memory BC1.
 #ifndef BASISD_SUPPORT_BC7_MODE5
-#define BASISD_SUPPORT_BC7_MODE5 1
+	#define BASISD_SUPPORT_BC7_MODE5 1
 #endif
 
 #ifndef BASISD_SUPPORT_PVRTC1
-#define BASISD_SUPPORT_PVRTC1 1
+	#define BASISD_SUPPORT_PVRTC1 1
 #endif
 
 #ifndef BASISD_SUPPORT_ETC2_EAC_A8
-#define BASISD_SUPPORT_ETC2_EAC_A8 1
+	#define BASISD_SUPPORT_ETC2_EAC_A8 1
 #endif
 
 // Set BASISD_SUPPORT_UASTC to 0 to completely disable support for transcoding UASTC files.
 #ifndef BASISD_SUPPORT_UASTC
-#define BASISD_SUPPORT_UASTC 1
+	#define BASISD_SUPPORT_UASTC 1
 #endif
 
 #ifndef BASISD_SUPPORT_ASTC
-#define BASISD_SUPPORT_ASTC 1
+	#define BASISD_SUPPORT_ASTC 1
 #endif
 
 // Note that if BASISD_SUPPORT_ATC is enabled, BASISD_SUPPORT_DXT5A should also be enabled for alpha support.
 #ifndef BASISD_SUPPORT_ATC
-#define BASISD_SUPPORT_ATC 1
+	#define BASISD_SUPPORT_ATC 1
 #endif
 
 // Support for ETC2 EAC R11 and ETC2 EAC RG11
 #ifndef BASISD_SUPPORT_ETC2_EAC_RG11
-#define BASISD_SUPPORT_ETC2_EAC_RG11 1
+	#define BASISD_SUPPORT_ETC2_EAC_RG11 1
 #endif
 
 // If BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY is 1, opaque blocks will be transcoded to ASTC at slightly higher quality (higher than BC1), but the transcoder tables will be 2x as large.
@@ -134,23 +5088,23 @@
 #endif
 
 #ifndef BASISD_SUPPORT_FXT1
-#define BASISD_SUPPORT_FXT1 1
+	#define BASISD_SUPPORT_FXT1 1
 #endif
 
 #ifndef BASISD_SUPPORT_PVRTC2
-#define BASISD_SUPPORT_PVRTC2 1
+	#define BASISD_SUPPORT_PVRTC2 1
 #endif
 
 #if BASISD_SUPPORT_PVRTC2
-#if !BASISD_SUPPORT_ATC
-#error BASISD_SUPPORT_ATC must be 1 if BASISD_SUPPORT_PVRTC2 is 1
-#endif
+	#if !BASISD_SUPPORT_ATC
+		#error BASISD_SUPPORT_ATC must be 1 if BASISD_SUPPORT_PVRTC2 is 1
+	#endif
 #endif
 
 #if BASISD_SUPPORT_ATC
-#if !BASISD_SUPPORT_DXT5A
-#error BASISD_SUPPORT_DXT5A must be 1 if BASISD_SUPPORT_ATC is 1
-#endif
+	#if !BASISD_SUPPORT_DXT5A
+		#error BASISD_SUPPORT_DXT5A must be 1 if BASISD_SUPPORT_ATC is 1
+	#endif
 #endif
 
 #define BASISD_WRITE_NEW_BC7_MODE5_TABLES			0
@@ -161,7 +5115,18 @@
 #define BASISD_WRITE_NEW_ETC2_EAC_R11_TABLES		0
 
 #ifndef BASISD_ENABLE_DEBUG_FLAGS
-#define BASISD_ENABLE_DEBUG_FLAGS	0
+	#define BASISD_ENABLE_DEBUG_FLAGS	0
+#endif
+
+// If KTX2 support is enabled, we may need Zstd for decompression of supercompressed UASTC files. Include this header.
+#if BASISD_SUPPORT_KTX2
+   // If BASISD_SUPPORT_KTX2_ZSTD is 0, UASTC files compressed with Zstd cannot be loaded.
+	#if BASISD_SUPPORT_KTX2_ZSTD
+		// We only use two Zstd API's: ZSTD_decompress() and ZSTD_isError()
+/**** start inlining zstd ****/
+#error Unable to find "zstd"
+/**** ended inlining zstd ****/
+	#endif
 #endif
 
 namespace basisu
@@ -227,7 +5192,7 @@
 	{
 		crc = ~crc;
 
-		const uint8_t* p = reinterpret_cast<const uint8_t*>(r);
+		const uint8_t* p = static_cast<const uint8_t*>(r);
 		for (; size; --size)
 		{
 			const uint16_t q = *p++ ^ (crc >> 8);
@@ -237,305 +5202,7 @@
 
 		return static_cast<uint16_t>(~crc);
 	}
-
-	const uint32_t g_global_selector_cb[] =
-/**** start inlining basisu_global_selector_cb.h ****/
-// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-{
-0x0, 0x505, 0x5555, 0x5F5F, 0x5055050, 0x5055F5F, 0x50AAA551, 0xFAA5A5AA, 0x6AAA5095, 0x41E6FBAB, 0x19AE99F5, 0x1057AAA4, 0x54005A1A, 0x4459AEAF, 0x56015B, 0xBAA9A554,
-0x4335E5E0, 0xD9FE5FBB, 0x2525256A, 0x9AE892, 0xC0D5FAF5, 0x5BA5E641, 0x7EDEC8B8, 0xBB671211, 0x4C9844EE, 0xEE042415, 0xE5663EAE, 0x90909091, 0xAAA45AFF, 0x15556E1D, 0xA6959195, 0x4BFF8BF,
-0x5166AAF, 0x15490065, 0x6F5BAFAF, 0xFF00FF00, 0xD96956AA, 0x15AF6B, 0xFF5A00AA, 0xE0E557AA, 0x1A6F19BD, 0x69655555, 0xD0500158, 0xEEEDD894, 0xE4E4FE, 0xC71B7B10, 0x55AA5AAF, 0x50AA59BE,
-0xE4E990E4, 0x5353B63B, 0xFEE5D0E4, 0x96AF051A, 0x3CC95A6, 0x70B5A40D, 0x9504196E, 0x4A0BD7A3, 0x11B89592, 0xAAFF4095, 0x55A5D4E0, 0xBBA55050, 0x1111666, 0xA5544000, 0xED994444, 0x5A56BF,
-0x94A954B9, 0xFB651000, 0x604E633E, 0x14291A15, 0x56965956, 0xB8E0D0C0, 0x5A565A55, 0x65A61A6A, 0xE490F990, 0xCA87AAF5, 0x6060A0B, 0x24C23143, 0x55AA9A40, 0x505E1605, 0xCEC0486E, 0x156E55EA,
-0x79978B0B, 0x4595F53C, 0x405C4AF7, 0xC1897D75, 0xD5F40BA6, 0x95444017, 0x14AD6935, 0x87C7A7BD, 0x4A4E8597, 0xFF1D7E55, 0x451400F9, 0x1112277B, 0x9A0590F8, 0x53E3492E, 0xE590E995, 0x7E730A9A,
-0x929697E7, 0x2E781609, 0xE22317A1, 0xEDE9D884, 0xDDD75CDD, 0xAF1B6F1A, 0xE6909047, 0xA77DAD5D, 0x184C0D5D, 0xFAB56010, 0x5EA4F1D0, 0x11166B6B, 0xF51A7AD6, 0xF79950F4, 0x1B6B1B11, 0x9A6D6469,
-0x441997E, 0x4546869A, 0x95AA6965, 0x155A6A, 0x6E68B0E6, 0x5A55A665, 0x1B051605, 0x601D8BE6, 0xBD2F1B06, 0x409A429B, 0x23272721, 0xB07454A9, 0x7E66A3A1, 0x1B6A5500, 0xA0E0F5A6, 0xBF5A0500,
-0x55A5A9A9, 0x99D9E995, 0xE440566F, 0x6550BE99, 0x2267777B, 0xFA50FE50, 0xA657B441, 0xB4E29343, 0x555090E5, 0x45465B6B, 0xE654E6, 0xEA90469B, 0x2E05D2F4, 0x99594444, 0xF1C20746, 0x295AD2E0,
-0xF990EA95, 0x804459AE, 0xA9999894, 0x1F41E4A5, 0x4040E5E5, 0x5481E1F2, 0x2AFF59F1, 0x6B6B1712, 0xA7131051, 0xF9406F16, 0x1B2B5B9E, 0x587E0F2F, 0x547E1919, 0xD0F5645B, 0xB1B1B1B, 0x5756A4FE,
-0x46A9B965, 0x1529F99D, 0xE490E490, 0x4495FE, 0x985E0B06, 0x5FD2D23A, 0x5D0E95A, 0xF69103F4, 0x4029790, 0x1B062F1B, 0xEE594500, 0xB6539B5A, 0x106165BA, 0xD26B7C8D, 0x8B2A25A5, 0x55EAD5E3,
-0x431FB8E1, 0xBEB4E646, 0x9A5A4545, 0x5015A6B, 0x90D4B83D, 0xDB8A99A4, 0x9E894905, 0xDD7D1101, 0xA95E00BF, 0x579FA5A5, 0xA292D145, 0x93292C96, 0xF9A995A5, 0xBFE8A450, 0xB990D15B, 0x45D1E01A,
-0x4BD3F767, 0xF243479A, 0x7E420927, 0xF9E5E090, 0xA1C869F, 0x253A36, 0x9BAB569A, 0x4147031F, 0xA059AFE, 0xE0D6590F, 0xD5EAD5E6, 0x9A4B4641, 0x5AAA4406, 0x55EA90E4, 0x10179BC4, 0x44485999,
-0x5156253E, 0x1F29E054, 0xCDDAA773, 0x5601AB05, 0x94FC94C0, 0x116166BB, 0xBF964006, 0x414196EB, 0x8498D9ED, 0xB5E08687, 0xBD564150, 0x2B8D9DF8, 0x7F12017E, 0x90904747, 0x50B56AB, 0xDBD19490,
-0xBB5A5659, 0xBAF40E4, 0x6D649014, 0x1D29166F, 0x414F3D75, 0x6F929540, 0x565AAF05, 0xBD9884E5, 0xF5342A25, 0x157915AE, 0x1A055A55, 0x9019A19F, 0x64B96A05, 0x35689CCC, 0x996012E2, 0x5252677B,
-0x156AA401, 0x25BCE483, 0xAA665555, 0xD6AF4B0F, 0x3F4BBDE0, 0x9404A9AF, 0xA590F9E4, 0x8191A5FD, 0x568190B4, 0x591A6616, 0x92C11D3E, 0x97D2E5FC, 0xF5A55A6F, 0xBEE0969B, 0x8918B4CA, 0xE0915397,
-0x5243472F, 0x95EA4055, 0x55E6E0A4, 0x9AEBD181, 0xF4A25357, 0x11115666, 0xFE45FF0A, 0x8BC7D2E1, 0x800556BB, 0x757D6A96, 0xFA909A5B, 0x68962FDB, 0xEB0056AA, 0x69970241, 0xAA58AD64, 0xC4D9DED5,
-0x5A5BF2F0, 0xBD0905B4, 0x197D7801, 0x8987EDC4, 0xFF40565A, 0x460978A4, 0xE4067FE4, 0x5DA23153, 0xB90565AE, 0x5E14B946, 0x4E35879F, 0xC72F8666, 0x1816472F, 0x9A5A4949, 0x64A0D1E5, 0xC7025B1A,
-0x1B061B55, 0xFFAA051B, 0xAF5DEDA1, 0xAA955094, 0x6659965A, 0x99A95DAD, 0x9450A5A5, 0xA550A595, 0x6914B950, 0xEF454944, 0x906BB990, 0xD680944B, 0xE091461B, 0x5363B7BB, 0xF0743906, 0x66566A69,
-0x4B85D0BC, 0x40E494A5, 0x1161B6B6, 0x519BD59, 0x5998401, 0x1651F26B, 0x5709BB1B, 0x6AE1D1B9, 0xD19297BF, 0x1A69FEE4, 0x6066B5A, 0x74A56491, 0xB4661151, 0x559191A4, 0x96756A68, 0xF5C791A6,
-0x20297A15, 0x6B660100, 0x313177A2, 0x55054150, 0x6A969669, 0xF0B82111, 0x555A6996, 0xB666295A, 0x1EA95441, 0x6A166BA5, 0x8C18566D, 0x2797278A, 0x82A552BD, 0xF964BD14, 0x41540668, 0x5078785A,
-0x5754FE, 0xF9E0E5FA, 0x15453D3F, 0x5A9699A9, 0xD9854147, 0x849494E9, 0x1DC39734, 0x67E797B3, 0x107066F, 0xAED9986C, 0xAB564140, 0x9B51A6B7, 0x5FD3E2F4, 0x5A5429F9, 0xF9A05161, 0x5A5A6965,
-0xDDD88484, 0xFA50FA55, 0x90E5E4FA, 0x6BF166B, 0x6566665A, 0xE450A6E5, 0xEB45AA04, 0xDA9A4646, 0xD7A37235, 0x11431B97, 0xD41D6E64, 0xD3D3A1A0, 0x5D540E9, 0x627777BB, 0x5054A4BE, 0x593A05ED,
-0x2EBE454B, 0x1ABA1015, 0x7C64B460, 0xC358B47F, 0x176F4293, 0xA6E417AB, 0xF611756E, 0x1F40D499, 0x84885D5F, 0x2F0B9B9B, 0x14BE05, 0xE5919590, 0x101B146E, 0x7B261190, 0xDC96F8B0, 0xF460257E,
-0x34B0AFC0, 0xEB9140FE, 0xC5C589DD, 0x1F6D6865, 0xF5100195, 0xAF560607, 0x505066B5, 0x7E590999, 0x13E190E4, 0xA56ABD59, 0xC21B68D7, 0xE594E4, 0xF6576E50, 0xFFA751D1, 0x19A179CB, 0x2726797,
-0xA1931C7C, 0xE1D90F1B, 0x7F2B2510, 0x6AF90055, 0x5F1E4C88, 0xE410757A, 0x95702212, 0x7B762100, 0x1B05BF6A, 0x16F05AB, 0xDDC5C9C9, 0x72BE594, 0xE490E555, 0xC5E50106, 0x816DAC16, 0x5540FA90,
-0x156605FF, 0x3B372621, 0x2B57A67D, 0x6C661E16, 0x1E97A917, 0xE6E2D383, 0x1B40F91B, 0xD9A63333, 0x34E18629, 0xA71616E9, 0x84946D99, 0x1B6906AF, 0xEFDE8904, 0x88F52470, 0x50E990F8, 0x4182E1B4,
-0xBAE1865B, 0xF48E4F4, 0x64A0517F, 0xA1F45902, 0x12177BF5, 0x465EBD91, 0x37A747, 0xF0A5106, 0x4C4E8A5, 0x62779E65, 0xDE494989, 0x7B6796D1, 0xC5C5C58A, 0xE4786B07, 0x6F07E0F9, 0x5554A550,
-0x95559333, 0x747A6B5, 0xA4A45500, 0xE998444, 0xF5966371, 0x111116BB, 0x783A679, 0x95409AFF, 0xFF9690E4, 0x60743EBD, 0x1C5A90FD, 0x2B051EE9, 0x5B7A1624, 0xEB415701, 0x1B6B0155, 0x9BCB8586,
-0x599E5C51, 0x510064BE, 0x50FA6060, 0x16066B5B, 0x54DA89D5, 0xA01468B5, 0xC1655E5, 0x55FF6657, 0xE4985E9, 0xD738BE27, 0x6938D450, 0x47D0E4FE, 0x4858986E, 0xE793431E, 0x1A05FFFD, 0x18939141,
-0x15EE4620, 0x79E45151, 0x663AA556, 0xD1266DD9, 0x7E0655E0, 0xB6A7676D, 0x54A96AA5, 0x1664092B, 0x56517AA0, 0xD6402CB8, 0x40A7773C, 0x554F0646, 0x488D5F2F, 0xE4E49095, 0x1C7CB4E0, 0x7C27529A,
-0xF6FAA151, 0xCC7358D6, 0xE8406D15, 0x6E074B5F, 0x638359F7, 0xD4E9A88C, 0xE888050F, 0xE6546A0B, 0xB9904EBD, 0x755061AF, 0xA371285C, 0xE95A1904, 0xCADD042D, 0x757F6ED6, 0xE4A91F06, 0x6D5D0909,
-0xE49559B8, 0xF4B0569B, 0x8454B5B9, 0x2161B5B6, 0x855AADEE, 0x575B0544, 0xBFE4D086, 0xE484CBEB, 0xF9F5426F, 0xCC653366, 0xA3524656, 0x9A5989E4, 0x10451466, 0x71F1655, 0x9B90A4ED, 0x14599FF,
-0x9666AA91, 0x5A99A945, 0x9685CD8F, 0xB8506A91, 0xB427E0F8, 0x50A990FE, 0xA5FA9090, 0x60D4DA80, 0x28E35CB3, 0x55E4AA, 0xD20B55A4, 0xE15F86, 0x36E6995E, 0x54036FFF, 0xA79D2250, 0xBA11A500,
-0x404603AC, 0x641065A0, 0x9DD84A0A, 0x969B061B, 0x36737313, 0x7B65631A, 0xA4E4C099, 0x9590448F, 0xD57F0680, 0x6094D86D, 0x15D8E3BD, 0x757F7DD7, 0xB45B854, 0x6560FA98, 0x7A805637, 0xD68416BB,
-0x7B767131, 0x90F9E8FE, 0xA4E54045, 0xE0411F6E, 0xD57D7DDD, 0x33CB1C33, 0x58ADD010, 0x9B1FA5C6, 0xA401BE95, 0xA950F994, 0xA851971E, 0x33CC33CC, 0x10F0B164, 0x151A6F01, 0x78B5660C, 0x33333225,
-0x41162402, 0x5F0506CB, 0xFD96166F, 0xE4417643, 0x56A51A94, 0x5323BFEA, 0xD12DD12D, 0xA999959A, 0x547C6482, 0x499EE652, 0x4AC7D1E5, 0x2D3DAD07, 0x6B171201, 0xAF065854, 0xD6C4891D, 0xCC739CE7,
-0x9D692663, 0x3E41597C, 0xF38314BB, 0x1150A4F4, 0xE1E50FA, 0xF4D60B6F, 0x5A54E590, 0x227AB5F0, 0x73A3D7FC, 0xD7420A59, 0x12015A59, 0x4F1999D5, 0xA90EE44A, 0x1065B9B5, 0xD10533E3, 0xBA918409,
-0xE5409FEF, 0x4549047F, 0x6B57A6A5, 0xE94691AA, 0x111A6E7E, 0x45496BA, 0x49FD999, 0x414D5B8D, 0xAB10EF5E, 0xE9878505, 0x8C910499, 0xC0C5DA3E, 0x6F1B7298, 0x177D78D0, 0x687B5665, 0x3F470353,
-0x1441A590, 0xE1965F6F, 0x5A5B4A8D, 0x47D7C98, 0xD1404115, 0xB89A053F, 0x8C4095FE, 0x4861E055, 0x3B417607, 0xF9E0E4E0, 0x65B0506, 0x93633236, 0xAA07A5E4, 0x77747080, 0x776160F0, 0x1672B05,
-0xA54E0428, 0x520A9625, 0xE581065, 0x90C76D76, 0x2157B2B3, 0x5C5BE06, 0x151A5A01, 0xA9D5C081, 0xCBCD9854, 0xFDD1061F, 0xB66111B1, 0x9DC3D7B0, 0x650A7642, 0x8095734D, 0xD052011B, 0xE0A1479B,
-0x9501BFF8, 0xE9D9BD0D, 0x7A017925, 0x69A67373, 0x41E0E557, 0x5F844124, 0xEAB0695E, 0x566B5040, 0xCCC9D693, 0xA79684DE, 0x6B5BC3C1, 0x9595667B, 0x9C33CA5C, 0x8984C5C5, 0x459BBDE9, 0x1F10A5F4,
-0x22A55AA, 0x97C3430D, 0xAA569A55, 0x552E1E00, 0xD3C3C78B, 0x82C7521E, 0x5B0605EA, 0x5FF69268, 0xD081460B, 0xE4517F06, 0x4448C9CE, 0x2F69F940, 0x476DA470, 0x9F96FE12, 0x4D8D9E8E, 0x6A6A16B5,
-0x1D05BE66, 0x84F5BD, 0x691E1B41, 0xE0939B17, 0x159059AA, 0x1E5792B9, 0x25A701A5, 0x439162E, 0xE994077C, 0x5CC396AA, 0x1D0D9AA5, 0x4A4A598D, 0x1B6F156B, 0x1A1B0F40, 0x34CB34CB, 0x6F542E,
-0x32CC739C, 0x94EB9669, 0xDA8D4E1D, 0xC6C5C46E, 0x10152B3F, 0x8787F9F9, 0x5E42D064, 0x699B05E9, 0x7030295E, 0x495E09BE, 0xEE191016, 0x801D2D56, 0x3A0099F9, 0xEA09059E, 0x5BAB5100, 0x393D49C,
-0x10E15DC2, 0xB056DD4, 0x3536915, 0xE0C18719, 0xEB964090, 0x6172727, 0xFD5900FA, 0xD10B78D1, 0x33332626, 0x50F990F9, 0x78600A5B, 0xE2B5401B, 0xAE5E9404, 0xF2CF0C0, 0x9E9D8080, 0x84E4F4F9,
-0x41F0E59F, 0x90787E12, 0xE4E19143, 0x761D6706, 0x6560BCE5, 0x134A9BD3, 0x23768995, 0x22ADF6, 0x434A5C72, 0xD4985444, 0x70936BFF, 0xAB54E0E7, 0x45E7A682, 0x7A786D90, 0xF8546A00, 0x5F5E4540,
-0x999A651E, 0xF9E297FD, 0xAF86E5, 0xD00B6E54, 0x5442878A, 0x50E940A5, 0x61F6AF1, 0x479701AA, 0xAE455E5D, 0x6560123E, 0x22D17625, 0x83071B64, 0xF9460251, 0x5F4B064A, 0x8742417C, 0x5F89C51A,
-0x14A29F50, 0x5013BF6B, 0x76395676, 0x54A590F9, 0x40915AA7, 0xEB95E041, 0x7E560504, 0x65B9E4D1, 0x3F63A594, 0x17448216, 0x1A4F87F1, 0xF990E696, 0xECE89A50, 0x2266B17, 0x6A959A98, 0x50F5001A,
-0xBF056A55, 0x74470FFE, 0x65251011, 0x9F7D6597, 0x51BB962, 0xA0D04297, 0xA257F0D1, 0x5B1569D5, 0x4F40959E, 0xEC5D1D0D, 0x51A1A, 0x1DF56462, 0xC4491A6F, 0x4B4A55EF, 0xFD741D5F, 0xE1526713,
-0x875153E2, 0x9752A2E2, 0xEFDA8504, 0xF0E84756, 0xE0A196E9, 0x5FAF5C40, 0x9A3359CC, 0xE056062E, 0xB07B71D7, 0x5966475D, 0x66161100, 0x444A0151, 0xDAC7D6F5, 0xFBE8E314, 0x35098512, 0x1A7F7690,
-0xAF970158, 0x666A6996, 0xD1D10938, 0x742969B, 0x4542A5FE, 0x6EBE50A1, 0x816E7955, 0x64E1D0C1, 0x105156AA, 0x6A9AD0F5, 0xB4909E2E, 0x55A6A959, 0x45B4999F, 0x3266CC26, 0x9B915EE, 0x9769E58B,
-0x2EF59968, 0x3F2F0711, 0x79798469, 0x6161B6B6, 0xA79504B1, 0x9B92A351, 0x61C08573, 0xAB1B656F, 0x37271601, 0xE4840979, 0x45D1C1DA, 0xA4C4961B, 0x59A7F2F0, 0xEA9FC147, 0x635362B0, 0x9561EAF,
-0x6B6762A1, 0x585A43D3, 0x8484819F, 0xD1C30D5D, 0x2123101D, 0xA0F8E4F9, 0x63676220, 0x17EB6A5, 0x90E63F27, 0xDD256045, 0x7B66A1A0, 0x64143F6, 0x41D479D7, 0xF1520F82, 0x12B44687, 0x1504BE1A,
-0x90E45401, 0xC4C98E4F, 0x919097C, 0xA7A52919, 0xB9B62313, 0x9695C089, 0x30C5E6BD, 0xAA55669B, 0xD19F0645, 0x1150E2FF, 0x36213121, 0x1F1F0A05, 0x2A315099, 0x2A1E0414, 0xA3E3D04, 0xD5992851,
-0x19A56A45, 0x5D0669E5, 0xA7C1F8C0, 0x84D1E5AA, 0x7292A464, 0x9040F5E4, 0xF185405, 0x1FAE4509, 0xF91690BE, 0x5540A540, 0xA1D2874B, 0x560B65F8, 0xC207E1E6, 0x646D0F9, 0x5A1440, 0xBB454116,
-0x13597242, 0x413A4504, 0x66E7D2D2, 0x61DA6950, 0x519DF0A0, 0xD2926EB4, 0xA583060B, 0x247E1587, 0xE50590BD, 0xEFF50146, 0x6252B722, 0x4B9AF552, 0x42445A5, 0x5D0844D0, 0xD7C1D18, 0x6B53900,
-0x7DD68434, 0xE6964247, 0xE0A50B5, 0x72635347, 0x669A6B06, 0x91549A65, 0x8F097CA5, 0x849458EC, 0xF9B09275, 0x71390D5A, 0x478BC9D6, 0x5D579AA5, 0x9ED08605, 0xCA1C35D3, 0x1029669, 0x1344FEA7,
-0x5B468B87, 0xA7F29990, 0x60BDB855, 0x3430B574, 0x544461FF, 0xC5C9550E, 0x69E716A7, 0x112336, 0x3F11D2D7, 0x2F0796E4, 0xB5250B00, 0x33CC33DD, 0x20357676, 0x7B6F9272, 0x114B09BB, 0xA7F6C987,
-0x32959833, 0x40D25BB6, 0x13170353, 0xD52E5949, 0x93626538, 0x43449A56, 0x655890BA, 0x2F56811B, 0xE5E4C88, 0xA6079500, 0xA4F90507, 0x6460A055, 0xE990906D, 0x156F56AC, 0x54CF00, 0x181D5A0A,
-0x7C09E947, 0xAD9E898C, 0xFF914212, 0x6933A7CC, 0xB2935B2E, 0x4454D8A2, 0xA6A560B6, 0x519E2075, 0x575FA6A5, 0xB8B06916, 0x598B471B, 0x10686AD1, 0x45EA0170, 0xD0470B9B, 0x3B511E0B, 0x53D79D0,
-0xCBCAC5C5, 0xDAD54CD0, 0x3542EE79, 0xB4AD4FD, 0x642DFF01, 0xB99109B9, 0xE1919B9F, 0x97B84162, 0xE995460A, 0x1060F5F5, 0x166DBCF1, 0x4214957A, 0x6C60626, 0x50FE4F0B, 0xB466470A, 0x808596E2,
-0x70D1440D, 0x818617B6, 0xC8E8DDED, 0x40443474, 0x103E0750, 0x1559A9, 0x16E29FF, 0x54FE0447, 0x34CAB25C, 0x9B30756A, 0xB0E74B05, 0xE19051D, 0x402E7450, 0xF5E0D1AB, 0x87979B5F, 0x8707BA71,
-0x90B4A491, 0x1A2F5301, 0x6C44D318, 0x8AC0A1F4, 0x5A6F0306, 0xE1159090, 0xF9A54183, 0x4CC7321C, 0x7E64868B, 0xFDE60582, 0x4BE77014, 0x1B902D01, 0x104D8FA7, 0x16A7CD, 0x94693912, 0x62E759A,
-0x594BA906, 0x5D023747, 0xDF9757AD, 0x97364CCA, 0xFA011265, 0x12E16116, 0x7A615600, 0x501196F9, 0x5067E247, 0x2A75B070, 0xBC0196BE, 0x19FD8907, 0xCA8511AE, 0x7B671210, 0xB8F0966F, 0x600AE5F4,
-0x4146858E, 0xA579C124, 0x19F26C13, 0x2320776, 0x595BF900, 0xFB059055, 0x6FD6E460, 0x86CAD5D, 0x948153A5, 0xC6C546FF, 0xE199AD5A, 0x656A566A, 0x81256994, 0x7C285400, 0x37CD6A37, 0x4CF4E1B,
-0xD181E0B5, 0x90F89F46, 0x5AD2D072, 0xF1F44D4, 0xB5E091C6, 0xFF90E764, 0x656B9965, 0x833471C7, 0xE6470700, 0x521A517, 0x56620BF, 0x7A6458C9, 0x566959A6, 0x5A5FF3D2, 0xD050063F, 0x9AC17C39,
-0xC1F03D19, 0xE7939343, 0x35312404, 0x76671223, 0xA0D05804, 0x7B773262, 0x5E2E6465, 0xE6860519, 0xDE909B5A, 0xB5C094E4, 0xAF019B15, 0x1A57027F, 0x7874E7D3, 0xB35674A, 0xD0854FB6, 0x916509FD,
-0x431F91FF, 0x9B605420, 0x566978B4, 0xE8D1042D, 0x2533074, 0xEC904443, 0xD404A4D1, 0xB9984945, 0x435181E6, 0xDFD0520A, 0x37FC61D, 0x1540FA94, 0x876DB853, 0x9D686C9D, 0x5D7DE642, 0x556A6669,
-0x6B166F05, 0xF0F06616, 0xE490051F, 0x147B0606, 0xFD76D9D9, 0x3B814E5D, 0x16E6460, 0x91F05406, 0x37444D34, 0x1B17BF00, 0xA8465A05, 0x12429D1C, 0x79753935, 0x639291F4, 0x6761F0F4, 0xBC789460,
-0xF890D79E, 0x54780743, 0x1131367B, 0xD6487C64, 0x8E582E4F, 0x6A972A65, 0x1BA6D0E5, 0x17D6007A, 0x82590727, 0x95D0FA, 0x1540E47E, 0x56B91A0B, 0x8A85C4D4, 0x9F8205E4, 0x80D4C58B, 0x75D3E647,
-0x5956D966, 0x74ED4500, 0x167EA440, 0x255E191, 0x31811515, 0x82999DDF, 0x11670BB8, 0x2BDAD965, 0x5AA5669A, 0x55BF105A, 0x88496E59, 0x5AF56600, 0x4858E751, 0xF4811BB9, 0xB501A7B0, 0x11B26DB,
-0x767C9887, 0x602D7703, 0x1219F8FD, 0x464297D, 0xFF06DB95, 0x156A04BF, 0x5050A39C, 0x35CA4F94, 0x7F00EF1B, 0x68353273, 0x150663B6, 0x79666190, 0xDA650647, 0xA962959A, 0x96E596FF, 0x537E17A3,
-0x57F9E440, 0x101458FF, 0xA4D4E441, 0x1898C4E0, 0x7E189481, 0xB6C71904, 0x9A95EDDD, 0x944449FF, 0x61E4C997, 0x52DF8F5, 0x6A51F46E, 0x9145AD, 0xD9A8DDD2, 0x8784E63D, 0xFF5B906E, 0x2998A559,
-0xCCCC6633, 0xB954C0D0, 0x70B5663B, 0x531C8B25, 0xFFBA0191, 0xF4E35B90, 0x40FF7150, 0x1F075AE5, 0xD0015BFF, 0xDCC3D6DB, 0x4E54A07F, 0x7E9647A, 0xA19D4E1, 0x51504404, 0xE7D68A8A, 0xAF10A450,
-0x71B71184, 0x79940A0E, 0x821B196E, 0x50413A5B, 0x5707962D, 0xD1B63962, 0x819DEDE4, 0xEEC5CB54, 0x251DBAD, 0x50BD6D1D, 0x20976E74, 0xDDC98A4F, 0x451079E9, 0x69146E, 0x68590311, 0x8045A9F4,
-0xEEC58B96, 0x98CCC996, 0x94784451, 0xE6D6015B, 0x3035B95, 0x12E39F6, 0x50EE4058, 0x4D1C74A0, 0xA4291505, 0x936B67D4, 0x2AC1449D, 0xA4015A6D, 0xAFCB4414, 0x74A50038, 0xDED051C8, 0x347B76DA,
-0x817ED01B, 0xDD2D79D2, 0x5A1A011B, 0xA040F556, 0x540246B8, 0xF2B45A06, 0x6A4999B4, 0x4B67D0BF, 0x31614701, 0x456C84C1, 0xB8F4814C, 0xFF009669, 0x4F4A4999, 0x907D95AB, 0xB7A49402, 0x526E61D,
-0x5A9542D3, 0xF8792606, 0xA913569, 0x3193534B, 0x7A61D074, 0x51A452E3, 0x40E490E5, 0x4106377, 0x404A1709, 0x1562727, 0xC0B89996, 0x4440781E, 0x78FA9053, 0x5D1E00E8, 0x1C3C75D0, 0xD581AB05,
-0x85C58A4, 0x44E490E4, 0xCD94CDCA, 0xB252E6D6, 0x1FC345AB, 0x40C5B905, 0x26693851, 0xEC3741D, 0x1B5869B, 0xA161510, 0xE061977B, 0x8A580510, 0xD960D554, 0xB53E091, 0x14B900FA, 0x3E094659,
-0x6090906, 0xE6B47C17, 0xECEC9840, 0xF9A405FA, 0x90F994FA, 0x2B750A5, 0x803B3D25, 0x14AE405F, 0x6F97E0FD, 0xCD34E38C, 0xDED0D4AF, 0x96D1C038, 0x51E78187, 0x93D7CACD, 0xD4D052A7, 0xE6558B4F,
-0xF6025766, 0xE54074B0, 0x6613252C, 0x257A75, 0x1B1266B5, 0xF956E0B6, 0x44D3E3B9, 0xC5C5E9E6, 0xEBD69599, 0x9F91D0F8, 0xB0A05253, 0x6E0F1761, 0x425FE480, 0xA5A051FF, 0xB1384DC7, 0x1CE31CD3,
-0xEBF6701B, 0x6B152998, 0x35A62510, 0xD140E5E6, 0x9070791D, 0x3CA6D1, 0xDAC98985, 0x90917E97, 0x19BCF91, 0xD0C7CBC7, 0xB5466B37, 0xB111D25B, 0x9A29978C, 0x3196C50, 0xCAC5C1FF, 0x4F4A4192,
-0xB14E708B, 0xD5E958D3, 0x73747E24, 0xEDE0D6A2, 0x1B91436E, 0x79252511, 0xCBC58C44, 0x7E64F890, 0x9F05B9F4, 0x1B55E0D0, 0x21D1E969, 0xF4558028, 0xF9E5C3C2, 0x1974325B, 0x6A94E0F0, 0xD101A5BD,
-0x1A17075, 0x5B2D78E1, 0x17194807, 0xF5C24B1D, 0xFA40E655, 0x7A095515, 0xE106F993, 0x565A1103, 0x5A54F6E0, 0xF5E0016E, 0xD6CBC5C1, 0xF940E696, 0xAE316D90, 0x6A146A00, 0x9B96E9E4, 0x6351D86F,
-0x5A466995, 0xD4460B8A, 0x2CC3744E, 0x99666696, 0x20257ABF, 0xA3F2955, 0xD5D0919A, 0x54444859, 0xFC5C606F, 0xA6653749, 0x306E696, 0x2528BD70, 0xA07BCE5, 0xF0A46662, 0xDC649440, 0x99C7874A,
-0x656CBEB, 0x5151A67B, 0x60DB4B05, 0xD1444107, 0x514B74, 0x165025A, 0x5B5A1101, 0x7101179A, 0xD09A070B, 0x50A096BF, 0x47A9521D, 0x4B2D7492, 0x3F0F1B53, 0x1941ABFF, 0x7B666111, 0xD3C345BA,
-0x1CA9D6, 0xE18359B9, 0x1590E6F8, 0xF4285902, 0xBF1B92D0, 0x1BE76D, 0x2A5582C3, 0x8979164B, 0x3C721B40, 0x33C0CF30, 0x2DD21EE1, 0xE9D30707, 0x192DF65, 0x1B92E7C5, 0x33333669, 0x1E0560F4,
-0xFB53034B, 0x966A6699, 0x6F9797A7, 0xEF469BDB, 0x1943F5F6, 0x1DA7C70A, 0x74741E9E, 0xF5709967, 0x520B74F1, 0x4741FBAB, 0xDA6712B9, 0xCBC6C1D0, 0x67E2D64, 0x5E03B625, 0xC088D9D5, 0xF89005FA,
-0x5A2516AA, 0xE5C7D9D6, 0x69FE5096, 0x435BE0D1, 0x7193DB8, 0x9CCC9967, 0x54613301, 0x461211BC, 0x730C6FAB, 0x6050A5B1, 0x7EB91141, 0x6A152F00, 0x69665AE5, 0xAE5780F8, 0x7D06F90, 0x2032D510,
-0x4E0746D1, 0xA69C33CC, 0x1A462616, 0x36C6C6C6, 0xFFA59D1F, 0x9B6D74F1, 0x1197907E, 0x656A9995, 0x91742E48, 0xD00538F5, 0x441542, 0x40D2469B, 0xFF5999, 0x15A9966A, 0x94841EF9, 0xA5651D1F,
-0xCC9C6633, 0x2F7D107E, 0x9B81411F, 0x9E59A669, 0x5E970007, 0x7E2E1F03, 0x29B5F244, 0x86C5D48, 0x548581B4, 0xF955CF44, 0xE35CB2CD, 0xA956B6D7, 0xE0E69125, 0x1969C59D, 0x550099EF, 0x68D99F24,
-0x5FA65010, 0x2B1256FD, 0xF8244147, 0x1D56470C, 0x2162A76, 0x62F16510, 0xAA556995, 0x540669, 0x14C29726, 0x4790FC0, 0x2CC19B44, 0x6CD7759D, 0xD7F64140, 0x425F81E4, 0x5034348D, 0x65095966,
-0x7261B0F5, 0xAF5681D1, 0x935A4051, 0x5262767B, 0x595EC2E8, 0xDB743847, 0x3C60A513, 0xC21F3991, 0xCB34C738, 0xD19AE995, 0xB9171781, 0x1107AB66, 0x5FC0D790, 0x2070F1F6, 0x6E5E1A09, 0xB5407E5E,
-0x505494EA, 0xE4703556, 0xF890999E, 0xA65BF595, 0x2919A7F, 0x49DD3880, 0x94EF960A, 0x183D1906, 0x1500F9F9, 0x9DEE4509, 0xF5FA6313, 0xE0526850, 0xBFE60005, 0xF16E685, 0xF90065A5, 0x916AF859,
-0xFF608454, 0xE6BF5094, 0x81D1E0FA, 0xA19FF91, 0x60D59CD9, 0x39524274, 0xD7994F4, 0x36295C8D, 0xE0503945, 0x2D85C545, 0xD1500658, 0x22172635, 0x338C5626, 0xDE812506, 0xDB57E8D0, 0xE9D955F6,
-0x9F6C94E6, 0x9636CC33, 0x2A1A3542, 0xF95D80F5, 0xD4266F42, 0x1C9E5F9, 0x74A1C30E, 0x6B16A050, 0xFB03506A, 0x4218B469, 0x94D1422D, 0x427D3531, 0xC25BAD40, 0x6692B292, 0x5A50E9A5, 0x717CD4C3,
-0x71938F59, 0x5147400A, 0x41BC6EB5, 0xD4A0D4E4, 0x1D0E5FA, 0xF6869F06, 0xA7E35252, 0xF4D1421B, 0xA4894DE7, 0x2D946B1A, 0xD0FFC684, 0x17C291E4, 0x7F525000, 0x15848139, 0x669669A5, 0xA7A76353,
-0xA996966A, 0x2FE155C0, 0x1E1E42D6, 0x4CB19976, 0x1103BE69, 0x57699082, 0x71624AF, 0xE1445A09, 0x969A4504, 0x5602955E, 0x5A1548E4, 0x5B074314, 0x74A6CE66, 0x9038152E, 0xD8C4554F, 0x3EBB0657,
-0xA8A65669, 0xB9B56010, 0x96B92D86, 0xE9D9CCA4, 0x33DE69A7, 0x5C8CD8E4, 0xF5D1401A, 0x4B59A46B, 0xCD258D07, 0xF3C3A475, 0x105DFC2E, 0xF7929140, 0xA4A450FE, 0x5B4598, 0x17137B75, 0x1B05F082,
-0xD62474D1, 0x673AB500, 0xA9460B5E, 0x47025AD1, 0x6E6695D9, 0x1262B6F6, 0x6AE65190, 0x66A69499, 0xD0993A76, 0x22321727, 0x55C0C56F, 0x6F7D1E43, 0x352F53E, 0x6AD6662F, 0x62713623, 0x948484F9,
-0x27161E04, 0xE996065B, 0xFED0411E, 0x8BC59A4B, 0x689E4B01, 0x40467E7E, 0x550E0F8, 0x1832D78D, 0x6CEC9580, 0x6A9E8F6E, 0x5340D4B1, 0x1449C6D, 0x22163530, 0xB4F6C3C6, 0xAD01DA95, 0x656F051E,
-0x8151167B, 0xA0C3966B, 0x40F85B15, 0xDD4B8D2A, 0x24BB667, 0x52E193F, 0x56A7019B, 0x12625504, 0x11A16297, 0x4B59066C, 0x59E990A0, 0xA45C0B00, 0x92C34B3F, 0xD06F6325, 0xC68D18B9, 0x74783C30,
-0xEA58C5CD, 0x142151F7, 0xDA649E04, 0xDA950602, 0x6E641510, 0xA7249144, 0xF46A1FF, 0xA950F611, 0x752E1FC, 0x7A460551, 0xB8E0D6D1, 0xE9D09151, 0xC745A104, 0x2072E687, 0x9F870105, 0x65A0F1FB,
-0xD0D500F9, 0x1A901FE1, 0x6F4E9401, 0x33322558, 0x52532732, 0xDB16B782, 0xBAA56046, 0xBCA19396, 0x411B39B7, 0xDC847184, 0x31C8B364, 0x6A66906E, 0x3F85D8F0, 0x36EB4393, 0xD518985, 0x34392F47,
-0x1FCF4E96, 0xF4A05E06, 0xBD5B102C, 0xFF056A65, 0x561BB601, 0xACC50A51, 0xA3733235, 0x7E1B53E2, 0x5251A0E5, 0xE078156F, 0x1F56E790, 0xA9965D59, 0xE0949C7D, 0xD87D560A, 0xD0649CED, 0x1065A47E,
-0x257CE4C3, 0xE2409597, 0x71D49220, 0x253929F, 0x47673ECA, 0x716B51F3, 0x4C1C48C5, 0xC7C1520B, 0x7B593060, 0x4F88491E, 0xA566A956, 0x7767A34, 0x65149A00, 0xB6D75A5F, 0x96A757A6, 0xA050617B,
-0xB007D141, 0x12A377B0, 0xC4E8D5F5, 0xE0C74B95, 0xC13C06F9, 0x7996213D, 0x9AC68F45, 0x65460B95, 0x99A7C292, 0x889C9C6C, 0xA4EDD440, 0xB0693DF, 0xFF50E490, 0x507A977, 0xE4147ED0, 0x80F19505,
-0xFCC5066E, 0x74B64319, 0x103A1EC6, 0xB288507E, 0x56A779A0, 0x7C9056AA, 0x381294FD, 0x5D194EC1, 0xF0C19B1F, 0xD60A1DE1, 0x94C0C4DF, 0x7E66824B, 0x69670667, 0x461520C7, 0x7466071F, 0x96FD1A00,
-0x10C4EA65, 0x93DE045, 0x6F01E2E5, 0xB196946, 0x504246AD, 0xB45E0F4, 0xABF1194, 0x84C94D5A, 0x111D27E, 0xF6951BBD, 0xE081166B, 0x56699965, 0xA1F16C54, 0x46107A64, 0x821C3491, 0x88D86E5B,
-0xE5D2C7CB, 0x5203432F, 0xE616332B, 0xF9854938, 0xD2736742, 0x671061F7, 0x9248BDB8, 0x544106E2, 0x6134280D, 0xC65E091D, 0x5599E860, 0xECC245B4, 0xFE4564E0, 0xD500F4F4, 0x59FDC0ED, 0x44530376,
-0xB152767E, 0x6A074351, 0xC565DE, 0xAD995844, 0x57F1D099, 0xB252D484, 0xD1984D8E, 0x5196A66, 0x2C59E167, 0xC0E9055F, 0xFD196F06, 0xF5F0C1C7, 0xFAD05152, 0x2126E0F5, 0xE9F42911, 0xC8339895,
-0x8D405BD, 0x50E94377, 0x443AF945, 0x5011293C, 0x76A297AD, 0x1EE440C5, 0x35DAD9A0, 0xA4419590, 0x165BA619, 0xF60B9A65, 0x1F89444A, 0x71B0255, 0x476DF0A6, 0x61D4A1A, 0x469F9DB0, 0x32355C8C,
-0x86091D46, 0xE795060B, 0x2296305C, 0x6E41F80, 0xF490E594, 0xF4E25699, 0xBF65AC50, 0x4A7E75DB, 0x1015F458, 0xB04ACD9E, 0xEE5478F4, 0x7F630C53, 0x4E49F5F0, 0xCDA3319C, 0xBE116D20, 0x4589D9E9,
-0x7435434B, 0xFDD96656, 0x5F681E4, 0x99AD1C50, 0x552A5401, 0x68F04549, 0x73B66D2, 0x1F86D101, 0xD041160B, 0xF906FFA5, 0x689676DE, 0x5F4780BD, 0xF0939AA5, 0x1F0B5F40, 0x501B3F, 0xA8C193B5,
-0x8C73CC33, 0xE24F7CD3, 0x9090A565, 0xA990D59A, 0xA1C14877, 0x946D8180, 0xF8343124, 0x14C1E915, 0x4919CBA5, 0xFF55D001, 0xF4E152A2, 0x46687EF0, 0xF05ABD48, 0xB7B37460, 0x1474C58, 0x94E490E8,
-0xB9E44183, 0x78D32DE1, 0xC8E4489F, 0x5BDAA45B, 0xB5610147, 0x5162A5F9, 0x5D6191C8, 0x4595C0A6, 0xA57DC996, 0x6D9D0D1E, 0xF58BC767, 0x1A0E5509, 0x409BD2D7, 0xFF5C135, 0x33B7579, 0xEF035600,
-0x779152B, 0x50B091B7, 0x363A3162, 0x491069DB, 0x9C4E0C5F, 0x945A1B12, 0x5490FF00, 0xB5A0B400, 0x1F82D64A, 0xB2C7426, 0x329CDA73, 0xA9995AA5, 0xF65876B4, 0x1E4E890E, 0x857AB576, 0xA52E054,
-0x1D11D88C, 0xA35895B0, 0x6F196890, 0xC7143E70, 0x6BE401FE, 0x111BA961, 0x66070AF5, 0x5F07130F, 0xA15061B6, 0x8C73CD32, 0x6C6481D6, 0x6AD5A6C1, 0x14A5872C, 0x46B90BE4, 0xE5586946, 0x3163C58C,
-0x1431B550, 0x91E94F09, 0x66F5C24B, 0xEE40461E, 0x97593992, 0x19E059F4, 0x94F92E05, 0x8CC445EA, 0x403D64A7, 0xB0FE5D8, 0x9C9B5C00, 0x7EDED9A4, 0x7CD393B5, 0xF1A50DA, 0x372B4077, 0xBF9046E7,
-0x56261E6D, 0x51232570, 0x40153CAB, 0x9B6F6712, 0x51C0160A, 0xB990052F, 0x3788B955, 0x555064A0, 0x6F0B5B05, 0x9776666B, 0xAA069B45, 0xAFF81440, 0x91E4DE1A, 0x80D5C9C6, 0x1E9E5DA4, 0x2B65B450,
-0x1213362B, 0xD766654A, 0x461346C, 0x197B2441, 0x5F9547EB, 0x15AF0506, 0x464C8D9D, 0x4021957A, 0xFE54E413, 0x13295035, 0x145E0D2, 0x4D4854E9, 0xBE50919B, 0xA76CF940, 0x91E1406E, 0x9A655A9A,
-0x3E65005A, 0x1C0B6666, 0x2611AC57, 0xBE7D1442, 0x311E3FC, 0xA4858185, 0x6E54D2A1, 0x9140A5DE, 0x844EC85, 0xF4A81540, 0x1159BF9, 0x2896D07D, 0xD5E690EA, 0x40A47D47, 0x6F672C16, 0xF1611217,
-0xE4096F65, 0x621D05EF, 0x9040979F, 0xC1C5CA46, 0x5B9606B5, 0x5101767A, 0x448C9568, 0xE4983D12, 0xCBC8B5E9, 0x1B15E402, 0xE1C60E15, 0x44E68419, 0x90D48DC, 0x7A9042BB, 0x151285D, 0x676B2613,
-0x121644B4, 0x920F52E3, 0x56DE09E, 0x6E6607A4, 0x5E560B0E, 0xC20B7616, 0xAF464146, 0x8C5C2835, 0x474BC2C6, 0xBC6419CA, 0x5C4353F0, 0x31392547, 0x504295AA, 0xCC593363, 0x1C0C6DBA, 0x4D71B7F,
-0xCC593633, 0xE71A250B, 0xE8CC599D, 0x894FA695, 0x4EC3513E, 0x39C0C645, 0x7B526034, 0xF91E2E06, 0x1A663699, 0x202599EF, 0xC1D1E195, 0x75E600FF, 0xD0D1ECDC, 0xD6DB6050, 0xE0E5016F, 0xF0910B15,
-0xD68C6917, 0x6F1A3D00, 0xF5D61BA6, 0x336D1CA5, 0x7888941D, 0x78D0912D, 0xE1D1C34C, 0x592E4C41, 0x1539F804, 0xFB019650, 0xD5E50609, 0xE8C553F9, 0x411BEBD, 0xE580D1DA, 0x1CACD693, 0xAF15BCE1,
-0x1104909C, 0x4B3B4686, 0xB3A31251, 0x50F5665, 0x1D4EC1D3, 0x2830D712, 0x401F0475, 0xC089D4DD, 0xB2425701, 0x40076F65, 0xA950F5A4, 0x9F9990BF, 0x70214431, 0x22753C31, 0xCC7369D9, 0xE9919061,
-0xF45A1F1, 0xBD0690D0, 0x79A6C180, 0x2FDD105A, 0x584296CA, 0x116F906E, 0x58484C5C, 0xDF9B10E6, 0x5184486D, 0xE9D89D90, 0x69B05ED2, 0xD9841D2B, 0xC2C6C5E6, 0xF88947CD, 0x90696469, 0xC5C0C6DF,
-0x677B0640, 0x4B479BC5, 0x83D1B811, 0x5B47E440, 0x1A419DEA, 0x3DD8D4A9, 0x8752B0F3, 0xD1D18189, 0xCCA632CC, 0x725CCB73, 0x6325CC33, 0x14464A25, 0x5410303F, 0xF8C12F85, 0x39D205E5, 0xFE850549,
-0xC68710B5, 0x471F7842, 0xD34A4C6C, 0x4742D188, 0xBFD50A44, 0x1987777C, 0x5B61C12, 0xB2530677, 0xD995A916, 0x75B496A0, 0x31E61E7, 0x53939DED, 0x51705B00, 0xBA5A4046, 0xFF14A4, 0x2599DCF8,
-0x7D47F02E, 0x9B959064, 0xDCD4E014, 0x38C22DD1, 0x65647C88, 0xD4606066, 0x3551E0D2, 0xD06157FE, 0x1131260B, 0x8B81E454, 0x1E1865FF, 0x1966A524, 0x4B4684D8, 0x3450F1B6, 0xE4FE41D1, 0x1D31F1E6,
-0xC47C1F4A, 0x94656AB, 0x45D101B, 0xB681B712, 0x9BD09215, 0x77924154, 0xFCECD4A0, 0xF0F1C72D, 0x74D0834F, 0xE4908117, 0x65E0365C, 0x955A09EF, 0x6E744349, 0xF4692B13, 0x34750F6E, 0x94D198A9,
-0x839C2DB2, 0xED929580, 0xEA609E65, 0x1A15F8D1, 0xFE00D6AD, 0xD5DC0141, 0xD90D3995, 0x6E6640ED, 0xA9B443CF, 0xE50681D9, 0x3F705659, 0x5F5E4844, 0x9A254A5B, 0x26618195, 0x8B945DAD, 0xDFD4E490,
-0x674352C0, 0x9967C1C0, 0x5990E995, 0xDEC544BA, 0xA7DA444, 0x16457E8, 0x142DFE04, 0xA4D5C084, 0x13172539, 0x84FC3590, 0xA9734748, 0xFE07F451, 0x42119AED, 0x8F087916, 0x13A65D1, 0x82856E75,
-0x3511B1B9, 0xA61BD018, 0x8619B893, 0x40BD89E5, 0x9A15F640, 0x4052464F, 0x9D87C1E0, 0x972C58F0, 0x84D8ED54, 0xE64D9C5D, 0xF421502F, 0xF24864, 0x59B8A154, 0x9A2596DF, 0x1441E6FA, 0x1BE46F90,
-0xA8915051, 0xA5B62611, 0xF582841D, 0x825362B7, 0x55F367E, 0x415263BD, 0x3E463930, 0x68754A06, 0x1117F280, 0x8B16B855, 0xEC75E1C4, 0xA0771117, 0x669DB850, 0x6D171238, 0x47167213, 0x90B21746,
-0xD92649C8, 0xCAC6C6F1, 0xE392C769, 0x60E7939B, 0x14130475, 0x56001A7D, 0xF657028F, 0x6F6819E, 0x29974C33, 0x49A57C5C, 0xD590E890, 0x6FF1116, 0x132B7665, 0xA0C60B56, 0x9844DF41, 0xA1B103E,
-0x44845EEF, 0xA6D2F5D5, 0x55FA4600, 0xBF611264, 0x6AD53610, 0xED594A11, 0xCFC15015, 0x96264247, 0xA4F1D3DA, 0x257EC166, 0xB8D6C114, 0xEA90CF4F, 0x6D5A804, 0x3F0695E8, 0x9C90BCE9, 0xAB4701D1,
-0x136357B5, 0x994D9E0E, 0x121652B2, 0x6D9FD261, 0x5CCC9733, 0x873B95E5, 0x905E9C80, 0xA051BA6F, 0x5F478589, 0x197AD19A, 0x50015DE9, 0x5BAE412F, 0x4111162B, 0x738F386, 0xE4835BAE, 0x5B55A2D0,
-0x5E4B038, 0xF9F91014, 0x8FCBD1E1, 0xD98B4743, 0xB9955DAF, 0xC5373DCD, 0xBD1C8114, 0x250532F1, 0x24504E0E, 0x62D7C50, 0xE8D44154, 0xA8944044, 0x9D669995, 0x530C6FFA, 0x65167BA7, 0xE641BE,
-0x811AB5D2, 0xD18A9D09, 0x6F064045, 0xBF902991, 0xC54F07FF, 0xF6962B11, 0xA4819FDB, 0x60F46A54, 0xE451815B, 0x7C197B, 0x9D44DA0C, 0x869640FF, 0x18C44D18, 0x40247F66, 0x5221F711, 0x510196FF,
-0x353261EF, 0x44490E0E, 0xF994E956, 0x926C45BF, 0x7D96855F, 0x84D4C98A, 0xE06BF456, 0xC21B1641, 0x4B5CB493, 0xDA90D366, 0xE8964D44, 0x197C75DA, 0xD6D46333, 0x465E78A1, 0x1942461E, 0xC1356656,
-0x58D631CC, 0xD773384C, 0x4A9940E5, 0xA0F97414, 0x53532327, 0x78107AB5, 0x3C091BA5, 0xA6469144, 0x44E45841, 0xC50D5842, 0x4A199678, 0x46365DF8, 0x5463E87, 0x8D2C1151, 0xFF5895, 0x2FD231C8,
-0x6C9CD2B3, 0xD74243B2, 0xC4409CDE, 0xB8E0411F, 0x26670F05, 0x5B6697A9, 0x55464A2B, 0x2E0F569A, 0x4660E25B, 0xFE500715, 0x9C683532, 0xCB0C5949, 0x806C6592, 0x679B0156, 0xFE005F15, 0xA540FE50,
-0x1F281510, 0x906D4F00, 0x57052C80, 0xA95BC0B5, 0x6C98C5CB, 0x44581E4E, 0xAD9D4846, 0x8352C7DB, 0xB21E50D9, 0xEB4607B7, 0x99AC9C77, 0x1509D9FC, 0x5460133E, 0x4244EE5A, 0xEF5A259A, 0x3439461B,
-0x6B940B05, 0x59E94484, 0x5062B45E, 0x859B9363, 0xFDE48194, 0x325362D2, 0xE460FB46, 0xD78B98E7, 0x64A3D346, 0xE5B06277, 0x9F655A41, 0x676B5302, 0xEA950B7D, 0xFD158A, 0x775362B4, 0x75C21938,
-0x861EB9D2, 0x589532CC, 0x1025190B, 0xD46B06FF, 0xB50E7C44, 0xCDD7A372, 0xB5B96100, 0xF2B491A1, 0x5E9960F4, 0xF1CC582E, 0x89C99575, 0x7AA1475F, 0x7B66C124, 0xD0D681EF, 0x44156E08, 0x8F1F1056,
-0x6E67D240, 0x9A4C4951, 0xD451E4A7, 0xEA941B13, 0x7E470070, 0x85422E7F, 0x5B9690FE, 0xDE06E626, 0xC8854A15, 0x5A05ED4D, 0x80F46E1B, 0x9011F302, 0x4493D2D8, 0xE50D9D68, 0xFB764400, 0x64D1816F,
-0x9965A616, 0xBF011811, 0x155B0252, 0x3D9DA411, 0x58D96D90, 0x5FAC14F, 0x34E09F47, 0x7470303D, 0x35316E5B, 0x84782D19, 0x6074B114, 0xDF85124F, 0x5B47B723, 0x91327673, 0x31A3C548, 0x6D04F9,
-0x65DA62CC, 0x779B6270, 0x9037699, 0xD59A061B, 0xD140F5D0, 0x546F04E5, 0x197A6193, 0x150F46FA, 0xE0592151, 0x54A9D0DB, 0xAA55D851, 0x50616F64, 0x35307994, 0x1198C72F, 0x472E79D1, 0xFF01981,
-0xF6195AFF, 0x58E66219, 0x69D3D67C, 0x3395C832, 0x5DA31DFF, 0xD66E7583, 0x802979D7, 0x1C419805, 0xA596916A, 0x6A464414, 0x64702547, 0xD31AB704, 0x91E6C21B, 0x41AF55, 0xC600DF64, 0x2D162960,
-0xD0D9AC64, 0x921C6378, 0xD0051C18, 0xCC26969C, 0x3C856899, 0x56A9D3A3, 0xC8CCD966, 0xB42552E2, 0xFF055B5B, 0x4AC7E641, 0xE747D0DD, 0x45A5F75D, 0xFD960205, 0xA60391FD, 0x5EAD8484, 0x13D0E563,
-0x11011BBF, 0xF4A4414, 0xCB5D0939, 0x1D6DE804, 0x2D6DF850, 0xB7E11431, 0x8B253410, 0x24D7A918, 0xA6591F6A, 0xFA0545, 0x10D3421A, 0x4E4CE453, 0x99C48C3D, 0x71D1C0F, 0xB4B94045, 0xE0815D05,
-0x9D854214, 0xF5F06136, 0x1A58FD15, 0x4742A650, 0x7E66464A, 0xCAC5D1F2, 0x99A91441, 0xB06115FB, 0xC0F43522, 0x9CE9CDC8, 0x59EA404E, 0x5B42A707, 0x16914BD, 0x872DF087, 0x4114AE9E, 0x3B1284FE,
-0x5E5E5808, 0x4680D66C, 0x364246C2, 0x65C2462E, 0x421C78A1, 0x1EB8D5EA, 0x28F24C55, 0x5021670B, 0x90BD7875, 0x71385CCA, 0xE7F07411, 0x1F11A366, 0x5FDFB852, 0x4C4DE05A, 0x9E0D9AD6, 0xE0953C84,
-0xE5CC0641, 0xE01BF406, 0x51E4072A, 0x4D66B8B5, 0x272DAD04, 0xF2C3986D, 0x6D680158, 0x464B0755, 0x550047FB, 0x74B41D1, 0xAF58676C, 0x7A655241, 0x64B9505B, 0x4D1D4D8D, 0xFFA5909B, 0x2F0754C1,
-0x4052256D, 0xB9D68609, 0x66615EB, 0x8A3D64D1, 0x2392D1E5, 0xF0A15ABD, 0xF7375B01, 0x699291A1, 0x607D0154, 0x6F19CDC6, 0x869DAC51, 0x11164726, 0xB0B50B1A, 0x9550E320, 0x257FF450, 0x3264669A,
-0xDF5D405B, 0x819855E7, 0x6B97C0A4, 0x88394945, 0xBCF14411, 0x6ED499, 0x570193E3, 0xFA346401, 0xF479095, 0xE6E6850B, 0xB2F1615, 0x95E2160, 0x144D8A3D, 0x3932D186, 0x5B64F801, 0x1F0746E7,
-0x1443EF98, 0xE8444E49, 0x4740EB50, 0xB9035B58, 0xD3D21B3E, 0xB8767699, 0x67D78366, 0xBD1187CB, 0x6C5C0C1C, 0x906D3E13, 0x9E1E6C49, 0x400598ED, 0x529106FD, 0xB4C10D2D, 0x740DC334, 0xE99458A0,
-0x94C8D572, 0xD7D18228, 0x13E566E, 0x1BC3D629, 0x63136172, 0x51A1B10, 0xF305F4E0, 0x679F6813, 0xD38356EA, 0xD669C856, 0xC5CE5A4, 0xC738CB74, 0x567DB480, 0xA65F9440, 0xE207176B, 0x9D5E4A88,
-0x1EFD6440, 0x1095738A, 0xF005646E, 0x19E1870, 0xEC481545, 0x2E598105, 0x55F7439F, 0xDBDB41A1, 0xE64F995, 0x6A9164A9, 0xA5191E04, 0xF4D1413C, 0x646EA056, 0xE3911263, 0x20117A65, 0xF9059304,
-0x48366754, 0x12166B67, 0x868A677, 0x46DB80C0, 0x1878D69A, 0x60D26DC1, 0x405EE804, 0x84D990BF, 0xDAD54606, 0xD93365CC, 0x93B2971B, 0x31269151, 0x5F0E58C2, 0x19B96390, 0xA1AD444, 0x90286D94,
-0xB1D10B7E, 0x40FAA451, 0x8276815E, 0x8669F400, 0xB5A61B12, 0x65FC9B8, 0x446AA55, 0xA11D8378, 0xAD9F4468, 0x21266E1E, 0x868544FC, 0x67520318, 0x858DD0EB, 0x86D4906F, 0x51D3A9A6, 0x9767857E,
-0xD6C99C68, 0x33CC2667, 0x660795F4, 0xD1F10155, 0x86D90D16, 0xE66A1603, 0xF5F86440, 0xE98E4504, 0x17424B9A, 0xB0839F59, 0xE70438F0, 0x432A7560, 0x92D89FC5, 0x37C42399, 0x11617570, 0x249605BE,
-0x101BE265, 0xEE905053, 0x4540DA59, 0x9729A4FF, 0xEE850607, 0x9FD15248, 0x50E5B323, 0x7579F946, 0x498459AF, 0x919C2C1E, 0x6AD78374, 0xB1647421, 0x9EE0B603, 0xA6D35E9, 0x96696C9E, 0x587A5003,
-0x49454A9, 0x6D6D9393, 0xC98677E9, 0x4095C2EF, 0x9C608505, 0x15940C2C, 0x5B60D2DB, 0x1C8151A9, 0x60F491D2, 0x2B670453, 0x4F1964BA, 0xB4430B51, 0xA9860454, 0xB0E41E46, 0x1406B7E3, 0x562701E4,
-0x20D1F6A1, 0x5352A966, 0x64E1D62F, 0x92C6ED07, 0xF882561B, 0x99195A04, 0xC934E3AC, 0xD9B91810, 0x474B04F4, 0x3D9A66, 0xCA9C10AF, 0xE1942916, 0xA125355, 0x32295C8C, 0x675A64E1, 0x9D906494,
-0x6C60EB1F, 0x7874C1BC, 0x1CB53038, 0x56D0F9D1, 0xB1EB954, 0x7C781540, 0x5323619E, 0x6FE055A9, 0xE7440D22, 0x7431A347, 0x6F1BE146, 0x1015BF00, 0x62381905, 0x7025B440, 0x176AF050, 0x5D270EF1,
-0x46AF4C18, 0x4B86D9ED, 0x8B177010, 0xFAD09606, 0xBFA15E50, 0x4701D72D, 0xA298A55E, 0x865CA950, 0x9B5B1227, 0xE0B9175, 0xF481F1FC, 0x7E5D0440, 0x1A697FE5, 0xDE84C5F0, 0xF9975303, 0x4CB46C54,
-0x3ED0D666, 0xE6B07065, 0xF0661263, 0x117C93A0, 0xD56DB4E1, 0x2E5E0E5D, 0x873DE11F, 0x5512413E, 0x5E9CE404, 0xB4C10B7F, 0xF990E956, 0x84C95E95, 0xB5E04B1F, 0x86815BA5, 0x7A5CB26C, 0x42F41F91,
-0xF4592E03, 0xF51F1300, 0x99D7810F, 0xB16051A7, 0x5B57B03E, 0xEE590B55, 0xC014FDD6, 0xF9C04505, 0x1A669669, 0x984C5F84, 0x1A6FD2E3, 0x1A075E5, 0xD80E450C, 0xD66942B6, 0xD050E31F, 0xBA605440,
-0x50E7856A, 0x406E53EF, 0xF01191B, 0xBF055243, 0x9B25E9E7, 0xF892A157, 0x2D598957, 0x85896B6, 0xEF44584B, 0x170667FB, 0xD1017F38, 0xCEC58687, 0x443C6C99, 0xB5B83D3, 0xFD05D91B, 0xA6D72DD3,
-0x6C98D9D0, 0xD66990E0, 0xDE850B0F, 0xE9964609, 0x8605B94B, 0x6DC0D196, 0xB194A5, 0x6F13424D, 0x9ED13215, 0x7064249F, 0x5751E0B0, 0x166F26D6, 0x559EC2F8, 0xE12C5C71, 0x59898D15, 0xBC015A05,
-0x90D18B0F, 0xB0E8649C, 0x170B98D0, 0x8181F491, 0xD990498C, 0x76312925, 0x93B53402, 0xCC1C4B54, 0x2F3F5006, 0x56EC4F10, 0x1AE501E4, 0x6F4D40DB, 0xA3525D69, 0x1329E491, 0x400AF995, 0x2D5C4806,
-0xB091667D, 0xCC6C1627, 0x425CD9EC, 0xF552931A, 0xA6D46580, 0xF4AE50E0, 0x1979909A, 0xF1750058, 0x3365CCD9, 0xBA166500, 0x86779739, 0x67F2611, 0x55109A0B, 0x51016A65, 0x1160BBF, 0x5A6C1401,
-0xC4DC9984, 0x33534244, 0x4057113A, 0xF1F431C1, 0xF8A44BE5, 0x1A976851, 0xB9B5C28F, 0xFE099454, 0xB065B8B5, 0xC0217707, 0xF3831BD1, 0x9757401B, 0x1D293580, 0x7A951200, 0x20615AF5, 0x6272757,
-0xE0FC2907, 0xD860B5C1, 0x985F89C, 0xC25C2C5F, 0x7F941C01, 0xA95A450A, 0x44D89A95, 0xCE1C0C58, 0x194184F9, 0x579BB371, 0x90111F40, 0x5F037075, 0xBB516996, 0x4EC9791, 0xCCEC9C44, 0x5D8404F8,
-0x48FE9D1, 0x4792E3E2, 0x6F35E381, 0x5F0350FB, 0xFF6C56, 0x6050A5F7, 0x589625C9, 0x8F79061D, 0x11D0421F, 0x9061F184, 0x504662BE, 0x8FC7D190, 0xB4404627, 0xEA1F1B03, 0xAF59D0EA, 0x79E50E04,
-0xCF80445D, 0x4191D38B, 0x8F51328, 0x6653E282, 0x8C5C3573, 0x90E05929, 0xC5910D2B, 0xAE479D46, 0x2F7C1610, 0x24B1637E, 0xCB001F55, 0x118384B5, 0xA3421710, 0xAA0725F4, 0xE9C0959A, 0xE5E74114,
-0x4A44CB25, 0x1763BD90, 0x1B095404, 0xCF09D095, 0x5B8344AC, 0xC5C0F9D1, 0xA7615441, 0xE990474B, 0x63536723, 0x1E305BA6, 0xE56B066B, 0xCA817916, 0x9F798578, 0xD7692E06, 0x56F01481, 0x37215100,
-0x17111401, 0xF890F955, 0xA68D68D6, 0xD64A06F0, 0x9F65D9D2, 0xBE90095F, 0x9967072F, 0xE01C947B, 0x12235709, 0x589DFD, 0xBB154A41, 0x2D5DC01F, 0xC58B5C08, 0xCB64DC4C, 0x9F9393AC, 0x7303467E,
-0x599EC984, 0x46E111FC, 0x7A679404, 0xA90647FB, 0x40EE949A, 0x77076E08, 0x10736423, 0x53D90AB, 0x1F510BB, 0x85CC253A, 0xAA905666, 0x45EC0B4, 0x835121F1, 0x2E5A4247, 0x1D8664A0, 0x51442CE0,
-0xDD88451E, 0xE85C0113, 0xFA409601, 0xA4211627, 0x459F50BA, 0x984C458A, 0xE65A60D7, 0x958143B2, 0xD3835BFA, 0xF727F110, 0x4F074354, 0x11113D3D, 0x1558312B, 0xFF08E6D6, 0x89BC5910, 0x99F99287,
-0x40D4183F, 0xDA66A550, 0xA45CE279, 0x7F97A251, 0xB1B025F, 0x70772265, 0xF0E21E16, 0xE79959B5, 0x496669C6, 0xECD00716, 0x5619ECE1, 0x6E5C11F, 0x1101FFF8, 0x55A01DCB, 0xA5053970, 0x3D448855,
-0x9F92E540, 0x35B8A4D0, 0x84119CA7, 0x5429406F, 0x90E05B56, 0x3752E1D8, 0xE1B42D87, 0x9454B0F1, 0xDD382505, 0x1904ED6F, 0xD94D0C58, 0x7C52031F, 0x14B06706, 0x79928799, 0x40149BC7, 0xE9DCD6D0,
-0x104F4D1, 0xD9663448, 0xBD85D003, 0x358CCDA3, 0x66F5412B, 0x6E65066A, 0x6E19B161, 0x55D1AA50, 0x785C9D30, 0x162530BE, 0x7AE64548, 0xA51B9B0E, 0xF9A56400, 0x32CE35DB, 0x17285370, 0xB0117B76,
-0x7CE04741, 0x9D0C51A4, 0xBF004D55, 0xAAD5AC11, 0x6599CB41, 0xFBD09444, 0x936291E5, 0x2D5EA056, 0x1129656A, 0x7FC09506, 0xD44149C8, 0xBA750352, 0xD2431943, 0x7967E182, 0xB813C497, 0x24219465,
-0xC43C9169, 0x6FD81483, 0x6152E3C, 0x59947B6, 0x27F90D12, 0x54046D2A, 0xD521309B, 0xE6653921, 0x9164F4F1, 0x1521FB4A, 0x61C54C38, 0x416484BF, 0x2776B11F, 0x59A9D884, 0xED806B54, 0x1BB0D59B,
-0x4E8D91A1, 0x782FD131, 0xC05A0377, 0x7A121126, 0xDFC5D0E0, 0xEF859E05, 0x7BB60515, 0x5DD26D89, 0x2E291157, 0x9F2F3530, 0xC08156A9, 0x6D91CDC5, 0xF1548117, 0xB52517FE, 0x2053E947, 0xAB5785C5,
-0x479A91E0, 0x9F90FE01, 0x312484DE, 0x441BC1A5, 0x66B501F5, 0xC4617A5B, 0x916BB0F4, 0x20657C7A, 0xE25390FF, 0x1F3861B4, 0x550586AF, 0x6B8599D4, 0x9F4294EC, 0xF4D48147, 0xD0965233, 0x1E05CFED,
-0x59176903, 0x2D2976C, 0xCD33338C, 0x1441E4AD, 0x4196C088, 0xF6079403, 0x969DC030, 0x64990D29, 0x2453F372, 0x8658909B, 0x458046FB, 0x11A185C4, 0x58FC1144, 0x31E641A, 0x8E47709E, 0xF1611277,
-0x61F907A, 0xB7100F5C, 0x5A170B19, 0x160557E, 0x55B39D0, 0xC0C556EF, 0xBBF50299, 0xD1D23E12, 0x69588543, 0x4546890, 0x90FA4115, 0x11C5ED8, 0x68B74789, 0xC06F0117, 0x172790AD, 0x606DAC54,
-0x8D488154, 0x8444EC98, 0x9A6592D3, 0xE5E15040, 0xDB031762, 0x2F099450, 0x404F0F5A, 0x1D09E9F4, 0x56045E1F, 0xF9C99F00, 0x136C6599, 0xB0C36E91, 0x1C2835B0, 0x60BE6B5, 0x462FB4D1, 0xCB19B6D7,
-0x6C48C4D9, 0xD0D19B69, 0xA5E544FC, 0xE4851E31, 0x41E464B8, 0x59C6F9D4, 0xF0A6527E, 0x5333D1E0, 0x4862F606, 0x72370631, 0xC2B490C5, 0x4643DE5C, 0x54F9C489, 0xFC526C40, 0xB0A56895, 0x45C1C134,
-0x768F1981, 0x6E295005, 0x950B6964, 0x4B4CD5E9, 0xEB131245, 0x447F844, 0x131B1D76, 0x99981A7, 0x500B1F0A, 0x91011B1B, 0x64E64709, 0xA45C8145, 0xB53001D1, 0xDB462885, 0xBE06D015, 0x4858541B,
-0x6C86C3B5, 0x70A1967B, 0x969B0414, 0xEF485C04, 0x7FE151C1, 0xEBE7F444, 0x5341990B, 0x71D58060, 0xF5A61E10, 0x79F2C53, 0xE5E41F00, 0xBFF4505, 0xE01BB4E9, 0xBFE3905D, 0xDB7664D0, 0x956A065B,
-0x12894E1, 0xBF14B401, 0x30F7A97, 0xA85C97E0, 0xF40643E9, 0xF8506706, 0x9B9B06AB, 0x5AA05995, 0x45448E3D, 0xE1D1907F, 0x24F4C22C, 0x82C2959A, 0xBF065243, 0x8785812D, 0x9A94A916, 0x315B2C60,
-0x523291D9, 0xF2991609, 0x2830579C, 0x999973CC, 0x4B14C098, 0xC1C5053, 0xD1C1C68A, 0x64B67F3, 0xD09B801D, 0x6A7C1044, 0x48580954, 0x859F095A, 0x46533620, 0x592C852D, 0xA5419141, 0x4D16A1D0,
-0xB0B153B, 0xE1944E4A, 0x21371633, 0xCEC9C4E4, 0xA5D70E08, 0x4E4C4DD, 0x591CAC04, 0x5B021DBC, 0x9967CC33, 0x81C0E5FD, 0xF651C104, 0x966760C9, 0x95E1303C, 0xEC5C6440, 0x50BF9F4, 0x3561C31E,
-0x45A096AD, 0xFF92500B, 0xF6598904, 0x19F9C50B, 0x60C64D18, 0xD640584F, 0x2194E1F3, 0xF031163D, 0x101B56E0, 0xDE5921DA, 0xBF01FD1, 0x706FD94, 0x34CB30CE, 0xB857061C, 0x5225B609, 0xC43C3135,
-0xC03B9350, 0xF001676F, 0xF0C18767, 0x6611391B, 0x4F89A46, 0x1F42441A, 0x649F0F46, 0xB5E0015E, 0x7966830E, 0x9740F4F7, 0xC8CC4451, 0x59BD404B, 0xCC9435E1, 0x3B15B2A0, 0x123C1607, 0x59468D90,
-0x40E0D749, 0x14C1F46E, 0x448BD6A9, 0x659AF556, 0x50E7435B, 0xAED2933C, 0x56051308, 0xCE146E50, 0x1C904F10, 0xEC485E0D, 0x169A936C, 0x51633605, 0xDB57A830, 0x62957AB, 0xF4F14448, 0x45CAD5D,
-0x695CC006, 0x104606B9, 0x81D1D86D, 0x8649F050, 0x20D1DB56, 0xE5E53CC0, 0xCB470256, 0xC629E494, 0x49385F8F, 0x65056E31, 0x9C86F095, 0xC8CC9865, 0x869D8118, 0xD1021F11, 0x87DAD1E2, 0xAF04D500,
-0x4518464, 0xB857B085, 0x4505E834, 0xB1E25F04, 0xD8DD9084, 0x35978C33, 0x11A64257, 0x991A1E41, 0x66B06752, 0xBAF41443, 0xBD4A4544, 0xD6753B66, 0xCAC6C639, 0x154480FD, 0x99D19BC, 0x546EE442,
-0xD0C0193E, 0x7491E21F, 0x24B0B3C7, 0xF981D501, 0x74438B58, 0x54C47391, 0x9EDD1180, 0xE697874F, 0x2E41F8E5, 0x468FDD9, 0x4F1E439C, 0x5542EB81, 0xA554FAE0, 0x1E43E2B0, 0x606D5898, 0x6587C4E8,
-0x6B86C511, 0x6050A6D3, 0x1C523485, 0xF2530396, 0x4A2D5884, 0x6FD09193, 0x29978F84, 0xFE490999, 0x9189675E, 0x99277401, 0xDD6F2494, 0x8CD39344, 0xB5783D9, 0xC859EE, 0xAC701503, 0x8FC1D7E1,
-0xA0854317, 0xD09E8444, 0x4510F2BD, 0xED484CD4, 0xDE5D0C1C, 0xC0854367, 0xD1642134, 0x503876E, 0x7C046A5E, 0xE5644035, 0x3D043173, 0x5DE8991, 0xA603D6D0, 0x70644B1B, 0x41C3F55, 0x799EC50,
-0x5C1D005A, 0xB54027BB, 0x501703FB, 0xCE5E2D84, 0xC248776B, 0x34C31DB1, 0xD08146F6, 0x6194D488, 0x902D95DF, 0x56239699, 0x79566996, 0x46366797, 0x2075B449, 0xE619904B, 0xF1FC491D, 0x4026925F,
-0x85CE994, 0xC62D651B, 0xE0F50B4, 0x60976F55, 0x5B7706A6, 0x49471F41, 0xF6D91352, 0x551A062F, 0x4145CFCA, 0x55B14234, 0xABF12531, 0xB915F0F4, 0x8AC7E275, 0x4679C12F, 0xE21353EC, 0x693602A5,
-0x4BC256AB, 0x463D64A6, 0x5E7C6B8, 0x143983F0, 0x3646871A, 0x601D1FE, 0x4D4601FC, 0xB489D0DF, 0xF7814046, 0xE5A50BA7, 0xB34A116, 0x707C4151, 0x804466BB, 0xCF450C5C, 0x1329B9F4, 0xFE400655,
-0xF4A1431B, 0x51D3202D, 0x74B0A444, 0xA6F1612, 0x6C584C8C, 0xC014F665, 0xD3982957, 0x36C136D1, 0x5C45CCD0, 0x9F459882, 0x91A753A2, 0xA9752C4C, 0x421B4297, 0x776A2313, 0x35397906, 0x48065762,
-0x1129B440, 0xAF410B5D, 0x7F9582D2, 0x4B51E650, 0x891D0D77, 0xE59909E5, 0x705D234B, 0xC3DA9055, 0xF1B11501, 0x44BC94D9, 0x1FA55C48, 0x2A422657, 0xC41091A1, 0x18970E74, 0x7699099, 0x10616AD7,
-0x1AE19F10, 0x475A6C31, 0xA27257E9, 0xC484582C, 0xD699665A, 0x17C94C18, 0xE1569B, 0x11855ABF, 0xA580D607, 0x64C18B7E, 0x7030A605, 0x2B3660D0, 0x165BE103, 0x809E1867, 0x47407FD7, 0x34E4858B,
-0x90ED740B, 0x1B43C2F5, 0xF451527C, 0x5723E1C5, 0xE1D1065B, 0xA51A9DF4, 0x7F099A85, 0x4157030B, 0x1D9F60B8, 0xC11491FD, 0xFE850A55, 0xB990431F, 0xA0C78D16, 0x6F5A4D40, 0xFE4411E, 0x34426B56,
-0x5014FE0F, 0xBD5DC51A, 0xC63637C3, 0xE1B459B2, 0xFA871B05, 0x4544D0AD, 0xE5E5093D, 0x3306C5DD, 0x47C33484, 0x6F1E146, 0xE4974F1, 0xDD48050F, 0xAB151E12, 0x5169FC92, 0x5055B27A, 0xD142AD5C,
-0x4B16B0F4, 0x312BC955, 0x75B10FC, 0x4A9E0458, 0xA65D34CE, 0x90D366CF, 0x10099D29, 0x8B358419, 0x87D2B5DA, 0x31162EF5, 0xF61989C5, 0x1F4341F8, 0xBC4C6D9C, 0x11A759, 0x9553A21, 0xCFC41530,
-0x1F05B610, 0x5B2F64C0, 0x117F1124, 0xF3D24D9, 0x44447874, 0x74351A06, 0x64C48979, 0x63C185B, 0x91F094A4, 0x4E1D1C48, 0x969103AE, 0x9CFC6067, 0x2FC94045, 0xED3844C4, 0x676C935B, 0x6E2091B5,
-0x2D9744EC, 0x6A111227, 0x78DB90E5, 0x8045587E, 0xC4E4591E, 0x479BE1E0, 0x5B667499, 0x50E0493E, 0xFB54819C, 0x431491F8, 0xD3EF50E0, 0x7363926, 0x50FF6440, 0x44DC6C9D, 0xF6D9965A, 0x548499,
-0x150A1403, 0xC7324CC7, 0xAFF0474B, 0xB479966E, 0xAD9E5451, 0xF9913C41, 0x1F09B46, 0x3E35461E, 0xA0C66C10, 0xA4D1D103, 0xA05470B4, 0x6F1A3531, 0x4047B2F5, 0x11472CE0, 0x3AE41631, 0xFD36A905,
-0xF4436F91, 0x4A419766, 0x50699E67, 0x5408D93D, 0x112711AF, 0xF500642F, 0x160BD66A, 0x11B0BFE, 0xC2C55CA1, 0x31CF859, 0xC6C95401, 0xD9C0E5C1, 0xE46056AE, 0xF5E30710, 0xA5FF0150, 0x4454203F,
-0xB6660959, 0x2522776, 0x2D586970, 0x7370757, 0x60953317, 0x81E7072D, 0x68149E6, 0x653834, 0x363D9D81, 0xB429113F, 0x566C0258, 0x51F92F00, 0xE19C5812, 0x11FB150, 0x59A65200, 0x2775E0F4,
-0x106752A3, 0x7574326, 0x5CC34739, 0xDAD54805, 0x5E936BC7, 0xCFCAC5C9, 0x2FC42555, 0xD06994C0, 0x5CAC7400, 0x5D84D074, 0x6D668604, 0xE50B19BD, 0xE7969F55, 0xFF925005, 0x65B266B, 0x4743035E,
-0x972C44C7, 0x5AB47260, 0x77123580, 0xD92DB404, 0x451BB8D1, 0xB94B4447, 0x9B955210, 0xA253815, 0x79AC5184, 0xE3D10C5C, 0x452410FB, 0xDFD3A148, 0xE0336E46, 0x4E287599, 0x12B750A9, 0xDAC0DE47,
-0xC18459A9, 0xC764CD30, 0x2334675E, 0xF6490DB8, 0x49172679, 0x659B6994, 0x7A912691, 0xB2B76074, 0xB9E58704, 0xCC33D9A9, 0x207F6A7, 0xC405F801, 0x560297CF, 0x48140E5F, 0xC44E676A, 0xB191DED,
-0x11035F60, 0x5B1F60B4, 0xA4EB1443, 0x5A6C06E6, 0x1915AE9, 0x33CD2696, 0x879F4444, 0xD442170B, 0xC9C9D9D6, 0xA1D4C124, 0xC40366B7, 0x22497E9, 0xCD18D7F9, 0x99FD111A, 0x9F6B53A1, 0xE051ABFC,
-0xBC10071D, 0xA566A759, 0x6DBC5E5, 0x4A9DA851, 0x2266B15, 0x123D6E9B, 0x66E05449, 0xD10D3870, 0x1706E462, 0xAF0B9B51, 0xE0109D6C, 0xF144861B, 0x813ED01B, 0xB6496C10, 0x1ED00768, 0x60747C10,
-0x46494376, 0x5E4C10B0, 0x573D5906, 0x5A02E5FE, 0xB3E005F2, 0xC05C4F45, 0xDBD58982, 0x6D13171F, 0x9741FA00, 0x61DB852, 0x8D46D299, 0x47421E4D, 0x74A94E6, 0x4DA31CD3, 0x681602FF, 0xC67962D7,
-0x1560BBA, 0xF8849D85, 0x7DD6D5B8, 0x5B6B4640, 0x441A349F, 0x61B3601F, 0xD550E2D3, 0xCF18111B, 0x97673333, 0xC59909E4, 0x57DAE840, 0xBB910E5, 0x144484F9, 0xA85C2511, 0x4092D5EA, 0x89910391,
-0x7D385500, 0x3D1D2176, 0x802FD41B, 0xA5D94890, 0x656E9099, 0xE66906E6, 0x4331A2D5, 0x4478E25B, 0xE78C41D, 0x3565D979, 0x4147133B, 0x89C6C64A, 0x45C9CDCF, 0x532315FA, 0x3E5A0D04, 0xB111DBFC,
-0x63162FF7, 0x9E0B9CD0, 0xA758E46B, 0xA097421F, 0xAE544262, 0xA59A5DDD, 0x3732261D, 0x71736790, 0x8895A173, 0x687470B1, 0x57E564FE, 0x6A765404, 0xF5E21482, 0x667150E1, 0xC0A764D9, 0x89950C14,
-0x3756912B, 0x3913499, 0x6FB553C3, 0x29920995, 0x750EC738, 0xC35C6992, 0xAA55E404, 0xCB4B0753, 0x20396156, 0xDB04F4C1, 0x7876825F, 0xD562319A, 0x592D121B, 0xF9360956, 0x47811B47, 0xD9BD5884,
-0x170A35E, 0x844EE59A, 0x8684EDDA, 0x405F1000, 0x813641A7, 0x176681A2, 0x7DBE6440, 0x87C6A54, 0xC485D905, 0x1AB0811D, 0xDBA59291, 0xFA52817C, 0x3981E5F0, 0x5109FEE5, 0xC1284557, 0x573B2703,
-0x30640659, 0xF1D3E997, 0x906253B7, 0x9076C3FD, 0xCA34D35C, 0xF481491C, 0x5976A4C0, 0x3FF1144, 0x26170649, 0x196B9065, 0x90693D09, 0xF0211A17, 0xAF016DD4, 0x4E449C48, 0x6F9A84F4, 0xB4449818,
-0x606DB144, 0x916B97A6, 0xEBD0FD19, 0xAA08D541, 0xD71BE505, 0x1E6507AF, 0x3E4B1361, 0xF391619, 0x4264BC50, 0x60563321, 0x4A370617, 0x436452E7, 0xBE107604, 0xDDA9C18E, 0xBF5B0371, 0x906F1107,
-0x55C5C83C, 0x31364782, 0x68DD9F1, 0xF99607E6, 0x2996367E, 0x5AF4458, 0x96582D30, 0xC4E4D98A, 0xE68719BE, 0x20C56C64, 0x5A550246, 0xB759B079, 0x64439BFB, 0xB1240D2E, 0x271350B2, 0x4314AF56,
-0xA9998D72, 0x5E8D1481, 0xB65D7253, 0x669392B8, 0xF0919B6C, 0x9F9790DD, 0x60979838, 0x3D625168, 0x12196D58, 0x59E9448A, 0x30547C92, 0x8E317590, 0x7B954843, 0x323D4907, 0x5A4D5308, 0xD10490DB,
-0x92CE4511, 0xB9353125, 0xA91F4050, 0xDC485D08, 0x2F11742, 0xB9B4090F, 0xD09891FF, 0x9503936, 0x80E3505F, 0xED99405F, 0xEA9058DE, 0x55019FC0, 0x9A1F412C, 0x471660DA, 0xF4D0C505, 0x4926666D,
-0x4191C3EF, 0x1A0526F5, 0x55F889C7, 0xD33CC738, 0x7A66C601, 0xF701C662, 0x6E953906, 0x6E6559AD, 0x1626C57A, 0xE0075266, 0x67069A5B, 0xA5F70401, 0x47786090, 0x8111C53B, 0xB4640B1A, 0x269D5DAE,
-0x50C4A474, 0x2A94987D, 0x44C05226, 0x6114423F, 0xB8474F1D, 0x8E0E05D8, 0x87B40FD2, 0x1F4392FD, 0x170E995, 0x1563DE0, 0x6F9AC5CA, 0x752A0D1D, 0xFF0A44D0, 0x7FD0D366, 0x51A62E05, 0xD0D5E8DE,
-0x56113F30, 0xE4056D87, 0xAB3B5100, 0x46539C6, 0x7F676290, 0xB851130D, 0xF5E09183, 0x64AC5D2D, 0x55F9404B, 0xE256F977, 0x70617760, 0xEE48E450, 0xDD5E35C8, 0x94D400F8, 0x446A0667, 0xEA935184,
-0x464B07F8, 0x8C74B6D4, 0x8D9D59A5, 0xCEC6ADD5, 0x2D0956A3, 0xA5BA5103, 0x31353123, 0x55FF4140, 0xA2440F15, 0x89266792, 0x44E1E154, 0xE5B41338, 0x2FCE9901, 0x95E6811B, 0x475C871, 0xAE9E4A44,
-0x5E0D141, 0xE702D66B, 0x474707E2, 0x6486CBC5, 0x7476016E, 0xB1D29B56, 0x7A34C605, 0x51427906, 0xE7967F97, 0x65D29C90, 0x55081AD, 0xD6E64045, 0x67134368, 0x9101E6DD, 0x68957841, 0x3D3D5909,
-0xE0525BAF, 0x134A4D3C, 0xC1B11909, 0x16493B76, 0x6B96D0A0, 0xBF0E79D6, 0x2C7096F9, 0x90FC04D6, 0x1742E472, 0x531CA1AC, 0xDAD08646, 0x9066669, 0xA7588D86, 0x913C54C2, 0x742A4514, 0x17111ABC,
-0xDDD7EDE5, 0x6699C498, 0x75313135, 0xE4995F64, 0x2582667E, 0x9C5C4C48, 0x1D0D10D9, 0x5B4C164, 0x46E616CB, 0xD18444BF, 0xE1D4695C, 0x40095E99, 0xECE02955, 0xFF40A411, 0x54D2421B, 0xE1C7DB05,
-0x80E5095D, 0x6745E907, 0xA6197007, 0x760674F9, 0x1522170A, 0x65181FF, 0xE0FDA45, 0x521C7867, 0x6B91D382, 0x657A83C1, 0x73C98C35, 0xB3CC31D6, 0x164B257B, 0x6F9605F9, 0x52F49EB5, 0x87592907,
-0x1B10D6F8, 0x949FD54, 0x910315BA, 0x40F09B47, 0xB761C144, 0x6D039D5, 0x59B113F, 0xE5C6D9, 0x176990FB, 0x90F8A465, 0x6DB8D144, 0x4552E392, 0xC94396B9, 0x36315A97, 0x7E874215, 0x40116F6F,
-0xDF193401, 0x70689317, 0x7E021740, 0xD5C044BD, 0xF1636E94, 0x5B0D34A1, 0x1027F689, 0x44A45590, 0xD443035F, 0xD7A4418C, 0xD949F480, 0x84C1E154, 0x6090966E, 0x40B56C1E, 0x9669C540, 0xA6305505,
-0x1667E244, 0xC245885D, 0xD504A5E, 0xA4F0C549, 0x358472A8, 0xC414BF20, 0x43369729, 0xB1112711, 0x78644352, 0x811CB856, 0x6F9792F0, 0x1E1E4195, 0x62C58C14, 0x2619D3A2, 0x998056BD, 0x653F3410,
-0x919184E4, 0x64906D6C, 0xE3D7996, 0x47660563, 0x50A91F03, 0x49F8C5CB, 0xFE15235E, 0x9783451, 0x716257B7, 0x1AB5351, 0x1710DC84, 0x3FA01D5A, 0x9844AD64, 0xE4D901BC, 0xEE546352, 0xF9547987,
-0xF6834B47, 0xB651F6D, 0xE09C9045, 0xE491424E, 0x94F9050B, 0x7DA6091D, 0xB5C14D74, 0x123A699D, 0x1F413CC3, 0x9F434158, 0x55D8C8C4, 0x51106FF, 0xD36CB1C, 0x1541FCE1, 0xC3344ED3, 0x3E29D905,
-0x150A6579, 0x502475A, 0x454B0F04, 0x65902722, 0x9EA055, 0x26568CC5, 0x7EB1443, 0x42186F96, 0x132357A9, 0x74091F13, 0xEE55480F, 0x7A0B4A45, 0xF01A8637, 0x937D68D5, 0xE2611115, 0x8A853663,
-0x60D295AB, 0x1946461A, 0xBB5190A1, 0x6D514218, 0x7C6482CF, 0x8759ACC8, 0x7E86C805, 0x42D3755F, 0xF4C10529, 0x471F1400, 0x5394238C, 0x112D54A8, 0x55E450F8, 0xDC2191BA, 0xA5F93011, 0x1A2159F5,
-0x4051A297, 0x66066999, 0xA644C058, 0x54EC9148, 0xFAFB0146, 0x1150FCEC, 0x4141D171, 0xF09F0444, 0x488E5E0E, 0x3D190F10, 0x406E405F, 0xFE9904AD, 0x101F5C6F, 0x454B093B, 0x40946C1F, 0xF0B966D,
-0xC16464DB, 0x91E2D6F9, 0x97789074, 0x537C5C40, 0x44A533C3, 0x74102B33, 0x40D425C1, 0xD748C936, 0x786619C9, 0x40917985, 0x66594C1C, 0x2F05D2D2, 0x8C711B4, 0xF9602757, 0x1013854A, 0x7D6296D0,
-0x3D99B8F0, 0x495D3243, 0x916864BE, 0x4E43B947, 0xE756A110, 0xA8D09D7D, 0xBF625095, 0xC1D48F87, 0x8BD47099, 0xDB631315, 0xE54139A0, 0xE4DC9C63, 0x846C2544, 0x4F0E04D1, 0xE0B15B26, 0x3249499D,
-
-}
-/**** ended inlining basisu_global_selector_cb.h ****/
-		;
-
-	const uint32_t g_global_selector_cb_size = sizeof(g_global_selector_cb) / sizeof(g_global_selector_cb[0]);
-
-	void etc1_global_selector_codebook::init(uint32_t N, const uint32_t* pEntries)
-	{
-		m_palette.resize(N);
-		for (uint32_t i = 0; i < N; i++)
-			m_palette[i].set_uint32(pEntries[i]);
-	}
-
-	void etc1_global_selector_codebook::print_code(FILE* pFile)
-	{
-		fprintf(pFile, "{\n");
-		for (uint32_t i = 0; i < m_palette.size(); i++)
-		{
-			fprintf(pFile, "0x%X,", m_palette[i].get_uint32());
-			if ((i & 15) == 15)
-				fprintf(pFile, "\n");
-		}
-		fprintf(pFile, "\n}\n");
-	}
-
+		
 	enum etc_constants
 	{
 		cETC1BytesPerBlock = 8U,
@@ -2637,8 +7304,8 @@
 		uint32_t m_base;
 		uint32_t m_table;
 		uint32_t m_multiplier;
-		std::vector<uint8_t> m_selectors;
-		std::vector<uint8_t> m_selectors_temp;
+		basisu::vector<uint8_t> m_selectors;
+		basisu::vector<uint8_t> m_selectors_temp;
 	};
 
 	static uint64_t pack_eac_a8_exhaustive(pack_eac_a8_results& results, const uint8_t* pPixels, uint32_t num_pixels)
@@ -3034,8 +7701,8 @@
 		uint32_t m_base;
 		uint32_t m_table;
 		uint32_t m_multiplier;
-		std::vector<uint8_t> m_selectors;
-		std::vector<uint8_t> m_selectors_temp;
+		basisu::vector<uint8_t> m_selectors;
+		basisu::vector<uint8_t> m_selectors_temp;
 	};
 
 	static uint64_t pack_eac_r11_exhaustive(pack_eac_r11_results& results, const uint8_t* pPixels, uint32_t num_pixels)
@@ -3193,13 +7860,14 @@
 #if BASISD_SUPPORT_UASTC
 	void uastc_init();
 #endif
+
+	static bool g_transcoder_initialized;
 		
 	// Library global initialization. Requires ~9 milliseconds when compiled and executed natively on a Core i7 2.2 GHz.
 	// If this is too slow, these computed tables can easilky be moved to be compiled in.
 	void basisu_transcoder_init()
 	{
-		static bool s_initialized;
-		if (s_initialized)
+		if (g_transcoder_initialized)
       {
          BASISU_DEVEL_ERROR("basisu_transcoder::basisu_transcoder_init: Called more than once\n");      
 			return;
@@ -3319,7 +7987,7 @@
 		transcoder_init_pvrtc2();
 #endif
 
-		s_initialized = true;
+		g_transcoder_initialized = true;
 	}
 
 #if BASISD_SUPPORT_DXT1
@@ -5641,7 +10309,7 @@
 		assert(num_bits < 32);
 		assert(val < (1ULL << num_bits));
 
-		uint32_t mask = (1 << num_bits) - 1;
+		uint32_t mask = static_cast<uint32_t>((1ULL << num_bits) - 1);
 
 		while (num_bits)
 		{
@@ -7873,7 +12541,7 @@
 
 		while (total_bits)
 		{
-			const uint32_t bits_to_write = std::min<int>(total_bits, 8 - (bit_pos & 7));
+			const uint32_t bits_to_write = basisu::minimum<int>(total_bits, 8 - (bit_pos & 7));
 
 			pBytes[bit_pos >> 3] |= static_cast<uint8_t>(value << (bit_pos & 7));
 
@@ -12111,7 +16779,7 @@
 
 					uint32_t m = (le * 5 + he * 3) / 8;
 
-					int err = labs((int)v - (int)m);
+					int err = (int)labs((int)v - (int)m);
 					if (err < lowest_err)
 					{
 						lowest_err = err;
@@ -12134,7 +16802,7 @@
 				uint32_t le = (l << 1);
 				le = (le << 4) | le;
 
-				int err = labs((int)v - (int)le);
+				int err = (int)labs((int)v - (int)le);
 				if (err < lowest_err)
 				{
 					lowest_err = err;
@@ -12156,7 +16824,7 @@
 				uint32_t he = (h << 1) | 1;
 				he = (he << 4) | he;
 
-				int err = labs((int)v - (int)he);
+				int err = (int)labs((int)v - (int)he);
 				if (err < lowest_err)
 				{
 					lowest_err = err;
@@ -12185,7 +16853,7 @@
 
 					uint32_t m = (le * 5 + he * 3) / 8;
 
-					int err = labs((int)v - (int)m);
+					int err = (int)labs((int)v - (int)m);
 					if (err < lowest_err)
 					{
 						lowest_err = err;
@@ -12215,7 +16883,7 @@
 
 					uint32_t m = (le * 5 + he * 3) / 8;
 
-					int err = labs((int)v - (int)m);
+					int err = (int)labs((int)v - (int)m);
 					if (err < lowest_err)
 					{
 						lowest_err = err;
@@ -12231,8 +16899,8 @@
 	}
 #endif // BASISD_SUPPORT_PVRTC2
 
-	basisu_lowlevel_etc1s_transcoder::basisu_lowlevel_etc1s_transcoder(const etc1_global_selector_codebook* pGlobal_sel_codebook) :
-		m_pGlobal_sel_codebook(pGlobal_sel_codebook),
+	basisu_lowlevel_etc1s_transcoder::basisu_lowlevel_etc1s_transcoder() :
+		m_pGlobal_codebook(nullptr),
 		m_selector_history_buf_size(0)
 	{
 	}
@@ -12241,6 +16909,11 @@
 		uint32_t num_endpoints, const uint8_t* pEndpoints_data, uint32_t endpoints_data_size,
 		uint32_t num_selectors, const uint8_t* pSelectors_data, uint32_t selectors_data_size)
 	{
+		if (m_pGlobal_codebook)
+		{
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 11\n");
+			return false;
+		}
 		bitwise_decoder sym_codec;
 
 		huffman_decoding_table color5_delta_model0, color5_delta_model1, color5_delta_model2, inten_delta_model;
@@ -12283,7 +16956,7 @@
 
 		const bool endpoints_are_grayscale = sym_codec.get_bits(1) != 0;
 
-		m_endpoints.resize(num_endpoints);
+		m_local_endpoints.resize(num_endpoints);
 
 		color32 prev_color5(16, 16, 16, 0);
 		uint32_t prev_inten = 0;
@@ -12291,8 +16964,8 @@
 		for (uint32_t i = 0; i < num_endpoints; i++)
 		{
 			uint32_t inten_delta = sym_codec.decode_huffman(inten_delta_model);
-			m_endpoints[i].m_inten5 = static_cast<uint8_t>((inten_delta + prev_inten) & 7);
-			prev_inten = m_endpoints[i].m_inten5;
+			m_local_endpoints[i].m_inten5 = static_cast<uint8_t>((inten_delta + prev_inten) & 7);
+			prev_inten = m_local_endpoints[i].m_inten5;
 
 			for (uint32_t c = 0; c < (endpoints_are_grayscale ? 1U : 3U); c++)
 			{
@@ -12306,21 +16979,21 @@
 
 				int v = (prev_color5[c] + delta) & 31;
 
-				m_endpoints[i].m_color5[c] = static_cast<uint8_t>(v);
+				m_local_endpoints[i].m_color5[c] = static_cast<uint8_t>(v);
 
 				prev_color5[c] = static_cast<uint8_t>(v);
 			}
 
 			if (endpoints_are_grayscale)
 			{
-				m_endpoints[i].m_color5[1] = m_endpoints[i].m_color5[0];
-				m_endpoints[i].m_color5[2] = m_endpoints[i].m_color5[0];
+				m_local_endpoints[i].m_color5[1] = m_local_endpoints[i].m_color5[0];
+				m_local_endpoints[i].m_color5[2] = m_local_endpoints[i].m_color5[0];
 			}
 		}
 
 		sym_codec.stop();
 
-		m_selectors.resize(num_selectors);
+		m_local_selectors.resize(num_selectors);
 		
 		if (!sym_codec.init(pSelectors_data, selectors_data_size))
 		{
@@ -12334,50 +17007,8 @@
 
 		if (used_global_selector_cb)
 		{
-			// global selector palette
-			uint32_t pal_bits = sym_codec.get_bits(4);
-			uint32_t mod_bits = sym_codec.get_bits(4);
-
-			basist::huffman_decoding_table mod_model;
-			if (mod_bits)
-			{
-				if (!sym_codec.read_huffman_table(mod_model))
-				{
-					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 6\n");
-					return false;
-				}
-				if (!mod_model.is_valid())
-				{
-					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 6a\n");
-					return false;
-				}
-			}
-
-			for (uint32_t i = 0; i < num_selectors; i++)
-			{
-				uint32_t pal_index = 0;
-				if (pal_bits)
-					pal_index = sym_codec.get_bits(pal_bits);
-
-				uint32_t mod_index = 0;
-				if (mod_bits)
-					mod_index = sym_codec.decode_huffman(mod_model);
-
-				if (pal_index >= m_pGlobal_sel_codebook->size())
-				{
-					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 7z\n");
-					return false;
-				}
-
-				const etc1_selector_palette_entry e(m_pGlobal_sel_codebook->get_entry(pal_index, etc1_global_palette_entry_modifier(mod_index)));
-
-				// TODO: Optimize this
-				for (uint32_t y = 0; y < 4; y++)
-					for (uint32_t x = 0; x < 4; x++)
-						m_selectors[i].set_selector(x, y, e[x + y * 4]);
-
-				m_selectors[i].init_flags();
-			}
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: global selector codebooks are unsupported\n");
+			return false;
 		}
 		else
 		{
@@ -12385,146 +17016,70 @@
 
 			if (used_hybrid_selector_cb)
 			{
-				const uint32_t pal_bits = sym_codec.get_bits(4);
-				const uint32_t mod_bits = sym_codec.get_bits(4);
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: hybrid global selector codebooks are unsupported\n");
+				return false;
+			}
+				
+			const bool used_raw_encoding = (sym_codec.get_bits(1) == 1);
 
-				basist::huffman_decoding_table uses_global_cb_bitflags_model;
-				if (!sym_codec.read_huffman_table(uses_global_cb_bitflags_model))
+			if (used_raw_encoding)
+			{
+				for (uint32_t i = 0; i < num_selectors; i++)
 				{
-					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 7\n");
-					return false;
-				}
-				if (!uses_global_cb_bitflags_model.is_valid())
-				{
-					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 7a\n");
-					return false;
-				}
-
-				basist::huffman_decoding_table global_mod_indices_model;
-				if (mod_bits)
-				{
-					if (!sym_codec.read_huffman_table(global_mod_indices_model))
+					for (uint32_t j = 0; j < 4; j++)
 					{
-						BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 8\n");
-						return false;
-					}
-					if (!global_mod_indices_model.is_valid())
-					{
-						BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 8a\n");
-						return false;
-					}
-				}
+						uint32_t cur_byte = sym_codec.get_bits(8);
 
-				uint32_t cur_uses_global_cb_bitflags = 0;
-				uint32_t uses_global_cb_bitflags_remaining = 0;
-
-				for (uint32_t q = 0; q < num_selectors; q++)
-				{
-					if (!uses_global_cb_bitflags_remaining)
-					{
-						cur_uses_global_cb_bitflags = sym_codec.decode_huffman(uses_global_cb_bitflags_model);
-
-						uses_global_cb_bitflags_remaining = 8;
-					}
-					uses_global_cb_bitflags_remaining--;
-
-					const bool used_global_cb_flag = (cur_uses_global_cb_bitflags & 1) != 0;
-					cur_uses_global_cb_bitflags >>= 1;
-
-					if (used_global_cb_flag)
-					{
-						const uint32_t pal_index = pal_bits ? sym_codec.get_bits(pal_bits) : 0;
-						const uint32_t mod_index = mod_bits ? sym_codec.decode_huffman(global_mod_indices_model) : 0;
-
-						if (pal_index >= m_pGlobal_sel_codebook->size())
-						{
-							BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 8b\n");
-							return false;
-						}
-
-						const etc1_selector_palette_entry e(m_pGlobal_sel_codebook->get_entry(pal_index, etc1_global_palette_entry_modifier(mod_index)));
-
-						for (uint32_t y = 0; y < 4; y++)
-							for (uint32_t x = 0; x < 4; x++)
-								m_selectors[q].set_selector(x, y, e[x + y * 4]);
-					}
-					else
-					{
-						for (uint32_t j = 0; j < 4; j++)
-						{
-							uint32_t cur_byte = sym_codec.get_bits(8);
-
-							for (uint32_t k = 0; k < 4; k++)
-								m_selectors[q].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
-						}
+						for (uint32_t k = 0; k < 4; k++)
+							m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
 					}
 
-					m_selectors[q].init_flags();
+					m_local_selectors[i].init_flags();
 				}
 			}
 			else
 			{
-				const bool used_raw_encoding = (sym_codec.get_bits(1) == 1);
-
-				if (used_raw_encoding)
+				if (!sym_codec.read_huffman_table(delta_selector_pal_model))
 				{
-					for (uint32_t i = 0; i < num_selectors; i++)
+					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 10\n");
+					return false;
+				}
+
+				if ((num_selectors > 1) && (!delta_selector_pal_model.is_valid()))
+				{
+					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 10a\n");
+					return false;
+				}
+
+				uint8_t prev_bytes[4] = { 0, 0, 0, 0 };
+
+				for (uint32_t i = 0; i < num_selectors; i++)
+				{
+					if (!i)
 					{
 						for (uint32_t j = 0; j < 4; j++)
 						{
 							uint32_t cur_byte = sym_codec.get_bits(8);
-
-							for (uint32_t k = 0; k < 4; k++)
-								m_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
-						}
-
-						m_selectors[i].init_flags();
-					}
-				}
-				else
-				{
-					if (!sym_codec.read_huffman_table(delta_selector_pal_model))
-					{
-						BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 10\n");
-						return false;
-					}
-
-					if ((num_selectors > 1) && (!delta_selector_pal_model.is_valid()))
-					{
-						BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 10a\n");
-						return false;
-					}
-
-					uint8_t prev_bytes[4] = { 0, 0, 0, 0 };
-
-					for (uint32_t i = 0; i < num_selectors; i++)
-					{
-						if (!i)
-						{
-							for (uint32_t j = 0; j < 4; j++)
-							{
-								uint32_t cur_byte = sym_codec.get_bits(8);
-								prev_bytes[j] = static_cast<uint8_t>(cur_byte);
-
-								for (uint32_t k = 0; k < 4; k++)
-									m_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
-							}
-							m_selectors[i].init_flags();
-							continue;
-						}
-
-						for (uint32_t j = 0; j < 4; j++)
-						{
-							int delta_byte = sym_codec.decode_huffman(delta_selector_pal_model);
-
-							uint32_t cur_byte = delta_byte ^ prev_bytes[j];
 							prev_bytes[j] = static_cast<uint8_t>(cur_byte);
 
 							for (uint32_t k = 0; k < 4; k++)
-								m_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
+								m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
 						}
-						m_selectors[i].init_flags();
+						m_local_selectors[i].init_flags();
+						continue;
 					}
+
+					for (uint32_t j = 0; j < 4; j++)
+					{
+						int delta_byte = sym_codec.decode_huffman(delta_selector_pal_model);
+
+						uint32_t cur_byte = delta_byte ^ prev_bytes[j];
+						prev_bytes[j] = static_cast<uint8_t>(cur_byte);
+
+						for (uint32_t k = 0; k < 4; k++)
+							m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
+					}
+					m_local_selectors[i].init_flags();
 				}
 			}
 		}
@@ -12592,6 +17147,12 @@
 		}
 
 		m_selector_history_buf_size = sym_codec.get_bits(13);
+		// Check for bogus values.
+		if (!m_selector_history_buf_size)
+		{
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 5\n");
+			return false;
+		}
 
 		sym_codec.stop();
 
@@ -12599,7 +17160,7 @@
 	}
 
 	bool basisu_lowlevel_etc1s_transcoder::transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
-		uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels,
+		uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const bool is_video, const bool is_alpha_slice, const uint32_t level_index, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels,
 		basisu_transcoder_state* pState, bool transcode_alpha, void *pAlpha_blocks, uint32_t output_rows_in_pixels)
 	{
 		// 'pDst_blocks' unused when disabling *all* hardware transcode options
@@ -12609,20 +17170,26 @@
 		BASISU_NOTE_UNUSED(transcode_alpha);
 		BASISU_NOTE_UNUSED(pAlpha_blocks);
 
+		assert(g_transcoder_initialized);
+		if (!g_transcoder_initialized)
+		{
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: Transcoder not globally initialized.\n");
+			return false;
+		}
+
 		if (!pState)
 			pState = &m_def_state;
 
-		const bool is_video = (header.m_tex_type == cBASISTexTypeVideoFrames);
 		const uint32_t total_blocks = num_blocks_x * num_blocks_y;
 
 		if (!output_row_pitch_in_blocks_or_pixels)
 		{
 			if (basis_block_format_is_uncompressed(fmt))
-				output_row_pitch_in_blocks_or_pixels = slice_desc.m_orig_width;
+				output_row_pitch_in_blocks_or_pixels = orig_width;
 			else
 			{
 				if (fmt == block_format::cFXT1_RGB)
-					output_row_pitch_in_blocks_or_pixels = (slice_desc.m_orig_width + 7) / 8;
+					output_row_pitch_in_blocks_or_pixels = (orig_width + 7) / 8;
 				else
 					output_row_pitch_in_blocks_or_pixels = num_blocks_x;
 			}
@@ -12631,15 +17198,15 @@
 		if (basis_block_format_is_uncompressed(fmt))
 		{
 			if (!output_rows_in_pixels)
-				output_rows_in_pixels = slice_desc.m_orig_height;
+				output_rows_in_pixels = orig_height;
 		}
 		
-		std::vector<uint32_t>* pPrev_frame_indices = nullptr;
+		basisu::vector<uint32_t>* pPrev_frame_indices = nullptr;
 		if (is_video)
 		{
 			// TODO: Add check to make sure the caller hasn't tried skipping past p-frames
-			const bool alpha_flag = (slice_desc.m_flags & cSliceDescFlagsHasAlpha) != 0;
-			const uint32_t level_index = slice_desc.m_level_index;
+			//const bool alpha_flag = (slice_desc.m_flags & cSliceDescFlagsHasAlpha) != 0;
+			//const uint32_t level_index = slice_desc.m_level_index;
 
 			if (level_index >= basisu_transcoder_state::cMaxPrevFrameLevels)
 			{
@@ -12647,7 +17214,7 @@
 				return false;
 			}
 
-			pPrev_frame_indices = &pState->m_prev_frame_indices[alpha_flag][level_index];
+			pPrev_frame_indices = &pState->m_prev_frame_indices[is_alpha_slice][level_index];
 			if (pPrev_frame_indices->size() < total_blocks)
 				pPrev_frame_indices->resize(total_blocks);
 		}
@@ -12661,15 +17228,16 @@
 		}
 
 		approx_move_to_front selector_history_buf(m_selector_history_buf_size);
-
-		const uint32_t SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX = (uint32_t)m_selectors.size();
-		const uint32_t SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX = m_selector_history_buf_size + SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX;
+				
 		uint32_t cur_selector_rle_count = 0;
 
 		decoder_etc_block block;
 		memset(&block, 0, sizeof(block));
+				
+		//block.set_flip_bit(true);
+		// Setting the flip bit to false to be compatible with the Khronos KDFS.
+		block.set_flip_bit(false);
 
-		block.set_flip_bit(true);
 		block.set_diff_bit(true);
 
 		void* pPVRTC_work_mem = nullptr;
@@ -12695,6 +17263,16 @@
 		int prev_endpoint_pred_sym = 0;
 		int endpoint_pred_repeat_count = 0;
 		uint32_t prev_endpoint_index = 0;
+		const endpoint_vec& endpoints = m_pGlobal_codebook ? m_pGlobal_codebook->m_local_endpoints : m_local_endpoints;
+		const selector_vec& selectors = m_pGlobal_codebook ? m_pGlobal_codebook->m_local_selectors : m_local_selectors;
+		if (!endpoints.size() || !selectors.size())
+		{
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: global codebooks must be unpacked first\n");
+			return false;
+		}
+
+		const uint32_t SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX = (uint32_t)selectors.size();
+		const uint32_t SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX = m_selector_history_buf_size + SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX;
 
 		for (uint32_t block_y = 0; block_y < num_blocks_y; block_y++)
 		{
@@ -12796,8 +17374,8 @@
 					const uint32_t delta_sym = sym_codec.decode_huffman(m_delta_endpoint_model);
 
 					endpoint_index = delta_sym + prev_endpoint_index;
-					if (endpoint_index >= m_endpoints.size())
-						endpoint_index -= (int)m_endpoints.size();
+					if (endpoint_index >= endpoints.size())
+						endpoint_index -= (int)endpoints.size();
 				}
 
 				pState->m_block_endpoint_preds[cur_block_endpoint_pred_array][block_x].m_endpoint_index = (uint16_t)endpoint_index;
@@ -12812,7 +17390,7 @@
 					{
 						cur_selector_rle_count--;
 
-						selector_sym = (int)m_selectors.size();
+						selector_sym = (int)selectors.size();
 					}
 					else
 					{
@@ -12836,17 +17414,17 @@
 								return false;
 							}
 
-							selector_sym = (int)m_selectors.size();
+							selector_sym = (int)selectors.size();
 
 							cur_selector_rle_count--;
 						}
 					}
 
-					if (selector_sym >= (int)m_selectors.size())
+					if (selector_sym >= (int)selectors.size())
 					{
 						assert(m_selector_history_buf_size > 0);
 
-						int history_buf_index = selector_sym - (int)m_selectors.size();
+						int history_buf_index = selector_sym - (int)selectors.size();
 
 						if (history_buf_index >= (int)selector_history_buf.size())
 						{
@@ -12871,7 +17449,7 @@
 					}
 				}
 
-				if ((endpoint_index >= m_endpoints.size()) || (selector_index >= m_selectors.size()))
+				if ((endpoint_index >= endpoints.size()) || (selector_index >= selectors.size()))
 				{
 					// The file is corrupted or we've got a bug.
 					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: invalid datastream (5)\n");
@@ -12895,8 +17473,8 @@
 				}
 #endif
 
-				const endpoint* pEndpoints = &m_endpoints[endpoint_index];
-				const selector* pSelector = &m_selectors[selector_index];
+				const endpoint* pEndpoints = &endpoints[endpoint_index];
+				const selector* pSelector = &selectors[selector_index];
 
 				switch (fmt)
 				{
@@ -13000,8 +17578,8 @@
 
 					const uint16_t* pAlpha_block = reinterpret_cast<uint16_t*>(static_cast<uint8_t*>(pAlpha_blocks) + (block_x + block_y * num_blocks_x) * sizeof(uint32_t));
 
-					const endpoint* pAlpha_endpoints = &m_endpoints[pAlpha_block[0]];
-					const selector* pAlpha_selector = &m_selectors[pAlpha_block[1]];
+					const endpoint* pAlpha_endpoints = &endpoints[pAlpha_block[0]];
+					const selector* pAlpha_selector = &selectors[pAlpha_block[1]];
 
 					const color32& alpha_base_color = pAlpha_endpoints->m_color5;
 					const uint32_t alpha_inten_table = pAlpha_endpoints->m_inten5;
@@ -13060,7 +17638,7 @@
 				{
 #if BASISD_SUPPORT_ASTC
 					void* pDst_block = static_cast<uint8_t*>(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes;
-					convert_etc1s_to_astc_4x4(pDst_block, pEndpoints, pSelector, transcode_alpha, &m_endpoints[0], &m_selectors[0]);
+					convert_etc1s_to_astc_4x4(pDst_block, pEndpoints, pSelector, transcode_alpha, &endpoints[0], &selectors[0]);
 #else
 					assert(0);
 #endif
@@ -13106,7 +17684,7 @@
 
 					void* pDst_block = static_cast<uint8_t*>(pDst_blocks) + (block_x + block_y * output_row_pitch_in_blocks_or_pixels) * output_block_or_pixel_stride_in_bytes;
 										
-					convert_etc1s_to_pvrtc2_rgba(pDst_block, pEndpoints, pSelector, &m_endpoints[0], &m_selectors[0]);
+					convert_etc1s_to_pvrtc2_rgba(pDst_block, pEndpoints, pSelector, &endpoints[0], &selectors[0]);
 #endif
 					break;
 				}
@@ -13234,7 +17812,7 @@
 						for (uint32_t i = 0; i < 4; i++)
 						{
 							packed_colors[i] = static_cast<uint16_t>((mul_8(colors[i].r, 31) << 11) | (mul_8(colors[i].g, 63) << 5) | mul_8(colors[i].b, 31));
-							if (IS_BIG_ENDIAN)
+							if (BASISD_IS_BIG_ENDIAN)
 								packed_colors[i] = byteswap_uint16(packed_colors[i]);
 						}
 					}
@@ -13243,7 +17821,7 @@
 						for (uint32_t i = 0; i < 4; i++)
 						{
 							packed_colors[i] = static_cast<uint16_t>((mul_8(colors[i].b, 31) << 11) | (mul_8(colors[i].g, 63) << 5) | mul_8(colors[i].r, 31));
-							if (IS_BIG_ENDIAN)
+							if (BASISD_IS_BIG_ENDIAN)
 								packed_colors[i] = byteswap_uint16(packed_colors[i]);
 						}
 					}
@@ -13284,12 +17862,12 @@
 						for (uint32_t x = 0; x < max_x; x++)
 						{
 							uint16_t cur = reinterpret_cast<uint16_t*>(pDst_pixels)[x];
-							if (IS_BIG_ENDIAN)
+							if (BASISD_IS_BIG_ENDIAN)
 								cur = byteswap_uint16(cur);
 
 							cur = (cur & 0xF) | packed_colors[(s >> (x * 2)) & 3];
 							
-							if (IS_BIG_ENDIAN)
+							if (BASISD_IS_BIG_ENDIAN)
 								cur = byteswap_uint16(cur);
 
 							reinterpret_cast<uint16_t*>(pDst_pixels)[x] = cur;
@@ -13315,7 +17893,7 @@
 					for (uint32_t i = 0; i < 4; i++)
 					{
 						packed_colors[i] = static_cast<uint16_t>((mul_8(colors[i].r, 15) << 12) | (mul_8(colors[i].g, 15) << 8) | (mul_8(colors[i].b, 15) << 4) | 0xF);
-						if (IS_BIG_ENDIAN)
+						if (BASISD_IS_BIG_ENDIAN)
 							packed_colors[i] = byteswap_uint16(packed_colors[i]);
 					}
 
@@ -13346,7 +17924,7 @@
 					for (uint32_t i = 0; i < 4; i++)
 					{
 						packed_colors[i] = mul_8(colors[i].g, 15);
-						if (IS_BIG_ENDIAN)
+						if (BASISD_IS_BIG_ENDIAN)
 							packed_colors[i] = byteswap_uint16(packed_colors[i]);
 					}
 
@@ -13398,7 +17976,7 @@
 		if (fmt == block_format::cPVRTC1_4_RGB)
 			fixup_pvrtc1_4_modulation_rgb((decoder_etc_block*)pPVRTC_work_mem, pPVRTC_endpoints, pDst_blocks, num_blocks_x, num_blocks_y);
 		else if (fmt == block_format::cPVRTC1_4_RGBA)
-			fixup_pvrtc1_4_modulation_rgba((decoder_etc_block*)pPVRTC_work_mem, pPVRTC_endpoints, pDst_blocks, num_blocks_x, num_blocks_y, pAlpha_blocks, &m_endpoints[0], &m_selectors[0]);
+			fixup_pvrtc1_4_modulation_rgba((decoder_etc_block*)pPVRTC_work_mem, pPVRTC_endpoints, pDst_blocks, num_blocks_x, num_blocks_y, pAlpha_blocks, &endpoints[0], &selectors[0]);
 #endif // BASISD_SUPPORT_PVRTC1
 
 		if (pPVRTC_work_mem)
@@ -13406,29 +17984,678 @@
 
 		return true;
 	}
+
+	bool basis_validate_output_buffer_size(transcoder_texture_format target_format,
+		uint32_t output_blocks_buf_size_in_blocks_or_pixels,
+		uint32_t orig_width, uint32_t orig_height,
+		uint32_t output_row_pitch_in_blocks_or_pixels,
+		uint32_t output_rows_in_pixels,
+		uint32_t total_slice_blocks)
+	{
+		if (basis_transcoder_format_is_uncompressed(target_format))
+		{
+			// Assume the output buffer is orig_width by orig_height
+			if (!output_row_pitch_in_blocks_or_pixels)
+				output_row_pitch_in_blocks_or_pixels = orig_width;
+
+			if (!output_rows_in_pixels) 
+				output_rows_in_pixels = orig_height;
+
+			// Now make sure the output buffer is large enough, or we'll overwrite memory.
+			if (output_blocks_buf_size_in_blocks_or_pixels < (output_rows_in_pixels * output_row_pitch_in_blocks_or_pixels))
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: output_blocks_buf_size_in_blocks_or_pixels < (output_rows_in_pixels * output_row_pitch_in_blocks_or_pixels)\n");
+				return false;
+			}
+		}
+		else if (target_format == transcoder_texture_format::cTFFXT1_RGB)
+		{
+			const uint32_t num_blocks_fxt1_x = (orig_width + 7) / 8;
+			const uint32_t num_blocks_fxt1_y = (orig_height + 3) / 4;
+			const uint32_t total_blocks_fxt1 = num_blocks_fxt1_x * num_blocks_fxt1_y;
+
+			if (output_blocks_buf_size_in_blocks_or_pixels < total_blocks_fxt1)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: output_blocks_buf_size_in_blocks_or_pixels < total_blocks_fxt1\n");
+				return false;
+			}
+		}
+		else
+		{
+			if (output_blocks_buf_size_in_blocks_or_pixels < total_slice_blocks)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: output_blocks_buf_size_in_blocks_or_pixels < transcode_image\n");
+				return false;
+			}
+		}
+		return true;
+	}
+
+	bool basisu_lowlevel_etc1s_transcoder::transcode_image(
+			transcoder_texture_format target_format,
+			void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
+			const uint8_t* pCompressed_data, uint32_t compressed_data_length,
+			uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
+			uint32_t rgb_offset, uint32_t rgb_length, uint32_t alpha_offset, uint32_t alpha_length,
+			uint32_t decode_flags,
+			bool basis_file_has_alpha_slices,
+			bool is_video,
+			uint32_t output_row_pitch_in_blocks_or_pixels,
+			basisu_transcoder_state* pState,
+			uint32_t output_rows_in_pixels)
+	{
+		if (((uint64_t)rgb_offset + rgb_length) > (uint64_t)compressed_data_length)
+		{
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: source data buffer too small (color)\n");
+			return false;
+		}
+
+		if (alpha_length)
+		{
+			if (((uint64_t)alpha_offset + alpha_length) > (uint64_t)compressed_data_length)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: source data buffer too small (alpha)\n");
+				return false;
+			}
+		}
+		else
+		{
+			assert(!basis_file_has_alpha_slices);
+		}
+
+		if ((target_format == transcoder_texture_format::cTFPVRTC1_4_RGB) || (target_format == transcoder_texture_format::cTFPVRTC1_4_RGBA))
+		{
+			if ((!basisu::is_pow2(num_blocks_x * 4)) || (!basisu::is_pow2(num_blocks_y * 4)))
+			{
+				// PVRTC1 only supports power of 2 dimensions
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: PVRTC1 only supports power of 2 dimensions\n");
+				return false;
+			}
+		}
+
+		if ((target_format == transcoder_texture_format::cTFPVRTC1_4_RGBA) && (!basis_file_has_alpha_slices))
+		{
+			// Switch to PVRTC1 RGB if the input doesn't have alpha.
+			target_format = transcoder_texture_format::cTFPVRTC1_4_RGB;
+		}
+				
+		const bool transcode_alpha_data_to_opaque_formats = (decode_flags & cDecodeFlagsTranscodeAlphaDataToOpaqueFormats) != 0;
+		const uint32_t bytes_per_block_or_pixel = basis_get_bytes_per_block_or_pixel(target_format);
+		const uint32_t total_slice_blocks = num_blocks_x * num_blocks_y;
+
+		if (!basis_validate_output_buffer_size(target_format, output_blocks_buf_size_in_blocks_or_pixels, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, output_rows_in_pixels, total_slice_blocks))
+		{
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: output buffer size too small\n");
+			return false;
+		}
+
+		bool status = false;
+
+		const uint8_t* pData = pCompressed_data + rgb_offset;
+		uint32_t data_len = rgb_length;
+		bool is_alpha_slice = false;
+
+		// If the caller wants us to transcode the mip level's alpha data, then use the next slice.
+		if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
+		{
+			pData = pCompressed_data + alpha_offset;
+			data_len = alpha_length;
+			is_alpha_slice = true;
+		}
+
+		switch (target_format)
+		{
+		case transcoder_texture_format::cTFETC1_RGB:
+		{
+			//status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC1, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pData, data_len, block_format::cETC1, bytes_per_block_or_pixel, false, is_video, is_alpha_slice, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+							
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ETC1 failed\n");
+			}
+			break;
+		}
+		case transcoder_texture_format::cTFBC1_RGB:
+		{
+#if !BASISD_SUPPORT_DXT1
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: BC1/DXT1 unsupported\n");
+			return false;
+#else
+			// status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC1, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pData, data_len, block_format::cBC1, bytes_per_block_or_pixel, true, is_video, is_alpha_slice, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to BC1 failed\n");
+			}
+			break;
+#endif
+		}
+		case transcoder_texture_format::cTFBC4_R:
+		{
+#if !BASISD_SUPPORT_DXT5A
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: BC4/DXT5A unsupported\n");
+			return false;
+#else
+			//status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pData, data_len, block_format::cBC4, bytes_per_block_or_pixel, false, is_video, is_alpha_slice, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to BC4 failed\n");
+			}
+			break;
+#endif
+		}
+		case transcoder_texture_format::cTFPVRTC1_4_RGB:
+		{
+#if !BASISD_SUPPORT_PVRTC1
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: PVRTC1 4 unsupported\n");
+			return false;
+#else
+			// output_row_pitch_in_blocks_or_pixels is actually ignored because we're transcoding to PVRTC1. (Print a dev warning if it's != 0?)
+			//status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC1_4_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pData, data_len, block_format::cPVRTC1_4_RGB, bytes_per_block_or_pixel, false, is_video, is_alpha_slice, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to PVRTC1 4 RGB failed\n");
+			}
+			break;
+#endif
+		}
+		case transcoder_texture_format::cTFPVRTC1_4_RGBA:
+		{
+#if !BASISD_SUPPORT_PVRTC1
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: PVRTC1 4 unsupported\n");
+			return false;
+#else
+			assert(basis_file_has_alpha_slices);
+			assert(alpha_length);
+
+			// Temp buffer to hold alpha block endpoint/selector indices
+			basisu::vector<uint32_t> temp_block_indices(total_slice_blocks);
+
+			// First transcode alpha data to temp buffer
+			//status = transcode_slice(pData, data_size, slice_index + 1, &temp_block_indices[0], total_slice_blocks, block_format::cIndices, sizeof(uint32_t), decode_flags, pSlice_descs[slice_index].m_num_blocks_x, pState);
+			status = transcode_slice(&temp_block_indices[0], num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cIndices, sizeof(uint32_t), false, is_video, true, level_index, orig_width, orig_height, num_blocks_x, pState, false, nullptr, 0);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to PVRTC1 4 RGBA failed (0)\n");
+			}
+			else
+			{
+				// output_row_pitch_in_blocks_or_pixels is actually ignored because we're transcoding to PVRTC1. (Print a dev warning if it's != 0?)
+				//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC1_4_RGBA, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState, &temp_block_indices[0]);
+				status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cPVRTC1_4_RGBA, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, &temp_block_indices[0], 0);
+				if (!status)
+				{
+					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to PVRTC1 4 RGBA failed (1)\n");
+				}
+			}
+
+			break;
+#endif
+		}
+		case transcoder_texture_format::cTFBC7_RGBA:
+		case transcoder_texture_format::cTFBC7_ALT:
+		{
+#if !BASISD_SUPPORT_BC7_MODE5
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: BC7 unsupported\n");
+			return false;
+#else
+			assert(bytes_per_block_or_pixel == 16);
+			// We used to support transcoding just alpha to BC7 - but is that useful at all?
+
+			// First transcode the color slice. The cBC7_M5_COLOR transcoder will output opaque mode 5 blocks.
+			//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC7_M5_COLOR, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cBC7_M5_COLOR, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+
+			if ((status) && (basis_file_has_alpha_slices))
+			{
+				// Now transcode the alpha slice. The cBC7_M5_ALPHA transcoder will now change the opaque mode 5 blocks to blocks with alpha.
+				//status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC7_M5_ALPHA, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+				status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cBC7_M5_ALPHA, bytes_per_block_or_pixel, false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+			}
+
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to BC7 failed (0)\n");
+			}
+
+			break;
+#endif
+		}
+		case transcoder_texture_format::cTFETC2_RGBA:
+		{
+#if !BASISD_SUPPORT_ETC2_EAC_A8
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: ETC2 EAC A8 unsupported\n");
+			return false;
+#else
+			assert(bytes_per_block_or_pixel == 16);
+
+			if (basis_file_has_alpha_slices)
+			{
+				// First decode the alpha data 
+				//status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_A8, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+				status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cETC2_EAC_A8, bytes_per_block_or_pixel, false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+			}
+			else
+			{
+				//write_opaque_alpha_blocks(pSlice_descs[slice_index].m_num_blocks_x, pSlice_descs[slice_index].m_num_blocks_y, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_A8, 16, output_row_pitch_in_blocks_or_pixels);
+				basisu_transcoder::write_opaque_alpha_blocks(num_blocks_x, num_blocks_y, pOutput_blocks, block_format::cETC2_EAC_A8, 16, output_row_pitch_in_blocks_or_pixels);
+				status = true;
+			}
+
+			if (status)
+			{
+				// Now decode the color data
+				//status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC1, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+				status = transcode_slice((uint8_t *)pOutput_blocks + 8, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cETC1, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+				if (!status)
+				{
+					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ETC2 RGB failed\n");
+				}
+			}
+			else
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ETC2 A failed\n");
+			}
+			break;
+#endif
+		}
+		case transcoder_texture_format::cTFBC3_RGBA:
+		{
+#if !BASISD_SUPPORT_DXT1
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: DXT1 unsupported\n");
+			return false;
+#elif !BASISD_SUPPORT_DXT5A
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: DXT5A unsupported\n");
+			return false;
+#else
+			assert(bytes_per_block_or_pixel == 16);
+						
+			// First decode the alpha data 
+			if (basis_file_has_alpha_slices)
+			{
+				//status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+				status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cBC4, bytes_per_block_or_pixel, false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+			}
+			else
+			{
+				basisu_transcoder::write_opaque_alpha_blocks(num_blocks_x, num_blocks_y, pOutput_blocks, block_format::cBC4, 16, output_row_pitch_in_blocks_or_pixels);
+				status = true;
+			}
+
+			if (status)
+			{
+				// Now decode the color data. Forbid 3 color blocks, which aren't allowed in BC3.
+				//status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC1, 16, decode_flags | cDecodeFlagsBC1ForbidThreeColorBlocks, output_row_pitch_in_blocks_or_pixels, pState);
+				status = transcode_slice((uint8_t *)pOutput_blocks + 8, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cBC1, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+				if (!status)
+				{
+					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to BC3 RGB failed\n");
+				}
+			}
+			else
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to BC3 A failed\n");
+			}
+
+			break;
+#endif
+		}
+		case transcoder_texture_format::cTFBC5_RG:
+		{
+#if !BASISD_SUPPORT_DXT5A
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: DXT5A unsupported\n");
+			return false;
+#else
+			assert(bytes_per_block_or_pixel == 16);
+
+			//bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
+				//	uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const bool is_video, const bool is_alpha_slice, const uint32_t level_index, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
+				//	basisu_transcoder_state* pState = nullptr, bool astc_transcode_alpha = false, void* pAlpha_blocks = nullptr, uint32_t output_rows_in_pixels = 0);
+
+			// Decode the R data (actually the green channel of the color data slice in the basis file)
+			//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cBC4, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+			if (status)
+			{
+				if (basis_file_has_alpha_slices)
+				{
+					// Decode the G data (actually the green channel of the alpha data slice in the basis file)
+					//status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+					status = transcode_slice((uint8_t *)pOutput_blocks + 8, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cBC4, bytes_per_block_or_pixel, false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+					if (!status)
+					{
+						BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to BC5 1 failed\n");
+					}
+				}
+				else
+				{
+					basisu_transcoder::write_opaque_alpha_blocks(num_blocks_x, num_blocks_y, (uint8_t*)pOutput_blocks + 8, block_format::cBC4, 16, output_row_pitch_in_blocks_or_pixels);
+					status = true;
+				}
+			}
+			else
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to BC5 channel 0 failed\n");
+			}
+			break;
+#endif
+		}
+		case transcoder_texture_format::cTFASTC_4x4_RGBA:
+		{
+#if !BASISD_SUPPORT_ASTC
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: ASTC unsupported\n");
+			return false;
+#else
+			assert(bytes_per_block_or_pixel == 16);
+
+			if (basis_file_has_alpha_slices)
+			{
+				// First decode the alpha data to the output (we're using the output texture as a temp buffer here).
+				//status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cIndices, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+				status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cIndices, bytes_per_block_or_pixel, false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+				if (status)
+				{
+					// Now decode the color data and transcode to ASTC. The transcoder function will read the alpha selector data from the output texture as it converts and
+					// transcode both the alpha and color data at the same time to ASTC.
+					//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cASTC_4x4, 16, decode_flags | cDecodeFlagsOutputHasAlphaIndices, output_row_pitch_in_blocks_or_pixels, pState);
+					status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cASTC_4x4, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, true, nullptr, output_rows_in_pixels);
+				}
+			}
+			else
+				//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cASTC_4x4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+				status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cASTC_4x4, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ASTC failed (0)\n");
+			}
+
+			break;
+#endif
+		}
+		case transcoder_texture_format::cTFATC_RGB:
+		{
+#if !BASISD_SUPPORT_ATC
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: ATC unsupported\n");
+			return false;
+#else
+			//status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cATC_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pData, data_len, block_format::cATC_RGB, bytes_per_block_or_pixel, false, is_video, is_alpha_slice, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ATC_RGB failed\n");
+			}
+			break;
+#endif
+		}
+		case transcoder_texture_format::cTFATC_RGBA:
+		{
+#if !BASISD_SUPPORT_ATC
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: ATC unsupported\n");
+			return false;
+#elif !BASISD_SUPPORT_DXT5A
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: DXT5A unsupported\n");
+			return false;
+#else
+			assert(bytes_per_block_or_pixel == 16);
+
+			// First decode the alpha data 
+			if (basis_file_has_alpha_slices)
+			{
+				//status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+				status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cBC4, bytes_per_block_or_pixel, false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+			}
+			else
+			{
+				basisu_transcoder::write_opaque_alpha_blocks(num_blocks_x, num_blocks_y, pOutput_blocks, block_format::cBC4, 16, output_row_pitch_in_blocks_or_pixels);
+				status = true;
+			}
+
+			if (status)
+			{
+				//status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cATC_RGB, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+				status = transcode_slice((uint8_t *)pOutput_blocks + 8, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cATC_RGB, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+				if (!status)
+				{
+					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ATC RGB failed\n");
+				}
+			}
+			else
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ATC A failed\n");
+			}
+			break;
+#endif
+		}
+		case transcoder_texture_format::cTFPVRTC2_4_RGB:
+		{
+#if !BASISD_SUPPORT_PVRTC2
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: PVRTC2 unsupported\n");
+			return false;
+#else
+			//status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC2_4_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pData, data_len, block_format::cPVRTC2_4_RGB, bytes_per_block_or_pixel, false, is_video, is_alpha_slice, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to cPVRTC2_4_RGB failed\n");
+			}
+			break;
+#endif
+		}
+		case transcoder_texture_format::cTFPVRTC2_4_RGBA:
+		{
+#if !BASISD_SUPPORT_PVRTC2
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: PVRTC2 unsupported\n");
+			return false;
+#else
+			if (basis_file_has_alpha_slices)
+			{
+				// First decode the alpha data to the output (we're using the output texture as a temp buffer here).
+				//status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cIndices, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+				status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cIndices, bytes_per_block_or_pixel, false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+				if (!status)
+				{
+					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to failed\n");
+				}
+				else
+				{
+					// Now decode the color data and transcode to PVRTC2 RGBA. 
+					//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC2_4_RGBA, bytes_per_block_or_pixel, decode_flags | cDecodeFlagsOutputHasAlphaIndices, output_row_pitch_in_blocks_or_pixels, pState);
+					status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cPVRTC2_4_RGBA, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, true, nullptr, output_rows_in_pixels);
+				}
+			}
+			else
+				//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC2_4_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+				status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cPVRTC2_4_RGB, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to cPVRTC2_4_RGBA failed\n");
+			}
+
+			break;
+#endif
+		}
+		case transcoder_texture_format::cTFRGBA32:
+		{
+			// Raw 32bpp pixels, decoded in the usual raster order (NOT block order) into an image in memory.
+
+			// First decode the alpha data 
+			if (basis_file_has_alpha_slices)
+				//status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cA32, sizeof(uint32_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
+				status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cA32, sizeof(uint32_t), false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+			else
+				status = true;
+
+			if (status)
+			{
+				//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, basis_file_has_alpha_slices ? block_format::cRGB32 : block_format::cRGBA32, sizeof(uint32_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
+				status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, basis_file_has_alpha_slices ? block_format::cRGB32 : block_format::cRGBA32, sizeof(uint32_t), false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+				if (!status)
+				{
+					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to RGBA32 RGB failed\n");
+				}
+			}
+			else
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to RGBA32 A failed\n");
+			}
+
+			break;
+		}
+		case transcoder_texture_format::cTFRGB565:
+		case transcoder_texture_format::cTFBGR565:
+		{
+			// Raw 16bpp pixels, decoded in the usual raster order (NOT block order) into an image in memory.
+
+			//status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, (fmt == transcoder_texture_format::cTFRGB565) ? block_format::cRGB565 : block_format::cBGR565, sizeof(uint16_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pData, data_len, (target_format == transcoder_texture_format::cTFRGB565) ? block_format::cRGB565 : block_format::cBGR565, sizeof(uint16_t), false, is_video, is_alpha_slice, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to RGB565 RGB failed\n");
+			}
+
+			break;
+		}
+		case transcoder_texture_format::cTFRGBA4444:
+		{
+			// Raw 16bpp pixels, decoded in the usual raster order (NOT block order) into an image in memory.
+
+			// First decode the alpha data 
+			if (basis_file_has_alpha_slices)
+				//status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cRGBA4444_ALPHA, sizeof(uint16_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
+				status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cRGBA4444_ALPHA, sizeof(uint16_t), false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+			else
+				status = true;
+
+			if (status)
+			{
+				//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, basis_file_has_alpha_slices ? block_format::cRGBA4444_COLOR : block_format::cRGBA4444_COLOR_OPAQUE, sizeof(uint16_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
+				status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, basis_file_has_alpha_slices ? block_format::cRGBA4444_COLOR : block_format::cRGBA4444_COLOR_OPAQUE, sizeof(uint16_t), false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+				if (!status)
+				{
+					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to RGBA4444 RGB failed\n");
+				}
+			}
+			else
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to RGBA4444 A failed\n");
+			}
+
+			break;
+		}
+		case transcoder_texture_format::cTFFXT1_RGB:
+		{
+#if !BASISD_SUPPORT_FXT1
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: FXT1 unsupported\n");
+			return false;
+#else
+			//status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cFXT1_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pData, data_len, block_format::cFXT1_RGB, bytes_per_block_or_pixel, false, is_video, is_alpha_slice, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to FXT1_RGB failed\n");
+			}
+			break;
+#endif
+		}
+		case transcoder_texture_format::cTFETC2_EAC_R11:
+		{
+#if !BASISD_SUPPORT_ETC2_EAC_RG11
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: EAC_RG11 unsupported\n");
+			return false;
+#else
+			//status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pData, data_len, block_format::cETC2_EAC_R11, bytes_per_block_or_pixel, false, is_video, is_alpha_slice, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ETC2_EAC_R11 failed\n");
+			}
+
+			break;
+#endif
+		}
+		case transcoder_texture_format::cTFETC2_EAC_RG11:
+		{
+#if !BASISD_SUPPORT_ETC2_EAC_RG11
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: EAC_RG11 unsupported\n");
+			return false;
+#else
+			assert(bytes_per_block_or_pixel == 16);
+
+			if (basis_file_has_alpha_slices)
+			{
+				// First decode the alpha data to G
+				//status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+				status = transcode_slice((uint8_t *)pOutput_blocks + 8, num_blocks_x, num_blocks_y, pCompressed_data + alpha_offset, alpha_length, block_format::cETC2_EAC_R11, bytes_per_block_or_pixel, false, is_video, true, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+			}
+			else
+			{
+				basisu_transcoder::write_opaque_alpha_blocks(num_blocks_x, num_blocks_y, (uint8_t*)pOutput_blocks + 8, block_format::cETC2_EAC_R11, 16, output_row_pitch_in_blocks_or_pixels);
+				status = true;
+			}
+
+			if (status)
+			{
+				// Now decode the color data to R
+				//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+				status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + rgb_offset, rgb_length, block_format::cETC2_EAC_R11, bytes_per_block_or_pixel, false, is_video, false, level_index, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, false, nullptr, output_rows_in_pixels);
+				if (!status)
+				{
+					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ETC2_EAC_R11 R failed\n");
+				}
+			}
+			else
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: transcode_slice() to ETC2_EAC_R11 G failed\n");
+			}
+
+			break;
+#endif
+		}
+		default:
+		{
+			assert(0);
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_image: Invalid fmt\n");
+			break;
+		}
+		}
+
+		return status;
+	}
 	
 	basisu_lowlevel_uastc_transcoder::basisu_lowlevel_uastc_transcoder()
 	{
 	}
 
 	bool basisu_lowlevel_uastc_transcoder::transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
-		uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels,
+        uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels,
 		basisu_transcoder_state* pState, uint32_t output_rows_in_pixels, int channel0, int channel1, uint32_t decode_flags)
 	{
 		BASISU_NOTE_UNUSED(pState);
 		BASISU_NOTE_UNUSED(bc1_allow_threecolor_blocks);
 
+		assert(g_transcoder_initialized);
+		if (!g_transcoder_initialized)
+		{
+			BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_slice: Transcoder not globally initialized.\n");
+			return false;
+		}
+
 #if BASISD_SUPPORT_UASTC
 		const uint32_t total_blocks = num_blocks_x * num_blocks_y;
 
 		if (!output_row_pitch_in_blocks_or_pixels)
 		{
 			if (basis_block_format_is_uncompressed(fmt))
-				output_row_pitch_in_blocks_or_pixels = slice_desc.m_orig_width;
+				output_row_pitch_in_blocks_or_pixels = orig_width;
 			else
 			{
 				if (fmt == block_format::cFXT1_RGB)
-					output_row_pitch_in_blocks_or_pixels = (slice_desc.m_orig_width + 7) / 8;
+					output_row_pitch_in_blocks_or_pixels = (orig_width + 7) / 8;
 				else
 					output_row_pitch_in_blocks_or_pixels = num_blocks_x;
 			}
@@ -13437,7 +18664,7 @@
 		if (basis_block_format_is_uncompressed(fmt))
 		{
 			if (!output_rows_in_pixels)
-				output_rows_in_pixels = slice_desc.m_orig_height;
+				output_rows_in_pixels = orig_height;
 		}
 
 		uint32_t total_expected_block_bytes = sizeof(uastc_block) * total_blocks;
@@ -13450,7 +18677,7 @@
 		const uastc_block* pSource_block = reinterpret_cast<const uastc_block *>(pImage_data);
 
 		const bool high_quality = (decode_flags & cDecodeFlagsHighQuality) != 0;
-		const bool from_alpha = ((header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0) &&	(decode_flags & cDecodeFlagsTranscodeAlphaDataToOpaqueFormats) != 0;
+		const bool from_alpha = has_alpha && (decode_flags & cDecodeFlagsTranscodeAlphaDataToOpaqueFormats) != 0;
 
 		bool status = false;
 		if ((fmt == block_format::cPVRTC1_4_RGB) || (fmt == block_format::cPVRTC1_4_RGBA))
@@ -13531,7 +18758,7 @@
 					{
 						if (channel0 < 0)
 							channel0 = 0;
-						if (channel1 < 3)
+						if (channel1 < 0)
 							channel1 = 3;
 						status = transcode_uastc_to_etc2_eac_rg11(*pSource_block, pDst_block, high_quality, channel0, channel1);
 						break;
@@ -13647,8 +18874,6 @@
 		BASISU_NOTE_UNUSED(channel1);
 		BASISU_NOTE_UNUSED(output_rows_in_pixels);
 		BASISU_NOTE_UNUSED(output_row_pitch_in_blocks_or_pixels);
-		BASISU_NOTE_UNUSED(slice_desc);
-		BASISU_NOTE_UNUSED(header);
 		BASISU_NOTE_UNUSED(output_block_or_pixel_stride_in_bytes);
 		BASISU_NOTE_UNUSED(fmt);
 		BASISU_NOTE_UNUSED(image_data_size);
@@ -13660,9 +18885,285 @@
 		return false;
 #endif
 	}
+		
+	bool basisu_lowlevel_uastc_transcoder::transcode_image(
+		transcoder_texture_format target_format,
+		void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
+		const uint8_t* pCompressed_data, uint32_t compressed_data_length,
+		uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
+		uint32_t slice_offset, uint32_t slice_length,
+		uint32_t decode_flags,
+		bool has_alpha,
+		bool is_video,
+		uint32_t output_row_pitch_in_blocks_or_pixels,
+		basisu_transcoder_state* pState,
+		uint32_t output_rows_in_pixels,
+		int channel0, int channel1)
+	{
+		BASISU_NOTE_UNUSED(is_video);
+		BASISU_NOTE_UNUSED(level_index);
+
+		if (((uint64_t)slice_offset + slice_length) > (uint64_t)compressed_data_length)
+		{
+			BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: source data buffer too small\n");
+			return false;
+		}	
+
+		if ((target_format == transcoder_texture_format::cTFPVRTC1_4_RGB) || (target_format == transcoder_texture_format::cTFPVRTC1_4_RGBA))
+		{
+			if ((!basisu::is_pow2(num_blocks_x * 4)) || (!basisu::is_pow2(num_blocks_y * 4)))
+			{
+				// PVRTC1 only supports power of 2 dimensions
+				BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: PVRTC1 only supports power of 2 dimensions\n");
+				return false;
+			}
+		}
+
+		if ((target_format == transcoder_texture_format::cTFPVRTC1_4_RGBA) && (!has_alpha))
+		{
+			// Switch to PVRTC1 RGB if the input doesn't have alpha.
+			target_format = transcoder_texture_format::cTFPVRTC1_4_RGB;
+		}
+
+		const bool transcode_alpha_data_to_opaque_formats = (decode_flags & cDecodeFlagsTranscodeAlphaDataToOpaqueFormats) != 0;
+		const uint32_t bytes_per_block_or_pixel = basis_get_bytes_per_block_or_pixel(target_format);
+		const uint32_t total_slice_blocks = num_blocks_x * num_blocks_y;
+
+		if (!basis_validate_output_buffer_size(target_format, output_blocks_buf_size_in_blocks_or_pixels, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, output_rows_in_pixels, total_slice_blocks))
+		{
+			BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: output buffer size too small\n");
+			return false;
+		}
+				
+		bool status = false;
+
+		// UASTC4x4
+		switch (target_format)
+		{
+		case transcoder_texture_format::cTFETC1_RGB:
+		{
+			//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC1, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cETC1,
+				bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1);
+				
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: transcode_slice() to ETC1 failed\n");
+			}
+			break;
+		}
+		case transcoder_texture_format::cTFETC2_RGBA:
+		{
+			//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_RGBA, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cETC2_RGBA,
+				bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: transcode_slice() to ETC2 failed\n");
+			}
+			break;
+		}
+		case transcoder_texture_format::cTFBC1_RGB:
+		{
+			// TODO: ETC1S allows BC1 from alpha channel. That doesn't seem actually useful, though.
+			//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC1, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cBC1,
+				bytes_per_block_or_pixel, true, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: transcode_slice() to BC1 failed\n");
+			}
+			break;
+		}
+		case transcoder_texture_format::cTFBC3_RGBA:
+		{
+			//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC3, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cBC3,
+				bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels, channel0, channel1);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: transcode_slice() to BC3 failed\n");
+			}
+			break;
+		}
+		case transcoder_texture_format::cTFBC4_R:
+		{
+			//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState,
+			//	nullptr, 0,
+			//	((has_alpha) && (transcode_alpha_data_to_opaque_formats)) ? 3 : 0);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cBC4,
+				bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels,
+				((has_alpha) && (transcode_alpha_data_to_opaque_formats)) ? 3 : 0);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: transcode_slice() to BC4 failed\n");
+			}
+			break;
+		}
+		case transcoder_texture_format::cTFBC5_RG:
+		{
+			//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC5, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState,
+			//	nullptr, 0,
+			//	0, 3);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cBC5,
+				bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels,
+				0, 3);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: transcode_slice() to BC5 failed\n");
+			}
+			break;
+		}
+		case transcoder_texture_format::cTFBC7_RGBA:
+		case transcoder_texture_format::cTFBC7_ALT:
+		{
+			//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC7, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cBC7,
+				bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: transcode_slice() to BC7 failed\n");
+			}
+			break;
+		}
+		case transcoder_texture_format::cTFPVRTC1_4_RGB:
+		{
+			//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC1_4_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cPVRTC1_4_RGB,
+				bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: transcode_slice() to PVRTC1 RGB 4bpp failed\n");
+			}
+			break;
+		}
+		case transcoder_texture_format::cTFPVRTC1_4_RGBA:
+		{
+			//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC1_4_RGBA, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cPVRTC1_4_RGBA,
+				bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: transcode_slice() to PVRTC1 RGBA 4bpp failed\n");
+			}
+			break;
+		}
+		case transcoder_texture_format::cTFASTC_4x4_RGBA:
+		{
+			//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cASTC_4x4, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cASTC_4x4,
+				bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: transcode_slice() to ASTC 4x4 failed\n");
+			}
+			break;
+		}
+		case transcoder_texture_format::cTFATC_RGB:
+		case transcoder_texture_format::cTFATC_RGBA:
+		{
+			BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: UASTC->ATC currently unsupported\n");
+			return false;
+		}
+		case transcoder_texture_format::cTFFXT1_RGB:
+		{
+			BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: UASTC->FXT1 currently unsupported\n");
+			return false;
+		}
+		case transcoder_texture_format::cTFPVRTC2_4_RGB:
+		{
+			BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: UASTC->PVRTC2 currently unsupported\n");
+			return false;
+		}
+		case transcoder_texture_format::cTFPVRTC2_4_RGBA:
+		{
+			BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: UASTC->PVRTC2 currently unsupported\n");
+			return false;
+		}
+		case transcoder_texture_format::cTFETC2_EAC_R11:
+		{
+			//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState,
+			//	nullptr, 0,
+			//	((has_alpha) && (transcode_alpha_data_to_opaque_formats)) ? 3 : 0);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cETC2_EAC_R11,
+				bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels,
+				((has_alpha) && (transcode_alpha_data_to_opaque_formats)) ? 3 : 0);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: transcode_slice() to EAC R11 failed\n");
+			}
+			break;
+		}
+		case transcoder_texture_format::cTFETC2_EAC_RG11:
+		{
+			//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_RG11, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState,
+			//	nullptr, 0,
+			//	0, 3);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cETC2_EAC_RG11,
+				bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels,
+				0, 3);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_basisu_lowlevel_uastc_transcodertranscoder::transcode_image: transcode_slice() to EAC RG11 failed\n");
+			}
+			break;
+		}
+		case transcoder_texture_format::cTFRGBA32:
+		{
+			//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cRGBA32, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cRGBA32,
+				bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: transcode_slice() to RGBA32 failed\n");
+			}
+			break;
+		}
+		case transcoder_texture_format::cTFRGB565:
+		{
+			//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cRGB565, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cRGB565,
+				bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: transcode_slice() to RGB565 failed\n");
+			}
+			break;
+		}
+		case transcoder_texture_format::cTFBGR565:
+		{
+			//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBGR565, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cBGR565,
+				bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: transcode_slice() to RGB565 failed\n");
+			}
+			break;
+		}
+		case transcoder_texture_format::cTFRGBA4444:
+		{
+			//status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cRGBA4444, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
+			status = transcode_slice(pOutput_blocks, num_blocks_x, num_blocks_y, pCompressed_data + slice_offset, slice_length, block_format::cRGBA4444,
+				bytes_per_block_or_pixel, false, has_alpha, orig_width, orig_height, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels);
+			if (!status)
+			{
+				BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: transcode_slice() to RGBA4444 failed\n");
+			}
+			break;
+		}
+		default:
+		{
+			assert(0);
+			BASISU_DEVEL_ERROR("basisu_lowlevel_uastc_transcoder::transcode_image: Invalid format\n");
+			break;
+		}
+		}
+
+		return status;
+	}
 	
-	basisu_transcoder::basisu_transcoder(const etc1_global_selector_codebook* pGlobal_sel_codebook) :
-		m_lowlevel_etc1s_decoder(pGlobal_sel_codebook),
+	basisu_transcoder::basisu_transcoder() :
 		m_ready_to_transcode(false)
 	{
 	}
@@ -14053,6 +19554,21 @@
 		image_info.m_total_blocks = image_info.m_num_blocks_x * image_info.m_num_blocks_y;
 		image_info.m_first_slice_index = slice_index;
 
+		image_info.m_rgb_file_ofs = slice_desc.m_file_ofs;
+		image_info.m_rgb_file_len = slice_desc.m_file_size;
+		image_info.m_alpha_file_ofs = 0;
+		image_info.m_alpha_file_len = 0;
+
+		if (pHeader->m_tex_format == (int)basis_tex_format::cETC1S)
+		{
+			if (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices)
+			{
+				assert((slice_index + 1) < (int)pHeader->m_total_slices);
+				image_info.m_alpha_file_ofs = pSlice_descs[slice_index + 1].m_file_ofs;
+				image_info.m_alpha_file_len = pSlice_descs[slice_index + 1].m_file_size;
+			}
+		}
+
 		return true;
 	}
 
@@ -14072,11 +19588,14 @@
 		file_info.m_total_header_size = sizeof(basis_file_header) + pHeader->m_total_slices * sizeof(basis_slice_desc);
 
 		file_info.m_total_selectors = pHeader->m_total_selectors;
+		file_info.m_selector_codebook_ofs = pHeader->m_selector_cb_file_ofs;
 		file_info.m_selector_codebook_size = pHeader->m_selector_cb_file_size;
 
 		file_info.m_total_endpoints = pHeader->m_total_endpoints;
+		file_info.m_endpoint_codebook_ofs = pHeader->m_endpoint_cb_file_ofs;
 		file_info.m_endpoint_codebook_size = pHeader->m_endpoint_cb_file_size;
 
+		file_info.m_tables_ofs = pHeader->m_tables_file_ofs;
 		file_info.m_tables_size = pHeader->m_tables_file_size;
 
 		file_info.m_tex_format = static_cast<basis_tex_format>(static_cast<int>(pHeader->m_tex_format));
@@ -14161,46 +19680,84 @@
 
 		if (pHeader->m_tex_format == (int)basis_tex_format::cETC1S)
 		{
-			if (m_lowlevel_etc1s_decoder.m_endpoints.size())
+			if (m_lowlevel_etc1s_decoder.m_local_endpoints.size())
 			{
 				m_lowlevel_etc1s_decoder.clear();
 			}
 
-			if (!pHeader->m_endpoint_cb_file_size || !pHeader->m_selector_cb_file_size || !pHeader->m_tables_file_size)
+			if (pHeader->m_flags & cBASISHeaderFlagUsesGlobalCodebook)
 			{
-				BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted (0)\n");
+				if (!m_lowlevel_etc1s_decoder.get_global_codebooks())
+				{
+					BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: File uses global codebooks, but set_global_codebooks() has not been called\n");
+					return false;
+				}
+				if (!m_lowlevel_etc1s_decoder.get_global_codebooks()->get_endpoints().size())
+				{
+					BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: Global codebooks must be unpacked first by calling start_transcoding()\n");
+					return false;
+				}
+				if ((m_lowlevel_etc1s_decoder.get_global_codebooks()->get_endpoints().size() != pHeader->m_total_endpoints) ||
+					 (m_lowlevel_etc1s_decoder.get_global_codebooks()->get_selectors().size() != pHeader->m_total_selectors))
+				{
+					BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: Global codebook size mismatch (wrong codebooks for file).\n");
+					return false;
+				}
+				if (!pHeader->m_tables_file_size)
+				{
+					BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted (2)\n");
+					return false;
+				}
+				if (pHeader->m_tables_file_ofs > data_size)
+				{
+					BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (4)\n");
+					return false;
+				}
+				if (pHeader->m_tables_file_size > (data_size - pHeader->m_tables_file_ofs))
+				{
+					BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (5)\n");
+					return false;
+				}
 			}
-
-			if ((pHeader->m_endpoint_cb_file_ofs > data_size) || (pHeader->m_selector_cb_file_ofs > data_size) || (pHeader->m_tables_file_ofs > data_size))
+			else
 			{
-				BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (1)\n");
-				return false;
-			}
+				if (!pHeader->m_endpoint_cb_file_size || !pHeader->m_selector_cb_file_size || !pHeader->m_tables_file_size)
+				{
+					BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted (0)\n");
+						return false;
+				}
 
-			if (pHeader->m_endpoint_cb_file_size > (data_size - pHeader->m_endpoint_cb_file_ofs))
-			{
-				BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (2)\n");
-				return false;
-			}
+				if ((pHeader->m_endpoint_cb_file_ofs > data_size) || (pHeader->m_selector_cb_file_ofs > data_size) || (pHeader->m_tables_file_ofs > data_size))
+				{
+					BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (1)\n");
+					return false;
+				}
 
-			if (pHeader->m_selector_cb_file_size > (data_size - pHeader->m_selector_cb_file_ofs))
-			{
-				BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (3)\n");
-				return false;
-			}
+				if (pHeader->m_endpoint_cb_file_size > (data_size - pHeader->m_endpoint_cb_file_ofs))
+				{
+					BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (2)\n");
+					return false;
+				}
 
-			if (pHeader->m_tables_file_size > (data_size - pHeader->m_tables_file_ofs))
-			{
-				BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (3)\n");
-				return false;
-			}
+				if (pHeader->m_selector_cb_file_size > (data_size - pHeader->m_selector_cb_file_ofs))
+				{
+					BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (3)\n");
+					return false;
+				}
 
-			if (!m_lowlevel_etc1s_decoder.decode_palettes(
-				pHeader->m_total_endpoints, pDataU8 + pHeader->m_endpoint_cb_file_ofs, pHeader->m_endpoint_cb_file_size,
-				pHeader->m_total_selectors, pDataU8 + pHeader->m_selector_cb_file_ofs, pHeader->m_selector_cb_file_size))
-			{
-				BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: decode_palettes failed\n");
-				return false;
+				if (pHeader->m_tables_file_size > (data_size - pHeader->m_tables_file_ofs))
+				{
+					BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: file is corrupted or passed in buffer too small (3)\n");
+					return false;
+				}
+
+				if (!m_lowlevel_etc1s_decoder.decode_palettes(
+					pHeader->m_total_endpoints, pDataU8 + pHeader->m_endpoint_cb_file_ofs, pHeader->m_endpoint_cb_file_size,
+					pHeader->m_total_selectors, pDataU8 + pHeader->m_selector_cb_file_ofs, pHeader->m_selector_cb_file_size))
+				{
+					BASISU_DEVEL_ERROR("basisu_transcoder::start_transcoding: decode_palettes failed\n");
+					return false;
+				}
 			}
 
 			if (!m_lowlevel_etc1s_decoder.decode_tables(pDataU8 + pHeader->m_tables_file_ofs, pHeader->m_tables_file_size))
@@ -14212,7 +19769,7 @@
 		else
 		{
 			// Nothing special to do for UASTC.
-			if (m_lowlevel_etc1s_decoder.m_endpoints.size())
+			if (m_lowlevel_etc1s_decoder.m_local_endpoints.size())
 			{
 				m_lowlevel_etc1s_decoder.clear();
 			}
@@ -14405,9 +19962,9 @@
 		return -1;
 	}
 
-	static void write_opaque_alpha_blocks(
+	void basisu_transcoder::write_opaque_alpha_blocks(
 		uint32_t num_blocks_x, uint32_t num_blocks_y,
-		void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels, block_format fmt,
+		void* pOutput_blocks, block_format fmt,
 		uint32_t block_stride_in_bytes, uint32_t output_row_pitch_in_blocks_or_pixels)
 	{
 		// 'num_blocks_y', 'pOutput_blocks' & 'block_stride_in_bytes' unused
@@ -14415,7 +19972,6 @@
 		BASISU_NOTE_UNUSED(num_blocks_y);
 		BASISU_NOTE_UNUSED(pOutput_blocks);
 		BASISU_NOTE_UNUSED(block_stride_in_bytes);
-		BASISU_NOTE_UNUSED(output_blocks_buf_size_in_blocks_or_pixels);
 
 		if (!output_row_pitch_in_blocks_or_pixels)
 			output_row_pitch_in_blocks_or_pixels = num_blocks_x;
@@ -14478,7 +20034,7 @@
 			return false;
 		}
 
-		const bool transcode_alpha_data_to_opaque_formats = (decode_flags & cDecodeFlagsTranscodeAlphaDataToOpaqueFormats) != 0;
+		//const bool transcode_alpha_data_to_opaque_formats = (decode_flags & cDecodeFlagsTranscodeAlphaDataToOpaqueFormats) != 0;
 
 		if (decode_flags & cDecodeFlagsPVRTCDecodeToNextPow2)
 		{
@@ -14566,708 +20122,40 @@
 		
 		if (pHeader->m_tex_format == (int)basis_tex_format::cUASTC4x4)
 		{
-			// UASTC4x4
-			switch (fmt)
-			{
-			case transcoder_texture_format::cTFETC1_RGB:
-			{
-				status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC1, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC1 failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFETC2_RGBA:
-			{
-				status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_RGBA, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC2 failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFBC1_RGB:
-			{
-				// TODO: ETC1S allows BC1 from alpha channel. That doesn't seem actually useful, though.
-				status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC1, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC1 failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFBC3_RGBA:
-			{
-				status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC3, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC3 failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFBC4_R:
-			{
-				status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState,
-					nullptr, 0,
-					((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats)) ? 3 : 0);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC4 failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFBC5_RG:
-			{
-				status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC5, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState, 
-					nullptr, 0, 
-					0, 3);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC5 failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFBC7_RGBA:
-			case transcoder_texture_format::cTFBC7_ALT:
-			{
-				status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC7, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC7 failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFPVRTC1_4_RGB:
-			{
-				status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC1_4_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to PVRTC1 RGB 4bpp failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFPVRTC1_4_RGBA:
-			{
-				status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC1_4_RGBA, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to PVRTC1 RGBA 4bpp failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFASTC_4x4_RGBA:
-			{
-				status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cASTC_4x4, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ASTC 4x4 failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFATC_RGB:
-			case transcoder_texture_format::cTFATC_RGBA:
-			{
-				BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: UASTC->ATC currently unsupported\n");
-				return false;
-			}
-			case transcoder_texture_format::cTFFXT1_RGB:
-			{
-				BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: UASTC->FXT1 currently unsupported\n");
-				return false;
-			}
-			case transcoder_texture_format::cTFPVRTC2_4_RGB:
-			{
-				BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: UASTC->PVRTC2 currently unsupported\n");
-				return false;
-			}
-			case transcoder_texture_format::cTFPVRTC2_4_RGBA:
-			{
-				BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: UASTC->PVRTC2 currently unsupported\n");
-				return false;
-			}
-			case transcoder_texture_format::cTFETC2_EAC_R11:
-			{
-				status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState, 
-					nullptr, 0,
-					((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats)) ? 3 : 0);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to EAC R11 failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFETC2_EAC_RG11:
-			{
-				status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_RG11, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState, 
-					nullptr, 0, 
-					0, 3);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to EAC RG11 failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFRGBA32:
-			{
-				status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cRGBA32, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGBA32 failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFRGB565:
-			{
-				status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cRGB565, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGB565 failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFBGR565:
-			{
-				status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBGR565, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGB565 failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFRGBA4444:
-			{
-				status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cRGBA4444, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGBA4444 failed\n");
-				}
-				break;
-			}
-			default:
-			{
-				assert(0);
-				BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: Invalid fmt\n");
-				break;
-			}
-			}
+			const basis_slice_desc* pSlice_desc = &pSlice_descs[slice_index];
+
+			// Use the container independent image transcode method.
+			status = m_lowlevel_uastc_decoder.transcode_image(fmt,
+				pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels,
+				(const uint8_t*)pData, data_size, pSlice_desc->m_num_blocks_x, pSlice_desc->m_num_blocks_y, pSlice_desc->m_orig_width, pSlice_desc->m_orig_height, pSlice_desc->m_level_index,
+				pSlice_desc->m_file_ofs, pSlice_desc->m_file_size,
+				decode_flags, basis_file_has_alpha_slices, pHeader->m_tex_type == cBASISTexTypeVideoFrames, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels);
 		}
 		else 
 		{
 			// ETC1S
+			const basis_slice_desc* pSlice_desc = &pSlice_descs[slice_index];
+			const basis_slice_desc* pAlpha_slice_desc = basis_file_has_alpha_slices ? &pSlice_descs[slice_index + 1] : nullptr;
 
-			switch (fmt)
+			assert((pSlice_desc->m_flags & cSliceDescFlagsHasAlpha) == 0);
+
+			if (pAlpha_slice_desc)
 			{
-			case transcoder_texture_format::cTFETC1_RGB:
-			{
-				uint32_t slice_index_to_decode = slice_index;
-				// If the caller wants us to transcode the mip level's alpha data, then use the next slice.
-				if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
-					slice_index_to_decode++;
-
-				status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC1, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC1 failed\n");
-				}
-				break;
+				// Basic sanity checks
+				assert((pAlpha_slice_desc->m_flags & cSliceDescFlagsHasAlpha) != 0);
+				assert(pSlice_desc->m_num_blocks_x == pAlpha_slice_desc->m_num_blocks_x);
+				assert(pSlice_desc->m_num_blocks_y == pAlpha_slice_desc->m_num_blocks_y);
+				assert(pSlice_desc->m_level_index == pAlpha_slice_desc->m_level_index);
 			}
-			case transcoder_texture_format::cTFBC1_RGB:
-			{
-#if !BASISD_SUPPORT_DXT1
-            BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: DXT1 unsupported\n");
-				return false;
-#endif
-				uint32_t slice_index_to_decode = slice_index;
-				// If the caller wants us to transcode the mip level's alpha data, then use the next slice.
-				if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
-					slice_index_to_decode++;
 
-				status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC1, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC1 failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFBC4_R:
-			{
-#if !BASISD_SUPPORT_DXT5A
-            BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: DXT5A unsupported\n");
-				return false;
-#endif
-				uint32_t slice_index_to_decode = slice_index;
-				// If the caller wants us to transcode the mip level's alpha data, then use the next slice.
-				if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
-					slice_index_to_decode++;
+			// Use the container independent image transcode method.
+			status = m_lowlevel_etc1s_decoder.transcode_image(fmt,
+				pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels,
+				(const uint8_t *)pData, data_size, pSlice_desc->m_num_blocks_x, pSlice_desc->m_num_blocks_y, pSlice_desc->m_orig_width, pSlice_desc->m_orig_height, pSlice_desc->m_level_index,
+				pSlice_desc->m_file_ofs, pSlice_desc->m_file_size,
+				(pAlpha_slice_desc != nullptr) ? (uint32_t)pAlpha_slice_desc->m_file_ofs : 0U, (pAlpha_slice_desc != nullptr) ? (uint32_t)pAlpha_slice_desc->m_file_size : 0U,
+				decode_flags, basis_file_has_alpha_slices, pHeader->m_tex_type == cBASISTexTypeVideoFrames, output_row_pitch_in_blocks_or_pixels, pState, output_rows_in_pixels);
 
-				status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC4 failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFPVRTC1_4_RGB:
-			{
-#if !BASISD_SUPPORT_PVRTC1
-            BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: PVRTC1 unsupported\n");
-				return false;
-#endif
-				uint32_t slice_index_to_decode = slice_index;
-				// If the caller wants us to transcode the mip level's alpha data, then use the next slice.
-				if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
-					slice_index_to_decode++;
-
-				// output_row_pitch_in_blocks_or_pixels is actually ignored because we're transcoding to PVRTC1. (Print a dev warning if it's != 0?)
-				status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC1_4_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to PVRTC1 4 RGB failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFPVRTC1_4_RGBA:
-			{
-#if !BASISD_SUPPORT_PVRTC1
-            BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: PVRTC1 unsupported\n");
-				return false;
-#endif
-				assert(basis_file_has_alpha_slices);
-
-				// Temp buffer to hold alpha block endpoint/selector indices
-				std::vector<uint32_t> temp_block_indices(total_slice_blocks);
-
-				// First transcode alpha data to temp buffer
-				status = transcode_slice(pData, data_size, slice_index + 1, &temp_block_indices[0], total_slice_blocks, block_format::cIndices, sizeof(uint32_t), decode_flags, pSlice_descs[slice_index].m_num_blocks_x, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to PVRTC1 4 RGBA failed (0)\n");
-				}
-				else
-				{
-					// output_row_pitch_in_blocks_or_pixels is actually ignored because we're transcoding to PVRTC1. (Print a dev warning if it's != 0?)
-					status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC1_4_RGBA, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState, &temp_block_indices[0]);
-					if (!status)
-					{
-						BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to PVRTC1 4 RGBA failed (1)\n");
-					}
-				}
-
-				break;
-			}
-			case transcoder_texture_format::cTFBC7_RGBA:
-			case transcoder_texture_format::cTFBC7_ALT:
-			{
-#if !BASISD_SUPPORT_BC7_MODE5
-            BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: BC7 unsupported\n");
-				return false;
-#else
-				assert(bytes_per_block_or_pixel == 16);
-				// We used to support transcoding just alpha to BC7 - but is that useful at all?
-
-				// First transcode the color slice. The cBC7_M5_COLOR transcoder will output opaque mode 5 blocks.
-				status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC7_M5_COLOR, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-
-				if ((status) && (basis_file_has_alpha_slices))
-				{
-					// Now transcode the alpha slice. The cBC7_M5_ALPHA transcoder will now change the opaque mode 5 blocks to blocks with alpha.
-					status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC7_M5_ALPHA, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				}
-            
-            if (!status)
-            {
-               BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC7 failed (0)\n");
-            }
-
-				break;
-#endif
-			}
-			case transcoder_texture_format::cTFETC2_RGBA:
-			{
-#if !BASISD_SUPPORT_ETC2_EAC_A8
-            BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: ETC2 EAC A8 unsupported\n");
-				return false;
-#endif
-				assert(bytes_per_block_or_pixel == 16);
-
-				if (basis_file_has_alpha_slices)
-				{
-					// First decode the alpha data 
-					status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_A8, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				}
-				else
-				{
-					write_opaque_alpha_blocks(pSlice_descs[slice_index].m_num_blocks_x, pSlice_descs[slice_index].m_num_blocks_y, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_A8, 16, output_row_pitch_in_blocks_or_pixels);
-					status = true;
-				}
-
-				if (status)
-				{
-					// Now decode the color data
-					status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC1, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-					if (!status)
-					{
-						BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC2 RGB failed\n");
-					}
-				}
-				else
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC2 A failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFBC3_RGBA:
-			{
-#if !BASISD_SUPPORT_DXT1
-            BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: DXT1 unsupported\n");
-				return false;
-#endif
-#if !BASISD_SUPPORT_DXT5A
-            BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: DXT5A unsupported\n");
-				return false;
-#endif
-				assert(bytes_per_block_or_pixel == 16);
-
-				// First decode the alpha data 
-				if (basis_file_has_alpha_slices)
-				{
-					status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				}
-				else
-				{
-					write_opaque_alpha_blocks(pSlice_descs[slice_index].m_num_blocks_x, pSlice_descs[slice_index].m_num_blocks_y, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, output_row_pitch_in_blocks_or_pixels);
-					status = true;
-				}
-
-				if (status)
-				{
-					// Now decode the color data. Forbid 3 color blocks, which aren't allowed in BC3.
-					status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC1, 16, decode_flags | cDecodeFlagsBC1ForbidThreeColorBlocks, output_row_pitch_in_blocks_or_pixels, pState);
-					if (!status)
-					{
-						BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC3 RGB failed\n");
-					}
-				}
-				else
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC3 A failed\n");
-				}
-
-				break;
-			}
-			case transcoder_texture_format::cTFBC5_RG:
-			{
-#if !BASISD_SUPPORT_DXT5A
-            BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: DXT5A unsupported\n");
-				return false;
-#endif
-				assert(bytes_per_block_or_pixel == 16);
-
-				// Decode the R data (actually the green channel of the color data slice in the basis file)
-				status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (status)
-				{
-					if (basis_file_has_alpha_slices)
-					{
-						// Decode the G data (actually the green channel of the alpha data slice in the basis file)
-						status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-						if (!status)
-						{
-							BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC5 1 failed\n");
-						}
-					}
-					else
-					{
-						write_opaque_alpha_blocks(pSlice_descs[slice_index].m_num_blocks_x, pSlice_descs[slice_index].m_num_blocks_y, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, output_row_pitch_in_blocks_or_pixels);
-						status = true;
-					}
-				}
-				else
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to BC5 channel 0 failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFASTC_4x4_RGBA:
-			{
-#if !BASISD_SUPPORT_ASTC
-            BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: ASTC unsupported\n");
-				return false;
-#endif
-				assert(bytes_per_block_or_pixel == 16);
-
-				if (basis_file_has_alpha_slices)
-				{
-					// First decode the alpha data to the output (we're using the output texture as a temp buffer here).
-					status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cIndices, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-					if (status)
-					{
-						// Now decode the color data and transcode to ASTC. The transcoder function will read the alpha selector data from the output texture as it converts and
-						// transcode both the alpha and color data at the same time to ASTC.
-						status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cASTC_4x4, 16, decode_flags | cDecodeFlagsOutputHasAlphaIndices, output_row_pitch_in_blocks_or_pixels, pState);
-					}
-				}
-				else
-					status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cASTC_4x4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-
-            if (!status)
-            {               
-               BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ASTC failed (0)\n");
-            }
-
-				break;
-			}
-			case transcoder_texture_format::cTFATC_RGB:
-			{
-#if !BASISD_SUPPORT_ATC
-            BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: ATC unsupported\n");
-				return false;
-#endif
-				uint32_t slice_index_to_decode = slice_index;
-				// If the caller wants us to transcode the mip level's alpha data, then use the next slice.
-				if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
-					slice_index_to_decode++;
-
-				status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cATC_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ATC_RGB failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFATC_RGBA:
-			{
-#if !BASISD_SUPPORT_ATC
-            BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: ATC unsupported\n");
-				return false;
-#endif
-#if !BASISD_SUPPORT_DXT5A
-            BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: DXT5A unsupported\n");
-   			return false;
-#endif
-				assert(bytes_per_block_or_pixel == 16);
-
-				// First decode the alpha data 
-				if (basis_file_has_alpha_slices)
-				{
-					status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				}
-				else
-				{
-					write_opaque_alpha_blocks(pSlice_descs[slice_index].m_num_blocks_x, pSlice_descs[slice_index].m_num_blocks_y, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cBC4, 16, output_row_pitch_in_blocks_or_pixels);
-					status = true;
-				}
-
-				if (status)
-				{
-					status = transcode_slice(pData, data_size, slice_index, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cATC_RGB, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-					if (!status)
-					{
-						BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ATC RGB failed\n");
-					}
-				}
-				else
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ATC A failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFPVRTC2_4_RGB:
-			{
-#if !BASISD_SUPPORT_PVRTC2
-            BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: PVRTC2 unsupported\n");
-				return false;
-#endif
-				uint32_t slice_index_to_decode = slice_index;
-				// If the caller wants us to transcode the mip level's alpha data, then use the next slice.
-				if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
-					slice_index_to_decode++;
-
-				status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC2_4_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to cPVRTC2_4_RGB failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFPVRTC2_4_RGBA:
-			{
-#if !BASISD_SUPPORT_PVRTC2
-            BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: PVRTC2 unsupported\n");
-				return false;
-#endif
-				if (basis_file_has_alpha_slices)
-				{
-					// First decode the alpha data to the output (we're using the output texture as a temp buffer here).
-					status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cIndices, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-					if (!status)
-					{
-						BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to failed\n");
-					}
-					else
-					{
-						// Now decode the color data and transcode to PVRTC2 RGBA. 
-						status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC2_4_RGBA, bytes_per_block_or_pixel, decode_flags | cDecodeFlagsOutputHasAlphaIndices, output_row_pitch_in_blocks_or_pixels, pState);
-					}
-				}
-				else
-					status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cPVRTC2_4_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to cPVRTC2_4_RGBA failed\n");
-				}
-
-				break;
-			}
-			case transcoder_texture_format::cTFRGBA32:
-			{
-				// Raw 32bpp pixels, decoded in the usual raster order (NOT block order) into an image in memory.
-
-				// First decode the alpha data 
-				if (basis_file_has_alpha_slices)
-					status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cA32, sizeof(uint32_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
-				else
-					status = true;
-
-				if (status)
-				{
-					status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, basis_file_has_alpha_slices ? block_format::cRGB32 : block_format::cRGBA32, sizeof(uint32_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
-					if (!status)
-					{
-						BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGBA32 RGB failed\n");
-					}
-				}
-				else
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGBA32 A failed\n");
-				}
-
-				break;
-			}
-			case transcoder_texture_format::cTFRGB565:
-			case transcoder_texture_format::cTFBGR565:
-			{
-				// Raw 16bpp pixels, decoded in the usual raster order (NOT block order) into an image in memory.
-
-				uint32_t slice_index_to_decode = slice_index;
-				// If the caller wants us to transcode the mip level's alpha data, then use the next slice.
-				if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
-					slice_index_to_decode++;
-
-				status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, (fmt == transcoder_texture_format::cTFRGB565) ? block_format::cRGB565 : block_format::cBGR565, sizeof(uint16_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGB565 RGB failed\n");
-				}
-
-				break;
-			}
-			case transcoder_texture_format::cTFRGBA4444:
-			{
-				// Raw 16bpp pixels, decoded in the usual raster order (NOT block order) into an image in memory.
-
-				// First decode the alpha data 
-				if (basis_file_has_alpha_slices)
-					status = transcode_slice(pData, data_size, slice_index + 1, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cRGBA4444_ALPHA, sizeof(uint16_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
-				else
-					status = true;
-
-				if (status)
-				{
-					status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, basis_file_has_alpha_slices ? block_format::cRGBA4444_COLOR : block_format::cRGBA4444_COLOR_OPAQUE, sizeof(uint16_t), decode_flags, output_row_pitch_in_blocks_or_pixels, pState, nullptr, output_rows_in_pixels);
-					if (!status)
-					{
-						BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGBA4444 RGB failed\n");
-					}
-				}
-				else
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to RGBA4444 A failed\n");
-				}
-
-				break;
-			}
-			case transcoder_texture_format::cTFFXT1_RGB:
-			{
-#if !BASISD_SUPPORT_FXT1
-            BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: FXT1 unsupported\n");
-				return false;
-#endif
-				uint32_t slice_index_to_decode = slice_index;
-				// If the caller wants us to transcode the mip level's alpha data, then use the next slice.
-				if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
-					slice_index_to_decode++;
-
-				status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cFXT1_RGB, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to FXT1_RGB failed\n");
-				}
-				break;
-			}
-			case transcoder_texture_format::cTFETC2_EAC_R11:
-			{
-#if !BASISD_SUPPORT_ETC2_EAC_RG11
-            BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: EAC_RG11 unsupported\n");
-				return false;
-#endif
-				uint32_t slice_index_to_decode = slice_index;
-				// If the caller wants us to transcode the mip level's alpha data, then use the next slice.
-				if ((basis_file_has_alpha_slices) && (transcode_alpha_data_to_opaque_formats))
-					slice_index_to_decode++;
-
-				status = transcode_slice(pData, data_size, slice_index_to_decode, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, bytes_per_block_or_pixel, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				if (!status)
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC2_EAC_R11 failed\n");
-				}
-
-				break;
-			}
-			case transcoder_texture_format::cTFETC2_EAC_RG11:
-			{
-#if !BASISD_SUPPORT_ETC2_EAC_RG11
-            BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: EAC_RG11 unsupported\n");
-				return false;
-#endif
-				assert(bytes_per_block_or_pixel == 16);
-
-				if (basis_file_has_alpha_slices)
-				{
-					// First decode the alpha data to G
-					status = transcode_slice(pData, data_size, slice_index + 1, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-				}
-				else
-				{
-					write_opaque_alpha_blocks(pSlice_descs[slice_index].m_num_blocks_x, pSlice_descs[slice_index].m_num_blocks_y, (uint8_t*)pOutput_blocks + 8, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, 16, output_row_pitch_in_blocks_or_pixels);
-					status = true;
-				}
-
-				if (status)
-				{
-					// Now decode the color data to R
-					status = transcode_slice(pData, data_size, slice_index, pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, block_format::cETC2_EAC_R11, 16, decode_flags, output_row_pitch_in_blocks_or_pixels, pState);
-					if (!status)
-					{
-						BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC2_EAC_R11 R failed\n");
-					}
-				}
-				else
-				{
-					BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: transcode_slice() to ETC2_EAC_R11 G failed\n");
-				}
-
-				break;
-			}
-			default:
-			{
-				assert(0);
-				BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: Invalid fmt\n");
-				break;
-			}
-			}
 		} // if (pHeader->m_tex_format == (int)basis_tex_format::cUASTC4x4)
       
       if (!status)
@@ -15354,6 +20242,36 @@
 		return "";
 	}
 
+	const char* basis_get_block_format_name(block_format fmt)
+	{
+		switch (fmt)
+		{
+		case block_format::cETC1: return "ETC1";
+		case block_format::cBC1: return "BC1";
+		case block_format::cPVRTC1_4_RGB: return "PVRTC1_4_RGB";
+		case block_format::cPVRTC1_4_RGBA: return "PVRTC1_4_RGBA";
+		case block_format::cBC7: return "BC7";
+		case block_format::cETC2_RGBA: return "ETC2_RGBA";
+		case block_format::cBC3: return "BC3";
+		case block_format::cASTC_4x4: return "ASTC_4x4";
+		case block_format::cATC_RGB: return "ATC_RGB";
+		case block_format::cRGBA32: return "RGBA32";
+		case block_format::cRGB565: return "RGB565";
+		case block_format::cBGR565: return "BGR565";
+		case block_format::cRGBA4444: return "RGBA4444";
+		case block_format::cFXT1_RGB: return "FXT1_RGB";
+		case block_format::cPVRTC2_4_RGB: return "PVRTC2_4_RGB";
+		case block_format::cPVRTC2_4_RGBA: return "PVRTC2_4_RGBA";
+		case block_format::cETC2_EAC_R11: return "ETC2_EAC_R11";
+		case block_format::cETC2_EAC_RG11: return "ETC2_EAC_RG11";
+		default:
+			assert(0);
+			BASISU_DEVEL_ERROR("basis_get_basisu_texture_format: Invalid fmt\n");
+		break;
+		}
+		return "";
+	}
+
 	const char* basis_get_texture_type_name(basis_texture_type tex_type)
 	{
 		switch (tex_type)
@@ -16317,7 +21235,7 @@
 			{
 				uint8_t vals[5] = { 0, 0, 0, 0, 0 };
 
-				const int limit = std::min(group_size, num_vals - group_index * group_size);
+				const int limit = basisu::minimum(group_size, num_vals - group_index * group_size);
 				for (int i = 0; i < limit; i++)
 					vals[i] = pSrc_vals[group_index * group_size + i];
 
@@ -16519,7 +21437,7 @@
 		if (!codesize)
 			return 0;
 
-		if ((IS_BIG_ENDIAN) || (!USE_UNALIGNED_WORD_READS) || (bit_offset >= 112))
+		if ((BASISD_IS_BIG_ENDIAN) || (!BASISD_USE_UNALIGNED_WORD_READS) || (bit_offset >= 112))
 		{
 			const uint8_t* pBytes = &pBuf[bit_offset >> 3U];
 
@@ -16573,7 +21491,7 @@
 			return 0;
 		assert(bit_offset < 112);
 
-		if ((IS_BIG_ENDIAN) || (!USE_UNALIGNED_WORD_READS))
+		if ((BASISD_IS_BIG_ENDIAN) || (!BASISD_USE_UNALIGNED_WORD_READS))
 		{
 			const uint8_t* pBytes = &pBuf[bit_offset >> 3U];
 
@@ -16903,11 +21821,16 @@
 			uint64_t bits;
 			
 			// Read the weight bits
-			if ((IS_BIG_ENDIAN) || (!USE_UNALIGNED_WORD_READS))
-				bits = read_bits64(blk.m_bytes, bit_ofs, std::min<int>(64, 128 - (int)bit_ofs));
+			if ((BASISD_IS_BIG_ENDIAN) || (!BASISD_USE_UNALIGNED_WORD_READS))
+				bits = read_bits64(blk.m_bytes, bit_ofs, basisu::minimum<int>(64, 128 - (int)bit_ofs));
 			else
 			{
+#ifdef __EMSCRIPTEN__
+				bits = blk.m_dwords[2];
+				bits |= (((uint64_t)blk.m_dwords[3]) << 32U);
+#else
 				bits = blk.m_qwords[1];
+#endif
 				
 				if (bit_ofs >= 64U)
 					bits >>= (bit_ofs - 64U);
@@ -17068,7 +21991,7 @@
 		color32 endpoints[3][2];
 
 		const uint32_t total_subsets = g_uastc_mode_subsets[mode];
-		const uint32_t total_comps = std::min<uint32_t>(4U, g_uastc_mode_comps[mode]);
+		const uint32_t total_comps = basisu::minimum<uint32_t>(4U, g_uastc_mode_comps[mode]);
 		const uint32_t endpoint_range = g_uastc_mode_endpoint_ranges[mode];
 		const uint32_t total_planes = g_uastc_mode_planes[mode];
 		const uint32_t weight_bits = g_uastc_mode_weight_bits[mode];
@@ -18264,7 +23187,7 @@
 						uint32_t sels[4] = { 0,0,0,0 };
 
 						const uint32_t N = 4;
-						for (uint32_t i = 0; i < std::min<uint32_t>(N, (256 - c)); i++)
+						for (uint32_t i = 0; i < basisu::minimum<uint32_t>(N, (256 - c)); i++)
 						{
 							uint32_t best_sel_e = UINT32_MAX;
 							uint32_t best_sel = 0;
@@ -18344,7 +23267,7 @@
 						uint32_t sels[4] = { 0,0,0,0 };
 
 						const uint32_t N = 1;
-						for (uint32_t i = 0; i < std::min<uint32_t>(N, (256 - c)); i++)
+						for (uint32_t i = 0; i < basisu::minimum<uint32_t>(N, (256 - c)); i++)
 						{
 							uint32_t best_sel_e = UINT32_MAX;
 							uint32_t best_sel = 0;
@@ -18448,8 +23371,8 @@
 				for (uint32_t x = 0; x < 4; x++)
 				{
 					const uint32_t v = block_y[y][x];
-					low[0] = std::min(low[0], v);
-					high[0] = std::max(high[0], v);
+					low[0] = basisu::minimum(low[0], v);
+					high[0] = basisu::maximum(high[0], v);
 				}
 			}
 			for (uint32_t y = 2; y < 4; y++)
@@ -18457,8 +23380,8 @@
 				for (uint32_t x = 0; x < 4; x++)
 				{
 					const uint32_t v = block_y[y][x];
-					low[1] = std::min(low[1], v);
-					high[1] = std::max(high[1], v);
+					low[1] = basisu::minimum(low[1], v);
+					high[1] = basisu::maximum(high[1], v);
 				}
 			}
 		}
@@ -18469,8 +23392,8 @@
 				for (uint32_t x = 0; x < 2; x++)
 				{
 					const uint32_t v = block_y[y][x];
-					low[0] = std::min(low[0], v);
-					high[0] = std::max(high[0], v);
+					low[0] = basisu::minimum(low[0], v);
+					high[0] = basisu::maximum(high[0], v);
 				}
 			}
 			for (uint32_t y = 0; y < 4; y++)
@@ -18478,8 +23401,8 @@
 				for (uint32_t x = 2; x < 4; x++)
 				{
 					const uint32_t v = block_y[y][x];
-					low[1] = std::min(low[1], v);
-					high[1] = std::max(high[1], v);
+					low[1] = basisu::minimum(low[1], v);
+					high[1] = basisu::maximum(high[1], v);
 				}
 			}
 		}
@@ -18739,10 +23662,10 @@
 				if ((inten_table_mask & (1 << inten)) == 0)
 					continue;
 
-				const int t0 = std::max(low_limit, g_etc1_inten_tables[inten][0]);
-				const int t1 = std::max(low_limit, g_etc1_inten_tables[inten][1]);
-				const int t2 = std::min(high_limit, g_etc1_inten_tables[inten][2]);
-				const int t3 = std::min(high_limit, g_etc1_inten_tables[inten][3]);
+				const int t0 = basisu::maximum(low_limit, g_etc1_inten_tables[inten][0]);
+				const int t1 = basisu::maximum(low_limit, g_etc1_inten_tables[inten][1]);
+				const int t2 = basisu::minimum(high_limit, g_etc1_inten_tables[inten][2]);
+				const int t3 = basisu::minimum(high_limit, g_etc1_inten_tables[inten][3]);
 				assert((t0 <= t1) && (t1 <= t2) && (t2 <= t3));
 
 				const int tv[4] = { t2, t3, t1, t0 };
@@ -18960,8 +23883,8 @@
 		uint32_t min_a = 255, max_a = 0;
 		for (uint32_t i = 0; i < 16; i++)
 		{
-			min_a = std::min<uint32_t>(min_a, pSrc_pixels[i].a);
-			max_a = std::max<uint32_t>(max_a, pSrc_pixels[i].a);
+			min_a = basisu::minimum<uint32_t>(min_a, pSrc_pixels[i].a);
+			max_a = basisu::maximum<uint32_t>(max_a, pSrc_pixels[i].a);
 		}
 
 		if (min_a == max_a)
@@ -19059,24 +23982,24 @@
 		}
 
 		{
-			uint32_t v0 = pPixels[4 * stride]; min0_v = std::min(min0_v, v0); max0_v = std::max(max0_v, v0);
-			uint32_t v1 = pPixels[5 * stride]; min1_v = std::min(min1_v, v1); max1_v = std::max(max1_v, v1);
-			uint32_t v2 = pPixels[6 * stride]; min2_v = std::min(min2_v, v2); max2_v = std::max(max2_v, v2);
-			uint32_t v3 = pPixels[7 * stride]; min3_v = std::min(min3_v, v3); max3_v = std::max(max3_v, v3);
+			uint32_t v0 = pPixels[4 * stride]; min0_v = basisu::minimum(min0_v, v0); max0_v = basisu::maximum(max0_v, v0);
+			uint32_t v1 = pPixels[5 * stride]; min1_v = basisu::minimum(min1_v, v1); max1_v = basisu::maximum(max1_v, v1);
+			uint32_t v2 = pPixels[6 * stride]; min2_v = basisu::minimum(min2_v, v2); max2_v = basisu::maximum(max2_v, v2);
+			uint32_t v3 = pPixels[7 * stride]; min3_v = basisu::minimum(min3_v, v3); max3_v = basisu::maximum(max3_v, v3);
 		}
 
 		{
-			uint32_t v0 = pPixels[8 * stride]; min0_v = std::min(min0_v, v0); max0_v = std::max(max0_v, v0);
-			uint32_t v1 = pPixels[9 * stride]; min1_v = std::min(min1_v, v1); max1_v = std::max(max1_v, v1);
-			uint32_t v2 = pPixels[10 * stride]; min2_v = std::min(min2_v, v2); max2_v = std::max(max2_v, v2);
-			uint32_t v3 = pPixels[11 * stride]; min3_v = std::min(min3_v, v3); max3_v = std::max(max3_v, v3);
+			uint32_t v0 = pPixels[8 * stride]; min0_v = basisu::minimum(min0_v, v0); max0_v = basisu::maximum(max0_v, v0);
+			uint32_t v1 = pPixels[9 * stride]; min1_v = basisu::minimum(min1_v, v1); max1_v = basisu::maximum(max1_v, v1);
+			uint32_t v2 = pPixels[10 * stride]; min2_v = basisu::minimum(min2_v, v2); max2_v = basisu::maximum(max2_v, v2);
+			uint32_t v3 = pPixels[11 * stride]; min3_v = basisu::minimum(min3_v, v3); max3_v = basisu::maximum(max3_v, v3);
 		}
 
 		{
-			uint32_t v0 = pPixels[12 * stride]; min0_v = std::min(min0_v, v0); max0_v = std::max(max0_v, v0);
-			uint32_t v1 = pPixels[13 * stride]; min1_v = std::min(min1_v, v1); max1_v = std::max(max1_v, v1);
-			uint32_t v2 = pPixels[14 * stride]; min2_v = std::min(min2_v, v2); max2_v = std::max(max2_v, v2);
-			uint32_t v3 = pPixels[15 * stride]; min3_v = std::min(min3_v, v3); max3_v = std::max(max3_v, v3);
+			uint32_t v0 = pPixels[12 * stride]; min0_v = basisu::minimum(min0_v, v0); max0_v = basisu::maximum(max0_v, v0);
+			uint32_t v1 = pPixels[13 * stride]; min1_v = basisu::minimum(min1_v, v1); max1_v = basisu::maximum(max1_v, v1);
+			uint32_t v2 = pPixels[14 * stride]; min2_v = basisu::minimum(min2_v, v2); max2_v = basisu::maximum(max2_v, v2);
+			uint32_t v3 = pPixels[15 * stride]; min3_v = basisu::minimum(min3_v, v3); max3_v = basisu::maximum(max3_v, v3);
 		}
 
 		const uint32_t min_v = basisu::minimum(min0_v, min1_v, min2_v, min3_v);
@@ -19401,12 +24324,14 @@
 			for (uint32_t i = 1; i < 16; i++)
 			{
 				const int r = pSrc_pixels[i].r, g = pSrc_pixels[i].g, b = pSrc_pixels[i].b;
-				max_r = std::max(max_r, r); max_g = std::max(max_g, g); max_b = std::max(max_b, b);
-				min_r = std::min(min_r, r); min_g = std::min(min_g, g); min_b = std::min(min_b, b);
+				max_r = basisu::maximum(max_r, r); max_g = basisu::maximum(max_g, g); max_b = basisu::maximum(max_b, b);
+				min_r = basisu::minimum(min_r, r); min_g = basisu::minimum(min_g, g); min_b = basisu::minimum(min_b, b);
 				total_r += r; total_g += g; total_b += b;
 			}
 
-			avg_r = (total_r + 8) >> 4, avg_g = (total_g + 8) >> 4, avg_b = (total_b + 8) >> 4;
+			avg_r = (total_r + 8) >> 4;
+			avg_g = (total_g + 8) >> 4;
+			avg_b = (total_b + 8) >> 4;
 
 			int icov[6] = { 0, 0, 0, 0, 0, 0 };
 			for (uint32_t i = 0; i < 16; i++)
@@ -19502,7 +24427,9 @@
 						total_b += pSrc_pixels[i].b;
 					}
 
-					avg_r = (total_r + 8) >> 4, avg_g = (total_g + 8) >> 4, avg_b = (total_b + 8) >> 4;
+					avg_r = (total_r + 8) >> 4;
+					avg_g = (total_g + 8) >> 4;
+					avg_b = (total_b + 8) >> 4;
 				}
 
 				// All selectors equal - treat it as a solid block which should always be equal or better.
@@ -19629,8 +24556,8 @@
 			{
 				const int r = pSrc_pixels[i].r, g = pSrc_pixels[i].g, b = pSrc_pixels[i].b;
 				grayscale_flag &= ((r == g) && (r == b));
-				max_r = std::max(max_r, r); max_g = std::max(max_g, g); max_b = std::max(max_b, b);
-				min_r = std::min(min_r, r); min_g = std::min(min_g, g); min_b = std::min(min_b, b);
+				max_r = basisu::maximum(max_r, r); max_g = basisu::maximum(max_g, g); max_b = basisu::maximum(max_b, b);
+				min_r = basisu::minimum(min_r, r); min_g = basisu::minimum(min_g, g); min_b = basisu::minimum(min_b, b);
 				total_r += r; total_g += g; total_b += b;
 			}
 						
@@ -19653,7 +24580,9 @@
 			}
 			else
 			{
-				avg_r = (total_r + 8) >> 4, avg_g = (total_g + 8) >> 4, avg_b = (total_b + 8) >> 4;
+				avg_r = (total_r + 8) >> 4;
+				avg_g = (total_g + 8) >> 4;
+				avg_b = (total_b + 8) >> 4;
 
 				// Find the shortest vector from a AABB corner to the block's average color.
 				// This is to help avoid outliers.
@@ -19665,25 +24594,25 @@
 
 				uint32_t min_d0 = (dist[0][0] + dist[1][0] + dist[2][0]);
 				uint32_t d4 = (dist[0][0] + dist[1][0] + dist[2][1]) | 4;
-				min_d0 = std::min(min_d0, d4);
+				min_d0 = basisu::minimum(min_d0, d4);
 
 				uint32_t min_d1 = (dist[0][1] + dist[1][0] + dist[2][0]) | 1;
 				uint32_t d5 = (dist[0][1] + dist[1][0] + dist[2][1]) | 5;
-				min_d1 = std::min(min_d1, d5);
+				min_d1 = basisu::minimum(min_d1, d5);
 
 				uint32_t d2 = (dist[0][0] + dist[1][1] + dist[2][0]) | 2;
-				min_d0 = std::min(min_d0, d2);
+				min_d0 = basisu::minimum(min_d0, d2);
 
 				uint32_t d3 = (dist[0][1] + dist[1][1] + dist[2][0]) | 3;
-				min_d1 = std::min(min_d1, d3);
+				min_d1 = basisu::minimum(min_d1, d3);
 
 				uint32_t d6 = (dist[0][0] + dist[1][1] + dist[2][1]) | 6;
-				min_d0 = std::min(min_d0, d6);
+				min_d0 = basisu::minimum(min_d0, d6);
 
 				uint32_t d7 = (dist[0][1] + dist[1][1] + dist[2][1]) | 7;
-				min_d1 = std::min(min_d1, d7);
+				min_d1 = basisu::minimum(min_d1, d7);
 
-				uint32_t min_d = std::min(min_d0, min_d1);
+				uint32_t min_d = basisu::minimum(min_d0, min_d1);
 				uint32_t best_i = min_d & 7;
 
 				int delta_r = (best_i & 1) ? (max_r - avg_r) : (avg_r - min_r);
@@ -19763,20 +24692,29 @@
 						}
 					}
 
-					low_c = low_dot0 & 15, high_c = ~high_dot0 & 15;
+					low_c = low_dot0 & 15;
+					high_c = ~high_dot0 & 15;
 					uint32_t r = (high_dot0 & ~15) - (low_dot0 & ~15);
 
 					uint32_t tr = (high_dot1 & ~15) - (low_dot1 & ~15);
-					if (tr > r)
-						low_c = low_dot1 & 15, high_c = ~high_dot1 & 15, r = tr;
+					if (tr > r) {
+						low_c = low_dot1 & 15;
+						high_c = ~high_dot1 & 15;
+						r = tr;
+					}
 
 					tr = (high_dot2 & ~15) - (low_dot2 & ~15);
-					if (tr > r)
-						low_c = low_dot2 & 15, high_c = ~high_dot2 & 15, r = tr;
+					if (tr > r) {
+						low_c = low_dot2 & 15;
+						high_c = ~high_dot2 & 15;
+						r = tr;
+					}
 
 					tr = (high_dot3 & ~15) - (low_dot3 & ~15);
-					if (tr > r)
-						low_c = low_dot3 & 15, high_c = ~high_dot3 & 15;
+					if (tr > r) {
+						low_c = low_dot3 & 15;
+						high_c = ~high_dot3 & 15;
+					}
 				}
 
 				lr = to_5(pSrc_pixels[low_c].r);
@@ -19810,7 +24748,9 @@
 						total_b += pSrc_pixels[i].b;
 					}
 
-					avg_r = (total_r + 8) >> 4, avg_g = (total_g + 8) >> 4, avg_b = (total_b + 8) >> 4;
+					avg_r = (total_r + 8) >> 4;
+					avg_g = (total_g + 8) >> 4;
+					avg_b = (total_b + 8) >> 4;
 				}
 
 				// All selectors equal - treat it as a solid block which should always be equal or better.
@@ -20257,10 +25197,10 @@
 					const int v2 = clamp255(mul[2] * g_eac_modifier_table[T2][s] + base[2]);
 					const int v3 = clamp255(mul[3] * g_eac_modifier_table[T3][s] + base[3]);
 
-					l0 = std::min(l0, (basisu::iabs(v0 - a) << 3) | s);
-					l1 = std::min(l1, (basisu::iabs(v1 - a) << 3) | s);
-					l2 = std::min(l2, (basisu::iabs(v2 - a) << 3) | s);
-					l3 = std::min(l3, (basisu::iabs(v3 - a) << 3) | s);
+					l0 = basisu::minimum(l0, (basisu::iabs(v0 - a) << 3) | s);
+					l1 = basisu::minimum(l1, (basisu::iabs(v1 - a) << 3) | s);
+					l2 = basisu::minimum(l2, (basisu::iabs(v2 - a) << 3) | s);
+					l3 = basisu::minimum(l3, (basisu::iabs(v3 - a) << 3) | s);
 				}
 			}
 			else if (mul_or == 1)
@@ -20274,10 +25214,10 @@
 					const int v2 = g_eac_modifier_table[T2][s] + a2;
 					const int v3 = g_eac_modifier_table[T3][s] + a3;
 
-					l0 = std::min(l0, (basisu::iabs(v0) << 3) | s);
-					l1 = std::min(l1, (basisu::iabs(v1) << 3) | s);
-					l2 = std::min(l2, (basisu::iabs(v2) << 3) | s);
-					l3 = std::min(l3, (basisu::iabs(v3) << 3) | s);
+					l0 = basisu::minimum(l0, (basisu::iabs(v0) << 3) | s);
+					l1 = basisu::minimum(l1, (basisu::iabs(v1) << 3) | s);
+					l2 = basisu::minimum(l2, (basisu::iabs(v2) << 3) | s);
+					l3 = basisu::minimum(l3, (basisu::iabs(v3) << 3) | s);
 				}
 			}
 			else
@@ -20291,10 +25231,10 @@
 					const int v2 = mul[2] * g_eac_modifier_table[T2][s] + a2;
 					const int v3 = mul[3] * g_eac_modifier_table[T3][s] + a3;
 
-					l0 = std::min(l0, (basisu::iabs(v0) << 3) | s);
-					l1 = std::min(l1, (basisu::iabs(v1) << 3) | s);
-					l2 = std::min(l2, (basisu::iabs(v2) << 3) | s);
-					l3 = std::min(l3, (basisu::iabs(v3) << 3) | s);
+					l0 = basisu::minimum(l0, (basisu::iabs(v0) << 3) | s);
+					l1 = basisu::minimum(l1, (basisu::iabs(v1) << 3) | s);
+					l2 = basisu::minimum(l2, (basisu::iabs(v2) << 3) | s);
+					l3 = basisu::minimum(l3, (basisu::iabs(v3) << 3) | s);
 				}
 			}
 
@@ -20413,13 +25353,13 @@
 				else
 				{
 					uint32_t l = basisu::iabs(clamp255(m * pTable[0] + b) - a) << 3;
-					l = std::min(l, (basisu::iabs(clamp255(m * pTable[1] + b) - a) << 3) | 1);
-					l = std::min(l, (basisu::iabs(clamp255(m * pTable[2] + b) - a) << 3) | 2);
-					l = std::min(l, (basisu::iabs(clamp255(m * pTable[3] + b) - a) << 3) | 3);
-					l = std::min(l, (basisu::iabs(clamp255(m * pTable[4] + b) - a) << 3) | 4);
-					l = std::min(l, (basisu::iabs(clamp255(m * pTable[5] + b) - a) << 3) | 5);
-					l = std::min(l, (basisu::iabs(clamp255(m * pTable[6] + b) - a) << 3) | 6);
-					l = std::min(l, (basisu::iabs(clamp255(m * pTable[7] + b) - a) << 3) | 7);
+					l = basisu::minimum(l, (basisu::iabs(clamp255(m * pTable[1] + b) - a) << 3) | 1);
+					l = basisu::minimum(l, (basisu::iabs(clamp255(m * pTable[2] + b) - a) << 3) | 2);
+					l = basisu::minimum(l, (basisu::iabs(clamp255(m * pTable[3] + b) - a) << 3) | 3);
+					l = basisu::minimum(l, (basisu::iabs(clamp255(m * pTable[4] + b) - a) << 3) | 4);
+					l = basisu::minimum(l, (basisu::iabs(clamp255(m * pTable[5] + b) - a) << 3) | 5);
+					l = basisu::minimum(l, (basisu::iabs(clamp255(m * pTable[6] + b) - a) << 3) | 6);
+					l = basisu::minimum(l, (basisu::iabs(clamp255(m * pTable[7] + b) - a) << 3) | 7);
 
 					sels[table][i] = l & 7;
 					total_err[table] += basisu::square<uint32_t>(l >> 3);
@@ -20848,7 +25788,7 @@
 		if (!basisu::is_pow2(width) || !basisu::is_pow2(height))
 			return false;
 
-		std::vector<uint32_t> temp_endpoints(num_blocks_x * num_blocks_y);
+		basisu::vector<uint32_t> temp_endpoints(num_blocks_x * num_blocks_y);
 
 		for (uint32_t y = 0; y < num_blocks_y; y++)
 		{
@@ -20866,8 +25806,8 @@
 					uint32_t low_a = 255, high_a = 0;
 					for (uint32_t i = 0; i < 16; i++)
 					{
-						low_a = std::min<uint32_t>(low_a, block_pixels[i].a);
-						high_a = std::max<uint32_t>(high_a, block_pixels[i].a);
+						low_a = basisu::minimum<uint32_t>(low_a, block_pixels[i].a);
+						high_a = basisu::maximum<uint32_t>(high_a, block_pixels[i].a);
 					}
 					low_color.set(low_a, low_a, low_a, 255);
 					high_color.set(high_a, high_a, high_a, 255);
@@ -20907,7 +25847,7 @@
 		if (!basisu::is_pow2(width) || !basisu::is_pow2(height))
 			return false;
 
-		std::vector<uint32_t> temp_endpoints(num_blocks_x * num_blocks_y);
+		basisu::vector<uint32_t> temp_endpoints(num_blocks_x * num_blocks_y);
 
 		for (uint32_t y = 0; y < num_blocks_y; y++)
 		{
@@ -21033,12 +25973,891 @@
 
 #endif // #if BASISD_SUPPORT_UASTC
 
+// ------------------------------------------------------------------------------------------------------ 
+// KTX2
+// ------------------------------------------------------------------------------------------------------ 
+
+#if BASISD_SUPPORT_KTX2
+	const uint8_t g_ktx2_file_identifier[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
+
+	ktx2_transcoder::ktx2_transcoder() :
+		m_etc1s_transcoder()
+	{
+		clear();
+	}
+
+	void ktx2_transcoder::clear()
+	{
+		m_pData = nullptr;
+		m_data_size = 0;
+
+		memset(&m_header, 0, sizeof(m_header));
+		m_levels.clear();
+		m_dfd.clear();
+		m_key_values.clear();
+		memset(&m_etc1s_header, 0, sizeof(m_etc1s_header));
+		m_etc1s_image_descs.clear();
+				
+		m_format = basist::basis_tex_format::cETC1S;
+
+		m_dfd_color_model = 0;
+		m_dfd_color_prims = KTX2_DF_PRIMARIES_UNSPECIFIED;
+		m_dfd_transfer_func = 0;
+		m_dfd_flags = 0;
+		m_dfd_samples = 0;
+		m_dfd_chan0 = KTX2_DF_CHANNEL_UASTC_RGB;
+		m_dfd_chan1 = KTX2_DF_CHANNEL_UASTC_RGB;
+
+		m_etc1s_transcoder.clear();
+				
+		m_def_transcoder_state.clear();
+		
+		m_has_alpha = false;
+		m_is_video = false;
+	}
+
+	bool ktx2_transcoder::init(const void* pData, uint32_t data_size)
+	{
+		clear();
+
+		if (!pData)
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: pData is nullptr\n");
+			assert(0);
+			return false;
+		}
+
+		if (data_size <= sizeof(ktx2_header))
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: File is impossibly too small to be a valid KTX2 file\n");
+			return false;
+		}
+
+		if (memcmp(pData, g_ktx2_file_identifier, sizeof(g_ktx2_file_identifier)) != 0)
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: KTX2 file identifier is not present\n");
+			return false;
+		}
+
+		m_pData = static_cast<const uint8_t *>(pData);
+		m_data_size = data_size;
+
+		memcpy(&m_header, pData, sizeof(m_header));
+
+		// We only support UASTC and ETC1S
+		if (m_header.m_vk_format != KTX2_VK_FORMAT_UNDEFINED)
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: KTX2 file must be in ETC1S or UASTC format\n");
+			return false;
+		}
+
+		// 3.3: "When format is VK_FORMAT_UNDEFINED, typeSize must equal 1."
+		if (m_header.m_type_size != 1)
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid type_size\n");
+			return false;
+		}
+
+		// We only currently support 2D textures (plain, cubemapped, or texture array), which is by far the most common use case.
+		// The BasisU library does not support 1D or 3D textures at all.
+		if ((m_header.m_pixel_width < 1) || (m_header.m_pixel_height < 1) || (m_header.m_pixel_depth > 0))
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: Only 2D or cubemap textures are supported\n");
+			return false;
+		}
+
+		// Face count must be 1 or 6
+		if ((m_header.m_face_count != 1) && (m_header.m_face_count != 6))
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid face count, file is corrupted or invalid\n");
+			return false;
+		}
+
+		if (m_header.m_face_count > 1)
+		{
+			// 3.4: Make sure cubemaps are square.
+			if (m_header.m_pixel_width != m_header.m_pixel_height)
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::init: Cubemap is not square\n");
+				return false;
+			}
+		}
+		
+		// 3.7 levelCount: "levelCount=0 is allowed, except for block-compressed formats"
+		if (m_header.m_level_count < 1)
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid level count\n");
+			return false;
+		}
+
+		// Sanity check the level count.
+		if (m_header.m_level_count > KTX2_MAX_SUPPORTED_LEVEL_COUNT)
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: Too many levels or file is corrupted or invalid\n");
+			return false;
+		}
+
+		if (m_header.m_supercompression_scheme > KTX2_SS_ZSTANDARD)
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid/unsupported supercompression or file is corrupted or invalid\n");
+			return false;
+		}
+
+		if (m_header.m_supercompression_scheme == KTX2_SS_BASISLZ)
+		{
+			if (m_header.m_sgd_byte_length <= sizeof(ktx2_etc1s_global_data_header))
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::init: Supercompression global data is too small\n");
+				return false;
+			}
+
+			if (m_header.m_sgd_byte_offset < sizeof(ktx2_header))
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::init: Supercompression global data offset is too low\n");
+				return false;
+			}
+
+			if (m_header.m_sgd_byte_offset + m_header.m_sgd_byte_length > m_data_size)
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::init: Supercompression global data offset and/or length is too high\n");
+				return false;
+			}
+		}
+
+		if (!m_levels.try_resize(m_header.m_level_count))
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: Out of memory\n");
+			return false;
+		}
+
+		const uint32_t level_index_size_in_bytes = basisu::maximum(1U, (uint32_t)m_header.m_level_count) * sizeof(ktx2_level_index);
+
+		if ((sizeof(ktx2_header) + level_index_size_in_bytes) > m_data_size)
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: File is too small (can't read level index array)\n");
+			return false;
+		}
+
+		memcpy(&m_levels[0], m_pData + sizeof(ktx2_header), level_index_size_in_bytes);
+		
+		// Sanity check the level offsets and byte sizes
+		for (uint32_t i = 0; i < m_levels.size(); i++)
+		{
+			if (m_levels[i].m_byte_offset < sizeof(ktx2_header))
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid level offset (too low)\n");
+				return false;
+			}
+
+			if (!m_levels[i].m_byte_length)
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid level byte length\n");
+			}
+
+			if ((m_levels[i].m_byte_offset + m_levels[i].m_byte_length) > m_data_size)
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid level offset and/or length\n");
+				return false;
+			}
+			
+			const uint64_t MAX_SANE_LEVEL_UNCOMP_SIZE = 2048ULL * 1024ULL * 1024ULL;
+			
+			if (m_levels[i].m_uncompressed_byte_length >= MAX_SANE_LEVEL_UNCOMP_SIZE)
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid level offset (too large)\n");
+				return false;
+			}
+
+			if (m_header.m_supercompression_scheme == KTX2_SS_BASISLZ)
+			{
+				if (m_levels[i].m_uncompressed_byte_length)
+				{
+					BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid uncompressed length (0)\n");
+					return false;
+				}
+			}
+			else if (m_header.m_supercompression_scheme >= KTX2_SS_ZSTANDARD)
+			{
+				if (!m_levels[i].m_uncompressed_byte_length)
+				{
+					BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid uncompressed length (1)\n");
+					return false;
+				}
+			}
+		}
+
+		const uint32_t DFD_MINIMUM_SIZE = 44, DFD_MAXIMUM_SIZE = 60;
+		if ((m_header.m_dfd_byte_length != DFD_MINIMUM_SIZE) && (m_header.m_dfd_byte_length != DFD_MAXIMUM_SIZE))
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: Unsupported DFD size\n");
+			return false;
+		}
+
+		if (((m_header.m_dfd_byte_offset + m_header.m_dfd_byte_length) > m_data_size) || (m_header.m_dfd_byte_offset < sizeof(ktx2_header)))
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid DFD offset and/or length\n");
+			return false;
+		}
+				
+		const uint8_t* pDFD = m_pData + m_header.m_dfd_byte_offset;
+
+		if (!m_dfd.try_resize(m_header.m_dfd_byte_length))
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: Out of memory\n");
+			return false;
+		}
+
+		memcpy(m_dfd.data(), pDFD, m_header.m_dfd_byte_length);
+		
+		// This is all hard coded for only ETC1S and UASTC.
+		uint32_t dfd_total_size = basisu::read_le_dword(pDFD);
+		
+		// 3.10.3: Sanity check
+		if (dfd_total_size != m_header.m_dfd_byte_length)
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: DFD size validation failed (1)\n");
+			return false;
+		}
+				
+		// 3.10.3: More sanity checking
+		if (m_header.m_kvd_byte_length)
+		{
+			if (dfd_total_size != m_header.m_kvd_byte_offset - m_header.m_dfd_byte_offset)
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::init: DFD size validation failed (2)\n");
+				return false;
+			}
+		}
+
+		const uint32_t dfd_bits = basisu::read_le_dword(pDFD + 3 * sizeof(uint32_t));
+		const uint32_t sample_channel0 = basisu::read_le_dword(pDFD + 7 * sizeof(uint32_t));
+		 
+		m_dfd_color_model = dfd_bits & 255;
+		m_dfd_color_prims = (ktx2_df_color_primaries)((dfd_bits >> 8) & 255);
+		m_dfd_transfer_func = (dfd_bits >> 16) & 255;
+		m_dfd_flags = (dfd_bits >> 24) & 255;
+
+		// See 3.10.1.Restrictions
+		if ((m_dfd_transfer_func != KTX2_KHR_DF_TRANSFER_LINEAR) && (m_dfd_transfer_func != KTX2_KHR_DF_TRANSFER_SRGB))
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: Invalid DFD transfer function\n");
+			return false;
+		}
+
+		if (m_dfd_color_model == KTX2_KDF_DF_MODEL_ETC1S)
+		{
+			m_format = basist::basis_tex_format::cETC1S;
+			
+			// 3.10.2: "Whether the image has 1 or 2 slices can be determined from the DFDÂ’s sample count."
+			// If m_has_alpha is true it may be 2-channel RRRG or 4-channel RGBA, but we let the caller deal with that.
+			m_has_alpha = (m_header.m_dfd_byte_length == 60);
+			
+			m_dfd_samples = m_has_alpha ? 2 : 1;
+			m_dfd_chan0 = (ktx2_df_channel_id)((sample_channel0 >> 24) & 15);
+
+			if (m_has_alpha)
+			{
+				const uint32_t sample_channel1 = basisu::read_le_dword(pDFD + 11 * sizeof(uint32_t));
+				m_dfd_chan1 = (ktx2_df_channel_id)((sample_channel1 >> 24) & 15);
+			}
+		}
+		else if (m_dfd_color_model == KTX2_KDF_DF_MODEL_UASTC)
+		{
+			m_format = basist::basis_tex_format::cUASTC4x4;
+
+			m_dfd_samples = 1;
+			m_dfd_chan0 = (ktx2_df_channel_id)((sample_channel0 >> 24) & 15);
+			
+			// We're assuming "DATA" means RGBA so it has alpha.
+			m_has_alpha = (m_dfd_chan0 == KTX2_DF_CHANNEL_UASTC_RGBA) || (m_dfd_chan0 == KTX2_DF_CHANNEL_UASTC_RRRG);
+		}
+		else
+		{
+			// Unsupported DFD color model.
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: Unsupported DFD color model\n");
+			return false;
+		}
+				
+		if (!read_key_values())
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::init: read_key_values() failed\n");
+			return false;
+		}
+
+		// Check for a KTXanimData key
+		for (uint32_t i = 0; i < m_key_values.size(); i++)
+		{
+			if (strcmp(reinterpret_cast<const char*>(m_key_values[i].m_key.data()), "KTXanimData") == 0)
+			{
+				m_is_video = true;
+				break;
+			}
+		}
+
+		return true;
+	}
+
+	uint32_t ktx2_transcoder::get_etc1s_image_descs_image_flags(uint32_t level_index, uint32_t layer_index, uint32_t face_index) const
+	{
+		const uint32_t etc1s_image_index =
+			(level_index * basisu::maximum<uint32_t>(m_header.m_layer_count, 1) * m_header.m_face_count) +
+			layer_index * m_header.m_face_count +
+			face_index;
+
+		if (etc1s_image_index >= get_etc1s_image_descs().size())
+		{
+			assert(0);
+			return 0;
+		}
+
+		return get_etc1s_image_descs()[etc1s_image_index].m_image_flags;
+	}
+
+	const basisu::uint8_vec* ktx2_transcoder::find_key(const std::string& key_name) const
+	{
+		for (uint32_t i = 0; i < m_key_values.size(); i++)
+			if (strcmp((const char *)m_key_values[i].m_key.data(), key_name.c_str()) == 0)
+				return &m_key_values[i].m_value;
+
+		return nullptr;
+	}
+	
+	bool ktx2_transcoder::start_transcoding()
+	{
+		if (!m_pData)
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::start_transcoding: Must call init() first\n");
+			return false;
+		}
+
+		if (m_header.m_supercompression_scheme == KTX2_SS_BASISLZ) 
+		{
+			// Check if we've already decompressed the ETC1S global data. If so don't unpack it again.
+			if (!m_etc1s_transcoder.get_endpoints().empty())
+				return true;
+
+			if (!decompress_etc1s_global_data())
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::start_transcoding: decompress_etc1s_global_data() failed\n");
+				return false;
+			}
+			
+			if (!m_is_video)
+			{
+				// See if there are any P-frames. If so it must be a video, even if there wasn't a KTXanimData key.
+				// Video cannot be a cubemap, and it must be a texture array.
+				if ((m_header.m_face_count == 1) && (m_header.m_layer_count > 1))
+				{
+					for (uint32_t i = 0; i < m_etc1s_image_descs.size(); i++)
+					{
+						if (m_etc1s_image_descs[i].m_image_flags & KTX2_IMAGE_IS_P_FRAME)
+						{
+							m_is_video = true;
+							break;
+						}
+					}
+				}
+			}
+		}
+		else if (m_header.m_supercompression_scheme == KTX2_SS_ZSTANDARD)
+		{
+#if !BASISD_SUPPORT_KTX2_ZSTD
+			BASISU_DEVEL_ERROR("ktx2_transcoder::start_transcoding: File uses zstd supercompression, but zstd support was not enabled at compilation time (BASISD_SUPPORT_KTX2_ZSTD == 0)\n");
+			return false;
+#endif
+		}
+
+		return true;
+	}
+
+	bool ktx2_transcoder::get_image_level_info(ktx2_image_level_info& level_info, uint32_t level_index, uint32_t layer_index, uint32_t face_index) const
+	{
+		if (level_index >= m_levels.size())
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::get_image_level_info: level_index >= m_levels.size()\n");
+			return false;
+		}
+
+		if (m_header.m_face_count > 1)
+		{
+			if (face_index >= 6)
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::get_image_level_info: face_index >= 6\n");
+				return false;
+			}
+		}
+		else if (face_index != 0)
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::get_image_level_info: face_index != 0\n");
+			return false;
+		}
+
+		if (layer_index >= basisu::maximum<uint32_t>(m_header.m_layer_count, 1))
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::get_image_level_info: layer_index >= maximum<uint32_t>(m_header.m_layer_count, 1)\n");
+			return false;
+		}
+				
+		const uint32_t level_width = basisu::maximum<uint32_t>(m_header.m_pixel_width >> level_index, 1);
+		const uint32_t level_height = basisu::maximum<uint32_t>(m_header.m_pixel_height >> level_index, 1);
+		const uint32_t num_blocks_x = (level_width + 3) >> 2;
+		const uint32_t num_blocks_y = (level_height + 3) >> 2;
+
+		level_info.m_face_index = face_index;
+		level_info.m_layer_index = layer_index;
+		level_info.m_level_index = level_index;
+		level_info.m_orig_width = level_width;
+		level_info.m_orig_height = level_height;
+		level_info.m_width = num_blocks_x * 4;
+		level_info.m_height = num_blocks_y * 4;
+		level_info.m_num_blocks_x = num_blocks_x;
+		level_info.m_num_blocks_y = num_blocks_y;
+		level_info.m_total_blocks = num_blocks_x * num_blocks_y;
+		level_info.m_alpha_flag = m_has_alpha;
+		level_info.m_iframe_flag = false;
+		if (m_etc1s_image_descs.size())
+		{
+			const uint32_t etc1s_image_index =
+				(level_index * basisu::maximum<uint32_t>(m_header.m_layer_count, 1) * m_header.m_face_count) +
+				layer_index * m_header.m_face_count +
+				face_index;
+
+			level_info.m_iframe_flag = (m_etc1s_image_descs[etc1s_image_index].m_image_flags & KTX2_IMAGE_IS_P_FRAME) == 0;
+		}
+
+		return true;
+	}
+		
+	bool ktx2_transcoder::transcode_image_level(
+		uint32_t level_index, uint32_t layer_index, uint32_t face_index, 
+		void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
+		basist::transcoder_texture_format fmt,
+		uint32_t decode_flags, uint32_t output_row_pitch_in_blocks_or_pixels, uint32_t output_rows_in_pixels, int channel0, int channel1,
+		ktx2_transcoder_state* pState)
+	{
+		if (!m_pData)
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: Must call init() first\n");
+			return false;
+		}
+
+		if (!pState)
+			pState = &m_def_transcoder_state;
+										
+		if (level_index >= m_levels.size())
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: level_index >= m_levels.size()\n");
+			return false;
+		}
+
+		if (m_header.m_face_count > 1)
+		{
+			if (face_index >= 6)
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: face_index >= 6\n");
+				return false;
+			}
+		}
+		else if (face_index != 0)
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: face_index != 0\n");
+			return false;
+		}
+
+		if (layer_index >= basisu::maximum<uint32_t>(m_header.m_layer_count, 1))
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: layer_index >= maximum<uint32_t>(m_header.m_layer_count, 1)\n");
+			return false;
+		}
+
+		const uint8_t* pComp_level_data = m_pData + m_levels[level_index].m_byte_offset;
+		uint64_t comp_level_data_size = m_levels[level_index].m_byte_length;
+		
+		const uint8_t* pUncomp_level_data = pComp_level_data;
+		uint64_t uncomp_level_data_size = comp_level_data_size;
+
+		if (uncomp_level_data_size > UINT32_MAX)
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: uncomp_level_data_size > UINT32_MAX\n");
+			return false;
+		}
+				
+		if (m_header.m_supercompression_scheme == KTX2_SS_ZSTANDARD)
+		{
+			// Check if we've already decompressed this level's supercompressed data.
+			if ((int)level_index != pState->m_uncomp_data_level_index)
+			{
+				// Uncompress the entire level's supercompressed data.
+				if (!decompress_level_data(level_index, pState->m_level_uncomp_data))
+				{
+					BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: decompress_level_data() failed\n");
+					return false;
+				}
+				pState->m_uncomp_data_level_index = level_index;
+			}
+
+			pUncomp_level_data = pState->m_level_uncomp_data.data();
+			uncomp_level_data_size = pState->m_level_uncomp_data.size();
+		}
+				
+		const uint32_t level_width = basisu::maximum<uint32_t>(m_header.m_pixel_width >> level_index, 1);
+		const uint32_t level_height = basisu::maximum<uint32_t>(m_header.m_pixel_height >> level_index, 1);
+		const uint32_t num_blocks_x = (level_width + 3) >> 2;
+		const uint32_t num_blocks_y = (level_height + 3) >> 2;
+		
+		if (m_format == basist::basis_tex_format::cETC1S)
+		{
+			// Ensure start_transcoding() was called.
+			if (m_etc1s_transcoder.get_endpoints().empty())
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: must call start_transcoding() first\n");
+				return false;
+			}
+
+			const uint32_t etc1s_image_index =
+				(level_index * basisu::maximum<uint32_t>(m_header.m_layer_count, 1) * m_header.m_face_count) +
+				layer_index * m_header.m_face_count +
+				face_index;
+		
+			// Sanity check
+			if (etc1s_image_index >= m_etc1s_image_descs.size())
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: etc1s_image_index >= m_etc1s_image_descs.size()\n");
+				assert(0);
+				return false;
+			}
+
+			if (static_cast<uint32_t>(m_data_size) != m_data_size)
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: File is too large\n");
+				return false;
+			}
+
+			const ktx2_etc1s_image_desc& image_desc = m_etc1s_image_descs[etc1s_image_index];
+
+			if (!m_etc1s_transcoder.transcode_image(fmt,
+				pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels, m_pData, static_cast<uint32_t>(m_data_size),
+				num_blocks_x, num_blocks_y, level_width, level_height,
+				level_index,
+				m_levels[level_index].m_byte_offset + image_desc.m_rgb_slice_byte_offset, image_desc.m_rgb_slice_byte_length,
+				image_desc.m_alpha_slice_byte_length ? (m_levels[level_index].m_byte_offset + image_desc.m_alpha_slice_byte_offset) : 0, image_desc.m_alpha_slice_byte_length,
+				decode_flags, m_has_alpha,
+				m_is_video, output_row_pitch_in_blocks_or_pixels, &pState->m_transcoder_state, output_rows_in_pixels))
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: ETC1S transcode_image() failed, this is either a bug or the file is corrupted/invalid\n");
+				return false;
+			}
+		}
+		else if (m_format == basist::basis_tex_format::cUASTC4x4)
+		{
+			// Compute length and offset to uncompressed 2D UASTC texture data, given the face/layer indices.
+			assert(uncomp_level_data_size == m_levels[level_index].m_uncompressed_byte_length);
+			const uint32_t total_2D_image_size = num_blocks_x * num_blocks_y * KTX2_UASTC_BLOCK_SIZE;
+						
+			const uint32_t uncomp_ofs = (layer_index * m_header.m_face_count + face_index) * total_2D_image_size;
+
+			// Sanity checks
+			if (uncomp_ofs >= uncomp_level_data_size)
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: uncomp_ofs >= total_2D_image_size\n");
+				return false;
+			}
+
+			if ((uncomp_level_data_size - uncomp_ofs) < total_2D_image_size)
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: (uncomp_level_data_size - uncomp_ofs) < total_2D_image_size\n");
+				return false;
+			}
+
+			if (!m_uastc_transcoder.transcode_image(fmt,
+				pOutput_blocks, output_blocks_buf_size_in_blocks_or_pixels,
+				(const uint8_t*)pUncomp_level_data + uncomp_ofs, (uint32_t)total_2D_image_size, num_blocks_x, num_blocks_y, level_width, level_height, level_index,
+				0, (uint32_t)total_2D_image_size,
+				decode_flags, m_has_alpha, m_is_video, output_row_pitch_in_blocks_or_pixels, nullptr, output_rows_in_pixels, channel0, channel1))
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: UASTC transcode_image() failed, this is either a bug or the file is corrupted/invalid\n");
+				return false;
+			}
+		}
+		else
+		{
+			// Shouldn't get here.
+			BASISU_DEVEL_ERROR("ktx2_transcoder::transcode_image_2D: Internal error\n");
+			assert(0);
+			return false;
+		}
+
+		return true;
+	}
+		
+	bool ktx2_transcoder::decompress_level_data(uint32_t level_index, basisu::uint8_vec& uncomp_data)
+	{
+		const uint8_t* pComp_data = m_levels[level_index].m_byte_offset + m_pData;
+		const uint64_t comp_size = m_levels[level_index].m_byte_length;
+		
+		const uint64_t uncomp_size = m_levels[level_index].m_uncompressed_byte_length;
+
+		if (((size_t)comp_size) != comp_size)
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: Compressed data too large\n");
+			return false;
+		}
+		if (((size_t)uncomp_size) != uncomp_size)
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: Uncompressed data too large\n");
+			return false;
+		}
+
+		if (!uncomp_data.try_resize((size_t)uncomp_size))
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: Out of memory\n");
+			return false;
+		}
+		
+		if (m_header.m_supercompression_scheme == KTX2_SS_ZSTANDARD)
+		{
+#if BASISD_SUPPORT_KTX2_ZSTD
+			size_t actualUncompSize = ZSTD_decompress(uncomp_data.data(), (size_t)uncomp_size, pComp_data, (size_t)comp_size);
+			if (ZSTD_isError(actualUncompSize))
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: Zstd decompression failed, file is invalid or corrupted\n");
+				return false;
+			}
+			if (actualUncompSize != uncomp_size)
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: Zstd decompression returned too few bytes, file is invalid or corrupted\n");
+				return false;
+			}
+#else
+			BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: File uses Zstd supercompression, but Zstd support was not enabled at compile time (BASISD_SUPPORT_KTX2_ZSTD is 0)\n");
+			return false;
+#endif
+		}
+
+		return true;
+	}
+		
+	bool ktx2_transcoder::decompress_etc1s_global_data()
+	{
+		// Note: we don't actually support 3D textures in here yet
+		//uint32_t layer_pixel_depth = basisu::maximum<uint32_t>(m_header.m_pixel_depth, 1);
+		//for (uint32_t i = 1; i < m_header.m_level_count; i++)
+		//	layer_pixel_depth += basisu::maximum<uint32_t>(m_header.m_pixel_depth >> i, 1);
+
+		const uint32_t image_count = basisu::maximum<uint32_t>(m_header.m_layer_count, 1) * m_header.m_face_count * m_header.m_level_count;
+		assert(image_count);
+
+		const uint8_t* pSrc = m_pData + m_header.m_sgd_byte_offset;
+
+		memcpy(&m_etc1s_header, pSrc, sizeof(ktx2_etc1s_global_data_header));
+		pSrc += sizeof(ktx2_etc1s_global_data_header);
+
+		if ((!m_etc1s_header.m_endpoints_byte_length) || (!m_etc1s_header.m_selectors_byte_length) || (!m_etc1s_header.m_tables_byte_length))
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: Invalid ETC1S global data\n");
+			return false;
+		}
+
+		if ((!m_etc1s_header.m_endpoint_count) || (!m_etc1s_header.m_selector_count))
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: endpoint and/or selector count is 0, file is invalid or corrupted\n");
+			return false;
+		}
+
+		// Sanity check the ETC1S header.
+		if ((sizeof(ktx2_etc1s_global_data_header) +
+			sizeof(ktx2_etc1s_image_desc) * image_count +
+			m_etc1s_header.m_endpoints_byte_length +
+			m_etc1s_header.m_selectors_byte_length +
+			m_etc1s_header.m_tables_byte_length +
+			m_etc1s_header.m_extended_byte_length) > m_header.m_sgd_byte_length)
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: SGD byte length is too small, file is invalid or corrupted\n");
+			return false;
+		}
+				
+		if (!m_etc1s_image_descs.try_resize(image_count))
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: Out of memory\n");
+			return false;
+		}
+		
+		memcpy(m_etc1s_image_descs.data(), pSrc, sizeof(ktx2_etc1s_image_desc) * image_count);
+		pSrc += sizeof(ktx2_etc1s_image_desc) * image_count;
+
+		// Sanity check the ETC1S image descs
+		for (uint32_t i = 0; i < image_count; i++)
+		{
+			// m_etc1s_transcoder.transcode_image() will validate the slice offsets/lengths before transcoding.
+
+			if (!m_etc1s_image_descs[i].m_rgb_slice_byte_length)
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: ETC1S image descs sanity check failed (1)\n");
+				return false;
+			}
+
+			if (m_has_alpha)
+			{
+				if (!m_etc1s_image_descs[i].m_alpha_slice_byte_length)
+				{
+					BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: ETC1S image descs sanity check failed (2)\n");
+					return false;
+				}
+			}
+		}
+
+		const uint8_t* pEndpoint_data = pSrc;
+		const uint8_t* pSelector_data = pSrc + m_etc1s_header.m_endpoints_byte_length;
+		const uint8_t* pTables_data = pSrc + m_etc1s_header.m_endpoints_byte_length + m_etc1s_header.m_selectors_byte_length;
+
+		if (!m_etc1s_transcoder.decode_tables(pTables_data, m_etc1s_header.m_tables_byte_length))
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: decode_tables() failed, file is invalid or corrupted\n");
+			return false;
+		}
+				
+		if (!m_etc1s_transcoder.decode_palettes(
+			m_etc1s_header.m_endpoint_count,	pEndpoint_data, m_etc1s_header.m_endpoints_byte_length,
+			m_etc1s_header.m_selector_count,	pSelector_data, m_etc1s_header.m_selectors_byte_length))
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_etc1s_global_data: decode_palettes() failed, file is likely corrupted\n");
+			return false;
+		}
+				
+		return true;
+	}
+
+	bool ktx2_transcoder::read_key_values()
+	{
+		if (!m_header.m_kvd_byte_length)
+		{
+			if (m_header.m_kvd_byte_offset)
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Invalid KVD byte offset (it should be zero when the length is zero)\n");
+				return false;
+			}
+
+			return true;
+		}
+
+		if (m_header.m_kvd_byte_offset < sizeof(ktx2_header))
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Invalid KVD byte offset\n");
+			return false;
+		}
+
+		if ((m_header.m_kvd_byte_offset + m_header.m_kvd_byte_length) > m_data_size)
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Invalid KVD byte offset and/or length\n");
+			return false;
+		}
+
+		const uint8_t* pSrc = m_pData + m_header.m_kvd_byte_offset;
+		uint32_t src_left = m_header.m_kvd_byte_length;
+
+		if (!m_key_values.try_reserve(8))
+		{
+			BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Out of memory\n");
+			return false;
+		}
+
+		while (src_left > sizeof(uint32_t))
+		{
+			uint32_t l = basisu::read_le_dword(pSrc);
+			
+			pSrc += sizeof(uint32_t);
+			src_left -= sizeof(uint32_t);
+
+			if (l < 2)
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Failed reading key value fields (0)\n");
+				return false;
+			}
+
+			if (src_left < l)
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Failed reading key value fields (1)\n");
+				return false;
+			}
+
+			if (!m_key_values.try_resize(m_key_values.size() + 1))
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Out of memory\n");
+				return false;
+			}
+			
+			basisu::uint8_vec& key_data = m_key_values.back().m_key;
+			basisu::uint8_vec& value_data = m_key_values.back().m_value;
+
+			do
+			{
+				if (!l)
+				{
+					BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Failed reading key value fields (2)\n");
+					return false;
+				}
+
+				if (!key_data.try_push_back(*pSrc++))
+				{
+					BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Out of memory\n");
+					return false;
+				}
+
+				src_left--;
+				l--;
+
+			} while (key_data.back());
+						
+			if (!value_data.try_resize(l))
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Out of memory\n");
+				return false;
+			}
+
+			if (l)
+			{
+				memcpy(value_data.data(), pSrc, l);
+				pSrc += l;
+				src_left -= l;
+			}
+
+			uint32_t ofs = (uint32_t)(pSrc - m_pData) & 3;
+			uint32_t alignment_bytes = (4 - ofs) & 3;
+
+			if (src_left < alignment_bytes)
+			{
+				BASISU_DEVEL_ERROR("ktx2_transcoder::read_key_values: Failed reading key value fields (3)\n");
+				return false;
+			}
+
+			pSrc += alignment_bytes;
+			src_left -= alignment_bytes;
+		}
+
+		return true;
+	}
+		
+#endif // BASISD_SUPPORT_KTX2
+
+	bool basisu_transcoder_supports_ktx2()
+	{
+#if BASISD_SUPPORT_KTX2
+		return true;
+#else
+		return false;
+#endif
+	}
+
+	bool basisu_transcoder_supports_ktx2_zstd()
+	{
+#if BASISD_SUPPORT_KTX2_ZSTD
+		return true;
+#else
+		return false;
+#endif
+	}
+
 } // namespace basist
-
-
-
-
-
 /**** ended inlining basisu_transcoder.cpp ****/
 
 /**
diff --git a/contrib/previewers/lib/basisu_transcoder.h b/contrib/previewers/lib/basisu_transcoder.h
deleted file mode 100644
index d1d893b..0000000
--- a/contrib/previewers/lib/basisu_transcoder.h
+++ /dev/null
@@ -1,2705 +0,0 @@
-// basisu_transcoder.h
-// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
-// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-#pragma once
-
-// Set BASISU_FORCE_DEVEL_MESSAGES to 1 to enable debug printf()'s whenever an error occurs, for easier debugging during development.
-#ifndef BASISU_FORCE_DEVEL_MESSAGES
-#define BASISU_FORCE_DEVEL_MESSAGES 0
-#endif
-
-/**** start inlining basisu_transcoder_internal.h ****/
-// basisu_transcoder_internal.h - Universal texture format transcoder library.
-// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
-//
-// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifdef _MSC_VER
-#pragma warning (disable: 4127) //  conditional expression is constant
-#endif
-
-#define BASISD_LIB_VERSION 112
-#define BASISD_VERSION_STRING "01.12"
-
-#ifdef _DEBUG
-#define BASISD_BUILD_DEBUG
-#else
-#define BASISD_BUILD_RELEASE
-#endif
-
-/**** start inlining basisu.h ****/
-// basisu.h
-// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
-// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifdef _MSC_VER
-
-	#pragma warning (disable : 4201)
-	#pragma warning (disable : 4127) // warning C4127: conditional expression is constant
-	#pragma warning (disable : 4530) // C++ exception handler used, but unwind semantics are not enabled.
-
-	#ifndef BASISU_NO_ITERATOR_DEBUG_LEVEL
-		//#define _HAS_ITERATOR_DEBUGGING 0
-
-		#if defined(_DEBUG) || defined(DEBUG)
-			// This is madness, but we need to disable iterator debugging in debug builds or the encoder is unsable because MSVC's iterator debugging implementation is totally broken.
-			#ifndef _ITERATOR_DEBUG_LEVEL
-			#define _ITERATOR_DEBUG_LEVEL 1
-			#endif
-			#ifndef _SECURE_SCL
-			#define _SECURE_SCL 1
-			#endif
-		#else // defined(_DEBUG) || defined(DEBUG)
-			#ifndef _SECURE_SCL
-			#define _SECURE_SCL 0
-			#endif
-			#ifndef _ITERATOR_DEBUG_LEVEL
-			#define _ITERATOR_DEBUG_LEVEL 0
-			#endif
-		#endif // defined(_DEBUG) || defined(DEBUG)
-
-		#ifndef NOMINMAX
-			#define NOMINMAX
-		#endif
-
-	#endif // BASISU_NO_ITERATOR_DEBUG_LEVEL
-
-#endif // _MSC_VER
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <math.h>
-#include <stdarg.h>
-#include <string.h>
-#include <memory.h>
-#include <limits.h>
-#include <stdint.h>
-
-#include <algorithm>
-#include <limits>
-#include <functional>
-#include <iterator>
-#include <type_traits>
-#include <vector>
-#include <assert.h>
-#include <random>
-
-#ifdef max
-#undef max
-#endif
-
-#ifdef min
-#undef min
-#endif
-
-#ifdef _WIN32
-#define strcasecmp _stricmp
-#endif
-
-// Set to one to enable debug printf()'s when any errors occur, for development/debugging. Especially useful for WebGL development.
-#ifndef BASISU_FORCE_DEVEL_MESSAGES
-#define BASISU_FORCE_DEVEL_MESSAGES 0
-#endif
-
-#define BASISU_NOTE_UNUSED(x) (void)(x)
-#define BASISU_ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
-#define BASISU_NO_EQUALS_OR_COPY_CONSTRUCT(x) x(const x &) = delete; x& operator= (const x &) = delete;
-#define BASISU_ASSUME(x) static_assert(x, #x);
-#define BASISU_OFFSETOF(s, m) (uint32_t)(intptr_t)(&((s *)(0))->m)
-#define BASISU_STRINGIZE(x) #x
-#define BASISU_STRINGIZE2(x) BASISU_STRINGIZE(x)
-
-#if BASISU_FORCE_DEVEL_MESSAGES
-	#define BASISU_DEVEL_ERROR(...) do { basisu::debug_printf(__VA_ARGS__); } while(0)
-#else
-	#define BASISU_DEVEL_ERROR(...)
-#endif
-
-namespace basisu
-{
-	// Types/utilities
-
-#ifdef _WIN32
-	const char BASISU_PATH_SEPERATOR_CHAR = '\\';
-#else
-	const char BASISU_PATH_SEPERATOR_CHAR = '/';
-#endif
-
-	typedef std::vector<uint8_t> uint8_vec;
-	typedef std::vector<int16_t> int16_vec;
-	typedef std::vector<uint16_t> uint16_vec;
-	typedef std::vector<uint32_t> uint_vec;
-	typedef std::vector<uint64_t> uint64_vec;
-	typedef std::vector<int> int_vec;
-	typedef std::vector<bool> bool_vec;
-
-	void enable_debug_printf(bool enabled);
-	void debug_printf(const char *pFmt, ...);
-		
-	template <typename T> inline void clear_obj(T& obj) { memset(&obj, 0, sizeof(obj)); }
-
-	template <typename T0, typename T1> inline T0 lerp(T0 a, T0 b, T1 c) { return a + (b - a) * c; }
-
-	template <typename S> inline S maximum(S a, S b) { return (a > b) ? a : b; }
-	template <typename S> inline S maximum(S a, S b, S c) { return maximum(maximum(a, b), c); }
-	template <typename S> inline S maximum(S a, S b, S c, S d) { return maximum(maximum(maximum(a, b), c), d); }
-	
-	template <typename S> inline S minimum(S a, S b) {	return (a < b) ? a : b; }
-	template <typename S> inline S minimum(S a, S b, S c) {	return minimum(minimum(a, b), c); }
-	template <typename S> inline S minimum(S a, S b, S c, S d) { return minimum(minimum(minimum(a, b), c), d); }
-
-	inline float clampf(float value, float low, float high) { if (value < low) value = low; else if (value > high) value = high;	return value; }
-	inline float saturate(float value) { return clampf(value, 0, 1.0f); }
-	inline uint8_t minimumub(uint8_t a, uint8_t b) { return (a < b) ? a : b; }
-	inline uint32_t minimumu(uint32_t a, uint32_t b) { return (a < b) ? a : b; }
-	inline int32_t minimumi(int32_t a, int32_t b) { return (a < b) ? a : b; }
-	inline float minimumf(float a, float b) { return (a < b) ? a : b; }
-	inline uint8_t maximumub(uint8_t a, uint8_t b) { return (a > b) ? a : b; }
-	inline uint32_t maximumu(uint32_t a, uint32_t b) { return (a > b) ? a : b; }
-	inline int32_t maximumi(int32_t a, int32_t b) { return (a > b) ? a : b; }
-	inline float maximumf(float a, float b) { return (a > b) ? a : b; }
-	inline int squarei(int i) { return i * i; }
-	inline float squaref(float i) { return i * i; }
-	template<typename T> inline T square(T a) { return a * a; }
-
-	template <typename S> inline S clamp(S value, S low, S high) { return (value < low) ? low : ((value > high) ? high : value); }
-
-	inline uint32_t iabs(int32_t i) { return (i < 0) ? static_cast<uint32_t>(-i) : static_cast<uint32_t>(i);	}
-	inline uint64_t iabs64(int64_t i) {	return (i < 0) ? static_cast<uint64_t>(-i) : static_cast<uint64_t>(i); }
-
-	template<typename T> inline void clear_vector(T &vec) { vec.erase(vec.begin(), vec.end()); }		
-	template<typename T> inline typename T::value_type *enlarge_vector(T &vec, size_t n) { size_t cs = vec.size(); vec.resize(cs + n); return &vec[cs]; }
-
-	inline bool is_pow2(uint32_t x) { return x && ((x & (x - 1U)) == 0U); }
-	inline bool is_pow2(uint64_t x) { return x && ((x & (x - 1U)) == 0U); }
-
-	template<typename T> inline T open_range_check(T v, T minv, T maxv) { assert(v >= minv && v < maxv); BASISU_NOTE_UNUSED(minv); BASISU_NOTE_UNUSED(maxv); return v; }
-	template<typename T> inline T open_range_check(T v, T maxv) { assert(v < maxv); BASISU_NOTE_UNUSED(maxv); return v; }
-
-	inline uint32_t total_bits(uint32_t v) { uint32_t l = 0; for ( ; v > 0U; ++l) v >>= 1; return l; }
-
-	template<typename T> inline T saturate(T val) { return clamp(val, 0.0f, 1.0f); }
-
-	template<typename T, typename R> inline void append_vector(T &vec, const R *pObjs, size_t n) 
-	{ 
-		if (n)
-		{
-			const size_t cur_s = vec.size();
-			vec.resize(cur_s + n);
-			memcpy(&vec[cur_s], pObjs, sizeof(R) * n);
-		}
-	}
-
-	template<typename T> inline void append_vector(T &vec, const T &other_vec)
-	{
-		if (other_vec.size())
-			append_vector(vec, &other_vec[0], other_vec.size());
-	}
-
-	template<typename T> inline void vector_ensure_element_is_valid(T &vec, size_t idx)
-	{
-		if (idx >= vec.size())
-			vec.resize(idx + 1);
-	}
-
-	template<typename T> inline void vector_sort(T &vec)
-	{
-		if (vec.size())
-			std::sort(vec.begin(), vec.end());
-	}
-
-	template<typename T, typename U> inline bool unordered_set_contains(T& set, const U&obj)
-	{
-		return set.find(obj) != set.end();
-	}
-
-	template<typename T> int vector_find(const T &vec, const typename T::value_type &obj)
-	{
-		assert(vec.size() <= INT_MAX);
-		for (size_t i = 0; i < vec.size(); i++)
-			if (vec[i] == obj)
-				return static_cast<int>(i);
-		return -1;
-	}
-
-	template<typename T> void vector_set_all(T &vec, const typename T::value_type &obj)
-	{
-		for (size_t i = 0; i < vec.size(); i++)
-			vec[i] = obj;
-	}
-		
-	inline uint64_t read_be64(const void *p)
-	{
-		uint64_t val = 0;
-		for (uint32_t i = 0; i < 8; i++)
-			val |= (static_cast<uint64_t>(static_cast<const uint8_t *>(p)[7 - i]) << (i * 8));
-		return val;
-	}
-
-	inline void write_be64(void *p, uint64_t x)
-	{
-		for (uint32_t i = 0; i < 8; i++)
-			static_cast<uint8_t *>(p)[7 - i] = static_cast<uint8_t>(x >> (i * 8));
-	}
-
-	static inline uint16_t byteswap16(uint16_t x) { return static_cast<uint16_t>((x << 8) | (x >> 8)); }
-	static inline uint32_t byteswap32(uint32_t x) { return ((x << 24) | ((x << 8) & 0x00FF0000) | ((x >> 8) & 0x0000FF00) | (x >> 24)); }
-
-	inline uint32_t floor_log2i(uint32_t v)
-	{
-		uint32_t b = 0;
-		for (; v > 1U; ++b)
-			v >>= 1;
-		return b;
-	}
-
-	inline uint32_t ceil_log2i(uint32_t v)
-	{
-		uint32_t b = floor_log2i(v);
-		if ((b != 32) && (v > (1U << b)))
-			++b;
-		return b;
-	}
-
-	inline int posmod(int x, int y)
-	{
-		if (x >= 0)
-			return (x < y) ? x : (x % y);
-		int m = (-x) % y;
-		return (m != 0) ? (y - m) : m;
-	}
-
-	inline bool do_excl_ranges_overlap(int la, int ha, int lb, int hb)
-	{
-		assert(la < ha && lb < hb);
-		if ((ha <= lb) || (la >= hb)) return false;
-		return true;
-	}
-		
-	// Always little endian 2-4 byte unsigned int
-	template<uint32_t NumBytes>
-	struct packed_uint
-	{
-		uint8_t m_bytes[NumBytes];
-
-		inline packed_uint() { static_assert(NumBytes <= 4, "NumBytes <= 4"); }
-		inline packed_uint(uint32_t v) { *this = v; }
-		inline packed_uint(const packed_uint& other) { *this = other; }
-
-		inline packed_uint& operator= (uint32_t v) { for (uint32_t i = 0; i < NumBytes; i++) m_bytes[i] = static_cast<uint8_t>(v >> (i * 8)); return *this; }
-		inline packed_uint& operator= (const packed_uint& rhs) { memcpy(m_bytes, rhs.m_bytes, sizeof(m_bytes)); return *this; }
-
-		inline operator uint32_t() const
-		{
-			switch (NumBytes)
-			{
-				case 1:  return  m_bytes[0];
-				case 2:  return (m_bytes[1] << 8U) | m_bytes[0];
-				case 3:  return (m_bytes[2] << 16U) | (m_bytes[1] << 8U) | (m_bytes[0]);
-				default: return (m_bytes[3] << 24U) | (m_bytes[2] << 16U) | (m_bytes[1] << 8U) | (m_bytes[0]);
-			}
-		}
-	};
-
-	enum eZero { cZero };
-	enum eNoClamp { cNoClamp };
-	
-	// Rice/Huffman entropy coding
-		
-	// This is basically Deflate-style canonical Huffman, except we allow for a lot more symbols.
-	enum
-	{
-		cHuffmanMaxSupportedCodeSize = 16, cHuffmanMaxSupportedInternalCodeSize = 31, 
-		cHuffmanFastLookupBits = 10, cHuffmanFastLookupSize = 1 << cHuffmanFastLookupBits,
-		cHuffmanMaxSymsLog2 = 14, cHuffmanMaxSyms = 1 << cHuffmanMaxSymsLog2,
-
-		// Small zero runs
-		cHuffmanSmallZeroRunSizeMin = 3, cHuffmanSmallZeroRunSizeMax = 10, cHuffmanSmallZeroRunExtraBits = 3,
-
-		// Big zero run
-		cHuffmanBigZeroRunSizeMin = 11, cHuffmanBigZeroRunSizeMax = 138, cHuffmanBigZeroRunExtraBits = 7,
-
-		// Small non-zero run
-		cHuffmanSmallRepeatSizeMin = 3, cHuffmanSmallRepeatSizeMax = 6, cHuffmanSmallRepeatExtraBits = 2,
-
-		// Big non-zero run
-		cHuffmanBigRepeatSizeMin = 7, cHuffmanBigRepeatSizeMax = 134, cHuffmanBigRepeatExtraBits = 7,
-
-		cHuffmanTotalCodelengthCodes = 21, cHuffmanSmallZeroRunCode = 17, cHuffmanBigZeroRunCode = 18, cHuffmanSmallRepeatCode = 19, cHuffmanBigRepeatCode = 20
-	};
-
-	static const uint8_t g_huffman_sorted_codelength_codes[] = { cHuffmanSmallZeroRunCode, cHuffmanBigZeroRunCode,	cHuffmanSmallRepeatCode, cHuffmanBigRepeatCode, 0, 8, 7, 9, 6, 0xA, 5, 0xB, 4, 0xC, 3, 0xD, 2, 0xE, 1, 0xF, 0x10 };
-	const uint32_t cHuffmanTotalSortedCodelengthCodes = sizeof(g_huffman_sorted_codelength_codes) / sizeof(g_huffman_sorted_codelength_codes[0]);
-
-	// GPU texture formats
-
-	enum class texture_format
-	{
-		cInvalidTextureFormat = -1,
-		
-		// Block-based formats
-		cETC1,			// ETC1
-		cETC1S,			// ETC1 (subset: diff colors only, no subblocks)
-		cETC2_RGB,		// ETC2 color block (basisu doesn't support ETC2 planar/T/H modes - just basic ETC1)
-		cETC2_RGBA,		// ETC2 EAC alpha block followed by ETC2 color block
-		cETC2_ALPHA,	// ETC2 EAC alpha block 
-		cBC1,				// DXT1
-		cBC3,				// DXT5 (BC4/DXT5A block followed by a BC1/DXT1 block)
-		cBC4,				// DXT5A
-		cBC5,				// 3DC/DXN (two BC4/DXT5A blocks)
-		cBC7,
-		cASTC4x4,		// LDR only
-		cPVRTC1_4_RGB,
-		cPVRTC1_4_RGBA,
-		cATC_RGB,
-		cATC_RGBA_INTERPOLATED_ALPHA,
-		cFXT1_RGB,
-		cPVRTC2_4_RGBA,
-		cETC2_R11_EAC,
-		cETC2_RG11_EAC,
-		cUASTC4x4,		
-		
-		// Uncompressed/raw pixels
-		cRGBA32,
-		cRGB565,
-		cBGR565,
-		cRGBA4444,
-		cABGR4444
-	};
-
-	inline uint32_t get_bytes_per_block(texture_format fmt)
-	{
-		switch (fmt)
-		{
-		case texture_format::cETC1:
-		case texture_format::cETC1S:
-		case texture_format::cETC2_RGB:
-		case texture_format::cETC2_ALPHA:
-		case texture_format::cBC1:
-		case texture_format::cBC4:
-		case texture_format::cPVRTC1_4_RGB:
-		case texture_format::cPVRTC1_4_RGBA:
-		case texture_format::cATC_RGB:
-		case texture_format::cPVRTC2_4_RGBA:
-		case texture_format::cETC2_R11_EAC:
-			return 8;
-		case texture_format::cRGBA32:
-			return sizeof(uint32_t) * 16;
-		default:
-			break;
-		}
-		return 16;
-	}
-
-	inline uint32_t get_qwords_per_block(texture_format fmt)
-	{
-		return get_bytes_per_block(fmt) >> 3;
-	}
-
-	inline uint32_t get_block_width(texture_format fmt)
-	{
-		BASISU_NOTE_UNUSED(fmt);
-		switch (fmt)
-		{
-		case texture_format::cFXT1_RGB:
-			return 8;
-		default:
-			break;
-		}
-		return 4;
-	}
-
-	inline uint32_t get_block_height(texture_format fmt)
-	{
-		BASISU_NOTE_UNUSED(fmt);
-		return 4;
-	}
-							
-} // namespace basisu
-
-/**** ended inlining basisu.h ****/
-
-#define BASISD_znew (z = 36969 * (z & 65535) + (z >> 16))
-
-namespace basisu
-{
-	extern bool g_debug_printf;
-}
-
-namespace basist
-{
-	// Low-level formats directly supported by the transcoder (other supported texture formats are combinations of these low-level block formats).
-	// You probably don't care about these enum's unless you are going pretty low-level and calling the transcoder to decode individual slices.
-	enum class block_format
-	{
-		cETC1,								// ETC1S RGB 
-		cETC2_RGBA,							// full ETC2 EAC RGBA8 block
-		cBC1,									// DXT1 RGB 
-		cBC3,									// BC4 block followed by a four color BC1 block
-		cBC4,									// DXT5A (alpha block only)
-		cBC5,									// two BC4 blocks
-		cPVRTC1_4_RGB,						// opaque-only PVRTC1 4bpp
-		cPVRTC1_4_RGBA,					// PVRTC1 4bpp RGBA
-		cBC7,									// Full BC7 block, any mode
-		cBC7_M5_COLOR,						// RGB BC7 mode 5 color (writes an opaque mode 5 block)
-		cBC7_M5_ALPHA,						// alpha portion of BC7 mode 5 (cBC7_M5_COLOR output data must have been written to the output buffer first to set the mode/rot fields etc.)
-		cETC2_EAC_A8,						// alpha block of ETC2 EAC (first 8 bytes of the 16-bit ETC2 EAC RGBA format)
-		cASTC_4x4,							// ASTC 4x4 (either color-only or color+alpha). Note that the transcoder always currently assumes sRGB is not enabled when outputting ASTC 
-												// data. If you use a sRGB ASTC format you'll get ~1 LSB of additional error, because of the different way ASTC decoders scale 8-bit endpoints to 16-bits during unpacking.
-		
-		cATC_RGB,
-		cATC_RGBA_INTERPOLATED_ALPHA,
-		cFXT1_RGB,							// Opaque-only, has oddball 8x4 pixel block size
-
-		cPVRTC2_4_RGB,
-		cPVRTC2_4_RGBA,
-
-		cETC2_EAC_R11,
-		cETC2_EAC_RG11,
-												
-		cIndices,							// Used internally: Write 16-bit endpoint and selector indices directly to output (output block must be at least 32-bits)
-
-		cRGB32,								// Writes RGB components to 32bpp output pixels
-		cRGBA32,								// Writes RGB255 components to 32bpp output pixels
-		cA32,									// Writes alpha component to 32bpp output pixels
-				
-		cRGB565,
-		cBGR565,
-		
-		cRGBA4444_COLOR,
-		cRGBA4444_ALPHA,
-		cRGBA4444_COLOR_OPAQUE,
-		cRGBA4444,
-						
-		cTotalBlockFormats
-	};
-
-	const int COLOR5_PAL0_PREV_HI = 9, COLOR5_PAL0_DELTA_LO = -9, COLOR5_PAL0_DELTA_HI = 31;
-	const int COLOR5_PAL1_PREV_HI = 21, COLOR5_PAL1_DELTA_LO = -21, COLOR5_PAL1_DELTA_HI = 21;
-	const int COLOR5_PAL2_PREV_HI = 31, COLOR5_PAL2_DELTA_LO = -31, COLOR5_PAL2_DELTA_HI = 9;
-	const int COLOR5_PAL_MIN_DELTA_B_RUNLEN = 3, COLOR5_PAL_DELTA_5_RUNLEN_VLC_BITS = 3;
-
-	const uint32_t ENDPOINT_PRED_TOTAL_SYMBOLS = (4 * 4 * 4 * 4) + 1;
-	const uint32_t ENDPOINT_PRED_REPEAT_LAST_SYMBOL = ENDPOINT_PRED_TOTAL_SYMBOLS - 1;
-	const uint32_t ENDPOINT_PRED_MIN_REPEAT_COUNT = 3;
-	const uint32_t ENDPOINT_PRED_COUNT_VLC_BITS = 4;
-
-	const uint32_t NUM_ENDPOINT_PREDS = 3;// BASISU_ARRAY_SIZE(g_endpoint_preds);
-	const uint32_t CR_ENDPOINT_PRED_INDEX = NUM_ENDPOINT_PREDS - 1;
-	const uint32_t NO_ENDPOINT_PRED_INDEX = 3;//NUM_ENDPOINT_PREDS;
-	const uint32_t MAX_SELECTOR_HISTORY_BUF_SIZE = 64;
-	const uint32_t SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH = 3;
-	const uint32_t SELECTOR_HISTORY_BUF_RLE_COUNT_BITS = 6;
-	const uint32_t SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL = (1 << SELECTOR_HISTORY_BUF_RLE_COUNT_BITS);
-		
-	uint16_t crc16(const void *r, size_t size, uint16_t crc);
-		
-	class huffman_decoding_table
-	{
-		friend class bitwise_decoder;
-
-	public:
-		huffman_decoding_table()
-		{
-		}
-
-		void clear()
-		{
-			basisu::clear_vector(m_code_sizes);
-			basisu::clear_vector(m_lookup);
-			basisu::clear_vector(m_tree);
-		}
-
-		bool init(uint32_t total_syms, const uint8_t *pCode_sizes)
-		{
-			if (!total_syms)
-			{
-				clear();
-				return true;
-			}
-
-			m_code_sizes.resize(total_syms);
-			memcpy(&m_code_sizes[0], pCode_sizes, total_syms);
-
-			m_lookup.resize(0);
-			m_lookup.resize(basisu::cHuffmanFastLookupSize);
-
-			m_tree.resize(0);
-			m_tree.resize(total_syms * 2);
-
-			uint32_t syms_using_codesize[basisu::cHuffmanMaxSupportedInternalCodeSize + 1];
-			basisu::clear_obj(syms_using_codesize);
-			for (uint32_t i = 0; i < total_syms; i++)
-			{
-				if (pCode_sizes[i] > basisu::cHuffmanMaxSupportedInternalCodeSize)
-					return false;
-				syms_using_codesize[pCode_sizes[i]]++;
-			}
-
-			uint32_t next_code[basisu::cHuffmanMaxSupportedInternalCodeSize + 1];
-			next_code[0] = next_code[1] = 0;
-
-			uint32_t used_syms = 0, total = 0;
-			for (uint32_t i = 1; i < basisu::cHuffmanMaxSupportedInternalCodeSize; i++)
-			{
-				used_syms += syms_using_codesize[i];
-				next_code[i + 1] = (total = ((total + syms_using_codesize[i]) << 1));
-			}
-
-			if (((1U << basisu::cHuffmanMaxSupportedInternalCodeSize) != total) && (used_syms > 1U))
-				return false;
-
-			for (int tree_next = -1, sym_index = 0; sym_index < (int)total_syms; ++sym_index)
-			{
-				uint32_t rev_code = 0, l, cur_code, code_size = pCode_sizes[sym_index];
-				if (!code_size)
-					continue;
-
-				cur_code = next_code[code_size]++;
-
-				for (l = code_size; l > 0; l--, cur_code >>= 1)
-					rev_code = (rev_code << 1) | (cur_code & 1);
-
-				if (code_size <= basisu::cHuffmanFastLookupBits)
-				{
-					uint32_t k = (code_size << 16) | sym_index;
-					while (rev_code < basisu::cHuffmanFastLookupSize)
-					{
-						if (m_lookup[rev_code] != 0)
-						{
-							// Supplied codesizes can't create a valid prefix code.
-							return false;
-						}
-
-						m_lookup[rev_code] = k;
-						rev_code += (1 << code_size);
-					}
-					continue;
-				}
-
-				int tree_cur;
-				if (0 == (tree_cur = m_lookup[rev_code & (basisu::cHuffmanFastLookupSize - 1)]))
-				{
-					const uint32_t idx = rev_code & (basisu::cHuffmanFastLookupSize - 1);
-					if (m_lookup[idx] != 0)
-					{
-						// Supplied codesizes can't create a valid prefix code.
-						return false;
-					}
-
-					m_lookup[idx] = tree_next;
-					tree_cur = tree_next;
-					tree_next -= 2;
-				}
-
-				if (tree_cur >= 0)
-				{
-					// Supplied codesizes can't create a valid prefix code.
-					return false;
-				}
-
-				rev_code >>= (basisu::cHuffmanFastLookupBits - 1);
-
-				for (int j = code_size; j > (basisu::cHuffmanFastLookupBits + 1); j--)
-				{
-					tree_cur -= ((rev_code >>= 1) & 1);
-
-					const int idx = -tree_cur - 1;
-					if (idx < 0)
-						return false;
-					else if (idx >= (int)m_tree.size())
-						m_tree.resize(idx + 1);
-										
-					if (!m_tree[idx])
-					{
-						m_tree[idx] = (int16_t)tree_next;
-						tree_cur = tree_next;
-						tree_next -= 2;
-					}
-					else
-					{
-						tree_cur = m_tree[idx];
-						if (tree_cur >= 0)
-						{
-							// Supplied codesizes can't create a valid prefix code.
-							return false;
-						}
-					}
-				}
-
-				tree_cur -= ((rev_code >>= 1) & 1);
-
-				const int idx = -tree_cur - 1;
-				if (idx < 0)
-					return false;
-				else if (idx >= (int)m_tree.size())
-					m_tree.resize(idx + 1);
-
-				if (m_tree[idx] != 0)
-				{
-					// Supplied codesizes can't create a valid prefix code.
-					return false;
-				}
-
-				m_tree[idx] = (int16_t)sym_index;
-			}
-
-			return true;
-		}
-
-		const basisu::uint8_vec &get_code_sizes() const { return m_code_sizes; }
-
-		bool is_valid() const { return m_code_sizes.size() > 0; }
-
-	private:
-		basisu::uint8_vec m_code_sizes;
-		basisu::int_vec m_lookup;
-		basisu::int16_vec m_tree;
-	};
-
-	class bitwise_decoder
-	{
-	public:
-		bitwise_decoder() :
-			m_buf_size(0),
-			m_pBuf(nullptr),
-			m_pBuf_start(nullptr),
-			m_pBuf_end(nullptr),
-			m_bit_buf(0),
-			m_bit_buf_size(0)
-		{
-		}
-
-		void clear()
-		{
-			m_buf_size = 0;
-			m_pBuf = nullptr;
-			m_pBuf_start = nullptr;
-			m_pBuf_end = nullptr;
-			m_bit_buf = 0;
-			m_bit_buf_size = 0;
-		}
-
-		bool init(const uint8_t *pBuf, uint32_t buf_size)
-		{
-			if ((!pBuf) && (buf_size))
-				return false;
-
-			m_buf_size = buf_size;
-			m_pBuf = pBuf;
-			m_pBuf_start = pBuf;
-			m_pBuf_end = pBuf + buf_size;
-			m_bit_buf = 0;
-			m_bit_buf_size = 0;
-			return true;
-		}
-
-		void stop()
-		{
-		}
-
-		inline uint32_t peek_bits(uint32_t num_bits)
-		{
-			if (!num_bits)
-				return 0;
-
-			assert(num_bits <= 25);
-
-			while (m_bit_buf_size < num_bits)
-			{
-				uint32_t c = 0;
-				if (m_pBuf < m_pBuf_end)
-					c = *m_pBuf++;
-
-				m_bit_buf |= (c << m_bit_buf_size);
-				m_bit_buf_size += 8;
-				assert(m_bit_buf_size <= 32);
-			}
-
-			return m_bit_buf & ((1 << num_bits) - 1);
-		}
-
-		void remove_bits(uint32_t num_bits)
-		{
-			assert(m_bit_buf_size >= num_bits);
-
-			m_bit_buf >>= num_bits;
-			m_bit_buf_size -= num_bits;
-		}
-
-		uint32_t get_bits(uint32_t num_bits)
-		{
-			if (num_bits > 25)
-			{
-				assert(num_bits <= 32);
-
-				const uint32_t bits0 = peek_bits(25);
-				m_bit_buf >>= 25;
-				m_bit_buf_size -= 25;
-				num_bits -= 25;
-
-				const uint32_t bits = peek_bits(num_bits);
-				m_bit_buf >>= num_bits;
-				m_bit_buf_size -= num_bits;
-
-				return bits0 | (bits << 25);
-			}
-
-			const uint32_t bits = peek_bits(num_bits);
-
-			m_bit_buf >>= num_bits;
-			m_bit_buf_size -= num_bits;
-
-			return bits;
-		}
-
-		uint32_t decode_truncated_binary(uint32_t n)
-		{
-			assert(n >= 2);
-
-			const uint32_t k = basisu::floor_log2i(n);
-			const uint32_t u = (1 << (k + 1)) - n;
-
-			uint32_t result = get_bits(k);
-
-			if (result >= u)
-				result = ((result << 1) | get_bits(1)) - u;
-
-			return result;
-		}
-
-		uint32_t decode_rice(uint32_t m)
-		{
-			assert(m);
-
-			uint32_t q = 0;
-			for (;;)
-			{
-				uint32_t k = peek_bits(16);
-				
-				uint32_t l = 0;
-				while (k & 1)
-				{
-					l++;
-					k >>= 1;
-				}
-				
-				q += l;
-
-				remove_bits(l);
-
-				if (l < 16)
-					break;
-			}
-
-			return (q << m) + (get_bits(m + 1) >> 1);
-		}
-
-		inline uint32_t decode_vlc(uint32_t chunk_bits)
-		{
-			assert(chunk_bits);
-
-			const uint32_t chunk_size = 1 << chunk_bits;
-			const uint32_t chunk_mask = chunk_size - 1;
-					
-			uint32_t v = 0;
-			uint32_t ofs = 0;
-
-			for ( ; ; )
-			{
-				uint32_t s = get_bits(chunk_bits + 1);
-				v |= ((s & chunk_mask) << ofs);
-				ofs += chunk_bits;
-
-				if ((s & chunk_size) == 0)
-					break;
-				
-				if (ofs >= 32)
-				{
-					assert(0);
-					break;
-				}
-			}
-
-			return v;
-		}
-
-		inline uint32_t decode_huffman(const huffman_decoding_table &ct)
-		{
-			assert(ct.m_code_sizes.size());
-						
-			while (m_bit_buf_size < 16)
-			{
-				uint32_t c = 0;
-				if (m_pBuf < m_pBuf_end)
-					c = *m_pBuf++;
-
-				m_bit_buf |= (c << m_bit_buf_size);
-				m_bit_buf_size += 8;
-				assert(m_bit_buf_size <= 32);
-			}
-						
-			int code_len;
-
-			int sym;
-			if ((sym = ct.m_lookup[m_bit_buf & (basisu::cHuffmanFastLookupSize - 1)]) >= 0)
-			{
-				code_len = sym >> 16;
-				sym &= 0xFFFF;
-			}
-			else
-			{
-				code_len = basisu::cHuffmanFastLookupBits;
-				do
-				{
-					sym = ct.m_tree[~sym + ((m_bit_buf >> code_len++) & 1)]; // ~sym = -sym - 1
-				} while (sym < 0);
-			}
-
-			m_bit_buf >>= code_len;
-			m_bit_buf_size -= code_len;
-
-			return sym;
-		}
-
-		bool read_huffman_table(huffman_decoding_table &ct)
-		{
-			ct.clear();
-
-			const uint32_t total_used_syms = get_bits(basisu::cHuffmanMaxSymsLog2);
-
-			if (!total_used_syms)
-				return true;
-			if (total_used_syms > basisu::cHuffmanMaxSyms)
-				return false;
-
-			uint8_t code_length_code_sizes[basisu::cHuffmanTotalCodelengthCodes];
-			basisu::clear_obj(code_length_code_sizes);
-
-			const uint32_t num_codelength_codes = get_bits(5);
-			if ((num_codelength_codes < 1) || (num_codelength_codes > basisu::cHuffmanTotalCodelengthCodes))
-				return false;
-
-			for (uint32_t i = 0; i < num_codelength_codes; i++)
-				code_length_code_sizes[basisu::g_huffman_sorted_codelength_codes[i]] = static_cast<uint8_t>(get_bits(3));
-
-			huffman_decoding_table code_length_table;
-			if (!code_length_table.init(basisu::cHuffmanTotalCodelengthCodes, code_length_code_sizes))
-				return false;
-
-			if (!code_length_table.is_valid())
-				return false;
-
-			basisu::uint8_vec code_sizes(total_used_syms);
-
-			uint32_t cur = 0;
-			while (cur < total_used_syms)
-			{
-				int c = decode_huffman(code_length_table);
-
-				if (c <= 16)
-					code_sizes[cur++] = static_cast<uint8_t>(c);
-				else if (c == basisu::cHuffmanSmallZeroRunCode)
-					cur += get_bits(basisu::cHuffmanSmallZeroRunExtraBits) + basisu::cHuffmanSmallZeroRunSizeMin;
-				else if (c == basisu::cHuffmanBigZeroRunCode)
-					cur += get_bits(basisu::cHuffmanBigZeroRunExtraBits) + basisu::cHuffmanBigZeroRunSizeMin;
-				else
-				{
-					if (!cur)
-						return false;
-
-					uint32_t l;
-					if (c == basisu::cHuffmanSmallRepeatCode)
-						l = get_bits(basisu::cHuffmanSmallRepeatExtraBits) + basisu::cHuffmanSmallRepeatSizeMin;
-					else
-						l = get_bits(basisu::cHuffmanBigRepeatExtraBits) + basisu::cHuffmanBigRepeatSizeMin;
-
-					const uint8_t prev = code_sizes[cur - 1];
-					if (prev == 0)
-						return false;
-					do
-					{
-						if (cur >= total_used_syms)
-							return false;
-						code_sizes[cur++] = prev;
-					} while (--l > 0);
-				}
-			}
-
-			if (cur != total_used_syms)
-				return false;
-
-			return ct.init(total_used_syms, &code_sizes[0]);
-		}
-
-	private:
-		uint32_t m_buf_size;
-		const uint8_t *m_pBuf;
-		const uint8_t *m_pBuf_start;
-		const uint8_t *m_pBuf_end;
-
-		uint32_t m_bit_buf;
-		uint32_t m_bit_buf_size;
-	};
-
-	inline uint32_t basisd_rand(uint32_t seed)
-	{
-		if (!seed)
-			seed++;
-		uint32_t z = seed;
-		BASISD_znew;
-		return z;
-	}
-
-	// Returns random number in [0,limit). Max limit is 0xFFFF.
-	inline uint32_t basisd_urand(uint32_t& seed, uint32_t limit)
-	{
-		seed = basisd_rand(seed);
-		return (((seed ^ (seed >> 16)) & 0xFFFF) * limit) >> 16;
-	}
-
-	class approx_move_to_front
-	{
-	public:
-		approx_move_to_front(uint32_t n)
-		{
-			init(n);
-		}
-
-		void init(uint32_t n)
-		{
-			m_values.resize(n);
-			m_rover = n / 2;
-		}
-
-		const basisu::int_vec& get_values() const { return m_values; }
-		basisu::int_vec& get_values() { return m_values; }
-
-		uint32_t size() const { return (uint32_t)m_values.size(); }
-
-		const int& operator[] (uint32_t index) const { return m_values[index]; }
-		int operator[] (uint32_t index) { return m_values[index]; }
-
-		void add(int new_value)
-		{
-			m_values[m_rover++] = new_value;
-			if (m_rover == m_values.size())
-				m_rover = (uint32_t)m_values.size() / 2;
-		}
-
-		void use(uint32_t index)
-		{
-			if (index)
-			{
-				//std::swap(m_values[index / 2], m_values[index]);
-				int x = m_values[index / 2];
-				int y = m_values[index];
-				m_values[index / 2] = y;
-				m_values[index] = x;
-			}
-		}
-
-		// returns -1 if not found
-		int find(int value) const
-		{
-			for (uint32_t i = 0; i < m_values.size(); i++)
-				if (m_values[i] == value)
-					return i;
-			return -1;
-		}
-
-		void reset()
-		{
-			const uint32_t n = (uint32_t)m_values.size();
-
-			m_values.clear();
-
-			init(n);
-		}
-
-	private:
-		basisu::int_vec m_values;
-		uint32_t m_rover;
-	};
-
-	struct decoder_etc_block;
-	
-	inline uint8_t clamp255(int32_t i)
-	{
-		return (uint8_t)((i & 0xFFFFFF00U) ? (~(i >> 31)) : i);
-	}
-
-	enum eNoClamp
-	{
-		cNoClamp = 0
-	};
-
-	struct color32
-	{
-		union
-		{
-			struct
-			{
-				uint8_t r;
-				uint8_t g;
-				uint8_t b;
-				uint8_t a;
-			};
-
-			uint8_t c[4];
-			
-			uint32_t m;
-		};
-
-		color32() { }
-
-		color32(uint32_t vr, uint32_t vg, uint32_t vb, uint32_t va) { set(vr, vg, vb, va); }
-		color32(eNoClamp unused, uint32_t vr, uint32_t vg, uint32_t vb, uint32_t va) { (void)unused; set_noclamp_rgba(vr, vg, vb, va); }
-
-		void set(uint32_t vr, uint32_t vg, uint32_t vb, uint32_t va) { c[0] = static_cast<uint8_t>(vr); c[1] = static_cast<uint8_t>(vg); c[2] = static_cast<uint8_t>(vb); c[3] = static_cast<uint8_t>(va); }
-
-		void set_noclamp_rgb(uint32_t vr, uint32_t vg, uint32_t vb) { c[0] = static_cast<uint8_t>(vr); c[1] = static_cast<uint8_t>(vg); c[2] = static_cast<uint8_t>(vb); }
-		void set_noclamp_rgba(uint32_t vr, uint32_t vg, uint32_t vb, uint32_t va) { set(vr, vg, vb, va); }
-
-		void set_clamped(int vr, int vg, int vb, int va) { c[0] = clamp255(vr); c[1] = clamp255(vg);	c[2] = clamp255(vb); c[3] = clamp255(va); }
-
-		uint8_t operator[] (uint32_t idx) const { assert(idx < 4); return c[idx]; }
-		uint8_t &operator[] (uint32_t idx) { assert(idx < 4); return c[idx]; }
-
-		bool operator== (const color32&rhs) const { return m == rhs.m; }
-
-		static color32 comp_min(const color32& a, const color32& b) { return color32(cNoClamp, std::min(a[0], b[0]), std::min(a[1], b[1]), std::min(a[2], b[2]), std::min(a[3], b[3])); }
-		static color32 comp_max(const color32& a, const color32& b) { return color32(cNoClamp, std::max(a[0], b[0]), std::max(a[1], b[1]), std::max(a[2], b[2]), std::max(a[3], b[3])); }
-	};
-
-	struct endpoint
-	{
-		color32 m_color5;
-		uint8_t m_inten5;
-	};
-
-	struct selector
-	{
-		// Plain selectors (2-bits per value)
-		uint8_t m_selectors[4];
-
-		// ETC1 selectors
-		uint8_t m_bytes[4];
-
-		uint8_t m_lo_selector, m_hi_selector;
-		uint8_t m_num_unique_selectors;
-
-		void init_flags()
-		{
-			uint32_t hist[4] = { 0, 0, 0, 0 };
-			for (uint32_t y = 0; y < 4; y++)
-			{
-				for (uint32_t x = 0; x < 4; x++)
-				{
-					uint32_t s = get_selector(x, y);
-					hist[s]++;
-				}
-			}
-
-			m_lo_selector = 3;
-			m_hi_selector = 0;
-			m_num_unique_selectors = 0;
-
-			for (uint32_t i = 0; i < 4; i++)
-			{
-				if (hist[i])
-				{
-					m_num_unique_selectors++;
-					if (i < m_lo_selector) m_lo_selector = static_cast<uint8_t>(i);
-					if (i > m_hi_selector) m_hi_selector = static_cast<uint8_t>(i);
-				}
-			}
-		}
-
-		// Returned selector value ranges from 0-3 and is a direct index into g_etc1_inten_tables.
-		inline uint32_t get_selector(uint32_t x, uint32_t y) const
-		{
-			assert((x < 4) && (y < 4));
-			return (m_selectors[y] >> (x * 2)) & 3;
-		}
-
-		void set_selector(uint32_t x, uint32_t y, uint32_t val)
-		{
-			static const uint8_t s_selector_index_to_etc1[4] = { 3, 2, 0, 1 };
-
-			assert((x | y | val) < 4);
-
-			m_selectors[y] &= ~(3 << (x * 2));
-			m_selectors[y] |= (val << (x * 2));
-
-			const uint32_t etc1_bit_index = x * 4 + y;
-
-			uint8_t *p = &m_bytes[3 - (etc1_bit_index >> 3)];
-
-			const uint32_t byte_bit_ofs = etc1_bit_index & 7;
-			const uint32_t mask = 1 << byte_bit_ofs;
-
-			const uint32_t etc1_val = s_selector_index_to_etc1[val];
-
-			const uint32_t lsb = etc1_val & 1;
-			const uint32_t msb = etc1_val >> 1;
-
-			p[0] &= ~mask;
-			p[0] |= (lsb << byte_bit_ofs);
-
-			p[-2] &= ~mask;
-			p[-2] |= (msb << byte_bit_ofs);
-		}
-	};
-
-	bool basis_block_format_is_uncompressed(block_format tex_type);
-	
-} // namespace basist
-
-
-
-/**** ended inlining basisu_transcoder_internal.h ****/
-/**** start inlining basisu_transcoder_uastc.h ****/
-// basisu_transcoder_uastc.h
-/**** skipping file: basisu_transcoder_internal.h ****/
-
-namespace basist
-{
-	struct color_quad_u8
-	{ 
-		uint8_t m_c[4]; 
-	};
-
-	const uint32_t TOTAL_UASTC_MODES = 19;
-	const uint32_t UASTC_MODE_INDEX_SOLID_COLOR = 8;
-
-	const uint32_t TOTAL_ASTC_BC7_COMMON_PARTITIONS2 = 30;
-	const uint32_t TOTAL_ASTC_BC7_COMMON_PARTITIONS3 = 11;
-	const uint32_t TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS = 19;
-
-	extern const uint8_t g_uastc_mode_weight_bits[TOTAL_UASTC_MODES];
-	extern const uint8_t g_uastc_mode_weight_ranges[TOTAL_UASTC_MODES];
-	extern const uint8_t g_uastc_mode_endpoint_ranges[TOTAL_UASTC_MODES];
-	extern const uint8_t g_uastc_mode_subsets[TOTAL_UASTC_MODES];
-	extern const uint8_t g_uastc_mode_planes[TOTAL_UASTC_MODES];
-	extern const uint8_t g_uastc_mode_comps[TOTAL_UASTC_MODES];
-	extern const uint8_t g_uastc_mode_has_etc1_bias[TOTAL_UASTC_MODES];
-	extern const uint8_t g_uastc_mode_has_bc1_hint0[TOTAL_UASTC_MODES];
-	extern const uint8_t g_uastc_mode_has_bc1_hint1[TOTAL_UASTC_MODES];
-	extern const uint8_t g_uastc_mode_has_alpha[TOTAL_UASTC_MODES];
-	extern const uint8_t g_uastc_mode_is_la[TOTAL_UASTC_MODES];
-
-	struct astc_bc7_common_partition2_desc
-	{
-		uint8_t m_bc7;
-		uint16_t m_astc;
-		bool m_invert;
-	};
-
-	extern const astc_bc7_common_partition2_desc g_astc_bc7_common_partitions2[TOTAL_ASTC_BC7_COMMON_PARTITIONS2];
-
-	struct bc73_astc2_common_partition_desc
-	{
-		uint8_t m_bc73;
-		uint16_t m_astc2;
-		uint8_t k;		// 0-5 - how to modify the BC7 3-subset pattern to match the ASTC pattern (LSB=invert)
-	};
-
-	extern const bc73_astc2_common_partition_desc g_bc7_3_astc2_common_partitions[TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS];
-
-	struct astc_bc7_common_partition3_desc
-	{
-		uint8_t m_bc7;
-		uint16_t m_astc;
-		uint8_t m_astc_to_bc7_perm; // converts ASTC to BC7 partition using g_astc_bc7_partition_index_perm_tables[][]
-	};
-
-	extern const astc_bc7_common_partition3_desc g_astc_bc7_common_partitions3[TOTAL_ASTC_BC7_COMMON_PARTITIONS3];
-
-	extern const uint8_t g_astc_bc7_patterns2[TOTAL_ASTC_BC7_COMMON_PARTITIONS2][16];
-	extern const uint8_t g_astc_bc7_patterns3[TOTAL_ASTC_BC7_COMMON_PARTITIONS3][16];
-	extern const uint8_t g_bc7_3_astc2_patterns2[TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS][16];
-
-	extern const uint8_t g_astc_bc7_pattern2_anchors[TOTAL_ASTC_BC7_COMMON_PARTITIONS2][3];
-	extern const uint8_t g_astc_bc7_pattern3_anchors[TOTAL_ASTC_BC7_COMMON_PARTITIONS3][3];
-	extern const uint8_t g_bc7_3_astc2_patterns2_anchors[TOTAL_BC7_3_ASTC2_COMMON_PARTITIONS][3];
-
-	extern const uint32_t g_uastc_mode_huff_codes[TOTAL_UASTC_MODES + 1][2];
-
-	extern const uint8_t g_astc_to_bc7_partition_index_perm_tables[6][3];
-	extern const uint8_t g_bc7_to_astc_partition_index_perm_tables[6][3]; // inverse of g_astc_to_bc7_partition_index_perm_tables
-
-	extern const uint8_t* s_uastc_to_bc1_weights[6];
-
-	uint32_t bc7_convert_partition_index_3_to_2(uint32_t p, uint32_t k);
-
-	inline uint32_t astc_interpolate(uint32_t l, uint32_t h, uint32_t w, bool srgb)
-	{
-		if (srgb)
-		{
-			l = (l << 8) | 0x80;
-			h = (h << 8) | 0x80;
-		}
-		else
-		{
-			l = (l << 8) | l;
-			h = (h << 8) | h;
-		}
-
-		uint32_t k = (l * (64 - w) + h * w + 32) >> 6;
-
-		return k >> 8;
-	}
-
-	struct astc_block_desc
-	{
-		int m_weight_range;	// weight BISE range
-
-		int m_subsets;			// number of ASTC partitions
-		int m_partition_seed;	// partition pattern seed
-		int m_cem;				// color endpoint mode used by all subsets
-
-		int m_ccs;				// color component selector (dual plane only)
-		bool m_dual_plane;	// true if dual plane
-
-		// Weight and endpoint BISE values. 
-		// Note these values are NOT linear, they must be BISE encoded. See Table 97 and Table 107.
-		uint8_t m_endpoints[18];	// endpoint values, in RR GG BB etc. order 
-		uint8_t m_weights[64];		// weight index values, raster order, in P0 P1, P0 P1, etc. or P0, P0, P0, P0, etc. order
-	};
-
-	const uint32_t BC7ENC_TOTAL_ASTC_RANGES = 21;
-
-	// See tables 81, 93, 18.13.Endpoint Unquantization
-	const uint32_t TOTAL_ASTC_RANGES = 21;
-	extern const int g_astc_bise_range_table[TOTAL_ASTC_RANGES][3];
-
-	struct astc_quant_bin
-	{
-		uint8_t m_unquant; // unquantized value
-		uint8_t m_index; // sorted index
-	};
-
-	extern astc_quant_bin g_astc_unquant[BC7ENC_TOTAL_ASTC_RANGES][256]; // [ASTC encoded endpoint index]
-
-	int astc_get_levels(int range);
-	bool astc_is_valid_endpoint_range(uint32_t range);
-	uint32_t unquant_astc_endpoint(uint32_t packed_bits, uint32_t packed_trits, uint32_t packed_quints, uint32_t range);
-	uint32_t unquant_astc_endpoint_val(uint32_t packed_val, uint32_t range);
-
-	const uint8_t* get_anchor_indices(uint32_t subsets, uint32_t mode, uint32_t common_pattern, const uint8_t*& pPartition_pattern);
-
-	// BC7
-	const uint32_t BC7ENC_BLOCK_SIZE = 16;
-
-	struct bc7_block
-	{
-		uint64_t m_qwords[2];
-	};
-
-	struct bc7_optimization_results
-	{
-		uint32_t m_mode;
-		uint32_t m_partition;
-		uint8_t m_selectors[16];
-		uint8_t m_alpha_selectors[16];
-		color_quad_u8 m_low[3];
-		color_quad_u8 m_high[3];
-		uint32_t m_pbits[3][2];
-		uint32_t m_index_selector;
-		uint32_t m_rotation;
-	};
-
-	extern const uint32_t g_bc7_weights1[2];
-	extern const uint32_t g_bc7_weights2[4];
-	extern const uint32_t g_bc7_weights3[8];
-	extern const uint32_t g_bc7_weights4[16];
-	extern const uint32_t g_astc_weights4[16];
-	extern const uint32_t g_astc_weights5[32];
-	extern const uint32_t g_astc_weights_3levels[3];
-	extern const uint8_t g_bc7_partition1[16];
-	extern const uint8_t g_bc7_partition2[64 * 16];
-	extern const uint8_t g_bc7_partition3[64 * 16];
-	extern const uint8_t g_bc7_table_anchor_index_second_subset[64];
-	extern const uint8_t g_bc7_table_anchor_index_third_subset_1[64];
-	extern const uint8_t g_bc7_table_anchor_index_third_subset_2[64];
-	extern const uint8_t g_bc7_num_subsets[8];
-	extern const uint8_t g_bc7_partition_bits[8];
-	extern const uint8_t g_bc7_color_index_bitcount[8];
-	extern const uint8_t g_bc7_mode_has_p_bits[8];
-	extern const uint8_t g_bc7_mode_has_shared_p_bits[8];
-	extern const uint8_t g_bc7_color_precision_table[8];
-	extern const int8_t g_bc7_alpha_precision_table[8];
-	extern const uint8_t g_bc7_alpha_index_bitcount[8];
-
-	inline bool get_bc7_mode_has_seperate_alpha_selectors(int mode) { return (mode == 4) || (mode == 5); }
-	inline int get_bc7_color_index_size(int mode, int index_selection_bit) { return g_bc7_color_index_bitcount[mode] + index_selection_bit; }
-	inline int get_bc7_alpha_index_size(int mode, int index_selection_bit) { return g_bc7_alpha_index_bitcount[mode] - index_selection_bit; }
-
-	struct endpoint_err
-	{
-		uint16_t m_error; uint8_t m_lo; uint8_t m_hi;
-	};
-
-	extern endpoint_err g_bc7_mode_6_optimal_endpoints[256][2]; // [c][pbit]
-	const uint32_t BC7ENC_MODE_6_OPTIMAL_INDEX = 5;
-
-	extern endpoint_err g_bc7_mode_5_optimal_endpoints[256]; // [c]
-	const uint32_t BC7ENC_MODE_5_OPTIMAL_INDEX = 1;
-
-	// Packs a BC7 block from a high-level description. Handles all BC7 modes.
-	void encode_bc7_block(void* pBlock, const bc7_optimization_results* pResults);
-
-	// Packs an ASTC block
-	// Constraints: Always 4x4, all subset CEM's must be equal, only tested with LDR CEM's.
-	bool pack_astc_block(uint32_t* pDst, const astc_block_desc* pBlock, uint32_t mode);
-
-	void pack_astc_solid_block(void* pDst_block, const color32& color);
-
-#ifdef _DEBUG
-	int astc_compute_texel_partition(int seed, int x, int y, int z, int partitioncount, bool small_block);
-#endif
-		
-	struct uastc_block
-	{
-		union
-		{
-			uint8_t m_bytes[16];
-			uint32_t m_dwords[4];
-			uint64_t m_qwords[2];
-		};
-	};
-
-	struct unpacked_uastc_block
-	{
-		astc_block_desc m_astc;
-
-		uint32_t m_mode;
-		uint32_t m_common_pattern;
-
-		color32 m_solid_color;
-
-		bool m_bc1_hint0;
-		bool m_bc1_hint1;
-
-		bool m_etc1_flip;
-		bool m_etc1_diff;
-		uint32_t m_etc1_inten0;
-		uint32_t m_etc1_inten1;
-
-		uint32_t m_etc1_bias;
-
-		uint32_t m_etc2_hints;
-
-		uint32_t m_etc1_selector;
-		uint32_t m_etc1_r, m_etc1_g, m_etc1_b;
-	};
-
-	color32 apply_etc1_bias(const color32 &block_color, uint32_t bias, uint32_t limit, uint32_t subblock);
-	
-	struct decoder_etc_block;
-	struct eac_block;
-		
-	bool unpack_uastc(uint32_t mode, uint32_t common_pattern, const color32& solid_color, const astc_block_desc& astc, color32* pPixels, bool srgb);
-	bool unpack_uastc(const unpacked_uastc_block& unpacked_blk, color32* pPixels, bool srgb);
-
-	bool unpack_uastc(const uastc_block& blk, color32* pPixels, bool srgb);
-	bool unpack_uastc(const uastc_block& blk, unpacked_uastc_block& unpacked, bool undo_blue_contract, bool read_hints = true);
-
-	bool transcode_uastc_to_astc(const uastc_block& src_blk, void* pDst);
-
-	bool transcode_uastc_to_bc7(const unpacked_uastc_block& unpacked_src_blk, bc7_optimization_results& dst_blk);
-	bool transcode_uastc_to_bc7(const uastc_block& src_blk, bc7_optimization_results& dst_blk);
-	bool transcode_uastc_to_bc7(const uastc_block& src_blk, void* pDst);
-
-	void transcode_uastc_to_etc1(unpacked_uastc_block& unpacked_src_blk, color32 block_pixels[4][4], void* pDst);
-	bool transcode_uastc_to_etc1(const uastc_block& src_blk, void* pDst);
-	bool transcode_uastc_to_etc1(const uastc_block& src_blk, void* pDst, uint32_t channel);
-
-	void transcode_uastc_to_etc2_eac_a8(unpacked_uastc_block& unpacked_src_blk, color32 block_pixels[4][4], void* pDst);
-	bool transcode_uastc_to_etc2_rgba(const uastc_block& src_blk, void* pDst);
-
-	// Packs 16 scalar values to BC4. Same PSNR as stb_dxt's BC4 encoder, around 13% faster.
-	void encode_bc4(void* pDst, const uint8_t* pPixels, uint32_t stride);
-	
-	void encode_bc1_solid_block(void* pDst, uint32_t fr, uint32_t fg, uint32_t fb);
-
-	enum
-	{
-		cEncodeBC1HighQuality = 1,
-		cEncodeBC1HigherQuality = 2,
-		cEncodeBC1UseSelectors = 4,
-	};
-	void encode_bc1(void* pDst, const uint8_t* pPixels, uint32_t flags);
-	
-	// Alternate PCA-free encoder, around 15% faster, same (or slightly higher) avg. PSNR
-	void encode_bc1_alt(void* pDst, const uint8_t* pPixels, uint32_t flags);
-
-	void transcode_uastc_to_bc1_hint0(const unpacked_uastc_block& unpacked_src_blk, void* pDst);
-	void transcode_uastc_to_bc1_hint1(const unpacked_uastc_block& unpacked_src_blk, const color32 block_pixels[4][4], void* pDst, bool high_quality);
-
-	bool transcode_uastc_to_bc1(const uastc_block& src_blk, void* pDst, bool high_quality);
-	bool transcode_uastc_to_bc3(const uastc_block& src_blk, void* pDst, bool high_quality);
-	bool transcode_uastc_to_bc4(const uastc_block& src_blk, void* pDst, bool high_quality, uint32_t chan0);
-	bool transcode_uastc_to_bc5(const uastc_block& src_blk, void* pDst, bool high_quality, uint32_t chan0, uint32_t chan1);
-
-	bool transcode_uastc_to_etc2_eac_r11(const uastc_block& src_blk, void* pDst, bool high_quality, uint32_t chan0);
-	bool transcode_uastc_to_etc2_eac_rg11(const uastc_block& src_blk, void* pDst, bool high_quality, uint32_t chan0, uint32_t chan1);
-
-	bool transcode_uastc_to_pvrtc1_4_rgb(const uastc_block* pSrc_blocks, void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, bool high_quality, bool from_alpha);
-	bool transcode_uastc_to_pvrtc1_4_rgba(const uastc_block* pSrc_blocks, void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, bool high_quality);
-		
-	// uastc_init() MUST be called before using this module.
-	void uastc_init();
-
-} // namespace basist
-/**** ended inlining basisu_transcoder_uastc.h ****/
-/**** start inlining basisu_global_selector_palette.h ****/
-// basisu_global_selector_palette.h
-// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-/**** skipping file: basisu_transcoder_internal.h ****/
-#include <algorithm>
-
-namespace basist
-{
-	class etc1_global_palette_entry_modifier
-	{
-	public:
-		enum { cTotalBits = 15, cTotalValues = 1 << cTotalBits };
-
-		etc1_global_palette_entry_modifier(uint32_t index = 0)
-		{
-#ifdef _DEBUG
-			static bool s_tested;
-			if (!s_tested)
-			{
-				s_tested = true;
-				for (uint32_t i = 0; i < cTotalValues; i++)
-				{
-					etc1_global_palette_entry_modifier m(i);
-					etc1_global_palette_entry_modifier n = m;
-
-					assert(n.get_index() == i);
-				}
-			}
-#endif
-
-			set_index(index);
-		}
-
-		void set_index(uint32_t index)
-		{
-			assert(index < cTotalValues);
-			m_rot = index & 3;
-			m_flip = (index >> 2) & 1;
-			m_inv = (index >> 3) & 1;
-			m_contrast = (index >> 4) & 3;
-			m_shift = (index >> 6) & 1;
-			m_median = (index >> 7) & 1;
-			m_div = (index >> 8) & 1;
-			m_rand = (index >> 9) & 1;
-			m_dilate = (index >> 10) & 1;
-			m_shift_x = (index >> 11) & 1;
-			m_shift_y = (index >> 12) & 1;
-			m_erode = (index >> 13) & 1;
-			m_high_pass = (index >> 14) & 1;
-		}
-
-		uint32_t get_index() const
-		{
-			return m_rot | (m_flip << 2) | (m_inv << 3) | (m_contrast << 4) | (m_shift << 6) | (m_median << 7) | (m_div << 8) | (m_rand << 9) | (m_dilate << 10) | (m_shift_x << 11) | (m_shift_y << 12) | (m_erode << 13) | (m_high_pass << 14);
-		}
-
-		void clear()
-		{
-			basisu::clear_obj(*this);
-		}
-
-		uint8_t m_contrast;
-		bool m_rand;
-		bool m_median;
-		bool m_div;
-		bool m_shift;
-		bool m_inv;
-		bool m_flip;
-		bool m_dilate;
-		bool m_shift_x;
-		bool m_shift_y;
-		bool m_erode;
-		bool m_high_pass;
-		uint8_t m_rot;
-	};
-
-	enum modifier_types
-	{
-		cModifierContrast,
-		cModifierRand,
-		cModifierMedian,
-		cModifierDiv,
-		cModifierShift,
-		cModifierInv,
-		cModifierFlippedAndRotated,
-		cModifierDilate,
-		cModifierShiftX,
-		cModifierShiftY,
-		cModifierErode,
-		cModifierHighPass,
-		cTotalModifiers
-	};
-
-#define ETC1_GLOBAL_SELECTOR_CODEBOOK_MAX_MOD_BITS (etc1_global_palette_entry_modifier::cTotalBits)
-
-	struct etc1_selector_palette_entry
-	{
-		etc1_selector_palette_entry()
-		{
-			clear();
-		}
-
-		void clear()
-		{
-			basisu::clear_obj(*this);
-		}
-
-		uint8_t operator[] (uint32_t i) const { assert(i < 16); return m_selectors[i]; }
-		uint8_t&operator[] (uint32_t i) { assert(i < 16); return m_selectors[i]; }
-
-		void set_uint32(uint32_t v)
-		{
-			for (uint32_t byte_index = 0; byte_index < 4; byte_index++)
-			{
-				uint32_t b = (v >> (byte_index * 8)) & 0xFF;
-
-				m_selectors[byte_index * 4 + 0] = b & 3;
-				m_selectors[byte_index * 4 + 1] = (b >> 2) & 3;
-				m_selectors[byte_index * 4 + 2] = (b >> 4) & 3;
-				m_selectors[byte_index * 4 + 3] = (b >> 6) & 3;
-			}
-		}
-
-		uint32_t get_uint32() const
-		{
-			return get_byte(0) | (get_byte(1) << 8) | (get_byte(2) << 16) | (get_byte(3) << 24);
-		}
-
-		uint32_t get_byte(uint32_t byte_index) const
-		{
-			assert(byte_index < 4);
-
-			return m_selectors[byte_index * 4 + 0] |
-				(m_selectors[byte_index * 4 + 1] << 2) |
-				(m_selectors[byte_index * 4 + 2] << 4) |
-				(m_selectors[byte_index * 4 + 3] << 6);
-		}
-
-		uint8_t operator()(uint32_t x, uint32_t y) const { assert((x < 4) && (y < 4)); return m_selectors[x + y * 4]; }
-		uint8_t&operator()(uint32_t x, uint32_t y) { assert((x < 4) && (y < 4)); return m_selectors[x + y * 4]; }
-
-		uint32_t calc_distance(const etc1_selector_palette_entry &other) const
-		{
-			uint32_t dist = 0;
-			for (uint32_t i = 0; i < 8; i++)
-			{
-				int delta = static_cast<int>(m_selectors[i]) - static_cast<int>(other.m_selectors[i]);
-				dist += delta * delta;
-			}
-			return dist;
-		}
-
-#if 0
-		uint32_t calc_hamming_dist(const etc1_selector_palette_entry &other) const
-		{
-			uint32_t dist = 0;
-			for (uint32_t i = 0; i < 4; i++)
-				dist += g_hamming_dist[get_byte(i) ^ other.get_byte(i)];
-			return dist;
-		}
-#endif
-
-		etc1_selector_palette_entry get_inverted() const
-		{
-			etc1_selector_palette_entry result;
-
-			for (uint32_t i = 0; i < 16; i++)
-				result.m_selectors[i] = 3 - m_selectors[i];
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_divided() const
-		{
-			etc1_selector_palette_entry result;
-
-			const uint8_t div_selector[4] = { 2, 0, 3, 1 };
-
-			for (uint32_t i = 0; i < 16; i++)
-				result.m_selectors[i] = div_selector[m_selectors[i]];
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_shifted(int delta) const
-		{
-			etc1_selector_palette_entry result;
-
-			for (uint32_t i = 0; i < 16; i++)
-				result.m_selectors[i] = static_cast<uint8_t>(basisu::clamp<int>(m_selectors[i] + delta, 0, 3));
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_randomized() const
-		{
-			uint32_t seed = get_uint32();
-
-			etc1_selector_palette_entry result;
-
-			for (uint32_t y = 0; y < 4; y++)
-			{
-				for (uint32_t x = 0; x < 4; x++)
-				{
-					int s = (*this)(x, y);
-
-					// between 0 and 10
-					uint32_t i = basisd_urand(seed, 6) + basisd_urand(seed, 6);
-					if (i == 0)
-						s -= 2;
-					else if (i == 10)
-						s += 2;
-					else if (i < 3)
-						s -= 1;
-					else if (i > 7)
-						s += 1;
-
-					result(x, y) = static_cast<uint8_t>(basisu::clamp<int>(s, 0, 3));
-				}
-			}
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_contrast(int table_index) const
-		{
-			assert(table_index < 4);
-
-			etc1_selector_palette_entry result;
-
-			static const uint8_t s_contrast_tables[4][4] =
-			{
-				{ 0, 1, 2, 3 }, // not used
-				{ 0, 0, 3, 3 },
-				{ 1, 1, 2, 2 },
-				{ 1, 1, 3, 3 }
-			};
-
-			for (uint32_t i = 0; i < 16; i++)
-			{
-				result[i] = s_contrast_tables[table_index][(*this)[i]];
-			}
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_dilated() const
-		{
-			etc1_selector_palette_entry result;
-
-			for (uint32_t y = 0; y < 4; y++)
-			{
-				for (uint32_t x = 0; x < 4; x++)
-				{
-					uint32_t max_selector = 0;
-
-					for (int yd = -1; yd <= 1; yd++)
-					{
-						int fy = y + yd;
-						if ((fy < 0) || (fy > 3))
-							continue;
-
-						for (int xd = -1; xd <= 1; xd++)
-						{
-							int fx = x + xd;
-							if ((fx < 0) || (fx > 3))
-								continue;
-
-							max_selector = basisu::maximum<uint32_t>(max_selector, (*this)(fx, fy));
-						}
-					}
-
-					result(x, y) = static_cast<uint8_t>(max_selector);
-				}
-			}
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_eroded() const
-		{
-			etc1_selector_palette_entry result;
-
-			for (uint32_t y = 0; y < 4; y++)
-			{
-				for (uint32_t x = 0; x < 4; x++)
-				{
-					uint32_t min_selector = 99;
-
-					for (int yd = -1; yd <= 1; yd++)
-					{
-						int fy = y + yd;
-						if ((fy < 0) || (fy > 3))
-							continue;
-
-						for (int xd = -1; xd <= 1; xd++)
-						{
-							int fx = x + xd;
-							if ((fx < 0) || (fx > 3))
-								continue;
-
-							min_selector = basisu::minimum<uint32_t>(min_selector, (*this)(fx, fy));
-						}
-					}
-
-					result(x, y) = static_cast<uint8_t>(min_selector);
-				}
-			}
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_shift_x() const
-		{
-			etc1_selector_palette_entry result;
-
-			for (uint32_t y = 0; y < 4; y++)
-			{
-				for (uint32_t x = 0; x < 4; x++)
-				{
-					int sx = x - 1;
-					if (sx < 0)
-						sx = 0;
-
-					result(x, y) = (*this)(sx, y);
-				}
-			}
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_shift_y() const
-		{
-			etc1_selector_palette_entry result;
-
-			for (uint32_t y = 0; y < 4; y++)
-			{
-				int sy = y - 1;
-				if (sy < 0)
-					sy = 3;
-
-				for (uint32_t x = 0; x < 4; x++)
-					result(x, y) = (*this)(x, sy);
-			}
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_median() const
-		{
-			etc1_selector_palette_entry result;
-
-			for (uint32_t y = 0; y < 4; y++)
-			{
-				for (uint32_t x = 0; x < 4; x++)
-				{
-					// ABC
-					// D F
-					// GHI
-
-					uint8_t selectors[8];
-					uint32_t n = 0;
-
-					for (int yd = -1; yd <= 1; yd++)
-					{
-						int fy = y + yd;
-						if ((fy < 0) || (fy > 3))
-							continue;
-
-						for (int xd = -1; xd <= 1; xd++)
-						{
-							if ((xd | yd) == 0)
-								continue;
-
-							int fx = x + xd;
-							if ((fx < 0) || (fx > 3))
-								continue;
-
-							selectors[n++] = (*this)(fx, fy);
-						}
-					}
-
-					std::sort(selectors, selectors + n);
-
-					result(x, y) = selectors[n / 2];
-				}
-			}
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_high_pass() const
-		{
-			etc1_selector_palette_entry result;
-
-			static const int kernel[3][3] =
-			{
-				{ 0,  -1,  0 },
-				{ -1,  8, -1 },
-				{ 0,  -1,  0 }
-			};
-
-			for (uint32_t y = 0; y < 4; y++)
-			{
-				for (uint32_t x = 0; x < 4; x++)
-				{
-					// ABC
-					// D F
-					// GHI
-
-					int sum = 0;
-
-					for (int yd = -1; yd <= 1; yd++)
-					{
-						int fy = y + yd;
-						fy = basisu::clamp<int>(fy, 0, 3);
-
-						for (int xd = -1; xd <= 1; xd++)
-						{
-							int fx = x + xd;
-							fx = basisu::clamp<int>(fx, 0, 3);
-
-							int k = (*this)(fx, fy);
-							sum += k * kernel[yd + 1][xd + 1];
-						}
-					}
-
-					sum = sum / 4;
-
-					result(x, y) = static_cast<uint8_t>(basisu::clamp<int>(sum, 0, 3));
-				}
-			}
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_flipped_and_rotated(bool flip, uint32_t rotation_index) const
-		{
-			etc1_selector_palette_entry temp;
-
-			if (flip)
-			{
-				for (uint32_t y = 0; y < 4; y++)
-					for (uint32_t x = 0; x < 4; x++)
-						temp(x, y) = (*this)(x, 3 - y);
-			}
-			else
-			{
-				temp = *this;
-			}
-
-			etc1_selector_palette_entry result;
-
-			switch (rotation_index)
-			{
-			case 0:
-				result = temp;
-				break;
-			case 1:
-				for (uint32_t y = 0; y < 4; y++)
-					for (uint32_t x = 0; x < 4; x++)
-						result(x, y) = temp(y, 3 - x);
-				break;
-			case 2:
-				for (uint32_t y = 0; y < 4; y++)
-					for (uint32_t x = 0; x < 4; x++)
-						result(x, y) = temp(3 - x, 3 - y);
-				break;
-			case 3:
-				for (uint32_t y = 0; y < 4; y++)
-					for (uint32_t x = 0; x < 4; x++)
-						result(x, y) = temp(3 - y, x);
-				break;
-			default:
-				assert(0);
-				break;
-			}
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_modified(const etc1_global_palette_entry_modifier &modifier) const
-		{
-			etc1_selector_palette_entry r(*this);
-
-			if (modifier.m_shift_x)
-				r = r.get_shift_x();
-
-			if (modifier.m_shift_y)
-				r = r.get_shift_y();
-
-			r = r.get_flipped_and_rotated(modifier.m_flip != 0, modifier.m_rot);
-
-			if (modifier.m_dilate)
-				r = r.get_dilated();
-
-			if (modifier.m_erode)
-				r = r.get_eroded();
-
-			if (modifier.m_high_pass)
-				r = r.get_high_pass();
-
-			if (modifier.m_rand)
-				r = r.get_randomized();
-
-			if (modifier.m_div)
-				r = r.get_divided();
-
-			if (modifier.m_shift)
-				r = r.get_shifted(1);
-
-			if (modifier.m_contrast)
-				r = r.get_contrast(modifier.m_contrast);
-
-			if (modifier.m_inv)
-				r = r.get_inverted();
-
-			if (modifier.m_median)
-				r = r.get_median();
-
-			return r;
-		}
-
-		etc1_selector_palette_entry apply_modifier(modifier_types mod_type, const etc1_global_palette_entry_modifier &modifier) const
-		{
-			switch (mod_type)
-			{
-			case cModifierContrast:
-				return get_contrast(modifier.m_contrast);
-			case cModifierRand:
-				return get_randomized();
-			case cModifierMedian:
-				return get_median();
-			case cModifierDiv:
-				return get_divided();
-			case cModifierShift:
-				return get_shifted(1);
-			case cModifierInv:
-				return get_inverted();
-			case cModifierFlippedAndRotated:
-				return get_flipped_and_rotated(modifier.m_flip != 0, modifier.m_rot);
-			case cModifierDilate:
-				return get_dilated();
-			case cModifierShiftX:
-				return get_shift_x();
-			case cModifierShiftY:
-				return get_shift_y();
-			case cModifierErode:
-				return get_eroded();
-			case cModifierHighPass:
-				return get_high_pass();
-			default:
-				assert(0);
-				break;
-			}
-
-			return *this;
-		}
-
-		etc1_selector_palette_entry get_modified(const etc1_global_palette_entry_modifier &modifier, uint32_t num_order, const modifier_types *pOrder) const
-		{
-			etc1_selector_palette_entry r(*this);
-
-			for (uint32_t i = 0; i < num_order; i++)
-			{
-				r = r.apply_modifier(pOrder[i], modifier);
-			}
-
-			return r;
-		}
-
-		bool operator< (const etc1_selector_palette_entry &other) const
-		{
-			for (uint32_t i = 0; i < 16; i++)
-			{
-				if (m_selectors[i] < other.m_selectors[i])
-					return true;
-				else if (m_selectors[i] != other.m_selectors[i])
-					return false;
-			}
-
-			return false;
-		}
-
-		bool operator== (const etc1_selector_palette_entry &other) const
-		{
-			for (uint32_t i = 0; i < 16; i++)
-			{
-				if (m_selectors[i] != other.m_selectors[i])
-					return false;
-			}
-
-			return true;
-		}
-
-	private:
-		uint8_t m_selectors[16];
-	};
-
-	typedef std::vector<etc1_selector_palette_entry> etc1_selector_palette_entry_vec;
-
-	extern const uint32_t g_global_selector_cb[];
-	extern const uint32_t g_global_selector_cb_size;
-
-#define ETC1_GLOBAL_SELECTOR_CODEBOOK_MAX_PAL_BITS (12)
-
-	struct etc1_global_selector_codebook_entry_id
-	{
-		uint32_t m_palette_index;
-		etc1_global_palette_entry_modifier m_modifier;
-
-		etc1_global_selector_codebook_entry_id(uint32_t palette_index, const etc1_global_palette_entry_modifier &modifier) : m_palette_index(palette_index), m_modifier(modifier) { }
-
-		etc1_global_selector_codebook_entry_id() { }
-
-		void set(uint32_t palette_index, const etc1_global_palette_entry_modifier &modifier) { m_palette_index = palette_index; m_modifier = modifier; }
-	};
-
-	typedef std::vector<etc1_global_selector_codebook_entry_id> etc1_global_selector_codebook_entry_id_vec;
-
-	class etc1_global_selector_codebook
-	{
-	public:
-		etc1_global_selector_codebook() { }
-		etc1_global_selector_codebook(uint32_t N, const uint32_t *pEntries) { init(N, pEntries); }
-
-		void init(uint32_t N, const uint32_t* pEntries);
-
-		void print_code(FILE *pFile);
-
-		void clear()
-		{
-			m_palette.clear();
-		}
-
-		uint32_t size() const { return (uint32_t)m_palette.size(); }
-
-		const etc1_selector_palette_entry_vec &get_palette() const
-		{
-			return m_palette;
-		}
-
-		etc1_selector_palette_entry get_entry(uint32_t palette_index) const
-		{
-			return m_palette[palette_index];
-		}
-
-		etc1_selector_palette_entry get_entry(uint32_t palette_index, const etc1_global_palette_entry_modifier &modifier) const
-		{
-			return m_palette[palette_index].get_modified(modifier);
-		}
-
-		etc1_selector_palette_entry get_entry(const etc1_global_selector_codebook_entry_id &id) const
-		{
-			return m_palette[id.m_palette_index].get_modified(id.m_modifier);
-		}
-
-		etc1_selector_palette_entry_vec m_palette;
-	};
-
-} // namespace basist
-/**** ended inlining basisu_global_selector_palette.h ****/
-/**** start inlining basisu_file_headers.h ****/
-// basis_file_headers.h
-// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-/**** skipping file: basisu_transcoder_internal.h ****/
-
-namespace basist
-{
-	// Slice desc header flags
-	enum basis_slice_desc_flags
-	{
-		cSliceDescFlagsHasAlpha = 1,
-		cSliceDescFlagsFrameIsIFrame = 2			// Video only: Frame doesn't refer to previous frame (no usage of conditional replenishment pred symbols)
-	};
-
-#pragma pack(push)
-#pragma pack(1)
-	struct basis_slice_desc
-	{
-		basisu::packed_uint<3> m_image_index;  // The index of the source image provided to the encoder (will always appear in order from first to last, first image index is 0, no skipping allowed)
-		basisu::packed_uint<1> m_level_index;	// The mipmap level index (mipmaps will always appear from largest to smallest)
-		basisu::packed_uint<1> m_flags;			// enum basis_slice_desc_flags
-
-		basisu::packed_uint<2> m_orig_width;	// The original image width (may not be a multiple of 4 pixels)
-		basisu::packed_uint<2> m_orig_height;  // The original image height (may not be a multiple of 4 pixels)
-
-		basisu::packed_uint<2> m_num_blocks_x;	// The slice's block X dimensions. Each block is 4x4 pixels. The slice's pixel resolution may or may not be a power of 2.
-		basisu::packed_uint<2> m_num_blocks_y;	// The slice's block Y dimensions. 
-
-		basisu::packed_uint<4> m_file_ofs;		// Offset from the header to the start of the slice's data
-		basisu::packed_uint<4> m_file_size;		// The size of the compressed slice data in bytes
-
-		basisu::packed_uint<2> m_slice_data_crc16; // The CRC16 of the compressed slice data, for extra-paranoid use cases
-	};
-
-	// File header files
-	enum basis_header_flags
-	{
-		cBASISHeaderFlagETC1S = 1,					// Always set for ETC1S files. Not set for UASTC files.
-		cBASISHeaderFlagYFlipped = 2,				// Set if the texture had to be Y flipped before encoding
-		cBASISHeaderFlagHasAlphaSlices = 4		// True if any slices contain alpha (for ETC1S, if the odd slices contain alpha data)
-	};
-
-	// The image type field attempts to describe how to interpret the image data in a Basis file.
-	// The encoder library doesn't really do anything special or different with these texture types, this is mostly here for the benefit of the user. 
-	// We do make sure the various constraints are followed (2DArray/cubemap/videoframes/volume implies that each image has the same resolution and # of mipmap levels, etc., cubemap implies that the # of image slices is a multiple of 6)
-	enum basis_texture_type
-	{
-		cBASISTexType2D = 0,					// An arbitrary array of 2D RGB or RGBA images with optional mipmaps, array size = # images, each image may have a different resolution and # of mipmap levels
-		cBASISTexType2DArray = 1,			// An array of 2D RGB or RGBA images with optional mipmaps, array size = # images, each image has the same resolution and mipmap levels
-		cBASISTexTypeCubemapArray = 2,	// an array of cubemap levels, total # of images must be divisable by 6, in X+, X-, Y+, Y-, Z+, Z- order, with optional mipmaps
-		cBASISTexTypeVideoFrames = 3,		// An array of 2D video frames, with optional mipmaps, # frames = # images, each image has the same resolution and # of mipmap levels
-		cBASISTexTypeVolume = 4,			// A 3D texture with optional mipmaps, Z dimension = # images, each image has the same resolution and # of mipmap levels
-
-		cBASISTexTypeTotal
-	};
-
-	enum
-	{
-		cBASISMaxUSPerFrame = 0xFFFFFF
-	};
-
-	enum class basis_tex_format
-	{
-		cETC1S = 0,
-		cUASTC4x4 = 1
-	};
-
-	struct basis_file_header
-	{
-		enum
-		{
-			cBASISSigValue = ('B' << 8) | 's',
-			cBASISFirstVersion = 0x10
-		};
-
-		basisu::packed_uint<2>      m_sig;				// 2 byte file signature
-		basisu::packed_uint<2>      m_ver;				// Baseline file version
-		basisu::packed_uint<2>      m_header_size;	// Header size in bytes, sizeof(basis_file_header)
-		basisu::packed_uint<2>      m_header_crc16;	// crc16 of the remaining header data
-
-		basisu::packed_uint<4>      m_data_size;		// The total size of all data after the header
-		basisu::packed_uint<2>      m_data_crc16;		// The CRC16 of all data after the header
-
-		basisu::packed_uint<3>      m_total_slices;	// The total # of compressed slices (1 slice per image, or 2 for alpha basis files)
-
-		basisu::packed_uint<3>      m_total_images;	// The total # of images
-				
-		basisu::packed_uint<1>      m_tex_format;		// enum basis_tex_format
-		basisu::packed_uint<2>      m_flags;			// enum basist::header_flags
-		basisu::packed_uint<1>      m_tex_type;		// enum basist::basis_texture_type
-		basisu::packed_uint<3>      m_us_per_frame;	// Framerate of video, in microseconds per frame
-
-		basisu::packed_uint<4>      m_reserved;		// For future use
-		basisu::packed_uint<4>      m_userdata0;		// For client use
-		basisu::packed_uint<4>      m_userdata1;		// For client use
-
-		basisu::packed_uint<2>      m_total_endpoints;			// The number of endpoints in the endpoint codebook 
-		basisu::packed_uint<4>      m_endpoint_cb_file_ofs;	// The compressed endpoint codebook's file offset relative to the header
-		basisu::packed_uint<3>      m_endpoint_cb_file_size;	// The compressed endpoint codebook's size in bytes
-
-		basisu::packed_uint<2>      m_total_selectors;			// The number of selectors in the endpoint codebook 
-		basisu::packed_uint<4>      m_selector_cb_file_ofs;	// The compressed selectors codebook's file offset relative to the header
-		basisu::packed_uint<3>      m_selector_cb_file_size;	// The compressed selector codebook's size in bytes
-
-		basisu::packed_uint<4>      m_tables_file_ofs;			// The file offset of the compressed Huffman codelength tables, for decompressing slices
-		basisu::packed_uint<4>      m_tables_file_size;			// The file size in bytes of the compressed huffman codelength tables
-
-		basisu::packed_uint<4>      m_slice_desc_file_ofs;		// The file offset to the slice description array, usually follows the header
-		
-		basisu::packed_uint<4>      m_extended_file_ofs;		// The file offset of the "extended" header and compressed data, for future use
-		basisu::packed_uint<4>      m_extended_file_size;		// The file size in bytes of the "extended" header and compressed data, for future use
-	};
-#pragma pack (pop)
-
-} // namespace basist
-/**** ended inlining basisu_file_headers.h ****/
-
-namespace basist
-{
-	// High-level composite texture formats supported by the transcoder.
-	// Each of these texture formats directly correspond to OpenGL/D3D/Vulkan etc. texture formats.
-	// Notes:
-	// - If you specify a texture format that supports alpha, but the .basis file doesn't have alpha, the transcoder will automatically output a 
-	// fully opaque (255) alpha channel.
-	// - The PVRTC1 texture formats only support power of 2 dimension .basis files, but this may be relaxed in a future version.
-	// - The PVRTC1 transcoders are real-time encoders, so don't expect the highest quality. We may add a slower encoder with improved quality.
-	// - These enums must be kept in sync with Javascript code that calls the transcoder.
-	enum class transcoder_texture_format
-	{
-		// Compressed formats
-
-		// ETC1-2
-		cTFETC1_RGB = 0,							// Opaque only, returns RGB or alpha data if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
-		cTFETC2_RGBA = 1,							// Opaque+alpha, ETC2_EAC_A8 block followed by a ETC1 block, alpha channel will be opaque for opaque .basis files
-
-		// BC1-5, BC7 (desktop, some mobile devices)
-		cTFBC1_RGB = 2,							// Opaque only, no punchthrough alpha support yet, transcodes alpha slice if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
-		cTFBC3_RGBA = 3, 							// Opaque+alpha, BC4 followed by a BC1 block, alpha channel will be opaque for opaque .basis files
-		cTFBC4_R = 4,								// Red only, alpha slice is transcoded to output if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
-		cTFBC5_RG = 5,								// XY: Two BC4 blocks, X=R and Y=Alpha, .basis file should have alpha data (if not Y will be all 255's)
-		cTFBC7_RGBA = 6,							// RGB or RGBA, mode 5 for ETC1S, modes (1,2,3,5,6,7) for UASTC
-				
-		// PVRTC1 4bpp (mobile, PowerVR devices)
-		cTFPVRTC1_4_RGB = 8,						// Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified, nearly lowest quality of any texture format.
-		cTFPVRTC1_4_RGBA = 9,					// Opaque+alpha, most useful for simple opacity maps. If .basis file doesn't have alpha cTFPVRTC1_4_RGB will be used instead. Lowest quality of any supported texture format.
-
-		// ASTC (mobile, Intel devices, hopefully all desktop GPU's one day)
-		cTFASTC_4x4_RGBA = 10,					// Opaque+alpha, ASTC 4x4, alpha channel will be opaque for opaque .basis files. Transcoder uses RGB/RGBA/L/LA modes, void extent, and up to two ([0,47] and [0,255]) endpoint precisions.
-
-		// ATC (mobile, Adreno devices, this is a niche format)
-		cTFATC_RGB = 11,							// Opaque, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. ATI ATC (GL_ATC_RGB_AMD)
-		cTFATC_RGBA = 12,							// Opaque+alpha, alpha channel will be opaque for opaque .basis files. ATI ATC (GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD) 
-
-		// FXT1 (desktop, Intel devices, this is a super obscure format)
-		cTFFXT1_RGB = 17,							// Opaque only, uses exclusively CC_MIXED blocks. Notable for having a 8x4 block size. GL_3DFX_texture_compression_FXT1 is supported on Intel integrated GPU's (such as HD 630).
-														// Punch-through alpha is relatively easy to support, but full alpha is harder. This format is only here for completeness so opaque-only is fine for now.
-														// See the BASISU_USE_ORIGINAL_3DFX_FXT1_ENCODING macro in basisu_transcoder_internal.h.
-
-		cTFPVRTC2_4_RGB = 18,					// Opaque-only, almost BC1 quality, much faster to transcode and supports arbitrary texture dimensions (unlike PVRTC1 RGB).
-		cTFPVRTC2_4_RGBA = 19,					// Opaque+alpha, slower to encode than cTFPVRTC2_4_RGB. Premultiplied alpha is highly recommended, otherwise the color channel can leak into the alpha channel on transparent blocks.
-
-		cTFETC2_EAC_R11 = 20,					// R only (ETC2 EAC R11 unsigned)
-		cTFETC2_EAC_RG11 = 21,					// RG only (ETC2 EAC RG11 unsigned), R=opaque.r, G=alpha - for tangent space normal maps
-		
-		// Uncompressed (raw pixel) formats
-		cTFRGBA32 = 13,							// 32bpp RGBA image stored in raster (not block) order in memory, R is first byte, A is last byte.
-		cTFRGB565 = 14,							// 166pp RGB image stored in raster (not block) order in memory, R at bit position 11
-		cTFBGR565 = 15,							// 16bpp RGB image stored in raster (not block) order in memory, R at bit position 0
-		cTFRGBA4444 = 16,							// 16bpp RGBA image stored in raster (not block) order in memory, R at bit position 12, A at bit position 0
-
-		cTFTotalTextureFormats = 22,
-
-		// Old enums for compatibility with code compiled against previous versions
-		cTFETC1 = cTFETC1_RGB,
-		cTFETC2 = cTFETC2_RGBA,
-		cTFBC1 = cTFBC1_RGB,
-		cTFBC3 = cTFBC3_RGBA,
-		cTFBC4 = cTFBC4_R,
-		cTFBC5 = cTFBC5_RG,
-		
-		// Previously, the caller had some control over which BC7 mode the transcoder output. We've simplified this due to UASTC, which supports numerous modes.
-		cTFBC7_M6_RGB = cTFBC7_RGBA,			// Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. Highest quality of all the non-ETC1 formats.
-		cTFBC7_M5_RGBA = cTFBC7_RGBA,			// Opaque+alpha, alpha channel will be opaque for opaque .basis files
-		cTFBC7_M6_OPAQUE_ONLY = cTFBC7_RGBA,
-		cTFBC7_M5 = cTFBC7_RGBA,
-		cTFBC7_ALT = 7,
-
-		cTFASTC_4x4 = cTFASTC_4x4_RGBA,
-		
-		cTFATC_RGBA_INTERPOLATED_ALPHA = cTFATC_RGBA,
-	};
-
-	// For compressed texture formats, this returns the # of bytes per block. For uncompressed, it returns the # of bytes per pixel.
-	// NOTE: Previously, this function was called basis_get_bytes_per_block(), and it always returned 16*bytes_per_pixel for uncompressed formats which was confusing.
-	uint32_t basis_get_bytes_per_block_or_pixel(transcoder_texture_format fmt);
-
-	// Returns format's name in ASCII
-	const char* basis_get_format_name(transcoder_texture_format fmt);
-
-	// Returns true if the format supports an alpha channel.
-	bool basis_transcoder_format_has_alpha(transcoder_texture_format fmt);
-
-	// Returns the basisu::texture_format corresponding to the specified transcoder_texture_format.
-	basisu::texture_format basis_get_basisu_texture_format(transcoder_texture_format fmt);
-
-	// Returns the texture type's name in ASCII.
-	const char* basis_get_texture_type_name(basis_texture_type tex_type);
-	
-	// Returns true if the transcoder texture type is an uncompressed (raw pixel) format.
-	bool basis_transcoder_format_is_uncompressed(transcoder_texture_format tex_type);
-
-	// Returns the # of bytes per pixel for uncompressed formats, or 0 for block texture formats.
-	uint32_t basis_get_uncompressed_bytes_per_pixel(transcoder_texture_format fmt);
-	
-	// Returns the block width for the specified texture format, which is currently either 4 or 8 for FXT1.
-	uint32_t basis_get_block_width(transcoder_texture_format tex_type);
-	
-	// Returns the block height for the specified texture format, which is currently always 4.
-	uint32_t basis_get_block_height(transcoder_texture_format tex_type);
-
-	// Returns true if the specified format was enabled at compile time.
-	bool basis_is_format_supported(transcoder_texture_format tex_type, basis_tex_format fmt = basis_tex_format::cETC1S);
-		
-	class basisu_transcoder;
-
-	// This struct holds all state used during transcoding. For video, it needs to persist between image transcodes (it holds the previous frame).
-	// For threading you can use one state per thread.
-	struct basisu_transcoder_state
-	{
-		struct block_preds
-		{
-			uint16_t m_endpoint_index;
-			uint8_t m_pred_bits;
-		};
-
-		std::vector<block_preds> m_block_endpoint_preds[2];
-		
-		enum { cMaxPrevFrameLevels = 16 };
-		std::vector<uint32_t> m_prev_frame_indices[2][cMaxPrevFrameLevels]; // [alpha_flag][level_index] 
-	};
-	
-	// Low-level helper class that does the actual transcoding.
-	class basisu_lowlevel_etc1s_transcoder
-	{
-		friend class basisu_transcoder;
-	
-	public:
-		basisu_lowlevel_etc1s_transcoder(const basist::etc1_global_selector_codebook *pGlobal_sel_codebook);
-
-		bool decode_palettes(
-			uint32_t num_endpoints, const uint8_t *pEndpoints_data, uint32_t endpoints_data_size,
-			uint32_t num_selectors, const uint8_t *pSelectors_data, uint32_t selectors_data_size);
-
-		bool decode_tables(const uint8_t *pTable_data, uint32_t table_data_size);
-
-		bool transcode_slice(void *pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t *pImage_data, uint32_t image_data_size, block_format fmt, 
-			uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header &header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
-			basisu_transcoder_state *pState = nullptr, bool astc_transcode_alpha = false, void* pAlpha_blocks = nullptr, uint32_t output_rows_in_pixels = 0);
-
-		void clear()
-		{
-			m_endpoints.clear();
-			m_selectors.clear();
-			m_endpoint_pred_model.clear();
-			m_delta_endpoint_model.clear();
-			m_selector_model.clear();
-			m_selector_history_buf_rle_model.clear();
-			m_selector_history_buf_size = 0;
-		}
-
-	private:
-		typedef std::vector<endpoint> endpoint_vec;
-		endpoint_vec m_endpoints;
-
-		typedef std::vector<selector> selector_vec;
-		selector_vec m_selectors;
-
-		const etc1_global_selector_codebook *m_pGlobal_sel_codebook;
-
-		huffman_decoding_table m_endpoint_pred_model, m_delta_endpoint_model, m_selector_model, m_selector_history_buf_rle_model;
-
-		uint32_t m_selector_history_buf_size;
-		
-		basisu_transcoder_state m_def_state;
-	};
-
-	enum
-	{
-		// PVRTC1: decode non-pow2 ETC1S texture level to the next larger power of 2 (not implemented yet, but we're going to support it). Ignored if the slice's dimensions are already a power of 2.
-		cDecodeFlagsPVRTCDecodeToNextPow2 = 2,
-
-		// When decoding to an opaque texture format, if the basis file has alpha, decode the alpha slice instead of the color slice to the output texture format.
-		// This is primarily to allow decoding of textures with alpha to multiple ETC1 textures (one for color, another for alpha).
-		cDecodeFlagsTranscodeAlphaDataToOpaqueFormats = 4,
-
-		// Forbid usage of BC1 3 color blocks (we don't support BC1 punchthrough alpha yet).
-		// This flag is used internally when decoding to BC3.
-		cDecodeFlagsBC1ForbidThreeColorBlocks = 8,
-
-		// The output buffer contains alpha endpoint/selector indices. 
-		// Used internally when decoding formats like ASTC that require both color and alpha data to be available when transcoding to the output format.
-		cDecodeFlagsOutputHasAlphaIndices = 16,
-
-		cDecodeFlagsHighQuality = 32
-	};
-
-	class basisu_lowlevel_uastc_transcoder
-	{
-		friend class basisu_transcoder;
-
-	public:
-		basisu_lowlevel_uastc_transcoder();
-
-		bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
-			uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
-			basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);
-	};
-
-	struct basisu_slice_info
-	{
-		uint32_t m_orig_width;
-		uint32_t m_orig_height;
-
-		uint32_t m_width;
-		uint32_t m_height;
-
-		uint32_t m_num_blocks_x;
-		uint32_t m_num_blocks_y;
-		uint32_t m_total_blocks;
-
-		uint32_t m_compressed_size;
-
-		uint32_t m_slice_index;	// the slice index in the .basis file
-		uint32_t m_image_index;	// the source image index originally provided to the encoder
-		uint32_t m_level_index;	// the mipmap level within this image
-		
-		uint32_t m_unpacked_slice_crc16;
-		
-		bool m_alpha_flag;		// true if the slice has alpha data
-		bool m_iframe_flag;		// true if the slice is an I-Frame
-	};
-
-	typedef std::vector<basisu_slice_info> basisu_slice_info_vec;
-
-	struct basisu_image_info
-	{
-		uint32_t m_image_index;
-		uint32_t m_total_levels;	
-
-		uint32_t m_orig_width;
-		uint32_t m_orig_height;
-
-		uint32_t m_width;
-		uint32_t m_height;
-
-		uint32_t m_num_blocks_x;
-		uint32_t m_num_blocks_y;
-		uint32_t m_total_blocks;
-
-		uint32_t m_first_slice_index;	
-								
-		bool m_alpha_flag;		// true if the image has alpha data
-		bool m_iframe_flag;		// true if the image is an I-Frame
-	};
-
-	struct basisu_image_level_info
-	{
-		uint32_t m_image_index;
-		uint32_t m_level_index;
-
-		uint32_t m_orig_width;
-		uint32_t m_orig_height;
-
-		uint32_t m_width;
-		uint32_t m_height;
-
-		uint32_t m_num_blocks_x;
-		uint32_t m_num_blocks_y;
-		uint32_t m_total_blocks;
-
-		uint32_t m_first_slice_index;	
-								
-		bool m_alpha_flag;		// true if the image has alpha data
-		bool m_iframe_flag;		// true if the image is an I-Frame
-	};
-
-	struct basisu_file_info
-	{
-		uint32_t m_version;
-		uint32_t m_total_header_size;
-
-		uint32_t m_total_selectors;
-		uint32_t m_selector_codebook_size;
-
-		uint32_t m_total_endpoints;
-		uint32_t m_endpoint_codebook_size;
-
-		uint32_t m_tables_size;
-		uint32_t m_slices_size;	
-
-		basis_texture_type m_tex_type;
-		uint32_t m_us_per_frame;
-
-		// Low-level slice information (1 slice per image for color-only basis files, 2 for alpha basis files)
-		basisu_slice_info_vec m_slice_info;
-
-		uint32_t m_total_images;	 // total # of images
-		std::vector<uint32_t> m_image_mipmap_levels; // the # of mipmap levels for each image
-
-		uint32_t m_userdata0;
-		uint32_t m_userdata1;
-
-		basis_tex_format m_tex_format; // ETC1S, UASTC, etc.
-				
-		bool m_y_flipped;				// true if the image was Y flipped
-		bool m_etc1s;					// true if the file is ETC1S
-		bool m_has_alpha_slices;	// true if the texture has alpha slices (for ETC1S: even slices RGB, odd slices alpha)
-	};
-
-	// High-level transcoder class which accepts .basis file data and allows the caller to query information about the file and transcode image levels to various texture formats.
-	// If you're just starting out this is the class you care about.
-	class basisu_transcoder
-	{
-		basisu_transcoder(basisu_transcoder&);
-		basisu_transcoder& operator= (const basisu_transcoder&);
-
-	public:
-		basisu_transcoder(const etc1_global_selector_codebook *pGlobal_sel_codebook);
-
-		// Validates the .basis file. This computes a crc16 over the entire file, so it's slow.
-		bool validate_file_checksums(const void *pData, uint32_t data_size, bool full_validation) const;
-
-		// Quick header validation - no crc16 checks.
-		bool validate_header(const void *pData, uint32_t data_size) const;
-
-		basis_texture_type get_texture_type(const void *pData, uint32_t data_size) const;
-		bool get_userdata(const void *pData, uint32_t data_size, uint32_t &userdata0, uint32_t &userdata1) const;
-		
-		// Returns the total number of images in the basis file (always 1 or more).
-		// Note that the number of mipmap levels for each image may differ, and that images may have different resolutions.
-		uint32_t get_total_images(const void *pData, uint32_t data_size) const;
-
-		basis_tex_format get_tex_format(const void* pData, uint32_t data_size) const;
-
-		// Returns the number of mipmap levels in an image.
-		uint32_t get_total_image_levels(const void *pData, uint32_t data_size, uint32_t image_index) const;
-		
-		// Returns basic information about an image. Note that orig_width/orig_height may not be a multiple of 4.
-		bool get_image_level_desc(const void *pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, uint32_t &orig_width, uint32_t &orig_height, uint32_t &total_blocks) const;
-
-		// Returns information about the specified image.
-		bool get_image_info(const void *pData, uint32_t data_size, basisu_image_info &image_info, uint32_t image_index) const;
-
-		// Returns information about the specified image's mipmap level.
-		bool get_image_level_info(const void *pData, uint32_t data_size, basisu_image_level_info &level_info, uint32_t image_index, uint32_t level_index) const;
-				
-		// Get a description of the basis file and low-level information about each slice.
-		bool get_file_info(const void *pData, uint32_t data_size, basisu_file_info &file_info) const;
-				
-		// start_transcoding() must be called before calling transcode_slice() or transcode_image_level().
-		// For ETC1S files, this call decompresses the selector/endpoint codebooks, so ideally you would only call this once per .basis file (not each image/mipmap level).
-		bool start_transcoding(const void *pData, uint32_t data_size);
-		
-		bool stop_transcoding();
-		
-		// Returns true if start_transcoding() has been called.
-		bool get_ready_to_transcode() const { return m_ready_to_transcode; }
-											
-		// transcode_image_level() decodes a single mipmap level from the .basis file to any of the supported output texture formats.
-		// It'll first find the slice(s) to transcode, then call transcode_slice() one or two times to decode both the color and alpha texture data (or RG texture data from two slices for BC5).
-		// If the .basis file doesn't have alpha slices, the output alpha blocks will be set to fully opaque (all 255's).
-		// Currently, to decode to PVRTC1 the basis texture's dimensions in pixels must be a power of 2, due to PVRTC1 format requirements. 
-		// output_blocks_buf_size_in_blocks_or_pixels should be at least the image level's total_blocks (num_blocks_x * num_blocks_y), or the total number of output pixels if fmt==cTFRGBA32.
-		// output_row_pitch_in_blocks_or_pixels: Number of blocks or pixels per row. If 0, the transcoder uses the slice's num_blocks_x or orig_width (NOT num_blocks_x * 4). Ignored for PVRTC1 (due to texture swizzling).
-		// output_rows_in_pixels: Ignored unless fmt is cRGBA32. The total number of output rows in the output buffer. If 0, the transcoder assumes the slice's orig_height (NOT num_blocks_y * 4).
-		// Notes: 
-		// - basisu_transcoder_init() must have been called first to initialize the transcoder lookup tables before calling this function.
-		// - This method assumes the output texture buffer is readable. In some cases to handle alpha, the transcoder will write temporary data to the output texture in
-		// a first pass, which will be read in a second pass.
-		bool transcode_image_level(
-			const void *pData, uint32_t data_size, 
-			uint32_t image_index, uint32_t level_index, 
-			void *pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
-			transcoder_texture_format fmt,
-			uint32_t decode_flags = 0, uint32_t output_row_pitch_in_blocks_or_pixels = 0, basisu_transcoder_state *pState = nullptr, uint32_t output_rows_in_pixels = 0) const;
-
-		// Finds the basis slice corresponding to the specified image/level/alpha params, or -1 if the slice can't be found.
-		int find_slice(const void *pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, bool alpha_data) const;
-
-		// transcode_slice() decodes a single slice from the .basis file. It's a low-level API - most likely you want to use transcode_image_level().
-		// This is a low-level API, and will be needed to be called multiple times to decode some texture formats (like BC3, BC5, or ETC2).
-		// output_blocks_buf_size_in_blocks_or_pixels is just used for verification to make sure the output buffer is large enough.
-		// output_blocks_buf_size_in_blocks_or_pixels should be at least the image level's total_blocks (num_blocks_x * num_blocks_y), or the total number of output pixels if fmt==cTFRGBA32.
-		// output_block_stride_in_bytes: Number of bytes between each output block.
-		// output_row_pitch_in_blocks_or_pixels: Number of blocks or pixels per row. If 0, the transcoder uses the slice's num_blocks_x or orig_width (NOT num_blocks_x * 4). Ignored for PVRTC1 (due to texture swizzling).
-		// output_rows_in_pixels: Ignored unless fmt is cRGBA32. The total number of output rows in the output buffer. If 0, the transcoder assumes the slice's orig_height (NOT num_blocks_y * 4).
-		// Notes:
-		// - basisu_transcoder_init() must have been called first to initialize the transcoder lookup tables before calling this function.
-		bool transcode_slice(const void *pData, uint32_t data_size, uint32_t slice_index, 
-			void *pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
-			block_format fmt, uint32_t output_block_stride_in_bytes, uint32_t decode_flags = 0, uint32_t output_row_pitch_in_blocks_or_pixels = 0, basisu_transcoder_state * pState = nullptr, void* pAlpha_blocks = nullptr, 
-			uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1) const;
-
-	private:
-		mutable basisu_lowlevel_etc1s_transcoder m_lowlevel_etc1s_decoder;
-		mutable basisu_lowlevel_uastc_transcoder m_lowlevel_uastc_decoder;
-
-		bool m_ready_to_transcode;
-
-		int find_first_slice_index(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index) const;
-		
-		bool validate_header_quick(const void* pData, uint32_t data_size) const;
-	};
-
-	// basisu_transcoder_init() must be called before a .basis file can be transcoded.
-	void basisu_transcoder_init();
-
-	enum debug_flags_t
-	{
-		cDebugFlagVisCRs = 1,
-		cDebugFlagVisBC1Sels = 2,
-		cDebugFlagVisBC1Endpoints = 4
-	};
-	uint32_t get_debug_flags();
-	void set_debug_flags(uint32_t f);
-
-} // namespace basisu
diff --git a/contrib/previewers/win/basisthumbprovider.cpp b/contrib/previewers/win/basisthumbprovider.cpp
index 4a8c6ba..292376e 100644
--- a/contrib/previewers/win/basisthumbprovider.cpp
+++ b/contrib/previewers/win/basisthumbprovider.cpp
@@ -2,7 +2,7 @@
 
 #include <Shlwapi.h>
 
-#include "basisu_transcoder.h"
+#include "basisu_transcoder.cpp"
 
 #include "helpers.h"
 
@@ -10,14 +10,9 @@
 
 using namespace basist;
 
-static etc1_global_selector_codebook* globalCodebook = NULL;
-
 BasisThumbProvider::BasisThumbProvider() : count(1), stream(NULL) {
 	dprintf("BasisThumbProvider ctor");
 	basisu_transcoder_init();
-	if (!globalCodebook) {
-		 globalCodebook = new etc1_global_selector_codebook(g_global_selector_cb_size, g_global_selector_cb);
-	}
 }
 
 BasisThumbProvider::~BasisThumbProvider() {
@@ -66,7 +61,7 @@
 			ULONG size = 0;
 			if (SUCCEEDED(stream->Read(data, static_cast<ULONG>(stat.cbSize.LowPart), &size))) {
 				if (size == stat.cbSize.LowPart) {
-					basisu_transcoder transcoder(globalCodebook);
+					basisu_transcoder transcoder;
 					if (transcoder.validate_header(data, size)) {
 						dprintf("Requested %d bytes for %dx%d image", size, cx, cx);
 						basisu_image_info info;
diff --git a/contrib/previewers/win/previewers.vcxproj b/contrib/previewers/win/previewers.vcxproj
index 2a4b22e..ffeff96 100644
--- a/contrib/previewers/win/previewers.vcxproj
+++ b/contrib/previewers/win/previewers.vcxproj
@@ -165,7 +165,6 @@
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="basisthumbprovider.cpp" />
-    <ClCompile Include="..\lib\basisu_transcoder.cpp" />
     <ClCompile Include="helpers.cpp" />
     <ClCompile Include="previewers.cpp">
       <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</CompileAsManaged>
diff --git a/contrib/previewers/win/previewers.vcxproj.filters b/contrib/previewers/win/previewers.vcxproj.filters
index 9ca8c63..90f7017 100644
--- a/contrib/previewers/win/previewers.vcxproj.filters
+++ b/contrib/previewers/win/previewers.vcxproj.filters
@@ -34,8 +34,5 @@
     <ClCompile Include="helpers.cpp">
       <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="..\lib\basisu_transcoder.cpp">
-      <Filter>src</Filter>
-    </ClCompile>
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/contrib/single_file_transcoder/create_transcoder.sh b/contrib/single_file_transcoder/create_transcoder.sh
index d0dd82d..6016549 100755
--- a/contrib/single_file_transcoder/create_transcoder.sh
+++ b/contrib/single_file_transcoder/create_transcoder.sh
@@ -22,7 +22,7 @@
 if [ $? -ne 0 ]; then
   echo "(Skipping compile test)"
 else
-  cc -std=c++11 -lstdc++ -Wall -Wextra -Werror -Os -g0 -fno-exceptions -fno-rtti -o $OUT_FILE examples/simple.cpp
+  cc -lm -std=c++11 -lstdc++ -Wall -Wextra -Werror -Os -g0 -fno-exceptions -fno-rtti -o $OUT_FILE examples/simple.cpp
   # Did compilation work?
   if [ $? -ne 0 ]; then
     echo "Compiling simple.cpp: FAILED"
diff --git a/contrib/single_file_transcoder/examples/simple.cpp b/contrib/single_file_transcoder/examples/simple.cpp
index 9f61c26..64c8b09 100644
--- a/contrib/single_file_transcoder/examples/simple.cpp
+++ b/contrib/single_file_transcoder/examples/simple.cpp
@@ -31,20 +31,14 @@
 
 //****************************************************************************/
 
-/**
- * Shared codebook instance.
- */
-static etc1_global_selector_codebook* globalCodebook = NULL;
 
 /**
  * Simple single-file test to test the transcoder can build and run.
  */
 int main() {
 	basisu_transcoder_init();
-	if (!globalCodebook) {
-		 globalCodebook = new etc1_global_selector_codebook(g_global_selector_cb_size, g_global_selector_cb);
-	}
-	basisu_transcoder transcoder(globalCodebook);
+	
+	basisu_transcoder transcoder;
 	if (transcoder.validate_header(srcRgb, sizeof srcRgb)) {
 		basisu_file_info fileInfo;
 		if (transcoder.get_file_info(srcRgb, sizeof srcRgb, fileInfo)) {
diff --git a/encoder/apg_bmp.c b/encoder/apg_bmp.c
deleted file mode 100644
index d342b20..0000000
--- a/encoder/apg_bmp.c
+++ /dev/null
@@ -1,541 +0,0 @@
-/*
-BMP File Reader/Writer Implementation
-Anton Gerdelan
-Version: 3
-Licence: see apg_bmp.h
-C99
-*/
-
-#ifdef _MSC_VER
-#define _CRT_SECURE_NO_WARNINGS 1
-#endif
-
-#include "apg_bmp.h"
-#include <assert.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-/* Maximum pixel dimensions of width or height of an image. Should accommodate max used in graphics APIs.
-   NOTE: 65536*65536 is the biggest number storable in 32 bits.
-   This needs to be multiplied by n_channels so actual memory indices are not uint32 but size_t to avoid overflow.
-   Note this will crash stb_image_write et al at maximum size which use 32bits, so reduce max size to accom. */
-#define _BMP_MAX_DIMS 65536
-#define _BMP_FILE_HDR_SZ 14
-#define _BMP_MIN_DIB_HDR_SZ 40
-#define _BMP_MIN_HDR_SZ ( _BMP_FILE_HDR_SZ + _BMP_MIN_DIB_HDR_SZ )
-#define _BMP_MAX_IMAGE_FILE_SIZE (1024ULL*1024ULL*1024ULL)
-
-#pragma pack( push, 1 ) // supported on GCC in addition to individual packing attribs
-/* All BMP files, regardless of type, start with this file header */
-typedef struct _bmp_file_header_t {
-  char file_type[2];
-  uint32_t file_sz;
-  uint16_t reserved1;
-  uint16_t reserved2;
-  uint32_t image_data_offset;
-} _bmp_file_header_t;
-
-/* Following the file header is the BMP type header. this is the most commonly used format */
-typedef struct _bmp_dib_BITMAPINFOHEADER_t {
-  uint32_t this_header_sz;
-  int32_t w;                      // in older headers w & h these are shorts and may be unsigned
-  int32_t h;                      //
-  uint16_t n_planes;              // must be 1
-  uint16_t bpp;                   // bits per pixel. 1,4,8,16,24,32.
-  uint32_t compression_method;    // 16 and 32-bit images must have a value of 3 here
-  uint32_t image_uncompressed_sz; // not consistently used in the wild, so ignored here.
-  int32_t horiz_pixels_per_meter; // not used.
-  int32_t vert_pixels_per_meter;  // not used.
-  uint32_t n_colours_in_palette;  //
-  uint32_t n_important_colours;   // not used.
-  /* NOTE(Anton) a DIB header may end here at 40-bytes. be careful using sizeof() */
-  /* if 'compression' value, above, is set to 3 ie the image is 16 or 32-bit, then these colour channel masks follow the headers.
-  these are big-endian order bit masks to assign bits of each pixel to different colours. bits used must be contiguous and not overlap. */
-  uint32_t bitmask_r;
-  uint32_t bitmask_g;
-  uint32_t bitmask_b;
-} _bmp_dib_BITMAPINFOHEADER_t;
-#pragma pack( pop )
-
-typedef enum _bmp_compression_t {
-  BI_RGB            = 0,
-  BI_RLE8           = 1,
-  BI_RLE4           = 2,
-  BI_BITFIELDS      = 3,
-  BI_JPEG           = 4,
-  BI_PNG            = 5,
-  BI_ALPHABITFIELDS = 6,
-  BI_CMYK           = 11,
-  BI_CMYKRLE8       = 12,
-  BI_CMYRLE4        = 13
-} _bmp_compression_t;
-
-/* convenience struct and file->memory function */
-typedef struct _entire_file_t {
-  void* data;
-  size_t sz;
-} _entire_file_t;
-
-/*
-RETURNS
-- true on success. record->data is allocated memory and must be freed by the caller.
-- false on any error. Any allocated memory is freed if false is returned */
-static bool _read_entire_file( const char* filename, _entire_file_t* record ) {
-  FILE* fp = fopen( filename, "rb" );
-  if ( !fp ) { return false; }
-  fseek( fp, 0L, SEEK_END );
-  record->sz   = (size_t)ftell( fp );
-
-  // Immediately bail on anything larger than _BMP_MAX_IMAGE_FILE_SIZE. 
-  if (record->sz > _BMP_MAX_IMAGE_FILE_SIZE) {
-    fclose( fp );
-    return false;
-  }
-
-  record->data = malloc( record->sz );
-  if ( !record->data ) {
-    fclose( fp );
-    return false;
-  }
-  rewind( fp );
-  size_t nr = fread( record->data, record->sz, 1, fp );
-  fclose( fp );
-  if ( 1 != nr ) { return false; }
-  return true;
-}
-
-static bool _validate_file_hdr( _bmp_file_header_t* file_hdr_ptr, size_t file_sz ) {
-  if ( !file_hdr_ptr ) { return false; }
-  if ( file_hdr_ptr->file_type[0] != 'B' || file_hdr_ptr->file_type[1] != 'M' ) { return false; }
-  if ( file_hdr_ptr->image_data_offset > file_sz ) { return false; }
-  return true;
-}
-
-static bool _validate_dib_hdr( _bmp_dib_BITMAPINFOHEADER_t* dib_hdr_ptr, size_t file_sz ) {
-  if ( !dib_hdr_ptr ) { return false; }
-  if ( _BMP_FILE_HDR_SZ + dib_hdr_ptr->this_header_sz > file_sz ) { return false; }
-  if ( ( 32 == dib_hdr_ptr->bpp || 16 == dib_hdr_ptr->bpp ) && ( BI_BITFIELDS != dib_hdr_ptr->compression_method && BI_ALPHABITFIELDS != dib_hdr_ptr->compression_method ) ) {
-    return false;
-  }
-  if ( BI_RGB != dib_hdr_ptr->compression_method && BI_BITFIELDS != dib_hdr_ptr->compression_method && BI_ALPHABITFIELDS != dib_hdr_ptr->compression_method ) {
-    return false;
-  }
-  // NOTE(Anton) using abs() in the if-statement was blowing up on large negative numbers. switched to labs()
-  if ( 0 == dib_hdr_ptr->w || 0 == dib_hdr_ptr->h || labs( dib_hdr_ptr->w ) > _BMP_MAX_DIMS || labs( dib_hdr_ptr->h ) > _BMP_MAX_DIMS ) { return false; }
-
-  /* NOTE(Anton) if images reliably used n_colours_in_palette we could have done a palette/file size integrity check here.
-  because some always set 0 then we have to check every palette indexing as we read them */
-  return true;
-}
-
-/* NOTE(Anton) this could have ifdef branches on different compilers for the intrinsics versions for perf */
-static uint32_t _bitscan( uint32_t dword ) {
-  for ( uint32_t i = 0; i < 32; i++ ) {
-    if ( 1 & dword ) { return i; }
-    dword = dword >> 1;
-  }
-  return (uint32_t)-1;
-}
-
-unsigned char* apg_bmp_read( const char* filename, int* w, int* h, unsigned int* n_chans ) {
-  if ( !filename || !w || !h || !n_chans ) { return NULL; }
-
-  // read in the whole file into memory first - much faster than parsing on-the-fly
-  _entire_file_t record;
-  if ( !_read_entire_file( filename, &record ) ) { return NULL; }
-  if ( record.sz < _BMP_MIN_HDR_SZ ) {
-    free( record.data );
-    return NULL;
-  }
-
-  // grab and validate the first, file, header
-  _bmp_file_header_t* file_hdr_ptr = (_bmp_file_header_t*)record.data;
-  if ( !_validate_file_hdr( file_hdr_ptr, record.sz ) ) {
-    free( record.data );
-    return NULL;
-  }
-
-  // grad and validate the second, DIB, header
-  _bmp_dib_BITMAPINFOHEADER_t* dib_hdr_ptr = (_bmp_dib_BITMAPINFOHEADER_t*)( (uint8_t*)record.data + _BMP_FILE_HDR_SZ );
-  if ( !_validate_dib_hdr( dib_hdr_ptr, record.sz ) ) {
-    free( record.data );
-    return NULL;
-  }
-
-  // bitmaps can have negative dims to indicate the image should be flipped
-  uint32_t width = *w = abs( dib_hdr_ptr->w );
-  uint32_t height = *h = abs( dib_hdr_ptr->h );
-
-  // TODO(Anton) flip image memory at the end if this is true. because doing it per row was making me write bugs.
-  // bool vertically_flip = dib_hdr_ptr->h > 0 ? false : true;
-
-  // channel count and palette are not well defined in the header so we make a good guess here
-  uint32_t n_dst_chans = 3, n_src_chans = 3;
-  bool has_palette = false;
-  switch ( dib_hdr_ptr->bpp ) {
-  case 32: n_dst_chans = n_src_chans = 4; break; // technically can be RGB but not supported
-  case 24: n_dst_chans = n_src_chans = 3; break; // technically can be RGBA but not supported
-  case 8:                                        // seems to always use a BGR0 palette, even for greyscale
-    n_dst_chans = 3;
-    has_palette = true;
-    n_src_chans = 1;
-    break;
-  case 4: // always has a palette - needed for a MS-saved BMP
-    n_dst_chans = 3;
-    has_palette = true;
-    n_src_chans = 1;
-    break;
-  case 1: // 1-bpp means the palette has 3 colour channels with 2 colours i.e. monochrome but not always black & white
-    n_dst_chans = 3;
-    has_palette = true;
-    n_src_chans = 1;
-    break;
-  default: // this includes 2bpp and 16bpp
-    free( record.data );
-    return NULL;
-  } // endswitch
-  *n_chans = n_dst_chans;
-  // NOTE(Anton) some image formats are not allowed a palette - could check for a bad header spec here also
-  if ( dib_hdr_ptr->n_colours_in_palette > 0 ) { has_palette = true; }
-
-#ifdef APG_BMP_DEBUG_OUTPUT
-  printf( "apg_bmp_debug: reading image\n|-filename `%s`\n|-dims %ux%u pixels\n|-bpp %u\n|-n_src_chans %u\n|-n_dst_chans %u\n", filename, *w, *h,
-    dib_hdr_ptr->bpp, n_src_chans, n_dst_chans );
-#endif
-
-  uint32_t palette_offset = _BMP_FILE_HDR_SZ + dib_hdr_ptr->this_header_sz;
-  bool has_bitmasks       = false;
-  if ( BI_BITFIELDS == dib_hdr_ptr->compression_method || BI_ALPHABITFIELDS == dib_hdr_ptr->compression_method ) {
-    has_bitmasks = true;
-    palette_offset += 12;
-  }
-  if ( palette_offset > record.sz ) {
-    free( record.data );
-    return NULL;
-  }
-
-  // work out if any padding how much to skip at end of each row
-  uint32_t unpadded_row_sz = width * n_src_chans;
-  // bit-encoded palette indices have different padding properties
-  if ( 4 == dib_hdr_ptr->bpp ) {
-    unpadded_row_sz = width % 2 > 0 ? width / 2 + 1 : width / 2; // find how many whole bytes required for this bit width
-  }
-  if ( 1 == dib_hdr_ptr->bpp ) {
-    unpadded_row_sz = width % 8 > 0 ? width / 8 + 1 : width / 8; // find how many whole bytes required for this bit width
-  }
-  uint32_t row_padding_sz = 0 == unpadded_row_sz % 4 ? 0 : 4 - ( unpadded_row_sz % 4 ); // NOTE(Anton) didn't expect operator precedence of - over %
-
-  // another file size integrity check: partially validate source image data size
-  // 'image_data_offset' is by row padded to 4 bytes and is either colour data or palette indices.
-  if ( file_hdr_ptr->image_data_offset + ( unpadded_row_sz + row_padding_sz ) * height > record.sz ) {
-    free( record.data );
-    return NULL;
-  }
-
-  // find which bit number each colour channel starts at, so we can separate colours out
-  uint32_t bitshift_rgba[4] = {0, 0, 0, 0}; // NOTE(Anton) noticed this was int and not uint32_t so changed it. 17 Mar 2020
-  uint32_t bitmask_a        = 0;
-  if ( has_bitmasks ) {
-    bitmask_a        = ~( dib_hdr_ptr->bitmask_r | dib_hdr_ptr->bitmask_g | dib_hdr_ptr->bitmask_b );
-    bitshift_rgba[0] = _bitscan( dib_hdr_ptr->bitmask_r );
-    bitshift_rgba[1] = _bitscan( dib_hdr_ptr->bitmask_g );
-    bitshift_rgba[2] = _bitscan( dib_hdr_ptr->bitmask_b );
-    bitshift_rgba[3] = _bitscan( bitmask_a );
-  }
-
-  // allocate memory for the output pixels block. cast to size_t in case width and height are both the max of 65536 and n_dst_chans > 1
-  unsigned char* dst_img_ptr = (unsigned char*)malloc( (size_t)width * (size_t)height * (size_t)n_dst_chans );
-  if ( !dst_img_ptr ) {
-    free( record.data );
-    return NULL;
-  }
-
-  uint8_t* palette_data_ptr = (uint8_t*)record.data + palette_offset;
-  uint8_t* src_img_ptr      = (uint8_t*)record.data + file_hdr_ptr->image_data_offset;
-  size_t dst_stride_sz      = width * n_dst_chans;
-
-  //   == 32-bpp -> 32-bit RGBA. == 32-bit and 16-bit require bitmasks
-  if ( 32 == dib_hdr_ptr->bpp ) {
-    // check source image has enough data in it to read from
-    if ( (size_t)file_hdr_ptr->image_data_offset + (size_t)height * (size_t)width * (size_t)n_src_chans > record.sz ) {
-      free( record.data );
-      free( dst_img_ptr );
-      return NULL;
-    }
-    size_t src_byte_idx = 0;
-    for ( uint32_t r = 0; r < height; r++ ) {
-      size_t dst_pixels_idx = r * dst_stride_sz;
-      for ( uint32_t c = 0; c < width; c++ ) {
-        uint32_t pixel;
-        memcpy( &pixel, &src_img_ptr[src_byte_idx], 4 );
-        // NOTE(Anton) the below assumes 32-bits is always RGBA 1 byte per channel. 10,10,10 RGB exists though and isn't handled.
-        dst_img_ptr[dst_pixels_idx++] = ( uint8_t )( ( pixel & dib_hdr_ptr->bitmask_r ) >> bitshift_rgba[0] );
-        dst_img_ptr[dst_pixels_idx++] = ( uint8_t )( ( pixel & dib_hdr_ptr->bitmask_g ) >> bitshift_rgba[1] );
-        dst_img_ptr[dst_pixels_idx++] = ( uint8_t )( ( pixel & dib_hdr_ptr->bitmask_b ) >> bitshift_rgba[2] );
-        dst_img_ptr[dst_pixels_idx++] = ( uint8_t )( ( pixel & bitmask_a ) >> bitshift_rgba[3] );
-        src_byte_idx += 4;
-      }
-      src_byte_idx += row_padding_sz;
-    }
-
-    // == 8-bpp -> 24-bit RGB ==
-  } else if ( 8 == dib_hdr_ptr->bpp && has_palette ) {
-    // validate indices (body of image data) fits in file
-    if ( file_hdr_ptr->image_data_offset + height * width > record.sz ) {
-      free( record.data );
-      free( dst_img_ptr );
-      return NULL;
-    }
-    size_t src_byte_idx = 0;
-    for ( uint32_t r = 0; r < height; r++ ) {
-      size_t dst_pixels_idx = ( height - 1 - r ) * dst_stride_sz;
-      for ( uint32_t c = 0; c < width; c++ ) {
-        // "most palettes are 4 bytes in RGB0 order but 3 for..." - it was actually BRG0 in old images -- Anton
-        uint8_t index = src_img_ptr[src_byte_idx]; // 8-bit index value per pixel
-
-        if ( palette_offset + index * 4 + 2 >= record.sz ) {
-          free( record.data );
-          return dst_img_ptr;
-        }
-        dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[index * 4 + 2];
-        dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[index * 4 + 1];
-        dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[index * 4 + 0];
-        src_byte_idx++;
-      }
-      src_byte_idx += row_padding_sz;
-    }
-
-    // == 4-bpp (16-colour) -> 24-bit RGB ==
-  } else if ( 4 == dib_hdr_ptr->bpp && has_palette ) {
-    size_t src_byte_idx = 0;
-    for ( uint32_t r = 0; r < height; r++ ) {
-      size_t dst_pixels_idx = ( height - 1 - r ) * dst_stride_sz;
-      for ( uint32_t c = 0; c < width; c++ ) {
-        if ( file_hdr_ptr->image_data_offset + src_byte_idx > record.sz ) {
-          free( record.data );
-          free( dst_img_ptr );
-          return NULL;
-        }
-        // handle 2 pixels at a time
-        uint8_t pixel_duo = src_img_ptr[src_byte_idx];
-        uint8_t a_index   = ( 0xFF & pixel_duo ) >> 4;
-        uint8_t b_index   = 0xF & pixel_duo;
-
-        if ( palette_offset + a_index * 4 + 2 >= record.sz ) { // invalid src image
-          free( record.data );
-          return dst_img_ptr;
-        }
-        if ( dst_pixels_idx + 3 > width * height * n_dst_chans ) { // done
-          free( record.data );
-          return dst_img_ptr;
-        }
-        dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[a_index * 4 + 2];
-        dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[a_index * 4 + 1];
-        dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[a_index * 4 + 0];
-        if ( ++c >= width ) { // advance a column
-          c = 0;
-          r++;
-          if ( r >= height ) { // done. no need to get second pixel. eg a 1x1 pixel image.
-            free( record.data );
-            return dst_img_ptr;
-          }
-          dst_pixels_idx = ( height - 1 - r ) * dst_stride_sz;
-        }
-
-        if ( palette_offset + b_index * 4 + 2 >= record.sz ) { // invalid src image
-          free( record.data );
-          return dst_img_ptr;
-        }
-        if ( dst_pixels_idx + 3 > width * height * n_dst_chans ) { // done. probably redundant check since checking r >= height.
-          free( record.data );
-          return dst_img_ptr;
-        }
-        dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[b_index * 4 + 2];
-        dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[b_index * 4 + 1];
-        dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[b_index * 4 + 0];
-        src_byte_idx++;
-      }
-      src_byte_idx += row_padding_sz;
-    }
-
-    // == 1-bpp -> 24-bit RGB ==
-  } else if ( 1 == dib_hdr_ptr->bpp && has_palette ) {
-    /* encoding method for monochrome is not well documented.
-    a 2x2 pixel image is stored as 4 1-bit palette indexes
-    the palette is stored as any 2 RGB0 colours (not necessarily B&W)
-    so for an image with indexes like so:
-    1 1
-    0 1
-    it is bit-encoded as follows, starting at MSB:
-    01000000 00000000 00000000 00000000 (first byte val  64)
-    11000000 00000000 00000000 00000000 (first byte val 192)
-    data is still split by row and each row padded to 4 byte multiples
-     */
-    size_t src_byte_idx = 0;
-    for ( uint32_t r = 0; r < height; r++ ) {
-      uint8_t bit_idx       = 0; // used in monochrome
-      size_t dst_pixels_idx = ( height - 1 - r ) * dst_stride_sz;
-      for ( uint32_t c = 0; c < width; c++ ) {
-        if ( 8 == bit_idx ) { // start reading from the next byte
-          src_byte_idx++;
-          bit_idx = 0;
-        }
-        if ( file_hdr_ptr->image_data_offset + src_byte_idx > record.sz ) {
-          free( record.data );
-          return dst_img_ptr;
-        }
-        uint8_t pixel_oct   = src_img_ptr[src_byte_idx];
-        uint8_t bit         = 128 >> bit_idx;
-        uint8_t masked      = pixel_oct & bit;
-        uint8_t palette_idx = masked > 0 ? 1 : 0;
-
-        if ( palette_offset + palette_idx * 4 + 2 >= record.sz ) {
-          free( record.data );
-          return dst_img_ptr;
-        }
-        dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[palette_idx * 4 + 2];
-        dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[palette_idx * 4 + 1];
-        dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[palette_idx * 4 + 0];
-        bit_idx++;
-      }
-      src_byte_idx += ( row_padding_sz + 1 ); // 1bpp is special here
-    }
-
-    // == 24-bpp -> 24-bit RGB == (but also should handle some other n_chans cases)
-  } else {
-    // NOTE(Anton) this only supports 1 byte per channel
-    if ( file_hdr_ptr->image_data_offset + height * width * n_dst_chans > record.sz ) {
-      free( record.data );
-      free( dst_img_ptr );
-      return NULL;
-    }
-    size_t src_byte_idx = 0;
-    for ( uint32_t r = 0; r < height; r++ ) {
-      size_t dst_pixels_idx = ( height - 1 - r ) * dst_stride_sz;
-      for ( uint32_t c = 0; c < width; c++ ) {
-        // re-orders from BGR to RGB
-        if ( n_dst_chans > 3 ) { dst_img_ptr[dst_pixels_idx++] = src_img_ptr[src_byte_idx + 3]; }
-        if ( n_dst_chans > 2 ) { dst_img_ptr[dst_pixels_idx++] = src_img_ptr[src_byte_idx + 2]; }
-        if ( n_dst_chans > 1 ) { dst_img_ptr[dst_pixels_idx++] = src_img_ptr[src_byte_idx + 1]; }
-        dst_img_ptr[dst_pixels_idx++] = src_img_ptr[src_byte_idx];
-        src_byte_idx += n_src_chans;
-      }
-      src_byte_idx += row_padding_sz;
-    }
-  } // endif bpp
-
-  free( record.data );
-  return dst_img_ptr;
-}
-
-void apg_bmp_free( unsigned char* pixels_ptr ) {
-  if ( !pixels_ptr ) { return; }
-  free( pixels_ptr );
-}
-
-unsigned int apg_bmp_write( const char* filename, unsigned char* pixels_ptr, int w, int h, unsigned int n_chans ) {
-  if ( !filename || !pixels_ptr ) { return 0; }
-  if ( 0 == w || 0 == h ) { return 0; }
-  if ( labs( w ) > _BMP_MAX_DIMS || labs( h ) > _BMP_MAX_DIMS ) { return 0; }
-  if ( n_chans != 3 && n_chans != 4 ) { return 0; }
-
-  uint32_t height = (uint32_t)labs( h );
-  uint32_t width  = (uint32_t)labs( w );
-  // work out if any padding how much to skip at end of each row
-  const size_t unpadded_row_sz      = width * n_chans;
-  const size_t row_padding_sz       = 0 == unpadded_row_sz % 4 ? 0 : 4 - unpadded_row_sz % 4;
-  const size_t row_sz               = unpadded_row_sz + row_padding_sz;
-  const size_t dst_pixels_padded_sz = row_sz * height;
-
-  const size_t dib_hdr_sz = sizeof( _bmp_dib_BITMAPINFOHEADER_t );
-  _bmp_file_header_t file_hdr;
-  {
-    file_hdr.file_type[0]      = 'B';
-    file_hdr.file_type[1]      = 'M';
-    file_hdr.file_sz           = _BMP_FILE_HDR_SZ + (uint32_t)dib_hdr_sz + (uint32_t)dst_pixels_padded_sz;
-    file_hdr.reserved1         = 0;
-    file_hdr.reserved2         = 0;
-    file_hdr.image_data_offset = _BMP_FILE_HDR_SZ + (uint32_t)dib_hdr_sz;
-  }
-  _bmp_dib_BITMAPINFOHEADER_t dib_hdr;
-  {
-    dib_hdr.this_header_sz         = _BMP_MIN_DIB_HDR_SZ; // NOTE: must be 40 and not include the bitmask memory in size here
-    dib_hdr.w                      = w;
-    dib_hdr.h                      = h;
-    dib_hdr.n_planes               = 1;
-    dib_hdr.bpp                    = 3 == n_chans ? 24 : 32;
-    dib_hdr.compression_method     = 3 == n_chans ? BI_RGB : BI_BITFIELDS;
-    dib_hdr.image_uncompressed_sz  = 0;
-    dib_hdr.horiz_pixels_per_meter = 0;
-    dib_hdr.vert_pixels_per_meter  = 0;
-    dib_hdr.n_colours_in_palette   = 0;
-    dib_hdr.n_important_colours    = 0;
-    // big-endian masks. only used in BI_BITFIELDS and BI_ALPHABITFIELDS ( 16 and 32-bit images )
-    // important note: GIMP stores BMP data in this array order for 32-bit: [A][B][G][R]
-    dib_hdr.bitmask_r = 0xFF000000;
-    dib_hdr.bitmask_g = 0x00FF0000;
-    dib_hdr.bitmask_b = 0x0000FF00;
-  }
-
-  uint8_t* dst_pixels_ptr = (uint8_t*)malloc( dst_pixels_padded_sz );
-  if ( !dst_pixels_ptr ) { return 0; }
-  {
-    size_t dst_byte_idx = 0;
-    uint8_t padding[4]  = {0, 0, 0, 0};
-    uint8_t rgba[4]     = {0, 0, 0, 0};
-    uint8_t bgra[4]     = {0, 0, 0, 0};
-
-    for ( uint32_t row = 0; row < height; row++ ) {
-      size_t src_byte_idx = ( height - 1 - row ) * n_chans * width;
-      for ( uint32_t col = 0; col < width; col++ ) {
-        for ( uint32_t chan = 0; chan < n_chans; chan++ ) { rgba[chan] = pixels_ptr[src_byte_idx++]; }
-        if ( 3 == n_chans ) {
-          bgra[0] = rgba[2];
-          bgra[1] = rgba[1];
-          bgra[2] = rgba[0];
-        } else {
-          /* NOTE(Anton) RGBA with alpha channel would be better supported with an extended DIB header */
-          bgra[0] = rgba[3];
-          bgra[1] = rgba[2];
-          bgra[2] = rgba[1];
-          bgra[3] = rgba[0]; // alpha
-        }
-        memcpy( &dst_pixels_ptr[dst_byte_idx], bgra, n_chans );
-        dst_byte_idx += (size_t)n_chans;
-      } // endfor col
-      if ( row_padding_sz > 0 ) {
-        memcpy( &dst_pixels_ptr[dst_byte_idx], padding, row_padding_sz );
-        dst_byte_idx += row_padding_sz;
-      }
-    } // endfor row
-  }
-  {
-    FILE* fp = fopen( filename, "wb" );
-    if ( !fp ) {
-      free( dst_pixels_ptr );
-      return 0;
-    }
-    if ( 1 != fwrite( &file_hdr, _BMP_FILE_HDR_SZ, 1, fp ) ) {
-      free( dst_pixels_ptr );
-      fclose( fp );
-      return 0;
-    }
-    if ( 1 != fwrite( &dib_hdr, dib_hdr_sz, 1, fp ) ) {
-      free( dst_pixels_ptr );
-      fclose( fp );
-      return 0;
-    }
-    if ( 1 != fwrite( dst_pixels_ptr, dst_pixels_padded_sz, 1, fp ) ) {
-      free( dst_pixels_ptr );
-      fclose( fp );
-      return 0;
-    }
-    fclose( fp );
-  }
-  free( dst_pixels_ptr );
-
-  return 1;
-}
diff --git a/encoder/apg_bmp.h b/encoder/apg_bmp.h
deleted file mode 100644
index 8cd73b6..0000000
--- a/encoder/apg_bmp.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
-BMP File Reader/Writer Implementation
-Anton Gerdelan
-Version: 3.1 18 March 2020.
-Licence: see bottom of file.
-C89 ( Implementation is C99 )
-
-Contributors:
-- Anton Gerdelan - Initial code.
-- Saija Sorsa    - Fuzz testing.
-
-Instructions:
-- Just drop this header, and the matching .c file into your project.
-- To get debug printouts during parsing define APG_BMP_DEBUG_OUTPUT.
-
-Advantages:
-- The implementation is fast, simple, and supports more formats than most BMP reader libraries.
-- The reader function is fuzzed with AFL https://lcamtuf.coredump.cx/afl/.
-- The reader is robust to large files and malformed files, and will return any valid partial data in an image.
-- Reader supports 32bpp (with alpha channel), 24bpp, 8bpp, 4bpp, and 1bpp monochrome BMP images.
-- Reader handles indexed BMP images using a colour palette.
-- Writer supports 32bpp RGBA and 24bpp uncompressed RGB images.
-
-Current Limitations:
-- 16-bit images not supported (don't have any samples to test on).
-- No support for interleaved channel bit layouts eg RGB101010 RGB555 RGB565.
-- No support for compressed BMP images, although in practice these are not used.
-- Output images with alpha channel are written in BITMAPINFOHEADER format.
-  For better alpha support in other apps the 124-bit v5 header could be used instead,
-	at the cost of some backward compatibility and bloat.
-
-To Do:
-- FUZZING
-  - create a unique fuzz test set for (8,4,1 BPP).
-- (maybe) FEATURE Flipping the image based on negative width and height in header, and/or function arguments. 
-- (maybe) PERF ifdef intrinsics/asm for bitscan. Platform-specific code so won't include unless necessary.
-- (maybe) FEATURE Add parameter for padding output memory to eg 4-byte alignment or n channels.
-- (maybe) FEATURE Improved apps support in alpha channel writing (using v5 header).
-*/
-
-#ifndef APG_BMP_H_
-#define APG_BMP_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* CPP */
-
-/* Reads a bitmap from a file, allocates memory for the raw image data, and returns it.
-PARAMS
-  * w,h,     - Retrieves the width and height of the BMP in pixels.
-  * n_chans  - Retrieves the number of channels in the BMP.
-RETURNS
-  * Tightly-packed pixel memory in RGBA order. The caller must call free() on the memory.
-  * NULL on any error. Any allocated memory is freed before returning NULL. */
-unsigned char* apg_bmp_read( const char* filename, int* w, int* h, unsigned int* n_chans );
-
-/* Calls free() on memory created by apg_bmp_read */
-void apg_bmp_free( unsigned char* pixels_ptr );
-
-/* Writes a bitmap to a file.
-PARAMS
-  * filename   - e.g."my_bitmap.bmp". Must not be NULL.
-  * pixels_ptr - Pointer to tightly-packed pixel memory in RGBA order. Must not be NULL. There must be abs(w)*abs(h)*n_chans bytes in the memory pointed to.
-  * w,h,       - Width and height of the image in pixels.
-  * n_chans    - The number of channels in the BMP. 3 or 4 supported for writing, which means RGB or RGBA memory, respectively.
-RETURNS
-  * Zero on any error, non zero on success. */
-unsigned int apg_bmp_write( const char* filename, unsigned char* pixels_ptr, int w, int h, unsigned int n_chans );
-
-#ifdef __cplusplus
-}
-#endif /* CPP */
-
-#endif /*_APG_BMP_H_ */
-
-/*
--------------------------------------------------------------------------------------
-This software is available under two licences - you may use it under either licence.
--------------------------------------------------------------------------------------
-FIRST LICENCE OPTION
-
->                                  Apache License
->                            Version 2.0, January 2004
->                         http://www.apache.org/licenses/
->    Copyright 2019 Anton Gerdelan.
->    Licensed under the Apache License, Version 2.0 (the "License");
->    you may not use this file except in compliance with the License.
->    You may obtain a copy of the License at
->        http://www.apache.org/licenses/LICENSE-2.0
->    Unless required by applicable law or agreed to in writing, software
->    distributed under the License is distributed on an "AS IS" BASIS,
->    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
->    See the License for the specific language governing permissions and
->    limitations under the License.
--------------------------------------------------------------------------------------
-SECOND LICENCE OPTION
-
-> This is free and unencumbered software released into the public domain.
->
-> Anyone is free to copy, modify, publish, use, compile, sell, or
-> distribute this software, either in source code form or as a compiled
-> binary, for any purpose, commercial or non-commercial, and by any
-> means.
->
-> In jurisdictions that recognize copyright laws, the author or authors
-> of this software dedicate any and all copyright interest in the
-> software to the public domain. We make this dedication for the benefit
-> of the public at large and to the detriment of our heirs and
-> successors. We intend this dedication to be an overt act of
-> relinquishment in perpetuity of all present and future rights to this
-> software under copyright law.
->
-> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-> IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-> OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-> ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-> OTHER DEALINGS IN THE SOFTWARE.
->
-> For more information, please refer to <http://unlicense.org>
--------------------------------------------------------------------------------------
-*/
diff --git a/encoder/basisu_astc_decomp.cpp b/encoder/basisu_astc_decomp.cpp
deleted file mode 100644
index 53bccfc..0000000
--- a/encoder/basisu_astc_decomp.cpp
+++ /dev/null
@@ -1,1561 +0,0 @@
-// basisu_astc_decomp.cpp: Only used for ASTC decompression, to validate the transcoder's output.
-// This version does not support HDR.
-
-/*-------------------------------------------------------------------------
- * drawElements Quality Program Tester Core
- * ----------------------------------------
- *
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * rg: Removed external dependencies, remarked out HDR support because
- * we don't need it, minor fix to decompress() so it converts non-sRGB
- * output to 8-bits correctly. I've compared this decoder's output
- * vs. astc-codec with random inputs on 4x4 blocks, and after fixing a few obvious
- * bugs in astc-codec where it didn't correctly follow the spec they match so 
- * I'm assuming they are both correct for 4x4 now.
- * HDR support should be easily added back in, but as we don't need it 
- * I'm leaving this for someone else.
- * 
- *//*!
- * \file
- * \brief ASTC Utilities.
- *//*--------------------------------------------------------------------*/
-#include "basisu_astc_decomp.h"
-#include <assert.h>
-#include <algorithm>
-
-#define DE_LENGTH_OF_ARRAY(x) (sizeof(x)/sizeof(x[0]))
-#define DE_UNREF(x) (void)x
-
-typedef uint8_t deUint8;
-typedef int8_t deInt8;
-typedef uint32_t deUint32;
-typedef int32_t deInt32;
-typedef uint16_t deUint16;
-typedef int16_t deInt16;
-typedef int64_t deInt64;
-typedef uint64_t deUint64;
-
-#define DE_ASSERT assert
-
-#ifdef _MSC_VER
-#pragma warning (disable:4505) // unreferenced local function has been removed
-#elif defined(__GNUC__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-function"
-#endif
-
-namespace basisu_astc
-{
-	static bool inBounds(int v, int l, int h)
-	{
-		return (v >= l) && (v < h);
-	}
-
-	static bool inRange(int v, int l, int h)
-	{
-		return (v >= l) && (v <= h);
-	}
-
-	template<typename T>
-	static inline T max(T a, T b)
-	{
-		return (a > b) ? a : b;
-	}
-
-	template<typename T>
-	static inline T min(T a, T b)
-	{
-		return (a < b) ? a : b;
-	}
-
-	template<typename T>
-	static inline T clamp(T a, T l, T h)
-	{
-		if (a < l)
-			return l;
-		else if (a > h)
-			return h;
-		return a;
-	}
-
-	struct UVec4
-	{
-		uint32_t m_c[4];
-
-		UVec4()
-		{
-			m_c[0] = 0;
-			m_c[1] = 0;
-			m_c[2] = 0;
-			m_c[3] = 0;
-		}
-
-		UVec4(uint32_t x, uint32_t y, uint32_t z, uint32_t w)
-		{
-			m_c[0] = x;
-			m_c[1] = y;
-			m_c[2] = z;
-			m_c[3] = w;
-		}
-
-		uint32_t x() const { return m_c[0]; }
-		uint32_t y() const { return m_c[1]; }
-		uint32_t z() const { return m_c[2]; }
-		uint32_t w() const { return m_c[3]; }
-
-		uint32_t& x() { return m_c[0]; }
-		uint32_t& y() { return m_c[1]; }
-		uint32_t& z() { return m_c[2]; }
-		uint32_t& w() { return m_c[3]; }
-
-		uint32_t operator[] (uint32_t idx) const { assert(idx < 4);  return m_c[idx]; }
-		uint32_t& operator[] (uint32_t idx) { assert(idx < 4);  return m_c[idx]; }
-	};
-
-	struct IVec4
-	{
-		int32_t m_c[4];
-
-		IVec4()
-		{
-			m_c[0] = 0;
-			m_c[1] = 0;
-			m_c[2] = 0;
-			m_c[3] = 0;
-		}
-
-		IVec4(int32_t x, int32_t y, int32_t z, int32_t w)
-		{
-			m_c[0] = x;
-			m_c[1] = y;
-			m_c[2] = z;
-			m_c[3] = w;
-		}
-
-		int32_t x() const { return m_c[0]; }
-		int32_t y() const { return m_c[1]; }
-		int32_t z() const { return m_c[2]; }
-		int32_t w() const { return m_c[3]; }
-
-		int32_t& x() { return m_c[0]; }
-		int32_t& y() { return m_c[1]; }
-		int32_t& z() { return m_c[2]; }
-		int32_t& w() { return m_c[3]; }
-
-		UVec4 asUint() const
-		{
-			return UVec4(basisu::maximum(0, m_c[0]), basisu::maximum(0, m_c[1]), basisu::maximum(0, m_c[2]), basisu::maximum(0, m_c[3]));
-		}
-
-		int32_t operator[] (uint32_t idx) const { assert(idx < 4);  return m_c[idx]; }
-		int32_t& operator[] (uint32_t idx) { assert(idx < 4);  return m_c[idx]; }
-	};
-
-	struct IVec3
-	{
-		int32_t m_c[3];
-
-		IVec3()
-		{
-			m_c[0] = 0;
-			m_c[1] = 0;
-			m_c[2] = 0;
-		}
-
-		IVec3(int32_t x, int32_t y, int32_t z)
-		{
-			m_c[0] = x;
-			m_c[1] = y;
-			m_c[2] = z;
-		}
-
-		int32_t x() const { return m_c[0]; }
-		int32_t y() const { return m_c[1]; }
-		int32_t z() const { return m_c[2]; }
-
-		int32_t& x() { return m_c[0]; }
-		int32_t& y() { return m_c[1]; }
-		int32_t& z() { return m_c[2]; }
-
-		int32_t operator[] (uint32_t idx) const { assert(idx < 3);  return m_c[idx]; }
-		int32_t& operator[] (uint32_t idx) { assert(idx < 3);  return m_c[idx]; }
-	};
-
-	static uint32_t deDivRoundUp32(uint32_t a, uint32_t b)
-	{
-		return (a + b - 1) / b;
-	}
-
-	static bool deInBounds32(uint32_t v, uint32_t l, uint32_t h)
-	{
-		return (v >= l) && (v < h);
-	}
-
-namespace astc
-{
-using std::vector;
-namespace
-{
-// Common utilities
-enum
-{
-	MAX_BLOCK_WIDTH		= 12,
-	MAX_BLOCK_HEIGHT	= 12
-};
-inline deUint32 getBit (deUint32 src, int ndx)
-{
-	DE_ASSERT(basisu_astc::inBounds(ndx, 0, 32));
-	return (src >> ndx) & 1;
-}
-inline deUint32 getBits (deUint32 src, int low, int high)
-{
-	const int numBits = (high-low) + 1;
-	DE_ASSERT(basisu_astc::inRange(numBits, 1, 32));
-	if (numBits < 32)
-		return (deUint32)((src >> low) & ((1u<<numBits)-1));
-	else
-		return (deUint32)((src >> low) & 0xFFFFFFFFu);
-}
-inline bool isBitSet (deUint32 src, int ndx)
-{
-	return getBit(src, ndx) != 0;
-}
-inline deUint32 reverseBits (deUint32 src, int numBits)
-{
-	DE_ASSERT(basisu_astc::inRange(numBits, 0, 32));
-	deUint32 result = 0;
-	for (int i = 0; i < numBits; i++)
-		result |= ((src >> i) & 1) << (numBits-1-i);
-	return result;
-}
-inline deUint32 bitReplicationScale (deUint32 src, int numSrcBits, int numDstBits)
-{
-	DE_ASSERT(numSrcBits <= numDstBits);
-	DE_ASSERT((src & ((1<<numSrcBits)-1)) == src);
-	deUint32 dst = 0;
-	for (int shift = numDstBits-numSrcBits; shift > -numSrcBits; shift -= numSrcBits)
-		dst |= shift >= 0 ? src << shift : src >> -shift;
-	return dst;
-}
-
-inline deInt32 signExtend (deInt32 src, int numSrcBits)
-{
-	DE_ASSERT(basisu_astc::inRange(numSrcBits, 2, 31));
-	const bool negative = (src & (1 << (numSrcBits-1))) != 0;
-	return src | (negative ? ~((1 << numSrcBits) - 1) : 0);
-}
-
-//inline bool isFloat16InfOrNan (deFloat16 v)
-//{
-//	return getBits(v, 10, 14) == 31;
-//}
-
-enum ISEMode
-{
-	ISEMODE_TRIT = 0,
-	ISEMODE_QUINT,
-	ISEMODE_PLAIN_BIT,
-	ISEMODE_LAST
-};
-struct ISEParams
-{
-	ISEMode		mode;
-	int			numBits;
-	ISEParams (ISEMode mode_, int numBits_) : mode(mode_), numBits(numBits_) {}
-};
-inline int computeNumRequiredBits (const ISEParams& iseParams, int numValues)
-{
-	switch (iseParams.mode)
-	{
-		case ISEMODE_TRIT:			return deDivRoundUp32(numValues*8, 5) + numValues*iseParams.numBits;
-		case ISEMODE_QUINT:			return deDivRoundUp32(numValues*7, 3) + numValues*iseParams.numBits;
-		case ISEMODE_PLAIN_BIT:		return numValues*iseParams.numBits;
-		default:
-			DE_ASSERT(false);
-			return -1;
-	}
-}
-ISEParams computeMaximumRangeISEParams (int numAvailableBits, int numValuesInSequence)
-{
-	int curBitsForTritMode		= 6;
-	int curBitsForQuintMode		= 5;
-	int curBitsForPlainBitMode	= 8;
-	while (true)
-	{
-		DE_ASSERT(curBitsForTritMode > 0 || curBitsForQuintMode > 0 || curBitsForPlainBitMode > 0);
-		const int tritRange			= curBitsForTritMode > 0		? (3 << curBitsForTritMode) - 1			: -1;
-		const int quintRange		= curBitsForQuintMode > 0		? (5 << curBitsForQuintMode) - 1		: -1;
-		const int plainBitRange		= curBitsForPlainBitMode > 0	? (1 << curBitsForPlainBitMode) - 1		: -1;
-		const int maxRange			= basisu_astc::max(basisu_astc::max(tritRange, quintRange), plainBitRange);
-		if (maxRange == tritRange)
-		{
-			const ISEParams params(ISEMODE_TRIT, curBitsForTritMode);
-			if (computeNumRequiredBits(params, numValuesInSequence) <= numAvailableBits)
-				return ISEParams(ISEMODE_TRIT, curBitsForTritMode);
-			curBitsForTritMode--;
-		}
-		else if (maxRange == quintRange)
-		{
-			const ISEParams params(ISEMODE_QUINT, curBitsForQuintMode);
-			if (computeNumRequiredBits(params, numValuesInSequence) <= numAvailableBits)
-				return ISEParams(ISEMODE_QUINT, curBitsForQuintMode);
-			curBitsForQuintMode--;
-		}
-		else
-		{
-			const ISEParams params(ISEMODE_PLAIN_BIT, curBitsForPlainBitMode);
-			DE_ASSERT(maxRange == plainBitRange);
-			if (computeNumRequiredBits(params, numValuesInSequence) <= numAvailableBits)
-				return ISEParams(ISEMODE_PLAIN_BIT, curBitsForPlainBitMode);
-			curBitsForPlainBitMode--;
-		}
-	}
-}
-inline int computeNumColorEndpointValues (deUint32 endpointMode)
-{
-	DE_ASSERT(endpointMode < 16);
-	return (endpointMode/4 + 1) * 2;
-}
-// Decompression utilities
-enum DecompressResult
-{
-	DECOMPRESS_RESULT_VALID_BLOCK	= 0,	//!< Decompressed valid block
-	DECOMPRESS_RESULT_ERROR,				//!< Encountered error while decompressing, error color written
-	DECOMPRESS_RESULT_LAST
-};
-// A helper for getting bits from a 128-bit block.
-class Block128
-{
-private:
-	typedef deUint64 Word;
-	enum
-	{
-		WORD_BYTES	= sizeof(Word),
-		WORD_BITS	= 8*WORD_BYTES,
-		NUM_WORDS	= 128 / WORD_BITS
-	};
-	//DE_STATIC_ASSERT(128 % WORD_BITS == 0);
-public:
-	Block128 (const deUint8* src)
-	{
-		for (int wordNdx = 0; wordNdx < NUM_WORDS; wordNdx++)
-		{
-			m_words[wordNdx] = 0;
-			for (int byteNdx = 0; byteNdx < WORD_BYTES; byteNdx++)
-				m_words[wordNdx] |= (Word)src[wordNdx*WORD_BYTES + byteNdx] << (8*byteNdx);
-		}
-	}
-	deUint32 getBit (int ndx) const
-	{
-		DE_ASSERT(basisu_astc::inBounds(ndx, 0, 128));
-		return (m_words[ndx / WORD_BITS] >> (ndx % WORD_BITS)) & 1;
-	}
-	deUint32 getBits (int low, int high) const
-	{
-		DE_ASSERT(basisu_astc::inBounds(low, 0, 128));
-		DE_ASSERT(basisu_astc::inBounds(high, 0, 128));
-		DE_ASSERT(basisu_astc::inRange(high-low+1, 0, 32));
-		if (high-low+1 == 0)
-			return 0;
-		const int word0Ndx = low / WORD_BITS;
-		const int word1Ndx = high / WORD_BITS;
-		// \note "foo << bar << 1" done instead of "foo << (bar+1)" to avoid overflow, i.e. shift amount being too big.
-		if (word0Ndx == word1Ndx)
-			return (deUint32)((m_words[word0Ndx] & ((((Word)1 << high%WORD_BITS << 1) - 1))) >> ((Word)low % WORD_BITS));
-		else
-		{
-			DE_ASSERT(word1Ndx == word0Ndx + 1);
-			return (deUint32)(m_words[word0Ndx] >> (low%WORD_BITS)) |
-				   (deUint32)((m_words[word1Ndx] & (((Word)1 << high%WORD_BITS << 1) - 1)) << (high-low - high%WORD_BITS));
-		}
-	}
-	bool isBitSet (int ndx) const
-	{
-		DE_ASSERT(basisu_astc::inBounds(ndx, 0, 128));
-		return getBit(ndx) != 0;
-	}
-private:
-	Word m_words[NUM_WORDS];
-};
-// A helper for sequential access into a Block128.
-class BitAccessStream
-{
-public:
-	BitAccessStream (const Block128& src, int startNdxInSrc, int length, bool forward)
-		: m_src				(src)
-		, m_startNdxInSrc	(startNdxInSrc)
-		, m_length			(length)
-		, m_forward			(forward)
-		, m_ndx				(0)
-	{
-	}
-	// Get the next num bits. Bits at positions greater than or equal to m_length are zeros.
-	deUint32 getNext (int num)
-	{
-		if (num == 0 || m_ndx >= m_length)
-			return 0;
-		const int end				= m_ndx + num;
-		const int numBitsFromSrc	= basisu_astc::max(0, basisu_astc::min(m_length, end) - m_ndx);
-		const int low				= m_ndx;
-		const int high				= m_ndx + numBitsFromSrc - 1;
-		m_ndx += num;
-		return m_forward ?			   m_src.getBits(m_startNdxInSrc + low,  m_startNdxInSrc + high)
-						 : reverseBits(m_src.getBits(m_startNdxInSrc - high, m_startNdxInSrc - low), numBitsFromSrc);
-	}
-private:
-	const Block128&		m_src;
-	const int			m_startNdxInSrc;
-	const int			m_length;
-	const bool			m_forward;
-	int					m_ndx;
-};
-struct ISEDecodedResult
-{
-	deUint32 m;
-	deUint32 tq; //!< Trit or quint value, depending on ISE mode.
-	deUint32 v;
-};
-// Data from an ASTC block's "block mode" part (i.e. bits [0,10]).
-struct ASTCBlockMode
-{
-	bool		isError;
-	// \note Following fields only relevant if !isError.
-	bool		isVoidExtent;
-	// \note Following fields only relevant if !isVoidExtent.
-	bool		isDualPlane;
-	int			weightGridWidth;
-	int			weightGridHeight;
-	ISEParams	weightISEParams;
-	ASTCBlockMode (void)
-		: isError			(true)
-		, isVoidExtent		(true)
-		, isDualPlane		(true)
-		, weightGridWidth	(-1)
-		, weightGridHeight	(-1)
-		, weightISEParams	(ISEMODE_LAST, -1)
-	{
-	}
-};
-inline int computeNumWeights (const ASTCBlockMode& mode)
-{
-	return mode.weightGridWidth * mode.weightGridHeight * (mode.isDualPlane ? 2 : 1);
-}
-struct ColorEndpointPair
-{
-	UVec4 e0;
-	UVec4 e1;
-};
-struct TexelWeightPair
-{
-	deUint32 w[2];
-};
-ASTCBlockMode getASTCBlockMode (deUint32 blockModeData)
-{
-	ASTCBlockMode blockMode;
-	blockMode.isError = true; // \note Set to false later, if not error.
-	blockMode.isVoidExtent = getBits(blockModeData, 0, 8) == 0x1fc;
-	if (!blockMode.isVoidExtent)
-	{
-		if ((getBits(blockModeData, 0, 1) == 0 && getBits(blockModeData, 6, 8) == 7) || getBits(blockModeData, 0, 3) == 0)
-			return blockMode; // Invalid ("reserved").
-		deUint32 r = (deUint32)-1; // \note Set in the following branches.
-		if (getBits(blockModeData, 0, 1) == 0)
-		{
-			const deUint32 r0	= getBit(blockModeData, 4);
-			const deUint32 r1	= getBit(blockModeData, 2);
-			const deUint32 r2	= getBit(blockModeData, 3);
-			const deUint32 i78	= getBits(blockModeData, 7, 8);
-			r = (r2 << 2) | (r1 << 1) | (r0 << 0);
-			if (i78 == 3)
-			{
-				const bool i5 = isBitSet(blockModeData, 5);
-				blockMode.weightGridWidth	= i5 ? 10 : 6;
-				blockMode.weightGridHeight	= i5 ? 6  : 10;
-			}
-			else
-			{
-				const deUint32 a = getBits(blockModeData, 5, 6);
-				switch (i78)
-				{
-					case 0:		blockMode.weightGridWidth = 12;		blockMode.weightGridHeight = a + 2;									break;
-					case 1:		blockMode.weightGridWidth = a + 2;	blockMode.weightGridHeight = 12;									break;
-					case 2:		blockMode.weightGridWidth = a + 6;	blockMode.weightGridHeight = getBits(blockModeData, 9, 10) + 6;		break;
-					default: DE_ASSERT(false);
-				}
-			}
-		}
-		else
-		{
-			const deUint32 r0	= getBit(blockModeData, 4);
-			const deUint32 r1	= getBit(blockModeData, 0);
-			const deUint32 r2	= getBit(blockModeData, 1);
-			const deUint32 i23	= getBits(blockModeData, 2, 3);
-			const deUint32 a	= getBits(blockModeData, 5, 6);
-			r = (r2 << 2) | (r1 << 1) | (r0 << 0);
-			if (i23 == 3)
-			{
-				const deUint32	b	= getBit(blockModeData, 7);
-				const bool		i8	= isBitSet(blockModeData, 8);
-				blockMode.weightGridWidth	= i8 ? b+2 : a+2;
-				blockMode.weightGridHeight	= i8 ? a+2 : b+6;
-			}
-			else
-			{
-				const deUint32 b = getBits(blockModeData, 7, 8);
-				switch (i23)
-				{
-					case 0:		blockMode.weightGridWidth = b + 4;	blockMode.weightGridHeight = a + 2;	break;
-					case 1:		blockMode.weightGridWidth = b + 8;	blockMode.weightGridHeight = a + 2;	break;
-					case 2:		blockMode.weightGridWidth = a + 2;	blockMode.weightGridHeight = b + 8;	break;
-					default: DE_ASSERT(false);
-				}
-			}
-		}
-		const bool	zeroDH		= getBits(blockModeData, 0, 1) == 0 && getBits(blockModeData, 7, 8) == 2;
-		const bool	h			= zeroDH ? 0 : isBitSet(blockModeData, 9);
-		blockMode.isDualPlane	= zeroDH ? 0 : isBitSet(blockModeData, 10);
-		{
-			ISEMode&	m	= blockMode.weightISEParams.mode;
-			int&		b	= blockMode.weightISEParams.numBits;
-			m = ISEMODE_PLAIN_BIT;
-			b = 0;
-			if (h)
-			{
-				switch (r)
-				{
-					case 2:							m = ISEMODE_QUINT;	b = 1;	break;
-					case 3:		m = ISEMODE_TRIT;						b = 2;	break;
-					case 4:												b = 4;	break;
-					case 5:							m = ISEMODE_QUINT;	b = 2;	break;
-					case 6:		m = ISEMODE_TRIT;						b = 3;	break;
-					case 7:												b = 5;	break;
-					default:	DE_ASSERT(false);
-				}
-			}
-			else
-			{
-				switch (r)
-				{
-					case 2:												b = 1;	break;
-					case 3:		m = ISEMODE_TRIT;								break;
-					case 4:												b = 2;	break;
-					case 5:							m = ISEMODE_QUINT;			break;
-					case 6:		m = ISEMODE_TRIT;						b = 1;	break;
-					case 7:												b = 3;	break;
-					default:	DE_ASSERT(false);
-				}
-			}
-		}
-	}
-	blockMode.isError = false;
-	return blockMode;
-}
-inline void setASTCErrorColorBlock (void* dst, int blockWidth, int blockHeight, bool isSRGB)
-{
-	if (isSRGB)
-	{
-		deUint8* const dstU = (deUint8*)dst;
-		for (int i = 0; i < blockWidth*blockHeight; i++)
-		{
-			dstU[4*i + 0] = 0xff;
-			dstU[4*i + 1] = 0;
-			dstU[4*i + 2] = 0xff;
-			dstU[4*i + 3] = 0xff;
-		}
-	}
-	else
-	{
-		float* const dstF = (float*)dst;
-		for (int i = 0; i < blockWidth*blockHeight; i++)
-		{
-			dstF[4*i + 0] = 1.0f;
-			dstF[4*i + 1] = 0.0f;
-			dstF[4*i + 2] = 1.0f;
-			dstF[4*i + 3] = 1.0f;
-		}
-	}
-}
-DecompressResult decodeVoidExtentBlock (void* dst, const Block128& blockData, int blockWidth, int blockHeight, bool isSRGB, bool isLDRMode)
-{
-	const deUint32	minSExtent			= blockData.getBits(12, 24);
-	const deUint32	maxSExtent			= blockData.getBits(25, 37);
-	const deUint32	minTExtent			= blockData.getBits(38, 50);
-	const deUint32	maxTExtent			= blockData.getBits(51, 63);
-	const bool		allExtentsAllOnes	= minSExtent == 0x1fff && maxSExtent == 0x1fff && minTExtent == 0x1fff && maxTExtent == 0x1fff;
-	const bool		isHDRBlock			= blockData.isBitSet(9);
-	if ((isLDRMode && isHDRBlock) || (!allExtentsAllOnes && (minSExtent >= maxSExtent || minTExtent >= maxTExtent)))
-	{
-		setASTCErrorColorBlock(dst, blockWidth, blockHeight, isSRGB);
-		return DECOMPRESS_RESULT_ERROR;
-	}
-	const deUint32 rgba[4] =
-	{
-		blockData.getBits(64,  79),
-		blockData.getBits(80,  95),
-		blockData.getBits(96,  111),
-		blockData.getBits(112, 127)
-	};
-	if (isSRGB)
-	{
-		deUint8* const dstU = (deUint8*)dst;
-		for (int i = 0; i < blockWidth*blockHeight; i++)
-		for (int c = 0; c < 4; c++)
-			dstU[i*4 + c] = (deUint8)((rgba[c] & 0xff00) >> 8);
-	}
-	else
-	{
-		float* const dstF = (float*)dst;
-		if (isHDRBlock)
-		{
-			// rg - REMOVING HDR SUPPORT FOR NOW
-#if 0
-			for (int c = 0; c < 4; c++)
-			{
-				if (isFloat16InfOrNan((deFloat16)rgba[c]))
-					throw InternalError("Infinity or NaN color component in HDR void extent block in ASTC texture (behavior undefined by ASTC specification)");
-			}
-			for (int i = 0; i < blockWidth*blockHeight; i++)
-			for (int c = 0; c < 4; c++)
-				dstF[i*4 + c] = deFloat16To32((deFloat16)rgba[c]);
-#endif
-		}
-		else
-		{
-			for (int i = 0; i < blockWidth*blockHeight; i++)
-			for (int c = 0; c < 4; c++)
-				dstF[i*4 + c] = rgba[c] == 65535 ? 1.0f : (float)rgba[c] / 65536.0f;
-		}
-	}
-	return DECOMPRESS_RESULT_VALID_BLOCK;
-}
-void decodeColorEndpointModes (deUint32* endpointModesDst, const Block128& blockData, int numPartitions, int extraCemBitsStart)
-{
-	if (numPartitions == 1)
-		endpointModesDst[0] = blockData.getBits(13, 16);
-	else
-	{
-		const deUint32 highLevelSelector = blockData.getBits(23, 24);
-		if (highLevelSelector == 0)
-		{
-			const deUint32 mode = blockData.getBits(25, 28);
-			for (int i = 0; i < numPartitions; i++)
-				endpointModesDst[i] = mode;
-		}
-		else
-		{
-			for (int partNdx = 0; partNdx < numPartitions; partNdx++)
-			{
-				const deUint32 cemClass		= highLevelSelector - (blockData.isBitSet(25 + partNdx) ? 0 : 1);
-				const deUint32 lowBit0Ndx	= numPartitions + 2*partNdx;
-				const deUint32 lowBit1Ndx	= numPartitions + 2*partNdx + 1;
-				const deUint32 lowBit0		= blockData.getBit(lowBit0Ndx < 4 ? 25+lowBit0Ndx : extraCemBitsStart+lowBit0Ndx-4);
-				const deUint32 lowBit1		= blockData.getBit(lowBit1Ndx < 4 ? 25+lowBit1Ndx : extraCemBitsStart+lowBit1Ndx-4);
-				endpointModesDst[partNdx] = (cemClass << 2) | (lowBit1 << 1) | lowBit0;
-			}
-		}
-	}
-}
-int computeNumColorEndpointValues (const deUint32* endpointModes, int numPartitions)
-{
-	int result = 0;
-	for (int i = 0; i < numPartitions; i++)
-		result += computeNumColorEndpointValues(endpointModes[i]);
-	return result;
-}
-void decodeISETritBlock (ISEDecodedResult* dst, int numValues, BitAccessStream& data, int numBits)
-{
-	DE_ASSERT(basisu_astc::inRange(numValues, 1, 5));
-	deUint32 m[5];
-	m[0]			= data.getNext(numBits);
-	deUint32 T01	= data.getNext(2);
-	m[1]			= data.getNext(numBits);
-	deUint32 T23	= data.getNext(2);
-	m[2]			= data.getNext(numBits);
-	deUint32 T4		= data.getNext(1);
-	m[3]			= data.getNext(numBits);
-	deUint32 T56	= data.getNext(2);
-	m[4]			= data.getNext(numBits);
-	deUint32 T7		= data.getNext(1);
-	switch (numValues)
-	{
-		// \note Fall-throughs.
-		case 1: T23		= 0;
-		case 2: T4		= 0;
-		case 3: T56		= 0;
-		case 4: T7		= 0;
-		case 5: break;
-		default:
-			DE_ASSERT(false);
-	}
-	const deUint32 T = (T7 << 7) | (T56 << 5) | (T4 << 4) | (T23 << 2) | (T01 << 0);
-	static const deUint32 tritsFromT[256][5] =
-	{
-		{ 0,0,0,0,0 }, { 1,0,0,0,0 }, { 2,0,0,0,0 }, { 0,0,2,0,0 }, { 0,1,0,0,0 }, { 1,1,0,0,0 }, { 2,1,0,0,0 }, { 1,0,2,0,0 }, { 0,2,0,0,0 }, { 1,2,0,0,0 }, { 2,2,0,0,0 }, { 2,0,2,0,0 }, { 0,2,2,0,0 }, { 1,2,2,0,0 }, { 2,2,2,0,0 }, { 2,0,2,0,0 },
-		{ 0,0,1,0,0 }, { 1,0,1,0,0 }, { 2,0,1,0,0 }, { 0,1,2,0,0 }, { 0,1,1,0,0 }, { 1,1,1,0,0 }, { 2,1,1,0,0 }, { 1,1,2,0,0 }, { 0,2,1,0,0 }, { 1,2,1,0,0 }, { 2,2,1,0,0 }, { 2,1,2,0,0 }, { 0,0,0,2,2 }, { 1,0,0,2,2 }, { 2,0,0,2,2 }, { 0,0,2,2,2 },
-		{ 0,0,0,1,0 }, { 1,0,0,1,0 }, { 2,0,0,1,0 }, { 0,0,2,1,0 }, { 0,1,0,1,0 }, { 1,1,0,1,0 }, { 2,1,0,1,0 }, { 1,0,2,1,0 }, { 0,2,0,1,0 }, { 1,2,0,1,0 }, { 2,2,0,1,0 }, { 2,0,2,1,0 }, { 0,2,2,1,0 }, { 1,2,2,1,0 }, { 2,2,2,1,0 }, { 2,0,2,1,0 },
-		{ 0,0,1,1,0 }, { 1,0,1,1,0 }, { 2,0,1,1,0 }, { 0,1,2,1,0 }, { 0,1,1,1,0 }, { 1,1,1,1,0 }, { 2,1,1,1,0 }, { 1,1,2,1,0 }, { 0,2,1,1,0 }, { 1,2,1,1,0 }, { 2,2,1,1,0 }, { 2,1,2,1,0 }, { 0,1,0,2,2 }, { 1,1,0,2,2 }, { 2,1,0,2,2 }, { 1,0,2,2,2 },
-		{ 0,0,0,2,0 }, { 1,0,0,2,0 }, { 2,0,0,2,0 }, { 0,0,2,2,0 }, { 0,1,0,2,0 }, { 1,1,0,2,0 }, { 2,1,0,2,0 }, { 1,0,2,2,0 }, { 0,2,0,2,0 }, { 1,2,0,2,0 }, { 2,2,0,2,0 }, { 2,0,2,2,0 }, { 0,2,2,2,0 }, { 1,2,2,2,0 }, { 2,2,2,2,0 }, { 2,0,2,2,0 },
-		{ 0,0,1,2,0 }, { 1,0,1,2,0 }, { 2,0,1,2,0 }, { 0,1,2,2,0 }, { 0,1,1,2,0 }, { 1,1,1,2,0 }, { 2,1,1,2,0 }, { 1,1,2,2,0 }, { 0,2,1,2,0 }, { 1,2,1,2,0 }, { 2,2,1,2,0 }, { 2,1,2,2,0 }, { 0,2,0,2,2 }, { 1,2,0,2,2 }, { 2,2,0,2,2 }, { 2,0,2,2,2 },
-		{ 0,0,0,0,2 }, { 1,0,0,0,2 }, { 2,0,0,0,2 }, { 0,0,2,0,2 }, { 0,1,0,0,2 }, { 1,1,0,0,2 }, { 2,1,0,0,2 }, { 1,0,2,0,2 }, { 0,2,0,0,2 }, { 1,2,0,0,2 }, { 2,2,0,0,2 }, { 2,0,2,0,2 }, { 0,2,2,0,2 }, { 1,2,2,0,2 }, { 2,2,2,0,2 }, { 2,0,2,0,2 },
-		{ 0,0,1,0,2 }, { 1,0,1,0,2 }, { 2,0,1,0,2 }, { 0,1,2,0,2 }, { 0,1,1,0,2 }, { 1,1,1,0,2 }, { 2,1,1,0,2 }, { 1,1,2,0,2 }, { 0,2,1,0,2 }, { 1,2,1,0,2 }, { 2,2,1,0,2 }, { 2,1,2,0,2 }, { 0,2,2,2,2 }, { 1,2,2,2,2 }, { 2,2,2,2,2 }, { 2,0,2,2,2 },
-		{ 0,0,0,0,1 }, { 1,0,0,0,1 }, { 2,0,0,0,1 }, { 0,0,2,0,1 }, { 0,1,0,0,1 }, { 1,1,0,0,1 }, { 2,1,0,0,1 }, { 1,0,2,0,1 }, { 0,2,0,0,1 }, { 1,2,0,0,1 }, { 2,2,0,0,1 }, { 2,0,2,0,1 }, { 0,2,2,0,1 }, { 1,2,2,0,1 }, { 2,2,2,0,1 }, { 2,0,2,0,1 },
-		{ 0,0,1,0,1 }, { 1,0,1,0,1 }, { 2,0,1,0,1 }, { 0,1,2,0,1 }, { 0,1,1,0,1 }, { 1,1,1,0,1 }, { 2,1,1,0,1 }, { 1,1,2,0,1 }, { 0,2,1,0,1 }, { 1,2,1,0,1 }, { 2,2,1,0,1 }, { 2,1,2,0,1 }, { 0,0,1,2,2 }, { 1,0,1,2,2 }, { 2,0,1,2,2 }, { 0,1,2,2,2 },
-		{ 0,0,0,1,1 }, { 1,0,0,1,1 }, { 2,0,0,1,1 }, { 0,0,2,1,1 }, { 0,1,0,1,1 }, { 1,1,0,1,1 }, { 2,1,0,1,1 }, { 1,0,2,1,1 }, { 0,2,0,1,1 }, { 1,2,0,1,1 }, { 2,2,0,1,1 }, { 2,0,2,1,1 }, { 0,2,2,1,1 }, { 1,2,2,1,1 }, { 2,2,2,1,1 }, { 2,0,2,1,1 },
-		{ 0,0,1,1,1 }, { 1,0,1,1,1 }, { 2,0,1,1,1 }, { 0,1,2,1,1 }, { 0,1,1,1,1 }, { 1,1,1,1,1 }, { 2,1,1,1,1 }, { 1,1,2,1,1 }, { 0,2,1,1,1 }, { 1,2,1,1,1 }, { 2,2,1,1,1 }, { 2,1,2,1,1 }, { 0,1,1,2,2 }, { 1,1,1,2,2 }, { 2,1,1,2,2 }, { 1,1,2,2,2 },
-		{ 0,0,0,2,1 }, { 1,0,0,2,1 }, { 2,0,0,2,1 }, { 0,0,2,2,1 }, { 0,1,0,2,1 }, { 1,1,0,2,1 }, { 2,1,0,2,1 }, { 1,0,2,2,1 }, { 0,2,0,2,1 }, { 1,2,0,2,1 }, { 2,2,0,2,1 }, { 2,0,2,2,1 }, { 0,2,2,2,1 }, { 1,2,2,2,1 }, { 2,2,2,2,1 }, { 2,0,2,2,1 },
-		{ 0,0,1,2,1 }, { 1,0,1,2,1 }, { 2,0,1,2,1 }, { 0,1,2,2,1 }, { 0,1,1,2,1 }, { 1,1,1,2,1 }, { 2,1,1,2,1 }, { 1,1,2,2,1 }, { 0,2,1,2,1 }, { 1,2,1,2,1 }, { 2,2,1,2,1 }, { 2,1,2,2,1 }, { 0,2,1,2,2 }, { 1,2,1,2,2 }, { 2,2,1,2,2 }, { 2,1,2,2,2 },
-		{ 0,0,0,1,2 }, { 1,0,0,1,2 }, { 2,0,0,1,2 }, { 0,0,2,1,2 }, { 0,1,0,1,2 }, { 1,1,0,1,2 }, { 2,1,0,1,2 }, { 1,0,2,1,2 }, { 0,2,0,1,2 }, { 1,2,0,1,2 }, { 2,2,0,1,2 }, { 2,0,2,1,2 }, { 0,2,2,1,2 }, { 1,2,2,1,2 }, { 2,2,2,1,2 }, { 2,0,2,1,2 },
-		{ 0,0,1,1,2 }, { 1,0,1,1,2 }, { 2,0,1,1,2 }, { 0,1,2,1,2 }, { 0,1,1,1,2 }, { 1,1,1,1,2 }, { 2,1,1,1,2 }, { 1,1,2,1,2 }, { 0,2,1,1,2 }, { 1,2,1,1,2 }, { 2,2,1,1,2 }, { 2,1,2,1,2 }, { 0,2,2,2,2 }, { 1,2,2,2,2 }, { 2,2,2,2,2 }, { 2,1,2,2,2 }
-	};
-	const deUint32 (& trits)[5] = tritsFromT[T];
-	for (int i = 0; i < numValues; i++)
-	{
-		dst[i].m	= m[i];
-		dst[i].tq	= trits[i];
-		dst[i].v	= (trits[i] << numBits) + m[i];
-	}
-}
-void decodeISEQuintBlock (ISEDecodedResult* dst, int numValues, BitAccessStream& data, int numBits)
-{
-	DE_ASSERT(basisu_astc::inRange(numValues, 1, 3));
-	deUint32 m[3];
-	m[0]			= data.getNext(numBits);
-	deUint32 Q012	= data.getNext(3);
-	m[1]			= data.getNext(numBits);
-	deUint32 Q34	= data.getNext(2);
-	m[2]			= data.getNext(numBits);
-	deUint32 Q56	= data.getNext(2);
-	switch (numValues)
-	{
-		// \note Fall-throughs.
-		case 1: Q34		= 0;
-		case 2: Q56		= 0;
-		case 3: break;
-		default:
-			DE_ASSERT(false);
-	}
-	const deUint32 Q = (Q56 << 5) | (Q34 << 3) | (Q012 << 0);
-	static const deUint32 quintsFromQ[256][3] =
-	{
-		{ 0,0,0 }, { 1,0,0 }, { 2,0,0 }, { 3,0,0 }, { 4,0,0 }, { 0,4,0 }, { 4,4,0 }, { 4,4,4 }, { 0,1,0 }, { 1,1,0 }, { 2,1,0 }, { 3,1,0 }, { 4,1,0 }, { 1,4,0 }, { 4,4,1 }, { 4,4,4 },
-		{ 0,2,0 }, { 1,2,0 }, { 2,2,0 }, { 3,2,0 }, { 4,2,0 }, { 2,4,0 }, { 4,4,2 }, { 4,4,4 }, { 0,3,0 }, { 1,3,0 }, { 2,3,0 }, { 3,3,0 }, { 4,3,0 }, { 3,4,0 }, { 4,4,3 }, { 4,4,4 },
-		{ 0,0,1 }, { 1,0,1 }, { 2,0,1 }, { 3,0,1 }, { 4,0,1 }, { 0,4,1 }, { 4,0,4 }, { 0,4,4 }, { 0,1,1 }, { 1,1,1 }, { 2,1,1 }, { 3,1,1 }, { 4,1,1 }, { 1,4,1 }, { 4,1,4 }, { 1,4,4 },
-		{ 0,2,1 }, { 1,2,1 }, { 2,2,1 }, { 3,2,1 }, { 4,2,1 }, { 2,4,1 }, { 4,2,4 }, { 2,4,4 }, { 0,3,1 }, { 1,3,1 }, { 2,3,1 }, { 3,3,1 }, { 4,3,1 }, { 3,4,1 }, { 4,3,4 }, { 3,4,4 },
-		{ 0,0,2 }, { 1,0,2 }, { 2,0,2 }, { 3,0,2 }, { 4,0,2 }, { 0,4,2 }, { 2,0,4 }, { 3,0,4 }, { 0,1,2 }, { 1,1,2 }, { 2,1,2 }, { 3,1,2 }, { 4,1,2 }, { 1,4,2 }, { 2,1,4 }, { 3,1,4 },
-		{ 0,2,2 }, { 1,2,2 }, { 2,2,2 }, { 3,2,2 }, { 4,2,2 }, { 2,4,2 }, { 2,2,4 }, { 3,2,4 }, { 0,3,2 }, { 1,3,2 }, { 2,3,2 }, { 3,3,2 }, { 4,3,2 }, { 3,4,2 }, { 2,3,4 }, { 3,3,4 },
-		{ 0,0,3 }, { 1,0,3 }, { 2,0,3 }, { 3,0,3 }, { 4,0,3 }, { 0,4,3 }, { 0,0,4 }, { 1,0,4 }, { 0,1,3 }, { 1,1,3 }, { 2,1,3 }, { 3,1,3 }, { 4,1,3 }, { 1,4,3 }, { 0,1,4 }, { 1,1,4 },
-		{ 0,2,3 }, { 1,2,3 }, { 2,2,3 }, { 3,2,3 }, { 4,2,3 }, { 2,4,3 }, { 0,2,4 }, { 1,2,4 }, { 0,3,3 }, { 1,3,3 }, { 2,3,3 }, { 3,3,3 }, { 4,3,3 }, { 3,4,3 }, { 0,3,4 }, { 1,3,4 }
-	};
-	const deUint32 (& quints)[3] = quintsFromQ[Q];
-	for (int i = 0; i < numValues; i++)
-	{
-		dst[i].m	= m[i];
-		dst[i].tq	= quints[i];
-		dst[i].v	= (quints[i] << numBits) + m[i];
-	}
-}
-inline void decodeISEBitBlock (ISEDecodedResult* dst, BitAccessStream& data, int numBits)
-{
-	dst[0].m = data.getNext(numBits);
-	dst[0].v = dst[0].m;
-}
-void decodeISE (ISEDecodedResult* dst, int numValues, BitAccessStream& data, const ISEParams& params)
-{
-	if (params.mode == ISEMODE_TRIT)
-	{
-		const int numBlocks = deDivRoundUp32(numValues, 5);
-		for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
-		{
-			const int numValuesInBlock = blockNdx == numBlocks-1 ? numValues - 5*(numBlocks-1) : 5;
-			decodeISETritBlock(&dst[5*blockNdx], numValuesInBlock, data, params.numBits);
-		}
-	}
-	else if (params.mode == ISEMODE_QUINT)
-	{
-		const int numBlocks = deDivRoundUp32(numValues, 3);
-		for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
-		{
-			const int numValuesInBlock = blockNdx == numBlocks-1 ? numValues - 3*(numBlocks-1) : 3;
-			decodeISEQuintBlock(&dst[3*blockNdx], numValuesInBlock, data, params.numBits);
-		}
-	}
-	else
-	{
-		DE_ASSERT(params.mode == ISEMODE_PLAIN_BIT);
-		for (int i = 0; i < numValues; i++)
-			decodeISEBitBlock(&dst[i], data, params.numBits);
-	}
-}
-void unquantizeColorEndpoints (deUint32* dst, const ISEDecodedResult* iseResults, int numEndpoints, const ISEParams& iseParams)
-{
-	if (iseParams.mode == ISEMODE_TRIT || iseParams.mode == ISEMODE_QUINT)
-	{
-		const int rangeCase				= iseParams.numBits*2 - (iseParams.mode == ISEMODE_TRIT ? 2 : 1);
-		DE_ASSERT(basisu_astc::inRange(rangeCase, 0, 10));
-		static const deUint32	Ca[11]	= { 204, 113, 93, 54, 44, 26, 22, 13, 11, 6, 5 };
-		const deUint32			C		= Ca[rangeCase];
-		for (int endpointNdx = 0; endpointNdx < numEndpoints; endpointNdx++)
-		{
-			const deUint32 a = getBit(iseResults[endpointNdx].m, 0);
-			const deUint32 b = getBit(iseResults[endpointNdx].m, 1);
-			const deUint32 c = getBit(iseResults[endpointNdx].m, 2);
-			const deUint32 d = getBit(iseResults[endpointNdx].m, 3);
-			const deUint32 e = getBit(iseResults[endpointNdx].m, 4);
-			const deUint32 f = getBit(iseResults[endpointNdx].m, 5);
-			const deUint32 A = a == 0 ? 0 : (1<<9)-1;
-			const deUint32 B = rangeCase == 0	? 0
-							 : rangeCase == 1	? 0
-							 : rangeCase == 2	? (b << 8) |									(b << 4) |				(b << 2) |	(b << 1)
-							 : rangeCase == 3	? (b << 8) |												(b << 3) |	(b << 2)
-							 : rangeCase == 4	? (c << 8) | (b << 7) |										(c << 3) |	(b << 2) |	(c << 1) |	(b << 0)
-							 : rangeCase == 5	? (c << 8) | (b << 7) |													(c << 2) |	(b << 1) |	(c << 0)
-							 : rangeCase == 6	? (d << 8) | (c << 7) | (b << 6) |										(d << 2) |	(c << 1) |	(b << 0)
-							 : rangeCase == 7	? (d << 8) | (c << 7) | (b << 6) |													(d << 1) |	(c << 0)
-							 : rangeCase == 8	? (e << 8) | (d << 7) | (c << 6) | (b << 5) |										(e << 1) |	(d << 0)
-							 : rangeCase == 9	? (e << 8) | (d << 7) | (c << 6) | (b << 5) |													(e << 0)
-							 : rangeCase == 10	? (f << 8) | (e << 7) | (d << 6) | (c << 5) |	(b << 4) |										(f << 0)
-							 : (deUint32)-1;
-			DE_ASSERT(B != (deUint32)-1);
-			dst[endpointNdx] = (((iseResults[endpointNdx].tq*C + B) ^ A) >> 2) | (A & 0x80);
-		}
-	}
-	else
-	{
-		DE_ASSERT(iseParams.mode == ISEMODE_PLAIN_BIT);
-		for (int endpointNdx = 0; endpointNdx < numEndpoints; endpointNdx++)
-			dst[endpointNdx] = bitReplicationScale(iseResults[endpointNdx].v, iseParams.numBits, 8);
-	}
-}
-inline void bitTransferSigned (deInt32& a, deInt32& b)
-{
-	b >>= 1;
-	b |= a & 0x80;
-	a >>= 1;
-	a &= 0x3f;
-	if (isBitSet(a, 5))
-		a -= 0x40;
-}
-inline UVec4 clampedRGBA (const IVec4& rgba)
-{
-	return UVec4(basisu_astc::clamp(rgba.x(), 0, 0xff),
-		basisu_astc::clamp(rgba.y(), 0, 0xff),
-		basisu_astc::clamp(rgba.z(), 0, 0xff),
-		basisu_astc::clamp(rgba.w(), 0, 0xff));
-}
-inline IVec4 blueContract (int r, int g, int b, int a)
-{
-	return IVec4((r+b)>>1, (g+b)>>1, b, a);
-}
-inline bool isColorEndpointModeHDR (deUint32 mode)
-{
-	return mode == 2	||
-		   mode == 3	||
-		   mode == 7	||
-		   mode == 11	||
-		   mode == 14	||
-		   mode == 15;
-}
-void decodeHDREndpointMode7 (UVec4& e0, UVec4& e1, deUint32 v0, deUint32 v1, deUint32 v2, deUint32 v3)
-{
-	const deUint32 m10		= getBit(v1, 7) | (getBit(v2, 7) << 1);
-	const deUint32 m23		= getBits(v0, 6, 7);
-	const deUint32 majComp	= m10 != 3	? m10
-							: m23 != 3	? m23
-							:			  0;
-	const deUint32 mode		= m10 != 3	? m23
-							: m23 != 3	? 4
-							:			  5;
-	deInt32			red		= (deInt32)getBits(v0, 0, 5);
-	deInt32			green	= (deInt32)getBits(v1, 0, 4);
-	deInt32			blue	= (deInt32)getBits(v2, 0, 4);
-	deInt32			scale	= (deInt32)getBits(v3, 0, 4);
-	{
-#define SHOR(DST_VAR, SHIFT, BIT_VAR) (DST_VAR) |= (BIT_VAR) << (SHIFT)
-#define ASSIGN_X_BITS(V0,S0, V1,S1, V2,S2, V3,S3, V4,S4, V5,S5, V6,S6) do { SHOR(V0,S0,x0); SHOR(V1,S1,x1); SHOR(V2,S2,x2); SHOR(V3,S3,x3); SHOR(V4,S4,x4); SHOR(V5,S5,x5); SHOR(V6,S6,x6); } while (false)
-		const deUint32	x0	= getBit(v1, 6);
-		const deUint32	x1	= getBit(v1, 5);
-		const deUint32	x2	= getBit(v2, 6);
-		const deUint32	x3	= getBit(v2, 5);
-		const deUint32	x4	= getBit(v3, 7);
-		const deUint32	x5	= getBit(v3, 6);
-		const deUint32	x6	= getBit(v3, 5);
-		deInt32&		R	= red;
-		deInt32&		G	= green;
-		deInt32&		B	= blue;
-		deInt32&		S	= scale;
-		switch (mode)
-		{
-			case 0: ASSIGN_X_BITS(R,9,  R,8,  R,7,  R,10,  R,6,  S,6,   S,5); break;
-			case 1: ASSIGN_X_BITS(R,8,  G,5,  R,7,  B,5,   R,6,  R,10,  R,9); break;
-			case 2: ASSIGN_X_BITS(R,9,  R,8,  R,7,  R,6,   S,7,  S,6,   S,5); break;
-			case 3: ASSIGN_X_BITS(R,8,  G,5,  R,7,  B,5,   R,6,  S,6,   S,5); break;
-			case 4: ASSIGN_X_BITS(G,6,  G,5,  B,6,  B,5,   R,6,  R,7,   S,5); break;
-			case 5: ASSIGN_X_BITS(G,6,  G,5,  B,6,  B,5,   R,6,  S,6,   S,5); break;
-			default:
-				DE_ASSERT(false);
-		}
-#undef ASSIGN_X_BITS
-#undef SHOR
-	}
-	static const int shiftAmounts[] = { 1, 1, 2, 3, 4, 5 };
-	DE_ASSERT(mode < DE_LENGTH_OF_ARRAY(shiftAmounts));
-	red		<<= shiftAmounts[mode];
-	green	<<= shiftAmounts[mode];
-	blue	<<= shiftAmounts[mode];
-	scale	<<= shiftAmounts[mode];
-	if (mode != 5)
-	{
-		green	= red - green;
-		blue	= red - blue;
-	}
-	if (majComp == 1)
-		std::swap(red, green);
-	else if (majComp == 2)
-		std::swap(red, blue);
-	e0 = UVec4(basisu_astc::clamp(red	- scale,	0, 0xfff),
-		basisu_astc::clamp(green	- scale,	0, 0xfff),
-		basisu_astc::clamp(blue	- scale,	0, 0xfff),
-			   0x780);
-	e1 = UVec4(basisu_astc::clamp(red,				0, 0xfff),
-		basisu_astc::clamp(green,				0, 0xfff),
-		basisu_astc::clamp(blue,				0, 0xfff),
-			   0x780);
-}
-void decodeHDREndpointMode11 (UVec4& e0, UVec4& e1, deUint32 v0, deUint32 v1, deUint32 v2, deUint32 v3, deUint32 v4, deUint32 v5)
-{
-	const deUint32 major = (getBit(v5, 7) << 1) | getBit(v4, 7);
-	if (major == 3)
-	{
-		e0 = UVec4(v0<<4, v2<<4, getBits(v4,0,6)<<5, 0x780);
-		e1 = UVec4(v1<<4, v3<<4, getBits(v5,0,6)<<5, 0x780);
-	}
-	else
-	{
-		const deUint32 mode = (getBit(v3, 7) << 2) | (getBit(v2, 7) << 1) | getBit(v1, 7);
-		deInt32 a	= (deInt32)((getBit(v1, 6) << 8) | v0);
-		deInt32 c	= (deInt32)(getBits(v1, 0, 5));
-		deInt32 b0	= (deInt32)(getBits(v2, 0, 5));
-		deInt32 b1	= (deInt32)(getBits(v3, 0, 5));
-		deInt32 d0	= (deInt32)(getBits(v4, 0, 4));
-		deInt32 d1	= (deInt32)(getBits(v5, 0, 4));
-		{
-#define SHOR(DST_VAR, SHIFT, BIT_VAR) (DST_VAR) |= (BIT_VAR) << (SHIFT)
-#define ASSIGN_X_BITS(V0,S0, V1,S1, V2,S2, V3,S3, V4,S4, V5,S5) do { SHOR(V0,S0,x0); SHOR(V1,S1,x1); SHOR(V2,S2,x2); SHOR(V3,S3,x3); SHOR(V4,S4,x4); SHOR(V5,S5,x5); } while (false)
-			const deUint32 x0 = getBit(v2, 6);
-			const deUint32 x1 = getBit(v3, 6);
-			const deUint32 x2 = getBit(v4, 6);
-			const deUint32 x3 = getBit(v5, 6);
-			const deUint32 x4 = getBit(v4, 5);
-			const deUint32 x5 = getBit(v5, 5);
-			switch (mode)
-			{
-				case 0: ASSIGN_X_BITS(b0,6,  b1,6,   d0,6,  d1,6,  d0,5,  d1,5); break;
-				case 1: ASSIGN_X_BITS(b0,6,  b1,6,   b0,7,  b1,7,  d0,5,  d1,5); break;
-				case 2: ASSIGN_X_BITS(a,9,   c,6,    d0,6,  d1,6,  d0,5,  d1,5); break;
-				case 3: ASSIGN_X_BITS(b0,6,  b1,6,   a,9,   c,6,   d0,5,  d1,5); break;
-				case 4: ASSIGN_X_BITS(b0,6,  b1,6,   b0,7,  b1,7,  a,9,   a,10); break;
-				case 5: ASSIGN_X_BITS(a,9,   a,10,   c,7,   c,6,   d0,5,  d1,5); break;
-				case 6: ASSIGN_X_BITS(b0,6,  b1,6,   a,11,  c,6,   a,9,   a,10); break;
-				case 7: ASSIGN_X_BITS(a,9,   a,10,   a,11,  c,6,   d0,5,  d1,5); break;
-				default:
-					DE_ASSERT(false);
-			}
-#undef ASSIGN_X_BITS
-#undef SHOR
-		}
-		static const int numDBits[] = { 7, 6, 7, 6, 5, 6, 5, 6 };
-		DE_ASSERT(mode < DE_LENGTH_OF_ARRAY(numDBits));
-		d0 = signExtend(d0, numDBits[mode]);
-		d1 = signExtend(d1, numDBits[mode]);
-		const int shiftAmount = (mode >> 1) ^ 3;
-		a	<<= shiftAmount;
-		c	<<= shiftAmount;
-		b0	<<= shiftAmount;
-		b1	<<= shiftAmount;
-		d0	<<= shiftAmount;
-		d1	<<= shiftAmount;
-		e0 = UVec4(basisu_astc::clamp(a-c,			0, 0xfff),
-			basisu_astc::clamp(a-b0-c-d0,		0, 0xfff),
-			basisu_astc::clamp(a-b1-c-d1,		0, 0xfff),
-				   0x780);
-		e1 = UVec4(basisu_astc::clamp(a,				0, 0xfff),
-			basisu_astc::clamp(a-b0,			0, 0xfff),
-			basisu_astc::clamp(a-b1,			0, 0xfff),
-				   0x780);
-		if (major == 1)
-		{
-			std::swap(e0.x(), e0.y());
-			std::swap(e1.x(), e1.y());
-		}
-		else if (major == 2)
-		{
-			std::swap(e0.x(), e0.z());
-			std::swap(e1.x(), e1.z());
-		}
-	}
-}
-void decodeHDREndpointMode15(UVec4& e0, UVec4& e1, deUint32 v0, deUint32 v1, deUint32 v2, deUint32 v3, deUint32 v4, deUint32 v5, deUint32 v6In, deUint32 v7In)
-{
-	decodeHDREndpointMode11(e0, e1, v0, v1, v2, v3, v4, v5);
-	const deUint32	mode	= (getBit(v7In, 7) << 1) | getBit(v6In, 7);
-	deInt32			v6		= (deInt32)getBits(v6In, 0, 6);
-	deInt32			v7		= (deInt32)getBits(v7In, 0, 6);
-	if (mode == 3)
-	{
-		e0.w() = v6 << 5;
-		e1.w() = v7 << 5;
-	}
-	else
-	{
-		v6 |= (v7 << (mode+1)) & 0x780;
-		v7 &= (0x3f >> mode);
-		v7 ^= 0x20 >> mode;
-		v7 -= 0x20 >> mode;
-		v6 <<= 4-mode;
-		v7 <<= 4-mode;
-		v7 += v6;
-		v7 = basisu_astc::clamp(v7, 0, 0xfff);
-		e0.w() = v6;
-		e1.w() = v7;
-	}
-}
-void decodeColorEndpoints (ColorEndpointPair* dst, const deUint32* unquantizedEndpoints, const deUint32* endpointModes, int numPartitions)
-{
-	int unquantizedNdx = 0;
-	for (int partitionNdx = 0; partitionNdx < numPartitions; partitionNdx++)
-	{
-		const deUint32		endpointMode	= endpointModes[partitionNdx];
-		const deUint32*		v				= &unquantizedEndpoints[unquantizedNdx];
-		UVec4&				e0				= dst[partitionNdx].e0;
-		UVec4&				e1				= dst[partitionNdx].e1;
-		unquantizedNdx += computeNumColorEndpointValues(endpointMode);
-		switch (endpointMode)
-		{
-			case 0:
-				e0 = UVec4(v[0], v[0], v[0], 0xff);
-				e1 = UVec4(v[1], v[1], v[1], 0xff);
-				break;
-			case 1:
-			{
-				const deUint32 L0 = (v[0] >> 2) | (getBits(v[1], 6, 7) << 6);
-				const deUint32 L1 = basisu_astc::min(0xffu, L0 + getBits(v[1], 0, 5));
-				e0 = UVec4(L0, L0, L0, 0xff);
-				e1 = UVec4(L1, L1, L1, 0xff);
-				break;
-			}
-			case 2:
-			{
-				const deUint32 v1Gr		= v[1] >= v[0];
-				const deUint32 y0		= v1Gr ? v[0]<<4 : (v[1]<<4) + 8;
-				const deUint32 y1		= v1Gr ? v[1]<<4 : (v[0]<<4) - 8;
-				e0 = UVec4(y0, y0, y0, 0x780);
-				e1 = UVec4(y1, y1, y1, 0x780);
-				break;
-			}
-			case 3:
-			{
-				const bool		m	= isBitSet(v[0], 7);
-				const deUint32	y0	= m ? (getBits(v[1], 5, 7) << 9) | (getBits(v[0], 0, 6) << 2)
-										: (getBits(v[1], 4, 7) << 8) | (getBits(v[0], 0, 6) << 1);
-				const deUint32	d	= m ? getBits(v[1], 0, 4) << 2
-										: getBits(v[1], 0, 3) << 1;
-				const deUint32	y1	= basisu_astc::min(0xfffu, y0+d);
-				e0 = UVec4(y0, y0, y0, 0x780);
-				e1 = UVec4(y1, y1, y1, 0x780);
-				break;
-			}
-			case 4:
-				e0 = UVec4(v[0], v[0], v[0], v[2]);
-				e1 = UVec4(v[1], v[1], v[1], v[3]);
-				break;
-			case 5:
-			{
-				deInt32 v0 = (deInt32)v[0];
-				deInt32 v1 = (deInt32)v[1];
-				deInt32 v2 = (deInt32)v[2];
-				deInt32 v3 = (deInt32)v[3];
-				bitTransferSigned(v1, v0);
-				bitTransferSigned(v3, v2);
-				e0 = clampedRGBA(IVec4(v0,		v0,		v0,		v2));
-				e1 = clampedRGBA(IVec4(v0+v1,	v0+v1,	v0+v1,	v2+v3));
-				break;
-			}
-			case 6:
-				e0 = UVec4((v[0]*v[3]) >> 8,	(v[1]*v[3]) >> 8,	(v[2]*v[3]) >> 8,	0xff);
-				e1 = UVec4(v[0],				v[1],				v[2],				0xff);
-				break;
-			case 7:
-				decodeHDREndpointMode7(e0, e1, v[0], v[1], v[2], v[3]);
-				break;
-			case 8:
-				if (v[1]+v[3]+v[5] >= v[0]+v[2]+v[4])
-				{
-					e0 = UVec4(v[0], v[2], v[4], 0xff);
-					e1 = UVec4(v[1], v[3], v[5], 0xff);
-				}
-				else
-				{
-					e0 = blueContract(v[1], v[3], v[5], 0xff).asUint();
-					e1 = blueContract(v[0], v[2], v[4], 0xff).asUint();
-				}
-				break;
-			case 9:
-			{
-				deInt32 v0 = (deInt32)v[0];
-				deInt32 v1 = (deInt32)v[1];
-				deInt32 v2 = (deInt32)v[2];
-				deInt32 v3 = (deInt32)v[3];
-				deInt32 v4 = (deInt32)v[4];
-				deInt32 v5 = (deInt32)v[5];
-				bitTransferSigned(v1, v0);
-				bitTransferSigned(v3, v2);
-				bitTransferSigned(v5, v4);
-				if (v1+v3+v5 >= 0)
-				{
-					e0 = clampedRGBA(IVec4(v0,		v2,		v4,		0xff));
-					e1 = clampedRGBA(IVec4(v0+v1,	v2+v3,	v4+v5,	0xff));
-				}
-				else
-				{
-					e0 = clampedRGBA(blueContract(v0+v1,	v2+v3,	v4+v5,	0xff));
-					e1 = clampedRGBA(blueContract(v0,		v2,		v4,		0xff));
-				}
-				break;
-			}
-			case 10:
-				e0 = UVec4((v[0]*v[3]) >> 8,	(v[1]*v[3]) >> 8,	(v[2]*v[3]) >> 8,	v[4]);
-				e1 = UVec4(v[0],				v[1],				v[2],				v[5]);
-				break;
-			case 11:
-				decodeHDREndpointMode11(e0, e1, v[0], v[1], v[2], v[3], v[4], v[5]);
-				break;
-			case 12:
-				if (v[1]+v[3]+v[5] >= v[0]+v[2]+v[4])
-				{
-					e0 = UVec4(v[0], v[2], v[4], v[6]);
-					e1 = UVec4(v[1], v[3], v[5], v[7]);
-				}
-				else
-				{
-					e0 = clampedRGBA(blueContract(v[1], v[3], v[5], v[7]));
-					e1 = clampedRGBA(blueContract(v[0], v[2], v[4], v[6]));
-				}
-				break;
-			case 13:
-			{
-				deInt32 v0 = (deInt32)v[0];
-				deInt32 v1 = (deInt32)v[1];
-				deInt32 v2 = (deInt32)v[2];
-				deInt32 v3 = (deInt32)v[3];
-				deInt32 v4 = (deInt32)v[4];
-				deInt32 v5 = (deInt32)v[5];
-				deInt32 v6 = (deInt32)v[6];
-				deInt32 v7 = (deInt32)v[7];
-				bitTransferSigned(v1, v0);
-				bitTransferSigned(v3, v2);
-				bitTransferSigned(v5, v4);
-				bitTransferSigned(v7, v6);
-				if (v1+v3+v5 >= 0)
-				{
-					e0 = clampedRGBA(IVec4(v0,		v2,		v4,		v6));
-					e1 = clampedRGBA(IVec4(v0+v1,	v2+v3,	v4+v5,	v6+v7));
-				}
-				else
-				{
-					e0 = clampedRGBA(blueContract(v0+v1,	v2+v3,	v4+v5,	v6+v7));
-					e1 = clampedRGBA(blueContract(v0,		v2,		v4,		v6));
-				}
-				break;
-			}
-			case 14:
-				decodeHDREndpointMode11(e0, e1, v[0], v[1], v[2], v[3], v[4], v[5]);
-				e0.w() = v[6];
-				e1.w() = v[7];
-				break;
-			case 15:
-				decodeHDREndpointMode15(e0, e1, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
-				break;
-			default:
-				DE_ASSERT(false);
-		}
-	}
-}
-void computeColorEndpoints (ColorEndpointPair* dst, const Block128& blockData, const deUint32* endpointModes, int numPartitions, int numColorEndpointValues, const ISEParams& iseParams, int numBitsAvailable)
-{
-	const int			colorEndpointDataStart = numPartitions == 1 ? 17 : 29;
-	ISEDecodedResult	colorEndpointData[18];
-	{
-		BitAccessStream dataStream(blockData, colorEndpointDataStart, numBitsAvailable, true);
-		decodeISE(&colorEndpointData[0], numColorEndpointValues, dataStream, iseParams);
-	}
-	{
-		deUint32 unquantizedEndpoints[18];
-		unquantizeColorEndpoints(&unquantizedEndpoints[0], &colorEndpointData[0], numColorEndpointValues, iseParams);
-		decodeColorEndpoints(dst, &unquantizedEndpoints[0], &endpointModes[0], numPartitions);
-	}
-}
-void unquantizeWeights (deUint32 dst[64], const ISEDecodedResult* weightGrid, const ASTCBlockMode& blockMode)
-{
-	const int			numWeights	= computeNumWeights(blockMode);
-	const ISEParams&	iseParams	= blockMode.weightISEParams;
-	if (iseParams.mode == ISEMODE_TRIT || iseParams.mode == ISEMODE_QUINT)
-	{
-		const int rangeCase = iseParams.numBits*2 + (iseParams.mode == ISEMODE_QUINT ? 1 : 0);
-		if (rangeCase == 0 || rangeCase == 1)
-		{
-			static const deUint32 map0[3]	= { 0, 32, 63 };
-			static const deUint32 map1[5]	= { 0, 16, 32, 47, 63 };
-			const deUint32* const map		= rangeCase == 0 ? &map0[0] : &map1[0];
-			for (int i = 0; i < numWeights; i++)
-			{
-				DE_ASSERT(weightGrid[i].v < (rangeCase == 0 ? 3u : 5u));
-				dst[i] = map[weightGrid[i].v];
-			}
-		}
-		else
-		{
-			DE_ASSERT(rangeCase <= 6);
-			static const deUint32	Ca[5]	= { 50, 28, 23, 13, 11 };
-			const deUint32			C		= Ca[rangeCase-2];
-			for (int weightNdx = 0; weightNdx < numWeights; weightNdx++)
-			{
-				const deUint32 a = getBit(weightGrid[weightNdx].m, 0);
-				const deUint32 b = getBit(weightGrid[weightNdx].m, 1);
-				const deUint32 c = getBit(weightGrid[weightNdx].m, 2);
-				const deUint32 A = a == 0 ? 0 : (1<<7)-1;
-				const deUint32 B = rangeCase == 2 ? 0
-								 : rangeCase == 3 ? 0
-								 : rangeCase == 4 ? (b << 6) |					(b << 2) |				(b << 0)
-								 : rangeCase == 5 ? (b << 6) |								(b << 1)
-								 : rangeCase == 6 ? (c << 6) | (b << 5) |					(c << 1) |	(b << 0)
-								 : (deUint32)-1;
-				dst[weightNdx] = (((weightGrid[weightNdx].tq*C + B) ^ A) >> 2) | (A & 0x20);
-			}
-		}
-	}
-	else
-	{
-		DE_ASSERT(iseParams.mode == ISEMODE_PLAIN_BIT);
-		for (int weightNdx = 0; weightNdx < numWeights; weightNdx++)
-			dst[weightNdx] = bitReplicationScale(weightGrid[weightNdx].v, iseParams.numBits, 6);
-	}
-	for (int weightNdx = 0; weightNdx < numWeights; weightNdx++)
-		dst[weightNdx] += dst[weightNdx] > 32 ? 1 : 0;
-	// Initialize nonexistent weights to poison values
-	for (int weightNdx = numWeights; weightNdx < 64; weightNdx++)
-		dst[weightNdx] = ~0u;
-}
-void interpolateWeights (TexelWeightPair* dst, const deUint32 (&unquantizedWeights) [64], int blockWidth, int blockHeight, const ASTCBlockMode& blockMode)
-{
-	const int		numWeightsPerTexel	= blockMode.isDualPlane ? 2 : 1;
-	const deUint32	scaleX				= (1024 + blockWidth/2) / (blockWidth-1);
-	const deUint32	scaleY				= (1024 + blockHeight/2) / (blockHeight-1);
-	DE_ASSERT(blockMode.weightGridWidth*blockMode.weightGridHeight*numWeightsPerTexel <= (int)DE_LENGTH_OF_ARRAY(unquantizedWeights));
-	for (int texelY = 0; texelY < blockHeight; texelY++)
-	{
-		for (int texelX = 0; texelX < blockWidth; texelX++)
-		{
-			const deUint32 gX	= (scaleX*texelX*(blockMode.weightGridWidth-1) + 32) >> 6;
-			const deUint32 gY	= (scaleY*texelY*(blockMode.weightGridHeight-1) + 32) >> 6;
-			const deUint32 jX	= gX >> 4;
-			const deUint32 jY	= gY >> 4;
-			const deUint32 fX	= gX & 0xf;
-			const deUint32 fY	= gY & 0xf;
-			const deUint32 w11	= (fX*fY + 8) >> 4;
-			const deUint32 w10	= fY - w11;
-			const deUint32 w01	= fX - w11;
-			const deUint32 w00	= 16 - fX - fY + w11;
-			const deUint32 i00	= jY*blockMode.weightGridWidth + jX;
-			const deUint32 i01	= i00 + 1;
-			const deUint32 i10	= i00 + blockMode.weightGridWidth;
-			const deUint32 i11	= i00 + blockMode.weightGridWidth + 1;
-			// These addresses can be out of bounds, but respective weights will be 0 then.
-			DE_ASSERT(deInBounds32(i00, 0, blockMode.weightGridWidth*blockMode.weightGridHeight) || w00 == 0);
-			DE_ASSERT(deInBounds32(i01, 0, blockMode.weightGridWidth*blockMode.weightGridHeight) || w01 == 0);
-			DE_ASSERT(deInBounds32(i10, 0, blockMode.weightGridWidth*blockMode.weightGridHeight) || w10 == 0);
-			DE_ASSERT(deInBounds32(i11, 0, blockMode.weightGridWidth*blockMode.weightGridHeight) || w11 == 0);
-			for (int texelWeightNdx = 0; texelWeightNdx < numWeightsPerTexel; texelWeightNdx++)
-			{
-				// & 0x3f clamps address to bounds of unquantizedWeights
-				const deUint32 p00	= unquantizedWeights[(i00 * numWeightsPerTexel + texelWeightNdx) & 0x3f];
-				const deUint32 p01	= unquantizedWeights[(i01 * numWeightsPerTexel + texelWeightNdx) & 0x3f];
-				const deUint32 p10	= unquantizedWeights[(i10 * numWeightsPerTexel + texelWeightNdx) & 0x3f];
-				const deUint32 p11	= unquantizedWeights[(i11 * numWeightsPerTexel + texelWeightNdx) & 0x3f];
-				dst[texelY*blockWidth + texelX].w[texelWeightNdx] = (p00*w00 + p01*w01 + p10*w10 + p11*w11 + 8) >> 4;
-			}
-		}
-	}
-}
-void computeTexelWeights (TexelWeightPair* dst, const Block128& blockData, int blockWidth, int blockHeight, const ASTCBlockMode& blockMode)
-{
-	ISEDecodedResult weightGrid[64];
-	{
-		BitAccessStream dataStream(blockData, 127, computeNumRequiredBits(blockMode.weightISEParams, computeNumWeights(blockMode)), false);
-		decodeISE(&weightGrid[0], computeNumWeights(blockMode), dataStream, blockMode.weightISEParams);
-	}
-	{
-		deUint32 unquantizedWeights[64];
-		unquantizeWeights(&unquantizedWeights[0], &weightGrid[0], blockMode);
-		interpolateWeights(dst, unquantizedWeights, blockWidth, blockHeight, blockMode);
-	}
-}
-inline deUint32 hash52 (deUint32 v)
-{
-	deUint32 p = v;
-	p ^= p >> 15;	p -= p << 17;	p += p << 7;	p += p << 4;
-	p ^= p >>  5;	p += p << 16;	p ^= p >> 7;	p ^= p >> 3;
-	p ^= p <<  6;	p ^= p >> 17;
-	return p;
-}
-int computeTexelPartition (deUint32 seedIn, deUint32 xIn, deUint32 yIn, deUint32 zIn, int numPartitions, bool smallBlock)
-{
-	DE_ASSERT(zIn == 0);
-	const deUint32	x		= smallBlock ? xIn << 1 : xIn;
-	const deUint32	y		= smallBlock ? yIn << 1 : yIn;
-	const deUint32	z		= smallBlock ? zIn << 1 : zIn;
-	const deUint32	seed	= seedIn + 1024*(numPartitions-1);
-	const deUint32	rnum	= hash52(seed);
-	deUint8			seed1	= (deUint8)( rnum							& 0xf);
-	deUint8			seed2	= (deUint8)((rnum >>  4)					& 0xf);
-	deUint8			seed3	= (deUint8)((rnum >>  8)					& 0xf);
-	deUint8			seed4	= (deUint8)((rnum >> 12)					& 0xf);
-	deUint8			seed5	= (deUint8)((rnum >> 16)					& 0xf);
-	deUint8			seed6	= (deUint8)((rnum >> 20)					& 0xf);
-	deUint8			seed7	= (deUint8)((rnum >> 24)					& 0xf);
-	deUint8			seed8	= (deUint8)((rnum >> 28)					& 0xf);
-	deUint8			seed9	= (deUint8)((rnum >> 18)					& 0xf);
-	deUint8			seed10	= (deUint8)((rnum >> 22)					& 0xf);
-	deUint8			seed11	= (deUint8)((rnum >> 26)					& 0xf);
-	deUint8			seed12	= (deUint8)(((rnum >> 30) | (rnum << 2))	& 0xf);
-	seed1  = (deUint8)(seed1  * seed1 );
-	seed2  = (deUint8)(seed2  * seed2 );
-	seed3  = (deUint8)(seed3  * seed3 );
-	seed4  = (deUint8)(seed4  * seed4 );
-	seed5  = (deUint8)(seed5  * seed5 );
-	seed6  = (deUint8)(seed6  * seed6 );
-	seed7  = (deUint8)(seed7  * seed7 );
-	seed8  = (deUint8)(seed8  * seed8 );
-	seed9  = (deUint8)(seed9  * seed9 );
-	seed10 = (deUint8)(seed10 * seed10);
-	seed11 = (deUint8)(seed11 * seed11);
-	seed12 = (deUint8)(seed12 * seed12);
-	const int shA = (seed & 2) != 0		? 4		: 5;
-	const int shB = numPartitions == 3	? 6		: 5;
-	const int sh1 = (seed & 1) != 0		? shA	: shB;
-	const int sh2 = (seed & 1) != 0		? shB	: shA;
-	const int sh3 = (seed & 0x10) != 0	? sh1	: sh2;
-	seed1  = (deUint8)(seed1  >> sh1);
-	seed2  = (deUint8)(seed2  >> sh2);
-	seed3  = (deUint8)(seed3  >> sh1);
-	seed4  = (deUint8)(seed4  >> sh2);
-	seed5  = (deUint8)(seed5  >> sh1);
-	seed6  = (deUint8)(seed6  >> sh2);
-	seed7  = (deUint8)(seed7  >> sh1);
-	seed8  = (deUint8)(seed8  >> sh2);
-	seed9  = (deUint8)(seed9  >> sh3);
-	seed10 = (deUint8)(seed10 >> sh3);
-	seed11 = (deUint8)(seed11 >> sh3);
-	seed12 = (deUint8)(seed12 >> sh3);
-	const int a =						0x3f & (seed1*x + seed2*y + seed11*z + (rnum >> 14));
-	const int b =						0x3f & (seed3*x + seed4*y + seed12*z + (rnum >> 10));
-	const int c = numPartitions >= 3 ?	0x3f & (seed5*x + seed6*y + seed9*z  + (rnum >>  6))	: 0;
-	const int d = numPartitions >= 4 ?	0x3f & (seed7*x + seed8*y + seed10*z + (rnum >>  2))	: 0;
-	return a >= b && a >= c && a >= d	? 0
-		 : b >= c && b >= d				? 1
-		 : c >= d						? 2
-		 :								  3;
-}
-DecompressResult setTexelColors (void* dst, ColorEndpointPair* colorEndpoints, TexelWeightPair* texelWeights, int ccs, deUint32 partitionIndexSeed,
-								 int numPartitions, int blockWidth, int blockHeight, bool isSRGB, bool isLDRMode, const deUint32* colorEndpointModes)
-{
-	const bool			smallBlock	= blockWidth*blockHeight < 31;
-	DecompressResult	result		= DECOMPRESS_RESULT_VALID_BLOCK;
-	bool				isHDREndpoint[4];
-	for (int i = 0; i < numPartitions; i++)
-	{
-		isHDREndpoint[i] = isColorEndpointModeHDR(colorEndpointModes[i]);
-		
-		// rg - REMOVING HDR SUPPORT FOR NOW
-		if (isHDREndpoint[i])
-			return DECOMPRESS_RESULT_ERROR;
-	}
-
-	for (int texelY = 0; texelY < blockHeight; texelY++)
-	for (int texelX = 0; texelX < blockWidth; texelX++)
-	{
-		const int				texelNdx			= texelY*blockWidth + texelX;
-		const int				colorEndpointNdx	= numPartitions == 1 ? 0 : computeTexelPartition(partitionIndexSeed, texelX, texelY, 0, numPartitions, smallBlock);
-		DE_ASSERT(colorEndpointNdx < numPartitions);
-		const UVec4&			e0					= colorEndpoints[colorEndpointNdx].e0;
-		const UVec4&			e1					= colorEndpoints[colorEndpointNdx].e1;
-		const TexelWeightPair&	weight				= texelWeights[texelNdx];
-		if (isLDRMode && isHDREndpoint[colorEndpointNdx])
-		{
-			if (isSRGB)
-			{
-				((deUint8*)dst)[texelNdx*4 + 0] = 0xff;
-				((deUint8*)dst)[texelNdx*4 + 1] = 0;
-				((deUint8*)dst)[texelNdx*4 + 2] = 0xff;
-				((deUint8*)dst)[texelNdx*4 + 3] = 0xff;
-			}
-			else
-			{
-				((float*)dst)[texelNdx*4 + 0] = 1.0f;
-				((float*)dst)[texelNdx*4 + 1] = 0;
-				((float*)dst)[texelNdx*4 + 2] = 1.0f;
-				((float*)dst)[texelNdx*4 + 3] = 1.0f;
-			}
-			result = DECOMPRESS_RESULT_ERROR;
-		}
-		else
-		{
-			for (int channelNdx = 0; channelNdx < 4; channelNdx++)
-			{
-				if (!isHDREndpoint[colorEndpointNdx] || (channelNdx == 3 && colorEndpointModes[colorEndpointNdx] == 14)) // \note Alpha for mode 14 is treated the same as LDR.
-				{
-					const deUint32 c0	= (e0[channelNdx] << 8) | (isSRGB ? 0x80 : e0[channelNdx]);
-					const deUint32 c1	= (e1[channelNdx] << 8) | (isSRGB ? 0x80 : e1[channelNdx]);
-					const deUint32 w	= weight.w[ccs == channelNdx ? 1 : 0];
-					const deUint32 c	= (c0*(64-w) + c1*w + 32) / 64;
-					if (isSRGB)
-						((deUint8*)dst)[texelNdx*4 + channelNdx] = (deUint8)((c & 0xff00) >> 8);
-					else
-						((float*)dst)[texelNdx*4 + channelNdx] = c == 65535 ? 1.0f : (float)c / 65536.0f;
-				}
-				else
-				{
-					//DE_STATIC_ASSERT((basisu_astc::meta::TypesSame<deFloat16, deUint16>::Value));
-					// rg - REMOVING HDR SUPPORT FOR NOW
-#if 0
-					const deUint32		c0	= e0[channelNdx] << 4;
-					const deUint32		c1	= e1[channelNdx] << 4;
-					const deUint32		w	= weight.w[ccs == channelNdx ? 1 : 0];
-					const deUint32		c	= (c0*(64-w) + c1*w + 32) / 64;
-					const deUint32		e	= getBits(c, 11, 15);
-					const deUint32		m	= getBits(c, 0, 10);
-					const deUint32		mt	= m < 512		? 3*m
-											: m >= 1536		? 5*m - 2048
-											:				  4*m - 512;
-					const deFloat16		cf	= (deFloat16)((e << 10) + (mt >> 3));
-					((float*)dst)[texelNdx*4 + channelNdx] = deFloat16To32(isFloat16InfOrNan(cf) ? 0x7bff : cf);
-#endif
-				}
-			}
-		}
-	}
-	return result;
-}
-DecompressResult decompressBlock (void* dst, const Block128& blockData, int blockWidth, int blockHeight, bool isSRGB, bool isLDR)
-{
-	DE_ASSERT(isLDR || !isSRGB);
-	// Decode block mode.
-	const ASTCBlockMode blockMode = getASTCBlockMode(blockData.getBits(0, 10));
-	// Check for block mode errors.
-	if (blockMode.isError)
-	{
-		setASTCErrorColorBlock(dst, blockWidth, blockHeight, isSRGB);
-		return DECOMPRESS_RESULT_ERROR;
-	}
-	// Separate path for void-extent.
-	if (blockMode.isVoidExtent)
-		return decodeVoidExtentBlock(dst, blockData, blockWidth, blockHeight, isSRGB, isLDR);
-	// Compute weight grid values.
-	const int numWeights			= computeNumWeights(blockMode);
-	const int numWeightDataBits		= computeNumRequiredBits(blockMode.weightISEParams, numWeights);
-	const int numPartitions			= (int)blockData.getBits(11, 12) + 1;
-	// Check for errors in weight grid, partition and dual-plane parameters.
-	if (numWeights > 64								||
-		numWeightDataBits > 96						||
-		numWeightDataBits < 24						||
-		blockMode.weightGridWidth > blockWidth		||
-		blockMode.weightGridHeight > blockHeight	||
-		(numPartitions == 4 && blockMode.isDualPlane))
-	{
-		setASTCErrorColorBlock(dst, blockWidth, blockHeight, isSRGB);
-		return DECOMPRESS_RESULT_ERROR;
-	}
-	// Compute number of bits available for color endpoint data.
-	const bool	isSingleUniqueCem			= numPartitions == 1 || blockData.getBits(23, 24) == 0;
-	const int	numConfigDataBits			= (numPartitions == 1 ? 17 : isSingleUniqueCem ? 29 : 25 + 3*numPartitions) +
-											  (blockMode.isDualPlane ? 2 : 0);
-	const int	numBitsForColorEndpoints	= 128 - numWeightDataBits - numConfigDataBits;
-	const int	extraCemBitsStart			= 127 - numWeightDataBits - (isSingleUniqueCem		? -1
-																		: numPartitions == 4	? 7
-																		: numPartitions == 3	? 4
-																		: numPartitions == 2	? 1
-																		: 0);
-	// Decode color endpoint modes.
-	deUint32 colorEndpointModes[4];
-	decodeColorEndpointModes(&colorEndpointModes[0], blockData, numPartitions, extraCemBitsStart);
-	const int numColorEndpointValues = computeNumColorEndpointValues(colorEndpointModes, numPartitions);
-	// Check for errors in color endpoint value count.
-	if (numColorEndpointValues > 18 || numBitsForColorEndpoints < (int)deDivRoundUp32(13*numColorEndpointValues, 5))
-	{
-		setASTCErrorColorBlock(dst, blockWidth, blockHeight, isSRGB);
-		return DECOMPRESS_RESULT_ERROR;
-	}
-	// Compute color endpoints.
-	ColorEndpointPair colorEndpoints[4];
-	computeColorEndpoints(&colorEndpoints[0], blockData, &colorEndpointModes[0], numPartitions, numColorEndpointValues,
-						  computeMaximumRangeISEParams(numBitsForColorEndpoints, numColorEndpointValues), numBitsForColorEndpoints);
-	// Compute texel weights.
-	TexelWeightPair texelWeights[MAX_BLOCK_WIDTH*MAX_BLOCK_HEIGHT];
-	computeTexelWeights(&texelWeights[0], blockData, blockWidth, blockHeight, blockMode);
-	// Set texel colors.
-	const int		ccs						= blockMode.isDualPlane ? (int)blockData.getBits(extraCemBitsStart-2, extraCemBitsStart-1) : -1;
-	const deUint32	partitionIndexSeed		= numPartitions > 1 ? blockData.getBits(13, 22) : (deUint32)-1;
-	return setTexelColors(dst, &colorEndpoints[0], &texelWeights[0], ccs, partitionIndexSeed, numPartitions, blockWidth, blockHeight, isSRGB, isLDR, &colorEndpointModes[0]);
-}
-
-} // anonymous
-
-bool decompress(uint8_t *pDst, const uint8_t * data, bool isSRGB, int blockWidth, int blockHeight)
-{
-	// rg - We only support LDR here, although adding back in HDR would be easy.
-	const bool isLDR = true;
-	DE_ASSERT(isLDR || !isSRGB);
-	
-	float linear[MAX_BLOCK_WIDTH * MAX_BLOCK_HEIGHT * 4];
-
-	const Block128 blockData(data);
-	if (decompressBlock(isSRGB ? (void*)pDst : (void*)& linear[0],
-		blockData, blockWidth, blockHeight, isSRGB, isLDR) != DECOMPRESS_RESULT_VALID_BLOCK)
-		return false;
-	
-	if (!isSRGB)
-	{
-		int pix = 0;
-		for (int i = 0; i < blockHeight; i++)
-		{
-			for (int j = 0; j < blockWidth; j++, pix++)
-			{
-				pDst[4 * pix + 0] = (uint8_t)(basisu_astc::clamp<int>((int)(linear[pix * 4 + 0] * 65536.0f + .5f), 0, 65535) >> 8);
-				pDst[4 * pix + 1] = (uint8_t)(basisu_astc::clamp<int>((int)(linear[pix * 4 + 1] * 65536.0f + .5f), 0, 65535) >> 8);
-				pDst[4 * pix + 2] = (uint8_t)(basisu_astc::clamp<int>((int)(linear[pix * 4 + 2] * 65536.0f + .5f), 0, 65535) >> 8);
-				pDst[4 * pix + 3] = (uint8_t)(basisu_astc::clamp<int>((int)(linear[pix * 4 + 3] * 65536.0f + .5f), 0, 65535) >> 8);
-			}
-		}
-	}
-
-	return true;
-}
-
-} // astc
-} // basisu_astc
-
-#if defined(__GNUC__)
-#pragma GCC diagnostic pop
-#endif
diff --git a/encoder/basisu_astc_decomp.h b/encoder/basisu_astc_decomp.h
deleted file mode 100644
index 9ec2e46..0000000
--- a/encoder/basisu_astc_decomp.h
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef _TCUASTCUTIL_HPP
-#define _TCUASTCUTIL_HPP
-/*-------------------------------------------------------------------------
- * drawElements Quality Program Tester Core
- * ----------------------------------------
- *
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *//*!
- * \file
- * \brief ASTC Utilities.
- *//*--------------------------------------------------------------------*/
-
-#include "../transcoder/basisu.h" // to pick up the iterator debug level madness
-#include <vector>
-#include <stdint.h>
-
-namespace basisu_astc
-{
-namespace astc
-{
-
-// Unpacks a single ASTC block to pDst
-// If isSRGB is true, the spec requires the decoder to scale the LDR 8-bit endpoints to 16-bit before interpolation slightly differently, 
-// which will lead to different outputs. So be sure to set it correctly (ideally it should match whatever the encoder did).
-bool decompress(uint8_t* pDst, const uint8_t* data, bool isSRGB, int blockWidth, int blockHeight);
-
-} // astc
-} // basisu
-
-#endif
diff --git a/encoder/basisu_backend.cpp b/encoder/basisu_backend.cpp
index 19911fc..abb6175 100644
--- a/encoder/basisu_backend.cpp
+++ b/encoder/basisu_backend.cpp
@@ -49,22 +49,17 @@
 		m_output.clear();
 	}
 
-	void basisu_backend::init(basisu_frontend* pFront_end, basisu_backend_params& params, const basisu_backend_slice_desc_vec& slice_descs, const basist::etc1_global_selector_codebook* pGlobal_sel_codebook)
+	void basisu_backend::init(basisu_frontend* pFront_end, basisu_backend_params& params, const basisu_backend_slice_desc_vec& slice_descs)
 	{
 		m_pFront_end = pFront_end;
 		m_params = params;
 		m_slices = slice_descs;
-		m_pGlobal_sel_codebook = pGlobal_sel_codebook;
-
-		debug_printf("basisu_backend::Init: Slices: %u, ETC1S: %u, EndpointRDOQualityThresh: %f, SelectorRDOQualityThresh: %f, UseGlobalSelCodebook: %u, GlobalSelCodebookPalBits: %u, GlobalSelCodebookModBits: %u, Use hybrid selector codebooks: %u\n",
+		
+		debug_printf("basisu_backend::Init: Slices: %u, ETC1S: %u, EndpointRDOQualityThresh: %f, SelectorRDOQualityThresh: %f\n",
 			m_slices.size(),
 			params.m_etc1s,
 			params.m_endpoint_rdo_quality_thresh,
-			params.m_selector_rdo_quality_thresh,
-			params.m_use_global_sel_codebook,
-			params.m_global_sel_codebook_pal_bits,
-			params.m_global_sel_codebook_mod_bits,
-			params.m_use_hybrid_sel_codebooks);
+			params.m_selector_rdo_quality_thresh);
 
 		debug_printf("Frontend endpoints: %u selectors: %u\n", m_pFront_end->get_total_endpoint_clusters(), m_pFront_end->get_total_selector_clusters());
 
@@ -106,63 +101,17 @@
 
 		m_selector_palette.resize(r.get_total_selector_clusters());
 
-		if (m_params.m_use_global_sel_codebook)
+		for (uint32_t i = 0; i < r.get_total_selector_clusters(); i++)
 		{
-			m_global_selector_palette_desc.resize(r.get_total_selector_clusters());
+			etc1_selector_palette_entry& s = m_selector_palette[i];
 
-			for (int i = 0; i < static_cast<int>(r.get_total_selector_clusters()); i++)
+			const etc_block& selector_bits = r.get_selector_cluster_selector_bits(i);
+
+			for (uint32_t y = 0; y < 4; y++)
 			{
-				basist::etc1_selector_palette_entry& selector_pal_entry = m_selector_palette[i];
-
-				etc1_global_selector_cb_entry_desc& pal_entry_desc = m_global_selector_palette_desc[i];
-				pal_entry_desc.m_pal_index = r.get_selector_cluster_global_selector_entry_ids()[i].m_palette_index;
-				pal_entry_desc.m_mod_index = r.get_selector_cluster_global_selector_entry_ids()[i].m_modifier.get_index();
-
-				pal_entry_desc.m_was_used = true;
-				if (m_params.m_use_hybrid_sel_codebooks)
-					pal_entry_desc.m_was_used = r.get_selector_cluster_uses_global_cb_vec()[i];
-
-				if (pal_entry_desc.m_was_used)
+				for (uint32_t x = 0; x < 4; x++)
 				{
-					const etc_block& selector_bits = r.get_selector_cluster_selector_bits(i);
-					(void)selector_bits;
-
-					basist::etc1_selector_palette_entry global_pal_entry(m_pGlobal_sel_codebook->get_entry(r.get_selector_cluster_global_selector_entry_ids()[i]));
-
-					for (uint32_t y = 0; y < 4; y++)
-					{
-						for (uint32_t x = 0; x < 4; x++)
-						{
-							selector_pal_entry(x, y) = global_pal_entry(x, y);
-
-							assert(selector_bits.get_selector(x, y) == global_pal_entry(x, y));
-						}
-					}
-				}
-				else
-				{
-					const etc_block& selector_bits = r.get_selector_cluster_selector_bits(i);
-
-					for (uint32_t y = 0; y < 4; y++)
-						for (uint32_t x = 0; x < 4; x++)
-							selector_pal_entry[y * 4 + x] = static_cast<uint8_t>(selector_bits.get_selector(x, y));
-				}
-			}
-		}
-		else
-		{
-			for (uint32_t i = 0; i < r.get_total_selector_clusters(); i++)
-			{
-				basist::etc1_selector_palette_entry& s = m_selector_palette[i];
-
-				const etc_block& selector_bits = r.get_selector_cluster_selector_bits(i);
-
-				for (uint32_t y = 0; y < 4; y++)
-				{
-					for (uint32_t x = 0; x < 4; x++)
-					{
-						s[y * 4 + x] = static_cast<uint8_t>(selector_bits.get_selector(x, y));
-					}
+					s[y * 4 + x] = static_cast<uint8_t>(selector_bits.get_selector(x, y));
 				}
 			}
 		}
@@ -388,6 +337,8 @@
 		if (!is_video)
 			return;
 
+		debug_printf("basisu_backend::check_for_valid_cr_blocks\n");
+
 		uint32_t total_crs = 0;
 		uint32_t total_invalid_crs = 0;
 
@@ -454,6 +405,11 @@
 
 	void basisu_backend::create_encoder_blocks()
 	{
+		debug_printf("basisu_backend::create_encoder_blocks\n");
+
+		interval_timer tm;
+		tm.start();
+
 		basisu_frontend& r = *m_pFront_end;
 		const bool is_video = r.get_params().m_tex_type == basist::cBASISTexTypeVideoFrames;
 
@@ -565,6 +521,7 @@
 							{
 								if ((is_video) && (endpoint_pred == basist::CR_ENDPOINT_PRED_INDEX))
 									continue;
+
 								int pred_block_x = block_x + g_endpoint_preds[endpoint_pred].m_dx;
 								if ((pred_block_x < 0) || (pred_block_x >= (int)num_blocks_x))
 									continue;
@@ -586,11 +543,23 @@
 								unpack_etc1(trial_etc_block, trial_colors);
 
 								uint64_t trial_err = 0;
-								for (uint32_t p = 0; p < 16; p++)
+								if (r.get_params().m_perceptual)
 								{
-									trial_err += color_distance(r.get_params().m_perceptual, src_pixels.get_ptr()[p], trial_colors[p], false);
-									if (trial_err > thresh_err)
-										break;
+									for (uint32_t p = 0; p < 16; p++)
+									{
+										trial_err += color_distance(true, src_pixels.get_ptr()[p], trial_colors[p], false);
+										if (trial_err > thresh_err)
+											break;
+									}
+								}
+								else
+								{
+									for (uint32_t p = 0; p < 16; p++)
+									{
+										trial_err += color_distance(false, src_pixels.get_ptr()[p], trial_colors[p], false);
+										if (trial_err > thresh_err)
+											break;
+									}
 								}
 
 								if (trial_err <= thresh_err)
@@ -643,6 +612,8 @@
 
 		sort_selector_codebook();
 		check_for_valid_cr_blocks();
+		
+		debug_printf("Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
 	}
 
 	void basisu_backend::compute_slice_crcs()
@@ -670,7 +641,9 @@
 						etc_block& output_block = *(etc_block*)gi.get_block_ptr(block_x, block_y);
 
 						output_block.set_diff_bit(true);
-						output_block.set_flip_bit(true);
+						// Setting the flip bit to false to be compatible with the Khronos KDFS.
+						//output_block.set_flip_bit(true);
+						output_block.set_flip_bit(false);
 
 						const uint32_t endpoint_index = m.m_endpoint_index;
 
@@ -679,7 +652,7 @@
 
 						const uint32_t selector_idx = m.m_selector_index;
 
-						const basist::etc1_selector_palette_entry& selectors = m_selector_palette[selector_idx];
+						const etc1_selector_palette_entry& selectors = m_selector_palette[selector_idx];
 						for (uint32_t sy = 0; sy < 4; sy++)
 							for (uint32_t sx = 0; sx < 4; sx++)
 								output_block.set_selector(sx, sy, selectors(sx, sy));
@@ -707,6 +680,9 @@
 		} // slice_index
 	}
 
+	//uint32_t g_color_delta_hist[255 * 3 + 1];
+	//uint32_t g_color_delta_bad_hist[255 * 3 + 1];
+		
 	// TODO: Split this into multiple methods.
 	bool basisu_backend::encode_image()
 	{
@@ -737,6 +713,12 @@
 
 		uint_vec block_endpoint_indices, block_selector_indices;
 
+		interval_timer tm;
+		tm.start();
+
+		const int COLOR_DELTA_THRESH = 8;
+		const int SEL_DIFF_THRESHOLD = 11;
+		
 		for (uint32_t slice_index = 0; slice_index < m_slices.size(); slice_index++)
 		{
 			//const int prev_frame_slice_index = is_video ? find_video_frame(slice_index, -1) : -1;
@@ -782,7 +764,7 @@
 
 				}  // block_x
 			} // block_y
-
+						
 			for (uint32_t block_y = 0; block_y < num_blocks_y; block_y++)
 			{
 				for (uint32_t block_x = 0; block_x < num_blocks_x; block_x++)
@@ -857,68 +839,170 @@
 							etc_block etc_blk(r.get_output_block(block_index));
 
 							const uint64_t cur_err = etc_blk.evaluate_etc1_error(src_pixels.get_ptr(), r.get_params().m_perceptual);
+							const uint32_t cur_inten5 = etc_blk.get_inten_table(0);
 
+							const etc1_endpoint_palette_entry& cur_endpoints = m_endpoint_palette[m.m_endpoint_index];
+														
 							if (cur_err)
 							{
 								const float endpoint_remap_thresh = maximum(1.0f, m_params.m_endpoint_rdo_quality_thresh);
 								const uint64_t thresh_err = (uint64_t)(cur_err * endpoint_remap_thresh);
 
-								uint64_t best_trial_err = UINT64_MAX;
-								int best_trial_idx = 0;
+								//const int MAX_ENDPOINT_SEARCH_DIST = (m_params.m_compression_level >= 2) ? 64 : 32;
+								const int MAX_ENDPOINT_SEARCH_DIST = (m_params.m_compression_level >= 2) ? 64 : 16;
 
-								etc_block trial_etc_blk(etc_blk);
-
-								const int MAX_ENDPOINT_SEARCH_DIST = 32;
-								const int search_dist = minimum<int>(iabs(endpoint_delta) - 1, MAX_ENDPOINT_SEARCH_DIST);
-								for (int d = -search_dist; d < search_dist; d++)
+								if (!g_cpu_supports_sse41)
 								{
-									int trial_idx = prev_endpoint_index + d;
-									if (trial_idx < 0)
-										trial_idx += (int)r.get_total_endpoint_clusters();
-									else if (trial_idx >= (int)r.get_total_endpoint_clusters())
-										trial_idx -= (int)r.get_total_endpoint_clusters();
+									const uint64_t initial_best_trial_err = UINT64_MAX;
+									uint64_t best_trial_err = initial_best_trial_err;
+									int best_trial_idx = 0;
 
-									if (trial_idx == new_endpoint_index)
-										continue;
-
-									// Skip it if this new endpoint palette entry is actually never used.
-									if (!m_new_endpoint_was_used[trial_idx])
-										continue;
-
-									const etc1_endpoint_palette_entry& p = m_endpoint_palette[m_endpoint_remap_table_new_to_old[trial_idx]];
-									trial_etc_blk.set_block_color5_etc1s(p.m_color5);
-									trial_etc_blk.set_inten_tables_etc1s(p.m_inten5);
-
-									uint64_t trial_err = trial_etc_blk.evaluate_etc1_error(src_pixels.get_ptr(), r.get_params().m_perceptual);
-
-									if (trial_err <= thresh_err)
+									etc_block trial_etc_blk(etc_blk);
+																		
+									const int search_dist = minimum<int>(iabs(endpoint_delta) - 1, MAX_ENDPOINT_SEARCH_DIST);
+									for (int d = -search_dist; d < search_dist; d++)
 									{
-										if (trial_err < best_trial_err)
+										int trial_idx = prev_endpoint_index + d;
+										if (trial_idx < 0)
+											trial_idx += (int)r.get_total_endpoint_clusters();
+										else if (trial_idx >= (int)r.get_total_endpoint_clusters())
+											trial_idx -= (int)r.get_total_endpoint_clusters();
+
+										if (trial_idx == new_endpoint_index)
+											continue;
+
+										// Skip it if this new endpoint palette entry is actually never used.
+										if (!m_new_endpoint_was_used[trial_idx])
+											continue;
+
+										const etc1_endpoint_palette_entry& p = m_endpoint_palette[m_endpoint_remap_table_new_to_old[trial_idx]];
+																				
+										if (m_params.m_compression_level <= 1)
+										{
+											if (p.m_inten5 > cur_inten5)
+												continue;
+
+											int delta_r = iabs(cur_endpoints.m_color5.r - p.m_color5.r);
+											int delta_g = iabs(cur_endpoints.m_color5.g - p.m_color5.g);
+											int delta_b = iabs(cur_endpoints.m_color5.b - p.m_color5.b);
+											int color_delta = delta_r + delta_g + delta_b;
+																						
+											if (color_delta > COLOR_DELTA_THRESH)
+												continue;
+										}
+
+										trial_etc_blk.set_block_color5_etc1s(p.m_color5);
+										trial_etc_blk.set_inten_tables_etc1s(p.m_inten5);
+
+										uint64_t trial_err = trial_etc_blk.evaluate_etc1_error(src_pixels.get_ptr(), r.get_params().m_perceptual);
+
+										if ((trial_err < best_trial_err) && (trial_err <= thresh_err))
 										{
 											best_trial_err = trial_err;
 											best_trial_idx = trial_idx;
 										}
 									}
-								}
 
-								if (best_trial_err != UINT64_MAX)
+									if (best_trial_err != initial_best_trial_err)
+									{
+										m.m_endpoint_index = m_endpoint_remap_table_new_to_old[best_trial_idx];
+
+										new_endpoint_index = best_trial_idx;
+
+										endpoint_delta = new_endpoint_index - prev_endpoint_index;
+
+										total_endpoint_indices_remapped++;
+									}
+								}
+								else
 								{
-									m.m_endpoint_index = m_endpoint_remap_table_new_to_old[best_trial_idx];
+#if BASISU_SUPPORT_SSE
+									uint8_t block_selectors[16];
+									for (uint32_t i = 0; i < 16; i++)
+										block_selectors[i] = (uint8_t)etc_blk.get_selector(i & 3, i >> 2);
 
-									new_endpoint_index = best_trial_idx;
+									const int64_t initial_best_trial_err = INT64_MAX;
+									int64_t best_trial_err = initial_best_trial_err;
+									int best_trial_idx = 0;
+																																				
+									const int search_dist = minimum<int>(iabs(endpoint_delta) - 1, MAX_ENDPOINT_SEARCH_DIST);
+									for (int d = -search_dist; d < search_dist; d++)
+									{
+										int trial_idx = prev_endpoint_index + d;
+										if (trial_idx < 0)
+											trial_idx += (int)r.get_total_endpoint_clusters();
+										else if (trial_idx >= (int)r.get_total_endpoint_clusters())
+											trial_idx -= (int)r.get_total_endpoint_clusters();
 
-									endpoint_delta = new_endpoint_index - prev_endpoint_index;
+										if (trial_idx == new_endpoint_index)
+											continue;
 
-									total_endpoint_indices_remapped++;
-								}
-							}
-						}
+										// Skip it if this new endpoint palette entry is actually never used.
+										if (!m_new_endpoint_was_used[trial_idx])
+											continue;
+
+										const etc1_endpoint_palette_entry& p = m_endpoint_palette[m_endpoint_remap_table_new_to_old[trial_idx]];
+																				
+										if (m_params.m_compression_level <= 1)
+										{
+											if (p.m_inten5 > cur_inten5)
+												continue;
+
+											int delta_r = iabs(cur_endpoints.m_color5.r - p.m_color5.r);
+											int delta_g = iabs(cur_endpoints.m_color5.g - p.m_color5.g);
+											int delta_b = iabs(cur_endpoints.m_color5.b - p.m_color5.b);
+											int color_delta = delta_r + delta_g + delta_b;
+											
+											if (color_delta > COLOR_DELTA_THRESH)
+												continue;
+										}
+
+										color_rgba block_colors[4];
+										etc_block::get_block_colors_etc1s(block_colors, p.m_color5, p.m_inten5);
+
+										int64_t trial_err;
+										if (r.get_params().m_perceptual)
+										{
+											perceptual_distance_rgb_4_N_sse41(&trial_err, block_selectors, block_colors, src_pixels.get_ptr(), 16, best_trial_err);
+										}
+										else
+										{
+											linear_distance_rgb_4_N_sse41(&trial_err, block_selectors, block_colors, src_pixels.get_ptr(), 16, best_trial_err);
+										}
+
+										//if (trial_err > thresh_err)
+										//	g_color_delta_bad_hist[color_delta]++;
+
+										if ((trial_err < best_trial_err) && (trial_err <= (int64_t)thresh_err))
+										{
+											best_trial_err = trial_err;
+											best_trial_idx = trial_idx;
+										}
+									}
+
+									if (best_trial_err != initial_best_trial_err)
+									{
+										m.m_endpoint_index = m_endpoint_remap_table_new_to_old[best_trial_idx];
+
+										new_endpoint_index = best_trial_idx;
+
+										endpoint_delta = new_endpoint_index - prev_endpoint_index;
+
+										total_endpoint_indices_remapped++;
+									}
+#endif // BASISU_SUPPORT_SSE
+								} // if (!g_cpu_supports_sse41)
+															
+							} // if (cur_err)
+
+						} // if ((m_params.m_endpoint_rdo_quality_thresh > 1.0f) && (iabs(endpoint_delta) > 1) && (!block_endpoints_are_referenced(block_x, block_y)))
 
 						if (endpoint_delta < 0)
 							endpoint_delta += (int)r.get_total_endpoint_clusters();
 
 						delta_endpoint_histogram.inc(endpoint_delta);
-					}
+
+					} // if (m.m_endpoint_predictor == basist::NO_ENDPOINT_PRED_INDEX)
 
 					block_endpoint_indices.push_back(m_endpoint_remap_table_new_to_old[new_endpoint_index]);
 
@@ -927,10 +1011,13 @@
 					if ((!is_video) || (m.m_endpoint_predictor != basist::CR_ENDPOINT_PRED_INDEX))
 					{
 						int new_selector_index = m_selector_remap_table_old_to_new[m.m_selector_index];
+												
+						const float selector_remap_thresh = maximum(1.0f, m_params.m_selector_rdo_quality_thresh); //2.5f;
 
 						int selector_history_buf_index = -1;
 
-						if (m.m_is_cr_target)
+						// At low comp levels this hurts compression a tiny amount, but is significantly faster so it's a good tradeoff.
+						if ((m.m_is_cr_target) || (m_params.m_compression_level <= 1))
 						{
 							for (uint32_t j = 0; j < selector_history_buf.size(); j++)
 							{
@@ -944,89 +1031,99 @@
 								}
 							}
 						}
-						else
+
+						// If the block is a CR target we can't override its selectors.
+						if ((!m.m_is_cr_target) && (selector_history_buf_index == -1))
 						{
 							const pixel_block& src_pixels = r.get_source_pixel_block(block_index);
 
-							const etc_block& etc_blk = r.get_output_block(block_index);
+							etc_block etc_blk = r.get_output_block(block_index);
 
-							color_rgba etc_blk_unpacked[16];
-							unpack_etc1(etc_blk, etc_blk_unpacked);
+							// This is new code - the initial release just used the endpoints from the frontend, which isn't correct/accurate.
+							const etc1_endpoint_palette_entry& q = m_endpoint_palette[m_endpoint_remap_table_new_to_old[new_endpoint_index]];
+							etc_blk.set_block_color5_etc1s(q.m_color5);
+							etc_blk.set_inten_tables_etc1s(q.m_inten5);
+
+							color_rgba block_colors[4];
+							etc_blk.get_block_colors(block_colors, 0);
+
+							const uint8_t* pCur_selectors = &m_selector_palette[m.m_selector_index][0];
 
 							uint64_t cur_err = 0;
 							if (r.get_params().m_perceptual)
 							{
 								for (uint32_t p = 0; p < 16; p++)
-									cur_err += color_distance(true, src_pixels.get_ptr()[p], etc_blk_unpacked[p], false);
+									cur_err += color_distance(true, src_pixels.get_ptr()[p], block_colors[pCur_selectors[p]], false);
 							}
 							else
 							{
 								for (uint32_t p = 0; p < 16; p++)
-									cur_err += color_distance(false, src_pixels.get_ptr()[p], etc_blk_unpacked[p], false);
+									cur_err += color_distance(false, src_pixels.get_ptr()[p], block_colors[pCur_selectors[p]], false);
 							}
-														
+							
+							const uint64_t limit_err = (uint64_t)ceilf(cur_err * selector_remap_thresh);
+
+							// Even if cur_err==limit_err, we still want to scan the history buffer because there may be equivalent entries that are cheaper to code.
+
 							uint64_t best_trial_err = UINT64_MAX;
 							int best_trial_idx = 0;
 							uint32_t best_trial_history_buf_idx = 0;
 
-							const float selector_remap_thresh = maximum(1.0f, m_params.m_selector_rdo_quality_thresh); //2.5f;
-							const bool use_strict_search = (m_params.m_compression_level == 0) && (selector_remap_thresh == 1.0f);
-
-							const uint64_t limit_err = (uint64_t)ceilf(cur_err * selector_remap_thresh);
-							
 							for (uint32_t j = 0; j < selector_history_buf.size(); j++)
 							{
 								const int trial_idx = selector_history_buf[j];
 
-								if (use_strict_search)
+								const uint8_t* pSelectors = &m_selector_palette[m_selector_remap_table_new_to_old[trial_idx]][0];
+
+								if (m_params.m_compression_level <= 1)
 								{
-									if (trial_idx == new_selector_index)
+									// Predict if evaluating the full color error would cause an early out, by summing the abs err of the selector indices.
+									int sel_diff = 0;
+									for (uint32_t p = 0; p < 16; p += 4)
 									{
-										best_trial_err = 0;
-										best_trial_idx = trial_idx;
-										best_trial_history_buf_idx = j;
-										break;
+										sel_diff += iabs(pCur_selectors[p + 0] - pSelectors[p + 0]);
+										sel_diff += iabs(pCur_selectors[p + 1] - pSelectors[p + 1]);
+										sel_diff += iabs(pCur_selectors[p + 2] - pSelectors[p + 2]);
+										sel_diff += iabs(pCur_selectors[p + 3] - pSelectors[p + 3]);
+										if (sel_diff >= SEL_DIFF_THRESHOLD)
+											break;
+									}
+									if (sel_diff >= SEL_DIFF_THRESHOLD)
+										continue;
+								}
+									
+								const uint64_t thresh_err = minimum(limit_err, best_trial_err);
+								uint64_t trial_err = 0;
+
+								// This tends to early out quickly, so SSE has a hard time competing.
+								if (r.get_params().m_perceptual)
+								{
+									for (uint32_t p = 0; p < 16; p++)
+									{
+										uint32_t sel = pSelectors[p];
+										trial_err += color_distance(true, src_pixels.get_ptr()[p], block_colors[sel], false);
+										if (trial_err > thresh_err)
+											break;
 									}
 								}
 								else
 								{
-									uint64_t trial_err = 0;
-									const uint64_t thresh_err = minimum(limit_err, best_trial_err);
-
-									color_rgba block_colors[4];
-									etc_blk.get_block_colors(block_colors, 0);
-
-									const uint8_t* pSelectors = &m_selector_palette[m_selector_remap_table_new_to_old[trial_idx]](0, 0);
-									
-									if (r.get_params().m_perceptual)
+									for (uint32_t p = 0; p < 16; p++)
 									{
-										for (uint32_t p = 0; p < 16; p++)
-										{
-											uint32_t sel = pSelectors[p];
-											trial_err += color_distance(true, src_pixels.get_ptr()[p], block_colors[sel], false);
-											if (trial_err > thresh_err)
-												break;
-										}
+										uint32_t sel = pSelectors[p];
+										trial_err += color_distance(false, src_pixels.get_ptr()[p], block_colors[sel], false);
+										if (trial_err > thresh_err)
+											break;
 									}
-									else
-									{
-										for (uint32_t p = 0; p < 16; p++)
-										{
-											uint32_t sel = pSelectors[p];
-											trial_err += color_distance(false, src_pixels.get_ptr()[p], block_colors[sel], false);
-											if (trial_err > thresh_err)
-												break;
-										}
-									}
+								}
 
-									if ((trial_err < best_trial_err) && (trial_err <= thresh_err))
-									{
-										assert(trial_err <= limit_err);
-										
-										best_trial_err = trial_err;
-										best_trial_idx = trial_idx;
-										best_trial_history_buf_idx = j;
-									}
+								if ((trial_err < best_trial_err) && (trial_err <= thresh_err))
+								{
+									assert(trial_err <= limit_err);
+
+									best_trial_err = trial_err;
+									best_trial_idx = trial_idx;
+									best_trial_history_buf_idx = j;
 								}
 							}
 
@@ -1043,6 +1140,7 @@
 
 								selector_history_buf_histogram.inc(best_trial_history_buf_idx);
 							}
+
 						} // if (m_params.m_selector_rdo_quality_thresh > 0.0f)
 
 						m.m_selector_index = m_selector_remap_table_new_to_old[new_selector_index];
@@ -1164,6 +1262,14 @@
 
 		} // slice_index
 
+		//for (int i = 0; i <= 255 * 3; i++)
+		//{
+		//	printf("%u, %u, %f\n", g_color_delta_bad_hist[i], g_color_delta_hist[i], g_color_delta_hist[i] ? g_color_delta_bad_hist[i] / (float)g_color_delta_hist[i] : 0);
+		//}
+				
+		double total_prep_time = tm.get_elapsed_secs();
+		debug_printf("basisu_backend::encode_image: Total prep time: %3.2f\n", total_prep_time);
+
 		debug_printf("Endpoint pred RDO total endpoint indices remapped: %u %3.2f%%\n",
 			total_endpoint_indices_remapped, total_endpoint_indices_remapped * 100.0f / get_total_blocks());
 
@@ -1554,215 +1660,82 @@
 	bool basisu_backend::encode_selector_palette()
 	{
 		const basisu_frontend& r = *m_pFront_end;
+		
+		histogram delta_selector_pal_histogram(256);
 
-		if ((m_params.m_use_global_sel_codebook) && (!m_params.m_use_hybrid_sel_codebooks))
+		for (uint32_t q = 0; q < r.get_total_selector_clusters(); q++)
 		{
-			histogram global_mod_indices(1 << m_params.m_global_sel_codebook_mod_bits);
+			if (!q)
+				continue;
 
-			for (uint32_t q = 0; q < r.get_total_selector_clusters(); q++)
-				global_mod_indices.inc(m_global_selector_palette_desc[q].m_mod_index);
+			const etc1_selector_palette_entry& cur = m_selector_palette[m_selector_remap_table_new_to_old[q]];
+			const etc1_selector_palette_entry predictor(m_selector_palette[m_selector_remap_table_new_to_old[q - 1]]);
 
-			huffman_encoding_table global_pal_model, global_mod_model;
-
-			if (!global_mod_model.init(global_mod_indices, 16))
-			{
-				error_printf("global_mod_model.init() failed!");
-				return false;
-			}
-
-			bitwise_coder coder;
-			coder.init(1024 * 1024);
-
-			coder.put_bits(1, 1); // use global codebook
-
-			coder.put_bits(m_params.m_global_sel_codebook_pal_bits, 4); // pal bits
-			coder.put_bits(m_params.m_global_sel_codebook_mod_bits, 4); // mod bits
-
-			uint32_t mod_model_bits = 0;
-			if (m_params.m_global_sel_codebook_mod_bits)
-				mod_model_bits = coder.emit_huffman_table(global_mod_model);
-
-			uint32_t total_pal_bits = 0;
-			uint32_t total_mod_bits = 0;
-			for (uint32_t q = 0; q < r.get_total_selector_clusters(); q++)
-			{
-				const uint32_t i = m_selector_remap_table_new_to_old[q];
-
-				if (m_params.m_global_sel_codebook_pal_bits)
-				{
-					coder.put_bits(m_global_selector_palette_desc[i].m_pal_index, m_params.m_global_sel_codebook_pal_bits);
-					total_pal_bits += m_params.m_global_sel_codebook_pal_bits;
-				}
-
-				if (m_params.m_global_sel_codebook_mod_bits)
-					total_mod_bits += coder.put_code(m_global_selector_palette_desc[i].m_mod_index, global_mod_model);
-			}
-
-			coder.flush();
-
-			m_output.m_selector_palette = coder.get_bytes();
-
-			debug_printf("Modifier model bits: %u Avg per entry: %3.3f\n", mod_model_bits, mod_model_bits / float(r.get_total_selector_clusters()));
-			debug_printf("Palette bits: %u Avg per entry: %3.3f, Modifier bits: %u Avg per entry: %3.3f\n", total_pal_bits, total_pal_bits / float(r.get_total_selector_clusters()), total_mod_bits, total_mod_bits / float(r.get_total_selector_clusters()));
+			for (uint32_t j = 0; j < 4; j++)
+				delta_selector_pal_histogram.inc(cur.get_byte(j) ^ predictor.get_byte(j));
 		}
-		else if (m_params.m_use_hybrid_sel_codebooks)
+
+		if (!delta_selector_pal_histogram.get_total())
+			delta_selector_pal_histogram.inc(0);
+
+		huffman_encoding_table delta_selector_pal_model;
+		if (!delta_selector_pal_model.init(delta_selector_pal_histogram, 16))
 		{
-			huff2D used_global_cb_bitflag_huff2D(1, 8);
-
-			histogram global_mod_indices(1 << m_params.m_global_sel_codebook_mod_bits);
-
-			for (uint32_t s = 0; s < r.get_total_selector_clusters(); s++)
-			{
-				const uint32_t q = m_selector_remap_table_new_to_old[s];
-
-				const bool used_global_cb_flag = r.get_selector_cluster_uses_global_cb_vec()[q];
-
-				used_global_cb_bitflag_huff2D.emit(used_global_cb_flag);
-
-				global_mod_indices.inc(m_global_selector_palette_desc[q].m_mod_index);
-			}
-
-			huffman_encoding_table global_mod_indices_model;
-			if (!global_mod_indices_model.init(global_mod_indices, 16))
-			{
-				error_printf("global_mod_indices_model.init() failed!");
-				return false;
-			}
-
-			bitwise_coder coder;
-			coder.init(1024 * 1024);
-
-			coder.put_bits(0, 1); // use global codebook
-			coder.put_bits(1, 1); // uses hybrid codebooks
-
-			coder.put_bits(m_params.m_global_sel_codebook_pal_bits, 4); // pal bits
-			coder.put_bits(m_params.m_global_sel_codebook_mod_bits, 4); // mod bits
-
-			used_global_cb_bitflag_huff2D.start_encoding(16);
-			coder.emit_huffman_table(used_global_cb_bitflag_huff2D.get_encoding_table());
-
-			if (m_params.m_global_sel_codebook_mod_bits)
-				coder.emit_huffman_table(global_mod_indices_model);
-
-			uint32_t total_global_cb_entries = 0;
-			uint32_t total_pal_bits = 0;
-			uint32_t total_mod_bits = 0;
-			uint32_t total_selectors = 0;
-			uint32_t total_selector_bits = 0;
-			uint32_t total_flag_bits = 0;
-
-			for (uint32_t s = 0; s < r.get_total_selector_clusters(); s++)
-			{
-				const uint32_t q = m_selector_remap_table_new_to_old[s];
-
-				total_flag_bits += used_global_cb_bitflag_huff2D.emit_next_sym(coder);
-
-				const bool used_global_cb_flag = r.get_selector_cluster_uses_global_cb_vec()[q];
-
-				if (used_global_cb_flag)
-				{
-					total_global_cb_entries++;
-
-					total_pal_bits += coder.put_bits(r.get_selector_cluster_global_selector_entry_ids()[q].m_palette_index, m_params.m_global_sel_codebook_pal_bits);
-					total_mod_bits += coder.put_code(r.get_selector_cluster_global_selector_entry_ids()[q].m_modifier.get_index(), global_mod_indices_model);
-				}
-				else
-				{
-					total_selectors++;
-					total_selector_bits += 32;
-
-					for (uint32_t j = 0; j < 4; j++)
-						coder.put_bits(m_selector_palette[q].get_byte(j), 8);
-				}
-			}
-
-			coder.flush();
-
-			m_output.m_selector_palette = coder.get_bytes();
-
-			debug_printf("Total global CB entries: %u %3.2f%%\n", total_global_cb_entries, total_global_cb_entries * 100.0f / r.get_total_selector_clusters());
-			debug_printf("Total selector entries: %u %3.2f%%\n", total_selectors, total_selectors * 100.0f / r.get_total_selector_clusters());
-			debug_printf("Total pal bits: %u, mod bits: %u, selector bits: %u, flag bits: %u\n", total_pal_bits, total_mod_bits, total_selector_bits, total_flag_bits);
+			error_printf("delta_selector_pal_model.init() failed!");
+			return false;
 		}
-		else
+
+		bitwise_coder coder;
+		coder.init(1024 * 1024);
+
+		coder.put_bits(0, 1); // use global codebook
+		coder.put_bits(0, 1); // uses hybrid codebooks
+
+		coder.put_bits(0, 1); // raw bytes
+
+		coder.emit_huffman_table(delta_selector_pal_model);
+
+		for (uint32_t q = 0; q < r.get_total_selector_clusters(); q++)
 		{
-			histogram delta_selector_pal_histogram(256);
-
-			for (uint32_t q = 0; q < r.get_total_selector_clusters(); q++)
+			if (!q)
 			{
-				if (!q)
-					continue;
-
-				const basist::etc1_selector_palette_entry& cur = m_selector_palette[m_selector_remap_table_new_to_old[q]];
-				const basist::etc1_selector_palette_entry predictor(m_selector_palette[m_selector_remap_table_new_to_old[q - 1]]);
-
 				for (uint32_t j = 0; j < 4; j++)
-					delta_selector_pal_histogram.inc(cur.get_byte(j) ^ predictor.get_byte(j));
+					coder.put_bits(m_selector_palette[m_selector_remap_table_new_to_old[q]].get_byte(j), 8);
+				continue;
 			}
 
-			if (!delta_selector_pal_histogram.get_total())
-				delta_selector_pal_histogram.inc(0);
+			const etc1_selector_palette_entry& cur = m_selector_palette[m_selector_remap_table_new_to_old[q]];
+			const etc1_selector_palette_entry predictor(m_selector_palette[m_selector_remap_table_new_to_old[q - 1]]);
 
-			huffman_encoding_table delta_selector_pal_model;
-			if (!delta_selector_pal_model.init(delta_selector_pal_histogram, 16))
-			{
-				error_printf("delta_selector_pal_model.init() failed!");
-				return false;
-			}
+			for (uint32_t j = 0; j < 4; j++)
+				coder.put_code(cur.get_byte(j) ^ predictor.get_byte(j), delta_selector_pal_model);
+		}
 
-			bitwise_coder coder;
+		coder.flush();
+
+		m_output.m_selector_palette = coder.get_bytes();
+
+		if (m_output.m_selector_palette.size() >= r.get_total_selector_clusters() * 4)
+		{
 			coder.init(1024 * 1024);
 
 			coder.put_bits(0, 1); // use global codebook
 			coder.put_bits(0, 1); // uses hybrid codebooks
 
-			coder.put_bits(0, 1); // raw bytes
-
-			coder.emit_huffman_table(delta_selector_pal_model);
+			coder.put_bits(1, 1); // raw bytes
 
 			for (uint32_t q = 0; q < r.get_total_selector_clusters(); q++)
 			{
-				if (!q)
-				{
-					for (uint32_t j = 0; j < 4; j++)
-						coder.put_bits(m_selector_palette[m_selector_remap_table_new_to_old[q]].get_byte(j), 8);
-					continue;
-				}
-
-				const basist::etc1_selector_palette_entry& cur = m_selector_palette[m_selector_remap_table_new_to_old[q]];
-				const basist::etc1_selector_palette_entry predictor(m_selector_palette[m_selector_remap_table_new_to_old[q - 1]]);
+				const uint32_t i = m_selector_remap_table_new_to_old[q];
 
 				for (uint32_t j = 0; j < 4; j++)
-					coder.put_code(cur.get_byte(j) ^ predictor.get_byte(j), delta_selector_pal_model);
+					coder.put_bits(m_selector_palette[i].get_byte(j), 8);
 			}
 
 			coder.flush();
 
 			m_output.m_selector_palette = coder.get_bytes();
-
-			if (m_output.m_selector_palette.size() >= r.get_total_selector_clusters() * 4)
-			{
-				coder.init(1024 * 1024);
-
-				coder.put_bits(0, 1); // use global codebook
-				coder.put_bits(0, 1); // uses hybrid codebooks
-
-				coder.put_bits(1, 1); // raw bytes
-
-				for (uint32_t q = 0; q < r.get_total_selector_clusters(); q++)
-				{
-					const uint32_t i = m_selector_remap_table_new_to_old[q];
-
-					for (uint32_t j = 0; j < 4; j++)
-						coder.put_bits(m_selector_palette[i].get_byte(j), 8);
-				}
-
-				coder.flush();
-
-				m_output.m_selector_palette = coder.get_bytes();
-			}
-
-		}  // if (m_params.m_use_global_sel_codebook)        
+		}
 
 		debug_printf("Selector codebook bits: %u bytes: %u, Bits per entry: %3.1f, Avg bits/texel: %3.3f\n",
 			(int)m_output.m_selector_palette.size() * 8, (int)m_output.m_selector_palette.size(),
diff --git a/encoder/basisu_backend.h b/encoder/basisu_backend.h
index 393dccd..07778ae 100644
--- a/encoder/basisu_backend.h
+++ b/encoder/basisu_backend.h
@@ -17,11 +17,86 @@
 #include "../transcoder/basisu.h"
 #include "basisu_enc.h"
 #include "../transcoder/basisu_transcoder_internal.h"
-#include "../transcoder/basisu_global_selector_palette.h"
 #include "basisu_frontend.h"
 
 namespace basisu
 {
+	struct etc1_selector_palette_entry
+	{
+		etc1_selector_palette_entry()
+		{
+			clear();
+		}
+
+		void clear()
+		{
+			basisu::clear_obj(*this);
+		}
+
+		uint8_t operator[] (uint32_t i) const { assert(i < 16); return m_selectors[i]; }
+		uint8_t& operator[] (uint32_t i) { assert(i < 16); return m_selectors[i]; }
+
+		void set_uint32(uint32_t v)
+		{
+			for (uint32_t byte_index = 0; byte_index < 4; byte_index++)
+			{
+				uint32_t b = (v >> (byte_index * 8)) & 0xFF;
+
+				m_selectors[byte_index * 4 + 0] = b & 3;
+				m_selectors[byte_index * 4 + 1] = (b >> 2) & 3;
+				m_selectors[byte_index * 4 + 2] = (b >> 4) & 3;
+				m_selectors[byte_index * 4 + 3] = (b >> 6) & 3;
+			}
+		}
+
+		uint32_t get_uint32() const
+		{
+			return get_byte(0) | (get_byte(1) << 8) | (get_byte(2) << 16) | (get_byte(3) << 24);
+		}
+
+		uint32_t get_byte(uint32_t byte_index) const
+		{
+			assert(byte_index < 4);
+
+			return m_selectors[byte_index * 4 + 0] |
+				(m_selectors[byte_index * 4 + 1] << 2) |
+				(m_selectors[byte_index * 4 + 2] << 4) |
+				(m_selectors[byte_index * 4 + 3] << 6);
+		}
+
+		uint8_t operator()(uint32_t x, uint32_t y) const { assert((x < 4) && (y < 4)); return m_selectors[x + y * 4]; }
+		uint8_t& operator()(uint32_t x, uint32_t y) { assert((x < 4) && (y < 4)); return m_selectors[x + y * 4]; }
+
+		bool operator< (const etc1_selector_palette_entry& other) const
+		{
+			for (uint32_t i = 0; i < 16; i++)
+			{
+				if (m_selectors[i] < other.m_selectors[i])
+					return true;
+				else if (m_selectors[i] != other.m_selectors[i])
+					return false;
+			}
+
+			return false;
+		}
+
+		bool operator== (const etc1_selector_palette_entry& other) const
+		{
+			for (uint32_t i = 0; i < 16; i++)
+			{
+				if (m_selectors[i] != other.m_selectors[i])
+					return false;
+			}
+
+			return true;
+		}
+
+	private:
+		uint8_t m_selectors[16];
+	};
+
+	typedef basisu::vector<etc1_selector_palette_entry> etc1_selector_palette_entry_vec;
+
 	struct encoder_block
 	{
 		encoder_block()
@@ -78,14 +153,11 @@
 		float m_endpoint_rdo_quality_thresh;
 		float m_selector_rdo_quality_thresh;
 		uint32_t m_compression_level;
-
-		bool m_use_global_sel_codebook;
-		uint32_t m_global_sel_codebook_pal_bits;
-		uint32_t m_global_sel_codebook_mod_bits;
-		bool m_use_hybrid_sel_codebooks;
-
+								
 		bool m_used_global_codebooks;
 
+		bool m_validate;
+
 		basisu_backend_params()
 		{
 			clear();
@@ -99,12 +171,8 @@
 			m_endpoint_rdo_quality_thresh = 0.0f;
 			m_selector_rdo_quality_thresh = 0.0f;
 			m_compression_level = 0;
-
-			m_use_global_sel_codebook = false;
-			m_global_sel_codebook_pal_bits = ETC1_GLOBAL_SELECTOR_CODEBOOK_MAX_PAL_BITS;
-			m_global_sel_codebook_mod_bits = basist::etc1_global_palette_entry_modifier::cTotalBits;
-			m_use_hybrid_sel_codebooks = false;
 			m_used_global_codebooks = false;
+			m_validate = true;
 		}
 	};
 
@@ -205,7 +273,7 @@
 
 		void clear();
 
-		void init(basisu_frontend *pFront_end, basisu_backend_params &params, const basisu_backend_slice_desc_vec &slice_desc, const basist::etc1_global_selector_codebook *pGlobal_sel_codebook);
+		void init(basisu_frontend *pFront_end, basisu_backend_params &params, const basisu_backend_slice_desc_vec &slice_desc);
 
 		uint32_t encode();
 
@@ -217,10 +285,9 @@
 		basisu_backend_params m_params;
 		basisu_backend_slice_desc_vec m_slices;
 		basisu_backend_output m_output;
-		const basist::etc1_global_selector_codebook *m_pGlobal_sel_codebook;
-
+		
 		etc1_endpoint_palette_entry_vec m_endpoint_palette;
-		basist::etc1_selector_palette_entry_vec m_selector_palette;
+		etc1_selector_palette_entry_vec m_selector_palette;
 
 		struct etc1_global_selector_cb_entry_desc
 		{
diff --git a/encoder/basisu_comp.cpp b/encoder/basisu_comp.cpp
index 10f96ce..166a1c4 100644
--- a/encoder/basisu_comp.cpp
+++ b/encoder/basisu_comp.cpp
@@ -21,6 +21,8 @@
 #define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
 #include "basisu_miniz.h"
 
+#include "basisu_opencl.h"
+
 #if !BASISD_SUPPORT_KTX2
 #error BASISD_SUPPORT_KTX2 must be enabled (set to 1).
 #endif
@@ -45,21 +47,45 @@
 namespace basisu
 {
    basis_compressor::basis_compressor() :
+	   m_pOpenCL_context(nullptr),
 		m_basis_file_size(0),
 		m_basis_bits_per_texel(0.0f),
 		m_total_blocks(0),
-		m_auto_global_sel_pal(false),
-		m_any_source_image_has_alpha(false)
+		m_any_source_image_has_alpha(false),
+	   m_opencl_failed(false)
 	{
 		debug_printf("basis_compressor::basis_compressor\n");
+		
+		assert(g_library_initialized);
 	}
 
+   basis_compressor::~basis_compressor()
+   {
+	   if (m_pOpenCL_context)
+	   {
+		   opencl_destroy_context(m_pOpenCL_context);
+		   m_pOpenCL_context = nullptr;
+	   }
+   }
+
 	bool basis_compressor::init(const basis_compressor_params &params)
 	{
 		debug_printf("basis_compressor::init\n");
+				
+		if (!g_library_initialized)
+		{
+			error_printf("basis_compressor::init: basisu_encoder_init() MUST be called before using any encoder functionality!\n");
+			return false;
+		}
 
+		if (!params.m_pJob_pool)
+		{
+			error_printf("basis_compressor::init: A non-null job_pool pointer must be specified\n");
+			return false;
+		}
+				
 		m_params = params;
-
+				
 		if (m_params.m_debug)
 		{
 			debug_printf("basis_compressor::init:\n");
@@ -68,9 +94,7 @@
 #define PRINT_INT_VALUE(v) debug_printf("%s: %i %u\n", BASISU_STRINGIZE2(v), static_cast<int>(m_params.v), m_params.v.was_changed());
 #define PRINT_UINT_VALUE(v) debug_printf("%s: %u %u\n", BASISU_STRINGIZE2(v), static_cast<uint32_t>(m_params.v), m_params.v.was_changed());
 #define PRINT_FLOAT_VALUE(v) debug_printf("%s: %f %u\n", BASISU_STRINGIZE2(v), static_cast<float>(m_params.v), m_params.v.was_changed());
-
-			debug_printf("Has global selector codebook: %i\n", m_params.m_pSel_codebook != nullptr);
-
+						
 			debug_printf("Source images: %u, source filenames: %u, source alpha filenames: %i, Source mipmap images: %u\n",
 				m_params.m_source_images.size(), m_params.m_source_filenames.size(), m_params.m_source_alpha_filenames.size(), m_params.m_source_mipmap_images.size());
 
@@ -83,14 +107,12 @@
 			}
 
 			PRINT_BOOL_VALUE(m_uastc);
+			PRINT_BOOL_VALUE(m_use_opencl);
 			PRINT_BOOL_VALUE(m_y_flip);
 			PRINT_BOOL_VALUE(m_debug);
-			PRINT_BOOL_VALUE(m_validate);
+			PRINT_BOOL_VALUE(m_validate_etc1s);
 			PRINT_BOOL_VALUE(m_debug_images);
-			PRINT_BOOL_VALUE(m_global_sel_pal);
-			PRINT_BOOL_VALUE(m_auto_global_sel_pal);
 			PRINT_INT_VALUE(m_compression_level);
-			PRINT_BOOL_VALUE(m_no_hybrid_sel_cb);
 			PRINT_BOOL_VALUE(m_perceptual);
 			PRINT_BOOL_VALUE(m_no_endpoint_rdo);
 			PRINT_BOOL_VALUE(m_no_selector_rdo);
@@ -107,12 +129,7 @@
 			PRINT_BOOL_VALUE(m_renormalize);
 			PRINT_BOOL_VALUE(m_multithreading);
 			PRINT_BOOL_VALUE(m_disable_hierarchical_endpoint_codebooks);
-			
-			PRINT_FLOAT_VALUE(m_hybrid_sel_cb_quality_thresh);
-			
-			PRINT_INT_VALUE(m_global_pal_bits);
-			PRINT_INT_VALUE(m_global_mod_bits);
-
+												
 			PRINT_FLOAT_VALUE(m_endpoint_rdo_thresh);
 			PRINT_FLOAT_VALUE(m_selector_rdo_thresh);
 			
@@ -148,6 +165,7 @@
 			PRINT_INT_VALUE(m_resample_width);
 			PRINT_INT_VALUE(m_resample_height);
 			PRINT_FLOAT_VALUE(m_resample_factor);
+			
 			debug_printf("Has global codebooks: %u\n", m_params.m_pGlobal_codebooks ? 1 : 0);
 			if (m_params.m_pGlobal_codebooks)
 			{
@@ -165,6 +183,8 @@
 				debug_printf("Key: \"%s\"\n", m_params.m_ktx2_key_values[i].m_key.data());
 				debug_printf("Value size: %u\n", m_params.m_ktx2_key_values[i].m_value.size());
 			}
+
+			PRINT_BOOL_VALUE(m_validate_output_data);
 						
 #undef PRINT_BOOL_VALUE
 #undef PRINT_INT_VALUE
@@ -178,6 +198,20 @@
 			return false;
 		}
 
+		if ((m_params.m_compute_stats) && (!m_params.m_validate_output_data))
+		{
+			m_params.m_validate_output_data = true;
+			
+			debug_printf("Note: m_compute_stats is true, so forcing m_validate_output_data to true as well\n");
+		}
+
+		if ((m_params.m_use_opencl) && opencl_is_available() && !m_pOpenCL_context && !m_opencl_failed)
+		{
+			m_pOpenCL_context = opencl_create_context();
+			if (!m_pOpenCL_context)
+				m_opencl_failed = true;
+		}
+
 		return true;
 	}
 		
@@ -424,7 +458,7 @@
 #endif
 
 		if (m_params.m_debug)
-			debug_printf("Total mipmap generation time: %f secs\n", tm.get_elapsed_secs());
+			debug_printf("Total mipmap generation time: %3.3f secs\n", tm.get_elapsed_secs());
 
 		return true;
 	}
@@ -579,11 +613,11 @@
 
 			if ((file_image.get_width() > BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION) || (file_image.get_height() > BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION))
 			{
-				error_printf("basis_compressor::read_source_images: Source image is too large!\n");
+				error_printf("basis_compressor::read_source_images: Source image \"%s\" is too large!\n", pSource_filename);
 				return false;
 			}
 
-			source_images.push_back(file_image);
+			source_images.enlarge(1)->swap(file_image);
 			source_filenames.push_back(pSource_filename);
 		}
 
@@ -624,17 +658,19 @@
 
 		for (uint32_t source_file_index = 0; source_file_index < total_source_files; source_file_index++)
 		{
-			image &file_image = source_images[source_file_index];
 			const std::string &source_filename = source_filenames[source_file_index];
 
 			// Now, for each source image, create the slices corresponding to that image.
 			basisu::vector<image> slices;
 			
 			slices.reserve(32);
-			
+						
 			// The first (largest) mipmap level.
-			slices.push_back(file_image);
-			
+			image& file_image = source_images[source_file_index];
+						
+			// Reserve a slot for mip0.
+			slices.resize(1);
+												
 			if (m_params.m_source_mipmap_images.size())
 			{
 				// User-provided mipmaps for each layer or image in the texture array.
@@ -666,6 +702,10 @@
 					return false;
 			}
 
+			// Swap in the largest mipmap level here to avoid copying it, because generate_mips() will change the array.
+			// NOTE: file_image is now blank.
+			slices[0].swap(file_image);
+
 			uint_vec mip_indices(slices.size());
 			for (uint32_t i = 0; i < slices.size(); i++)
 				mip_indices[i] = i;
@@ -734,18 +774,16 @@
 					save_png(string_format("basis_debug_source_image_%u_slice_%u.png", source_file_index, slice_index).c_str(), slice_image);
 				}
 
+				const uint32_t dest_image_index = m_slice_images.size();
+
 				enlarge_vector(m_stats, 1);
 				enlarge_vector(m_slice_images, 1);
 				enlarge_vector(m_slice_descs, 1);
-
-				const uint32_t dest_image_index = (uint32_t)m_stats.size() - 1;
-
+								
 				m_stats[dest_image_index].m_filename = source_filename.c_str();
 				m_stats[dest_image_index].m_width = orig_width;
 				m_stats[dest_image_index].m_height = orig_height;
-
-				m_slice_images[dest_image_index] = slice_image;
-
+								
 				debug_printf("****** Slice %u: mip %u, alpha_slice: %u, filename: \"%s\", original: %ux%u actual: %ux%u\n", m_slice_descs.size() - 1, mip_indices[slice_index], is_alpha_slice, source_filename.c_str(), orig_width, orig_height, slice_image.get_width(), slice_image.get_height());
 
 				basisu_backend_slice_desc &slice_desc = m_slice_descs[dest_image_index];
@@ -777,6 +815,10 @@
 
 				m_total_blocks += slice_desc.m_num_blocks_x * slice_desc.m_num_blocks_y;
 				total_macroblocks += slice_desc.m_num_macroblocks_x * slice_desc.m_num_macroblocks_y;
+
+				// Finally, swap in the slice's image to avoid copying it.
+				// NOTE: slice_image is now blank.
+				m_slice_images[dest_image_index].swap(slice_image);
 			
 			} // slice_index
 
@@ -1055,7 +1097,7 @@
 				endpoint_clusters = clamp<uint32_t>((uint32_t)(.5f + lerp<float>(ENDPOINT_CODEBOOK_MID_QUALITY_CODEBOOK_SIZE, static_cast<float>(max_endpoints), color_endpoint_quality)), 32, basisu_frontend::cMaxEndpointClusters);
 			}
 						
-			float bits_per_selector_cluster = m_params.m_global_sel_pal ? 21.0f : 14.0f;
+			float bits_per_selector_cluster = 14.0f;
 
 			const float max_desired_selector_cluster_bits_per_texel = 1.0f; // .15f
 			int max_selectors = static_cast<int>((max_desired_selector_cluster_bits_per_texel * total_texels) / bits_per_selector_cluster);
@@ -1110,21 +1152,7 @@
 					m_params.m_selector_rdo_thresh *= lerp<float>(1.0f, .75f, l);
 			}
 		}
-
-		m_auto_global_sel_pal = false;
-		if (!m_params.m_global_sel_pal && m_params.m_auto_global_sel_pal)
-		{
-			const float bits_per_selector_cluster = 31.0f;
-			double selector_codebook_bpp_est = (bits_per_selector_cluster * selector_clusters) / total_texels;
-			debug_printf("selector_codebook_bpp_est: %f\n", selector_codebook_bpp_est);
-			const float force_global_sel_pal_bpp_threshold = .15f;
-			if ((total_texels <= 128.0f*128.0f) && (selector_codebook_bpp_est > force_global_sel_pal_bpp_threshold))
-			{
-				m_auto_global_sel_pal = true;
-				debug_printf("Auto global selector palette enabled\n");
-			}
-		}
-
+				
 		basisu_frontend::params p;
 		p.m_num_source_blocks = m_total_blocks;
 		p.m_pSource_blocks = &m_source_blocks[0];
@@ -1137,27 +1165,24 @@
 		p.m_tex_type = m_params.m_tex_type;
 		p.m_multithreaded = m_params.m_multithreading;
 		p.m_disable_hierarchical_endpoint_codebooks = m_params.m_disable_hierarchical_endpoint_codebooks;
-		p.m_validate = m_params.m_validate;
+		p.m_validate = m_params.m_validate_etc1s;
 		p.m_pJob_pool = m_params.m_pJob_pool;
 		p.m_pGlobal_codebooks = m_params.m_pGlobal_codebooks;
-
-		if ((m_params.m_global_sel_pal) || (m_auto_global_sel_pal))
-		{
-			p.m_pGlobal_sel_codebook = m_params.m_pSel_codebook;
-			p.m_num_global_sel_codebook_pal_bits = m_params.m_global_pal_bits;
-			p.m_num_global_sel_codebook_mod_bits = m_params.m_global_mod_bits;
-			p.m_use_hybrid_selector_codebooks = !m_params.m_no_hybrid_sel_cb;
-			p.m_hybrid_codebook_quality_thresh = m_params.m_hybrid_sel_cb_quality_thresh;
-		}
+		
+		// Don't keep trying to use OpenCL if it ever fails.
+		p.m_pOpenCL_context = !m_opencl_failed ? m_pOpenCL_context : nullptr;
 
 		if (!m_frontend.init(p))
 		{
 			error_printf("basisu_frontend::init() failed!\n");
 			return false;
 		}
-
+				
 		m_frontend.compress();
 
+		if (m_frontend.get_opencl_failed())
+			m_opencl_failed = true;
+
 		if (m_params.m_debug_images)
 		{
 			for (uint32_t i = 0; i < m_slice_descs.size(); i++)
@@ -1184,6 +1209,9 @@
 
 	bool basis_compressor::extract_frontend_texture_data()
 	{
+		if (!m_params.m_compute_stats)
+			return true;
+
 		debug_printf("basis_compressor::extract_frontend_texture_data\n");
 
 		m_frontend_output_textures.resize(m_slice_descs.size());
@@ -1242,13 +1270,10 @@
 		if (!m_params.m_no_selector_rdo)
 			backend_params.m_selector_rdo_quality_thresh = m_params.m_selector_rdo_thresh;
 				
-		backend_params.m_use_global_sel_codebook = (m_frontend.get_params().m_pGlobal_sel_codebook != NULL);
-		backend_params.m_global_sel_codebook_pal_bits = m_frontend.get_params().m_num_global_sel_codebook_pal_bits;
-		backend_params.m_global_sel_codebook_mod_bits = m_frontend.get_params().m_num_global_sel_codebook_mod_bits;
-		backend_params.m_use_hybrid_sel_codebooks = m_frontend.get_params().m_use_hybrid_selector_codebooks;
 		backend_params.m_used_global_codebooks = m_frontend.get_params().m_pGlobal_codebooks != nullptr;
+		backend_params.m_validate = m_params.m_validate_output_data;
 
-		m_backend.init(&m_frontend, backend_params, m_slice_descs, m_params.m_pSel_codebook);
+		m_backend.init(&m_frontend, backend_params, m_slice_descs);
 		uint32_t total_packed_bytes = m_backend.encode();
 
 		if (!total_packed_bytes)
@@ -1278,140 +1303,143 @@
 
 		m_output_basis_file = comp_data;
 
-		interval_timer tm;
-		tm.start();
-
-		basist::basisu_transcoder_init();
-
-		debug_printf("basist::basisu_transcoder_init: Took %f ms\n", tm.get_elapsed_ms());
-
-		// Verify the compressed data by transcoding it to ASTC (or ETC1)/BC7 and validating the CRC's.
-		basist::basisu_transcoder decoder(m_params.m_pSel_codebook);
-		if (!decoder.validate_file_checksums(&comp_data[0], (uint32_t)comp_data.size(), true))
-		{
-			error_printf("decoder.validate_file_checksums() failed!\n");
-			return false;
-		}
-
-		m_decoded_output_textures.resize(m_slice_descs.size());
-		m_decoded_output_textures_unpacked.resize(m_slice_descs.size());
-
-		m_decoded_output_textures_bc7.resize(m_slice_descs.size());
-		m_decoded_output_textures_unpacked_bc7.resize(m_slice_descs.size());
-								
-		tm.start();
-		if (m_params.m_pGlobal_codebooks)
-		{
-			decoder.set_global_codebooks(m_params.m_pGlobal_codebooks);
-		}
-
-		if (!decoder.start_transcoding(&comp_data[0], (uint32_t)comp_data.size()))
-		{
-			error_printf("decoder.start_transcoding() failed!\n");
-			return false;
-		}
-
-		double start_transcoding_time = tm.get_elapsed_secs();
-
-		debug_printf("basisu_compressor::start_transcoding() took %3.3fms\n", start_transcoding_time * 1000.0f);
-
-		uint32_t total_orig_pixels = 0;
-		uint32_t total_texels = 0;
-
-		double total_time_etc1s_or_astc = 0;
-
+		uint32_t total_orig_pixels = 0, total_texels = 0, total_orig_texels = 0;
 		for (uint32_t i = 0; i < m_slice_descs.size(); i++)
 		{
-			gpu_image decoded_texture;
-			decoded_texture.init(m_params.m_uastc ? texture_format::cASTC4x4 : texture_format::cETC1, m_slice_descs[i].m_width, m_slice_descs[i].m_height);
-						
-			tm.start();
+			const basisu_backend_slice_desc& slice_desc = m_slice_descs[i];
 
-			basist::block_format format = m_params.m_uastc ? basist::block_format::cASTC_4x4 : basist::block_format::cETC1;
-			uint32_t bytes_per_block = m_params.m_uastc ? 16 : 8;
-						
-			if (!decoder.transcode_slice(&comp_data[0], (uint32_t)comp_data.size(), i,
-				reinterpret_cast<etc_block *>(decoded_texture.get_ptr()), m_slice_descs[i].m_num_blocks_x * m_slice_descs[i].m_num_blocks_y, format, bytes_per_block))
-			{
-				error_printf("Transcoding failed on slice %u!\n", i);
-				return false;
-			}
-
-			total_time_etc1s_or_astc += tm.get_elapsed_secs();
-
-			if (encoded_output.m_tex_format == basist::basis_tex_format::cETC1S)
-			{
-				uint32_t image_crc16 = basist::crc16(decoded_texture.get_ptr(), decoded_texture.get_size_in_bytes(), 0);
-				if (image_crc16 != encoded_output.m_slice_image_crcs[i])
-				{
-					error_printf("Decoded image data CRC check failed on slice %u!\n", i);
-					return false;
-				}
-				debug_printf("Decoded image data CRC check succeeded on slice %i\n", i);
-			}
-
-			m_decoded_output_textures[i] = decoded_texture;
-
-			total_orig_pixels += m_slice_descs[i].m_orig_width * m_slice_descs[i].m_orig_height;
-			total_texels += m_slice_descs[i].m_width * m_slice_descs[i].m_height;
-		}
-												
-		double total_time_bc7 = 0;
-
-		if (basist::basis_is_format_supported(basist::transcoder_texture_format::cTFBC7_RGBA, basist::basis_tex_format::cUASTC4x4) &&
-			basist::basis_is_format_supported(basist::transcoder_texture_format::cTFBC7_RGBA, basist::basis_tex_format::cETC1S))
-		{
-			for (uint32_t i = 0; i < m_slice_descs.size(); i++)
-			{
-				gpu_image decoded_texture;
-				decoded_texture.init(texture_format::cBC7, m_slice_descs[i].m_width, m_slice_descs[i].m_height);
-
-				tm.start();
-
-				if (!decoder.transcode_slice(&comp_data[0], (uint32_t)comp_data.size(), i,
-					reinterpret_cast<etc_block*>(decoded_texture.get_ptr()), m_slice_descs[i].m_num_blocks_x * m_slice_descs[i].m_num_blocks_y, basist::block_format::cBC7, 16))
-				{
-					error_printf("Transcoding failed to BC7 on slice %u!\n", i);
-					return false;
-				}
-
-				total_time_bc7 += tm.get_elapsed_secs();
-
-				m_decoded_output_textures_bc7[i] = decoded_texture;
-			}
-		}
-
-		for (uint32_t i = 0; i < m_slice_descs.size(); i++)
-		{
-			m_decoded_output_textures[i].unpack(m_decoded_output_textures_unpacked[i]);
-
-			if (m_decoded_output_textures_bc7[i].get_pixel_width())
-				m_decoded_output_textures_bc7[i].unpack(m_decoded_output_textures_unpacked_bc7[i]);
-		}
-
-		debug_printf("Transcoded to %s in %3.3fms, %f texels/sec\n", m_params.m_uastc ? "ASTC" : "ETC1", total_time_etc1s_or_astc * 1000.0f, total_orig_pixels / total_time_etc1s_or_astc);
-
-		if (total_time_bc7 != 0)
-			debug_printf("Transcoded to BC7 in %3.3fms, %f texels/sec\n", total_time_bc7 * 1000.0f, total_orig_pixels / total_time_bc7);
-
-		debug_printf("Total .basis output file size: %u, %3.3f bits/texel\n", comp_data.size(), comp_data.size() * 8.0f / total_orig_pixels);
-				
-		uint32_t total_orig_texels = 0;
-		for (uint32_t slice_index = 0; slice_index < m_slice_descs.size(); slice_index++)
-		{
-			const basisu_backend_slice_desc &slice_desc = m_slice_descs[slice_index];
-
-			total_orig_texels += slice_desc.m_orig_width * slice_desc.m_orig_height;
-
-			const uint32_t total_blocks = slice_desc.m_num_blocks_x * slice_desc.m_num_blocks_y;
-			BASISU_NOTE_UNUSED(total_blocks);
-
-			assert(m_decoded_output_textures[slice_index].get_total_blocks() == total_blocks);
+			total_orig_pixels += slice_desc.m_orig_width * slice_desc.m_orig_height;
+			total_texels += slice_desc.m_width * slice_desc.m_height;
 		}
 
 		m_basis_file_size = (uint32_t)comp_data.size();
-		m_basis_bits_per_texel = (comp_data.size() * 8.0f) / total_orig_texels;
+		m_basis_bits_per_texel = total_orig_texels ? (comp_data.size() * 8.0f) / total_orig_texels : 0;
 
+		debug_printf("Total .basis output file size: %u, %3.3f bits/texel\n", comp_data.size(), comp_data.size() * 8.0f / total_orig_pixels);
+
+		if (m_params.m_validate_output_data)
+		{
+			interval_timer tm;
+			tm.start();
+
+			basist::basisu_transcoder_init();
+
+			debug_printf("basist::basisu_transcoder_init: Took %f ms\n", tm.get_elapsed_ms());
+
+			// Verify the compressed data by transcoding it to ASTC (or ETC1)/BC7 and validating the CRC's.
+			basist::basisu_transcoder decoder;
+			if (!decoder.validate_file_checksums(&comp_data[0], (uint32_t)comp_data.size(), true))
+			{
+				error_printf("decoder.validate_file_checksums() failed!\n");
+				return false;
+			}
+
+			m_decoded_output_textures.resize(m_slice_descs.size());
+			m_decoded_output_textures_unpacked.resize(m_slice_descs.size());
+
+			m_decoded_output_textures_bc7.resize(m_slice_descs.size());
+			m_decoded_output_textures_unpacked_bc7.resize(m_slice_descs.size());
+
+			tm.start();
+			if (m_params.m_pGlobal_codebooks)
+			{
+				decoder.set_global_codebooks(m_params.m_pGlobal_codebooks);
+			}
+
+			if (!decoder.start_transcoding(&comp_data[0], (uint32_t)comp_data.size()))
+			{
+				error_printf("decoder.start_transcoding() failed!\n");
+				return false;
+			}
+
+			double start_transcoding_time = tm.get_elapsed_secs();
+
+			debug_printf("basisu_compressor::start_transcoding() took %3.3fms\n", start_transcoding_time * 1000.0f);
+
+			double total_time_etc1s_or_astc = 0;
+
+			for (uint32_t i = 0; i < m_slice_descs.size(); i++)
+			{
+				gpu_image decoded_texture;
+				decoded_texture.init(m_params.m_uastc ? texture_format::cUASTC4x4 : texture_format::cETC1, m_slice_descs[i].m_width, m_slice_descs[i].m_height);
+
+				tm.start();
+
+				basist::block_format format = m_params.m_uastc ? basist::block_format::cUASTC_4x4 : basist::block_format::cETC1;
+				uint32_t bytes_per_block = m_params.m_uastc ? 16 : 8;
+
+				if (!decoder.transcode_slice(&comp_data[0], (uint32_t)comp_data.size(), i,
+					reinterpret_cast<etc_block*>(decoded_texture.get_ptr()), m_slice_descs[i].m_num_blocks_x * m_slice_descs[i].m_num_blocks_y, format, bytes_per_block))
+				{
+					error_printf("Transcoding failed on slice %u!\n", i);
+					return false;
+				}
+
+				total_time_etc1s_or_astc += tm.get_elapsed_secs();
+
+				if (encoded_output.m_tex_format == basist::basis_tex_format::cETC1S)
+				{
+					uint32_t image_crc16 = basist::crc16(decoded_texture.get_ptr(), decoded_texture.get_size_in_bytes(), 0);
+					if (image_crc16 != encoded_output.m_slice_image_crcs[i])
+					{
+						error_printf("Decoded image data CRC check failed on slice %u!\n", i);
+						return false;
+					}
+					debug_printf("Decoded image data CRC check succeeded on slice %i\n", i);
+				}
+
+				m_decoded_output_textures[i] = decoded_texture;
+			}
+
+			double total_time_bc7 = 0;
+
+			if (basist::basis_is_format_supported(basist::transcoder_texture_format::cTFBC7_RGBA, basist::basis_tex_format::cUASTC4x4) &&
+				basist::basis_is_format_supported(basist::transcoder_texture_format::cTFBC7_RGBA, basist::basis_tex_format::cETC1S))
+			{
+				for (uint32_t i = 0; i < m_slice_descs.size(); i++)
+				{
+					gpu_image decoded_texture;
+					decoded_texture.init(texture_format::cBC7, m_slice_descs[i].m_width, m_slice_descs[i].m_height);
+
+					tm.start();
+
+					if (!decoder.transcode_slice(&comp_data[0], (uint32_t)comp_data.size(), i,
+						reinterpret_cast<etc_block*>(decoded_texture.get_ptr()), m_slice_descs[i].m_num_blocks_x * m_slice_descs[i].m_num_blocks_y, basist::block_format::cBC7, 16))
+					{
+						error_printf("Transcoding failed to BC7 on slice %u!\n", i);
+						return false;
+					}
+
+					total_time_bc7 += tm.get_elapsed_secs();
+
+					m_decoded_output_textures_bc7[i] = decoded_texture;
+				}
+			}
+
+			for (uint32_t i = 0; i < m_slice_descs.size(); i++)
+			{
+				m_decoded_output_textures[i].unpack(m_decoded_output_textures_unpacked[i]);
+
+				if (m_decoded_output_textures_bc7[i].get_pixel_width())
+					m_decoded_output_textures_bc7[i].unpack(m_decoded_output_textures_unpacked_bc7[i]);
+			}
+
+			debug_printf("Transcoded to %s in %3.3fms, %f texels/sec\n", m_params.m_uastc ? "ASTC" : "ETC1", total_time_etc1s_or_astc * 1000.0f, total_orig_pixels / total_time_etc1s_or_astc);
+
+			if (total_time_bc7 != 0)
+				debug_printf("Transcoded to BC7 in %3.3fms, %f texels/sec\n", total_time_bc7 * 1000.0f, total_orig_pixels / total_time_bc7);
+
+			for (uint32_t slice_index = 0; slice_index < m_slice_descs.size(); slice_index++)
+			{
+				const basisu_backend_slice_desc& slice_desc = m_slice_descs[slice_index];
+
+				const uint32_t total_blocks = slice_desc.m_num_blocks_x * slice_desc.m_num_blocks_y;
+				BASISU_NOTE_UNUSED(total_blocks);
+
+				assert(m_decoded_output_textures[slice_index].get_total_blocks() == total_blocks);
+			}
+		} // if (m_params.m_validate_output_data)
+				
 		return true;
 	}
 
@@ -1465,175 +1493,171 @@
 
 		m_stats.resize(m_slice_descs.size());
 		
-		uint32_t total_orig_texels = 0;
-
-		for (uint32_t slice_index = 0; slice_index < m_slice_descs.size(); slice_index++)
+		if (m_params.m_validate_output_data)
 		{
-			const basisu_backend_slice_desc &slice_desc = m_slice_descs[slice_index];
-						
-			total_orig_texels += slice_desc.m_orig_width * slice_desc.m_orig_height;
-
-			if (m_params.m_compute_stats)
+			for (uint32_t slice_index = 0; slice_index < m_slice_descs.size(); slice_index++)
 			{
-				printf("Slice: %u\n", slice_index);
+				const basisu_backend_slice_desc& slice_desc = m_slice_descs[slice_index];
 
-				image_stats &s = m_stats[slice_index];
-
-				// TODO: We used to output SSIM (during heavy encoder development), but this slowed down compression too much. We'll be adding it back.
-
-				image_metrics em;
-								
-				// ---- .basis stats
-				em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 3);
-				em.print(".basis RGB Avg:          ");
-				s.m_basis_rgb_avg_psnr = em.m_psnr;
-
-				em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 4);
-				em.print(".basis RGBA Avg:         ");
-				s.m_basis_rgba_avg_psnr = em.m_psnr;
-
-				em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 1);
-				em.print(".basis R   Avg:          ");
-				
-				em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 1, 1);
-				em.print(".basis G   Avg:          ");
-				
-				em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 2, 1);
-				em.print(".basis B   Avg:          ");
-
-				if (m_params.m_uastc)
+				if (m_params.m_compute_stats)
 				{
-					em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 3, 1);
-					em.print(".basis A   Avg:          ");
+					printf("Slice: %u\n", slice_index);
 
-					s.m_basis_a_avg_psnr = em.m_psnr;
-				}
+					image_stats& s = m_stats[slice_index];
 
-				em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 0);
-				em.print(".basis 709 Luma:         ");
-				s.m_basis_luma_709_psnr = static_cast<float>(em.m_psnr);
-				s.m_basis_luma_709_ssim = static_cast<float>(em.m_ssim);
+					// TODO: We used to output SSIM (during heavy encoder development), but this slowed down compression too much. We'll be adding it back.
 
-				em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 0, true, true);
-				em.print(".basis 601 Luma:         ");
-				s.m_basis_luma_601_psnr = static_cast<float>(em.m_psnr);
-								
-				if (m_slice_descs.size() == 1)
-				{
-					const uint32_t output_size = comp_size ? (uint32_t)comp_size : (uint32_t)comp_data.size();
-					debug_printf(".basis RGB PSNR per bit/texel*10000: %3.3f\n", 10000.0f * s.m_basis_rgb_avg_psnr / ((output_size * 8.0f) / (slice_desc.m_orig_width * slice_desc.m_orig_height)));
-					debug_printf(".basis Luma 709 PSNR per bit/texel*10000: %3.3f\n", 10000.0f * s.m_basis_luma_709_psnr / ((output_size * 8.0f) / (slice_desc.m_orig_width * slice_desc.m_orig_height)));
-				}
+					image_metrics em;
 
-				if (m_decoded_output_textures_unpacked_bc7[slice_index].get_width())
-				{
-					// ---- BC7 stats
-					em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 3);
-					em.print("BC7 RGB Avg:             ");
-					s.m_bc7_rgb_avg_psnr = em.m_psnr;
+					// ---- .basis stats
+					em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 3);
+					em.print(".basis RGB Avg:          ");
+					s.m_basis_rgb_avg_psnr = em.m_psnr;
 
-					em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 4);
-					em.print("BC7 RGBA Avg:            ");
-					s.m_bc7_rgba_avg_psnr = em.m_psnr;
+					em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 4);
+					em.print(".basis RGBA Avg:         ");
+					s.m_basis_rgba_avg_psnr = em.m_psnr;
 
-					em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 1);
-					em.print("BC7 R   Avg:             ");
+					em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 1);
+					em.print(".basis R   Avg:          ");
 
-					em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 1, 1);
-					em.print("BC7 G   Avg:             ");
+					em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 1, 1);
+					em.print(".basis G   Avg:          ");
 
-					em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 2, 1);
-					em.print("BC7 B   Avg:             ");
+					em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 2, 1);
+					em.print(".basis B   Avg:          ");
 
 					if (m_params.m_uastc)
 					{
-						em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 3, 1);
-						em.print("BC7 A   Avg:             ");
+						em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 3, 1);
+						em.print(".basis A   Avg:          ");
 
-						s.m_bc7_a_avg_psnr = em.m_psnr;
+						s.m_basis_a_avg_psnr = em.m_psnr;
 					}
 
-					em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 0);
-					em.print("BC7 709 Luma:            ");
-					s.m_bc7_luma_709_psnr = static_cast<float>(em.m_psnr);
-					s.m_bc7_luma_709_ssim = static_cast<float>(em.m_ssim);
+					em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 0);
+					em.print(".basis 709 Luma:         ");
+					s.m_basis_luma_709_psnr = static_cast<float>(em.m_psnr);
+					s.m_basis_luma_709_ssim = static_cast<float>(em.m_ssim);
 
-					em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 0, true, true);
-					em.print("BC7 601 Luma:            ");
-					s.m_bc7_luma_601_psnr = static_cast<float>(em.m_psnr);
+					em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked[slice_index], 0, 0, true, true);
+					em.print(".basis 601 Luma:         ");
+					s.m_basis_luma_601_psnr = static_cast<float>(em.m_psnr);
+
+					if (m_slice_descs.size() == 1)
+					{
+						const uint32_t output_size = comp_size ? (uint32_t)comp_size : (uint32_t)comp_data.size();
+						debug_printf(".basis RGB PSNR per bit/texel*10000: %3.3f\n", 10000.0f * s.m_basis_rgb_avg_psnr / ((output_size * 8.0f) / (slice_desc.m_orig_width * slice_desc.m_orig_height)));
+						debug_printf(".basis Luma 709 PSNR per bit/texel*10000: %3.3f\n", 10000.0f * s.m_basis_luma_709_psnr / ((output_size * 8.0f) / (slice_desc.m_orig_width * slice_desc.m_orig_height)));
+					}
+
+					if (m_decoded_output_textures_unpacked_bc7[slice_index].get_width())
+					{
+						// ---- BC7 stats
+						em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 3);
+						em.print("BC7 RGB Avg:             ");
+						s.m_bc7_rgb_avg_psnr = em.m_psnr;
+
+						em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 4);
+						em.print("BC7 RGBA Avg:            ");
+						s.m_bc7_rgba_avg_psnr = em.m_psnr;
+
+						em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 1);
+						em.print("BC7 R   Avg:             ");
+
+						em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 1, 1);
+						em.print("BC7 G   Avg:             ");
+
+						em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 2, 1);
+						em.print("BC7 B   Avg:             ");
+
+						if (m_params.m_uastc)
+						{
+							em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 3, 1);
+							em.print("BC7 A   Avg:             ");
+
+							s.m_bc7_a_avg_psnr = em.m_psnr;
+						}
+
+						em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 0);
+						em.print("BC7 709 Luma:            ");
+						s.m_bc7_luma_709_psnr = static_cast<float>(em.m_psnr);
+						s.m_bc7_luma_709_ssim = static_cast<float>(em.m_ssim);
+
+						em.calc(m_slice_images[slice_index], m_decoded_output_textures_unpacked_bc7[slice_index], 0, 0, true, true);
+						em.print("BC7 601 Luma:            ");
+						s.m_bc7_luma_601_psnr = static_cast<float>(em.m_psnr);
+					}
+
+					if (!m_params.m_uastc)
+					{
+						// ---- Nearly best possible ETC1S stats
+						em.calc(m_slice_images[slice_index], m_best_etc1s_images_unpacked[slice_index], 0, 3);
+						em.print("Unquantized ETC1S RGB Avg:     ");
+						s.m_best_etc1s_rgb_avg_psnr = static_cast<float>(em.m_psnr);
+
+						em.calc(m_slice_images[slice_index], m_best_etc1s_images_unpacked[slice_index], 0, 0);
+						em.print("Unquantized ETC1S 709 Luma:    ");
+						s.m_best_etc1s_luma_709_psnr = static_cast<float>(em.m_psnr);
+						s.m_best_etc1s_luma_709_ssim = static_cast<float>(em.m_ssim);
+
+						em.calc(m_slice_images[slice_index], m_best_etc1s_images_unpacked[slice_index], 0, 0, true, true);
+						em.print("Unquantized ETC1S 601 Luma:    ");
+						s.m_best_etc1s_luma_601_psnr = static_cast<float>(em.m_psnr);
+					}
 				}
 
-				if (!m_params.m_uastc)
+				std::string out_basename;
+				if (m_params.m_out_filename.size())
+					string_get_filename(m_params.m_out_filename.c_str(), out_basename);
+				else if (m_params.m_source_filenames.size())
+					string_get_filename(m_params.m_source_filenames[slice_desc.m_source_file_index].c_str(), out_basename);
+
+				string_remove_extension(out_basename);
+				out_basename = "basis_debug_" + out_basename + string_format("_slice_%u", slice_index);
+
+				if ((!m_params.m_uastc) && (m_frontend.get_params().m_debug_images))
 				{
-					// ---- Nearly best possible ETC1S stats
-					em.calc(m_slice_images[slice_index], m_best_etc1s_images_unpacked[slice_index], 0, 0);
-					em.print("Unquantized ETC1S 709 Luma:    ");
+					// Write "best" ETC1S debug images
+					if (!m_params.m_uastc)
+					{
+						gpu_image best_etc1s_gpu_image(m_best_etc1s_images[slice_index]);
+						best_etc1s_gpu_image.override_dimensions(slice_desc.m_orig_width, slice_desc.m_orig_height);
+						write_compressed_texture_file((out_basename + "_best_etc1s.ktx").c_str(), best_etc1s_gpu_image);
 
-					s.m_best_etc1s_luma_709_psnr = static_cast<float>(em.m_psnr);
-					s.m_best_etc1s_luma_709_ssim = static_cast<float>(em.m_ssim);
+						image best_etc1s_unpacked;
+						best_etc1s_gpu_image.unpack(best_etc1s_unpacked);
+						save_png(out_basename + "_best_etc1s.png", best_etc1s_unpacked);
+					}
+				}
 
-					em.calc(m_slice_images[slice_index], m_best_etc1s_images_unpacked[slice_index], 0, 0, true, true);
-					em.print("Unquantized ETC1S 601 Luma:    ");
+				if (m_params.m_debug_images)
+				{
+					// Write decoded ETC1S/ASTC debug images
+					{
+						gpu_image decoded_etc1s_or_astc(m_decoded_output_textures[slice_index]);
+						decoded_etc1s_or_astc.override_dimensions(slice_desc.m_orig_width, slice_desc.m_orig_height);
+						write_compressed_texture_file((out_basename + "_transcoded_etc1s_or_astc.ktx").c_str(), decoded_etc1s_or_astc);
 
-					s.m_best_etc1s_luma_601_psnr = static_cast<float>(em.m_psnr);
+						image temp(m_decoded_output_textures_unpacked[slice_index]);
+						temp.crop(slice_desc.m_orig_width, slice_desc.m_orig_height);
+						save_png(out_basename + "_transcoded_etc1s_or_astc.png", temp);
+					}
 
-					em.calc(m_slice_images[slice_index], m_best_etc1s_images_unpacked[slice_index], 0, 3);
-					em.print("Unquantized ETC1S RGB Avg:     ");
+					// Write decoded BC7 debug images
+					if (m_decoded_output_textures_bc7[slice_index].get_pixel_width())
+					{
+						gpu_image decoded_bc7(m_decoded_output_textures_bc7[slice_index]);
+						decoded_bc7.override_dimensions(slice_desc.m_orig_width, slice_desc.m_orig_height);
+						write_compressed_texture_file((out_basename + "_transcoded_bc7.ktx").c_str(), decoded_bc7);
 
-					s.m_best_etc1s_rgb_avg_psnr = static_cast<float>(em.m_psnr);
+						image temp(m_decoded_output_textures_unpacked_bc7[slice_index]);
+						temp.crop(slice_desc.m_orig_width, slice_desc.m_orig_height);
+						save_png(out_basename + "_transcoded_bc7.png", temp);
+					}
 				}
 			}
-		
-			std::string out_basename;
-			if (m_params.m_out_filename.size())
-				string_get_filename(m_params.m_out_filename.c_str(), out_basename);
-			else if (m_params.m_source_filenames.size())
-				string_get_filename(m_params.m_source_filenames[slice_desc.m_source_file_index].c_str(), out_basename);
-
-			string_remove_extension(out_basename);
-			out_basename = "basis_debug_" + out_basename + string_format("_slice_%u", slice_index);
-
-			if ((!m_params.m_uastc) && (m_frontend.get_params().m_debug_images))
-			{
-				// Write "best" ETC1S debug images
-				if (!m_params.m_uastc)
-				{
-					gpu_image best_etc1s_gpu_image(m_best_etc1s_images[slice_index]);
-					best_etc1s_gpu_image.override_dimensions(slice_desc.m_orig_width, slice_desc.m_orig_height);
-					write_compressed_texture_file((out_basename + "_best_etc1s.ktx").c_str(), best_etc1s_gpu_image);
-
-					image best_etc1s_unpacked;
-					best_etc1s_gpu_image.unpack(best_etc1s_unpacked);
-					save_png(out_basename + "_best_etc1s.png", best_etc1s_unpacked);
-				}
-			}
-
-			if (m_params.m_debug_images)
-			{
-				// Write decoded ETC1S/ASTC debug images
-				{
-					gpu_image decoded_etc1s_or_astc(m_decoded_output_textures[slice_index]);
-					decoded_etc1s_or_astc.override_dimensions(slice_desc.m_orig_width, slice_desc.m_orig_height);
-					write_compressed_texture_file((out_basename + "_transcoded_etc1s_or_astc.ktx").c_str(), decoded_etc1s_or_astc);
-
-					image temp(m_decoded_output_textures_unpacked[slice_index]);
-					temp.crop(slice_desc.m_orig_width, slice_desc.m_orig_height);
-					save_png(out_basename + "_transcoded_etc1s_or_astc.png", temp);
-				}
-
-				// Write decoded BC7 debug images
-				if (m_decoded_output_textures_bc7[slice_index].get_pixel_width())
-				{
-					gpu_image decoded_bc7(m_decoded_output_textures_bc7[slice_index]);
-					decoded_bc7.override_dimensions(slice_desc.m_orig_width, slice_desc.m_orig_height);
-					write_compressed_texture_file((out_basename + "_transcoded_bc7.ktx").c_str(), decoded_bc7);
-
-					image temp(m_decoded_output_textures_unpacked_bc7[slice_index]);
-					temp.crop(slice_desc.m_orig_width, slice_desc.m_orig_height);
-					save_png(out_basename + "_transcoded_bc7.png", temp);
-				}
-			}
-		}
+		} // if (m_params.m_validate_output_data)
 				
 		return true;
 	}
@@ -2116,4 +2140,252 @@
 		return true;
 	}
 
+	bool basis_parallel_compress(
+		uint32_t total_threads,
+		const basisu::vector<basis_compressor_params>& params_vec,
+		basisu::vector< parallel_results >& results_vec)
+	{
+		assert(g_library_initialized);
+		if (!g_library_initialized)
+		{
+			error_printf("basis_parallel_compress: basisu_encoder_init() MUST be called before using any encoder functionality!\n");
+			return false;
+		}
+
+		assert(total_threads >= 1);
+		total_threads = basisu::maximum<uint32_t>(total_threads, 1);
+
+		job_pool jpool(total_threads);
+
+		results_vec.resize(0);
+		results_vec.resize(params_vec.size());
+
+		std::atomic<bool> result;
+		result = true;
+		
+		std::atomic<bool> opencl_failed;
+		opencl_failed = false;
+
+		for (uint32_t pindex = 0; pindex < params_vec.size(); pindex++)
+		{
+			jpool.add_job([pindex, &params_vec, &results_vec, &result, &opencl_failed] {
+
+				basis_compressor_params params = params_vec[pindex];
+				parallel_results& results = results_vec[pindex];
+
+				interval_timer tm;
+				tm.start();
+
+				basis_compressor c;
+				
+				// Dummy job pool
+				job_pool task_jpool(1);
+				params.m_pJob_pool = &task_jpool;
+				// TODO: Remove this flag entirely
+				params.m_multithreading = true; 
+				
+				// Stop using OpenCL if a failure ever occurs.
+				if (opencl_failed)
+					params.m_use_opencl = false;
+
+				bool status = c.init(params);
+				
+				if (c.get_opencl_failed())
+					opencl_failed = true;
+
+				if (status)
+				{
+					basis_compressor::error_code ec = c.process();
+
+					if (c.get_opencl_failed())
+						opencl_failed = true;
+
+					results.m_error_code = ec;
+
+					if (ec == basis_compressor::cECSuccess)
+					{
+						results.m_basis_file = c.get_output_basis_file();
+						results.m_ktx2_file = c.get_output_ktx2_file();
+						results.m_stats = c.get_stats();
+						results.m_basis_bits_per_texel = c.get_basis_bits_per_texel();
+						results.m_any_source_image_has_alpha = c.get_any_source_image_has_alpha();
+					}
+					else
+					{
+						result = false;
+					}
+				}
+				else
+				{
+					results.m_error_code = basis_compressor::cECFailedInitializing;
+					
+					result = false;
+				}
+
+				results.m_total_time = tm.get_elapsed_secs();
+			} );
+
+		} // pindex
+
+		jpool.wait_for_all();
+
+		if (opencl_failed)
+			error_printf("An OpenCL error occured sometime during compression. The compressor fell back to CPU processing after the failure.\n");
+
+		return result;
+	}
+
+	void* basis_compress(
+		const basisu::vector<image>& source_images,
+		uint32_t flags_and_quality, float uastc_rdo_quality,
+		size_t* pSize,
+		image_stats* pStats)
+	{
+		// Check input parameters
+		if ((!source_images.size()) || (!pSize))
+		{
+			error_printf("basis_compress: Invalid parameter\n");
+			assert(0);
+			return nullptr;
+		}
+
+		*pSize = 0;
+
+		// Initialize a job pool
+		uint32_t num_threads = 1;
+		if (flags_and_quality & cFlagThreaded)
+			num_threads = basisu::maximum<uint32_t>(1, std::thread::hardware_concurrency());
+
+		job_pool jp(num_threads);
+
+		// Initialize the compressor parameter struct
+		basis_compressor_params comp_params;
+		comp_params.m_pJob_pool = &jp;
+
+		comp_params.m_y_flip = (flags_and_quality & cFlagYFlip) != 0;
+		comp_params.m_debug = (flags_and_quality & cFlagDebug) != 0;
+
+		// Copy the largest mipmap level
+		comp_params.m_source_images.resize(1);
+		comp_params.m_source_images[0] = source_images[0];
+
+		// Copy the smaller mipmap levels, if any
+		if (source_images.size() > 1)
+		{
+			comp_params.m_source_mipmap_images.resize(1);
+			comp_params.m_source_mipmap_images[0].resize(source_images.size() - 1);
+
+			for (uint32_t i = 1; i < source_images.size(); i++)
+				comp_params.m_source_mipmap_images[0][i - 1] = source_images[i];
+		}
+				
+		comp_params.m_multithreading = (flags_and_quality & cFlagThreaded) != 0;
+		comp_params.m_use_opencl = (flags_and_quality & cFlagUseOpenCL) != 0;
+
+		comp_params.m_write_output_basis_files = false;
+
+		comp_params.m_perceptual = (flags_and_quality & cFlagSRGB) != 0;
+		comp_params.m_mip_srgb = comp_params.m_perceptual;
+		comp_params.m_mip_gen = (flags_and_quality & (cFlagGenMipsWrap | cFlagGenMipsClamp)) != 0;
+		comp_params.m_mip_wrapping = (flags_and_quality & cFlagGenMipsWrap) != 0;
+
+		comp_params.m_uastc = (flags_and_quality & cFlagUASTC) != 0;
+		if (comp_params.m_uastc)
+		{
+			comp_params.m_pack_uastc_flags = flags_and_quality & cPackUASTCLevelMask;
+			comp_params.m_rdo_uastc = (flags_and_quality & cFlagUASTCRDO) != 0;
+			comp_params.m_rdo_uastc_quality_scalar = uastc_rdo_quality;
+		}
+		else
+			comp_params.m_quality_level = basisu::maximum<uint32_t>(1, flags_and_quality & 255);
+				
+		comp_params.m_create_ktx2_file = (flags_and_quality & cFlagKTX2) != 0;
+						
+		if (comp_params.m_create_ktx2_file)
+		{
+			// Set KTX2 specific parameters.
+			if ((flags_and_quality & cFlagKTX2UASTCSuperCompression) && (comp_params.m_uastc))
+				comp_params.m_ktx2_uastc_supercompression = basist::KTX2_SS_ZSTANDARD;
+
+			comp_params.m_ktx2_srgb_transfer_func = comp_params.m_perceptual;
+		}
+
+		comp_params.m_compute_stats = (pStats != nullptr);
+
+		// Create the compressor, initialize it, and process the input
+		basis_compressor comp;
+		if (!comp.init(comp_params))
+		{
+			error_printf("basis_compress: basis_compressor::init() failed!\n");
+			return nullptr;
+		}
+
+		basis_compressor::error_code ec = comp.process();
+
+		if (ec != basis_compressor::cECSuccess)
+		{
+			error_printf("basis_compress: basis_compressor::process() failed with error code %u\n", (uint32_t)ec);
+			return nullptr;
+		}
+
+		// Get the output file data and return it to the caller
+		void* pFile_data = nullptr;
+		const uint8_vec* pFile_data_vec = comp_params.m_create_ktx2_file ? &comp.get_output_ktx2_file() : &comp.get_output_basis_file();
+
+		pFile_data = malloc(pFile_data_vec->size());
+		if (!pFile_data)
+		{
+			error_printf("basis_compress: Out of memory\n");
+			return nullptr;
+		}
+		memcpy(pFile_data, pFile_data_vec->get_ptr(), pFile_data_vec->size());
+
+		*pSize = pFile_data_vec->size();
+
+		if ((pStats) && (comp.get_stats().size()))
+		{
+			*pStats = comp.get_stats()[0];
+		}
+
+		return pFile_data;
+	}
+
+	void* basis_compress(
+		const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels,
+		uint32_t flags_and_quality, float uastc_rdo_quality,
+		size_t* pSize,
+		image_stats* pStats)
+	{
+		if (!pitch_in_pixels)
+			pitch_in_pixels = width;
+
+		if ((!pImageRGBA) || (!width) || (!height) || (pitch_in_pixels < width) || (!pSize))
+		{
+			error_printf("basis_compress: Invalid parameter\n");
+			assert(0);
+			return nullptr;
+		}
+
+		*pSize = 0;
+
+		if ((width > BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION) || (height > BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION))
+		{
+			error_printf("basis_compress: Image too large\n");
+			return nullptr;
+		}
+
+		// Copy the source image
+		basisu::vector<image> source_image(1);
+		source_image[0].crop(width, height, width, g_black_color, false);
+		for (uint32_t y = 0; y < height; y++)
+			memcpy(source_image[0].get_ptr() + y * width, (const color_rgba*)pImageRGBA + y * pitch_in_pixels, width * sizeof(color_rgba));
+
+		return basis_compress(source_image, flags_and_quality, uastc_rdo_quality, pSize, pStats);
+	}
+
+	void basis_free_data(void* p)
+	{
+		free(p);
+	}
+
 } // namespace basisu
diff --git a/encoder/basisu_comp.h b/encoder/basisu_comp.h
index 2c3af96..aa5ea6f 100644
--- a/encoder/basisu_comp.h
+++ b/encoder/basisu_comp.h
@@ -16,12 +16,11 @@
 #include "basisu_frontend.h"
 #include "basisu_backend.h"
 #include "basisu_basis_file.h"
-#include "../transcoder/basisu_global_selector_palette.h"
 #include "../transcoder/basisu_transcoder.h"
 #include "basisu_uastc_enc.h"
 
-#define BASISU_LIB_VERSION 115
-#define BASISU_LIB_VERSION_STRING "1.15"
+#define BASISU_LIB_VERSION 116
+#define BASISU_LIB_VERSION_STRING "1.16"
 
 #ifndef BASISD_SUPPORT_KTX2
 	#error BASISD_SUPPORT_KTX2 is undefined
@@ -36,6 +35,9 @@
 
 namespace basisu
 {
+	struct opencl_context;
+	typedef opencl_context* opencl_context_ptr;
+
 	const uint32_t BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION = 16384;
 
 	// Allow block's color distance to increase by 1.5 while searching for an alternative nearby endpoint.
@@ -203,13 +205,9 @@
 	struct basis_compressor_params
 	{
 		basis_compressor_params() :
-			m_pSel_codebook(NULL),
 			m_compression_level((int)BASISU_DEFAULT_COMPRESSION_LEVEL, 0, (int)BASISU_MAX_COMPRESSION_LEVEL),
 			m_selector_rdo_thresh(BASISU_DEFAULT_SELECTOR_RDO_THRESH, 0.0f, 1e+10f),
 			m_endpoint_rdo_thresh(BASISU_DEFAULT_ENDPOINT_RDO_THRESH, 0.0f, 1e+10f),
-			m_hybrid_sel_cb_quality_thresh(BASISU_DEFAULT_HYBRID_SEL_CB_QUALITY_THRESH, 0.0f, 1e+10f),
-			m_global_pal_bits(8, 0, ETC1_GLOBAL_SELECTOR_CODEBOOK_MAX_PAL_BITS),
-			m_global_mod_bits(8, 0, basist::etc1_global_palette_entry_modifier::cTotalBits),
 			m_mip_scale(1.0f, .000125f, 4.0f),
 			m_mip_smallest_dimension(1, 1, 16384),
 			m_max_endpoint_clusters(512),
@@ -234,9 +232,8 @@
 
 		void clear()
 		{
-			m_pSel_codebook = NULL;
-
 			m_uastc.clear();
+			m_use_opencl.clear();
 			m_status_output.clear();
 
 			m_source_filenames.clear();
@@ -249,11 +246,8 @@
 
 			m_y_flip.clear();
 			m_debug.clear();
-			m_validate.clear();
+			m_validate_etc1s.clear();
 			m_debug_images.clear();
-			m_global_sel_pal.clear();
-			m_auto_global_sel_pal.clear();
-			m_no_hybrid_sel_cb.clear();
 			m_perceptual.clear();
 			m_no_selector_rdo.clear();
 			m_selector_rdo_thresh.clear();
@@ -269,9 +263,6 @@
 			m_swizzle[2] = 2;
 			m_swizzle[3] = 3;
 			m_renormalize.clear();
-			m_hybrid_sel_cb_quality_thresh.clear();
-			m_global_pal_bits.clear();
-			m_global_mod_bits.clear();
 			m_disable_hierarchical_endpoint_codebooks.clear();
 
 			m_no_endpoint_rdo.clear();
@@ -319,15 +310,16 @@
 			m_ktx2_zstd_supercompression_level.clear();
 			m_ktx2_srgb_transfer_func.clear();
 
+			m_validate_output_data.clear();
+
 			m_pJob_pool = nullptr;
 		}
-				
-		// Pointer to the global selector codebook, or nullptr to not use a global selector codebook
-		const basist::etc1_global_selector_codebook *m_pSel_codebook;
-
+						
 		// True to generate UASTC .basis file data, otherwise ETC1S.
 		bool_param<false> m_uastc;
 
+		bool_param<false> m_use_opencl;
+
 		// If m_read_source_images is true, m_source_filenames (and optionally m_source_alpha_filenames) contains the filenames of PNG images to read. 
 		// Otherwise, the compressor processes the images in m_source_images.
 		basisu::vector<std::string> m_source_filenames;
@@ -353,20 +345,16 @@
 		
 		// Output debug information during compression
 		bool_param<false> m_debug;
-		bool_param<false> m_validate;
+		bool_param<false> m_validate_etc1s;
 		
 		// m_debug_images is pretty slow
 		bool_param<false> m_debug_images;
 
-		// Compression level, from 0 to BASISU_MAX_COMPRESSION_LEVEL (higher is slower)
+		// ETC1S compression level, from 0 to BASISU_MAX_COMPRESSION_LEVEL (higher is slower). 
+		// This parameter controls numerous internal encoding speed vs. compression efficiency/performance tradeoffs.
+		// Note this is NOT the same as the ETC1S quality level, and most users shouldn't change this.
 		param<int> m_compression_level;
-
-		bool_param<false> m_global_sel_pal;
-		bool_param<false> m_auto_global_sel_pal;
-
-		// Frontend/backend codec parameters
-		bool_param<false> m_no_hybrid_sel_cb;
-		
+						
 		// Use perceptual sRGB colorspace metrics instead of linear
 		bool_param<true> m_perceptual;
 
@@ -398,13 +386,10 @@
 
 		bool_param<false> m_renormalize;
 
+		// If true the front end will not use 2 level endpoint codebook searching, for slightly higher quality but much slower execution.
+		// Note some m_compression_level's disable this automatically.
 		bool_param<false> m_disable_hierarchical_endpoint_codebooks;
-
-		// Global/hybrid selector codebook parameters
-		param<float> m_hybrid_sel_cb_quality_thresh;
-		param<int> m_global_pal_bits;
-		param<int> m_global_mod_bits;
-		
+						
 		// mipmap generation parameters
 		bool_param<false> m_mip_gen;
 		param<float> m_mip_scale;
@@ -415,9 +400,9 @@
 		bool_param<true> m_mip_wrapping;
 		bool_param<true> m_mip_fast;
 		param<int> m_mip_smallest_dimension;
-				
+						
 		// Codebook size (quality) control. 
-		// If m_quality_level != -1, it controls the quality level. It ranges from [0,255] or [BASISU_QUALITY_MIN, BASISU_QUALITY_MAX].
+		// If m_quality_level != -1, it controls the quality level. It ranges from [1,255] or [BASISU_QUALITY_MIN, BASISU_QUALITY_MAX].
 		// Otherwise m_max_endpoint_clusters/m_max_selector_clusters controls the codebook sizes directly.
 		uint32_t m_max_endpoint_clusters;
 		uint32_t m_max_selector_clusters;
@@ -444,6 +429,7 @@
 		param<int> m_resample_width;
 		param<int> m_resample_height;
 		param<float> m_resample_factor;
+
 		const basist::basisu_lowlevel_etc1s_transcoder *m_pGlobal_codebooks;
 
 		// KTX2 specific parameters.
@@ -454,21 +440,27 @@
 		param<int> m_ktx2_zstd_supercompression_level;
 		bool_param<false> m_ktx2_srgb_transfer_func;
 
+		bool_param<false> m_validate_output_data;
+
 		job_pool *m_pJob_pool;
 	};
-	
+
+	// Important: basisu_encoder_init() MUST be called first before using this class.
 	class basis_compressor
 	{
 		BASISU_NO_EQUALS_OR_COPY_CONSTRUCT(basis_compressor);
 
 	public:
 		basis_compressor();
+		~basis_compressor();
 
+		// Note it *should* be possible to call init() multiple times with different inputs, but this scenario isn't well tested. Ideally, create 1 object, compress, then delete it.
 		bool init(const basis_compressor_params &params);
 		
 		enum error_code
 		{
 			cECSuccess = 0,
+			cECFailedInitializing,
 			cECFailedReadingSourceImages,
 			cECFailedValidating,
 			cECFailedEncodeUASTC,
@@ -495,9 +487,13 @@
 		double get_basis_bits_per_texel() const { return m_basis_bits_per_texel; }
 		
 		bool get_any_source_image_has_alpha() const { return m_any_source_image_has_alpha; }
+
+		bool get_opencl_failed() const { return m_opencl_failed; }
 								
 	private:
 		basis_compressor_params m_params;
+
+		opencl_context_ptr m_pOpenCL_context;
 		
 		basisu::vector<image> m_slice_images;
 
@@ -509,8 +505,7 @@
 		basisu_backend_slice_desc_vec m_slice_descs;
 
 		uint32_t m_total_blocks;
-		bool m_auto_global_sel_pal;
-
+		
 		basisu_frontend m_frontend;
 		pixel_block_vec m_source_blocks;
 
@@ -536,6 +531,8 @@
 
 		bool m_any_source_image_has_alpha;
 
+		bool m_opencl_failed;
+
 		bool read_source_images();
 		bool extract_source_blocks();
 		bool process_frontend();
@@ -550,6 +547,98 @@
 		void get_dfd(uint8_vec& dfd, const basist::ktx2_header& hdr);
 		bool create_ktx2_file();
 	};
+				
+	// Alternative simple C-style wrapper API around the basis_compressor class. 
+	// This doesn't expose every encoder feature, but it's enough to get going.
+	// Important: basisu_encoder_init() MUST be called first before calling these functions.
+	//
+	// Input parameters:
+	//   source_images: Array of "image" objects, one per mipmap level, largest mipmap level first.
+	// OR
+	//   pImageRGBA: pointer to a 32-bpp RGBx or RGBA raster image, R first in memory, A last. Top scanline first in memory.
+	//   width/height/pitch_in_pixels: dimensions of pImageRGBA
+	//   
+	// flags_and_quality: Combination of the above flags logically OR'd with the ETC1S or UASTC level, i.e. "cFlagSRGB | cFlagGenMipsClamp | cFlagThreaded | 128" or "cFlagSRGB | cFlagGenMipsClamp | cFlagUASTC | cFlagThreaded | cPackUASTCLevelDefault".
+	//	  In ETC1S mode, the lower 8-bits are the ETC1S quality level which ranges from [1,255] (higher=better quality/larger files)
+	//	  In UASTC mode, the lower 8-bits are the UASTC pack level (see cPackUASTCLevelFastest, etc.). Fastest/lowest quality is 0, so be sure to set it correctly. 
+	// 
+	// uastc_rdo_quality: Float UASTC RDO quality level (0=no change, higher values lower quality but increase compressibility, initially try .5-1.5)
+	// 
+	// pSize: Returns the output data's compressed size in bytes
+	// 
+	// Return value is the compressed .basis or .ktx2 file data, or nullptr on failure. Must call basis_free() to free it.
+	enum
+	{
+		cFlagUseOpenCL = 1 << 8,		// use OpenCL if available
+		cFlagThreaded = 1 << 9,			// use multiple threads for compression
+		cFlagDebug = 1 << 10,			// enable debug output
 
+		cFlagKTX2 = 1 << 11,			// generate a KTX2 file
+		cFlagKTX2UASTCSuperCompression = 1 << 12, // use KTX2 Zstd supercompression on UASTC files
+
+		cFlagSRGB = 1 << 13,			// input texture is sRGB, use perceptual colorspace metrics, also use sRGB filtering during mipmap gen, and also sets KTX2 output transfer func to sRGB
+		cFlagGenMipsClamp = 1 << 14,  // generate mipmaps with clamp addressing
+		cFlagGenMipsWrap = 1 << 15,  // generate mipmaps with wrap addressing
+		
+		cFlagYFlip = 1 << 16,		// flip source image on Y axis before compression
+		
+		cFlagUASTC = 1 << 17,		// use UASTC compression vs. ETC1S
+		cFlagUASTCRDO = 1 << 18		// use RDO postprocessing when generating UASTC files (must set uastc_rdo_quality to the quality scalar)
+	};
+
+	// This function accepts an array of source images. 
+	// If more than one image is provided, it's assumed the images form a mipmap pyramid and automatic mipmap generation is disabled.
+	void* basis_compress(
+		const basisu::vector<image> &source_images,
+		uint32_t flags_and_quality, float uastc_rdo_quality,
+		size_t* pSize,
+		image_stats* pStats = nullptr);
+
+	// This function only accepts a single source image.
+	void* basis_compress(
+		const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels,
+		uint32_t flags_and_quality, float uastc_rdo_quality,
+		size_t* pSize,
+		image_stats* pStats = nullptr);
+
+	// Frees the dynamically allocated file data returned by basis_compress().
+	void basis_free_data(void* p);
+
+	// Parallel compression API
+	struct parallel_results
+	{
+		double m_total_time;
+		basis_compressor::error_code m_error_code;
+		uint8_vec m_basis_file;
+		uint8_vec m_ktx2_file;
+		basisu::vector<image_stats> m_stats;
+		double m_basis_bits_per_texel;
+		bool m_any_source_image_has_alpha;
+
+		parallel_results() 
+		{
+			clear();
+		}
+
+		void clear()
+		{
+			m_total_time = 0.0f;
+			m_error_code = basis_compressor::cECFailedInitializing;
+			m_basis_file.clear();
+			m_ktx2_file.clear();
+			m_stats.clear();
+			m_basis_bits_per_texel = 0.0f;
+			m_any_source_image_has_alpha = false;
+		}
+	};
+		
+	// Compresses an array of input textures across total_threads threads using the basis_compressor class.
+	// Compressing multiple textures at a time is substantially more efficient than just compressing one at a time.
+	// total_threads must be >= 1.
+	bool basis_parallel_compress(
+		uint32_t total_threads,
+		const basisu::vector<basis_compressor_params> &params_vec,
+		basisu::vector< parallel_results > &results_vec);
+		
 } // namespace basisu
 
diff --git a/encoder/basisu_enc.cpp b/encoder/basisu_enc.cpp
index a726208..b427215 100644
--- a/encoder/basisu_enc.cpp
+++ b/encoder/basisu_enc.cpp
@@ -13,16 +13,20 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 #include "basisu_enc.h"
-#include "lodepng.h"
 #include "basisu_resampler.h"
 #include "basisu_resampler_filters.h"
 #include "basisu_etc.h"
 #include "../transcoder/basisu_transcoder.h"
 #include "basisu_bc7enc.h"
-#include "apg_bmp.h"
 #include "jpgd.h"
+#include "pvpngreader.h"
+#include "basisu_opencl.h"
 #include <vector>
 
+#define MINIZ_HEADER_FILE_ONLY
+#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
+#include "basisu_miniz.h"
+
 #if defined(_WIN32)
 // For QueryPerformanceCounter/QueryPerformanceFrequency
 #define WIN32_LEAN_AND_MEAN
@@ -158,34 +162,62 @@
 	 { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+007E (~)
 	 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}    // U+007F
 	};
-			
+
+	bool g_library_initialized;
+	std::mutex g_encoder_init_mutex;
+
 	// Encoder library initialization (just call once at startup)
-	void basisu_encoder_init()
+	void basisu_encoder_init(bool use_opencl, bool opencl_force_serialization)
 	{
+		std::lock_guard<std::mutex> lock(g_encoder_init_mutex);
+
+		if (g_library_initialized)
+			return;
+
 		detect_sse41();
 
 		basist::basisu_transcoder_init();
 		pack_etc1_solid_color_init();
 		//uastc_init();
 		bc7enc_compress_block_init(); // must be after uastc_init()
+
+		// Don't bother initializing the OpenCL module at all if it's been completely disabled.
+		if (use_opencl)
+		{
+			opencl_init(opencl_force_serialization);
+		}
+
+		g_library_initialized = true;
 	}
 
-	void error_printf(const char *pFmt, ...)
+	void basisu_encoder_deinit()
 	{
-		char buf[2048];
+		opencl_deinit();
 
-		va_list args;
-		va_start(args, pFmt);
+		g_library_initialized = false;
+	}
+
+	void error_vprintf(const char* pFmt, va_list args)
+	{
+		char buf[8192];
+
 #ifdef _WIN32		
 		vsprintf_s(buf, sizeof(buf), pFmt, args);
 #else
 		vsnprintf(buf, sizeof(buf), pFmt, args);
 #endif
-		va_end(args);
 
 		fprintf(stderr, "ERROR: %s", buf);
 	}
 
+	void error_printf(const char *pFmt, ...)
+	{
+		va_list args;
+		va_start(args, pFmt);
+		error_vprintf(pFmt, args);
+		va_end(args);
+	}
+
 #if defined(_WIN32)
 	inline void query_counter(timer_ticks* pTicks)
 	{
@@ -284,59 +316,6 @@
 	}
 		
 	const uint32_t MAX_32BIT_ALLOC_SIZE = 250000000;
-
-	bool load_bmp(const char* pFilename, image& img)
-	{
-		int w = 0, h = 0;
-		unsigned int n_chans = 0;
-		unsigned char* pImage_data = apg_bmp_read(pFilename, &w, &h, &n_chans);
-				
-		if ((!pImage_data) || (!w) || (!h) || ((n_chans != 3) && (n_chans != 4)))
-		{
-			error_printf("Failed loading .BMP image \"%s\"!\n", pFilename);
-
-			if (pImage_data)
-				apg_bmp_free(pImage_data);
-						
-			return false;
-		}
-
-		if (sizeof(void *) == sizeof(uint32_t))
-		{
-			if ((w * h * n_chans) > MAX_32BIT_ALLOC_SIZE)
-			{
-				error_printf("Image \"%s\" is too large (%ux%u) to process in a 32-bit build!\n", pFilename, w, h);
-
-				if (pImage_data)
-					apg_bmp_free(pImage_data);
-
-				return false;
-			}
-		}
-		
-		img.resize(w, h);
-
-		const uint8_t *pSrc = pImage_data;
-		for (int y = 0; y < h; y++)
-		{
-			color_rgba *pDst = &img(0, y);
-
-			for (int x = 0; x < w; x++)
-			{
-				pDst->r = pSrc[0];
-				pDst->g = pSrc[1];
-				pDst->b = pSrc[2];
-				pDst->a = (n_chans == 3) ? 255 : pSrc[3];
-
-				pSrc += n_chans;
-				++pDst;
-			}
-		}
-
-		apg_bmp_free(pImage_data);
-
-		return true;
-	}
 		
 	bool load_tga(const char* pFilename, image& img)
 	{
@@ -392,53 +371,35 @@
 
 	bool load_png(const uint8_t *pBuf, size_t buf_size, image &img, const char *pFilename)
 	{
+		interval_timer tm;
+		tm.start();
+		
 		if (!buf_size)
 			return false;
 
-		unsigned err = 0, w = 0, h = 0;
-
-		if (sizeof(void*) == sizeof(uint32_t))
+		uint32_t width = 0, height = 0, num_chans = 0;
+		void* pImage = pv_png::load_png(pBuf, buf_size, 4, width, height, num_chans);
+		if (!pBuf)
 		{
-			// Inspect the image first on 32-bit builds, to see if the image would require too much memory.
-			lodepng::State state;
-			err = lodepng_inspect(&w, &h, &state, pBuf, buf_size);
-			if ((err != 0) || (!w) || (!h))
-				return false;
-
-			const uint32_t exepected_alloc_size = w * h * sizeof(uint32_t);
-
-			// If the file is too large on 32-bit builds then just bail now, to prevent causing a memory exception.
-			if (exepected_alloc_size >= MAX_32BIT_ALLOC_SIZE)
-			{
-				error_printf("Image \"%s\" is too large (%ux%u) to process in a 32-bit build!\n", (pFilename != nullptr) ? pFilename : "<memory>", w, h);
-				return false;
-			}
-
-			w = h = 0;
+			error_printf("pv_png::load_png failed while loading image \"%s\"\n", pFilename);
+			return false;
 		}
 
-		std::vector<uint8_t> out;
-		err = lodepng::decode(out, w, h, pBuf, buf_size);
-		if ((err != 0) || (!w) || (!h))
-			return false;
+		img.grant_ownership(reinterpret_cast<color_rgba*>(pImage), width, height);
 
-		if (out.size() != (w * h * 4))
-			return false;
-
-		img.resize(w, h);
-
-		memcpy(img.get_ptr(), &out[0], out.size());
+		//debug_printf("Total load_png() time: %3.3f secs\n", tm.get_elapsed_secs());
 
 		return true;
 	}
 		
 	bool load_png(const char* pFilename, image& img)
 	{
-		std::vector<uint8_t> buffer;
-		unsigned err = lodepng::load_file(buffer, std::string(pFilename));
-		if (err)
+		uint8_vec buffer;
+		if (!read_file_to_vec(pFilename, buffer))
+		{
+			error_printf("load_png: Failed reading file \"%s\"!\n", pFilename);
 			return false;
-
+		}
 
 		return load_png(buffer.data(), buffer.size(), img, pFilename);
 	}
@@ -468,8 +429,6 @@
 
 		if (strcasecmp(pExt, "png") == 0)
 			return load_png(pFilename, img);
-		if (strcasecmp(pExt, "bmp") == 0)
-			return load_bmp(pFilename, img);
 		if (strcasecmp(pExt, "tga") == 0)
 			return load_tga(pFilename, img);
 		if ( (strcasecmp(pExt, "jpg") == 0) || (strcasecmp(pExt, "jfif") == 0) || (strcasecmp(pExt, "jpeg") == 0) )
@@ -482,61 +441,67 @@
 	{
 		if (!img.get_total_pixels())
 			return false;
-
-		const uint32_t MAX_PNG_IMAGE_DIM = 32768;
-		if ((img.get_width() > MAX_PNG_IMAGE_DIM) || (img.get_height() > MAX_PNG_IMAGE_DIM))
-			return false;
-
-		std::vector<uint8_t> out;
-		unsigned err = 0;
 				
+		void* pPNG_data = nullptr;
+		size_t PNG_data_size = 0;
+
 		if (image_save_flags & cImageSaveGrayscale)
 		{
-			uint8_vec g_pixels(img.get_width() * img.get_height());
-			uint8_t *pDst = &g_pixels[0];
+			uint8_vec g_pixels(img.get_total_pixels());
+			uint8_t* pDst = &g_pixels[0];
 
 			for (uint32_t y = 0; y < img.get_height(); y++)
 				for (uint32_t x = 0; x < img.get_width(); x++)
 					*pDst++ = img(x, y)[grayscale_comp];
 
-			err = lodepng::encode(out, (const uint8_t*)&g_pixels[0], img.get_width(), img.get_height(), LCT_GREY, 8);
+			pPNG_data = buminiz::tdefl_write_image_to_png_file_in_memory_ex(g_pixels.data(), img.get_width(), img.get_height(), 1, &PNG_data_size, 1, false);
 		}
 		else
 		{
-			bool has_alpha = img.has_alpha();
-			if ((!has_alpha) || ((image_save_flags & cImageSaveIgnoreAlpha) != 0))
+			bool has_alpha = false;
+			
+			if ((image_save_flags & cImageSaveIgnoreAlpha) == 0)
+				has_alpha = img.has_alpha();
+
+			if (!has_alpha)
 			{
-				const uint64_t total_bytes = (uint64_t)img.get_width() * 3U * (uint64_t)img.get_height();
-				if (total_bytes > INT_MAX)
-					return false;
-				uint8_vec rgb_pixels(static_cast<size_t>(total_bytes));
-				uint8_t *pDst = &rgb_pixels[0];
-								
+				uint8_vec rgb_pixels(img.get_total_pixels() * 3);
+				uint8_t* pDst = &rgb_pixels[0];
+
 				for (uint32_t y = 0; y < img.get_height(); y++)
 				{
+					const color_rgba* pSrc = &img(0, y);
 					for (uint32_t x = 0; x < img.get_width(); x++)
 					{
-						const color_rgba& c = img(x, y);
-						pDst[0] = c.r;
-						pDst[1] = c.g;
-						pDst[2] = c.b;
+						pDst[0] = pSrc->r;
+						pDst[1] = pSrc->g;
+						pDst[2] = pSrc->b;
+						
+						pSrc++;
 						pDst += 3;
 					}
 				}
 
-				err = lodepng::encode(out, (const uint8_t*)& rgb_pixels[0], img.get_width(), img.get_height(), LCT_RGB, 8);
+				pPNG_data = buminiz::tdefl_write_image_to_png_file_in_memory_ex(rgb_pixels.data(), img.get_width(), img.get_height(), 3, &PNG_data_size, 1, false);
 			}
 			else
 			{
-				err = lodepng::encode(out, (const uint8_t*)img.get_ptr(), img.get_width(), img.get_height(), LCT_RGBA, 8);
+				pPNG_data = buminiz::tdefl_write_image_to_png_file_in_memory_ex(img.get_ptr(), img.get_width(), img.get_height(), 4, &PNG_data_size, 1, false);
 			}
 		}
 
-		err = lodepng::save_file(out, std::string(pFilename));
-		if (err)
+		if (!pPNG_data)
 			return false;
 
-		return true;
+		bool status = write_data_to_file(pFilename, pPNG_data, PNG_data_size);
+		if (!status)
+		{
+			error_printf("save_png: Failed writing to filename \"%s\"!\n", pFilename);
+		}
+
+		free(pPNG_data);
+						
+		return status;
 	}
 		
 	bool read_file_to_vec(const char* pFilename, uint8_vec& data)
@@ -1620,7 +1585,8 @@
 
 	void job_pool::job_thread(uint32_t index)
 	{
-		debug_printf("job_pool::job_thread: starting %u\n", index);
+		BASISU_NOTE_UNUSED(index);
+		//debug_printf("job_pool::job_thread: starting %u\n", index);
 		
 		while (true)
 		{
@@ -1656,7 +1622,7 @@
 				m_no_more_jobs.notify_all();
 		}
 
-		debug_printf("job_pool::job_thread: exiting\n");
+		//debug_printf("job_pool::job_thread: exiting\n");
 	}
 
 	// .TGA image loading
@@ -1779,6 +1745,8 @@
 			return nullptr;
 		}
 
+		//const uint32_t bytes_per_line = hdr.m_width * tga_bytes_per_pixel;
+
 		const uint8_t *pSrc = pBuf + sizeof(tga_header);
 		uint32_t bytes_remaining = buf_size - sizeof(tga_header);
 
diff --git a/encoder/basisu_enc.h b/encoder/basisu_enc.h
index 0ce0114..0efeaa4 100644
--- a/encoder/basisu_enc.h
+++ b/encoder/basisu_enc.h
@@ -33,14 +33,23 @@
 // If BASISU_USE_HIGH_PRECISION_COLOR_DISTANCE is 1, quality in perceptual mode will be slightly greater, but at a large increase in encoding CPU time.
 #define BASISU_USE_HIGH_PRECISION_COLOR_DISTANCE (0)
 
+#if BASISU_SUPPORT_SSE
+// Declared in basisu_kernels_imp.h, but we can't include that here otherwise it would lead to circular type errors.
+extern void update_covar_matrix_16x16_sse41(uint32_t num_vecs, const void* pWeighted_vecs, const void* pOrigin, const uint32_t *pVec_indices, void* pMatrix16x16);
+#endif
+
 namespace basisu
 {
 	extern uint8_t g_hamming_dist[256];
 	extern const uint8_t g_debug_font8x8_basic[127 - 32 + 1][8];
 
+	// true if basisu_encoder_init() has been called and returned.
+	extern bool g_library_initialized;
+
 	// Encoder library initialization.
 	// This function MUST be called before encoding anything!
-	void basisu_encoder_init();
+	void basisu_encoder_init(bool use_opencl = false, bool opencl_force_serialization = false);
+	void basisu_encoder_deinit();
 
 	// basisu_kernels_sse.cpp - will be a no-op and g_cpu_supports_sse41 will always be false unless compiled with BASISU_SUPPORT_SSE=1
 	extern void detect_sse41();
@@ -51,8 +60,9 @@
 	const bool g_cpu_supports_sse41 = false;
 #endif
 
+	void error_vprintf(const char* pFmt, va_list args);
 	void error_printf(const char *pFmt, ...);
-
+	
 	// Helpers
 
 	inline uint8_t clamp255(int32_t i)
@@ -170,18 +180,24 @@
 	class running_stat
 	{
 	public:
-		running_stat() :
-			m_n(0),
-			m_old_m(0), m_new_m(0), m_old_s(0), m_new_s(0)
-		{
-		}
+		running_stat() { clear(); }
+
 		void clear()
 		{
 			m_n = 0;
+			m_total = 0;
+			m_old_m = 0;
+			m_new_m = 0;
+			m_old_s = 0;
+			m_new_s = 0;
+			m_min = 0;
+			m_max = 0;
 		}
+
 		void push(double x)
 		{
 			m_n++;
+			m_total += x;
 			if (m_n == 1)
 			{
 				m_old_m = m_new_m = x;
@@ -191,6 +207,7 @@
 			}
 			else
 			{
+				// See Knuth TAOCP vol 2, 3rd edition, page 232
 				m_new_m = m_old_m + (x - m_old_m) / m_n;
 				m_new_s = m_old_s + (x - m_old_m) * (x - m_new_m);
 				m_old_m = m_new_m;
@@ -199,15 +216,23 @@
 				m_max = basisu::maximum(x, m_max);
 			}
 		}
+
 		uint32_t get_num() const
 		{
 			return m_n;
 		}
+
+		double get_total() const
+		{
+			return m_total;
+		}
+
 		double get_mean() const
 		{
 			return (m_n > 0) ? m_new_m : 0.0;
 		}
 
+		// Returns sample variance
 		double get_variance() const
 		{
 			return ((m_n > 1) ? m_new_s / (m_n - 1) : 0.0);
@@ -230,7 +255,7 @@
 
 	private:
 		uint32_t m_n;
-		double m_old_m, m_new_m, m_old_s, m_new_s, m_min, m_max;
+		double m_total, m_old_m, m_new_m, m_old_s, m_new_s, m_min, m_max;
 	};
 
 	// Linear algebra
@@ -401,6 +426,8 @@
 	typedef vec<3, float> vec3F;
 	typedef vec<2, float> vec2F;
 	typedef vec<1, float> vec1F;
+
+	typedef vec<16, float> vec16F;
 		
 	template <uint32_t Rows, uint32_t Cols, typename T>
 	class matrix
@@ -504,6 +531,164 @@
 			[pKeys](uint32_t a, uint32_t b) { return pKeys[a] < pKeys[b]; }
 		);
 	}
+
+	// 1-4 byte direct Radix sort.
+	template <typename T>
+	T* radix_sort(uint32_t num_vals, T* pBuf0, T* pBuf1, uint32_t key_ofs, uint32_t key_size)
+	{
+		assert(key_ofs < sizeof(T));
+		assert((key_size >= 1) && (key_size <= 4));
+
+		uint32_t hist[256 * 4];
+
+		memset(hist, 0, sizeof(hist[0]) * 256 * key_size);
+
+#define BASISU_GET_KEY(p) (*(uint32_t *)((uint8_t *)(p) + key_ofs))
+
+		if (key_size == 4)
+		{
+			T* p = pBuf0;
+			T* q = pBuf0 + num_vals;
+			for (; p != q; p++)
+			{
+				const uint32_t key = BASISU_GET_KEY(p);
+
+				hist[key & 0xFF]++;
+				hist[256 + ((key >> 8) & 0xFF)]++;
+				hist[512 + ((key >> 16) & 0xFF)]++;
+				hist[768 + ((key >> 24) & 0xFF)]++;
+			}
+		}
+		else if (key_size == 3)
+		{
+			T* p = pBuf0;
+			T* q = pBuf0 + num_vals;
+			for (; p != q; p++)
+			{
+				const uint32_t key = BASISU_GET_KEY(p);
+
+				hist[key & 0xFF]++;
+				hist[256 + ((key >> 8) & 0xFF)]++;
+				hist[512 + ((key >> 16) & 0xFF)]++;
+			}
+		}
+		else if (key_size == 2)
+		{
+			T* p = pBuf0;
+			T* q = pBuf0 + (num_vals >> 1) * 2;
+
+			for (; p != q; p += 2)
+			{
+				const uint32_t key0 = BASISU_GET_KEY(p);
+				const uint32_t key1 = BASISU_GET_KEY(p + 1);
+
+				hist[key0 & 0xFF]++;
+				hist[256 + ((key0 >> 8) & 0xFF)]++;
+
+				hist[key1 & 0xFF]++;
+				hist[256 + ((key1 >> 8) & 0xFF)]++;
+			}
+
+			if (num_vals & 1)
+			{
+				const uint32_t key = BASISU_GET_KEY(p);
+
+				hist[key & 0xFF]++;
+				hist[256 + ((key >> 8) & 0xFF)]++;
+			}
+		}
+		else
+		{
+			assert(key_size == 1);
+			if (key_size != 1)
+				return NULL;
+
+			T* p = pBuf0;
+			T* q = pBuf0 + (num_vals >> 1) * 2;
+
+			for (; p != q; p += 2)
+			{
+				const uint32_t key0 = BASISU_GET_KEY(p);
+				const uint32_t key1 = BASISU_GET_KEY(p + 1);
+
+				hist[key0 & 0xFF]++;
+				hist[key1 & 0xFF]++;
+			}
+
+			if (num_vals & 1)
+			{
+				const uint32_t key = BASISU_GET_KEY(p);
+				hist[key & 0xFF]++;
+			}
+		}
+
+		T* pCur = pBuf0;
+		T* pNew = pBuf1;
+
+		for (uint32_t pass = 0; pass < key_size; pass++)
+		{
+			const uint32_t* pHist = &hist[pass << 8];
+
+			uint32_t offsets[256];
+
+			uint32_t cur_ofs = 0;
+			for (uint32_t i = 0; i < 256; i += 2)
+			{
+				offsets[i] = cur_ofs;
+				cur_ofs += pHist[i];
+
+				offsets[i + 1] = cur_ofs;
+				cur_ofs += pHist[i + 1];
+			}
+
+			const uint32_t pass_shift = pass << 3;
+
+			T* p = pCur;
+			T* q = pCur + (num_vals >> 1) * 2;
+
+			for (; p != q; p += 2)
+			{
+				uint32_t c0 = (BASISU_GET_KEY(p) >> pass_shift) & 0xFF;
+				uint32_t c1 = (BASISU_GET_KEY(p + 1) >> pass_shift) & 0xFF;
+
+				if (c0 == c1)
+				{
+					uint32_t dst_offset0 = offsets[c0];
+
+					offsets[c0] = dst_offset0 + 2;
+
+					pNew[dst_offset0] = p[0];
+					pNew[dst_offset0 + 1] = p[1];
+				}
+				else
+				{
+					uint32_t dst_offset0 = offsets[c0]++;
+					uint32_t dst_offset1 = offsets[c1]++;
+
+					pNew[dst_offset0] = p[0];
+					pNew[dst_offset1] = p[1];
+				}
+			}
+
+			if (num_vals & 1)
+			{
+				uint32_t c = (BASISU_GET_KEY(p) >> pass_shift) & 0xFF;
+
+				uint32_t dst_offset = offsets[c];
+				offsets[c] = dst_offset + 1;
+
+				pNew[dst_offset] = *p;
+			}
+
+			T* t = pCur;
+			pCur = pNew;
+			pNew = t;
+		}
+
+		return pCur;
+	}
+
+#undef BASISU_GET_KEY
 	
 	// Very simple job pool with no dependencies.
 	class job_pool
@@ -805,17 +990,28 @@
 			int dg = e1.g - e2.g;
 			int db = e1.b - e2.b;
 
+#if 0
 			int delta_l = dr * 27 + dg * 92 + db * 9;
 			int delta_cr = dr * 128 - delta_l;
 			int delta_cb = db * 128 - delta_l;
-
+															
 			uint32_t id = ((uint32_t)(delta_l * delta_l) >> 7U) +
 				((((uint32_t)(delta_cr * delta_cr) >> 7U) * 26U) >> 7U) +
 				((((uint32_t)(delta_cb * delta_cb) >> 7U) * 3U) >> 7U);
+#else
+			int64_t delta_l = dr * 27 + dg * 92 + db * 9;
+			int64_t delta_cr = dr * 128 - delta_l;
+			int64_t delta_cb = db * 128 - delta_l;
+
+			uint32_t id = ((uint32_t)((delta_l * delta_l) >> 7U)) +
+				((((uint32_t)((delta_cr * delta_cr) >> 7U)) * 26U) >> 7U) +
+				((((uint32_t)((delta_cb * delta_cb) >> 7U)) * 3U) >> 7U);
+#endif
 
 			if (alpha)
 			{
 				int da = (e1.a - e2.a) << 7;
+				// This shouldn't overflow if da is 255 or -255: 29.99 bits after squaring.
 				id += ((uint32_t)(da * da) >> 7U);
 			}
 
@@ -1258,7 +1454,7 @@
             {
                codebook.resize(codebook.size() + 1);
                codebook.back() = cur.m_training_vecs;
-
+										
                if (node_stack.empty())
                   break;
 
@@ -1295,6 +1491,9 @@
 
 			uint32_t total_leaf_nodes = 1;
 
+			//interval_timer tm;
+			//tm.start();
+
 			while ((var_heap.size()) && (total_leaf_nodes < max_size))
 			{
 				const uint32_t node_index = var_heap.get_top_index();
@@ -1315,6 +1514,8 @@
 				}
 			}
 
+			//debug_printf("tree_vector_quant::generate %u: %3.3f secs\n", TrainingVectorType::num_elements, tm.get_elapsed_secs());
+
 			return true;
 		}
 
@@ -1443,17 +1644,32 @@
 		{
 			const uint32_t N = TrainingVectorType::num_elements;
 
-			matrix<N, N, float> cmatrix(cZero);
+			matrix<N, N, float> cmatrix;
 
-			// Compute covariance matrix from weighted input vectors
-			for (uint32_t i = 0; i < node.m_training_vecs.size(); i++)
+			if ((N != 16) || (!g_cpu_supports_sse41))
 			{
-				const TrainingVectorType v(m_training_vecs[node.m_training_vecs[i]].first - node.m_origin);
-				const TrainingVectorType w(static_cast<float>(m_training_vecs[node.m_training_vecs[i]].second) * v);
+				cmatrix.set_zero();
 
-				for (uint32_t x = 0; x < N; x++)
-					for (uint32_t y = x; y < N; y++)
-						cmatrix[x][y] = cmatrix[x][y] + v[x] * w[y];
+				// Compute covariance matrix from weighted input vectors
+				for (uint32_t i = 0; i < node.m_training_vecs.size(); i++)
+				{
+					const TrainingVectorType v(m_training_vecs[node.m_training_vecs[i]].first - node.m_origin);
+					const TrainingVectorType w(static_cast<float>(m_training_vecs[node.m_training_vecs[i]].second) * v);
+
+					for (uint32_t x = 0; x < N; x++)
+						for (uint32_t y = x; y < N; y++)
+							cmatrix[x][y] = cmatrix[x][y] + v[x] * w[y];
+				}
+			}
+			else
+			{
+#if BASISU_SUPPORT_SSE
+				// Specialize the case with 16x16 matrices, which are quite expensive without SIMD.
+				// This SSE function takes pointers to void types, so do some sanity checks.
+				assert(sizeof(TrainingVectorType) == sizeof(float) * 16);
+				assert(sizeof(training_vec_with_weight) == sizeof(std::pair<vec16F, uint64_t>));
+				update_covar_matrix_16x16_sse41(node.m_training_vecs.size(), m_training_vecs.data(), &node.m_origin, node.m_training_vecs.data(), &cmatrix);
+#endif
 			}
 
 			const float renorm_scale = 1.0f / node.m_weight;
@@ -1632,16 +1848,19 @@
 					}
 				}
 
+				// Node is unsplittable using the above algorithm - try something else to split it up.
 				if ((!l_weight) || (!r_weight))
 				{
 					l_children.resize(0);
 					new_l_child.set(0.0f);
 					l_ttsum = 0.0f;
 					l_weight = 0;
+
 					r_children.resize(0);
 					new_r_child.set(0.0f);
 					r_ttsum = 0.0f;
 					r_weight = 0;
+
 					TrainingVectorType firstVec;
 					for (uint32_t i = 0; i < node.m_training_vecs.size(); i++)
 					{
@@ -1847,31 +2066,67 @@
 		uint32_t max_codebook_size, uint32_t max_parent_codebook_size,
 		basisu::vector<uint_vec>& codebook,
 		basisu::vector<uint_vec>& parent_codebook,
-		uint32_t max_threads, job_pool *pJob_pool)
+		uint32_t max_threads, job_pool *pJob_pool,
+		bool even_odd_input_pairs_equal)
 	{
 		typedef bit_hasher<typename Quantizer::training_vec_type> training_vec_bit_hasher;
+		
 		typedef std::unordered_map < typename Quantizer::training_vec_type, weighted_block_group, 
 			training_vec_bit_hasher> group_hash;
 		
+		//interval_timer tm;
+		//tm.start();
+
 		group_hash unique_vecs;
 
+		unique_vecs.reserve(20000);
+
 		weighted_block_group g;
-		g.m_indices.resize(1);
-
-		for (uint32_t i = 0; i < q.get_training_vecs().size(); i++)
+		
+		if (even_odd_input_pairs_equal)
 		{
-			g.m_total_weight = q.get_training_vecs()[i].second;
-			g.m_indices[0] = i;
+			g.m_indices.resize(2);
 
-			auto ins_res = unique_vecs.insert(std::make_pair(q.get_training_vecs()[i].first, g));
+			assert(q.get_training_vecs().size() >= 2 && (q.get_training_vecs().size() & 1) == 0);
 
-			if (!ins_res.second)
+			for (uint32_t i = 0; i < q.get_training_vecs().size(); i += 2)
 			{
-				(ins_res.first)->second.m_total_weight += g.m_total_weight;
-				(ins_res.first)->second.m_indices.push_back(i);
+				assert(q.get_training_vecs()[i].first == q.get_training_vecs()[i + 1].first);
+
+				g.m_total_weight = q.get_training_vecs()[i].second + q.get_training_vecs()[i + 1].second;
+				g.m_indices[0] = i;
+				g.m_indices[1] = i + 1;
+
+				auto ins_res = unique_vecs.insert(std::make_pair(q.get_training_vecs()[i].first, g));
+
+				if (!ins_res.second)
+				{
+					(ins_res.first)->second.m_total_weight += g.m_total_weight;
+					(ins_res.first)->second.m_indices.push_back(i);
+					(ins_res.first)->second.m_indices.push_back(i + 1);
+				}
+			}
+		}
+		else
+		{
+			g.m_indices.resize(1);
+
+			for (uint32_t i = 0; i < q.get_training_vecs().size(); i++)
+			{
+				g.m_total_weight = q.get_training_vecs()[i].second;
+				g.m_indices[0] = i;
+
+				auto ins_res = unique_vecs.insert(std::make_pair(q.get_training_vecs()[i].first, g));
+
+				if (!ins_res.second)
+				{
+					(ins_res.first)->second.m_total_weight += g.m_total_weight;
+					(ins_res.first)->second.m_indices.push_back(i);
+				}
 			}
 		}
 
+		//debug_printf("generate_hierarchical_codebook_threaded: %u training vectors, %u unique training vectors, %3.3f secs\n", q.get_total_training_vecs(), (uint32_t)unique_vecs.size(), tm.get_elapsed_secs());
 		debug_printf("generate_hierarchical_codebook_threaded: %u training vectors, %u unique training vectors\n", q.get_total_training_vecs(), (uint32_t)unique_vecs.size());
 
 		Quantizer group_quant;
@@ -2491,7 +2746,27 @@
 			return *this;
 		}
 
-		image &crop(uint32_t w, uint32_t h, uint32_t p = UINT32_MAX, const color_rgba &background = g_black_color)
+		// pPixels MUST have been allocated using malloc() (basisu::vector will eventually use free() on the pointer).
+		image& grant_ownership(color_rgba* pPixels, uint32_t w, uint32_t h, uint32_t p = UINT32_MAX)
+		{
+			if (p == UINT32_MAX)
+				p = w;
+
+			clear();
+			
+			if ((!p) || (!w) || (!h))
+				return *this;
+
+			m_pixels.grant_ownership(pPixels, p * h, p * h);
+
+			m_width = w;
+			m_height = h;
+			m_pitch = p;
+
+			return *this;
+		}
+
+		image &crop(uint32_t w, uint32_t h, uint32_t p = UINT32_MAX, const color_rgba &background = g_black_color, bool init_image = true)
 		{
 			if (p == UINT32_MAX)
 				p = w;
@@ -2509,15 +2784,25 @@
 			cur_state.swap(m_pixels);
 
 			m_pixels.resize(p * h);
-			
-			for (uint32_t y = 0; y < h; y++)
+
+			if (init_image)
 			{
-				for (uint32_t x = 0; x < w; x++)
+				if (m_width || m_height)
 				{
-					if ((x < m_width) && (y < m_height))
-						m_pixels[x + y * p] = cur_state[x + y * m_pitch];
-					else
-						m_pixels[x + y * p] = background;
+					for (uint32_t y = 0; y < h; y++)
+					{
+						for (uint32_t x = 0; x < w; x++)
+						{
+							if ((x < m_width) && (y < m_height))
+								m_pixels[x + y * p] = cur_state[x + y * m_pitch];
+							else
+								m_pixels[x + y * p] = background;
+						}
+					}
+				}
+				else
+				{
+					m_pixels.set_all(background);
 				}
 			}
 
@@ -2590,9 +2875,25 @@
 
 		const image &extract_block_clamped(color_rgba *pDst, uint32_t src_x, uint32_t src_y, uint32_t w, uint32_t h) const
 		{
-			for (uint32_t y = 0; y < h; y++)
-				for (uint32_t x = 0; x < w; x++)
-					*pDst++ = get_clamped(src_x + x, src_y + y);
+			if (((src_x + w) > m_width) || ((src_y + h) > m_height))
+			{
+				// Slower clamping case
+				for (uint32_t y = 0; y < h; y++)
+					for (uint32_t x = 0; x < w; x++)
+						*pDst++ = get_clamped(src_x + x, src_y + y);
+			}
+			else
+			{
+				const color_rgba* pSrc = &m_pixels[src_x + src_y * m_pitch];
+
+				for (uint32_t y = 0; y < h; y++)
+				{
+					memcpy(pDst, pSrc, w * sizeof(color_rgba));
+					pSrc += m_pitch;
+					pDst += w;
+				}
+			}
+
 			return *this;
 		}
 
@@ -2947,21 +3248,18 @@
 	};
 
 	// Image saving/loading/resampling
-	
+
 	bool load_png(const uint8_t* pBuf, size_t buf_size, image& img, const char* pFilename = nullptr);
 	bool load_png(const char* pFilename, image& img);
 	inline bool load_png(const std::string &filename, image &img) { return load_png(filename.c_str(), img); }
 
-	bool load_bmp(const char* pFilename, image& img);
-	inline bool load_bmp(const std::string &filename, image &img) { return load_bmp(filename.c_str(), img); }
-		
 	bool load_tga(const char* pFilename, image& img);
 	inline bool load_tga(const std::string &filename, image &img) { return load_tga(filename.c_str(), img); }
 
 	bool load_jpg(const char *pFilename, image& img);
 	inline bool load_jpg(const std::string &filename, image &img) { return load_jpg(filename.c_str(), img); }
 	
-	// Currently loads .BMP, .PNG, or .TGA.
+	// Currently loads .PNG, .TGA, or .JPG
 	bool load_image(const char* pFilename, image& img);
 	inline bool load_image(const std::string &filename, image &img) { return load_image(filename.c_str(), img); }
 
@@ -3129,6 +3427,29 @@
 	}
 
 	void fill_buffer_with_random_bytes(void *pBuf, size_t size, uint32_t seed = 1);
+
+	const uint32_t cPixelBlockWidth = 4;
+	const uint32_t cPixelBlockHeight = 4;
+	const uint32_t cPixelBlockTotalPixels = cPixelBlockWidth * cPixelBlockHeight;
+
+	struct pixel_block
+	{
+		color_rgba m_pixels[cPixelBlockHeight][cPixelBlockWidth]; // [y][x]
+
+		inline const color_rgba& operator() (uint32_t x, uint32_t y) const { assert((x < cPixelBlockWidth) && (y < cPixelBlockHeight)); return m_pixels[y][x]; }
+		inline color_rgba& operator() (uint32_t x, uint32_t y) { assert((x < cPixelBlockWidth) && (y < cPixelBlockHeight)); return m_pixels[y][x]; }
+
+		inline const color_rgba* get_ptr() const { return &m_pixels[0][0]; }
+		inline color_rgba* get_ptr() { return &m_pixels[0][0]; }
+
+		inline void clear() { clear_obj(*this); }
+
+		inline bool operator== (const pixel_block& rhs) const
+		{
+			return memcmp(m_pixels, rhs.m_pixels, sizeof(m_pixels)) == 0;
+		}
+	};
+	typedef basisu::vector<pixel_block> pixel_block_vec;
 		
 } // namespace basisu
 
diff --git a/encoder/basisu_etc.cpp b/encoder/basisu_etc.cpp
index 232e896..f8bd0f1 100644
--- a/encoder/basisu_etc.cpp
+++ b/encoder/basisu_etc.cpp
@@ -158,6 +158,22 @@
 				}
 			}
 		}
+
+#if 0
+		for (uint32_t y = 0; y < 64; y++)
+		{
+			printf("{");
+			for (uint32_t x = 0; x < 256; x++)
+			{
+				printf("0x%X", g_etc1_inverse_lookup[y][x]);
+				if (x != 255)
+					printf(",");
+				if (((x & 63) == 63) && (x != 255))
+					printf("\n");
+			}
+			printf("},\n");
+		}
+#endif
 	}
 
 	// Packs solid color blocks efficiently using a set of small precomputed tables.
@@ -1126,6 +1142,7 @@
 				if (!g_eval_dist_tables[inten_table][m_max_comp_spread])
 					continue;
 			}
+
 #if 0
 			if (m_pParams->m_quality <= cETCQualityMedium)
 			{
diff --git a/encoder/basisu_etc.h b/encoder/basisu_etc.h
index 1e3ece4..208f2aa 100644
--- a/encoder/basisu_etc.h
+++ b/encoder/basisu_etc.h
@@ -490,6 +490,35 @@
 			return dc;
 		}
 
+		void get_block_colors_etc1s(color_rgba* pBlock_colors) const
+		{
+			color_rgba b;
+
+			unpack_color5(b, get_base5_color(), true);
+
+			const int* pInten_table = g_etc1_inten_tables[get_inten_table(0)];
+
+			pBlock_colors[0].set(clamp255(b.r + pInten_table[0]), clamp255(b.g + pInten_table[0]), clamp255(b.b + pInten_table[0]), 255);
+			pBlock_colors[1].set(clamp255(b.r + pInten_table[1]), clamp255(b.g + pInten_table[1]), clamp255(b.b + pInten_table[1]), 255);
+			pBlock_colors[2].set(clamp255(b.r + pInten_table[2]), clamp255(b.g + pInten_table[2]), clamp255(b.b + pInten_table[2]), 255);
+			pBlock_colors[3].set(clamp255(b.r + pInten_table[3]), clamp255(b.g + pInten_table[3]), clamp255(b.b + pInten_table[3]), 255);
+		}
+
+		static void get_block_colors_etc1s(color_rgba* pBlock_colors, const color_rgba &base5_color, uint32_t inten_table)
+		{
+			color_rgba b;
+			b.r = (base5_color.r << 3U) | (base5_color.r >> 2U);
+			b.g = (base5_color.g << 3U) | (base5_color.g >> 2U);
+			b.b = (base5_color.b << 3U) | (base5_color.b >> 2U);
+						
+			const int* pInten_table = g_etc1_inten_tables[inten_table];
+
+			pBlock_colors[0].set(clamp255(b.r + pInten_table[0]), clamp255(b.g + pInten_table[0]), clamp255(b.b + pInten_table[0]), 255);
+			pBlock_colors[1].set(clamp255(b.r + pInten_table[1]), clamp255(b.g + pInten_table[1]), clamp255(b.b + pInten_table[1]), 255);
+			pBlock_colors[2].set(clamp255(b.r + pInten_table[2]), clamp255(b.g + pInten_table[2]), clamp255(b.b + pInten_table[2]), 255);
+			pBlock_colors[3].set(clamp255(b.r + pInten_table[3]), clamp255(b.g + pInten_table[3]), clamp255(b.b + pInten_table[3]), 255);
+		}
+
 		void get_block_color(color_rgba& color, uint32_t subblock_index, uint32_t selector_index) const
 		{
 			color_rgba b;
diff --git a/encoder/basisu_frontend.cpp b/encoder/basisu_frontend.cpp
index 324fc8e..1f30a33 100644
--- a/encoder/basisu_frontend.cpp
+++ b/encoder/basisu_frontend.cpp
@@ -19,6 +19,7 @@
 //
 #include "../transcoder/basisu.h"
 #include "basisu_frontend.h"
+#include "basisu_opencl.h"
 #include <unordered_set>
 #include <unordered_map>
 
@@ -43,61 +44,15 @@
 	// TODO - How to handle internal verifies in the basisu lib
 	static inline void handle_verify_failure(int line)
 	{
-			fprintf(stderr, "ERROR: basisu_frontend: verify check failed at line %i!\n", line);
+			error_printf("basisu_frontend: verify check failed at line %i!\n", line);
 			abort();
 	}
 			
 	bool basisu_frontend::init(const params &p)
 	{
-#if 0
-		// HACK HACK
-		FILE* pFile;
-		fopen_s(&pFile, "tv.bin", "rb");
-		if (pFile)
-		{
-			debug_printf("Using tv.bin\n");
-
-			fseek(pFile, 0, SEEK_END);
-			uint32_t size = ftell(pFile);
-			fseek(pFile, 0, SEEK_SET);
-
-			uint32_t tv = size / sizeof(vec6F_quantizer::training_vec_with_weight);
-
-			basisu::vector<vec6F_quantizer::training_vec_with_weight> v(tv);
-			fread(&v[0], 1, sizeof(v[0]) * tv, pFile);
-
-			for (uint32_t i = 0; i < tv; i++)
-				m_endpoint_clusterizer.add_training_vec(v[i].first, v[i].second);
-
-			m_endpoint_clusterizer.generate(16128);
-			basisu::vector<uint_vec> codebook;
-			m_endpoint_clusterizer.retrieve(codebook);
-
-			printf("Generated %u entries\n", (uint32_t)codebook.size());
-
-			fclose(pFile);
-			exit(0);
-		}
-#endif
-
-		if (p.m_use_hybrid_selector_codebooks)
-		{
-			if (!p.m_pGlobal_sel_codebook)
-			{
-				debug_printf("basisu_frontend::init: No global sel codebook!\n");
-				assert(0);
-				return false;
-			}
-		}
-
-		debug_printf("basisu_frontend::init: Multithreaded: %u, NumEndpointClusters: %u, NumSelectorClusters: %u, Perceptual: %u, CompressionLevel: %u\n",
-			p.m_multithreaded, p.m_max_endpoint_clusters, p.m_max_selector_clusters, p.m_perceptual, p.m_compression_level);
-
-		debug_printf("Global sel codebook pal bits: %u, Global sel codebook mod bits: %u, Use hybrid selector codebook: %u, Hybrid codebook quality thresh: %f\n",
-			p.m_num_global_sel_codebook_pal_bits,
-			p.m_num_global_sel_codebook_mod_bits,
-			p.m_use_hybrid_selector_codebooks,
-			p.m_hybrid_codebook_quality_thresh);
+		debug_printf("basisu_frontend::init: Multithreaded: %u, Job pool total threads: %u, NumEndpointClusters: %u, NumSelectorClusters: %u, Perceptual: %u, CompressionLevel: %u\n",
+			p.m_multithreaded, p.m_pJob_pool ? p.m_pJob_pool->get_total_threads() : 0,
+			p.m_max_endpoint_clusters, p.m_max_selector_clusters, p.m_perceptual, p.m_compression_level);
 				
 		if ((p.m_max_endpoint_clusters < 1) || (p.m_max_endpoint_clusters > cMaxEndpointClusters))
 			return false;
@@ -106,8 +61,22 @@
 
 		m_source_blocks.resize(0);
 		append_vector(m_source_blocks, p.m_pSource_blocks, p.m_num_source_blocks);
-
+				
 		m_params = p;
+		
+		if (m_params.m_pOpenCL_context)
+		{
+			BASISU_ASSUME(sizeof(cl_pixel_block) == sizeof(pixel_block));
+
+			// Upload the RGBA pixel blocks a single time.
+			if (!opencl_set_pixel_blocks(m_params.m_pOpenCL_context, m_source_blocks.size(), (cl_pixel_block*)m_source_blocks.data()))
+			{
+				// This is not fatal, we just won't use OpenCL.
+				error_printf("basisu_frontend::init: opencl_set_pixel_blocks() failed\n");
+				m_params.m_pOpenCL_context = nullptr;
+				m_opencl_failed = true;
+			}
+		}
 
 		m_encoded_blocks.resize(m_params.m_num_source_blocks);
 		memset(&m_encoded_blocks[0], 0, m_encoded_blocks.size() * sizeof(m_encoded_blocks[0]));
@@ -194,8 +163,12 @@
 		m_total_blocks = m_params.m_num_source_blocks;
 		m_total_pixels = m_total_blocks * cPixelBlockTotalPixels;
 
+		// Encode the initial high quality ETC1S texture
+
 		init_etc1_images();
 
+		// First quantize the ETC1S endpoints
+
 		if (m_params.m_pGlobal_codebooks)
 		{
 			init_global_codebooks();
@@ -205,16 +178,26 @@
 			init_endpoint_training_vectors();
 
 			generate_endpoint_clusters();
-				
+
 			for (uint32_t refine_endpoint_step = 0; refine_endpoint_step < m_num_endpoint_codebook_iterations; refine_endpoint_step++)
 			{
-				BASISU_FRONTEND_VERIFY(check_etc1s_constraints());
+				if (m_params.m_validate)
+				{
+					BASISU_FRONTEND_VERIFY(check_etc1s_constraints());
+
+					BASISU_FRONTEND_VERIFY(validate_endpoint_cluster_hierarchy(false));
+				}
 
 				if (refine_endpoint_step)
 				{
 					introduce_new_endpoint_clusters();
 				}
 
+				if (m_params.m_validate)
+				{
+					BASISU_FRONTEND_VERIFY(validate_endpoint_cluster_hierarchy(false));
+				}
+
 				generate_endpoint_codebook(refine_endpoint_step);
 
 				if ((m_params.m_debug_images) && (m_params.m_dump_endpoint_clusterization))
@@ -236,7 +219,7 @@
 					if ((m_params.m_tex_type == basist::cBASISTexTypeVideoFrames) && (!refine_endpoint_step) && (m_num_endpoint_codebook_iterations == 1))
 					{
 						eliminate_redundant_or_empty_endpoint_clusters();
-						generate_endpoint_codebook(refine_endpoint_step);
+						generate_endpoint_codebook(basisu::maximum(1U, refine_endpoint_step));
 					}
 
 					if ((m_params.m_debug_images) && (m_params.m_dump_endpoint_clusterization))
@@ -250,22 +233,37 @@
 						dump_endpoint_clusterization_visualization(buf, true);
 					}
 				}
+
+				if (m_params.m_validate)
+				{
+					BASISU_FRONTEND_VERIFY(validate_endpoint_cluster_hierarchy(false));
+				}
 						
 				eliminate_redundant_or_empty_endpoint_clusters();
 
+				if (m_params.m_validate)
+				{
+					BASISU_FRONTEND_VERIFY(validate_endpoint_cluster_hierarchy(false));
+				}
+
 				if (m_params.m_debug_stats)
 					debug_printf("Total endpoint clusters: %u\n", (uint32_t)m_endpoint_clusters.size());
 
 				if (early_out)
 					break;
 			}
-
-			BASISU_FRONTEND_VERIFY(check_etc1s_constraints());
+			
+			if (m_params.m_validate)
+			{
+				BASISU_FRONTEND_VERIFY(check_etc1s_constraints());
+			}
 
 			generate_block_endpoint_clusters();
 
 			create_initial_packed_texture();
 
+			// Now quantize the ETC1S selectors
+
 			generate_selector_clusters();
 
 			if (m_use_hierarchical_selector_codebooks)
@@ -276,12 +274,12 @@
 				create_optimized_selector_codebook(0);
 
 				find_optimal_selector_clusters_for_each_block();
-			
+							
 				introduce_special_selector_clusters();
 			}
 			else
 			{
-				const uint32_t num_refine_selector_steps = m_params.m_pGlobal_sel_codebook ? 1 : m_num_selector_codebook_iterations;
+				const uint32_t num_refine_selector_steps = m_num_selector_codebook_iterations;
 				for (uint32_t refine_selector_steps = 0; refine_selector_steps < num_refine_selector_steps; refine_selector_steps++)
 				{
 					create_optimized_selector_codebook(refine_selector_steps);
@@ -289,7 +287,7 @@
 					find_optimal_selector_clusters_for_each_block();
 
 					introduce_special_selector_clusters();
-				
+
 					if ((m_params.m_compression_level >= 4) || (m_params.m_tex_type == basist::cBASISTexTypeVideoFrames))
 					{
 						if (!refine_block_endpoints_given_selectors())
@@ -297,7 +295,7 @@
 					}
 				}
 			}
-						
+				
 			optimize_selector_codebook();
 
 			if (m_params.m_debug_stats)
@@ -574,9 +572,6 @@
 	{
 		debug_printf("introduce_special_selector_clusters\n");
 
-		if (m_params.m_pGlobal_sel_codebook)
-			return;
-
 		uint32_t total_blocks_relocated = 0;
 		const uint32_t initial_selector_clusters = (uint32_t)m_selector_cluster_block_indices.size();
 
@@ -722,23 +717,15 @@
 		}
 
 		basisu::vector<etc_block> new_optimized_cluster_selectors(m_optimized_cluster_selectors.size() ? total_new_entries : 0);
-		basist::etc1_global_selector_codebook_entry_id_vec new_optimized_cluster_selector_global_cb_ids(m_optimized_cluster_selector_global_cb_ids.size() ? total_new_entries : 0);
 		basisu::vector<uint_vec> new_selector_cluster_indices(m_selector_cluster_block_indices.size() ? total_new_entries : 0);
-		bool_vec new_selector_cluster_uses_global_cb(m_selector_cluster_uses_global_cb.size() ? total_new_entries : 0);
 
 		for (uint32_t i = 0; i < total_new_entries; i++)
 		{
 			if (m_optimized_cluster_selectors.size())
 				new_optimized_cluster_selectors[i] = m_optimized_cluster_selectors[new_to_old[i]];
 
-			if (m_optimized_cluster_selector_global_cb_ids.size())
-				new_optimized_cluster_selector_global_cb_ids[i] = m_optimized_cluster_selector_global_cb_ids[new_to_old[i]];
-
 			//if (m_selector_cluster_block_indices.size())
 			//	new_selector_cluster_indices[i] = m_selector_cluster_block_indices[new_to_old[i]];
-
-			if (m_selector_cluster_uses_global_cb.size())
-				new_selector_cluster_uses_global_cb[i] = m_selector_cluster_uses_global_cb[new_to_old[i]];
 		}
 
 		for (uint32_t i = 0; i < m_block_selector_cluster_index.size(); i++)
@@ -747,9 +734,7 @@
 		}
 				
 		m_optimized_cluster_selectors.swap(new_optimized_cluster_selectors);
-		m_optimized_cluster_selector_global_cb_ids.swap(new_optimized_cluster_selector_global_cb_ids);
 		m_selector_cluster_block_indices.swap(new_selector_cluster_indices);
-		m_selector_cluster_uses_global_cb.swap(new_selector_cluster_uses_global_cb);
 
 		// This isn't strictly necessary - doing it for completeness/future sanity.
 		if (m_selector_clusters_within_each_parent_cluster.size())
@@ -771,66 +756,93 @@
 				
 		m_etc1_blocks_etc1s.resize(m_total_blocks);
 
-		const uint32_t N = 4096;
-		for (uint32_t block_index_iter = 0; block_index_iter < m_total_blocks; block_index_iter += N)
+		bool use_cpu = true;
+								
+		if (m_params.m_pOpenCL_context)
 		{
-			const uint32_t first_index = block_index_iter;                                        
-			const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);       
-
-#ifndef __EMSCRIPTEN__
-			m_params.m_pJob_pool->add_job( [this, first_index, last_index] {
-#endif
-
-				for (uint32_t block_index = first_index; block_index < last_index; block_index++) 
-				{
-					const pixel_block &source_blk = get_source_pixel_block(block_index);
-
-					etc1_optimizer optimizer;
-					etc1_optimizer::params optimizer_params;
-					etc1_optimizer::results optimizer_results;
-			
-					if (m_params.m_compression_level == 0)
-						optimizer_params.m_quality = cETCQualityFast;
-					else if (m_params.m_compression_level == 1)
-						optimizer_params.m_quality = cETCQualityMedium;
-					else if (m_params.m_compression_level == BASISU_MAX_COMPRESSION_LEVEL)
-						optimizer_params.m_quality = cETCQualityUber;
+			uint32_t total_perms = 64;
+			if (m_params.m_compression_level == 0)
+				total_perms = 4;
+			else if (m_params.m_compression_level == 1)
+				total_perms = 16;
+			else if (m_params.m_compression_level == BASISU_MAX_COMPRESSION_LEVEL)
+				total_perms = OPENCL_ENCODE_ETC1S_MAX_PERMS;
 						
-					optimizer_params.m_num_src_pixels = 16;
-					optimizer_params.m_pSrc_pixels = source_blk.get_ptr();
-					optimizer_params.m_perceptual = m_params.m_perceptual;
-
-					uint8_t selectors[16];
-					optimizer_results.m_pSelectors = selectors;
-					optimizer_results.m_n = 16;
-
-					optimizer.init(optimizer_params, optimizer_results);
-					if (!optimizer.compute())
-						BASISU_FRONTEND_VERIFY(false);
-
-					etc_block &blk = m_etc1_blocks_etc1s[block_index];
-
-					memset(&blk, 0, sizeof(blk));
-					blk.set_block_color5_etc1s(optimizer_results.m_block_color_unscaled);
-					blk.set_inten_tables_etc1s(optimizer_results.m_block_inten_table);
-					blk.set_flip_bit(true);
-
-					for (uint32_t y = 0; y < 4; y++)
-						for (uint32_t x = 0; x < 4; x++)
-							blk.set_selector(x, y, selectors[x + y * 4]);
-				}
-
-#ifndef __EMSCRIPTEN__
-			} );
-#endif
-
+			bool status = opencl_encode_etc1s_blocks(m_params.m_pOpenCL_context, m_etc1_blocks_etc1s.data(), m_params.m_perceptual, total_perms);
+			if (status)
+				use_cpu = false;
+			else
+			{
+				error_printf("basisu_frontend::init_etc1_images: opencl_encode_etc1s_blocks() failed! Using CPU.\n");
+				m_params.m_pOpenCL_context = nullptr;
+				m_opencl_failed = true;
+			}
 		}
+		
+		if (use_cpu)
+		{
+			const uint32_t N = 4096;
+			for (uint32_t block_index_iter = 0; block_index_iter < m_total_blocks; block_index_iter += N)
+			{
+				const uint32_t first_index = block_index_iter;
+				const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
 
 #ifndef __EMSCRIPTEN__
-		m_params.m_pJob_pool->wait_for_all();
+				m_params.m_pJob_pool->add_job([this, first_index, last_index] {
 #endif
 
-		debug_printf("Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
+					for (uint32_t block_index = first_index; block_index < last_index; block_index++)
+					{
+						const pixel_block& source_blk = get_source_pixel_block(block_index);
+
+						etc1_optimizer optimizer;
+						etc1_optimizer::params optimizer_params;
+						etc1_optimizer::results optimizer_results;
+
+						if (m_params.m_compression_level == 0)
+							optimizer_params.m_quality = cETCQualityFast;
+						else if (m_params.m_compression_level == 1)
+							optimizer_params.m_quality = cETCQualityMedium;
+						else if (m_params.m_compression_level == BASISU_MAX_COMPRESSION_LEVEL)
+							optimizer_params.m_quality = cETCQualityUber;
+
+						optimizer_params.m_num_src_pixels = 16;
+						optimizer_params.m_pSrc_pixels = source_blk.get_ptr();
+						optimizer_params.m_perceptual = m_params.m_perceptual;
+
+						uint8_t selectors[16];
+						optimizer_results.m_pSelectors = selectors;
+						optimizer_results.m_n = 16;
+
+						optimizer.init(optimizer_params, optimizer_results);
+						if (!optimizer.compute())
+							BASISU_FRONTEND_VERIFY(false);
+
+						etc_block& blk = m_etc1_blocks_etc1s[block_index];
+
+						memset(&blk, 0, sizeof(blk));
+						blk.set_block_color5_etc1s(optimizer_results.m_block_color_unscaled);
+						blk.set_inten_tables_etc1s(optimizer_results.m_block_inten_table);
+						blk.set_flip_bit(true);
+
+						for (uint32_t y = 0; y < 4; y++)
+							for (uint32_t x = 0; x < 4; x++)
+								blk.set_selector(x, y, selectors[x + y * 4]);
+					}
+
+#ifndef __EMSCRIPTEN__
+					});
+#endif
+
+			}
+
+#ifndef __EMSCRIPTEN__
+			m_params.m_pJob_pool->wait_for_all();
+#endif
+
+		} // use_cpu
+		 
+		debug_printf("init_etc1_images: Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
 	}
 
 	void basisu_frontend::init_endpoint_training_vectors()
@@ -889,13 +901,15 @@
 		const uint32_t parent_codebook_size = (m_params.m_max_endpoint_clusters >= 256) ? BASISU_ENDPOINT_PARENT_CODEBOOK_SIZE : 0;
 		uint32_t max_threads = 0;
 		max_threads = m_params.m_multithreaded ? minimum<int>(std::thread::hardware_concurrency(), cMaxCodebookCreationThreads) : 0;
+		if (m_params.m_pJob_pool)
+			max_threads = minimum<int>((int)m_params.m_pJob_pool->get_total_threads(), max_threads);
 
-		debug_printf("Using %u threads to create codebook\n", max_threads);
+		debug_printf("max_threads: %u\n", max_threads);
 		bool status = generate_hierarchical_codebook_threaded(m_endpoint_clusterizer,
 			m_params.m_max_endpoint_clusters, m_use_hierarchical_endpoint_codebooks ? parent_codebook_size : 0,
 			m_endpoint_clusters,
 			m_endpoint_parent_clusters,
-			max_threads, m_params.m_pJob_pool);
+			max_threads, m_params.m_pJob_pool, true);
 		BASISU_FRONTEND_VERIFY(status);
 
 		if (m_use_hierarchical_endpoint_codebooks)
@@ -940,6 +954,9 @@
 				for (uint32_t j = 0; j < cluster.size(); j++)
 				{
 					const uint32_t block_index = cluster[j] >> 1;
+					
+					BASISU_FRONTEND_VERIFY(block_index < m_block_parent_endpoint_cluster.size());
+
 					if (!j)
 					{
 						parent_cluster_index = m_block_parent_endpoint_cluster[block_index];
@@ -956,6 +973,7 @@
 			debug_printf("Total endpoint clusters: %u, parent clusters: %u\n", (uint32_t)m_endpoint_clusters.size(), (uint32_t)m_endpoint_parent_clusters.size());
 	}
 
+	// Iterate through each array of endpoint cluster block indices and set the m_block_endpoint_clusters_indices[][] array to indicaste which cluster index each block uses.
 	void basisu_frontend::generate_block_endpoint_clusters()
 	{
 		m_block_endpoint_clusters_indices.resize(m_total_blocks);
@@ -974,11 +992,14 @@
 			} // cluster_indices_iter
 		}
 
-		for (uint32_t block_index = 0; block_index < m_total_blocks; block_index++)
+		if (m_params.m_validate)
 		{
-			uint32_t cluster_0 = m_block_endpoint_clusters_indices[block_index][0];
-			uint32_t cluster_1 = m_block_endpoint_clusters_indices[block_index][1];
-			BASISU_FRONTEND_VERIFY(cluster_0 == cluster_1);
+			for (uint32_t block_index = 0; block_index < m_total_blocks; block_index++)
+			{
+				uint32_t cluster_0 = m_block_endpoint_clusters_indices[block_index][0];
+				uint32_t cluster_1 = m_block_endpoint_clusters_indices[block_index][1];
+				BASISU_FRONTEND_VERIFY(cluster_0 == cluster_1);
+			}
 		}
 	}
 
@@ -989,6 +1010,7 @@
 		m_endpoint_clusters_within_each_parent_cluster.resize(0);
 		m_endpoint_clusters_within_each_parent_cluster.resize(m_endpoint_parent_clusters.size());
 
+		// Note: It's possible that some blocks got moved into the same cluster, but live in different parent clusters.
 		for (uint32_t block_index = 0; block_index < m_total_blocks; block_index++)
 		{
 			const uint32_t cluster_index = m_block_endpoint_clusters_indices[block_index][0];
@@ -1125,6 +1147,8 @@
 
 		std::unordered_set<uint32_t> ignore_cluster;
 
+		uint32_t total_new_clusters = 0;
+
 		while (num_new_endpoint_clusters)
 		{
 			if (m_subblock_endpoint_quant_err_vec.size() == 0)
@@ -1173,10 +1197,14 @@
 			cluster_sizes[subblock_to_move.m_cluster_index] -= 2;
 						
 			ignore_cluster.insert(subblock_to_move.m_cluster_index);
+						
+			total_new_clusters++;
 
 			num_new_endpoint_clusters--;
 		}
 
+		debug_printf("Introduced %i new endpoint clusters\n", total_new_clusters);
+
 		for (uint32_t i = 0; i < num_orig_endpoint_clusters; i++)
 		{
 			uint_vec &cluster_indices = m_endpoint_clusters[i];
@@ -1200,150 +1228,433 @@
 		generate_block_endpoint_clusters();
 	}
 
+	struct color_rgba_hasher
+	{
+		inline std::size_t operator()(const color_rgba& k) const
+		{
+			uint32_t v = *(const uint32_t*)&k;
+			
+			//return bitmix32(v);
+			
+			//v ^= (v << 10);
+			//v ^= (v >> 12);
+			
+			return v;
+		}
+	};
+		
 	// Given each endpoint cluster, gather all the block pixels which are in that cluster and compute optimized ETC1S endpoints for them.
 	// TODO: Don't optimize endpoint clusters which haven't changed.
+	// If step>=1, we check to ensure the new endpoint values actually decrease quantization error.
 	void basisu_frontend::generate_endpoint_codebook(uint32_t step)
 	{
 		debug_printf("generate_endpoint_codebook\n");
+		
+		interval_timer tm;
+		tm.start();
 
 		m_endpoint_cluster_etc_params.resize(m_endpoint_clusters.size());
 
-		const uint32_t N = 128;
-		for (uint32_t cluster_index_iter = 0; cluster_index_iter < m_endpoint_clusters.size(); cluster_index_iter += N)
+		bool use_cpu = true;
+		// TODO: Get this working when step>0
+		if (m_params.m_pOpenCL_context && !step)
 		{
-			const uint32_t first_index = cluster_index_iter;                                    
-			const uint32_t last_index = minimum<uint32_t>((uint32_t)m_endpoint_clusters.size(), cluster_index_iter + N);   
+			const uint32_t total_clusters = m_endpoint_clusters.size();
 
-#ifndef __EMSCRIPTEN__
-			m_params.m_pJob_pool->add_job( [this, first_index, last_index, step ] {
-#endif
+			basisu::vector<cl_pixel_cluster> pixel_clusters(total_clusters);
+			
+			std::vector<color_rgba> input_pixels;
+			input_pixels.reserve(m_total_blocks * 16);
 
-				for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++)
+			std::vector<uint32_t> pixel_weights;
+			pixel_weights.reserve(m_total_blocks * 16);
+
+			uint_vec cluster_sizes(total_clusters);
+
+			//typedef basisu::hash_map<color_rgba, uint32_t, color_rgba_hasher> color_hasher_type;
+			//color_hasher_type color_hasher;
+			//color_hasher.reserve(2048);
+
+			interval_timer hash_tm;
+			hash_tm.start();
+
+			basisu::vector<uint32_t> colors, colors2;
+			colors.reserve(65536);
+			colors2.reserve(65536);
+
+			for (uint32_t cluster_index = 0; cluster_index < m_endpoint_clusters.size(); cluster_index++)
+			{
+				const basisu::vector<uint32_t>& cluster_indices = m_endpoint_clusters[cluster_index];
+				assert((cluster_indices.size() & 1) == 0);
+
+#if 0
+				uint64_t first_pixel_index = input_pixels.size();
+				const uint32_t total_pixels = 16 * (cluster_indices.size() / 2);
+
+				input_pixels.resize(input_pixels.size() + total_pixels);
+				pixel_weights.resize(pixel_weights.size() + total_pixels);
+
+				uint64_t dst_ofs = first_pixel_index;
+				
+				uint64_t total_r = 0, total_g = 0, total_b = 0;
+				for (uint32_t cluster_indices_iter = 0; cluster_indices_iter < cluster_indices.size(); cluster_indices_iter++)
 				{
-					const basisu::vector<uint32_t>& cluster_indices = m_endpoint_clusters[cluster_index];
+					const uint32_t subblock_index = cluster_indices[cluster_indices_iter] & 1;
+					if (subblock_index)
+						continue;
 
-					BASISU_FRONTEND_VERIFY(cluster_indices.size());
+					const uint32_t block_index = cluster_indices[cluster_indices_iter] >> 1;
+					const color_rgba* pBlock_pixels = get_source_pixel_block(block_index).get_ptr();
 
-					const uint32_t total_pixels = (uint32_t)cluster_indices.size() * 8;
-
-					basisu::vector<color_rgba> cluster_pixels(total_pixels);
-
-					for (uint32_t cluster_indices_iter = 0; cluster_indices_iter < cluster_indices.size(); cluster_indices_iter++)
+					for (uint32_t i = 0; i < 16; i++)
 					{
-						const uint32_t block_index = cluster_indices[cluster_indices_iter] >> 1;
-						const uint32_t subblock_index = cluster_indices[cluster_indices_iter] & 1;
+						input_pixels[dst_ofs] = pBlock_pixels[i];
+						pixel_weights[dst_ofs] = 1;
+						dst_ofs++;
 
-						const bool flipped = true;
-
-						const color_rgba *pBlock_pixels = get_source_pixel_block(block_index).get_ptr();
-
-						for (uint32_t pixel_index = 0; pixel_index < 8; pixel_index++)
-						{
-							const color_rgba &c = pBlock_pixels[g_etc1_pixel_indices[flipped][subblock_index][pixel_index]];
-							cluster_pixels[cluster_indices_iter * 8 + pixel_index] = c;
-						}
+						total_r += pBlock_pixels[i].r;
+						total_g += pBlock_pixels[i].g;
+						total_b += pBlock_pixels[i].b;
 					}
+				}
 
-					endpoint_cluster_etc_params new_subblock_params;
-						
+				//printf("%i %f %f %f\n", cluster_index, total_r / (float)total_pixels, total_g / (float)total_pixels, total_b / (float)total_pixels);
+
+				pixel_clusters[cluster_index].m_first_pixel_index = first_pixel_index;
+				pixel_clusters[cluster_index].m_total_pixels = total_pixels;
+				cluster_sizes[cluster_index] = total_pixels;
+#elif 1
+				colors.resize(cluster_indices.size() * 8);
+				colors2.resize(cluster_indices.size() * 8);
+				uint32_t dst_ofs = 0;
+
+				for (uint32_t cluster_indices_iter = 0; cluster_indices_iter < cluster_indices.size(); cluster_indices_iter++)
+				{
+					const uint32_t subblock_index = cluster_indices[cluster_indices_iter] & 1;
+					if (subblock_index)
+						continue;
+
+					const uint32_t block_index = cluster_indices[cluster_indices_iter] >> 1;
+					const color_rgba* pBlock_pixels = get_source_pixel_block(block_index).get_ptr();
+
+					memcpy(colors.data() + dst_ofs, pBlock_pixels, sizeof(color_rgba) * 16);
+					dst_ofs += 16;
+
+				} // cluster_indices_iter
+
+				uint32_t* pSorted = radix_sort(colors.size(), colors.data(), colors2.data(), 0, 3);
+
+				const uint64_t first_pixel_index = input_pixels.size();
+
+				uint32_t prev_color = 0, cur_weight = 0;
+																
+				for (uint32_t i = 0; i < colors.size(); i++)
+				{
+					uint32_t cur_color = pSorted[i];
+					if (cur_color == prev_color)
 					{
-						etc1_optimizer optimizer;
-						etc1_solution_coordinates solutions[2];
-
-						etc1_optimizer::params cluster_optimizer_params;
-						cluster_optimizer_params.m_num_src_pixels = total_pixels;
-						cluster_optimizer_params.m_pSrc_pixels = &cluster_pixels[0];
-
-						cluster_optimizer_params.m_use_color4 = false;
-						cluster_optimizer_params.m_perceptual = m_params.m_perceptual;
-
-						if (m_params.m_compression_level <= 1)
-							cluster_optimizer_params.m_quality = cETCQualityMedium;
-						else if (m_params.m_compression_level == BASISU_MAX_COMPRESSION_LEVEL)
-							cluster_optimizer_params.m_quality = cETCQualityUber;
-
-						etc1_optimizer::results cluster_optimizer_results;
-
-						basisu::vector<uint8_t> cluster_selectors(total_pixels);
-						cluster_optimizer_results.m_n = total_pixels;
-						cluster_optimizer_results.m_pSelectors = &cluster_selectors[0];
-
-						optimizer.init(cluster_optimizer_params, cluster_optimizer_results);
-
-						if (!optimizer.compute())
-							BASISU_FRONTEND_VERIFY(false);
-
-						new_subblock_params.m_color_unscaled[0] = cluster_optimizer_results.m_block_color_unscaled;
-						new_subblock_params.m_inten_table[0] = cluster_optimizer_results.m_block_inten_table;
-						new_subblock_params.m_color_error[0] = cluster_optimizer_results.m_error;
-					} 
-
-					endpoint_cluster_etc_params &prev_etc_params = m_endpoint_cluster_etc_params[cluster_index];
-
-					bool use_new_subblock_params = false;
-					if ((!step) || (!prev_etc_params.m_valid))
-						use_new_subblock_params = true;
+						if (++cur_weight == 0)
+							cur_weight--;
+					}
 					else
 					{
-						assert(prev_etc_params.m_valid);
-
-						uint64_t total_prev_err = 0;
-								
+						if (cur_weight)
 						{
-							color_rgba block_colors[4];
+							input_pixels.push_back(*(const color_rgba*)&prev_color);
+							pixel_weights.push_back(cur_weight);
+						}
 
-							etc_block::get_block_colors5(block_colors, prev_etc_params.m_color_unscaled[0], prev_etc_params.m_inten_table[0], false);
+						prev_color = cur_color;
+						cur_weight = 1;
+					}
+				}
 
-							uint64_t total_err = 0;
+				if (cur_weight)
+				{
+					input_pixels.push_back(*(const color_rgba*)&prev_color);
+					pixel_weights.push_back(cur_weight);
+				}
 
-							for (uint32_t i = 0; i < total_pixels; i++)
+				uint32_t total_unique_pixels = (uint32_t)(input_pixels.size() - first_pixel_index);
+
+				pixel_clusters[cluster_index].m_first_pixel_index = first_pixel_index;
+				pixel_clusters[cluster_index].m_total_pixels = total_unique_pixels;
+
+				cluster_sizes[cluster_index] = total_unique_pixels;
+#else
+				color_hasher.reset();
+
+				for (uint32_t cluster_indices_iter = 0; cluster_indices_iter < cluster_indices.size(); cluster_indices_iter++)
+				{
+					const uint32_t subblock_index = cluster_indices[cluster_indices_iter] & 1;
+					if (subblock_index)
+						continue;
+
+					const uint32_t block_index = cluster_indices[cluster_indices_iter] >> 1;
+					const color_rgba* pBlock_pixels = get_source_pixel_block(block_index).get_ptr();
+
+					uint32_t *pPrev_weight = nullptr;
+					color_rgba prev_color;
+					
+					{
+						color_rgba cur_color = pBlock_pixels[0];
+						auto res = color_hasher.insert(cur_color, 0);
+
+						uint32_t& weight = (res.first)->second;
+						if (weight != UINT32_MAX)
+							weight++;
+
+						prev_color = cur_color;
+						pPrev_weight = &(res.first)->second;
+					}
+									
+					for (uint32_t i = 1; i < 16; i++)
+					{
+						color_rgba cur_color = pBlock_pixels[i];
+
+						if (cur_color == prev_color)
+						{
+							if (*pPrev_weight != UINT32_MAX)
+								*pPrev_weight = *pPrev_weight + 1;
+						}
+						else
+						{
+							auto res = color_hasher.insert(cur_color, 0);
+
+							uint32_t& weight = (res.first)->second;
+							if (weight != UINT32_MAX)
+								weight++;
+
+							prev_color = cur_color;
+							pPrev_weight = &(res.first)->second;
+						}
+					}
+
+				} // cluster_indices_iter
+
+				const uint64_t first_pixel_index = input_pixels.size();
+				uint32_t total_unique_pixels = color_hasher.size();
+
+				pixel_clusters[cluster_index].m_first_pixel_index = first_pixel_index;
+				pixel_clusters[cluster_index].m_total_pixels = total_unique_pixels;
+
+				input_pixels.resize(first_pixel_index + total_unique_pixels);
+				pixel_weights.resize(first_pixel_index + total_unique_pixels);
+						
+				uint32_t j = 0;
+				
+				for (auto it = color_hasher.begin(); it != color_hasher.end(); ++it, ++j)
+				{
+					input_pixels[first_pixel_index + j] = it->first;
+					pixel_weights[first_pixel_index + j] = it->second;
+				}
+
+				cluster_sizes[cluster_index] = total_unique_pixels;
+#endif
+
+			} // cluster_index
+
+			debug_printf("Total hash time: %3.3f secs\n", hash_tm.get_elapsed_secs());
+
+			debug_printf("Total unique colors: %llu\n", input_pixels.size());
+
+			uint_vec sorted_cluster_indices_new_to_old(total_clusters);
+			indirect_sort(total_clusters, sorted_cluster_indices_new_to_old.data(), cluster_sizes.data());
+			//for (uint32_t i = 0; i < total_clusters; i++)
+			//	sorted_cluster_indices_new_to_old[i] = i;
+
+			uint_vec sorted_cluster_indices_old_to_new(total_clusters);
+			for (uint32_t i = 0; i < total_clusters; i++)
+				sorted_cluster_indices_old_to_new[sorted_cluster_indices_new_to_old[i]] = i;
+
+			basisu::vector<cl_pixel_cluster> sorted_pixel_clusters(total_clusters);
+			for (uint32_t i = 0; i < total_clusters; i++)
+				sorted_pixel_clusters[i] = pixel_clusters[sorted_cluster_indices_new_to_old[i]];
+
+			uint32_t total_perms = 64;
+			if (m_params.m_compression_level <= 1)
+				total_perms = 16;
+			else if (m_params.m_compression_level == BASISU_MAX_COMPRESSION_LEVEL)
+				total_perms = OPENCL_ENCODE_ETC1S_MAX_PERMS;
+
+			basisu::vector<etc_block> output_blocks(total_clusters);
+
+			if (opencl_encode_etc1s_pixel_clusters(
+				m_params.m_pOpenCL_context,
+				output_blocks.data(),
+				total_clusters,
+				sorted_pixel_clusters.data(),
+				input_pixels.size(),
+				input_pixels.data(),
+				pixel_weights.data(),
+				m_params.m_perceptual, total_perms))
+			{
+				for (uint32_t old_cluster_index = 0; old_cluster_index < m_endpoint_clusters.size(); old_cluster_index++)
+				{
+					const uint32_t new_cluster_index = sorted_cluster_indices_old_to_new[old_cluster_index];
+										
+					const etc_block& blk = output_blocks[new_cluster_index];
+
+					endpoint_cluster_etc_params& prev_etc_params = m_endpoint_cluster_etc_params[old_cluster_index];
+
+					prev_etc_params.m_valid = true;
+					etc_block::unpack_color5(prev_etc_params.m_color_unscaled[0], blk.get_base5_color(), false);
+					prev_etc_params.m_inten_table[0] = blk.get_inten_table(0);
+					prev_etc_params.m_color_error[0] = 0; // dummy value - we don't actually use this 
+				}
+
+				use_cpu = false;
+			}
+			else
+			{
+				error_printf("basisu_frontend::generate_endpoint_codebook: opencl_encode_etc1s_pixel_clusters() failed! Using CPU.\n");
+				m_params.m_pOpenCL_context = nullptr;
+				m_opencl_failed = true;
+			}
+
+		} // if (opencl_is_available() && m_params.m_use_opencl)
+
+		if (use_cpu)
+		{
+			const uint32_t N = 128;
+			for (uint32_t cluster_index_iter = 0; cluster_index_iter < m_endpoint_clusters.size(); cluster_index_iter += N)
+			{
+				const uint32_t first_index = cluster_index_iter;
+				const uint32_t last_index = minimum<uint32_t>((uint32_t)m_endpoint_clusters.size(), cluster_index_iter + N);
+
+#ifndef __EMSCRIPTEN__
+				m_params.m_pJob_pool->add_job([this, first_index, last_index, step] {
+#endif
+
+					for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++)
+					{
+						const basisu::vector<uint32_t>& cluster_indices = m_endpoint_clusters[cluster_index];
+
+						BASISU_FRONTEND_VERIFY(cluster_indices.size());
+
+						const uint32_t total_pixels = (uint32_t)cluster_indices.size() * 8;
+
+						basisu::vector<color_rgba> cluster_pixels(total_pixels);
+
+						for (uint32_t cluster_indices_iter = 0; cluster_indices_iter < cluster_indices.size(); cluster_indices_iter++)
+						{
+							const uint32_t block_index = cluster_indices[cluster_indices_iter] >> 1;
+							const uint32_t subblock_index = cluster_indices[cluster_indices_iter] & 1;
+
+							const bool flipped = true;
+
+							const color_rgba* pBlock_pixels = get_source_pixel_block(block_index).get_ptr();
+
+							for (uint32_t pixel_index = 0; pixel_index < 8; pixel_index++)
 							{
-								const color_rgba &c = cluster_pixels[i];
+								const color_rgba& c = pBlock_pixels[g_etc1_pixel_indices[flipped][subblock_index][pixel_index]];
+								cluster_pixels[cluster_indices_iter * 8 + pixel_index] = c;
+							}
+						}
 
-								uint64_t best_err = UINT64_MAX;
-								//uint32_t best_index = 0;
+						endpoint_cluster_etc_params new_subblock_params;
 
-								for (uint32_t s = 0; s < 4; s++)
+						{
+							etc1_optimizer optimizer;
+							etc1_solution_coordinates solutions[2];
+
+							etc1_optimizer::params cluster_optimizer_params;
+							cluster_optimizer_params.m_num_src_pixels = total_pixels;
+							cluster_optimizer_params.m_pSrc_pixels = &cluster_pixels[0];
+
+							cluster_optimizer_params.m_use_color4 = false;
+							cluster_optimizer_params.m_perceptual = m_params.m_perceptual;
+
+							if (m_params.m_compression_level <= 1)
+								cluster_optimizer_params.m_quality = cETCQualityMedium;
+							else if (m_params.m_compression_level == BASISU_MAX_COMPRESSION_LEVEL)
+								cluster_optimizer_params.m_quality = cETCQualityUber;
+
+							etc1_optimizer::results cluster_optimizer_results;
+
+							basisu::vector<uint8_t> cluster_selectors(total_pixels);
+							cluster_optimizer_results.m_n = total_pixels;
+							cluster_optimizer_results.m_pSelectors = &cluster_selectors[0];
+
+							optimizer.init(cluster_optimizer_params, cluster_optimizer_results);
+
+							if (!optimizer.compute())
+								BASISU_FRONTEND_VERIFY(false);
+
+							new_subblock_params.m_color_unscaled[0] = cluster_optimizer_results.m_block_color_unscaled;
+							new_subblock_params.m_inten_table[0] = cluster_optimizer_results.m_block_inten_table;
+							new_subblock_params.m_color_error[0] = cluster_optimizer_results.m_error;
+						}
+
+						endpoint_cluster_etc_params& prev_etc_params = m_endpoint_cluster_etc_params[cluster_index];
+
+						bool use_new_subblock_params = false;
+						if ((!step) || (!prev_etc_params.m_valid))
+							use_new_subblock_params = true;
+						else
+						{
+							assert(prev_etc_params.m_valid);
+
+							uint64_t total_prev_err = 0;
+
+							{
+								color_rgba block_colors[4];
+
+								etc_block::get_block_colors5(block_colors, prev_etc_params.m_color_unscaled[0], prev_etc_params.m_inten_table[0], false);
+
+								uint64_t total_err = 0;
+
+								for (uint32_t i = 0; i < total_pixels; i++)
 								{
-									uint64_t err = color_distance(m_params.m_perceptual, c, block_colors[s], false);
-									if (err < best_err)
+									const color_rgba& c = cluster_pixels[i];
+
+									uint64_t best_err = UINT64_MAX;
+									//uint32_t best_index = 0;
+
+									for (uint32_t s = 0; s < 4; s++)
 									{
-										best_err = err;
-										//best_index = s;
+										uint64_t err = color_distance(m_params.m_perceptual, c, block_colors[s], false);
+										if (err < best_err)
+										{
+											best_err = err;
+											//best_index = s;
+										}
 									}
+
+									total_err += best_err;
 								}
 
-								total_err += best_err;
+								total_prev_err += total_err;
 							}
 
-							total_prev_err += total_err;
+							// See if we should update this cluster's endpoints (if the error has actually fallen)
+							if (total_prev_err > new_subblock_params.m_color_error[0])
+							{
+								use_new_subblock_params = true;
+							}
 						}
 
-						// See if we should update this cluster's endpoints (if the error has actually fallen)
-						if (total_prev_err > new_subblock_params.m_color_error[0])
+						if (use_new_subblock_params)
 						{
-							use_new_subblock_params = true;
+							new_subblock_params.m_valid = true;
+
+							prev_etc_params = new_subblock_params;
 						}
-					}
 
-					if (use_new_subblock_params)
-					{
-						new_subblock_params.m_valid = true;
-
-						prev_etc_params = new_subblock_params;
-					}
-				
-				} // cluster_index
+					} // cluster_index
 
 #ifndef __EMSCRIPTEN__
-			} );
+					});
 #endif
 
-		} // cluster_index_iter
+			} // cluster_index_iter
 
 #ifndef __EMSCRIPTEN__
-		m_params.m_pJob_pool->wait_for_all();
+			m_params.m_pJob_pool->wait_for_all();
 #endif
+		}
+
+		debug_printf("Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
 	}
 
 	bool basisu_frontend::check_etc1s_constraints() const
@@ -1373,6 +1684,8 @@
 		return true;
 	}
 
+	// For each block, determine which ETC1S endpoint cluster can encode that block with lowest error.
+	// This reassigns blocks to different endpoint clusters.
 	uint32_t basisu_frontend::refine_endpoint_clusterization()
 	{
 		debug_printf("refine_endpoint_clusterization\n");
@@ -1380,6 +1693,8 @@
 		if (m_use_hierarchical_endpoint_codebooks)
 			compute_endpoint_clusters_within_each_parent_cluster();
 
+		// Note: It's possible that an endpoint cluster may live in more than one parent cluster after the first refinement step.
+
 		basisu::vector<vec2U> block_clusters(m_total_blocks);
 
 		for (int cluster_index = 0; cluster_index < static_cast<int>(m_endpoint_clusters.size()); cluster_index++)
@@ -1400,156 +1715,255 @@
 				
 		// Create a new endpoint clusterization
 
+		interval_timer tm;
+		tm.start();
+
 		uint_vec best_cluster_indices(m_total_blocks);
 
-		const uint32_t N = 1024;
-		for (uint32_t block_index_iter = 0; block_index_iter < m_total_blocks; block_index_iter += N)
+		bool use_cpu = true;
+		// TODO: Support non-hierarchical endpoint codebooks here
+		if (m_params.m_pOpenCL_context && m_use_hierarchical_endpoint_codebooks)
 		{
-			const uint32_t first_index = block_index_iter;
-			const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
+			// For the OpenCL kernel, we order the parent endpoint clusters by smallest to largest for efficiency.
+			// We also prepare an array of block info structs that point into this new parent endpoint cluster array.
+			const uint32_t total_parent_clusters = m_endpoint_clusters_within_each_parent_cluster.size();
+
+			basisu::vector<cl_block_info_struct> cl_block_info_structs(m_total_blocks);
+			
+			// the size of each parent cluster, in total clusters
+			uint_vec parent_cluster_sizes(total_parent_clusters);
+			for (uint32_t i = 0; i < total_parent_clusters; i++)
+				parent_cluster_sizes[i] = m_endpoint_clusters_within_each_parent_cluster[i].size();
+
+			uint_vec first_parent_cluster_ofs(total_parent_clusters);
+			uint32_t cur_ofs = 0;
+			for (uint32_t i = 0; i < total_parent_clusters; i++)
+			{
+				first_parent_cluster_ofs[i] = cur_ofs;
+
+				cur_ofs += parent_cluster_sizes[i];
+			}
+						
+			// Note: total_actual_endpoint_clusters is not necessarly equal to m_endpoint_clusters.size(), because clusters may live in multiple parent clusters after the first refinement step.
+			BASISU_FRONTEND_VERIFY(cur_ofs >= m_endpoint_clusters.size());
+			const uint32_t total_actual_endpoint_clusters = cur_ofs;
+			basisu::vector<cl_endpoint_cluster_struct> cl_endpoint_cluster_structs(total_actual_endpoint_clusters);
+
+			for (uint32_t i = 0; i < total_parent_clusters; i++)
+			{
+				const uint32_t dst_ofs = first_parent_cluster_ofs[i];
+
+				const uint32_t parent_cluster_size = parent_cluster_sizes[i];
+
+				assert(m_endpoint_clusters_within_each_parent_cluster[i].size() == parent_cluster_size);
+
+				for (uint32_t j = 0; j < parent_cluster_size; j++)
+				{
+					const uint32_t endpoint_cluster_index = m_endpoint_clusters_within_each_parent_cluster[i][j];
+
+					color_rgba cluster_etc_base_color(m_endpoint_cluster_etc_params[endpoint_cluster_index].m_color_unscaled[0]);
+					uint32_t cluster_etc_inten = m_endpoint_cluster_etc_params[endpoint_cluster_index].m_inten_table[0];
+
+					cl_endpoint_cluster_structs[dst_ofs + j].m_unscaled_color = cluster_etc_base_color;
+					cl_endpoint_cluster_structs[dst_ofs + j].m_etc_inten = (uint8_t)cluster_etc_inten;
+					cl_endpoint_cluster_structs[dst_ofs + j].m_cluster_index = (uint16_t)endpoint_cluster_index;
+				}
+			}
+			
+			for (uint32_t block_index = 0; block_index < m_total_blocks; block_index++)
+			{
+				const uint32_t block_parent_endpoint_cluster_index = m_block_parent_endpoint_cluster[block_index];
+				
+				cl_block_info_structs[block_index].m_num_clusters = (uint16_t)(parent_cluster_sizes[block_parent_endpoint_cluster_index]);
+				cl_block_info_structs[block_index].m_first_cluster_ofs = (uint16_t)(first_parent_cluster_ofs[block_parent_endpoint_cluster_index]);
+
+				const uint32_t block_cluster_index = block_clusters[block_index][0];
+				cl_block_info_structs[block_index].m_cur_cluster_index = (uint16_t)block_cluster_index;
+				cl_block_info_structs[block_index].m_cur_cluster_etc_inten = (uint8_t)m_endpoint_cluster_etc_params[block_cluster_index].m_inten_table[0];
+			}
+
+			uint_vec block_cluster_indices(m_total_blocks);
+			for (uint32_t i = 0; i < m_total_blocks; i++)
+				block_cluster_indices[i] = block_clusters[i][0];
+
+			uint_vec sorted_block_indices(m_total_blocks);
+			indirect_sort(m_total_blocks, sorted_block_indices.data(), block_cluster_indices.data());
+			
+			bool status = opencl_refine_endpoint_clusterization(
+				m_params.m_pOpenCL_context,
+				cl_block_info_structs.data(),
+				total_actual_endpoint_clusters,
+				cl_endpoint_cluster_structs.data(),
+				sorted_block_indices.data(),
+				best_cluster_indices.data(),
+				m_params.m_perceptual);
+
+			if (status)
+			{
+				use_cpu = false;
+			}
+			else
+			{
+				error_printf("basisu_frontend::refine_endpoint_clusterization: opencl_refine_endpoint_clusterization() failed! Using CPU.\n");
+				m_params.m_pOpenCL_context = nullptr;
+				m_opencl_failed = true;
+			}
+		}
+
+		if (use_cpu)
+		{
+			const uint32_t N = 1024;
+			for (uint32_t block_index_iter = 0; block_index_iter < m_total_blocks; block_index_iter += N)
+			{
+				const uint32_t first_index = block_index_iter;
+				const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
 
 #ifndef __EMSCRIPTEN__
-			m_params.m_pJob_pool->add_job( [this, first_index, last_index, &best_cluster_indices, &block_clusters] {
+				m_params.m_pJob_pool->add_job([this, first_index, last_index, &best_cluster_indices, &block_clusters] {
 #endif
 
-				for (uint32_t block_index = first_index; block_index < last_index; block_index++)
-				{
-					const uint32_t cluster_index = block_clusters[block_index][0];
-					BASISU_FRONTEND_VERIFY(cluster_index == block_clusters[block_index][1]);
-
-					const color_rgba *pSubblock_pixels = get_source_pixel_block(block_index).get_ptr();
-					const uint32_t num_subblock_pixels = 16;
-
-					uint64_t best_cluster_err = INT64_MAX;
-					uint32_t best_cluster_index = 0;
-
-					const uint32_t block_parent_endpoint_cluster_index = m_block_parent_endpoint_cluster.size() ? m_block_parent_endpoint_cluster[block_index] : 0;
-					const uint_vec *pCluster_indices = m_endpoint_clusters_within_each_parent_cluster.size() ? &m_endpoint_clusters_within_each_parent_cluster[block_parent_endpoint_cluster_index] : nullptr;
-
-					const uint32_t total_clusters = m_use_hierarchical_endpoint_codebooks ? (uint32_t)pCluster_indices->size() : (uint32_t)m_endpoint_clusters.size();
-			
-					for (uint32_t i = 0; i < total_clusters; i++)
+					for (uint32_t block_index = first_index; block_index < last_index; block_index++)
 					{
-						const uint32_t cluster_iter = m_use_hierarchical_endpoint_codebooks ? (*pCluster_indices)[i] : i;
+						const uint32_t cluster_index = block_clusters[block_index][0];
+						BASISU_FRONTEND_VERIFY(cluster_index == block_clusters[block_index][1]);
 
-						color_rgba cluster_etc_base_color(m_endpoint_cluster_etc_params[cluster_iter].m_color_unscaled[0]);
-						uint32_t cluster_etc_inten = m_endpoint_cluster_etc_params[cluster_iter].m_inten_table[0];
+						const color_rgba* pSubblock_pixels = get_source_pixel_block(block_index).get_ptr();
+						const uint32_t num_subblock_pixels = 16;
 
-						uint64_t total_err = 0;
+						uint64_t best_cluster_err = INT64_MAX;
+						uint32_t best_cluster_index = 0;
 
-						const uint32_t low_selector = 0;//subblock_etc_params_vec[j].m_low_selectors[0];
-						const uint32_t high_selector = 3;//subblock_etc_params_vec[j].m_high_selectors[0];
-						color_rgba subblock_colors[4];
-						// Can't assign it here - may result in too much error when selector quant occurs
-						if (cluster_etc_inten > m_endpoint_cluster_etc_params[cluster_index].m_inten_table[0])
+						const uint32_t block_parent_endpoint_cluster_index = m_block_parent_endpoint_cluster.size() ? m_block_parent_endpoint_cluster[block_index] : 0;
+						const uint_vec* pCluster_indices = m_endpoint_clusters_within_each_parent_cluster.size() ? &m_endpoint_clusters_within_each_parent_cluster[block_parent_endpoint_cluster_index] : nullptr;
+
+						const uint32_t total_clusters = m_use_hierarchical_endpoint_codebooks ? (uint32_t)pCluster_indices->size() : (uint32_t)m_endpoint_clusters.size();
+
+						for (uint32_t i = 0; i < total_clusters; i++)
 						{
-							total_err = INT64_MAX;
-							goto skip_cluster;
-						}
+							const uint32_t cluster_iter = m_use_hierarchical_endpoint_codebooks ? (*pCluster_indices)[i] : i;
 
-						etc_block::get_block_colors5(subblock_colors, cluster_etc_base_color, cluster_etc_inten);
-												
-#if 0
-						for (uint32_t p = 0; p < num_subblock_pixels; p++)
-						{
-							uint64_t best_err = UINT64_MAX;
+							color_rgba cluster_etc_base_color(m_endpoint_cluster_etc_params[cluster_iter].m_color_unscaled[0]);
+							uint32_t cluster_etc_inten = m_endpoint_cluster_etc_params[cluster_iter].m_inten_table[0];
 
-							for (uint32_t r = low_selector; r <= high_selector; r++)
+							uint64_t total_err = 0;
+
+							const uint32_t low_selector = 0;//subblock_etc_params_vec[j].m_low_selectors[0];
+							const uint32_t high_selector = 3;//subblock_etc_params_vec[j].m_high_selectors[0];
+							color_rgba subblock_colors[4];
+							// Can't assign it here - may result in too much error when selector quant occurs
+							if (cluster_etc_inten > m_endpoint_cluster_etc_params[cluster_index].m_inten_table[0])
 							{
-								uint64_t err = color_distance(m_params.m_perceptual, pSubblock_pixels[p], subblock_colors[r], false);
-								best_err = minimum(best_err, err);
-								if (!best_err)
+								total_err = INT64_MAX;
+								goto skip_cluster;
+							}
+
+							etc_block::get_block_colors5(subblock_colors, cluster_etc_base_color, cluster_etc_inten);
+
+#if 0
+							for (uint32_t p = 0; p < num_subblock_pixels; p++)
+							{
+								uint64_t best_err = UINT64_MAX;
+
+								for (uint32_t r = low_selector; r <= high_selector; r++)
+								{
+									uint64_t err = color_distance(m_params.m_perceptual, pSubblock_pixels[p], subblock_colors[r], false);
+									best_err = minimum(best_err, err);
+									if (!best_err)
+										break;
+								}
+
+								total_err += best_err;
+								if (total_err > best_cluster_err)
+									break;
+							} // p
+#else
+							if (m_params.m_perceptual)
+							{
+								if (!g_cpu_supports_sse41)
+								{
+									for (uint32_t p = 0; p < num_subblock_pixels; p++)
+									{
+										uint64_t best_err = UINT64_MAX;
+
+										for (uint32_t r = low_selector; r <= high_selector; r++)
+										{
+											uint64_t err = color_distance(true, pSubblock_pixels[p], subblock_colors[r], false);
+											best_err = minimum(best_err, err);
+											if (!best_err)
+												break;
+										}
+
+										total_err += best_err;
+										if (total_err > best_cluster_err)
+											break;
+									} // p
+								}
+								else
+								{
+#if BASISU_SUPPORT_SSE
+									find_lowest_error_perceptual_rgb_4_N_sse41((int64_t*)&total_err, subblock_colors, pSubblock_pixels, num_subblock_pixels, best_cluster_err);
+#endif
+								}
+							}
+							else
+							{
+								if (!g_cpu_supports_sse41)
+								{
+									for (uint32_t p = 0; p < num_subblock_pixels; p++)
+									{
+										uint64_t best_err = UINT64_MAX;
+
+										for (uint32_t r = low_selector; r <= high_selector; r++)
+										{
+											uint64_t err = color_distance(false, pSubblock_pixels[p], subblock_colors[r], false);
+											best_err = minimum(best_err, err);
+											if (!best_err)
+												break;
+										}
+
+										total_err += best_err;
+										if (total_err > best_cluster_err)
+											break;
+									} // p
+								}
+								else
+								{
+#if BASISU_SUPPORT_SSE
+									find_lowest_error_linear_rgb_4_N_sse41((int64_t*)&total_err, subblock_colors, pSubblock_pixels, num_subblock_pixels, best_cluster_err);
+#endif
+								}
+							}
+#endif
+
+						skip_cluster:
+							if ((total_err < best_cluster_err) ||
+								((cluster_iter == cluster_index) && (total_err == best_cluster_err)))
+							{
+								best_cluster_err = total_err;
+								best_cluster_index = cluster_iter;
+
+								if (!best_cluster_err)
 									break;
 							}
+						} // j
+												
+						best_cluster_indices[block_index] = best_cluster_index;
 
-							total_err += best_err;
-							if (total_err > best_cluster_err)
-								break;
-						} // p
-#else
-						if (m_params.m_perceptual)
-						{
-							if (!g_cpu_supports_sse41)
-							{
-								for (uint32_t p = 0; p < num_subblock_pixels; p++)
-								{
-									uint64_t best_err = UINT64_MAX;
-
-									for (uint32_t r = low_selector; r <= high_selector; r++)
-									{
-										uint64_t err = color_distance(true, pSubblock_pixels[p], subblock_colors[r], false);
-										best_err = minimum(best_err, err);
-										if (!best_err)
-											break;
-									}
-
-									total_err += best_err;
-									if (total_err > best_cluster_err)
-										break;
-								} // p
-							}
-							else
-							{
-#if BASISU_SUPPORT_SSE
-								find_lowest_error_perceptual_rgb_4_N_sse41((int64_t*)&total_err, subblock_colors, pSubblock_pixels, num_subblock_pixels, best_cluster_err);
-#endif
-							}
-						}
-						else
-						{
-							if (!g_cpu_supports_sse41)
-							{
-								for (uint32_t p = 0; p < num_subblock_pixels; p++)
-								{
-									uint64_t best_err = UINT64_MAX;
-
-									for (uint32_t r = low_selector; r <= high_selector; r++)
-									{
-										uint64_t err = color_distance(false, pSubblock_pixels[p], subblock_colors[r], false);
-										best_err = minimum(best_err, err);
-										if (!best_err)
-											break;
-									}
-
-									total_err += best_err;
-									if (total_err > best_cluster_err)
-										break;
-								} // p
-							}
-							else
-							{
-#if BASISU_SUPPORT_SSE
-								find_lowest_error_linear_rgb_4_N_sse41((int64_t*)&total_err, subblock_colors, pSubblock_pixels, num_subblock_pixels, best_cluster_err);
-#endif
-							}
-						}
-#endif
-
-					skip_cluster:
-						if ((total_err < best_cluster_err) ||
-							((cluster_iter == cluster_index) && (total_err == best_cluster_err)))
-						{
-							best_cluster_err = total_err;
-							best_cluster_index = cluster_iter;
-					
-							if (!best_cluster_err)
-								break;
-						}
-					} // j
-						
-					best_cluster_indices[block_index] = best_cluster_index;
-
-				} // block_index
+					} // block_index
 
 #ifndef __EMSCRIPTEN__
-			} );
+					});
 #endif
-						
-		} // block_index_iter
+
+			} // block_index_iter
 
 #ifndef __EMSCRIPTEN__
-		m_params.m_pJob_pool->wait_for_all();
+			m_params.m_pJob_pool->wait_for_all();
 #endif
+		
+		} // use_cpu
+						
+		debug_printf("refine_endpoint_clusterization time: %3.3f secs\n", tm.get_elapsed_secs());
 
 		basisu::vector<typename basisu::vector<uint32_t> > optimized_endpoint_clusters(m_endpoint_clusters.size());
 		uint32_t total_subblocks_reassigned = 0;
@@ -1647,51 +2061,91 @@
 	void basisu_frontend::create_initial_packed_texture()
 	{
 		debug_printf("create_initial_packed_texture\n");
+		
+		interval_timer tm;
+		tm.start();
 
-		const uint32_t N = 4096;
-		for (uint32_t block_index_iter = 0; block_index_iter < m_total_blocks; block_index_iter += N)
+		bool use_cpu = true;
+
+		if ((m_params.m_pOpenCL_context) && (opencl_is_available()))
 		{
-			const uint32_t first_index = block_index_iter;
-			const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
+			basisu::vector<color_rgba> block_etc5_color_intens(m_total_blocks);
 
-#ifndef __EMSCRIPTEN__
-			m_params.m_pJob_pool->add_job( [this, first_index, last_index] {
-#endif
+			for (uint32_t block_index = 0; block_index < m_total_blocks; block_index++)
+			{
+				uint32_t cluster0 = m_block_endpoint_clusters_indices[block_index][0];
 				
-				for (uint32_t block_index = first_index; block_index < last_index; block_index++)
-				{
-					uint32_t cluster0 = m_block_endpoint_clusters_indices[block_index][0];
-					uint32_t cluster1 = m_block_endpoint_clusters_indices[block_index][1];
-					BASISU_FRONTEND_VERIFY(cluster0 == cluster1);
+				const color_rgba& color_unscaled = m_endpoint_cluster_etc_params[cluster0].m_color_unscaled[0];
+				uint32_t inten = m_endpoint_cluster_etc_params[cluster0].m_inten_table[0];
 
-					const color_rgba *pSource_pixels = get_source_pixel_block(block_index).get_ptr();
+				block_etc5_color_intens[block_index].set(color_unscaled.r, color_unscaled.g, color_unscaled.b, inten);
+			}
 
-					etc_block &blk = m_encoded_blocks[block_index];
+			bool status = opencl_determine_selectors(m_params.m_pOpenCL_context, block_etc5_color_intens.data(),
+				m_encoded_blocks.data(),
+				m_params.m_perceptual);
+			if (!status)
+			{
+				error_printf("basisu_frontend::create_initial_packed_texture: opencl_determine_selectors() failed! Using CPU.\n");
+				m_params.m_pOpenCL_context = nullptr;
+				m_opencl_failed = true;
+			}
+			else
+			{
+				use_cpu = false;
+			}
+		}
 
-					color_rgba unscaled[2] = { m_endpoint_cluster_etc_params[cluster0].m_color_unscaled[0], m_endpoint_cluster_etc_params[cluster1].m_color_unscaled[0] };
-					uint32_t inten[2] = { m_endpoint_cluster_etc_params[cluster0].m_inten_table[0], m_endpoint_cluster_etc_params[cluster1].m_inten_table[0] };
-									
-					blk.set_block_color5(unscaled[0], unscaled[1]);
-					blk.set_flip_bit(true);
-
-					blk.set_inten_table(0, inten[0]);
-					blk.set_inten_table(1, inten[1]);
-
-					blk.determine_selectors(pSource_pixels, m_params.m_perceptual);
-						
-				} // block_index
+		if (use_cpu)
+		{
+			const uint32_t N = 4096;
+			for (uint32_t block_index_iter = 0; block_index_iter < m_total_blocks; block_index_iter += N)
+			{
+				const uint32_t first_index = block_index_iter;
+				const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
 
 #ifndef __EMSCRIPTEN__
-			} );
+				m_params.m_pJob_pool->add_job([this, first_index, last_index] {
 #endif
 
-		} // block_index_iter
+					for (uint32_t block_index = first_index; block_index < last_index; block_index++)
+					{
+						uint32_t cluster0 = m_block_endpoint_clusters_indices[block_index][0];
+						uint32_t cluster1 = m_block_endpoint_clusters_indices[block_index][1];
+						BASISU_FRONTEND_VERIFY(cluster0 == cluster1);
+
+						const color_rgba* pSource_pixels = get_source_pixel_block(block_index).get_ptr();
+
+						etc_block& blk = m_encoded_blocks[block_index];
+
+						color_rgba unscaled[2] = { m_endpoint_cluster_etc_params[cluster0].m_color_unscaled[0], m_endpoint_cluster_etc_params[cluster1].m_color_unscaled[0] };
+						uint32_t inten[2] = { m_endpoint_cluster_etc_params[cluster0].m_inten_table[0], m_endpoint_cluster_etc_params[cluster1].m_inten_table[0] };
+
+						blk.set_block_color5(unscaled[0], unscaled[1]);
+						blk.set_flip_bit(true);
+
+						blk.set_inten_table(0, inten[0]);
+						blk.set_inten_table(1, inten[1]);
+
+						blk.determine_selectors(pSource_pixels, m_params.m_perceptual);
+
+					} // block_index
 
 #ifndef __EMSCRIPTEN__
-		m_params.m_pJob_pool->wait_for_all();
+					});
 #endif
 
+			} // block_index_iter
+
+#ifndef __EMSCRIPTEN__
+			m_params.m_pJob_pool->wait_for_all();
+#endif
+
+		} // use_cpu
+				
 		m_orig_encoded_blocks = m_encoded_blocks;
+
+		debug_printf("Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
 	}
 
 	void basisu_frontend::compute_selector_clusters_within_each_parent_cluster()
@@ -1739,8 +2193,7 @@
 	void basisu_frontend::generate_selector_clusters()
 	{
 		debug_printf("generate_selector_clusters\n");
-
-		typedef vec<16, float> vec16F;
+				
 		typedef tree_vector_quant<vec16F> vec16F_clusterizer;
 				
 		vec16F_clusterizer::array_of_weighted_training_vecs training_vecs(m_total_blocks);
@@ -1800,12 +2253,14 @@
 
 		uint32_t max_threads = 0;
 		max_threads = m_params.m_multithreaded ? minimum<int>(std::thread::hardware_concurrency(), cMaxCodebookCreationThreads) : 0;
+		if (m_params.m_pJob_pool)
+			max_threads = minimum<int>((int)m_params.m_pJob_pool->get_total_threads(), max_threads);
 
 		bool status = generate_hierarchical_codebook_threaded(selector_clusterizer,
 			m_params.m_max_selector_clusters, m_use_hierarchical_selector_codebooks ? parent_codebook_size : 0,
 			m_selector_cluster_block_indices,
 			m_selector_parent_cluster_block_indices,
-			max_threads, m_params.m_pJob_pool);
+			max_threads, m_params.m_pJob_pool, false);
 		BASISU_FRONTEND_VERIFY(status);
 
 		if (m_use_hierarchical_selector_codebooks)
@@ -1864,235 +2319,103 @@
 	{
 		debug_printf("create_optimized_selector_codebook\n");
 
+		interval_timer tm;
+		tm.start();
+
 		const uint32_t total_selector_clusters = (uint32_t)m_selector_cluster_block_indices.size();
 
 		debug_printf("Total selector clusters (from m_selector_cluster_block_indices.size()): %u\n", (uint32_t)m_selector_cluster_block_indices.size());
 
 		m_optimized_cluster_selectors.resize(total_selector_clusters);
-
-		if ((m_params.m_pGlobal_sel_codebook) && (!m_params.m_use_hybrid_selector_codebooks))
+		
+		// For each selector codebook entry, and for each of the 4x4 selectors, determine which selector minimizes the error across all the blocks that use that quantized selector.
+		const uint32_t N = 256;
+		for (uint32_t cluster_index_iter = 0; cluster_index_iter < total_selector_clusters; cluster_index_iter += N)
 		{
-			uint32_t total_clusters_processed = 0;
-
-			m_optimized_cluster_selector_global_cb_ids.resize(total_selector_clusters);
-
-			const uint32_t N = 256;
-			for (uint32_t cluster_index_iter = 0; cluster_index_iter < total_selector_clusters; cluster_index_iter += N)
-			{
-				const uint32_t first_index = cluster_index_iter;                                    
-				const uint32_t last_index = minimum<uint32_t>((uint32_t)total_selector_clusters, cluster_index_iter + N);   
-
-#ifndef __EMSCRIPTEN__
-				m_params.m_pJob_pool->add_job( [this, first_index, last_index, &total_clusters_processed, &total_selector_clusters] {
-#endif
-					
-					for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++)
-					{
-						const basisu::vector<uint32_t> &cluster_block_indices = m_selector_cluster_block_indices[cluster_index];
-
-						if (!cluster_block_indices.size())
-							continue;
-
-						etc_block_vec etc_blocks;
-						pixel_block_vec pixel_blocks;
-
-						for (uint32_t cluster_block_index = 0; cluster_block_index < cluster_block_indices.size(); cluster_block_index++)
-						{
-							const uint32_t block_index = cluster_block_indices[cluster_block_index];
-
-							etc_blocks.push_back(m_encoded_blocks[block_index]);
-
-							pixel_blocks.push_back(get_source_pixel_block(block_index));
-						}
-
-						uint32_t palette_index;
-						basist::etc1_global_palette_entry_modifier palette_modifier;
-
-		#if 0
-						m_params.m_pGlobal_sel_codebook->find_best_entry(etc_blocks.size(), pixel_blocks.get_ptr(), etc_blocks.get_ptr(),
-							palette_index, palette_modifier,
-							m_params.m_perceptual, 1 << m_params.m_num_global_sel_codebook_pal_bits, 1 << m_params.m_num_global_sel_codebook_mod_bits);
-		#else
-						etc1_global_selector_codebook_find_best_entry(*m_params.m_pGlobal_sel_codebook,
-							(uint32_t)etc_blocks.size(), &pixel_blocks[0], &etc_blocks[0],
-							palette_index, palette_modifier,
-							m_params.m_perceptual, 1 << m_params.m_num_global_sel_codebook_pal_bits, 1 << m_params.m_num_global_sel_codebook_mod_bits);
-		#endif
-
-						m_optimized_cluster_selector_global_cb_ids[cluster_index].set(palette_index, palette_modifier);
-
-						basist::etc1_selector_palette_entry pal_entry(m_params.m_pGlobal_sel_codebook->get_entry(palette_index, palette_modifier));
-
-						for (uint32_t y = 0; y < 4; y++)
-							for (uint32_t x = 0; x < 4; x++)
-								m_optimized_cluster_selectors[cluster_index].set_selector(x, y, pal_entry(x, y));
-
-						{
-							std::lock_guard<std::mutex> lock(m_lock);
-
-							total_clusters_processed++;
-							if ((total_clusters_processed % 63) == 0)
-								debug_printf("Global selector palette optimization: %3.1f%% complete\n", total_clusters_processed * 100.0f / total_selector_clusters);
-						}
-
-					} // cluster_index
-
-#ifndef __EMSCRIPTEN__
-				} );
-#endif
-
-			} // cluster_index_iter
-
-#ifndef __EMSCRIPTEN__
-			m_params.m_pJob_pool->wait_for_all();
-#endif
-		}
-		else
-		{
-			const bool uses_hybrid_sel_codebook = ((m_params.m_pGlobal_sel_codebook) && (m_params.m_use_hybrid_selector_codebooks));
-			if (uses_hybrid_sel_codebook)
-			{
-				m_selector_cluster_uses_global_cb.resize(total_selector_clusters);
-				m_optimized_cluster_selector_global_cb_ids.resize(total_selector_clusters);
-			}
-
-			uint32_t total_clusters_processed = 0;
-
-			// For each selector codebook entry, and for each of the 4x4 selectors, determine which selector minimizes the error across all the blocks that use that quantized selector.
-
-			const uint32_t N = 256;
-			for (uint32_t cluster_index_iter = 0; cluster_index_iter < total_selector_clusters; cluster_index_iter += N)
-			{
-				const uint32_t first_index = cluster_index_iter;                                    
-				const uint32_t last_index = minimum<uint32_t>((uint32_t)total_selector_clusters, cluster_index_iter + N);   
+			const uint32_t first_index = cluster_index_iter;
+			const uint32_t last_index = minimum<uint32_t>((uint32_t)total_selector_clusters, cluster_index_iter + N);
 
 #ifndef __EMSCRIPTEN__			
-				m_params.m_pJob_pool->add_job( [this, first_index, last_index, &uses_hybrid_sel_codebook, &total_clusters_processed, &total_selector_clusters] {
+			m_params.m_pJob_pool->add_job([this, first_index, last_index] {
 #endif
-					
-					for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++)
+
+				for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++)
+				{
+					const basisu::vector<uint32_t>& cluster_block_indices = m_selector_cluster_block_indices[cluster_index];
+
+					if (!cluster_block_indices.size())
+						continue;
+
+					uint64_t overall_best_err = 0;
+
+					uint64_t total_err[4][4][4];
+					clear_obj(total_err);
+
+					for (uint32_t cluster_block_index = 0; cluster_block_index < cluster_block_indices.size(); cluster_block_index++)
 					{
-						const basisu::vector<uint32_t> &cluster_block_indices = m_selector_cluster_block_indices[cluster_index];
+						const uint32_t block_index = cluster_block_indices[cluster_block_index];
 
-						if (!cluster_block_indices.size())
-							continue;
+						const etc_block& blk = m_encoded_blocks[block_index];
 
-						uint64_t overall_best_err = 0;
+						color_rgba blk_colors[4];
+						blk.get_block_colors(blk_colors, 0);
 
 						for (uint32_t y = 0; y < 4; y++)
 						{
 							for (uint32_t x = 0; x < 4; x++)
 							{
-								uint64_t best_err = UINT64_MAX;
-								uint32_t best_s = 0;
+								const color_rgba& orig_color = get_source_pixel_block(block_index)(x, y);
 
-								for (uint32_t s = 0; s < 4; s++)
+								if (m_params.m_perceptual)
 								{
-									uint32_t total_err = 0;
-
-									for (uint32_t cluster_block_index = 0; cluster_block_index < cluster_block_indices.size(); cluster_block_index++)
-									{
-										const uint32_t block_index = cluster_block_indices[cluster_block_index];
-
-										const etc_block &blk = m_encoded_blocks[block_index];
-
-										const color_rgba &orig_color = get_source_pixel_block(block_index)(x, y);
-
-										color_rgba block_color;
-										blk.get_block_color(block_color, blk.get_subblock_index(x, y), s);
-										total_err += color_distance(m_params.m_perceptual, block_color, orig_color, false);
-
-										if (total_err > best_err)
-											break;
-
-									} // block_index
-
-									if (total_err < best_err)
-									{
-										best_err = total_err;
-										best_s = s;
-										if (!best_err)
-											break;
-									}
-
-								} // s
-
-								m_optimized_cluster_selectors[cluster_index].set_selector(x, y, best_s);
-
-								overall_best_err += best_err;
-
+									for (uint32_t s = 0; s < 4; s++)
+										total_err[y][x][s] += color_distance(true, blk_colors[s], orig_color, false);
+								}
+								else
+								{
+									for (uint32_t s = 0; s < 4; s++)
+										total_err[y][x][s] += color_distance(false, blk_colors[s], orig_color, false);
+								}
 							} // x
 						} // y
 
-						if (uses_hybrid_sel_codebook)
+					} // cluster_block_index
+
+					for (uint32_t y = 0; y < 4; y++)
+					{
+						for (uint32_t x = 0; x < 4; x++)
 						{
-							etc_block_vec etc_blocks;
-							pixel_block_vec pixel_blocks;
+							uint64_t best_err = total_err[y][x][0];
+							uint8_t best_sel = 0;
 
-							for (uint32_t cluster_block_index = 0; cluster_block_index < cluster_block_indices.size(); cluster_block_index++)
+							for (uint32_t s = 1; s < 4; s++)
 							{
-								const uint32_t block_index = cluster_block_indices[cluster_block_index];
-
-								etc_blocks.push_back(m_encoded_blocks[block_index]);
-
-								pixel_blocks.push_back(get_source_pixel_block(block_index));
+								if (total_err[y][x][s] < best_err)
+								{
+									best_err = total_err[y][x][s];
+									best_sel = (uint8_t)s;
+								}
 							}
 
-							uint32_t palette_index;
-							basist::etc1_global_palette_entry_modifier palette_modifier;
+							m_optimized_cluster_selectors[cluster_index].set_selector(x, y, best_sel);
 
-		#if 0
-							uint64_t best_global_cb_err = m_params.m_pGlobal_sel_codebook->find_best_entry(etc_blocks.size(), pixel_blocks.get_ptr(), etc_blocks.get_ptr(),
-								palette_index, palette_modifier,
-								m_params.m_perceptual, 1 << m_params.m_num_global_sel_codebook_pal_bits, 1 << m_params.m_num_global_sel_codebook_mod_bits);
-		#else
-							uint64_t best_global_cb_err = etc1_global_selector_codebook_find_best_entry(*m_params.m_pGlobal_sel_codebook, (uint32_t)etc_blocks.size(), &pixel_blocks[0], &etc_blocks[0],
-								palette_index, palette_modifier,
-								m_params.m_perceptual, 1 << m_params.m_num_global_sel_codebook_pal_bits, 1 << m_params.m_num_global_sel_codebook_mod_bits);
-		#endif
+							overall_best_err += best_err;
+						} // x
+					} // y
 
-							if (best_global_cb_err <= overall_best_err * m_params.m_hybrid_codebook_quality_thresh)
-							{
-								m_selector_cluster_uses_global_cb[cluster_index] = true;
-
-								m_optimized_cluster_selector_global_cb_ids[cluster_index].set(palette_index, palette_modifier);
-
-								basist::etc1_selector_palette_entry pal_entry(m_params.m_pGlobal_sel_codebook->get_entry(palette_index, palette_modifier));
-
-								for (uint32_t y = 0; y < 4; y++)
-									for (uint32_t x = 0; x < 4; x++)
-										m_optimized_cluster_selectors[cluster_index].set_selector(x, y, pal_entry(x, y));
-							}
-							else
-							{
-								m_optimized_cluster_selector_global_cb_ids[cluster_index].set(0, basist::etc1_global_palette_entry_modifier(0));
-
-								m_selector_cluster_uses_global_cb[cluster_index] = false;
-							}
-						}
-
-						if (uses_hybrid_sel_codebook)
-						{
-							std::lock_guard<std::mutex> lock(m_lock);
-		
-							total_clusters_processed++;
-							if ((total_clusters_processed % 63) == 0)
-								debug_printf("Global selector palette optimization: %3.1f%% complete\n", total_clusters_processed * 100.0f / total_selector_clusters);
-						}
-
-					} // cluster_index
+				} // cluster_index
 
 #ifndef __EMSCRIPTEN__
-				} );
+				});
 #endif
 
-			} // cluster_index_iter
+		} // cluster_index_iter
 
 #ifndef __EMSCRIPTEN__
-			m_params.m_pJob_pool->wait_for_all();
+		m_params.m_pJob_pool->wait_for_all();
 #endif
 
-		} // if (m_params.m_pGlobal_sel_codebook)
+		debug_printf("Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
 				
 		if (m_params.m_debug_images)
 		{
@@ -2133,17 +2456,25 @@
 		}
 	}
 
+	// For each block: Determine which quantized selectors best encode that block, given its quantized endpoints.
+	// Note that this method may leave some empty clusters (i.e. arrays with no block indices), including at the end.
 	void basisu_frontend::find_optimal_selector_clusters_for_each_block()
 	{
 		debug_printf("find_optimal_selector_clusters_for_each_block\n");
 
-		// Sanity checks
-		BASISU_FRONTEND_VERIFY(m_selector_cluster_block_indices.size() == m_optimized_cluster_selectors.size());
-		for (uint32_t i = 0; i < m_selector_clusters_within_each_parent_cluster.size(); i++)
+		interval_timer tm;
+		tm.start();
+		
+		if (m_params.m_validate)
 		{
-			for (uint32_t j = 0; j < m_selector_clusters_within_each_parent_cluster[i].size(); j++)
+			// Sanity checks
+			BASISU_FRONTEND_VERIFY(m_selector_cluster_block_indices.size() == m_optimized_cluster_selectors.size());
+			for (uint32_t i = 0; i < m_selector_clusters_within_each_parent_cluster.size(); i++)
 			{
-				BASISU_FRONTEND_VERIFY(m_selector_clusters_within_each_parent_cluster[i][j] < m_optimized_cluster_selectors.size());
+				for (uint32_t j = 0; j < m_selector_clusters_within_each_parent_cluster[i].size(); j++)
+				{
+					BASISU_FRONTEND_VERIFY(m_selector_clusters_within_each_parent_cluster[i][j] < m_optimized_cluster_selectors.size());
+				}
 			}
 		}
 
@@ -2151,20 +2482,120 @@
 							
 		if (m_params.m_compression_level == 0)
 		{
-			// Don't do anything, just leave the blocks in their original selector clusters.
-			for (uint32_t i = 0; i < m_selector_cluster_block_indices.size(); i++)
+			// Just leave the blocks in their original selector clusters.
+			for (uint32_t selector_cluster_index = 0; selector_cluster_index < m_selector_cluster_block_indices.size(); selector_cluster_index++)
 			{
-				for (uint32_t j = 0; j < m_selector_cluster_block_indices[i].size(); j++)
-					m_block_selector_cluster_index[m_selector_cluster_block_indices[i][j]] = i;
+				for (uint32_t j = 0; j < m_selector_cluster_block_indices[selector_cluster_index].size(); j++)
+				{
+					const uint32_t block_index = m_selector_cluster_block_indices[selector_cluster_index][j];
+
+					m_block_selector_cluster_index[block_index] = selector_cluster_index;
+
+					etc_block& blk = m_encoded_blocks[block_index];
+					blk.set_raw_selector_bits(m_optimized_cluster_selectors[selector_cluster_index].get_raw_selector_bits());
+				}
+			}
+
+			debug_printf("Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
+
+			return;
+		}
+		
+		bool use_cpu = true;
+
+		if ((m_params.m_pOpenCL_context) && m_use_hierarchical_selector_codebooks)
+		{
+			const uint32_t num_parent_clusters = m_selector_clusters_within_each_parent_cluster.size();
+
+			basisu::vector<fosc_selector_struct> selector_structs;
+			selector_structs.reserve(m_optimized_cluster_selectors.size());
+						
+			uint_vec parent_selector_cluster_offsets(num_parent_clusters);
+
+			uint_vec selector_cluster_indices;
+			selector_cluster_indices.reserve(m_optimized_cluster_selectors.size());
+						
+			uint32_t cur_ofs = 0;
+			for (uint32_t parent_index = 0; parent_index < num_parent_clusters; parent_index++)
+			{
+				parent_selector_cluster_offsets[parent_index] = cur_ofs;
+				
+				for (uint32_t j = 0; j < m_selector_clusters_within_each_parent_cluster[parent_index].size(); j++)
+				{
+					const uint32_t selector_cluster_index = m_selector_clusters_within_each_parent_cluster[parent_index][j];
+
+					uint32_t sel_bits = 0;
+					for (uint32_t p = 0; p < 16; p++)
+						sel_bits |= (m_optimized_cluster_selectors[selector_cluster_index].get_selector(p & 3, p >> 2) << (p * 2));
+
+					selector_structs.enlarge(1)->m_packed_selectors = sel_bits;
+										
+					selector_cluster_indices.push_back(selector_cluster_index);
+				}
+
+				cur_ofs += m_selector_clusters_within_each_parent_cluster[parent_index].size();
+			}
+
+			const uint32_t total_input_selectors = cur_ofs;
+						
+			basisu::vector<fosc_block_struct> block_structs(m_total_blocks);
+			for (uint32_t i = 0; i < m_total_blocks; i++)
+			{
+				const uint32_t parent_selector_cluster = m_block_parent_selector_cluster[i];
+
+				const etc_block& blk = m_encoded_blocks[i];
+				blk.unpack_color5(block_structs[i].m_etc_color5_inten, blk.get_base5_color(), false);
+
+				block_structs[i].m_etc_color5_inten.a = (uint8_t)blk.get_inten_table(0);
+				block_structs[i].m_first_selector = parent_selector_cluster_offsets[parent_selector_cluster];
+				block_structs[i].m_num_selectors = m_selector_clusters_within_each_parent_cluster[parent_selector_cluster].size();
+			}
+
+			uint_vec output_selector_cluster_indices(m_total_blocks);
+
+			bool status = opencl_find_optimal_selector_clusters_for_each_block(
+				m_params.m_pOpenCL_context,
+				block_structs.data(),
+				total_input_selectors,
+				selector_structs.data(),
+				selector_cluster_indices.data(),
+				output_selector_cluster_indices.data(),
+				m_params.m_perceptual);
+			
+			if (!status)
+			{
+				error_printf("basisu_frontend::find_optimal_selector_clusters_for_each_block: opencl_find_optimal_selector_clusters_for_each_block() failed! Using CPU.\n");
+				m_params.m_pOpenCL_context = nullptr;
+				m_opencl_failed = true;
+			}
+			else
+			{
+				for (uint32_t i = 0; i < m_selector_cluster_block_indices.size(); i++)
+				{
+					m_selector_cluster_block_indices[i].resize(0);
+					m_selector_cluster_block_indices[i].reserve(128);
+				}
+								
+				for (uint32_t block_index = 0; block_index < m_total_blocks; block_index++)
+				{
+					etc_block& blk = m_encoded_blocks[block_index];
+
+					uint32_t best_cluster_index = output_selector_cluster_indices[block_index];
+
+					blk.set_raw_selector_bits(m_optimized_cluster_selectors[best_cluster_index].get_raw_selector_bits());
+
+					m_block_selector_cluster_index[block_index] = best_cluster_index;
+
+					vector_ensure_element_is_valid(m_selector_cluster_block_indices, best_cluster_index);
+					m_selector_cluster_block_indices[best_cluster_index].push_back(block_index);
+				}
+
+				use_cpu = false;
 			}
 		}
-		else
-		{
-			// Note that this method may leave some empty clusters (i.e. arrays with no block indices), including at the end.
-			basisu::vector< basisu::vector<uint32_t> > new_cluster_indices(m_optimized_cluster_selectors.size());
-						
-			// For each block: Determine which quantized selectors best encode that block, given its quantized endpoints.
 
+		if (use_cpu)
+		{
 			basisu::vector<uint8_t> unpacked_optimized_cluster_selectors(16 * m_optimized_cluster_selectors.size());
 			for (uint32_t cluster_index = 0; cluster_index < m_optimized_cluster_selectors.size(); cluster_index++)
 			{
@@ -2176,36 +2607,74 @@
 					}
 				}
 			}
-						
-			const uint32_t N = 1024;
+												
+			const uint32_t N = 2048;
 			for (uint32_t block_index_iter = 0; block_index_iter < m_total_blocks; block_index_iter += N)
 			{
 				const uint32_t first_index = block_index_iter;
 				const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
 
-#ifndef __EMSCRIPTEN__
-				m_params.m_pJob_pool->add_job( [this, first_index, last_index, &new_cluster_indices, &unpacked_optimized_cluster_selectors] {
-#endif
+	#ifndef __EMSCRIPTEN__
+				m_params.m_pJob_pool->add_job( [this, first_index, last_index, &unpacked_optimized_cluster_selectors] {
+	#endif
+
+				int prev_best_cluster_index = 0;
 
 				for (uint32_t block_index = first_index; block_index < last_index; block_index++)
 				{
-					const color_rgba* pBlock_pixels = get_source_pixel_block(block_index).get_ptr();
-
+					const pixel_block& block = get_source_pixel_block(block_index);
+					
 					etc_block& blk = m_encoded_blocks[block_index];
-			
+
+					if ((block_index > first_index) && (block == get_source_pixel_block(block_index - 1)))
+					{
+						blk.set_raw_selector_bits(m_optimized_cluster_selectors[prev_best_cluster_index].get_raw_selector_bits());
+
+						m_block_selector_cluster_index[block_index] = prev_best_cluster_index;
+						
+						continue;
+					}
+					
+					const color_rgba* pBlock_pixels = block.get_ptr();
+													
 					color_rgba trial_block_colors[4];
-					blk.get_block_colors(trial_block_colors, 0);
+					blk.get_block_colors_etc1s(trial_block_colors);
 
 					// precompute errors for the i-th block pixel and selector sel: [sel][i]
 					uint32_t trial_errors[4][16];
-
-					for (int sel = 0; sel < 4; ++sel)
+										
+					if (m_params.m_perceptual)
 					{
-						for (int i = 0; i < 16; ++i)
-						{
-							trial_errors[sel][i] = color_distance(m_params.m_perceptual, pBlock_pixels[i], trial_block_colors[sel], false);
-						}
+						for (uint32_t sel = 0; sel < 4; ++sel)
+							for (uint32_t i = 0; i < 16; ++i)
+								trial_errors[sel][i] = color_distance(true, pBlock_pixels[i], trial_block_colors[sel], false);
 					}
+					else
+					{
+						for (uint32_t sel = 0; sel < 4; ++sel)
+							for (uint32_t i = 0; i < 16; ++i)
+								trial_errors[sel][i] = color_distance(false, pBlock_pixels[i], trial_block_colors[sel], false);
+					}
+
+					// Compute the minimum possible errors (given any selectors) for pixels 0-15
+					uint64_t min_possible_error_0_15 = 0;
+					for (uint32_t i = 0; i < 16; i++)
+						min_possible_error_0_15 += basisu::minimum(trial_errors[0][i], trial_errors[1][i], trial_errors[2][i], trial_errors[3][i]);
+
+					// Compute the minimum possible errors (given any selectors) for pixels 4-15
+					uint64_t min_possible_error_4_15 = 0;
+					for (uint32_t i = 4; i < 16; i++)
+						min_possible_error_4_15 += basisu::minimum(trial_errors[0][i], trial_errors[1][i], trial_errors[2][i], trial_errors[3][i]);
+
+					// Compute the minimum possible errors (given any selectors) for pixels 8-15
+					uint64_t min_possible_error_8_15 = 0;
+					for (uint32_t i = 8; i < 16; i++)
+						min_possible_error_8_15 += basisu::minimum(trial_errors[0][i], trial_errors[1][i], trial_errors[2][i], trial_errors[3][i]);
+
+					// Compute the minimum possible errors (given any selectors) for pixels 12-15
+					uint64_t min_possible_error_12_15 = 0;
+					for (uint32_t i = 12; i < 16; i++)
+						min_possible_error_12_15 += basisu::minimum(trial_errors[0][i], trial_errors[1][i], trial_errors[2][i], trial_errors[3][i]);
 
 					uint64_t best_cluster_err = INT64_MAX;
 					uint32_t best_cluster_index = 0;
@@ -2215,7 +2684,7 @@
 
 					const uint32_t total_clusters = m_use_hierarchical_selector_codebooks ? (uint32_t)pCluster_indices->size() : (uint32_t)m_selector_cluster_block_indices.size();
 
-#if 0
+	#if 0
 					for (uint32_t cluster_iter = 0; cluster_iter < total_clusters; cluster_iter++)
 					{
 						const uint32_t cluster_index = m_use_hierarchical_selector_codebooks ? (*pCluster_indices)[cluster_iter] : cluster_iter;
@@ -2246,99 +2715,73 @@
 					early_out:
 						;
 					}
-#else
-					if (m_params.m_perceptual)
+	#else
+					for (uint32_t cluster_iter = 0; cluster_iter < total_clusters; cluster_iter++)
 					{
-						for (uint32_t cluster_iter = 0; cluster_iter < total_clusters; cluster_iter++)
+						const uint32_t cluster_index = m_use_hierarchical_selector_codebooks ? (*pCluster_indices)[cluster_iter] : cluster_iter;
+						
+						const uint8_t* pSels = &unpacked_optimized_cluster_selectors[cluster_index * 16];
+
+						uint64_t trial_err = (uint64_t)trial_errors[pSels[0]][0] + trial_errors[pSels[1]][1] + trial_errors[pSels[2]][2] + trial_errors[pSels[3]][3];
+						if ((trial_err + min_possible_error_4_15) >= best_cluster_err)
+							continue;
+
+						trial_err += (uint64_t)trial_errors[pSels[4]][4] + trial_errors[pSels[5]][5] + trial_errors[pSels[6]][6] + trial_errors[pSels[7]][7];
+						if ((trial_err + min_possible_error_8_15) >= best_cluster_err)
+							continue;
+
+						trial_err += (uint64_t)trial_errors[pSels[8]][8] + trial_errors[pSels[9]][9] + trial_errors[pSels[10]][10] + trial_errors[pSels[11]][11];
+						if ((trial_err + min_possible_error_12_15) >= best_cluster_err)
+							continue;
+
+						trial_err += (uint64_t)trial_errors[pSels[12]][12] + trial_errors[pSels[13]][13] + trial_errors[pSels[14]][14] + trial_errors[pSels[15]][15];
+
+						if (trial_err < best_cluster_err)
 						{
-							const uint32_t cluster_index = m_use_hierarchical_selector_codebooks ? (*pCluster_indices)[cluster_iter] : cluster_iter;
-							//const etc_block& cluster_blk = m_optimized_cluster_selectors[cluster_index];
+							best_cluster_err = trial_err;
+							best_cluster_index = cluster_index;
+							if (best_cluster_err == min_possible_error_0_15)
+								break;
+						}
 
-							uint64_t trial_err = 0;
-																
-							for (int i = 0; i < 16; i++)
-							{
-								const uint32_t sel = unpacked_optimized_cluster_selectors[cluster_index * 16 + i];
-										
-								trial_err += trial_errors[sel][i];
-								if (trial_err > best_cluster_err)
-									goto early_out;
-							}
-
-							if (trial_err < best_cluster_err)
-							{
-								best_cluster_err = trial_err;
-								best_cluster_index = cluster_index;
-								if (!best_cluster_err)
-									break;
-							}
-
-						early_out:
-							;
-
-						} // cluster_iter
-					}
-					else
-					{
-						for (uint32_t cluster_iter = 0; cluster_iter < total_clusters; cluster_iter++)
-						{
-							const uint32_t cluster_index = m_use_hierarchical_selector_codebooks ? (*pCluster_indices)[cluster_iter] : cluster_iter;
-							//const etc_block& cluster_blk = m_optimized_cluster_selectors[cluster_index];
-
-							uint64_t trial_err = 0;
-
-							for (int i = 0; i < 16; i++)
-							{
-								const uint32_t sel = unpacked_optimized_cluster_selectors[cluster_index * 16 + i];
-
-								trial_err += trial_errors[sel][i];
-								if (trial_err > best_cluster_err)
-									goto early_out2;
-							}
-
-							if (trial_err < best_cluster_err)
-							{
-								best_cluster_err = trial_err;
-								best_cluster_index = cluster_index;
-								if (!best_cluster_err)
-									break;
-							}
-
-						early_out2:
-							;
-
-						} // cluster_iter
-					}
-#endif
+					} // cluster_iter
+	#endif
 
 					blk.set_raw_selector_bits(m_optimized_cluster_selectors[best_cluster_index].get_raw_selector_bits());
 
 					m_block_selector_cluster_index[block_index] = best_cluster_index;
-	
-					{
-						std::lock_guard<std::mutex> lock(m_lock);
 
-						vector_ensure_element_is_valid(new_cluster_indices, best_cluster_index);
-						new_cluster_indices[best_cluster_index].push_back(block_index);
-					}
+					prev_best_cluster_index = best_cluster_index;
 					
 				} // block_index
 
-#ifndef __EMSCRIPTEN__
+	#ifndef __EMSCRIPTEN__
 				} );
-#endif
+	#endif
 
 			} // block_index_iter
 
-#ifndef __EMSCRIPTEN__
+	#ifndef __EMSCRIPTEN__
 			m_params.m_pJob_pool->wait_for_all();
-#endif
+	#endif
+						
+			for (uint32_t i = 0; i < m_selector_cluster_block_indices.size(); i++)
+			{
+				m_selector_cluster_block_indices[i].resize(0);
+				m_selector_cluster_block_indices[i].reserve(128);
+			}
 
-			m_selector_cluster_block_indices.swap(new_cluster_indices);
-		}
+			for (uint32_t block_index = 0; block_index < m_total_blocks; block_index++)
+			{
+				const uint32_t best_cluster_index = m_block_selector_cluster_index[block_index];
 
-		for (uint32_t i = 0; i < m_selector_cluster_block_indices.size(); i++)
-			vector_sort(m_selector_cluster_block_indices[i]);
+				vector_ensure_element_is_valid(m_selector_cluster_block_indices, best_cluster_index);
+				m_selector_cluster_block_indices[best_cluster_index].push_back(block_index);
+			}
+		
+		} // if (use_cpu)
+
+		debug_printf("Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
 	}
 
 	// TODO: Remove old ETC1 specific stuff, and thread this.
@@ -2837,7 +3280,81 @@
 						
 		//debug_printf("validate_output: %u\n", validate_output());
 	}
-	
+
+	// Endpoint clusterization hierarchy integrity checker.
+	// Note this doesn't check for empty clusters.
+	bool basisu_frontend::validate_endpoint_cluster_hierarchy(bool ensure_clusters_have_same_parents) const
+	{
+		if (!m_endpoint_parent_clusters.size())
+			return true;
+
+		int_vec subblock_parent_indices(m_total_blocks * 2);
+		subblock_parent_indices.set_all(-1);
+
+		int_vec subblock_cluster_indices(m_total_blocks * 2);
+		subblock_cluster_indices.set_all(-1);
+
+		for (uint32_t parent_index = 0; parent_index < m_endpoint_parent_clusters.size(); parent_index++)
+		{
+			for (uint32_t i = 0; i < m_endpoint_parent_clusters[parent_index].size(); i++)
+			{
+				uint32_t subblock_index = m_endpoint_parent_clusters[parent_index][i];
+				if (subblock_index >= m_total_blocks * 2)
+					return false;
+
+				// If the endpoint cluster lives in more than one parent node, that's wrong.
+				if (subblock_parent_indices[subblock_index] != -1)
+					return false;
+				
+				subblock_parent_indices[subblock_index] = parent_index;
+			}
+		}
+
+		// Make sure all endpoint clusters are present in the parent cluster.
+		for (uint32_t i = 0; i < subblock_parent_indices.size(); i++)
+		{
+			if (subblock_parent_indices[i] == -1)
+				return false;
+		}
+
+		for (uint32_t cluster_index = 0; cluster_index < m_endpoint_clusters.size(); cluster_index++)
+		{
+			int parent_index = 0;
+
+			for (uint32_t i = 0; i < m_endpoint_clusters[cluster_index].size(); i++)
+			{
+				uint32_t subblock_index = m_endpoint_clusters[cluster_index][i];
+				if (subblock_index >= m_total_blocks * 2)
+					return false;
+
+				if (subblock_cluster_indices[subblock_index] != -1)
+					return false;
+				
+				subblock_cluster_indices[subblock_index] = cluster_index;
+
+				// There are transformations on the endpoint clusters that can break the strict tree requirement
+				if (ensure_clusters_have_same_parents)
+				{
+					// Make sure all the subblocks are in the same parent cluster
+					if (!i)
+						parent_index = subblock_parent_indices[subblock_index];
+					else if (subblock_parent_indices[subblock_index] != parent_index)
+						return false;
+				}
+			}
+		}
+				
+		// Make sure all endpoint clusters are present in the parent cluster.
+		for (uint32_t i = 0; i < subblock_cluster_indices.size(); i++)
+		{
+			if (subblock_cluster_indices[i] == -1)
+				return false;
+		}
+
+		return true;
+	}
+
+	// This is very slow and only intended for debugging/development. It's enabled using the "-validate_etc1s" command line option.
 	bool basisu_frontend::validate_output() const
 	{
 		debug_printf("validate_output\n");
@@ -2889,29 +3406,7 @@
 			CHECK(rdo_output_block.get_base5_color() == blk.get_base5_color());
 			CHECK(rdo_output_block.get_delta3_color() == blk.get_delta3_color());
 			CHECK(rdo_output_block.get_raw_selector_bits() == blk.get_raw_selector_bits());
-
-			if (m_params.m_pGlobal_sel_codebook)
-			{
-				bool used_global_cb = true;
-				if (m_params.m_use_hybrid_selector_codebooks)
-					used_global_cb = m_selector_cluster_uses_global_cb[selector_cluster_index];
-
-				if (used_global_cb)
-				{
-					basist::etc1_global_selector_codebook_entry_id pal_id(get_selector_cluster_global_selector_entry_ids()[selector_cluster_index]);
-
-					basist::etc1_selector_palette_entry pal_entry(m_params.m_pGlobal_sel_codebook->get_entry(pal_id));
-
-					for (uint32_t y = 0; y < 4; y++)
-					{
-						for (uint32_t x = 0; x < 4; x++)
-						{
-							CHECK(pal_entry(x, y) == blk.get_selector(x, y));
-						}
-					}
-				}
-			}
-
+						
 #undef CHECK
 		}
 
diff --git a/encoder/basisu_frontend.h b/encoder/basisu_frontend.h
index 4ff6d40..cda73f3 100644
--- a/encoder/basisu_frontend.h
+++ b/encoder/basisu_frontend.h
@@ -16,12 +16,14 @@
 #include "basisu_enc.h"
 #include "basisu_etc.h"
 #include "basisu_gpu_texture.h"
-#include "basisu_global_selector_palette_helpers.h"
 #include "../transcoder/basisu_file_headers.h"
 #include "../transcoder/basisu_transcoder.h"
 
 namespace basisu
 {
+	struct opencl_context;
+	typedef opencl_context* opencl_context_ptr;
+
 	struct vec2U
 	{
 		uint32_t m_comps[2];
@@ -51,7 +53,8 @@
 			m_use_hierarchical_endpoint_codebooks(false),
 			m_use_hierarchical_selector_codebooks(false),
 			m_num_endpoint_codebook_iterations(0),
-			m_num_selector_codebook_iterations(0)
+			m_num_selector_codebook_iterations(0),
+			m_opencl_failed(false)
 		{
 		}
 
@@ -73,19 +76,12 @@
 				m_perceptual(true),
 				m_debug_stats(false),
 				m_debug_images(false),
-																
 				m_dump_endpoint_clusterization(true),
 				m_validate(false),
 				m_multithreaded(false),
 				m_disable_hierarchical_endpoint_codebooks(false),
-				m_pGlobal_sel_codebook(NULL),
-				m_num_global_sel_codebook_pal_bits(0),
-				m_num_global_sel_codebook_mod_bits(0),
-				m_use_hybrid_selector_codebooks(false),
-				m_hybrid_codebook_quality_thresh(0.0f),
 				m_tex_type(basist::cBASISTexType2D),
-				m_pGlobal_codebooks(nullptr),
-				
+				m_pOpenCL_context(nullptr),
 				m_pJob_pool(nullptr)
 			{
 			}
@@ -106,13 +102,10 @@
 			bool m_multithreaded;
 			bool m_disable_hierarchical_endpoint_codebooks;
 			
-			const basist::etc1_global_selector_codebook *m_pGlobal_sel_codebook;
-			uint32_t m_num_global_sel_codebook_pal_bits;
-			uint32_t m_num_global_sel_codebook_mod_bits;
-			bool m_use_hybrid_selector_codebooks;
-			float m_hybrid_codebook_quality_thresh;
 			basist::basis_texture_type m_tex_type;
 			const basist::basisu_lowlevel_etc1s_transcoder *m_pGlobal_codebooks;
+						
+			opencl_context_ptr m_pOpenCL_context;
 			
 			job_pool *m_pJob_pool;
 		};
@@ -150,10 +143,7 @@
 		uint32_t get_total_selector_clusters() const { return static_cast<uint32_t>(m_selector_cluster_block_indices.size()); }
 		uint32_t get_block_selector_cluster_index(uint32_t block_index) const { return m_block_selector_cluster_index[block_index]; }
 		const etc_block &get_selector_cluster_selector_bits(uint32_t cluster_index) const { return m_optimized_cluster_selectors[cluster_index]; }
-
-		const basist::etc1_global_selector_codebook_entry_id_vec &get_selector_cluster_global_selector_entry_ids() const { return m_optimized_cluster_selector_global_cb_ids; }
-		const bool_vec &get_selector_cluster_uses_global_cb_vec() const { return m_selector_cluster_uses_global_cb; }
-
+				
 		// Returns block indices using each selector cluster
 		const uint_vec &get_selector_cluster_block_indices(uint32_t selector_cluster_index) const { return m_selector_cluster_block_indices[selector_cluster_index]; }
 
@@ -161,6 +151,8 @@
 		
 		void reoptimize_remapped_endpoints(const uint_vec &new_block_endpoints, int_vec &old_to_new_endpoint_cluster_indices, bool optimize_final_codebook, uint_vec *pBlock_selector_indices = nullptr);
 
+		bool get_opencl_failed() const { return m_opencl_failed; }
+
 	private:
 		params m_params;
 		uint32_t m_total_blocks;
@@ -192,13 +184,14 @@
 		vec6F_quantizer m_endpoint_clusterizer;
 
 		// For each endpoint cluster: An array of which subblock indices (block_index*2+subblock) are located in that cluster.
-		// Array of block indices for each endpoint cluster
 		basisu::vector<uint_vec> m_endpoint_clusters;
 
-		// Array of block indices for each parent endpoint cluster
+		// Array of subblock indices for each parent endpoint cluster
+		// Note: Initially, each endpoint cluster will only live in a single parent cluster, in a shallow tree. 
+		// As the endpoint clusters are manipulated this constraint gets broken.
 		basisu::vector<uint_vec> m_endpoint_parent_clusters;
 		
-		// Each block's parent cluster index
+		// Each block's parent endpoint cluster index
 		uint8_vec m_block_parent_endpoint_cluster; 
 
 		// Array of endpoint cluster indices for each parent endpoint cluster
@@ -295,10 +288,7 @@
 
 		// Array of selector cluster indices for each parent selector cluster
 		basisu::vector<uint_vec> m_selector_clusters_within_each_parent_cluster;
-
-		basist::etc1_global_selector_codebook_entry_id_vec m_optimized_cluster_selector_global_cb_ids;
-		bool_vec m_selector_cluster_uses_global_cb;
-
+				
 		// Each block's selector cluster index
 		basisu::vector<uint32_t> m_block_selector_cluster_index;
 
@@ -330,6 +320,8 @@
 
 		std::mutex m_lock;
 
+		bool m_opencl_failed;
+
 		//-----------------------------------------------------------------------------
 
 		void init_etc1_images();
@@ -351,6 +343,7 @@
 		void find_optimal_selector_clusters_for_each_block();
 		uint32_t refine_block_endpoints_given_selectors();
 		void finalize();
+		bool validate_endpoint_cluster_hierarchy(bool ensure_clusters_have_same_parents) const;
 		bool validate_output() const;
 		void introduce_special_selector_clusters();
 		void optimize_selector_codebook();
diff --git a/encoder/basisu_global_selector_palette_helpers.cpp b/encoder/basisu_global_selector_palette_helpers.cpp
deleted file mode 100644
index 102fc24..0000000
--- a/encoder/basisu_global_selector_palette_helpers.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-// basiu_global_selector_palette_helpers.cpp
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-#include "basisu_global_selector_palette_helpers.h"
-
-namespace basisu
-{
-	uint64_t etc1_global_selector_codebook_find_best_entry(const basist::etc1_global_selector_codebook &codebook,
-		uint32_t num_src_pixel_blocks, const pixel_block *pSrc_pixel_blocks, const etc_block *pBlock_endpoints,
-		uint32_t &palette_index, basist::etc1_global_palette_entry_modifier &palette_modifier,
-		bool perceptual, uint32_t max_pal_entries, uint32_t max_modifiers)
-	{
-		uint64_t best_err = UINT64_MAX;
-		uint32_t best_pal_index = 0;
-		basist::etc1_global_palette_entry_modifier best_pal_modifier;
-
-		if (!max_pal_entries)
-			max_pal_entries = codebook.size();
-
-		if (!max_modifiers)
-			max_modifiers = basist::etc1_global_palette_entry_modifier::cTotalValues;
-
-		for (uint32_t pal_index = 0; pal_index < max_pal_entries; pal_index++)
-		{
-			for (uint32_t mod_index = 0; mod_index < max_modifiers; mod_index++)
-			{
-				const basist::etc1_global_palette_entry_modifier pal_modifier(mod_index);
-
-				const basist::etc1_selector_palette_entry pal_entry(codebook.get_entry(pal_index, pal_modifier));
-
-				uint64_t trial_err = 0;
-				for (uint32_t block_index = 0; block_index < num_src_pixel_blocks; block_index++)
-				{
-					etc_block trial_block(pBlock_endpoints[block_index]);
-
-					for (uint32_t y = 0; y < 4; y++)
-						for (uint32_t x = 0; x < 4; x++)
-							trial_block.set_selector(x, y, pal_entry(x, y));
-
-					trial_err += trial_block.evaluate_etc1_error(reinterpret_cast<const basisu::color_rgba *>(pSrc_pixel_blocks[block_index].get_ptr()), perceptual);
-					if (trial_err >= best_err)
-						break;
-				}
-
-				if (trial_err < best_err)
-				{
-					best_err = trial_err;
-					best_pal_index = pal_index;
-					best_pal_modifier = pal_modifier;
-				}
-			} // mod_index
-		} // pal_index
-
-		palette_index = best_pal_index;
-		palette_modifier = best_pal_modifier;
-
-		return best_err;
-	}
-
-} // namespace basisu
diff --git a/encoder/basisu_global_selector_palette_helpers.h b/encoder/basisu_global_selector_palette_helpers.h
deleted file mode 100644
index 7c35439..0000000
--- a/encoder/basisu_global_selector_palette_helpers.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// File: basisu_global_selector_palette_helpers.h
-// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-#pragma once
-
-#include "../transcoder/basisu.h"
-#include "basisu_etc.h"
-#include "../transcoder/basisu_global_selector_palette.h"
-
-namespace basisu
-{
-	const uint32_t cPixelBlockWidth = 4;
-	const uint32_t cPixelBlockHeight = 4;
-	const uint32_t cPixelBlockTotalPixels = cPixelBlockWidth * cPixelBlockHeight;
-
-	struct pixel_block
-	{
-		color_rgba m_pixels[cPixelBlockHeight][cPixelBlockWidth]; // [y][x]
-
-		const color_rgba &operator() (uint32_t x, uint32_t y) const { assert((x < cPixelBlockWidth) && (y < cPixelBlockHeight)); return m_pixels[y][x]; }
-		color_rgba &operator() (uint32_t x, uint32_t y) { assert((x < cPixelBlockWidth) && (y < cPixelBlockHeight)); return m_pixels[y][x]; }
-
-		const color_rgba *get_ptr() const { return &m_pixels[0][0]; }
-		color_rgba *get_ptr() { return &m_pixels[0][0]; }
-
-		void clear() { clear_obj(*this); }
-	};
-	typedef basisu::vector<pixel_block> pixel_block_vec;
-
-	uint64_t etc1_global_selector_codebook_find_best_entry(const basist::etc1_global_selector_codebook &codebook,
-		uint32_t num_src_pixel_blocks, const pixel_block *pSrc_pixel_blocks, const etc_block *pBlock_endpoints,
-		uint32_t &palette_index, basist::etc1_global_palette_entry_modifier &palette_modifier,
-		bool perceptual, uint32_t max_pal_entries, uint32_t max_modifiers);
-
-} // namespace basisu
diff --git a/encoder/basisu_gpu_texture.cpp b/encoder/basisu_gpu_texture.cpp
index 3f9fb67..dec769d 100644
--- a/encoder/basisu_gpu_texture.cpp
+++ b/encoder/basisu_gpu_texture.cpp
@@ -15,7 +15,9 @@
 #include "basisu_gpu_texture.h"
 #include "basisu_enc.h"
 #include "basisu_pvrtc1_4.h"
+#if BASISU_USE_ASTC_DECOMPRESS
 #include "basisu_astc_decomp.h"
+#endif
 #include "basisu_bc7enc.h"
 
 namespace basisu
@@ -1150,8 +1152,12 @@
 		}
 		case texture_format::cASTC4x4:
 		{
+#if BASISU_USE_ASTC_DECOMPRESS
 			const bool astc_srgb = false;
 			basisu_astc::astc::decompress(reinterpret_cast<uint8_t*>(pPixels), static_cast<const uint8_t*>(pBlock), astc_srgb, 4, 4);
+#else
+			memset(pPixels, 255, 16 * sizeof(color_rgba));
+#endif
 			break;
 		}
 		case texture_format::cATC_RGB:
@@ -1498,6 +1504,8 @@
 		
 		header.m_pixelWidth = width;
 		header.m_pixelHeight = height;
+				
+		header.m_glTypeSize = 1;
 		
 		header.m_glInternalFormat = internal_fmt;
 		header.m_glBaseInternalFormat = base_internal_fmt;
diff --git a/encoder/basisu_kernels_declares.h b/encoder/basisu_kernels_declares.h
index e24bdd7..b03e2ea 100644
--- a/encoder/basisu_kernels_declares.h
+++ b/encoder/basisu_kernels_declares.h
@@ -22,4 +22,6 @@
 
 void CPPSPMD_NAME(find_lowest_error_perceptual_rgb_4_N)(int64_t* pDistance, const basisu::color_rgba* pBlock_colors, const basisu::color_rgba* pSrc_pixels, uint32_t n, int64_t early_out_error);
 void CPPSPMD_NAME(find_lowest_error_linear_rgb_4_N)(int64_t* pDistance, const basisu::color_rgba* pBlock_colors, const basisu::color_rgba* pSrc_pixels, uint32_t n, int64_t early_out_error);
+
+void CPPSPMD_NAME(update_covar_matrix_16x16)(uint32_t num_vecs, const void* pWeighted_vecs, const void *pOrigin, const uint32_t* pVec_indices, void *pMatrix16x16);
 #endif
diff --git a/encoder/basisu_kernels_imp.h b/encoder/basisu_kernels_imp.h
index 0468805..dcf1ce0 100644
--- a/encoder/basisu_kernels_imp.h
+++ b/encoder/basisu_kernels_imp.h
@@ -548,6 +548,65 @@
       }
    };
 
+   struct update_covar_matrix_16x16 : spmd_kernel
+   {
+      void _call(
+         uint32_t num_vecs, const void* pWeighted_vecs_void, const void* pOrigin_void, const uint32_t* pVec_indices, void* pMatrix16x16_void)
+      {
+         const std::pair<vec16F, uint64_t>* pWeighted_vecs = static_cast< const std::pair<vec16F, uint64_t> *>(pWeighted_vecs_void);
+         
+         const float* pOrigin = static_cast<const float*>(pOrigin_void);
+         vfloat org0 = loadu_linear_all(pOrigin), org1 = loadu_linear_all(pOrigin + 4), org2 = loadu_linear_all(pOrigin + 8), org3 = loadu_linear_all(pOrigin + 12);
+                  
+         vfloat mat[16][4];
+         vfloat vzero(zero_vfloat());
+
+         for (uint32_t i = 0; i < 16; i++)
+         {
+            store_all(mat[i][0], vzero);
+            store_all(mat[i][1], vzero);
+            store_all(mat[i][2], vzero);
+            store_all(mat[i][3], vzero);
+         }
+
+         for (uint32_t k = 0; k < num_vecs; k++)
+         {
+            const uint32_t vec_index = pVec_indices[k];
+
+            const float* pW = pWeighted_vecs[vec_index].first.get_ptr();
+            vfloat weight((float)pWeighted_vecs[vec_index].second);
+
+            vfloat vec[4] = { loadu_linear_all(pW) - org0, loadu_linear_all(pW + 4) - org1, loadu_linear_all(pW + 8) - org2, loadu_linear_all(pW + 12) - org3 };
+                                                
+            vfloat wvec0 = vec[0] * weight, wvec1 = vec[1] * weight, wvec2 = vec[2] * weight, wvec3 = vec[3] * weight;
+
+            for (uint32_t j = 0; j < 16; j++)
+            {
+               vfloat vx = ((const float*)vec)[j];
+
+               store_all(mat[j][0], mat[j][0] + vx * wvec0);
+               store_all(mat[j][1], mat[j][1] + vx * wvec1);
+               store_all(mat[j][2], mat[j][2] + vx * wvec2);
+               store_all(mat[j][3], mat[j][3] + vx * wvec3);
+
+            } // j
+
+         } // k
+
+         float* pMatrix = static_cast<float*>(pMatrix16x16_void);
+
+         float* pDst = pMatrix;
+         for (uint32_t i = 0; i < 16; i++)
+         {
+            storeu_linear_all(pDst, mat[i][0]);
+            storeu_linear_all(pDst + 4, mat[i][1]);
+            storeu_linear_all(pDst + 8, mat[i][2]);
+            storeu_linear_all(pDst + 12, mat[i][3]);
+            pDst += 16;
+         }
+      }
+   };
+
 } // namespace
 
 using namespace CPPSPMD_NAME(basisu_kernels_namespace);
@@ -582,3 +641,7 @@
    spmd_call< find_lowest_error_linear_rgb_4_N >(pDistance, pBlock_colors, pSrc_pixels, n, early_out_error);
 }
 
+void CPPSPMD_NAME(update_covar_matrix_16x16)(uint32_t num_vecs, const void* pWeighted_vecs, const void* pOrigin, const uint32_t *pVec_indices, void* pMatrix16x16)
+{
+   spmd_call < update_covar_matrix_16x16 >(num_vecs, pWeighted_vecs, pOrigin, pVec_indices, pMatrix16x16);
+}
diff --git a/encoder/basisu_miniz.h b/encoder/basisu_miniz.h
index 8627abe..18de997 100644
--- a/encoder/basisu_miniz.h
+++ b/encoder/basisu_miniz.h
@@ -76,6 +76,14 @@
 #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
 #endif
 
+// Using unaligned loads and stores causes errors when using UBSan. Jam it off.
+#if defined(__has_feature)
+#if __has_feature(undefined_behavior_sanitizer)
+#undef MINIZ_USE_UNALIGNED_LOADS_AND_STORES
+#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0
+#endif
+#endif
+
 #if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__)
 // Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions).
 #define MINIZ_HAS_64BIT_REGISTERS 1
@@ -236,6 +244,7 @@
 //   MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again
 //   with more input data, or with more room in the output buffer (except when using single call decompression, described above).
 int mz_inflate(mz_streamp pStream, int flush);
+int mz_inflate2(mz_streamp pStream, int flush, int adler32_checking);
 
 // Deinitializes a decompressor.
 int mz_inflateEnd(mz_streamp pStream);
@@ -880,10 +889,10 @@
    return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS);
 }
 
-int mz_inflate(mz_streamp pStream, int flush)
+int mz_inflate2(mz_streamp pStream, int flush, int adler32_checking)
 {
   inflate_state* pState;
-  mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32;
+  mz_uint n, first_call, decomp_flags = adler32_checking ? TINFL_FLAG_COMPUTE_ADLER32 : 0;
   size_t in_bytes, out_bytes, orig_avail_in;
   tinfl_status status;
 
@@ -971,6 +980,11 @@
   return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
 }
 
+int mz_inflate(mz_streamp pStream, int flush)
+{
+    return mz_inflate2(pStream, flush, MZ_TRUE);
+}
+
 int mz_inflateEnd(mz_streamp pStream)
 {
   if (!pStream)
@@ -1348,7 +1362,8 @@
 common_exit:
   r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start;
   *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next;
-  if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))
+  //if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))
+  if ((decomp_flags & TINFL_FLAG_COMPUTE_ADLER32) && (status >= 0))
   {
     const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size;
     mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552;
@@ -1362,7 +1377,9 @@
       for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
       s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
     }
-    r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH;
+    r->m_check_adler32 = (s2 << 16) + s1; 
+    if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) 
+        status = TINFL_STATUS_ADLER32_MISMATCH;
   }
   return status;
 }
@@ -2479,7 +2496,7 @@
   // write dummy header
   for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf);
   // compress image data
-  tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER);
+  tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER | (level <= 3 ? TDEFL_GREEDY_PARSING_FLAG : 0));
   for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); }
   if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; }
   // write real header
diff --git a/encoder/basisu_ocl_kernels.h b/encoder/basisu_ocl_kernels.h
new file mode 100644
index 0000000..46db61b
--- /dev/null
+++ b/encoder/basisu_ocl_kernels.h
@@ -0,0 +1,1439 @@
+unsigned char ocl_kernels_cl[] = {
+  0x2f, 0x2f, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x44, 0x45, 0x42, 0x55, 0x47, 0x0d, 0x0a, 0x0d, 0x0a, 0x23, 0x69, 0x66, 0x6e, 0x64, 0x65, 0x66, 0x20, 0x4e, 0x55, 0x4c, 0x4c,
+  0x0d, 0x0a, 0x09, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x4e, 0x55, 0x4c, 0x4c, 0x20, 0x30, 0x4c, 0x0d, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x79,
+  0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x3b, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x75, 0x63, 0x68, 0x61,
+  0x72, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x31,
+  0x36, 0x5f, 0x74, 0x3b, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x3b, 0x0d, 0x0a,
+  0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x3b, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20,
+  0x75, 0x69, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x69,
+  0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x3b, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x3b,
+  0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x34, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x3b, 0x0d, 0x0a,
+  0x0d, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x55, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x5f, 0x4d, 0x41, 0x58, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x55,
+  0x4c, 0x0d, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x5f, 0x4d, 0x41, 0x58, 0x20, 0x4c, 0x4f, 0x4e, 0x47, 0x5f, 0x4d, 0x41, 0x58, 0x0d, 0x0a, 0x23,
+  0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x5f, 0x4d, 0x41, 0x58, 0x20, 0x55, 0x4c, 0x4f, 0x4e, 0x47, 0x5f, 0x4d, 0x41, 0x58, 0x0d, 0x0a, 0x0d, 0x0a, 0x69,
+  0x6e, 0x74, 0x20, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x69, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x61, 0x29, 0x20, 0x7b, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x2a, 0x20, 0x61,
+  0x3b, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x23, 0x69, 0x66, 0x64, 0x65, 0x66, 0x20, 0x5f, 0x44, 0x45, 0x42, 0x55, 0x47, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x76, 0x6f,
+  0x69, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x78, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74,
+  0x61, 0x6e, 0x74, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x70, 0x4d, 0x73, 0x67, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09,
+  0x09, 0x69, 0x66, 0x20, 0x28, 0x21, 0x78, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x29, 0x20, 0x66, 0x61,
+  0x69, 0x6c, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x25, 0x69, 0x3a, 0x20, 0x25, 0x73, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x2c, 0x20, 0x70, 0x4d,
+  0x73, 0x67, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x78, 0x29, 0x20, 0x69, 0x6e, 0x74,
+  0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x78, 0x2c, 0x20, 0x23, 0x78, 0x2c, 0x20, 0x5f, 0x5f, 0x4c, 0x49, 0x4e, 0x45, 0x5f, 0x5f, 0x29, 0x0d, 0x0a, 0x23,
+  0x65, 0x6c, 0x73, 0x65, 0x0d, 0x0a, 0x09, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x78, 0x29, 0x0d, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66,
+  0x0d, 0x0a, 0x0d, 0x0a, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x28, 0x69, 0x6e, 0x74, 0x20,
+  0x78, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x78, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x29, 0x3b,
+  0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x5f, 0x66,
+  0x6c, 0x61, 0x67, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x78, 0x2c, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x2a, 0x70, 0x44, 0x69, 0x64, 0x5f, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x29, 0x0d, 0x0a, 0x7b, 0x0d,
+  0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x78, 0x20, 0x3c, 0x20, 0x30, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x2a, 0x70, 0x44, 0x69, 0x64, 0x5f, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x20,
+  0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x20,
+  0x69, 0x66, 0x20, 0x28, 0x78, 0x20, 0x3e, 0x20, 0x32, 0x35, 0x35, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x2a, 0x70, 0x44, 0x69, 0x64, 0x5f, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x20,
+  0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x32, 0x35, 0x35, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x72,
+  0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x29, 0x28, 0x78, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65,
+  0x66, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x20, 0x28, 0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x29,
+  0x29, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x0d,
+  0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6d, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3b, 0x0d, 0x0a, 0x09,
+  0x69, 0x6e, 0x74, 0x20, 0x6d, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x6d, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f,
+  0x70, 0x65, 0x72, 0x6d, 0x73, 0x3b, 0x0d, 0x0a, 0x7d, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x73, 0x74, 0x72,
+  0x75, 0x63, 0x74, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
+  0x74, 0x65, 0x5f, 0x5f, 0x20, 0x28, 0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x29, 0x29, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x61, 0x67, 0x0d,
+  0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x6d, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x5b, 0x31, 0x36, 0x5d, 0x3b, 0x20, 0x2f, 0x2f,
+  0x20, 0x5b, 0x79, 0x2a, 0x34, 0x2b, 0x78, 0x5d, 0x0d, 0x0a, 0x7d, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x75, 0x69, 0x6e, 0x74,
+  0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x28, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x2c,
+  0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x65, 0x31, 0x2c, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x65, 0x32, 0x2c, 0x20, 0x62,
+  0x6f, 0x6f, 0x6c, 0x20, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x29, 0x0d,
+  0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x30, 0x0d, 0x0a, 0x09, 0x09, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x72, 0x67, 0x62, 0x20, 0x3d,
+  0x20, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x29, 0x28, 0x65, 0x31, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x65, 0x32, 0x2e, 0x78, 0x2c, 0x20, 0x65, 0x31, 0x2e, 0x79, 0x20, 0x2d, 0x20, 0x65, 0x32,
+  0x2e, 0x79, 0x2c, 0x20, 0x65, 0x31, 0x2e, 0x7a, 0x20, 0x2d, 0x20, 0x65, 0x32, 0x2e, 0x7a, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x64, 0x65,
+  0x6c, 0x74, 0x61, 0x5f, 0x79, 0x63, 0x62, 0x63, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x79, 0x63, 0x62, 0x63, 0x72, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x64, 0x6f,
+  0x74, 0x28, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x72, 0x67, 0x62, 0x2c, 0x20, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x29, 0x28, 0x2e, 0x32, 0x31, 0x32, 0x36, 0x66, 0x2c, 0x20, 0x2e, 0x37,
+  0x31, 0x35, 0x32, 0x66, 0x2c, 0x20, 0x2e, 0x30, 0x37, 0x32, 0x32, 0x66, 0x29, 0x29, 0x3b, 0x20, 0x2f, 0x2f, 0x20, 0x79, 0x0d, 0x0a, 0x09, 0x09, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x79, 0x63,
+  0x62, 0x63, 0x72, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x72, 0x67, 0x62, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x79, 0x63, 0x62, 0x63,
+  0x72, 0x2e, 0x78, 0x3b, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x72, 0x0d, 0x0a, 0x09, 0x09, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x79, 0x63, 0x62, 0x63, 0x72, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x64, 0x65,
+  0x6c, 0x74, 0x61, 0x5f, 0x72, 0x67, 0x62, 0x2e, 0x7a, 0x20, 0x2d, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x79, 0x63, 0x62, 0x63, 0x72, 0x2e, 0x78, 0x3b, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x62,
+  0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x79, 0x63, 0x62, 0x63, 0x72, 0x20, 0x2a, 0x3d, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x79, 0x63, 0x62, 0x63, 0x72,
+  0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x64, 0x20, 0x3d, 0x20, 0x64, 0x6f, 0x74, 0x28, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x79, 0x63, 0x62, 0x63, 0x72,
+  0x2c, 0x20, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x29, 0x28, 0x31, 0x2e, 0x30, 0x66, 0x2c, 0x20, 0x30, 0x2e, 0x32, 0x30, 0x33, 0x31, 0x32, 0x35, 0x66, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x32,
+  0x33, 0x34, 0x33, 0x37, 0x35, 0x66, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7b, 0x0d, 0x0a,
+  0x09, 0x09, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x61, 0x20, 0x3d, 0x20, 0x65, 0x31, 0x2e, 0x77, 0x20, 0x2d, 0x20, 0x65, 0x32, 0x2e, 0x77, 0x3b, 0x0d, 0x0a, 0x09,
+  0x09, 0x09, 0x64, 0x20, 0x2b, 0x3d, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x61, 0x20, 0x2a, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x61, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a,
+  0x0d, 0x0a, 0x09, 0x09, 0x64, 0x20, 0x3d, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x64, 0x20, 0x2a, 0x20, 0x32, 0x35, 0x36, 0x2e, 0x30, 0x66, 0x20, 0x2b, 0x20, 0x2e, 0x35, 0x66, 0x2c, 0x20,
+  0x30, 0x2e, 0x30, 0x66, 0x2c, 0x20, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x29, 0x55, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x5f, 0x4d, 0x41, 0x58, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x72,
+  0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x29, 0x28, 0x64, 0x29, 0x3b, 0x0d, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0d, 0x0a, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x54, 0x68,
+  0x69, 0x73, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x43, 0x50, 0x55, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x69,
+  0x73, 0x20, 0x75, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x72, 0x20,
+  0x3d, 0x20, 0x65, 0x31, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x65, 0x32, 0x2e, 0x78, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x67, 0x20, 0x3d, 0x20, 0x65, 0x31, 0x2e, 0x79, 0x20,
+  0x2d, 0x20, 0x65, 0x32, 0x2e, 0x79, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x62, 0x20, 0x3d, 0x20, 0x65, 0x31, 0x2e, 0x7a, 0x20, 0x2d, 0x20, 0x65, 0x32, 0x2e, 0x7a, 0x3b,
+  0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x6c, 0x20, 0x3d, 0x20, 0x64, 0x72, 0x20, 0x2a, 0x20, 0x32, 0x37, 0x20, 0x2b, 0x20, 0x64, 0x67,
+  0x20, 0x2a, 0x20, 0x39, 0x32, 0x20, 0x2b, 0x20, 0x64, 0x62, 0x20, 0x2a, 0x20, 0x39, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x63, 0x72, 0x20,
+  0x3d, 0x20, 0x64, 0x72, 0x20, 0x2a, 0x20, 0x31, 0x32, 0x38, 0x20, 0x2d, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x6c, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x65, 0x6c,
+  0x74, 0x61, 0x5f, 0x63, 0x62, 0x20, 0x3d, 0x20, 0x64, 0x62, 0x20, 0x2a, 0x20, 0x31, 0x32, 0x38, 0x20, 0x2d, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x6c, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09,
+  0x09, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x64, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x29, 0x28, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x6c, 0x20, 0x2a, 0x20, 0x64, 0x65, 0x6c,
+  0x74, 0x61, 0x5f, 0x6c, 0x29, 0x20, 0x3e, 0x3e, 0x20, 0x37, 0x55, 0x29, 0x20, 0x2b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x28, 0x28, 0x28, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x29, 0x28, 0x64, 0x65, 0x6c,
+  0x74, 0x61, 0x5f, 0x63, 0x72, 0x20, 0x2a, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x63, 0x72, 0x29, 0x20, 0x3e, 0x3e, 0x20, 0x37, 0x55, 0x29, 0x20, 0x2a, 0x20, 0x32, 0x36, 0x55, 0x29, 0x20,
+  0x3e, 0x3e, 0x20, 0x37, 0x55, 0x29, 0x20, 0x2b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x28, 0x28, 0x28, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x29, 0x28, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x63, 0x62, 0x20,
+  0x2a, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x63, 0x62, 0x29, 0x20, 0x3e, 0x3e, 0x20, 0x37, 0x55, 0x29, 0x20, 0x2a, 0x20, 0x33, 0x55, 0x29, 0x20, 0x3e, 0x3e, 0x20, 0x37, 0x55, 0x29, 0x3b,
+  0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x61,
+  0x20, 0x3d, 0x20, 0x28, 0x65, 0x31, 0x2e, 0x77, 0x20, 0x2d, 0x20, 0x65, 0x32, 0x2e, 0x77, 0x29, 0x20, 0x3c, 0x3c, 0x20, 0x37, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x64, 0x20, 0x2b, 0x3d,
+  0x20, 0x28, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x29, 0x28, 0x64, 0x61, 0x20, 0x2a, 0x20, 0x64, 0x61, 0x29, 0x20, 0x3e, 0x3e, 0x20, 0x37, 0x55, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a,
+  0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x64, 0x3b, 0x0d, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x65,
+  0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x72, 0x20, 0x3d, 0x20, 0x65,
+  0x31, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x65, 0x32, 0x2e, 0x78, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x67, 0x20, 0x3d, 0x20, 0x65, 0x31, 0x2e, 0x79, 0x20, 0x2d, 0x20, 0x65,
+  0x32, 0x2e, 0x79, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x62, 0x20, 0x3d, 0x20, 0x65, 0x31, 0x2e, 0x7a, 0x20, 0x2d, 0x20, 0x65, 0x32, 0x2e, 0x7a, 0x3b, 0x09, 0x0d, 0x0a,
+  0x09, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x61, 0x20, 0x3d, 0x20, 0x65, 0x31, 0x2e, 0x77, 0x20, 0x2d, 0x20, 0x65, 0x32, 0x2e, 0x77, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72,
+  0x6e, 0x20, 0x64, 0x72, 0x20, 0x2a, 0x20, 0x64, 0x72, 0x20, 0x2b, 0x20, 0x64, 0x67, 0x20, 0x2a, 0x20, 0x64, 0x67, 0x20, 0x2b, 0x20, 0x64, 0x62, 0x20, 0x2a, 0x20, 0x64, 0x62, 0x20, 0x2b, 0x20,
+  0x64, 0x61, 0x20, 0x2a, 0x20, 0x64, 0x61, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64,
+  0x72, 0x20, 0x3d, 0x20, 0x65, 0x31, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x65, 0x32, 0x2e, 0x78, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x67, 0x20, 0x3d, 0x20, 0x65, 0x31, 0x2e,
+  0x79, 0x20, 0x2d, 0x20, 0x65, 0x32, 0x2e, 0x79, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x62, 0x20, 0x3d, 0x20, 0x65, 0x31, 0x2e, 0x7a, 0x20, 0x2d, 0x20, 0x65, 0x32, 0x2e,
+  0x7a, 0x3b, 0x09, 0x0d, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x64, 0x72, 0x20, 0x2a, 0x20, 0x64, 0x72, 0x20, 0x2b, 0x20, 0x64, 0x67, 0x20, 0x2a, 0x20, 0x64, 0x67, 0x20,
+  0x2b, 0x20, 0x64, 0x62, 0x20, 0x2a, 0x20, 0x64, 0x62, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x73, 0x74, 0x72,
+  0x75, 0x63, 0x74, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x20, 0x28, 0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x29, 0x29, 0x20, 0x65, 0x74, 0x63,
+  0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x61, 0x67, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x2f, 0x2f, 0x20, 0x62, 0x69, 0x67, 0x20, 0x65, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x20, 0x75, 0x69,
+  0x6e, 0x74, 0x36, 0x34, 0x3a, 0x0d, 0x0a, 0x09, 0x2f, 0x2f, 0x20, 0x62, 0x69, 0x74, 0x20, 0x6f, 0x66, 0x73, 0x3a, 0x20, 0x20, 0x35, 0x36, 0x20, 0x20, 0x34, 0x38, 0x20, 0x20, 0x34, 0x30, 0x20,
+  0x20, 0x33, 0x32, 0x20, 0x20, 0x32, 0x34, 0x20, 0x20, 0x31, 0x36, 0x20, 0x20, 0x20, 0x38, 0x20, 0x20, 0x20, 0x30, 0x0d, 0x0a, 0x09, 0x2f, 0x2f, 0x20, 0x62, 0x79, 0x74, 0x65, 0x20, 0x6f, 0x66,
+  0x73, 0x3a, 0x20, 0x62, 0x30, 0x2c, 0x20, 0x62, 0x31, 0x2c, 0x20, 0x62, 0x32, 0x2c, 0x20, 0x62, 0x33, 0x2c, 0x20, 0x62, 0x34, 0x2c, 0x20, 0x62, 0x35, 0x2c, 0x20, 0x62, 0x36, 0x2c, 0x20, 0x62,
+  0x37, 0x20, 0x0d, 0x0a, 0x09, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x6d, 0x5f, 0x75, 0x69, 0x6e,
+  0x74, 0x36, 0x34, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x20, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5b, 0x38, 0x5d, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x3b,
+  0x0d, 0x0a, 0x0d, 0x0a, 0x7d, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x65, 0x6e, 0x75, 0x6d, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x63, 0x6f, 0x6e,
+  0x73, 0x74, 0x61, 0x6e, 0x74, 0x73, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x42, 0x79, 0x74, 0x65, 0x73, 0x50, 0x65, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x3d,
+  0x20, 0x38, 0x55, 0x2c, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x42, 0x69, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x32, 0x55, 0x2c,
+  0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x31, 0x55, 0x20, 0x3c, 0x3c, 0x20, 0x63,
+  0x45, 0x54, 0x43, 0x31, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x42, 0x69, 0x74, 0x73, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f,
+  0x72, 0x4d, 0x61, 0x73, 0x6b, 0x20, 0x3d, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x2d, 0x20, 0x31, 0x55,
+  0x2c, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x68, 0x69, 0x66, 0x74, 0x20, 0x3d, 0x20, 0x32, 0x55, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x45,
+  0x54, 0x43, 0x31, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x20, 0x31, 0x55, 0x20, 0x3c, 0x3c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x53,
+  0x68, 0x69, 0x66, 0x74, 0x2c, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x4c, 0x53, 0x42, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65,
+  0x73, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x30, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x4d, 0x53, 0x42, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74,
+  0x6f, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x31, 0x36, 0x2c, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54,
+  0x43, 0x31, 0x46, 0x6c, 0x69, 0x70, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x33, 0x32, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x44, 0x69, 0x66,
+  0x66, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x33, 0x33, 0x2c, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x4d,
+  0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x4e, 0x75, 0x6d, 0x42, 0x69, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x33, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x49, 0x6e, 0x74, 0x65, 0x6e,
+  0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x31, 0x20, 0x3c, 0x3c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x49, 0x6e, 0x74, 0x65, 0x6e,
+  0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x4e, 0x75, 0x6d, 0x42, 0x69, 0x74, 0x73, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x52, 0x69, 0x67, 0x68, 0x74, 0x49, 0x6e, 0x74,
+  0x65, 0x6e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x33, 0x34, 0x2c, 0x0d, 0x0a,
+  0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x4c, 0x65, 0x66, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x69, 0x74, 0x4f,
+  0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x33, 0x37, 0x2c, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x2f, 0x2f, 0x20, 0x42, 0x61, 0x73, 0x65, 0x2b, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x20, 0x65, 0x6e,
+  0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x28, 0x35, 0x20, 0x62, 0x69, 0x74, 0x20, 0x62, 0x61, 0x73, 0x65, 0x73, 0x2c, 0x20, 0x33, 0x20, 0x62, 0x69, 0x74, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61,
+  0x29, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x42, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x4e, 0x75, 0x6d, 0x42, 0x69, 0x74, 0x73, 0x20, 0x3d, 0x20,
+  0x35, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x42, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x4d, 0x61, 0x78, 0x20, 0x3d, 0x20, 0x31, 0x20, 0x3c,
+  0x3c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x42, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x4e, 0x75, 0x6d, 0x42, 0x69, 0x74, 0x73, 0x2c, 0x0d, 0x0a, 0x0d, 0x0a,
+  0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x4e, 0x75, 0x6d, 0x42, 0x69, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x33, 0x2c,
+  0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x20, 0x3d, 0x20, 0x31, 0x20, 0x3c, 0x3c, 0x20, 0x63, 0x45,
+  0x54, 0x43, 0x31, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x4e, 0x75, 0x6d, 0x42, 0x69, 0x74, 0x73, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43,
+  0x31, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x4d, 0x61, 0x78, 0x20, 0x3d, 0x20, 0x31, 0x20, 0x3c, 0x3c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x44,
+  0x65, 0x6c, 0x74, 0x61, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x4e, 0x75, 0x6d, 0x42, 0x69, 0x74, 0x73, 0x2c, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x42,
+  0x61, 0x73, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x52, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x35, 0x39, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43,
+  0x31, 0x42, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x47, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x35, 0x31, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x45,
+  0x54, 0x43, 0x31, 0x42, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x42, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x34, 0x33, 0x2c, 0x0d, 0x0a, 0x0d,
+  0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x33, 0x52, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x35,
+  0x36, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x33, 0x47, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20,
+  0x3d, 0x20, 0x34, 0x38, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x33, 0x42, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73,
+  0x65, 0x74, 0x20, 0x3d, 0x20, 0x34, 0x30, 0x2c, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x2f, 0x2f, 0x20, 0x41, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x20, 0x28, 0x6e, 0x6f, 0x6e, 0x2d, 0x64, 0x65,
+  0x6c, 0x74, 0x61, 0x29, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x28, 0x74, 0x77, 0x6f, 0x20, 0x34, 0x2d, 0x62, 0x69, 0x74, 0x20, 0x70, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6d,
+  0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x20, 0x62, 0x61, 0x73, 0x65, 0x73, 0x29, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x43, 0x6f, 0x6d,
+  0x70, 0x4e, 0x75, 0x6d, 0x42, 0x69, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x34, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x43, 0x6f, 0x6d,
+  0x70, 0x4d, 0x61, 0x78, 0x20, 0x3d, 0x20, 0x31, 0x20, 0x3c, 0x3c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x4e, 0x75, 0x6d,
+  0x42, 0x69, 0x74, 0x73, 0x2c, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x52, 0x31, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66,
+  0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x36, 0x30, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x47, 0x31, 0x42, 0x69, 0x74, 0x4f,
+  0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x35, 0x32, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x42, 0x31, 0x42, 0x69,
+  0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x34, 0x34, 0x2c, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x34,
+  0x52, 0x32, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x35, 0x36, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f,
+  0x72, 0x34, 0x47, 0x32, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x34, 0x38, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f,
+  0x6c, 0x6f, 0x72, 0x34, 0x42, 0x32, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x34, 0x30, 0x2c, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x43,
+  0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x69, 0x6e, 0x20, 0x3d, 0x20, 0x2d, 0x34, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x45, 0x54, 0x43, 0x31, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x44,
+  0x65, 0x6c, 0x74, 0x61, 0x4d, 0x61, 0x78, 0x20, 0x3d, 0x20, 0x33, 0x2c, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x33, 0x3a, 0x0d, 0x0a, 0x09, 0x2f, 0x2f,
+  0x20, 0x30, 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x32, 0x20, 0x20, 0x20, 0x33, 0x20, 0x20, 0x20, 0x34, 0x20, 0x20, 0x20, 0x35, 0x20, 0x20, 0x20, 0x36, 0x20, 0x20, 0x20, 0x37, 0x0d, 0x0a,
+  0x09, 0x2f, 0x2f, 0x20, 0x30, 0x30, 0x30, 0x20, 0x30, 0x30, 0x31, 0x20, 0x30, 0x31, 0x30, 0x20, 0x30, 0x31, 0x31, 0x20, 0x31, 0x30, 0x30, 0x20, 0x31, 0x30, 0x31, 0x20, 0x31, 0x31, 0x30, 0x20,
+  0x31, 0x31, 0x31, 0x0d, 0x0a, 0x09, 0x2f, 0x2f, 0x20, 0x30, 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x32, 0x20, 0x20, 0x20, 0x33, 0x20, 0x20, 0x20, 0x2d, 0x34, 0x20, 0x20, 0x2d, 0x33, 0x20,
+  0x20, 0x2d, 0x32, 0x20, 0x20, 0x2d, 0x31, 0x0d, 0x0a, 0x7d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x42, 0x41, 0x53, 0x49, 0x53, 0x55, 0x5f, 0x45, 0x54,
+  0x43, 0x31, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x46, 0x49, 0x54, 0x5f, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x5f, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x53, 0x49, 0x5a, 0x45, 0x20,
+  0x28, 0x31, 0x36, 0x35, 0x29, 0x0d, 0x0a, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x7b, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74,
+  0x20, 0x6d, 0x5f, 0x76, 0x5b, 0x34, 0x5d, 0x3b, 0x20, 0x7d, 0x20, 0x67, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x74, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x74,
+  0x61, 0x62, 0x5b, 0x42, 0x41, 0x53, 0x49, 0x53, 0x55, 0x5f, 0x45, 0x54, 0x43, 0x31, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x46, 0x49, 0x54, 0x5f, 0x4f, 0x52, 0x44, 0x45, 0x52,
+  0x5f, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x53, 0x49, 0x5a, 0x45, 0x5d, 0x20, 0x3d, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c,
+  0x20, 0x38, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c,
+  0x20, 0x36, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c,
+  0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20,
+  0x38, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x35, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20,
+  0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x34, 0x20, 0x7d, 0x20,
+  0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x36, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x30,
+  0x2c, 0x20, 0x37, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x37, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20,
+  0x7b, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x33, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x20,
+  0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c,
+  0x20, 0x32, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c,
+  0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x33, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x31, 0x2c, 0x20,
+  0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x34, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20,
+  0x31, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20,
+  0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x31,
+  0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x35, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20,
+  0x7b, 0x20, 0x36, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x33, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x20,
+  0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x36, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x38, 0x2c,
+  0x20, 0x30, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x36, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09,
+  0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x31, 0x2c, 0x20,
+  0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x34, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20,
+  0x32, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x35, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d,
+  0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x35, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x35,
+  0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x32,
+  0x2c, 0x20, 0x35, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d,
+  0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x33, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x36, 0x2c, 0x20, 0x30, 0x2c,
+  0x20, 0x32, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b,
+  0x20, 0x32, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x32, 0x20, 0x7d,
+  0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20,
+  0x36, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x33, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b,
+  0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x30,
+  0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x34, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31,
+  0x2c, 0x20, 0x35, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d,
+  0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x32, 0x2c,
+  0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b,
+  0x20, 0x34, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x31, 0x20, 0x7d,
+  0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20,
+  0x33, 0x2c, 0x20, 0x34, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b,
+  0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x32,
+  0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x35, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x33,
+  0x2c, 0x20, 0x33, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x35, 0x20, 0x7d, 0x20, 0x7d,
+  0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x32, 0x2c,
+  0x20, 0x33, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x33, 0x2c,
+  0x20, 0x32, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x20, 0x7d,
+  0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x32, 0x2c, 0x20,
+  0x31, 0x2c, 0x20, 0x33, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x36, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20,
+  0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x33, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x30,
+  0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x37, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x33, 0x2c, 0x20, 0x31,
+  0x2c, 0x20, 0x34, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x33, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20,
+  0x7b, 0x20, 0x30, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x33, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x30, 0x2c,
+  0x20, 0x34, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x37, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c,
+  0x20, 0x30, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c,
+  0x7b, 0x20, 0x7b, 0x20, 0x33, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x32, 0x2c, 0x20,
+  0x34, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20,
+  0x34, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x33, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20,
+  0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x33,
+  0x2c, 0x20, 0x34, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x37, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20,
+  0x7b, 0x20, 0x33, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x32, 0x20,
+  0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x38, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x33, 0x2c,
+  0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x34, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x34, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c,
+  0x7b, 0x20, 0x7b, 0x20, 0x34, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x35, 0x2c, 0x20,
+  0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x34, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20,
+  0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x33, 0x20, 0x7d, 0x20,
+  0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x35, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x35, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30,
+  0x2c, 0x20, 0x33, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20,
+  0x7b, 0x20, 0x32, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x34, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x33, 0x20,
+  0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x34, 0x2c, 0x20, 0x32, 0x2c,
+  0x20, 0x32, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x34, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09,
+  0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x33, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x30, 0x2c, 0x20,
+  0x33, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x33, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x34, 0x2c, 0x20,
+  0x33, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x36, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d,
+  0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x33, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x32,
+  0x2c, 0x20, 0x34, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x34, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x35,
+  0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x33, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d,
+  0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x33, 0x2c, 0x20, 0x30, 0x2c,
+  0x20, 0x30, 0x2c, 0x20, 0x35, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x34, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b,
+  0x20, 0x35, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x32, 0x20, 0x7d,
+  0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x33, 0x2c, 0x20,
+  0x31, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x35, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b,
+  0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x30,
+  0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x34, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x34, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x32,
+  0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x35, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x35, 0x20, 0x7d, 0x20, 0x7d,
+  0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x34, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x34, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x2c,
+  0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b,
+  0x20, 0x33, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x34, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x35, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x20, 0x7d,
+  0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x33, 0x2c, 0x20, 0x32, 0x2c, 0x20,
+  0x31, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b,
+  0x20, 0x7b, 0x20, 0x36, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x34,
+  0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x33, 0x2c, 0x20, 0x32,
+  0x2c, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x35, 0x20, 0x7d, 0x20, 0x7d,
+  0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x30, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x33, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x2c,
+  0x20, 0x32, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x35, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c,
+  0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x36, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x34, 0x20, 0x7d,
+  0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x34, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20,
+  0x33, 0x2c, 0x20, 0x33, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x37, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20,
+  0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x37, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33,
+  0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x34, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x33, 0x2c, 0x20, 0x31,
+  0x2c, 0x20, 0x31, 0x2c, 0x20, 0x33, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x34, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20,
+  0x7b, 0x20, 0x32, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x35, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c,
+  0x20, 0x36, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x35, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x30, 0x2c,
+  0x20, 0x32, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x36, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x35, 0x20, 0x7d, 0x20, 0x7d, 0x2c,
+  0x7b, 0x20, 0x7b, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x36, 0x20, 0x7d, 0x20, 0x7d, 0x0d, 0x0a, 0x7d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61,
+  0x6e, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x67, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x5b, 0x63, 0x45, 0x54, 0x43, 0x31,
+  0x49, 0x6e, 0x74, 0x65, 0x6e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x5d, 0x5b, 0x63, 0x45, 0x54, 0x43, 0x31, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74,
+  0x6f, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x5d, 0x20, 0x3d, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x2d, 0x38, 0x2c, 0x20, 0x20, 0x2d, 0x32, 0x2c, 0x20, 0x20, 0x20, 0x32, 0x2c,
+  0x20, 0x20, 0x20, 0x38, 0x20, 0x7d, 0x2c, 0x20, 0x7b, 0x20, 0x2d, 0x31, 0x37, 0x2c, 0x20, 0x20, 0x2d, 0x35, 0x2c, 0x20, 0x20, 0x35, 0x2c, 0x20, 0x20, 0x31, 0x37, 0x20, 0x7d, 0x2c, 0x20, 0x7b,
+  0x20, 0x2d, 0x32, 0x39, 0x2c, 0x20, 0x20, 0x2d, 0x39, 0x2c, 0x20, 0x20, 0x20, 0x39, 0x2c, 0x20, 0x20, 0x32, 0x39, 0x20, 0x7d, 0x2c, 0x20, 0x7b, 0x20, 0x20, 0x2d, 0x34, 0x32, 0x2c, 0x20, 0x2d,
+  0x31, 0x33, 0x2c, 0x20, 0x31, 0x33, 0x2c, 0x20, 0x20, 0x34, 0x32, 0x20, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x2d, 0x36, 0x30, 0x2c, 0x20, 0x2d, 0x31, 0x38, 0x2c, 0x20, 0x31, 0x38, 0x2c,
+  0x20, 0x20, 0x36, 0x30, 0x20, 0x7d, 0x2c, 0x20, 0x7b, 0x20, 0x2d, 0x38, 0x30, 0x2c, 0x20, 0x2d, 0x32, 0x34, 0x2c, 0x20, 0x32, 0x34, 0x2c, 0x20, 0x20, 0x38, 0x30, 0x20, 0x7d, 0x2c, 0x20, 0x7b,
+  0x20, 0x2d, 0x31, 0x30, 0x36, 0x2c, 0x20, 0x2d, 0x33, 0x33, 0x2c, 0x20, 0x33, 0x33, 0x2c, 0x20, 0x31, 0x30, 0x36, 0x20, 0x7d, 0x2c, 0x20, 0x7b, 0x20, 0x2d, 0x31, 0x38, 0x33, 0x2c, 0x20, 0x2d,
+  0x34, 0x37, 0x2c, 0x20, 0x34, 0x37, 0x2c, 0x20, 0x31, 0x38, 0x33, 0x20, 0x7d, 0x0d, 0x0a, 0x7d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69,
+  0x6e, 0x74, 0x38, 0x5f, 0x74, 0x20, 0x67, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x5f, 0x74, 0x6f, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5b, 0x63,
+  0x45, 0x54, 0x43, 0x31, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x5d, 0x20, 0x3d, 0x20, 0x7b, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x31, 0x2c,
+  0x20, 0x30, 0x20, 0x7d, 0x3b, 0x0d, 0x0a, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x20, 0x67, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74,
+  0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x74, 0x6f, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x5b, 0x63, 0x45, 0x54, 0x43, 0x31, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x56, 0x61,
+  0x6c, 0x75, 0x65, 0x73, 0x5d, 0x20, 0x3d, 0x20, 0x7b, 0x20, 0x33, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x20, 0x7d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x33,
+  0x32, 0x5f, 0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x63, 0x6f, 0x6e, 0x73,
+  0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6f, 0x66, 0x73, 0x2c, 0x20, 0x75, 0x69,
+  0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x29, 0x20, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x28, 0x6f, 0x66, 0x73, 0x20, 0x2b, 0x20,
+  0x6e, 0x75, 0x6d, 0x29, 0x20, 0x3c, 0x3d, 0x20, 0x36, 0x34, 0x55, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x6e, 0x75, 0x6d, 0x20, 0x26, 0x26, 0x20, 0x28, 0x6e,
+  0x75, 0x6d, 0x20, 0x3c, 0x3d, 0x20, 0x38, 0x55, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x28, 0x6f, 0x66, 0x73, 0x20, 0x3e, 0x3e, 0x20, 0x33, 0x29, 0x20,
+  0x3d, 0x3d, 0x20, 0x28, 0x28, 0x6f, 0x66, 0x73, 0x20, 0x2b, 0x20, 0x6e, 0x75, 0x6d, 0x20, 0x2d, 0x20, 0x31, 0x29, 0x20, 0x3e, 0x3e, 0x20, 0x33, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f,
+  0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x6f, 0x66, 0x73, 0x20, 0x3d, 0x20, 0x37, 0x20, 0x2d, 0x20, 0x28, 0x6f, 0x66, 0x73,
+  0x20, 0x3e, 0x3e, 0x20, 0x33, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69,
+  0x74, 0x5f, 0x6f, 0x66, 0x73, 0x20, 0x3d, 0x20, 0x6f, 0x66, 0x73, 0x20, 0x26, 0x20, 0x37, 0x3b, 0x0d, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x70, 0x2d, 0x3e, 0x6d, 0x5f,
+  0x62, 0x79, 0x74, 0x65, 0x73, 0x5b, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x6f, 0x66, 0x73, 0x5d, 0x20, 0x3e, 0x3e, 0x20, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x5f, 0x6f, 0x66, 0x73, 0x29,
+  0x20, 0x26, 0x20, 0x28, 0x28, 0x31, 0x20, 0x3c, 0x3c, 0x20, 0x6e, 0x75, 0x6d, 0x29, 0x20, 0x2d, 0x20, 0x31, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20,
+  0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63,
+  0x6b, 0x20, 0x2a, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6f, 0x66, 0x73, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6e, 0x75, 0x6d,
+  0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x69, 0x74, 0x73, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x28, 0x6f, 0x66,
+  0x73, 0x20, 0x2b, 0x20, 0x6e, 0x75, 0x6d, 0x29, 0x20, 0x3c, 0x3d, 0x20, 0x36, 0x34, 0x55, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x6e, 0x75, 0x6d, 0x20, 0x26,
+  0x26, 0x20, 0x28, 0x6e, 0x75, 0x6d, 0x20, 0x3c, 0x20, 0x33, 0x32, 0x55, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x28, 0x6f, 0x66, 0x73, 0x20, 0x3e, 0x3e,
+  0x20, 0x33, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x28, 0x28, 0x6f, 0x66, 0x73, 0x20, 0x2b, 0x20, 0x6e, 0x75, 0x6d, 0x20, 0x2d, 0x20, 0x31, 0x29, 0x20, 0x3e, 0x3e, 0x20, 0x33, 0x29, 0x29, 0x3b, 0x0d,
+  0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x62, 0x69, 0x74, 0x73, 0x20, 0x3c, 0x20, 0x28, 0x31, 0x55, 0x20, 0x3c, 0x3c, 0x20, 0x6e, 0x75, 0x6d, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09,
+  0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x6f, 0x66, 0x73, 0x20, 0x3d, 0x20, 0x37, 0x20, 0x2d, 0x20, 0x28, 0x6f,
+  0x66, 0x73, 0x20, 0x3e, 0x3e, 0x20, 0x33, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x79, 0x74, 0x65, 0x5f,
+  0x62, 0x69, 0x74, 0x5f, 0x6f, 0x66, 0x73, 0x20, 0x3d, 0x20, 0x6f, 0x66, 0x73, 0x20, 0x26, 0x20, 0x37, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33,
+  0x32, 0x5f, 0x74, 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x20, 0x3d, 0x20, 0x28, 0x31, 0x20, 0x3c, 0x3c, 0x20, 0x6e, 0x75, 0x6d, 0x29, 0x20, 0x2d, 0x20, 0x31, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x2d, 0x3e,
+  0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5b, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x6f, 0x66, 0x73, 0x5d, 0x20, 0x26, 0x3d, 0x20, 0x7e, 0x28, 0x6d, 0x61, 0x73, 0x6b, 0x20, 0x3c, 0x3c, 0x20, 0x62,
+  0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x5f, 0x6f, 0x66, 0x73, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5b, 0x62, 0x79, 0x74, 0x65, 0x5f,
+  0x6f, 0x66, 0x73, 0x5d, 0x20, 0x7c, 0x3d, 0x20, 0x28, 0x62, 0x69, 0x74, 0x73, 0x20, 0x3c, 0x3c, 0x20, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x5f, 0x6f, 0x66, 0x73, 0x29, 0x3b, 0x0d,
+  0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x66, 0x6c, 0x69, 0x70, 0x5f, 0x62, 0x69,
+  0x74, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x29, 0x20, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75,
+  0x72, 0x6e, 0x20, 0x28, 0x70, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5b, 0x33, 0x5d, 0x20, 0x26, 0x20, 0x31, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d,
+  0x0a, 0x0d, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x66, 0x6c, 0x69, 0x70, 0x5f, 0x62, 0x69, 0x74, 0x28, 0x65,
+  0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x2c, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x66, 0x6c, 0x69, 0x70, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x70, 0x2d, 0x3e,
+  0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5b, 0x33, 0x5d, 0x20, 0x26, 0x3d, 0x20, 0x7e, 0x31, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5b, 0x33,
+  0x5d, 0x20, 0x7c, 0x3d, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x29, 0x28, 0x66, 0x6c, 0x69, 0x70, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x62, 0x6f, 0x6f, 0x6c,
+  0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x5f, 0x62, 0x69, 0x74, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x65, 0x74,
+  0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x29, 0x20, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x70, 0x2d, 0x3e, 0x6d, 0x5f, 0x62,
+  0x79, 0x74, 0x65, 0x73, 0x5b, 0x33, 0x5d, 0x20, 0x26, 0x20, 0x32, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x74,
+  0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x5f, 0x62, 0x69, 0x74, 0x28, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a,
+  0x70, 0x2c, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x64, 0x69, 0x66, 0x66, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x70, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5b, 0x33, 0x5d,
+  0x20, 0x26, 0x3d, 0x20, 0x7e, 0x32, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5b, 0x33, 0x5d, 0x20, 0x7c, 0x3d, 0x20, 0x28, 0x28, 0x75, 0x69, 0x6e,
+  0x74, 0x33, 0x32, 0x5f, 0x74, 0x29, 0x28, 0x64, 0x69, 0x66, 0x66, 0x29, 0x20, 0x3c, 0x3c, 0x20, 0x31, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74,
+  0x75, 0x72, 0x6e, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x28, 0x30,
+  0x2d, 0x37, 0x29, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64,
+  0x2e, 0x0d, 0x0a, 0x2f, 0x2f, 0x20, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x3d, 0x30, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x2f, 0x74, 0x6f, 0x70, 0x20, 0x28, 0x43, 0x57,
+  0x20, 0x31, 0x29, 0x2c, 0x20, 0x31, 0x3d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2f, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x20, 0x28, 0x43, 0x57, 0x20, 0x32, 0x29, 0x0d, 0x0a, 0x75, 0x69, 0x6e, 0x74,
+  0x33, 0x32, 0x5f, 0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x63,
+  0x6f, 0x6e, 0x73, 0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x73, 0x75, 0x62, 0x62,
+  0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x29, 0x20, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69,
+  0x64, 0x20, 0x3c, 0x20, 0x32, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6f, 0x66, 0x73, 0x20, 0x3d, 0x20, 0x73,
+  0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x20, 0x3f, 0x20, 0x32, 0x20, 0x3a, 0x20, 0x35, 0x3b, 0x0d, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x70, 0x2d,
+  0x3e, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5b, 0x33, 0x5d, 0x20, 0x3e, 0x3e, 0x20, 0x6f, 0x66, 0x73, 0x29, 0x20, 0x26, 0x20, 0x37, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x2f,
+  0x2f, 0x20, 0x53, 0x65, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20,
+  0x28, 0x30, 0x2d, 0x37, 0x29, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f,
+  0x69, 0x64, 0x20, 0x28, 0x30, 0x20, 0x6f, 0x72, 0x20, 0x31, 0x29, 0x0d, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f,
+  0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32,
+  0x5f, 0x74, 0x20, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x74, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a,
+  0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x20, 0x3c, 0x20, 0x32, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65,
+  0x72, 0x74, 0x28, 0x74, 0x20, 0x3c, 0x20, 0x38, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6f, 0x66, 0x73, 0x20,
+  0x3d, 0x20, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x20, 0x3f, 0x20, 0x32, 0x20, 0x3a, 0x20, 0x35, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x79,
+  0x74, 0x65, 0x73, 0x5b, 0x33, 0x5d, 0x20, 0x26, 0x3d, 0x20, 0x7e, 0x28, 0x37, 0x20, 0x3c, 0x3c, 0x20, 0x6f, 0x66, 0x73, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x79,
+  0x74, 0x65, 0x73, 0x5b, 0x33, 0x5d, 0x20, 0x7c, 0x3d, 0x20, 0x28, 0x74, 0x20, 0x3c, 0x3c, 0x20, 0x6f, 0x66, 0x73, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x76, 0x6f, 0x69, 0x64,
+  0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x5f, 0x65, 0x74, 0x63, 0x31,
+  0x73, 0x28, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x74, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a,
+  0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x70, 0x2c, 0x20, 0x30, 0x2c,
+  0x20, 0x74, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65,
+  0x28, 0x70, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x74, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c,
+  0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x61, 0x77, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62,
+  0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x78, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32,
+  0x5f, 0x74, 0x20, 0x79, 0x29, 0x20, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x28, 0x78, 0x20, 0x7c, 0x20, 0x79, 0x29, 0x20, 0x3c, 0x20, 0x34, 0x29, 0x3b,
+  0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x69, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20,
+  0x78, 0x20, 0x2a, 0x20, 0x34, 0x20, 0x2b, 0x20, 0x79, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x79, 0x74, 0x65,
+  0x5f, 0x62, 0x69, 0x74, 0x5f, 0x6f, 0x66, 0x73, 0x20, 0x3d, 0x20, 0x62, 0x69, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x26, 0x20, 0x37, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73,
+  0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x20, 0x2a, 0x70, 0x20, 0x3d, 0x20, 0x26, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5b,
+  0x37, 0x20, 0x2d, 0x20, 0x28, 0x62, 0x69, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3e, 0x3e, 0x20, 0x33, 0x29, 0x5d, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75,
+  0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6c, 0x73, 0x62, 0x20, 0x3d, 0x20, 0x28, 0x70, 0x5b, 0x30, 0x5d, 0x20, 0x3e, 0x3e, 0x20, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x5f,
+  0x6f, 0x66, 0x73, 0x29, 0x20, 0x26, 0x20, 0x31, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6d, 0x73, 0x62, 0x20, 0x3d,
+  0x20, 0x28, 0x70, 0x5b, 0x2d, 0x32, 0x5d, 0x20, 0x3e, 0x3e, 0x20, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x5f, 0x6f, 0x66, 0x73, 0x29, 0x20, 0x26, 0x20, 0x31, 0x3b, 0x0d, 0x0a, 0x09,
+  0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x76, 0x61, 0x6c, 0x20, 0x3d, 0x20, 0x6c, 0x73, 0x62, 0x20, 0x7c, 0x20, 0x28, 0x6d, 0x73, 0x62, 0x20,
+  0x3c, 0x3c, 0x20, 0x31, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x2f, 0x2f, 0x20,
+  0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, 0x66,
+  0x72, 0x6f, 0x6d, 0x20, 0x30, 0x2d, 0x33, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x69, 0x6e,
+  0x74, 0x6f, 0x20, 0x67, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x2e, 0x0d, 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f,
+  0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x65,
+  0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x78, 0x2c, 0x20, 0x75, 0x69,
+  0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x79, 0x29, 0x20, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x67, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x5f, 0x74, 0x6f,
+  0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5b, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x61,
+  0x77, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x78, 0x2c, 0x20, 0x79, 0x29, 0x5d, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d,
+  0x0a, 0x2f, 0x2f, 0x20, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x22, 0x76, 0x61, 0x6c, 0x22, 0x20, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x30,
+  0x2d, 0x33, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x67, 0x5f,
+  0x65, 0x74, 0x63, 0x31, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x2e, 0x0d, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f,
+  0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x28, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f, 0x63,
+  0x6b, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x78, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33,
+  0x32, 0x5f, 0x74, 0x20, 0x76, 0x61, 0x6c, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x28, 0x78, 0x20, 0x7c, 0x20, 0x79, 0x20, 0x7c, 0x20, 0x76, 0x61,
+  0x6c, 0x29, 0x20, 0x3c, 0x20, 0x34, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x69, 0x74, 0x5f, 0x69, 0x6e,
+  0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x78, 0x20, 0x2a, 0x20, 0x34, 0x20, 0x2b, 0x20, 0x79, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x20, 0x2a, 0x70, 0x20,
+  0x3d, 0x20, 0x26, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5b, 0x37, 0x20, 0x2d, 0x20, 0x28, 0x62, 0x69, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65,
+  0x78, 0x20, 0x3e, 0x3e, 0x20, 0x33, 0x29, 0x5d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x79, 0x74,
+  0x65, 0x5f, 0x62, 0x69, 0x74, 0x5f, 0x6f, 0x66, 0x73, 0x20, 0x3d, 0x20, 0x62, 0x69, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x26, 0x20, 0x37, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e,
+  0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x20, 0x3d, 0x20, 0x31, 0x20, 0x3c, 0x3c, 0x20, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74,
+  0x5f, 0x6f, 0x66, 0x73, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x65, 0x74, 0x63, 0x31, 0x5f, 0x76, 0x61,
+  0x6c, 0x20, 0x3d, 0x20, 0x67, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x74, 0x6f, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x5b, 0x76, 0x61, 0x6c,
+  0x5d, 0x3b, 0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6c, 0x73, 0x62, 0x20, 0x3d, 0x20, 0x65, 0x74, 0x63,
+  0x31, 0x5f, 0x76, 0x61, 0x6c, 0x20, 0x26, 0x20, 0x31, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6d, 0x73, 0x62, 0x20,
+  0x3d, 0x20, 0x65, 0x74, 0x63, 0x31, 0x5f, 0x76, 0x61, 0x6c, 0x20, 0x3e, 0x3e, 0x20, 0x31, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x70, 0x5b, 0x30, 0x5d, 0x20, 0x26, 0x3d, 0x20, 0x7e, 0x6d, 0x61,
+  0x73, 0x6b, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x5b, 0x30, 0x5d, 0x20, 0x7c, 0x3d, 0x20, 0x28, 0x6c, 0x73, 0x62, 0x20, 0x3c, 0x3c, 0x20, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x5f, 0x6f,
+  0x66, 0x73, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x70, 0x5b, 0x2d, 0x32, 0x5d, 0x20, 0x26, 0x3d, 0x20, 0x7e, 0x6d, 0x61, 0x73, 0x6b, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x5b, 0x2d, 0x32, 0x5d,
+  0x20, 0x7c, 0x3d, 0x20, 0x28, 0x6d, 0x73, 0x62, 0x20, 0x3c, 0x3c, 0x20, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x5f, 0x6f, 0x66, 0x73, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d,
+  0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x34, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28,
+  0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x69, 0x64, 0x78, 0x2c,
+  0x20, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x20, 0x63, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x64, 0x78, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a,
+  0x09, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
+  0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x52, 0x32, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x34, 0x2c, 0x20,
+  0x28, 0x63, 0x20, 0x3e, 0x3e, 0x20, 0x38, 0x29, 0x20, 0x26, 0x20, 0x31, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74,
+  0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f, 0x72,
+  0x34, 0x47, 0x32, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x28, 0x63, 0x20, 0x3e, 0x3e, 0x20, 0x34, 0x29, 0x20, 0x26, 0x20, 0x31, 0x35, 0x29, 0x3b,
+  0x0d, 0x0a, 0x09, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f,
+  0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x42, 0x32, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x34,
+  0x2c, 0x20, 0x63, 0x20, 0x26, 0x20, 0x31, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x65, 0x74, 0x63,
+  0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54,
+  0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x52, 0x31, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x28, 0x63, 0x20, 0x3e, 0x3e,
+  0x20, 0x38, 0x29, 0x20, 0x26, 0x20, 0x31, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65,
+  0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x47, 0x31, 0x42, 0x69,
+  0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x28, 0x63, 0x20, 0x3e, 0x3e, 0x20, 0x34, 0x29, 0x20, 0x26, 0x20, 0x31, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x65,
+  0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63,
+  0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x42, 0x31, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x63, 0x20, 0x26,
+  0x20, 0x31, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f,
+  0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x34, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f,
+  0x63, 0x6b, 0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x69, 0x64, 0x78, 0x29, 0x20, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09,
+  0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x72, 0x2c, 0x20, 0x67, 0x2c, 0x20, 0x62, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x64, 0x78, 0x29, 0x0d, 0x0a, 0x09, 0x7b,
+  0x0d, 0x0a, 0x09, 0x09, 0x72, 0x20, 0x3d, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28,
+  0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x52, 0x32, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65,
+  0x74, 0x2c, 0x20, 0x34, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x67, 0x20, 0x3d, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65,
+  0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x47, 0x32, 0x42, 0x69,
+  0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x34, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x62, 0x20, 0x3d, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65,
+  0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f,
+  0x72, 0x34, 0x42, 0x32, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x34, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0d, 0x0a, 0x09,
+  0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x72, 0x20, 0x3d, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73,
+  0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x52, 0x31, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73,
+  0x65, 0x74, 0x2c, 0x20, 0x34, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x67, 0x20, 0x3d, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x79, 0x74,
+  0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x47, 0x31, 0x42,
+  0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x34, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x62, 0x20, 0x3d, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67,
+  0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x41, 0x62, 0x73, 0x43, 0x6f, 0x6c,
+  0x6f, 0x72, 0x34, 0x42, 0x31, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x34, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
+  0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x29, 0x28, 0x62, 0x20, 0x7c, 0x20, 0x28, 0x67, 0x20, 0x3c, 0x3c, 0x20, 0x34, 0x55, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x72, 0x20, 0x3c,
+  0x3c, 0x20, 0x38, 0x55, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74,
+  0x5f, 0x62, 0x61, 0x73, 0x65, 0x35, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20,
+  0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x20, 0x63, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62,
+  0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x42, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x35,
+  0x52, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x28, 0x63, 0x20, 0x3e, 0x3e, 0x20, 0x31, 0x30, 0x29, 0x20, 0x26, 0x20, 0x33, 0x31, 0x29, 0x3b, 0x0d,
+  0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
+  0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x42, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x47, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x35, 0x2c, 0x20,
+  0x28, 0x63, 0x20, 0x3e, 0x3e, 0x20, 0x35, 0x29, 0x20, 0x26, 0x20, 0x33, 0x31, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f,
+  0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x42, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72,
+  0x35, 0x42, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x63, 0x20, 0x26, 0x20, 0x33, 0x31, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x75,
+  0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x35, 0x5f, 0x63, 0x6f, 0x6c, 0x6f,
+  0x72, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09,
+  0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x72, 0x20, 0x3d, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74,
+  0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x42, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6c, 0x6f,
+  0x72, 0x35, 0x52, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32,
+  0x5f, 0x74, 0x20, 0x67, 0x20, 0x3d, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70,
+  0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x42, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x47, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74,
+  0x2c, 0x20, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x20, 0x3d, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62,
+  0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31,
+  0x42, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x42, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75,
+  0x72, 0x6e, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x29, 0x28, 0x62, 0x20, 0x7c, 0x20, 0x28, 0x67, 0x20, 0x3c, 0x3c, 0x20, 0x35, 0x55, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x72,
+  0x20, 0x3c, 0x3c, 0x20, 0x31, 0x30, 0x55, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f,
+  0x73, 0x65, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x33, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f,
+  0x63, 0x6b, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x20, 0x63, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73,
+  0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x43,
+  0x6f, 0x6c, 0x6f, 0x72, 0x33, 0x52, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x28, 0x63, 0x20, 0x3e, 0x3e, 0x20, 0x36, 0x29, 0x20, 0x26, 0x20, 0x37,
+  0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c,
+  0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x33, 0x47, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c,
+  0x20, 0x33, 0x2c, 0x20, 0x28, 0x63, 0x20, 0x3e, 0x3e, 0x20, 0x33, 0x29, 0x20, 0x26, 0x20, 0x37, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73,
+  0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x43,
+  0x6f, 0x6c, 0x6f, 0x72, 0x33, 0x42, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x63, 0x20, 0x26, 0x20, 0x37, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a,
+  0x0d, 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x33, 0x5f,
+  0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x29, 0x20, 0x0d,
+  0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x72, 0x20, 0x3d, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63,
+  0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x44, 0x65, 0x6c,
+  0x74, 0x61, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x33, 0x52, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x33, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20,
+  0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x67, 0x20, 0x3d, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f,
+  0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x33, 0x47, 0x42, 0x69,
+  0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x33, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x20,
+  0x3d, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
+  0x2c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x33, 0x42, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x20, 0x33, 0x29,
+  0x3b, 0x0d, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x29, 0x28, 0x62, 0x20, 0x7c, 0x20, 0x28, 0x67, 0x20, 0x3c, 0x3c, 0x20,
+  0x33, 0x55, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x72, 0x20, 0x3c, 0x3c, 0x20, 0x36, 0x55, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x74, 0x63,
+  0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x75, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x33, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x70, 0x52, 0x2c, 0x20, 0x69, 0x6e,
+  0x74, 0x20, 0x2a, 0x70, 0x47, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x70, 0x42, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f,
+  0x64, 0x65, 0x6c, 0x74, 0x61, 0x33, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x72, 0x20, 0x3d, 0x20, 0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x64, 0x65, 0x6c,
+  0x74, 0x61, 0x33, 0x20, 0x3e, 0x3e, 0x20, 0x36, 0x29, 0x20, 0x26, 0x20, 0x37, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x67, 0x20, 0x3d, 0x20, 0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64,
+  0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x33, 0x20, 0x3e, 0x3e, 0x20, 0x33, 0x29, 0x20, 0x26, 0x20, 0x37, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x62, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x63,
+  0x6b, 0x65, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x33, 0x20, 0x26, 0x20, 0x37, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x72, 0x20, 0x3e, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x72, 0x20,
+  0x2d, 0x3d, 0x20, 0x38, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x67, 0x20, 0x3e, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x67, 0x20, 0x2d, 0x3d, 0x20, 0x38, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x66,
+  0x20, 0x28, 0x62, 0x20, 0x3e, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x62, 0x20, 0x2d, 0x3d, 0x20, 0x38, 0x3b, 0x0d, 0x0a, 0x09, 0x2a, 0x70, 0x52, 0x20, 0x3d, 0x20, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x2a,
+  0x70, 0x47, 0x20, 0x3d, 0x20, 0x67, 0x3b, 0x0d, 0x0a, 0x09, 0x2a, 0x70, 0x42, 0x20, 0x3d, 0x20, 0x62, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x65, 0x74,
+  0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x75, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x33, 0x28, 0x63, 0x6f, 0x6c,
+  0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x2a, 0x70, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65,
+  0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x33,
+  0x2c, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x29, 0x0d, 0x0a,
+  0x7b, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x72, 0x2c, 0x20, 0x64, 0x67, 0x2c, 0x20, 0x64, 0x62, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f,
+  0x75, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x33, 0x28, 0x26, 0x64, 0x72, 0x2c, 0x20, 0x26, 0x64, 0x67, 0x2c, 0x20, 0x26, 0x64, 0x62, 0x2c, 0x20, 0x70, 0x61, 0x63,
+  0x6b, 0x65, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x33, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x62, 0x20, 0x3d, 0x20, 0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64,
+  0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x20, 0x26, 0x20, 0x33, 0x31, 0x55, 0x29, 0x20, 0x2b, 0x20, 0x64, 0x62, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x67, 0x20, 0x3d, 0x20, 0x28,
+  0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x20, 0x3e, 0x3e, 0x20, 0x35, 0x55, 0x29, 0x20, 0x26, 0x20, 0x33, 0x31, 0x55, 0x29, 0x20, 0x2b, 0x20, 0x64,
+  0x67, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x72, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x20, 0x3e, 0x3e, 0x20,
+  0x31, 0x30, 0x55, 0x29, 0x20, 0x26, 0x20, 0x33, 0x31, 0x55, 0x29, 0x20, 0x2b, 0x20, 0x64, 0x72, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65,
+  0x73, 0x73, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x29, 0x28, 0x72, 0x20, 0x7c, 0x20,
+  0x67, 0x20, 0x7c, 0x20, 0x62, 0x29, 0x20, 0x3e, 0x20, 0x33, 0x31, 0x55, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x3d, 0x20, 0x66,
+  0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x72, 0x20, 0x3d, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x72, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x31, 0x29, 0x3b, 0x0d, 0x0a, 0x09,
+  0x09, 0x67, 0x20, 0x3d, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x67, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x31, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x62, 0x20, 0x3d, 0x20, 0x63, 0x6c, 0x61,
+  0x6d, 0x70, 0x28, 0x62, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x33, 0x31, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64,
+  0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x62, 0x20, 0x3d, 0x20, 0x28, 0x62, 0x20, 0x3c, 0x3c, 0x20, 0x33, 0x55, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x62, 0x20, 0x3e, 0x3e, 0x20, 0x32,
+  0x55, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x67, 0x20, 0x3d, 0x20, 0x28, 0x67, 0x20, 0x3c, 0x3c, 0x20, 0x33, 0x55, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x67, 0x20, 0x3e, 0x3e, 0x20, 0x32, 0x55, 0x29,
+  0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x72, 0x20, 0x3d, 0x20, 0x28, 0x72, 0x20, 0x3c, 0x3c, 0x20, 0x33, 0x55, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x72, 0x20, 0x3e, 0x3e, 0x20, 0x32, 0x55, 0x29, 0x3b, 0x0d,
+  0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x2a, 0x70, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x29, 0x28,
+  0x72, 0x2c, 0x20, 0x67, 0x2c, 0x20, 0x62, 0x2c, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x55, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x72, 0x65,
+  0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x65,
+  0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x75, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x20,
+  0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x2c, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74,
+  0x33, 0x32, 0x5f, 0x74, 0x20, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x20, 0x3d, 0x20, 0x70, 0x61,
+  0x63, 0x6b, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x20, 0x26, 0x20, 0x33, 0x31, 0x55, 0x3b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x67, 0x20,
+  0x3d, 0x20, 0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x20, 0x3e, 0x3e, 0x20, 0x35, 0x55, 0x29, 0x20, 0x26, 0x20, 0x33, 0x31, 0x55, 0x3b, 0x0d, 0x0a,
+  0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x72, 0x20, 0x3d, 0x20, 0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x20, 0x3e, 0x3e, 0x20,
+  0x31, 0x30, 0x55, 0x29, 0x20, 0x26, 0x20, 0x33, 0x31, 0x55, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d,
+  0x0a, 0x09, 0x09, 0x62, 0x20, 0x3d, 0x20, 0x28, 0x62, 0x20, 0x3c, 0x3c, 0x20, 0x33, 0x55, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x62, 0x20, 0x3e, 0x3e, 0x20, 0x32, 0x55, 0x29, 0x3b, 0x0d, 0x0a, 0x09,
+  0x09, 0x67, 0x20, 0x3d, 0x20, 0x28, 0x67, 0x20, 0x3c, 0x3c, 0x20, 0x33, 0x55, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x67, 0x20, 0x3e, 0x3e, 0x20, 0x32, 0x55, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x72,
+  0x20, 0x3d, 0x20, 0x28, 0x72, 0x20, 0x3c, 0x3c, 0x20, 0x33, 0x55, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x72, 0x20, 0x3e, 0x3e, 0x20, 0x32, 0x55, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x0d,
+  0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x29, 0x28, 0x72, 0x2c, 0x20, 0x67, 0x2c, 0x20, 0x62, 0x2c, 0x20, 0x6d,
+  0x69, 0x6e, 0x28, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x55, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67,
+  0x62, 0x61, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x75, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x31,
+  0x36, 0x5f, 0x74, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x2c, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2c, 0x20,
+  0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x20,
+  0x3d, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x20, 0x26, 0x20, 0x31, 0x35, 0x55, 0x3b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f,
+  0x74, 0x20, 0x67, 0x20, 0x3d, 0x20, 0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x20, 0x3e, 0x3e, 0x20, 0x34, 0x55, 0x29, 0x20, 0x26, 0x20, 0x31, 0x35,
+  0x55, 0x3b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x72, 0x20, 0x3d, 0x20, 0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x34,
+  0x20, 0x3e, 0x3e, 0x20, 0x38, 0x55, 0x29, 0x20, 0x26, 0x20, 0x31, 0x35, 0x55, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x29, 0x0d, 0x0a,
+  0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x62, 0x20, 0x3d, 0x20, 0x28, 0x62, 0x20, 0x3c, 0x3c, 0x20, 0x34, 0x55, 0x29, 0x20, 0x7c, 0x20, 0x62, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x67, 0x20, 0x3d, 0x20,
+  0x28, 0x67, 0x20, 0x3c, 0x3c, 0x20, 0x34, 0x55, 0x29, 0x20, 0x7c, 0x20, 0x67, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x72, 0x20, 0x3d, 0x20, 0x28, 0x72, 0x20, 0x3c, 0x3c, 0x20, 0x34, 0x55, 0x29, 0x20,
+  0x7c, 0x20, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x29,
+  0x28, 0x72, 0x2c, 0x20, 0x67, 0x2c, 0x20, 0x62, 0x2c, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x55, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d,
+  0x0a, 0x0d, 0x0a, 0x2f, 0x2f, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x64, 0x69, 0x64, 0x6e, 0x27, 0x74, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x2c, 0x20, 0x74, 0x72, 0x75,
+  0x65, 0x20, 0x69, 0x66, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x65, 0x64, 0x0d, 0x0a, 0x62, 0x6f, 0x6f, 0x6c,
+  0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x28, 0x63, 0x6f, 0x6e, 0x73,
+  0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x2a,
+  0x20, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63,
+  0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x29, 0x20, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x62, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a,
+  0x09, 0x69, 0x66, 0x20, 0x28, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x5f, 0x62, 0x69, 0x74, 0x28, 0x70, 0x42, 0x6c, 0x6f,
+  0x63, 0x6b, 0x29, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x29, 0x0d,
+  0x0a, 0x09, 0x09, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x75, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x64, 0x65, 0x6c, 0x74,
+  0x61, 0x33, 0x28, 0x26, 0x62, 0x2c, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x35, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
+  0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x29, 0x2c, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x33, 0x5f, 0x63,
+  0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x29, 0x2c, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x65, 0x6c, 0x73,
+  0x65, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x62, 0x20, 0x3d, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x75, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
+  0x35, 0x28, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x35, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x70, 0x42, 0x6c, 0x6f,
+  0x63, 0x6b, 0x29, 0x2c, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0d, 0x0a, 0x09, 0x7b, 0x0d,
+  0x0a, 0x09, 0x09, 0x62, 0x20, 0x3d, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x75, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x28, 0x65,
+  0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x34, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c,
+  0x20, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x29, 0x2c, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x09,
+  0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65,
+  0x20, 0x3d, 0x20, 0x67, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x5b, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
+  0x5f, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63,
+  0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x29, 0x5d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x64, 0x63, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0d,
+  0x0a, 0x09, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61,
+  0x29, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x28, 0x62, 0x2e, 0x78, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62,
+  0x6c, 0x65, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x26, 0x64, 0x63, 0x29, 0x2c, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x28, 0x62, 0x2e, 0x79, 0x20, 0x2b,
+  0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x26, 0x64, 0x63, 0x29, 0x2c, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35,
+  0x5f, 0x66, 0x6c, 0x61, 0x67, 0x28, 0x62, 0x2e, 0x7a, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x26, 0x64, 0x63,
+  0x29, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x28, 0x63,
+  0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x29, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x28, 0x62, 0x2e, 0x78, 0x20, 0x2b, 0x20, 0x70,
+  0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x26, 0x64, 0x63, 0x29, 0x2c, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x5f, 0x66,
+  0x6c, 0x61, 0x67, 0x28, 0x62, 0x2e, 0x79, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x26, 0x64, 0x63, 0x29, 0x2c,
+  0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x28, 0x62, 0x2e, 0x7a, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c,
+  0x65, 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x26, 0x64, 0x63, 0x29, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
+  0x73, 0x5b, 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x29, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x5f, 0x66, 0x6c, 0x61,
+  0x67, 0x28, 0x62, 0x2e, 0x78, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x26, 0x64, 0x63, 0x29, 0x2c, 0x20, 0x63,
+  0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x28, 0x62, 0x2e, 0x79, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b,
+  0x32, 0x5d, 0x2c, 0x20, 0x26, 0x64, 0x63, 0x29, 0x2c, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x28, 0x62, 0x2e, 0x7a, 0x20, 0x2b, 0x20, 0x70, 0x49,
+  0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x26, 0x64, 0x63, 0x29, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x42, 0x6c,
+  0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x33, 0x5d, 0x20, 0x3d, 0x20, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x29, 0x28, 0x63, 0x6c, 0x61,
+  0x6d, 0x70, 0x32, 0x35, 0x35, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x28, 0x62, 0x2e, 0x78, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x33, 0x5d,
+  0x2c, 0x20, 0x26, 0x64, 0x63, 0x29, 0x2c, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x28, 0x62, 0x2e, 0x79, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74,
+  0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x26, 0x64, 0x63, 0x29, 0x2c, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x5f, 0x66, 0x6c, 0x61, 0x67,
+  0x28, 0x62, 0x2e, 0x7a, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x26, 0x64, 0x63, 0x29, 0x2c, 0x20, 0x32, 0x35,
+  0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x64, 0x63, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x67, 0x65, 0x74, 0x5f,
+  0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x35, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
+  0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x2a, 0x70, 0x42, 0x61, 0x73, 0x65,
+  0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2c, 0x20, 0x62,
+  0x6f, 0x6f, 0x6c, 0x20, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x20, 0x2f, 0x2a, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x20, 0x2a, 0x2f, 0x29, 0x20, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x63, 0x6f,
+  0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x62, 0x20, 0x3d, 0x20, 0x2a, 0x70, 0x42, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09,
+  0x69, 0x66, 0x20, 0x28, 0x21, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x62, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x28, 0x62, 0x2e, 0x78, 0x20, 0x3c,
+  0x3c, 0x20, 0x33, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x62, 0x2e, 0x78, 0x20, 0x3e, 0x3e, 0x20, 0x32, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x62, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x28, 0x62, 0x2e, 0x79,
+  0x20, 0x3c, 0x3c, 0x20, 0x33, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x62, 0x2e, 0x79, 0x20, 0x3e, 0x3e, 0x20, 0x32, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x62, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x28, 0x62,
+  0x2e, 0x7a, 0x20, 0x3c, 0x3c, 0x20, 0x33, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x62, 0x2e, 0x7a, 0x20, 0x3e, 0x3e, 0x20, 0x32, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63,
+  0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x67, 0x5f, 0x65, 0x74,
+  0x63, 0x31, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x5b, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5d, 0x3b, 0x0d, 0x0a, 0x0d,
+  0x0a, 0x09, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61,
+  0x29, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x28, 0x62, 0x2e, 0x78, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x30, 0x5d,
+  0x29, 0x2c, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x28, 0x62, 0x2e, 0x79, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x30,
+  0x5d, 0x29, 0x2c, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x28, 0x62, 0x2e, 0x7a, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b,
+  0x30, 0x5d, 0x29, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x31, 0x5d, 0x20, 0x3d, 0x20,
+  0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x29, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x28, 0x62, 0x2e, 0x78, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74,
+  0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x31, 0x5d, 0x29, 0x2c, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x28, 0x62, 0x2e, 0x79, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e,
+  0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x31, 0x5d, 0x29, 0x2c, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x28, 0x62, 0x2e, 0x7a, 0x20, 0x2b, 0x20, 0x70, 0x49,
+  0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x31, 0x5d, 0x29, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63,
+  0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x29, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35,
+  0x28, 0x62, 0x2e, 0x78, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x32, 0x5d, 0x29, 0x2c, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35,
+  0x35, 0x28, 0x62, 0x2e, 0x79, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x32, 0x5d, 0x29, 0x2c, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32,
+  0x35, 0x35, 0x28, 0x62, 0x2e, 0x7a, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x32, 0x5d, 0x29, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x29, 0x3b,
+  0x0d, 0x0a, 0x09, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x33, 0x5d, 0x20, 0x3d, 0x20, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62,
+  0x61, 0x29, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x28, 0x62, 0x2e, 0x78, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x33,
+  0x5d, 0x29, 0x2c, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x28, 0x62, 0x2e, 0x79, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b,
+  0x33, 0x5d, 0x29, 0x2c, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x28, 0x62, 0x2e, 0x7a, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65,
+  0x5b, 0x33, 0x5d, 0x29, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62,
+  0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x28, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f,
+  0x63, 0x6b, 0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x2a, 0x20, 0x70, 0x53,
+  0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x2c, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x2c, 0x20, 0x75,
+  0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2f, 0x2a, 0x3d, 0x20, 0x30, 0x2a, 0x2f, 0x2c, 0x20,
+  0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x5f, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2f, 0x2a, 0x3d, 0x20, 0x32, 0x2a, 0x2f, 0x29, 0x0d, 0x0a,
+  0x7b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x0d,
+  0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x3d, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e,
+  0x5f, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x20, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x3c, 0x20, 0x65, 0x6e, 0x64, 0x5f, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f,
+  0x63, 0x6b, 0x3b, 0x20, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2b, 0x2b, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62,
+  0x61, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x34, 0x5d, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f,
+  0x67, 0x65, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63,
+  0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x2c, 0x20, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x65, 0x74, 0x63, 0x5f, 0x62,
+  0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x66, 0x6c, 0x69, 0x70, 0x5f, 0x62, 0x69, 0x74, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x29, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7b, 0x0d,
+  0x0a, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x79, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x79, 0x20, 0x3c, 0x20, 0x32, 0x3b, 0x20,
+  0x79, 0x2b, 0x2b, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x78, 0x20,
+  0x3d, 0x20, 0x30, 0x3b, 0x20, 0x78, 0x20, 0x3c, 0x20, 0x34, 0x3b, 0x20, 0x78, 0x2b, 0x2b, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x75, 0x69,
+  0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09,
+  0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x5f, 0x4d, 0x41, 0x58,
+  0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x73, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x73,
+  0x20, 0x3c, 0x20, 0x34, 0x3b, 0x20, 0x73, 0x2b, 0x2b, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34,
+  0x5f, 0x74, 0x20, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x28, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75,
+  0x61, 0x6c, 0x2c, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x73, 0x5d, 0x2c, 0x20, 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x69, 0x78,
+  0x65, 0x6c, 0x73, 0x5b, 0x78, 0x20, 0x2b, 0x20, 0x28, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x20, 0x32, 0x20, 0x2b, 0x20, 0x79, 0x29, 0x20, 0x2a, 0x20, 0x34, 0x5d, 0x2c,
+  0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x65, 0x72, 0x72, 0x20, 0x3c, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65,
+  0x72, 0x72, 0x6f, 0x72, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f,
+  0x72, 0x20, 0x3d, 0x20, 0x65, 0x72, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x3d,
+  0x20, 0x73, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x65, 0x74, 0x63,
+  0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x78, 0x2c, 0x20, 0x73,
+  0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x20, 0x32, 0x20, 0x2b, 0x20, 0x79, 0x2c, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x29, 0x3b,
+  0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x2b, 0x3d, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72,
+  0x6f, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0d, 0x0a, 0x09,
+  0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x79, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x79, 0x20, 0x3c, 0x20,
+  0x34, 0x3b, 0x20, 0x79, 0x2b, 0x2b, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74,
+  0x20, 0x78, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x78, 0x20, 0x3c, 0x20, 0x32, 0x3b, 0x20, 0x78, 0x2b, 0x2b, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09,
+  0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x09, 0x09,
+  0x09, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x5f,
+  0x4d, 0x41, 0x58, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x73, 0x20, 0x3d, 0x20, 0x30,
+  0x3b, 0x20, 0x73, 0x20, 0x3c, 0x20, 0x34, 0x3b, 0x20, 0x73, 0x2b, 0x2b, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x75, 0x69, 0x6e,
+  0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x28, 0x70, 0x65, 0x72, 0x63, 0x65,
+  0x70, 0x74, 0x75, 0x61, 0x6c, 0x2c, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x73, 0x5d, 0x2c, 0x20, 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f,
+  0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x5b, 0x28, 0x73, 0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x20, 0x32, 0x29, 0x20, 0x2b, 0x20, 0x78, 0x20, 0x2b, 0x20, 0x79, 0x20, 0x2a, 0x20,
+  0x34, 0x5d, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x65, 0x72, 0x72, 0x20, 0x3c, 0x20, 0x62, 0x65, 0x73,
+  0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65,
+  0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x65, 0x72, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f,
+  0x72, 0x20, 0x3d, 0x20, 0x73, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09,
+  0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x73,
+  0x75, 0x62, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x20, 0x32, 0x20, 0x2b, 0x20, 0x78, 0x2c, 0x20, 0x79, 0x2c, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f,
+  0x72, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x2b, 0x3d, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f,
+  0x65, 0x72, 0x72, 0x6f, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a,
+  0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x31,
+  0x36, 0x5f, 0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x5f, 0x72, 0x67, 0x62, 0x28, 0x75, 0x69,
+  0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x72, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x67, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62,
+  0x2c, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x69, 0x61,
+  0x73, 0x20, 0x3d, 0x20, 0x31, 0x32, 0x37, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09,
+  0x72, 0x20, 0x3d, 0x20, 0x28, 0x72, 0x20, 0x2a, 0x20, 0x31, 0x35, 0x55, 0x20, 0x2b, 0x20, 0x62, 0x69, 0x61, 0x73, 0x29, 0x20, 0x2f, 0x20, 0x32, 0x35, 0x35, 0x55, 0x3b, 0x0d, 0x0a, 0x09, 0x09,
+  0x67, 0x20, 0x3d, 0x20, 0x28, 0x67, 0x20, 0x2a, 0x20, 0x31, 0x35, 0x55, 0x20, 0x2b, 0x20, 0x62, 0x69, 0x61, 0x73, 0x29, 0x20, 0x2f, 0x20, 0x32, 0x35, 0x35, 0x55, 0x3b, 0x0d, 0x0a, 0x09, 0x09,
+  0x62, 0x20, 0x3d, 0x20, 0x28, 0x62, 0x20, 0x2a, 0x20, 0x31, 0x35, 0x55, 0x20, 0x2b, 0x20, 0x62, 0x69, 0x61, 0x73, 0x29, 0x20, 0x2f, 0x20, 0x32, 0x35, 0x35, 0x55, 0x3b, 0x0d, 0x0a, 0x09, 0x7d,
+  0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x72, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x72, 0x2c, 0x20, 0x31, 0x35, 0x55, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x67, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28,
+  0x67, 0x2c, 0x20, 0x31, 0x35, 0x55, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x62, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x62, 0x2c, 0x20, 0x31, 0x35, 0x55, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09,
+  0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x29, 0x28, 0x62, 0x20, 0x7c, 0x20, 0x28, 0x67, 0x20, 0x3c, 0x3c, 0x20, 0x34, 0x55, 0x29, 0x20,
+  0x7c, 0x20, 0x28, 0x72, 0x20, 0x3c, 0x3c, 0x20, 0x38, 0x55, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x20, 0x65, 0x74, 0x63,
+  0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x63, 0x6f,
+  0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20,
+  0x62, 0x69, 0x61, 0x73, 0x20, 0x3d, 0x20, 0x31, 0x32, 0x37, 0x3b, 0x0d, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70,
+  0x61, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x5f, 0x72, 0x67, 0x62, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x78, 0x2c, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x79, 0x2c,
+  0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x7a, 0x2c, 0x20, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f,
+  0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x33, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x72, 0x2c, 0x20, 0x69,
+  0x6e, 0x74, 0x20, 0x67, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x62, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x28, 0x72, 0x20, 0x3e, 0x3d, 0x20, 0x63,
+  0x45, 0x54, 0x43, 0x31, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x69, 0x6e, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x72, 0x20, 0x3c, 0x3d, 0x20, 0x63, 0x45, 0x54, 0x43,
+  0x31, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x61, 0x78, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x28, 0x67, 0x20, 0x3e, 0x3d,
+  0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x69, 0x6e, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x67, 0x20, 0x3c, 0x3d, 0x20, 0x63, 0x45,
+  0x54, 0x43, 0x31, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x61, 0x78, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x28, 0x28, 0x62, 0x20,
+  0x3e, 0x3d, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x69, 0x6e, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x62, 0x20, 0x3c, 0x3d, 0x20,
+  0x63, 0x45, 0x54, 0x43, 0x31, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x61, 0x78, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x72, 0x20, 0x3c, 0x20,
+  0x30, 0x29, 0x20, 0x72, 0x20, 0x2b, 0x3d, 0x20, 0x38, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x67, 0x20, 0x3c, 0x20, 0x30, 0x29, 0x20, 0x67, 0x20, 0x2b, 0x3d, 0x20, 0x38, 0x3b, 0x0d,
+  0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x62, 0x20, 0x3c, 0x20, 0x30, 0x29, 0x20, 0x62, 0x20, 0x2b, 0x3d, 0x20, 0x38, 0x3b, 0x0d, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x75,
+  0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x29, 0x28, 0x62, 0x20, 0x7c, 0x20, 0x28, 0x67, 0x20, 0x3c, 0x3c, 0x20, 0x33, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x72, 0x20, 0x3c, 0x3c, 0x20, 0x36, 0x29,
+  0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63,
+  0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x28, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x6f, 0x6c, 0x6f,
+  0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x63, 0x30, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2c, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x63,
+  0x31, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x64,
+  0x69, 0x66, 0x66, 0x5f, 0x62, 0x69, 0x74, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f,
+  0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x34, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x30, 0x2c,
+  0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x28, 0x63, 0x30, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c,
+  0x65, 0x64, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62, 0x61, 0x73,
+  0x65, 0x34, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x61,
+  0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x34, 0x28, 0x63, 0x31, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x29, 0x3b, 0x0d,
+  0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x63, 0x6f,
+  0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x72, 0x67, 0x62, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x72, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x67, 0x2c,
+  0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x2c, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x75,
+  0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x69, 0x61, 0x73, 0x20, 0x3d, 0x20, 0x31, 0x32, 0x37, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x63, 0x61, 0x6c,
+  0x65, 0x64, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x72, 0x20, 0x3d, 0x20, 0x28, 0x72, 0x20, 0x2a, 0x20, 0x33, 0x31, 0x55, 0x20, 0x2b, 0x20, 0x62, 0x69, 0x61, 0x73, 0x29, 0x20,
+  0x2f, 0x20, 0x32, 0x35, 0x35, 0x55, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x67, 0x20, 0x3d, 0x20, 0x28, 0x67, 0x20, 0x2a, 0x20, 0x33, 0x31, 0x55, 0x20, 0x2b, 0x20, 0x62, 0x69, 0x61, 0x73, 0x29, 0x20,
+  0x2f, 0x20, 0x32, 0x35, 0x35, 0x55, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x62, 0x20, 0x3d, 0x20, 0x28, 0x62, 0x20, 0x2a, 0x20, 0x33, 0x31, 0x55, 0x20, 0x2b, 0x20, 0x62, 0x69, 0x61, 0x73, 0x29, 0x20,
+  0x2f, 0x20, 0x32, 0x35, 0x35, 0x55, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x72, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x72, 0x2c, 0x20, 0x33, 0x31, 0x55, 0x29, 0x3b,
+  0x0d, 0x0a, 0x09, 0x67, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x67, 0x2c, 0x20, 0x33, 0x31, 0x55, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x62, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x62, 0x2c,
+  0x20, 0x33, 0x31, 0x55, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x29, 0x28, 0x62, 0x20, 0x7c,
+  0x20, 0x28, 0x67, 0x20, 0x3c, 0x3c, 0x20, 0x35, 0x55, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x72, 0x20, 0x3c, 0x3c, 0x20, 0x31, 0x30, 0x55, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a,
+  0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x28, 0x63,
+  0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x63, 0x2c, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x72,
+  0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x72, 0x67, 0x62, 0x28,
+  0x63, 0x2e, 0x78, 0x2c, 0x20, 0x63, 0x2e, 0x79, 0x2c, 0x20, 0x63, 0x2e, 0x7a, 0x2c, 0x20, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x76, 0x6f,
+  0x69, 0x64, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x28, 0x65, 0x74,
+  0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x63, 0x30, 0x5f, 0x75,
+  0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2c, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x63, 0x31, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x29,
+  0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x5f, 0x62, 0x69, 0x74, 0x28, 0x70, 0x42, 0x6c,
+  0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62, 0x61,
+  0x73, 0x65, 0x35, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x61, 0x63, 0x6b,
+  0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x28, 0x63, 0x30, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x0d,
+  0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x72, 0x20, 0x3d, 0x20, 0x63, 0x31, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x63, 0x30, 0x5f, 0x75, 0x6e,
+  0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2e, 0x78, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x67, 0x20, 0x3d, 0x20, 0x63, 0x31, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64,
+  0x2e, 0x79, 0x20, 0x2d, 0x20, 0x63, 0x30, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2e, 0x79, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x62, 0x20, 0x3d, 0x20, 0x63,
+  0x31, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2e, 0x7a, 0x20, 0x2d, 0x20, 0x63, 0x30, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2e, 0x7a, 0x3b, 0x0d, 0x0a, 0x0d,
+  0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x33, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x70, 0x42, 0x6c,
+  0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x33, 0x28, 0x64, 0x72, 0x2c, 0x20, 0x64,
+  0x67, 0x2c, 0x20, 0x64, 0x62, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65,
+  0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x73, 0x28, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a,
+  0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x63, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x29, 0x0d, 0x0a,
+  0x7b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x5f, 0x62, 0x69, 0x74, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63,
+  0x6b, 0x2c, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62,
+  0x61, 0x73, 0x65, 0x35, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x61, 0x63,
+  0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x28, 0x63, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09,
+  0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x33, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63,
+  0x6b, 0x2c, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x33, 0x28, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30,
+  0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62, 0x6c, 0x6f,
+  0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x28, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f, 0x63,
+  0x6b, 0x2c, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x63, 0x30, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2c, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
+  0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x63, 0x31, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63,
+  0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x5f, 0x62, 0x69, 0x74, 0x28, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x0d,
+  0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x35, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x70, 0x42, 0x6c, 0x6f,
+  0x63, 0x6b, 0x2c, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x28, 0x63, 0x30, 0x5f, 0x75, 0x6e, 0x73,
+  0x63, 0x61, 0x6c, 0x65, 0x64, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x72, 0x20, 0x3d, 0x20, 0x63, 0x31, 0x5f,
+  0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x63, 0x30, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2e, 0x78, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x6e,
+  0x74, 0x20, 0x64, 0x67, 0x20, 0x3d, 0x20, 0x63, 0x31, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2e, 0x79, 0x20, 0x2d, 0x20, 0x63, 0x30, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c,
+  0x65, 0x64, 0x2e, 0x79, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x62, 0x20, 0x3d, 0x20, 0x63, 0x31, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2e, 0x7a, 0x20, 0x2d,
+  0x20, 0x63, 0x30, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2e, 0x7a, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x28, 0x28, 0x64, 0x72, 0x20, 0x3c, 0x20, 0x63,
+  0x45, 0x54, 0x43, 0x31, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x69, 0x6e, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x64, 0x72, 0x20, 0x3e, 0x20, 0x63, 0x45, 0x54, 0x43,
+  0x31, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x61, 0x78, 0x29, 0x29, 0x20, 0x7c, 0x7c, 0x0d, 0x0a, 0x09, 0x09, 0x28, 0x28, 0x64, 0x67, 0x20, 0x3c, 0x20, 0x63, 0x45,
+  0x54, 0x43, 0x31, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x69, 0x6e, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x64, 0x67, 0x20, 0x3e, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31,
+  0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x61, 0x78, 0x29, 0x29, 0x20, 0x7c, 0x7c, 0x0d, 0x0a, 0x09, 0x09, 0x28, 0x28, 0x64, 0x62, 0x20, 0x3c, 0x20, 0x63, 0x45, 0x54,
+  0x43, 0x31, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x69, 0x6e, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x64, 0x62, 0x20, 0x3e, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x43,
+  0x6f, 0x6c, 0x6f, 0x72, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x4d, 0x61, 0x78, 0x29, 0x29, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b,
+  0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x33, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28,
+  0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x33, 0x28, 0x64, 0x72,
+  0x2c, 0x20, 0x64, 0x67, 0x2c, 0x20, 0x64, 0x62, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d,
+  0x0a, 0x0d, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x72, 0x61, 0x77, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63,
+  0x74, 0x6f, 0x72, 0x73, 0x28, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69,
+  0x6e, 0x74, 0x38, 0x5f, 0x74, 0x20, 0x2a, 0x70, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74,
+  0x20, 0x77, 0x6f, 0x72, 0x64, 0x33, 0x20, 0x3d, 0x20, 0x30, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x32, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69,
+  0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x79, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x79, 0x20, 0x3c, 0x20, 0x34, 0x3b, 0x20, 0x79, 0x2b, 0x2b, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09,
+  0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x78, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x78, 0x20, 0x3c, 0x20, 0x34, 0x3b, 0x20, 0x78, 0x2b, 0x2b,
+  0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x69, 0x74, 0x5f, 0x69, 0x6e,
+  0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x78, 0x20, 0x2a, 0x20, 0x34, 0x20, 0x2b, 0x20, 0x79, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33,
+  0x32, 0x5f, 0x74, 0x20, 0x73, 0x20, 0x3d, 0x20, 0x70, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x5b, 0x78, 0x20, 0x2b, 0x20, 0x79, 0x20, 0x2a, 0x20, 0x34, 0x5d, 0x3b, 0x0d, 0x0a,
+  0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6c, 0x73, 0x62, 0x20, 0x3d, 0x20, 0x73, 0x20, 0x26, 0x20,
+  0x31, 0x2c, 0x20, 0x6d, 0x73, 0x62, 0x20, 0x3d, 0x20, 0x73, 0x20, 0x3e, 0x3e, 0x20, 0x31, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x77, 0x6f, 0x72, 0x64, 0x33, 0x20, 0x7c,
+  0x3d, 0x20, 0x28, 0x6c, 0x73, 0x62, 0x20, 0x3c, 0x3c, 0x20, 0x62, 0x69, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x77, 0x6f, 0x72, 0x64, 0x32, 0x20,
+  0x7c, 0x3d, 0x20, 0x28, 0x6d, 0x73, 0x62, 0x20, 0x3c, 0x3c, 0x20, 0x62, 0x69, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x7d, 0x0d,
+  0x0a, 0x0d, 0x0a, 0x09, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5b, 0x37, 0x5d, 0x20, 0x3d, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f,
+  0x74, 0x29, 0x28, 0x77, 0x6f, 0x72, 0x64, 0x33, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5b, 0x36, 0x5d, 0x20,
+  0x3d, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x29, 0x28, 0x77, 0x6f, 0x72, 0x64, 0x33, 0x20, 0x3e, 0x3e, 0x20, 0x38, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x42, 0x6c, 0x6f, 0x63,
+  0x6b, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5b, 0x35, 0x5d, 0x20, 0x3d, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x29, 0x28, 0x77, 0x6f, 0x72, 0x64, 0x32, 0x29,
+  0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5b, 0x34, 0x5d, 0x20, 0x3d, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f,
+  0x74, 0x29, 0x28, 0x77, 0x6f, 0x72, 0x64, 0x32, 0x20, 0x3e, 0x3e, 0x20, 0x38, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x20, 0x45, 0x43,
+  0x31, 0x53, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x2f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6d,
+  0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0d, 0x0a, 0x0d, 0x0a, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x20, 0x67, 0x5f, 0x65, 0x76,
+  0x61, 0x6c, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x5b, 0x38, 0x5d, 0x5b, 0x32, 0x35, 0x36, 0x5d, 0x20, 0x3d, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x2f, 0x2f,
+  0x20, 0x39, 0x39, 0x25, 0x20, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b,
+  0x20, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31,
+  0x2c, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x30, 0x2c,
+  0x31, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x30, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x7d, 0x2c, 0x0d, 0x0a, 0x09, 0x7b, 0x20, 0x31, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30,
+  0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x30,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31,
+  0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x7d, 0x2c, 0x0d,
+  0x0a, 0x09, 0x7b, 0x20, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c,
+  0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c, 0x31, 0x2c,
+  0x31, 0x2c, 0x31, 0x2c, 0x7d, 0x0d, 0x0a, 0x7d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x65, 0x74, 0x63, 0x31,
+  0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73,
+  0x5f, 0x74, 0x61, 0x67, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x6d, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f,
+  0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6d, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x3b,
+  0x0d, 0x0a, 0x7d, 0x20, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6f,
+  0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x63, 0x61, 0x6c,
+  0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c,
+  0x6f, 0x72, 0x29, 0x20, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x62, 0x72, 0x2c, 0x20, 0x62, 0x67, 0x2c, 0x20, 0x62, 0x62, 0x3b, 0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x09, 0x62,
+  0x72, 0x20, 0x3d, 0x20, 0x28, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x78, 0x20, 0x3e, 0x3e, 0x20, 0x32, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x75,
+  0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x78, 0x20, 0x3c, 0x3c, 0x20, 0x33, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x62, 0x67, 0x20, 0x3d, 0x20, 0x28, 0x75,
+  0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x79, 0x20, 0x3e, 0x3e, 0x20, 0x32, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65,
+  0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x79, 0x20, 0x3c, 0x3c, 0x20, 0x33, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x62, 0x62, 0x20, 0x3d, 0x20, 0x28, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65,
+  0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x7a, 0x20, 0x3e, 0x3e, 0x20, 0x32, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f,
+  0x72, 0x2e, 0x7a, 0x20, 0x3c, 0x3c, 0x20, 0x33, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67,
+  0x62, 0x61, 0x29, 0x28, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x29, 0x62, 0x72, 0x2c, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x29, 0x62, 0x67, 0x2c, 0x20, 0x28, 0x75,
+  0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x29, 0x62, 0x62, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x73,
+  0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f,
+  0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x61, 0x67, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x09, 0x09, 0x09, 0x09, 0x09, 0x6d,
+  0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69,
+  0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x20, 0x6d, 0x5f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x75,
+  0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x6d, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x5b, 0x31, 0x36, 0x5d, 0x3b, 0x0d, 0x0a, 0x09, 0x62,
+  0x6f, 0x6f, 0x6c, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x6d, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x3b, 0x0d, 0x0a, 0x7d, 0x20, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d,
+  0x69, 0x7a, 0x65, 0x72, 0x5f, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65,
+  0x64, 0x65, 0x66, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65,
+  0x5f, 0x74, 0x61, 0x67, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x6d, 0x5f, 0x62, 0x72, 0x2c, 0x20, 0x6d, 0x5f, 0x62, 0x67, 0x2c, 0x20, 0x6d, 0x5f, 0x62, 0x62, 0x3b, 0x0d,
+  0x0a, 0x09, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x6d, 0x5f, 0x61, 0x76, 0x67, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x6d, 0x5f, 0x6d, 0x61,
+  0x78, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x5f, 0x73, 0x70, 0x72, 0x65, 0x61, 0x64, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72,
+  0x5f, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x5f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74,
+  0x69, 0x6f, 0x6e, 0x3b, 0x0d, 0x0a, 0x7d, 0x20, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3b, 0x0d, 0x0a,
+  0x0d, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x5f,
+  0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74,
+  0x65, 0x20, 0x2a, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65,
+  0x5f, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x2a, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x0d, 0x0a, 0x09,
+  0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61,
+  0x6c, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x2a, 0x70, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x2c, 0x20, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20,
+  0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x2a, 0x70, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x2c, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63,
+  0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65,
+  0x73, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2c, 0x20, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x70, 0x6f, 0x74,
+  0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x20, 0x70, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e,
+  0x2c, 0x20, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73,
+  0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x20, 0x70, 0x42, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x75, 0x69,
+  0x6e, 0x74, 0x38, 0x5f, 0x74, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x5b, 0x31, 0x36, 0x5d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x70, 0x54,
+  0x72, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x3e, 0x6d, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0d,
+  0x0a, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6f,
+  0x72, 0x20, 0x3d, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x6d, 0x5f, 0x75, 0x6e,
+  0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x09, 0x70, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74,
+  0x69, 0x6f, 0x6e, 0x2d, 0x3e, 0x6d, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x5f, 0x4d, 0x41, 0x58, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x0d, 0x0a, 0x09,
+  0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69,
+  0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x3c, 0x20, 0x63, 0x45, 0x54, 0x43, 0x31, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x56,
+  0x61, 0x6c, 0x75, 0x65, 0x73, 0x3b, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2b, 0x2b, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x2f, 0x2f, 0x20,
+  0x54, 0x4f, 0x44, 0x4f, 0x3a, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x69, 0x73, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x20, 0x74,
+  0x6f, 0x20, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x20, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x43, 0x2b, 0x2b, 0x20, 0x76, 0x65, 0x72, 0x73,
+  0x69, 0x6f, 0x6e, 0x2e, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x21, 0x67, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x5b,
+  0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5d, 0x5b, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e, 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x5f,
+  0x73, 0x70, 0x72, 0x65, 0x61, 0x64, 0x5d, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x6f, 0x6e, 0x73,
+  0x74, 0x61, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x67, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x5f,
+  0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x5b, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09,
+  0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x34, 0x5d, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x66,
+  0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x73, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x73, 0x20, 0x3c, 0x20, 0x34, 0x3b, 0x20, 0x73, 0x2b, 0x2b, 0x29, 0x0d,
+  0x0a, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x79, 0x64, 0x20, 0x3d, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x73,
+  0x5d, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x73, 0x5d, 0x20, 0x3d, 0x20, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f,
+  0x72, 0x67, 0x62, 0x61, 0x29, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x28, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x78, 0x20, 0x2b, 0x20, 0x79, 0x64,
+  0x29, 0x2c, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x28, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x79, 0x20, 0x2b, 0x20, 0x79, 0x64, 0x29, 0x2c, 0x20,
+  0x63, 0x6c, 0x61, 0x6d, 0x70, 0x32, 0x35, 0x35, 0x28, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x7a, 0x20, 0x2b, 0x20, 0x79, 0x64, 0x29, 0x2c, 0x20, 0x32, 0x35, 0x35,
+  0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f,
+  0x72, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x63, 0x20,
+  0x3d, 0x20, 0x30, 0x3b, 0x20, 0x63, 0x20, 0x3c, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x3b, 0x20, 0x63, 0x2b, 0x2b, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7b, 0x0d, 0x0a,
+  0x09, 0x09, 0x09, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x73, 0x72, 0x63, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x20, 0x3d, 0x20, 0x70, 0x50, 0x69, 0x78, 0x65, 0x6c,
+  0x73, 0x5b, 0x63, 0x5d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74,
+  0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x33, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f,
+  0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x28, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2d, 0x3e,
+  0x6d, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x2c, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63,
+  0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74,
+  0x20, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x28, 0x70, 0x50,
+  0x61, 0x72, 0x61, 0x6d, 0x73, 0x2d, 0x3e, 0x6d, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x2c, 0x20,
+  0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20,
+  0x28, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3c, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7b,
+  0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x3b, 0x0d,
+  0x0a, 0x09, 0x09, 0x09, 0x09, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x32, 0x3b, 0x0d, 0x0a, 0x09,
+  0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x64, 0x69,
+  0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x28, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2d, 0x3e, 0x6d, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x2c, 0x20, 0x73, 0x72,
+  0x63, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x2c, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29,
+  0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3c, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72,
+  0x6f, 0x72, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x69, 0x61,
+  0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65,
+  0x78, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d,
+  0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x28, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2d, 0x3e, 0x6d, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65,
+  0x70, 0x74, 0x75, 0x61, 0x6c, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x2c, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x33,
+  0x5d, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3c,
+  0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72,
+  0x6f, 0x72, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65,
+  0x63, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28,
+  0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x20, 0x3c, 0x3d, 0x20, 0x31, 0x36, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x73, 0x65, 0x6c, 0x65,
+  0x63, 0x74, 0x6f, 0x72, 0x73, 0x5b, 0x63, 0x5d, 0x20, 0x3d, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x29, 0x28, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74,
+  0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x2b, 0x3d, 0x20,
+  0x70, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x3f, 0x20, 0x28, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x2a, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34,
+  0x5f, 0x74, 0x29, 0x70, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x5b, 0x63, 0x5d, 0x29, 0x20, 0x3a, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x3b, 0x0d, 0x0a, 0x09,
+  0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3e, 0x3d, 0x20, 0x70, 0x54, 0x72, 0x69, 0x61, 0x6c,
+  0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x3e, 0x6d, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0d,
+  0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3c, 0x20, 0x70, 0x54, 0x72, 0x69,
+  0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x3e, 0x6d, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x70,
+  0x54, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x3e, 0x6d, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f,
+  0x65, 0x72, 0x72, 0x6f, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x70, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x3e, 0x6d, 0x5f, 0x63, 0x6f,
+  0x6f, 0x72, 0x64, 0x73, 0x2e, 0x6d, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65,
+  0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x20, 0x3c, 0x3d, 0x20, 0x31, 0x36, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09,
+  0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20,
+  0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x70, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f,
+  0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x3e, 0x6d, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x73, 0x65,
+  0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x5b, 0x69, 0x5d, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x70, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x6c,
+  0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x3e, 0x6d, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x7d, 0x0d,
+  0x0a, 0x09, 0x70, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x3e, 0x6d, 0x5f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x6d, 0x5f, 0x75, 0x6e,
+  0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x6d, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64,
+  0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65,
+  0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x70, 0x42, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x69,
+  0x66, 0x20, 0x28, 0x70, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x3e, 0x6d, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3c, 0x20, 0x70, 0x42,
+  0x65, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x3e, 0x6d, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09,
+  0x2a, 0x70, 0x42, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x2a, 0x70, 0x54, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69,
+  0x6f, 0x6e, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x7d,
+  0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x76,
+  0x6f, 0x69, 0x64, 0x20, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x28, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x31,
+  0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x2a, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e,
+  0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x73, 0x74, 0x72,
+  0x75, 0x63, 0x74, 0x20, 0x2a, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x69, 0x78,
+  0x65, 0x6c, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x2a, 0x70, 0x50,
+  0x69, 0x78, 0x65, 0x6c, 0x73, 0x2c, 0x20, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20,
+  0x2a, 0x70, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x20,
+  0x3d, 0x20, 0x33, 0x31, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
+  0x20, 0x3d, 0x20, 0x32, 0x35, 0x35, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d,
+  0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x30, 0x3b,
+  0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x73, 0x75, 0x6d, 0x5f, 0x72, 0x20, 0x3d, 0x20, 0x30, 0x2c, 0x20, 0x73, 0x75, 0x6d, 0x5f, 0x67, 0x20, 0x3d, 0x20, 0x30,
+  0x2c, 0x20, 0x73, 0x75, 0x6d, 0x5f, 0x62, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34,
+  0x5f, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0d, 0x0a,
+  0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x63, 0x20, 0x3d, 0x20, 0x70, 0x50, 0x69, 0x78, 0x65,
+  0x6c, 0x73, 0x5b, 0x69, 0x5d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x6d, 0x69, 0x6e, 0x5f,
+  0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x6d, 0x61, 0x78, 0x28, 0x6d, 0x61,
+  0x78, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x70, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x29, 0x0d,
+  0x0a, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x70, 0x57, 0x65, 0x69, 0x67,
+  0x68, 0x74, 0x73, 0x5b, 0x69, 0x5d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x73, 0x75, 0x6d, 0x5f, 0x72, 0x20, 0x2b, 0x3d, 0x20, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x2a, 0x20,
+  0x63, 0x2e, 0x78, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x73, 0x75, 0x6d, 0x5f, 0x67, 0x20, 0x2b, 0x3d, 0x20, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x2a, 0x20, 0x63, 0x2e, 0x79, 0x3b, 0x0d,
+  0x0a, 0x09, 0x09, 0x09, 0x73, 0x75, 0x6d, 0x5f, 0x62, 0x20, 0x2b, 0x3d, 0x20, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x2a, 0x20, 0x63, 0x2e, 0x7a, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x0d, 0x0a,
+  0x09, 0x09, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x2b, 0x3d, 0x20, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d,
+  0x0a, 0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0d, 0x0a, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x73, 0x75, 0x6d, 0x5f, 0x72, 0x20, 0x2b, 0x3d, 0x20, 0x63, 0x2e, 0x78, 0x3b, 0x0d, 0x0a,
+  0x09, 0x09, 0x09, 0x73, 0x75, 0x6d, 0x5f, 0x67, 0x20, 0x2b, 0x3d, 0x20, 0x63, 0x2e, 0x79, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x73, 0x75, 0x6d, 0x5f, 0x62, 0x20, 0x2b, 0x3d, 0x20, 0x63, 0x2e,
+  0x7a, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2b, 0x2b, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09,
+  0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x61, 0x76, 0x67, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x61, 0x76,
+  0x67, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x29, 0x73, 0x75, 0x6d, 0x5f, 0x72, 0x20, 0x2f, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c,
+  0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3b, 0x0d, 0x0a, 0x09, 0x61, 0x76, 0x67, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x29,
+  0x73, 0x75, 0x6d, 0x5f, 0x67, 0x20, 0x2f, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3b, 0x0d, 0x0a, 0x09, 0x61, 0x76, 0x67, 0x5f, 0x63, 0x6f, 0x6c, 0x6f,
+  0x72, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x29, 0x73, 0x75, 0x6d, 0x5f, 0x62, 0x20, 0x2f, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68,
+  0x74, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e, 0x6d, 0x5f, 0x61, 0x76, 0x67, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x61, 0x76, 0x67,
+  0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e, 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x5f, 0x73, 0x70, 0x72,
+  0x65, 0x61, 0x64, 0x20, 0x3d, 0x20, 0x6d, 0x61, 0x78, 0x28, 0x6d, 0x61, 0x78, 0x28, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x78, 0x20, 0x2d,
+  0x20, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x78, 0x2c, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6c, 0x6f,
+  0x72, 0x2e, 0x79, 0x20, 0x2d, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x79, 0x29, 0x2c, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x6d, 0x61,
+  0x78, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x7a, 0x20, 0x2d, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x7a, 0x29, 0x3b, 0x0d, 0x0a,
+  0x09, 0x09, 0x0d, 0x0a, 0x09, 0x2f, 0x2f, 0x20, 0x54, 0x4f, 0x44, 0x4f, 0x3a, 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x65, 0x72, 0x65, 0x20,
+  0x63, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6d, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x64, 0x2c, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x44, 0x58, 0x54,
+  0x31, 0x2f, 0x42, 0x43, 0x31, 0x2e, 0x0d, 0x0a, 0x09, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x72, 0x20, 0x3d, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x69,
+  0x6e, 0x74, 0x29, 0x28, 0x61, 0x76, 0x67, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x78, 0x20, 0x2a, 0x20, 0x28, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x20, 0x2f, 0x20, 0x32, 0x35, 0x35, 0x2e, 0x30,
+  0x66, 0x29, 0x20, 0x2b, 0x20, 0x2e, 0x35, 0x66, 0x29, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e,
+  0x6d, 0x5f, 0x62, 0x67, 0x20, 0x3d, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x28, 0x61, 0x76, 0x67, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x79, 0x20, 0x2a,
+  0x20, 0x28, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x20, 0x2f, 0x20, 0x32, 0x35, 0x35, 0x2e, 0x30, 0x66, 0x29, 0x20, 0x2b, 0x20, 0x2e, 0x35, 0x66, 0x29, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x4c, 0x49, 0x4d,
+  0x49, 0x54, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x62, 0x20, 0x3d, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x69, 0x6e, 0x74,
+  0x29, 0x28, 0x61, 0x76, 0x67, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x7a, 0x20, 0x2a, 0x20, 0x28, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x20, 0x2f, 0x20, 0x32, 0x35, 0x35, 0x2e, 0x30, 0x66, 0x29,
+  0x20, 0x2b, 0x20, 0x2e, 0x35, 0x66, 0x29, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e,
+  0x6d, 0x5f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x6d, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b,
+  0x0d, 0x0a, 0x09, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x6d, 0x5f, 0x65, 0x72, 0x72,
+  0x6f, 0x72, 0x20, 0x3d, 0x20, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x5f, 0x4d, 0x41, 0x58, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x74, 0x63, 0x31,
+  0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x74,
+  0x28, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x74, 0x72, 0x79, 0x2c,
+  0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x2a, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65,
+  0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x70, 0x61,
+  0x72, 0x61, 0x6d, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x2a, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20,
+  0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72,
+  0x67, 0x62, 0x61, 0x20, 0x2a, 0x70, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e,
+  0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x2a, 0x70, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x20,
+  0x4c, 0x49, 0x4d, 0x49, 0x54, 0x20, 0x3d, 0x20, 0x33, 0x31, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f,
+  0x70, 0x6f, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f,
+  0x6e, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
+  0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x20, 0x63, 0x75, 0x72, 0x5f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x75, 0x72, 0x5f, 0x63, 0x6f,
+  0x6f, 0x72, 0x64, 0x73, 0x2e, 0x6d, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72,
+  0x67, 0x62, 0x61, 0x29, 0x28, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x72, 0x2c, 0x20, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x67, 0x2c,
+  0x20, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x62, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74,
+  0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2c,
+  0x20, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x2c, 0x20, 0x70, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x2c, 0x20, 0x70,
+  0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x2c, 0x20, 0x63, 0x75, 0x72, 0x5f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2c, 0x20, 0x26, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x6c, 0x75,
+  0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x26, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x3b,
+  0x0d, 0x0a, 0x09, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74,
+  0x69, 0x6f, 0x6e, 0x2e, 0x6d, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a,
+  0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f,
+  0x70, 0x65, 0x72, 0x6d, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x74, 0x72, 0x79, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x65,
+  0x6c, 0x74, 0x61, 0x5f, 0x73, 0x75, 0x6d, 0x5f, 0x72, 0x20, 0x3d, 0x20, 0x30, 0x2c, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x73, 0x75, 0x6d, 0x5f, 0x67, 0x20, 0x3d, 0x20, 0x30, 0x2c, 0x20,
+  0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x73, 0x75, 0x6d, 0x5f, 0x62, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x69,
+  0x6e, 0x74, 0x20, 0x2a, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x67, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f,
+  0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x5b, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x6d,
+  0x5f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x6d, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5d, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74,
+  0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x63, 0x61,
+  0x6c, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f,
+  0x6e, 0x2e, 0x6d, 0x5f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x6d, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x29, 0x3b, 0x0d, 0x0a, 0x0d,
+  0x0a, 0x09, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x20, 0x2a, 0x70, 0x4e, 0x75, 0x6d, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74,
+  0x6f, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x67, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x74, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x74, 0x61, 0x62, 0x5b, 0x69, 0x5d,
+  0x2e, 0x6d, 0x5f, 0x76, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x71, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20,
+  0x71, 0x20, 0x3c, 0x20, 0x34, 0x3b, 0x20, 0x71, 0x2b, 0x2b, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x79,
+  0x64, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x20, 0x3d, 0x20, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x71, 0x5d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09,
+  0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x73, 0x75, 0x6d, 0x5f, 0x72, 0x20, 0x2b, 0x3d, 0x20, 0x70, 0x4e, 0x75, 0x6d, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x5b, 0x71, 0x5d,
+  0x20, 0x2a, 0x20, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x78, 0x20, 0x2b, 0x20, 0x79, 0x64, 0x5f, 0x74, 0x65, 0x6d, 0x70,
+  0x2c, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x29, 0x20, 0x2d, 0x20, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x78, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x64,
+  0x65, 0x6c, 0x74, 0x61, 0x5f, 0x73, 0x75, 0x6d, 0x5f, 0x67, 0x20, 0x2b, 0x3d, 0x20, 0x70, 0x4e, 0x75, 0x6d, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x5b, 0x71, 0x5d, 0x20,
+  0x2a, 0x20, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x79, 0x20, 0x2b, 0x20, 0x79, 0x64, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x2c,
+  0x20, 0x30, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x29, 0x20, 0x2d, 0x20, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x79, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x64, 0x65,
+  0x6c, 0x74, 0x61, 0x5f, 0x73, 0x75, 0x6d, 0x5f, 0x62, 0x20, 0x2b, 0x3d, 0x20, 0x70, 0x4e, 0x75, 0x6d, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x5b, 0x71, 0x5d, 0x20, 0x2a,
+  0x20, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x7a, 0x20, 0x2b, 0x20, 0x79, 0x64, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x2c, 0x20,
+  0x30, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x29, 0x20, 0x2d, 0x20, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x7a, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x0d,
+  0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x28, 0x21, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x73, 0x75, 0x6d, 0x5f, 0x72, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x21, 0x64, 0x65, 0x6c, 0x74, 0x61,
+  0x5f, 0x73, 0x75, 0x6d, 0x5f, 0x67, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x21, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x73, 0x75, 0x6d, 0x5f, 0x62, 0x29, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x63,
+  0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x61, 0x76, 0x67, 0x5f, 0x64, 0x65,
+  0x6c, 0x74, 0x61, 0x5f, 0x72, 0x5f, 0x66, 0x20, 0x3d, 0x20, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x29, 0x28, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x73, 0x75, 0x6d, 0x5f, 0x72, 0x29, 0x20, 0x2f,
+  0x20, 0x38, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x61, 0x76, 0x67, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x67, 0x5f, 0x66,
+  0x20, 0x3d, 0x20, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x29, 0x28, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x73, 0x75, 0x6d, 0x5f, 0x67, 0x29, 0x20, 0x2f, 0x20, 0x38, 0x3b, 0x0d, 0x0a, 0x09, 0x09,
+  0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x61, 0x76, 0x67, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x62, 0x5f, 0x66, 0x20, 0x3d, 0x20, 0x28, 0x66, 0x6c, 0x6f,
+  0x61, 0x74, 0x29, 0x28, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x73, 0x75, 0x6d, 0x5f, 0x62, 0x29, 0x20, 0x2f, 0x20, 0x38, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74,
+  0x20, 0x69, 0x6e, 0x74, 0x20, 0x62, 0x72, 0x31, 0x20, 0x3d, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x28, 0x28, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e,
+  0x6d, 0x5f, 0x61, 0x76, 0x67, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x61, 0x76, 0x67, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x72, 0x5f, 0x66, 0x29, 0x20, 0x2a,
+  0x20, 0x28, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x20, 0x2f, 0x20, 0x32, 0x35, 0x35, 0x2e, 0x30, 0x66, 0x29, 0x20, 0x2b, 0x20, 0x2e, 0x35, 0x66, 0x29, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x4c, 0x49, 0x4d,
+  0x49, 0x54, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x62, 0x67, 0x31, 0x20, 0x3d, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x69,
+  0x6e, 0x74, 0x29, 0x28, 0x28, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e, 0x6d, 0x5f, 0x61, 0x76, 0x67, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x79, 0x20, 0x2d, 0x20, 0x61, 0x76, 0x67,
+  0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x67, 0x5f, 0x66, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x20, 0x2f, 0x20, 0x32, 0x35, 0x35, 0x2e, 0x30, 0x66, 0x29, 0x20, 0x2b,
+  0x20, 0x2e, 0x35, 0x66, 0x29, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x62,
+  0x62, 0x31, 0x20, 0x3d, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x28, 0x28, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e, 0x6d, 0x5f, 0x61, 0x76, 0x67, 0x5f,
+  0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x7a, 0x20, 0x2d, 0x20, 0x61, 0x76, 0x67, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x62, 0x5f, 0x66, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x4c, 0x49, 0x4d, 0x49,
+  0x54, 0x20, 0x2f, 0x20, 0x32, 0x35, 0x35, 0x2e, 0x30, 0x66, 0x29, 0x20, 0x2b, 0x20, 0x2e, 0x35, 0x66, 0x29, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x29, 0x3b, 0x0d, 0x0a,
+  0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x75, 0x72, 0x5f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x6d, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f,
+  0x72, 0x20, 0x3d, 0x20, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x29, 0x28, 0x62, 0x72, 0x31, 0x2c, 0x20, 0x62, 0x67, 0x31, 0x2c, 0x20, 0x62, 0x62, 0x31, 0x2c, 0x20,
+  0x32, 0x35, 0x35, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75,
+  0x61, 0x74, 0x65, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x20, 0x6e, 0x75,
+  0x6d, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x2c, 0x20, 0x70, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x2c, 0x20, 0x70, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x2c, 0x20, 0x63, 0x75, 0x72,
+  0x5f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2c, 0x20, 0x26, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x26, 0x70, 0x53, 0x74, 0x61, 0x74,
+  0x65, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x70,
+  0x53, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x3e, 0x6d, 0x5f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x6d, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d,
+  0x3d, 0x20, 0x30, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x2f, 0x2f, 0x20, 0x45, 0x6e, 0x63,
+  0x6f, 0x64, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x45, 0x54, 0x43, 0x31, 0x53, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x61, 0x20, 0x34, 0x78, 0x34, 0x20, 0x70,
+  0x69, 0x78, 0x65, 0x6c, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x0d, 0x0a, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x5f,
+  0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x28, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20,
+  0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x2a, 0x70, 0x50, 0x61, 0x72, 0x61,
+  0x6d, 0x73, 0x2c, 0x20, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x62, 0x6c, 0x6f,
+  0x63, 0x6b, 0x20, 0x2a, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x65,
+  0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x63,
+  0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x67, 0x65, 0x74, 0x5f,
+  0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x28, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20,
+  0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x3d, 0x20, 0x26, 0x70, 0x49, 0x6e,
+  0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63,
+  0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x31,
+  0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x28, 0x26, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73,
+  0x2c, 0x20, 0x31, 0x36, 0x2c, 0x20, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x3e, 0x6d, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x2c, 0x20, 0x4e, 0x55,
+  0x4c, 0x4c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f,
+  0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x74, 0x28, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2d, 0x3e, 0x6d, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x65, 0x72,
+  0x6d, 0x73, 0x2c, 0x20, 0x26, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x20, 0x31, 0x36, 0x2c, 0x20, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f,
+  0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x3e, 0x6d, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x2c, 0x20, 0x4e, 0x55, 0x4c, 0x4c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63,
+  0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x62, 0x6c, 0x6b, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x66, 0x6c, 0x69, 0x70,
+  0x5f, 0x62, 0x69, 0x74, 0x28, 0x26, 0x62, 0x6c, 0x6b, 0x2c, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65,
+  0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x73, 0x28, 0x26, 0x62, 0x6c, 0x6b, 0x2c, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65,
+  0x2e, 0x6d, 0x5f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x6d, 0x5f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x6d, 0x5f, 0x75, 0x6e, 0x73, 0x63,
+  0x61, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x74,
+  0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x73, 0x28, 0x26, 0x62, 0x6c, 0x6b, 0x2c, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x6d, 0x5f, 0x62, 0x65,
+  0x73, 0x74, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x6d, 0x5f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x6d, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62,
+  0x6c, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x72, 0x61, 0x77, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74,
+  0x6f, 0x72, 0x73, 0x28, 0x26, 0x62, 0x6c, 0x6b, 0x2c, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x6d, 0x5f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
+  0x6d, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x70, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74,
+  0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x6c, 0x6b, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a,
+  0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x20, 0x28,
+  0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x29, 0x29, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x74, 0x61, 0x67, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a,
+  0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x6d, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x3b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74,
+  0x36, 0x34, 0x5f, 0x74, 0x20, 0x6d, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3b, 0x0d, 0x0a, 0x7d, 0x20, 0x70, 0x69, 0x78,
+  0x65, 0x6c, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20,
+  0x6f, 0x70, 0x74, 0x69, 0x6d, 0x61, 0x6c, 0x20, 0x45, 0x54, 0x43, 0x31, 0x53, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x20, 0x67,
+  0x69, 0x76, 0x65, 0x6e, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, 0x20, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, 0x66,
+  0x20, 0x34, 0x78, 0x34, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2e, 0x0d, 0x0a, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c,
+  0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x63, 0x6c,
+  0x75, 0x73, 0x74, 0x65, 0x72, 0x28, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x5f,
+  0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x2a, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x20, 0x0d, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x20, 0x2a, 0x70,
+  0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c,
+  0x6f, 0x62, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x2a, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x2c, 0x0d,
+  0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x2a, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f,
+  0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a,
+  0x70, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33,
+  0x32, 0x5f, 0x74, 0x20, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x69,
+  0x64, 0x28, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x63, 0x6c,
+  0x75, 0x73, 0x74, 0x65, 0x72, 0x20, 0x2a, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x26, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f,
+  0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x5b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x3b, 0x0d, 0x0a,
+  0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x20, 0x3d, 0x20, 0x70, 0x49, 0x6e, 0x70, 0x75,
+  0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2d, 0x3e, 0x6d, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e,
+  0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x2a, 0x70, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x20, 0x3d, 0x20,
+  0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2d, 0x3e,
+  0x6d, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f,
+  0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x2a, 0x70, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f,
+  0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x2b, 0x20, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2d, 0x3e, 0x6d, 0x5f, 0x66, 0x69, 0x72, 0x73,
+  0x74, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a,
+  0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x31, 0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65,
+  0x72, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x28, 0x26, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x69,
+  0x78, 0x65, 0x6c, 0x73, 0x2c, 0x20, 0x70, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x2c, 0x20, 0x70, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x31,
+  0x73, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x74,
+  0x28, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2d, 0x3e, 0x6d, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x73, 0x2c, 0x20, 0x26, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2c,
+  0x20, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x2c, 0x20, 0x70, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x2c,
+  0x20, 0x70, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x62, 0x6c, 0x6b, 0x3b, 0x0d,
+  0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x66, 0x6c, 0x69, 0x70, 0x5f, 0x62, 0x69, 0x74, 0x28, 0x26, 0x62, 0x6c, 0x6b, 0x2c, 0x20, 0x74,
+  0x72, 0x75, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f,
+  0x72, 0x35, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x73, 0x28, 0x26, 0x62, 0x6c, 0x6b, 0x2c, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x6d, 0x5f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x6c, 0x75,
+  0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x6d, 0x5f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x6d, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x29, 0x3b,
+  0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x5f, 0x65, 0x74,
+  0x63, 0x31, 0x73, 0x28, 0x26, 0x62, 0x6c, 0x6b, 0x2c, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x6d, 0x5f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
+  0x6d, 0x5f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x6d, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+  0x09, 0x09, 0x0d, 0x0a, 0x09, 0x70, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65,
+  0x78, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x6c, 0x6b, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x20, 0x72, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x5f, 0x65,
+  0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20,
+  0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x20, 0x28, 0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x29, 0x29, 0x20,
+  0x72, 0x65, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36,
+  0x5f, 0x74, 0x20, 0x6d, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6f, 0x66, 0x73, 0x3b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36,
+  0x5f, 0x74, 0x20, 0x6d, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x3b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x20, 0x6d, 0x5f,
+  0x63, 0x75, 0x72, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x20, 0x6d, 0x5f, 0x63,
+  0x75, 0x72, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x65, 0x74, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x3b, 0x0d, 0x0a, 0x7d, 0x20, 0x72, 0x65, 0x63, 0x5f, 0x62, 0x6c, 0x6f,
+  0x63, 0x6b, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x5f, 0x5f, 0x61,
+  0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x20, 0x28, 0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x29, 0x29, 0x20, 0x72, 0x65, 0x63, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69,
+  0x6e, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
+  0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x6d, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f,
+  0x74, 0x20, 0x6d, 0x5f, 0x65, 0x74, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x3b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x74, 0x20, 0x6d, 0x5f, 0x63, 0x6c, 0x75, 0x73,
+  0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3b, 0x0d, 0x0a, 0x7d, 0x20, 0x72, 0x65, 0x63, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74,
+  0x65, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x5f, 0x5f, 0x61,
+  0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x20, 0x28, 0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x29, 0x29, 0x20, 0x72, 0x65, 0x63, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f,
+  0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6d, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c,
+  0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x6d, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x3b, 0x0d, 0x0a, 0x7d, 0x20,
+  0x72, 0x65, 0x63, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x2f, 0x2f, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x65, 0x61, 0x63, 0x68,
+  0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x20, 0x66, 0x69, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x65, 0x73, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x70,
+  0x6f, 0x69, 0x6e, 0x74, 0x20, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x69, 0x74, 0x2e, 0x0d, 0x0a, 0x6b,
+  0x65, 0x72, 0x6e, 0x65, 0x6c, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x72, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74,
+  0x65, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x72, 0x65, 0x63, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f,
+  0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x20, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61,
+  0x6c, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2c, 0x0d, 0x0a, 0x09,
+  0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x2a, 0x70,
+  0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
+  0x20, 0x72, 0x65, 0x63, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x2a, 0x70, 0x49,
+  0x6e, 0x70, 0x75, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69,
+  0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x2a, 0x70, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x0d, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x2a, 0x70, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x64,
+  0x69, 0x63, 0x65, 0x73, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64,
+  0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x28, 0x30, 0x29, 0x3b,
+  0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20,
+  0x70, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x5b, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x6c, 0x6f,
+  0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61,
+  0x6c, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x6d, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e,
+  0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c,
+  0x6f, 0x63, 0x6b, 0x20, 0x3d, 0x20, 0x26, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78,
+  0x5d, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x70, 0x72, 0x69, 0x76, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c,
+  0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0d, 0x0a, 0x09, 0x70, 0x72, 0x69, 0x76, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x3d, 0x20, 0x2a, 0x70, 0x49,
+  0x6e, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x66,
+  0x69, 0x72, 0x73, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6f, 0x66, 0x73, 0x20, 0x3d, 0x20, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f,
+  0x69, 0x6e, 0x66, 0x6f, 0x5b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x2e, 0x6d, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65,
+  0x72, 0x5f, 0x6f, 0x66, 0x73, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6c, 0x75, 0x73,
+  0x74, 0x65, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69,
+  0x6e, 0x64, 0x65, 0x78, 0x5d, 0x2e, 0x6d, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69,
+  0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x63, 0x75, 0x72, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d,
+  0x20, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x2e,
+  0x6d, 0x5f, 0x63, 0x75, 0x72, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e,
+  0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x63, 0x75, 0x72, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x65, 0x74, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65,
+  0x6e, 0x20, 0x3d, 0x20, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65,
+  0x78, 0x5d, 0x2e, 0x6d, 0x5f, 0x63, 0x75, 0x72, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x65, 0x74, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x3b, 0x0d, 0x0a, 0x09, 0x0d, 0x0a,
+  0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x6c, 0x6c, 0x5f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x20, 0x55, 0x49, 0x4e,
+  0x54, 0x36, 0x34, 0x5f, 0x4d, 0x41, 0x58, 0x3b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72,
+  0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x69, 0x20,
+  0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d,
+  0x0a, 0x09, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20,
+  0x3d, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6f, 0x66, 0x73, 0x20, 0x2b, 0x20, 0x69, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x6f, 0x6c, 0x6f,
+  0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x63,
+  0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x5b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x2e, 0x6d, 0x5f, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65,
+  0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x74, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x69, 0x6e,
+  0x74, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x5b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e,
+  0x64, 0x65, 0x78, 0x5d, 0x2e, 0x6d, 0x5f, 0x65, 0x74, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x31,
+  0x36, 0x5f, 0x74, 0x20, 0x6f, 0x72, 0x69, 0x67, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f,
+  0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x5b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x2e, 0x6d, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65,
+  0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x65, 0x74, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x20, 0x3e, 0x20, 0x63, 0x75,
+  0x72, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x65, 0x74, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x63,
+  0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63,
+  0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x34, 0x5d, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x35, 0x28, 0x62,
+  0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x2c, 0x20, 0x26, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x65, 0x74,
+  0x63, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x74,
+  0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69,
+  0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x63, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x63, 0x20, 0x3c, 0x20, 0x31, 0x36, 0x3b, 0x20, 0x63, 0x2b, 0x2b, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7b, 0x0d,
+  0x0a, 0x09, 0x09, 0x09, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x73, 0x72, 0x63, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x20, 0x3d, 0x20, 0x70, 0x72, 0x69, 0x76, 0x5f,
+  0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x6d, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x5b, 0x63, 0x5d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x75,
+  0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x61,
+  0x6e, 0x63, 0x65, 0x28, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x2c, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
+  0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32,
+  0x5f, 0x74, 0x20, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x28,
+  0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x2c, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c,
+  0x6f, 0x72, 0x73, 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72,
+  0x72, 0x6f, 0x72, 0x20, 0x3c, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f,
+  0x72, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72,
+  0x6f, 0x72, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x28, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x2c, 0x20,
+  0x73, 0x72, 0x63, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x2c, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73,
+  0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3c, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65,
+  0x72, 0x72, 0x6f, 0x72, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72,
+  0x72, 0x6f, 0x72, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x64,
+  0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x28, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x2c, 0x20, 0x62,
+  0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28,
+  0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3c, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x62,
+  0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+  0x09, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x2b, 0x3d, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x3b,
+  0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x20, 0x28, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3c, 0x20, 0x6f,
+  0x76, 0x65, 0x72, 0x61, 0x6c, 0x6c, 0x5f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x29, 0x20, 0x7c, 0x7c, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x28, 0x6f, 0x72,
+  0x69, 0x67, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x3d, 0x20, 0x63, 0x75, 0x72, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6c,
+  0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x3d, 0x3d, 0x20,
+  0x6f, 0x76, 0x65, 0x72, 0x61, 0x6c, 0x6c, 0x5f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x29, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09,
+  0x09, 0x09, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x6c, 0x6c, 0x5f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72,
+  0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x6f, 0x72, 0x69, 0x67, 0x5f,
+  0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x21, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x6c, 0x6c, 0x5f,
+  0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a,
+  0x0d, 0x0a, 0x09, 0x70, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x5b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x20,
+  0x3d, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x2f, 0x2f, 0x20, 0x2d,
+  0x2d, 0x2d, 0x2d, 0x20, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65,
+  0x72, 0x73, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x65, 0x61, 0x63, 0x68, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x73, 0x74, 0x72,
+  0x75, 0x63, 0x74, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x20, 0x28, 0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x29, 0x29, 0x20, 0x66, 0x6f, 0x73,
+  0x63, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33,
+  0x32, 0x5f, 0x74, 0x20, 0x6d, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x3b, 0x09, 0x2f, 0x2f, 0x20, 0x34, 0x78, 0x34, 0x20, 0x67,
+  0x72, 0x69, 0x64, 0x20, 0x6f, 0x66, 0x20, 0x32, 0x2d, 0x62, 0x69, 0x74, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x0d, 0x0a, 0x7d, 0x20, 0x66, 0x6f, 0x73, 0x63, 0x5f, 0x73,
+  0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63,
+  0x74, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x20, 0x28, 0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x29, 0x29, 0x20, 0x66, 0x6f, 0x73, 0x63, 0x5f,
+  0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61,
+  0x20, 0x6d, 0x5f, 0x65, 0x74, 0x63, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x3b, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x75, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65,
+  0x64, 0x20, 0x35, 0x2d, 0x62, 0x69, 0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x20, 0x52, 0x47, 0x42, 0x2c, 0x20, 0x61, 0x6c, 0x70, 0x68,
+  0x61, 0x20, 0x68, 0x61, 0x73, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x27, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x0d, 0x0a, 0x09,
+  0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6d, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x3b, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x6f,
+  0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74,
+  0x33, 0x32, 0x5f, 0x74, 0x20, 0x6d, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x3b, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
+  0x20, 0x6f, 0x66, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x0d, 0x0a, 0x7d, 0x20, 0x66, 0x6f, 0x73, 0x63, 0x5f, 0x62,
+  0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x5f,
+  0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x20, 0x28, 0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x29, 0x29, 0x20, 0x66, 0x6f, 0x73, 0x63, 0x5f, 0x70, 0x61, 0x72,
+  0x61, 0x6d, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6d, 0x5f, 0x74, 0x6f,
+  0x74, 0x61, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x6d, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x3b, 0x0d,
+  0x0a, 0x7d, 0x20, 0x66, 0x6f, 0x73, 0x63, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x2f, 0x2f, 0x20, 0x46, 0x6f, 0x72, 0x20,
+  0x65, 0x61, 0x63, 0x68, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x20, 0x46, 0x69, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x71, 0x75, 0x61, 0x6e, 0x74,
+  0x69, 0x7a, 0x65, 0x64, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74,
+  0x68, 0x65, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x73, 0x74, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x0d, 0x0a, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x66, 0x69,
+  0x6e, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x66, 0x6f, 0x72,
+  0x5f, 0x65, 0x61, 0x63, 0x68, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x28, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x66, 0x6f, 0x73, 0x63, 0x5f, 0x70, 0x61, 0x72,
+  0x61, 0x6d, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x20, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c,
+  0x6f, 0x62, 0x61, 0x6c, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2c,
+  0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x66, 0x6f, 0x73, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63,
+  0x74, 0x20, 0x2a, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c,
+  0x6f, 0x62, 0x61, 0x6c, 0x20, 0x66, 0x6f, 0x73, 0x63, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x2a, 0x70, 0x49, 0x6e, 0x70, 0x75,
+  0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74,
+  0x33, 0x32, 0x5f, 0x74, 0x20, 0x2a, 0x70, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x2c,
+  0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x2a, 0x70, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x73,
+  0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x63, 0x6f,
+  0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x67,
+  0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x28, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x63,
+  0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x20, 0x3d, 0x20, 0x70, 0x49, 0x6e, 0x70, 0x75,
+  0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x2e, 0x6d, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x3b, 0x0d,
+  0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x66, 0x6f, 0x73, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74,
+  0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x20, 0x3d, 0x20, 0x26, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e,
+  0x66, 0x6f, 0x5b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x3b, 0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62,
+  0x61, 0x6c, 0x20, 0x66, 0x6f, 0x73, 0x63, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x2a, 0x70, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74,
+  0x6f, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x26, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x5b, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69,
+  0x6e, 0x66, 0x6f, 0x2d, 0x3e, 0x6d, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5d, 0x3b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20,
+  0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x5f,
+  0x69, 0x6e, 0x66, 0x6f, 0x2d, 0x3e, 0x6d, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
+  0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x34, 0x5d, 0x3b, 0x0d, 0x0a, 0x09, 0x63,
+  0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x70, 0x42, 0x6c,
+  0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x2d, 0x3e, 0x6d, 0x5f, 0x65, 0x74, 0x63, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x3b, 0x0d, 0x0a, 0x09,
+  0x67, 0x65, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x35, 0x28, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f,
+  0x6c, 0x6f, 0x72, 0x73, 0x2c, 0x20, 0x26, 0x65, 0x74, 0x63, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x2c, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x63, 0x6f, 0x6c,
+  0x6f, 0x72, 0x35, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x2e, 0x77, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f,
+  0x74, 0x20, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x5b, 0x34, 0x5d, 0x5b, 0x31, 0x36, 0x5d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x70,
+  0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x6d, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28,
+  0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x73, 0x65, 0x6c, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x73, 0x65, 0x6c, 0x20, 0x3c, 0x20, 0x34, 0x3b, 0x20, 0x2b, 0x2b, 0x73, 0x65, 0x6c,
+  0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x31,
+  0x36, 0x3b, 0x20, 0x2b, 0x2b, 0x69, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x5b, 0x73, 0x65, 0x6c, 0x5d, 0x5b, 0x69,
+  0x5d, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x28, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x20, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x5f,
+  0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x73, 0x65,
+  0x6c, 0x5d, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x66, 0x6f,
+  0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x73, 0x65, 0x6c, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x73, 0x65, 0x6c, 0x20, 0x3c, 0x20, 0x34, 0x3b, 0x20, 0x2b, 0x2b,
+  0x73, 0x65, 0x6c, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20,
+  0x3c, 0x20, 0x31, 0x36, 0x3b, 0x20, 0x2b, 0x2b, 0x69, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x5b, 0x73, 0x65, 0x6c,
+  0x5d, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x28, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x20, 0x70, 0x42, 0x6c,
+  0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x74, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
+  0x73, 0x5b, 0x73, 0x65, 0x6c, 0x5d, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74,
+  0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x20, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x5f, 0x4d, 0x41, 0x58, 0x3b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32,
+  0x5f, 0x74, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74,
+  0x33, 0x32, 0x5f, 0x74, 0x20, 0x73, 0x65, 0x6c, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x73, 0x65, 0x6c, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3c, 0x20,
+  0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x3b, 0x20, 0x73, 0x65, 0x6c, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2b, 0x2b, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d,
+  0x0a, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x73, 0x65, 0x6c, 0x73, 0x20, 0x3d, 0x20, 0x70, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x5b, 0x73, 0x65,
+  0x6c, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x2e, 0x6d, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x3b, 0x0d, 0x0a, 0x09, 0x09,
+  0x0d, 0x0a, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x66,
+  0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x31, 0x36, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x2c,
+  0x20, 0x73, 0x65, 0x6c, 0x73, 0x20, 0x3e, 0x3e, 0x3d, 0x20, 0x32, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x20, 0x2b, 0x3d, 0x20, 0x74, 0x72,
+  0x69, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x5b, 0x73, 0x65, 0x6c, 0x73, 0x20, 0x26, 0x20, 0x33, 0x5d, 0x5b, 0x69, 0x5d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66,
+  0x20, 0x28, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x20, 0x3c, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09,
+  0x09, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x62, 0x65, 0x73, 0x74, 0x5f,
+  0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x21, 0x62, 0x65,
+  0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a,
+  0x09, 0x70, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65,
+  0x73, 0x5b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x20, 0x3d, 0x20, 0x70, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74,
+  0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x5b, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x2d, 0x3e, 0x6d, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f,
+  0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x2b, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x2f, 0x2f,
+  0x20, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20,
+  0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x20, 0x28, 0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x29, 0x29, 0x20,
+  0x64, 0x73, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f,
+  0x74, 0x20, 0x6d, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x6d, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70,
+  0x74, 0x75, 0x61, 0x6c, 0x3b, 0x0d, 0x0a, 0x7d, 0x20, 0x64, 0x73, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x2f, 0x2f, 0x20,
+  0x46, 0x6f, 0x72, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x20, 0x44, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x20,
+  0x74, 0x68, 0x65, 0x20, 0x45, 0x54, 0x43, 0x31, 0x53, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20,
+  0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x73, 0x74, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2c, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20,
+  0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x27, 0x73, 0x20, 0x70, 0x72, 0x65, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x45, 0x54, 0x43, 0x31, 0x53, 0x20, 0x63, 0x6f, 0x6c, 0x6f,
+  0x72, 0x35, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x20, 0x0d, 0x0a, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x65,
+  0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x28, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x73,
+  0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x20, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73,
+  0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f,
+  0x63, 0x6b, 0x73, 0x2c, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x2a,
+  0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x65, 0x74, 0x63, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x2c, 0x0d, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x2a, 0x70, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63,
+  0x6b, 0x73, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e,
+  0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x28, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6e,
+  0x73, 0x74, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x2a, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x69, 0x78,
+  0x65, 0x6c, 0x73, 0x20, 0x3d, 0x20, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d,
+  0x2e, 0x6d, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x63, 0x6f,
+  0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x70, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x65, 0x74, 0x63, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x61,
+  0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
+  0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x34, 0x5d, 0x3b, 0x0d, 0x0a, 0x09, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x6c, 0x6f,
+  0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x35, 0x28, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x2c, 0x20, 0x26, 0x65, 0x74, 0x63, 0x5f, 0x63, 0x6f,
+  0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x2c, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x2e, 0x77, 0x2c, 0x20,
+  0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f,
+  0x63, 0x6b, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x66, 0x6c, 0x69, 0x70, 0x5f, 0x62, 0x69, 0x74, 0x28, 0x26, 0x6f, 0x75,
+  0x74, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73,
+  0x65, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x73, 0x28, 0x26, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c,
+  0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c,
+  0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x5f, 0x65, 0x74, 0x63, 0x31, 0x73, 0x28, 0x26, 0x6f, 0x75, 0x74, 0x70,
+  0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x65, 0x74, 0x63, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x35, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x2e, 0x77, 0x29, 0x3b, 0x0d, 0x0a,
+  0x0d, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x31, 0x36, 0x3b, 0x20,
+  0x69, 0x2b, 0x2b, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x67, 0x62, 0x61, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x63, 0x6f, 0x6c,
+  0x6f, 0x72, 0x20, 0x3d, 0x20, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x5b, 0x69, 0x5d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74,
+  0x20, 0x65, 0x72, 0x72, 0x30, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x28, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x6d, 0x5f,
+  0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x2c, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63,
+  0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x65, 0x72, 0x72, 0x31, 0x20, 0x3d,
+  0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x28, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x6d, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74,
+  0x75, 0x61, 0x6c, 0x2c, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x31,
+  0x5d, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x65, 0x72, 0x72, 0x32, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f,
+  0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x28, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x6d, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x2c, 0x20, 0x70, 0x69,
+  0x78, 0x65, 0x6c, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73,
+  0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x65, 0x72, 0x72, 0x33, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63,
+  0x65, 0x28, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x6d, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61, 0x6c, 0x2c, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x5f, 0x63, 0x6f, 0x6c,
+  0x6f, 0x72, 0x2c, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a,
+  0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x6d, 0x69, 0x6e, 0x28, 0x6d, 0x69, 0x6e, 0x28, 0x65, 0x72,
+  0x72, 0x30, 0x2c, 0x20, 0x65, 0x72, 0x72, 0x31, 0x29, 0x2c, 0x20, 0x65, 0x72, 0x72, 0x32, 0x29, 0x2c, 0x20, 0x65, 0x72, 0x72, 0x33, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x75, 0x69,
+  0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x20, 0x3d, 0x20, 0x28, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x3d, 0x20, 0x65,
+  0x72, 0x72, 0x32, 0x29, 0x20, 0x3f, 0x20, 0x32, 0x20, 0x3a, 0x20, 0x33, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x20, 0x3d, 0x20, 0x28, 0x62, 0x65, 0x73,
+  0x74, 0x5f, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x3d, 0x20, 0x65, 0x72, 0x72, 0x31, 0x29, 0x20, 0x3f, 0x20, 0x31, 0x20, 0x3a, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x3b, 0x0d, 0x0a,
+  0x09, 0x09, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x20, 0x3d, 0x20, 0x28, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x3d, 0x20, 0x65, 0x72, 0x72, 0x30, 0x29, 0x20,
+  0x3f, 0x20, 0x30, 0x20, 0x3a, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x65, 0x74, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73,
+  0x65, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x28, 0x26, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x69, 0x20, 0x26, 0x20, 0x33,
+  0x2c, 0x20, 0x69, 0x20, 0x3e, 0x3e, 0x20, 0x32, 0x2c, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x70, 0x4f, 0x75,
+  0x74, 0x70, 0x75, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x20, 0x3d, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75,
+  0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a
+};
+unsigned int ocl_kernels_cl_len = 45935;
diff --git a/encoder/basisu_opencl.cpp b/encoder/basisu_opencl.cpp
new file mode 100644
index 0000000..81e3090
--- /dev/null
+++ b/encoder/basisu_opencl.cpp
@@ -0,0 +1,1332 @@
+// basisu_opencl.cpp
+// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "basisu_opencl.h"
+
+// If 1, the kernel source code will come from encoders/ocl_kernels.h. Otherwise, it will be read from the "ocl_kernels.cl" file in the current directory (for development).
+#define BASISU_USE_OCL_KERNELS_HEADER (1)
+#define BASISU_OCL_KERNELS_FILENAME "ocl_kernels.cl"
+
+#if BASISU_SUPPORT_OPENCL
+
+#include "basisu_enc.h"
+
+// We only use OpenCL v1.2 or less.
+#define CL_TARGET_OPENCL_VERSION 120
+
+#ifdef __APPLE__
+#include <OpenCL/opencl.h>
+#else
+#include <CL/cl.h>
+#endif
+
+#define BASISU_OPENCL_ASSERT_ON_ANY_ERRORS (1)
+
+namespace basisu
+{
+#if BASISU_USE_OCL_KERNELS_HEADER
+#include "basisu_ocl_kernels.h"
+#endif
+
+	static void ocl_error_printf(const char* pFmt, ...)
+	{
+		va_list args;
+		va_start(args, pFmt);
+		error_vprintf(pFmt, args);
+		va_end(args);
+
+#if BASISU_OPENCL_ASSERT_ON_ANY_ERRORS
+		assert(0);
+#endif
+	}
+
+	class ocl
+	{
+	public:
+		ocl() 
+		{
+			memset(&m_dev_fp_config, 0, sizeof(m_dev_fp_config));
+			
+			m_ocl_mutex.lock();
+			m_ocl_mutex.unlock();
+		}
+
+		~ocl()
+		{
+		}
+
+		bool is_initialized() const { return m_device_id != nullptr; }
+
+		cl_device_id get_device_id() const { return m_device_id; }
+		cl_context get_context() const { return m_context; }
+		cl_command_queue get_command_queue() { return m_command_queue; }
+		cl_program get_program() const { return m_program; }
+
+		bool init(bool force_serialization)
+		{
+			deinit();
+
+			interval_timer tm;
+			tm.start();
+
+			cl_uint num_platforms = 0;
+			cl_int ret = clGetPlatformIDs(0, NULL, &num_platforms);
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::init: clGetPlatformIDs() failed with %i\n", ret);
+				return false;
+			}
+
+			if ((!num_platforms) || (num_platforms > INT_MAX))
+			{
+				ocl_error_printf("ocl::init: clGetPlatformIDs() returned an invalid number of num_platforms\n");
+				return false;
+			}
+
+			std::vector<cl_platform_id> platforms(num_platforms);
+
+			ret = clGetPlatformIDs(num_platforms, platforms.data(), NULL);
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::init: clGetPlatformIDs() failed\n");
+				return false;
+			}
+
+			cl_uint num_devices = 0;
+			ret = clGetDeviceIDs(platforms[0], CL_DEVICE_TYPE_GPU, 1, &m_device_id, &num_devices);
+
+			if (ret == CL_DEVICE_NOT_FOUND)
+			{
+				ocl_error_printf("ocl::init: Couldn't get any GPU device ID's, trying CL_DEVICE_TYPE_CPU\n");
+
+				ret = clGetDeviceIDs(platforms[0], CL_DEVICE_TYPE_CPU, 1, &m_device_id, &num_devices);
+			}
+
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::init: Unable to get any device ID's\n");
+
+				m_device_id = nullptr;
+				return false;
+			}
+
+			ret = clGetDeviceInfo(m_device_id,
+				CL_DEVICE_SINGLE_FP_CONFIG,
+				sizeof(m_dev_fp_config),
+				&m_dev_fp_config,
+				nullptr);
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::init: clGetDeviceInfo() failed\n");
+				return false;
+			}
+
+			char plat_vers[256];
+			size_t rv = 0;
+			ret = clGetPlatformInfo(platforms[0], CL_PLATFORM_VERSION, sizeof(plat_vers), plat_vers, &rv);
+			if (ret == CL_SUCCESS)
+				printf("OpenCL platform version: \"%s\"\n", plat_vers);
+
+			// Serialize CL calls with the AMD driver to avoid lockups when multiple command queues per thread are used. This sucks, but what can we do?
+			m_use_mutex = (strstr(plat_vers, "AMD") != nullptr) || force_serialization;
+
+			printf("Serializing OpenCL calls across threads: %u\n", (uint32_t)m_use_mutex);
+
+			m_context = clCreateContext(nullptr, 1, &m_device_id, nullptr, nullptr, &ret);
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::init: clCreateContext() failed\n");
+
+				m_device_id = nullptr;
+				m_context = nullptr;
+				return false;
+			}
+
+			m_command_queue = clCreateCommandQueue(m_context, m_device_id, 0, &ret);
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::init: clCreateCommandQueue() failed\n");
+
+				deinit();
+				return false;
+			}
+						
+			printf("OpenCL init time: %3.3f secs\n", tm.get_elapsed_secs());
+
+			return true;
+		}
+				
+		bool deinit()
+		{
+			if (m_program)
+			{
+				clReleaseProgram(m_program);
+				m_program = nullptr;
+			}
+
+			if (m_command_queue)
+			{
+				clReleaseCommandQueue(m_command_queue);
+				m_command_queue = nullptr;
+			}
+
+			if (m_context)
+			{
+				clReleaseContext(m_context);
+				m_context = nullptr;
+			}
+
+			m_device_id = nullptr;
+
+			return true;
+		}
+
+		cl_command_queue create_command_queue()
+		{
+			cl_serializer serializer(this);
+
+			cl_int ret = 0;
+			cl_command_queue p = clCreateCommandQueue(m_context, m_device_id, 0, &ret);
+			if (ret != CL_SUCCESS)
+				return nullptr;
+
+			return p;
+		}
+
+		void destroy_command_queue(cl_command_queue p)
+		{
+			if (p)
+			{
+				cl_serializer serializer(this);
+
+				clReleaseCommandQueue(p);
+			}
+		}
+
+		bool init_program(const char* pSrc, size_t src_size)
+		{
+			cl_int ret;
+
+			if (m_program != nullptr)
+			{
+				clReleaseProgram(m_program);
+				m_program = nullptr;
+			}
+
+			m_program = clCreateProgramWithSource(m_context, 1, (const char**)&pSrc, (const size_t*)&src_size, &ret);
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::init_program: clCreateProgramWithSource() failed!\n");
+				return false;
+			}
+
+			std::string options;
+			if (m_dev_fp_config & CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT)
+			{
+				options += "-cl-fp32-correctly-rounded-divide-sqrt";
+			}
+
+			options += " -cl-std=CL1.2";
+			//options += " -cl-opt-disable";
+			//options += " -cl-mad-enable";
+			//options += " -cl-fast-relaxed-math";
+
+			ret = clBuildProgram(m_program, 1, &m_device_id,
+				options.size() ? options.c_str() : nullptr,  // options
+				nullptr,  // notify
+				nullptr); // user_data
+
+			if (ret != CL_SUCCESS)
+			{
+				const cl_int build_program_result = ret;
+
+				size_t ret_val_size;
+				ret = clGetProgramBuildInfo(m_program, m_device_id, CL_PROGRAM_BUILD_LOG, 0, NULL, &ret_val_size);
+				if (ret != CL_SUCCESS)
+				{
+					ocl_error_printf("ocl::init_program: clGetProgramBuildInfo() failed!\n");
+					return false;
+				}
+
+				std::vector<char> build_log(ret_val_size + 1);
+
+				ret = clGetProgramBuildInfo(m_program, m_device_id, CL_PROGRAM_BUILD_LOG, ret_val_size, build_log.data(), NULL);
+
+				ocl_error_printf("\nclBuildProgram() failed with error %i:\n%s", build_program_result, build_log.data());
+
+				return false;
+			}
+
+			return true;
+		}
+
+		cl_kernel create_kernel(const char* pName)
+		{
+			if (!m_program)
+				return nullptr;
+
+			cl_serializer serializer(this);
+
+			cl_int ret;
+			cl_kernel kernel = clCreateKernel(m_program, pName, &ret);
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::create_kernel: clCreateKernel() failed!\n");
+				return nullptr;
+			}
+
+			return kernel;
+		}
+
+		bool destroy_kernel(cl_kernel k)
+		{
+			if (k)
+			{
+				cl_serializer serializer(this);
+
+				cl_int ret = clReleaseKernel(k);
+				if (ret != CL_SUCCESS)
+				{
+					ocl_error_printf("ocl::destroy_kernel: clReleaseKernel() failed!\n");
+					return false;
+				}
+			}
+			return true;
+		}
+
+		cl_mem alloc_read_buffer(size_t size)
+		{
+			cl_serializer serializer(this);
+
+			cl_int ret;
+			cl_mem obj = clCreateBuffer(m_context, CL_MEM_READ_ONLY, size, NULL, &ret);
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::alloc_read_buffer: clCreateBuffer() failed!\n");
+				return nullptr;
+			}
+
+			return obj;
+		}
+
+		cl_mem alloc_and_init_read_buffer(cl_command_queue command_queue, const void *pInit, size_t size)
+		{
+			cl_serializer serializer(this);
+
+			cl_int ret;
+			cl_mem obj = clCreateBuffer(m_context, CL_MEM_READ_ONLY, size, NULL, &ret);
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::alloc_and_init_read_buffer: clCreateBuffer() failed!\n");
+				return nullptr;
+			}
+
+#if 0
+			if (!write_to_buffer(command_queue, obj, pInit, size))
+			{
+				destroy_buffer(obj);
+				return nullptr;
+			}
+#else
+			ret = clEnqueueWriteBuffer(command_queue, obj, CL_TRUE, 0, size, pInit, 0, NULL, NULL);
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::alloc_and_init_read_buffer: clEnqueueWriteBuffer() failed!\n");
+				return nullptr;
+			}
+#endif
+
+			return obj;
+		}
+
+		cl_mem alloc_write_buffer(size_t size)
+		{
+			cl_serializer serializer(this);
+
+			cl_int ret;
+			cl_mem obj = clCreateBuffer(m_context, CL_MEM_WRITE_ONLY, size, NULL, &ret);
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::alloc_write_buffer: clCreateBuffer() failed!\n");
+				return nullptr;
+			}
+
+			return obj;
+		}
+				
+		bool destroy_buffer(cl_mem buf)
+		{
+			if (buf)
+			{
+				cl_serializer serializer(this);
+
+				cl_int ret = clReleaseMemObject(buf);
+				if (ret != CL_SUCCESS)
+				{
+					ocl_error_printf("ocl::destroy_buffer: clReleaseMemObject() failed!\n");
+					return false;
+				}
+			}
+
+			return true;
+		}
+
+		bool write_to_buffer(cl_command_queue command_queue, cl_mem clmem, const void* d, const size_t m)
+		{
+			cl_serializer serializer(this);
+
+			cl_int ret = clEnqueueWriteBuffer(command_queue, clmem, CL_TRUE, 0, m, d, 0, NULL, NULL);
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::write_to_buffer: clEnqueueWriteBuffer() failed!\n");
+				return false;
+			}
+
+			return true;
+		}
+
+		bool read_from_buffer(cl_command_queue command_queue, const cl_mem clmem, void* d, size_t m)
+		{
+			cl_serializer serializer(this);
+
+			cl_int ret = clEnqueueReadBuffer(command_queue, clmem, CL_TRUE, 0, m, d, 0, NULL, NULL);
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::read_from_buffer: clEnqueueReadBuffer() failed!\n");
+				return false;
+			}
+
+			return true;
+		}
+
+		cl_mem create_read_image_u8(uint32_t width, uint32_t height, const void* pPixels, uint32_t bytes_per_pixel, bool normalized)
+		{
+			cl_image_format fmt = get_image_format(bytes_per_pixel, normalized);
+
+			cl_image_desc desc;
+			memset(&desc, 0, sizeof(desc));
+			desc.image_type = CL_MEM_OBJECT_IMAGE2D;
+			desc.image_width = width;
+			desc.image_height = height;
+			desc.image_row_pitch = width * bytes_per_pixel;
+
+			cl_serializer serializer(this);
+
+			cl_int ret;
+			cl_mem img = clCreateImage(m_context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, &fmt, &desc, (void*)pPixels, &ret);
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::create_read_image_u8: clCreateImage() failed!\n");
+				return nullptr;
+			}
+
+			return img;
+		}
+
+		cl_mem create_write_image_u8(uint32_t width, uint32_t height, uint32_t bytes_per_pixel, bool normalized)
+		{
+			cl_image_format fmt = get_image_format(bytes_per_pixel, normalized);
+
+			cl_image_desc desc;
+			memset(&desc, 0, sizeof(desc));
+			desc.image_type = CL_MEM_OBJECT_IMAGE2D;
+			desc.image_width = width;
+			desc.image_height = height;
+
+			cl_serializer serializer(this);
+
+			cl_int ret;
+			cl_mem img = clCreateImage(m_context, CL_MEM_WRITE_ONLY, &fmt, &desc, nullptr, &ret);
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::create_write_image_u8: clCreateImage() failed!\n");
+				return nullptr;
+			}
+
+			return img;
+		}
+
+		bool read_from_image(cl_command_queue command_queue, cl_mem img, void* pPixels, uint32_t ofs_x, uint32_t ofs_y, uint32_t width, uint32_t height)
+		{
+			cl_serializer serializer(this);
+
+			size_t origin[3] = { ofs_x, ofs_y, 0 }, region[3] = { width, height, 1 };
+
+			cl_int err = clEnqueueReadImage(command_queue, img, CL_TRUE, origin, region, 0, 0, pPixels, 0, NULL, NULL);
+			if (err != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::read_from_image: clEnqueueReadImage() failed!\n");
+				return false;
+			}
+
+			return true;
+		}
+
+		bool run_1D(cl_command_queue command_queue, const cl_kernel kernel, size_t num_items)
+		{
+			cl_serializer serializer(this);
+
+			cl_int ret = clEnqueueNDRangeKernel(command_queue, kernel,
+				1,  // work_dim
+				nullptr, // global_work_offset
+				&num_items, // global_work_size
+				nullptr, // local_work_size
+				0, // num_events_in_wait_list
+				nullptr, // event_wait_list
+				nullptr // event
+			);
+
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::run_1D: clEnqueueNDRangeKernel() failed!\n");
+				return false;
+			}
+
+			return true;
+		}
+
+		bool run_2D(cl_command_queue command_queue, const cl_kernel kernel, size_t width, size_t height)
+		{
+			cl_serializer serializer(this);
+
+			size_t num_global_items[2] = { width, height };
+			//size_t num_local_items[2] = { 1, 1 };
+
+			cl_int ret = clEnqueueNDRangeKernel(command_queue, kernel,
+				2,  // work_dim
+				nullptr, // global_work_offset
+				num_global_items, // global_work_size
+				nullptr, // local_work_size
+				0, // num_events_in_wait_list
+				nullptr, // event_wait_list
+				nullptr // event
+			);
+
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::run_2D: clEnqueueNDRangeKernel() failed!\n");
+				return false;
+			}
+
+			return true;
+		}
+
+		bool run_2D(cl_command_queue command_queue, const cl_kernel kernel, size_t ofs_x, size_t ofs_y, size_t width, size_t height)
+		{
+			cl_serializer serializer(this);
+
+			size_t global_ofs[2] = { ofs_x, ofs_y };
+			size_t num_global_items[2] = { width, height };
+			//size_t num_local_items[2] = { 1, 1 };
+
+			cl_int ret = clEnqueueNDRangeKernel(command_queue, kernel,
+				2,  // work_dim
+				global_ofs, // global_work_offset
+				num_global_items, // global_work_size
+				nullptr, // local_work_size
+				0, // num_events_in_wait_list
+				nullptr, // event_wait_list
+				nullptr // event
+			);
+
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::run_2D: clEnqueueNDRangeKernel() failed!\n");
+				return false;
+			}
+
+			return true;
+		}
+
+		void flush(cl_command_queue command_queue)
+		{
+			cl_serializer serializer(this);
+
+			clFlush(command_queue);
+			clFinish(command_queue);
+		}
+
+		template<typename T>
+		bool set_kernel_arg(cl_kernel kernel, uint32_t index, const T& obj)
+		{
+			cl_serializer serializer(this);
+
+			cl_int ret = clSetKernelArg(kernel, index, sizeof(T), (void*)&obj);
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::set_kernel_arg: clSetKernelArg() failed!\n");
+				return false;
+			}
+			return true;
+		}
+
+		template<typename T>
+		bool set_kernel_args(cl_kernel kernel, const T& obj1)
+		{
+			cl_serializer serializer(this);
+
+			cl_int ret = clSetKernelArg(kernel, 0, sizeof(T), (void*)&obj1);
+			if (ret != CL_SUCCESS)
+			{
+				ocl_error_printf("ocl::set_kernel_arg: clSetKernelArg() failed!\n");
+				return false;
+			}
+			return true;
+		}
+
+#define BASISU_CHECK_ERR if (ret != CL_SUCCESS)	{ ocl_error_printf("ocl::set_kernel_args: clSetKernelArg() failed!\n"); return false; }
+
+		template<typename T, typename U>
+		bool set_kernel_args(cl_kernel kernel, const T& obj1, const U& obj2)
+		{
+			cl_serializer serializer(this);
+			cl_int ret = clSetKernelArg(kernel, 0, sizeof(T), (void*)&obj1); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 1, sizeof(U), (void*)&obj2); BASISU_CHECK_ERR
+			return true;
+		}
+
+		template<typename T, typename U, typename V>
+		bool set_kernel_args(cl_kernel kernel, const T& obj1, const U& obj2, const V& obj3)
+		{
+			cl_serializer serializer(this);
+			cl_int ret = clSetKernelArg(kernel, 0, sizeof(T), (void*)&obj1); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 1, sizeof(U), (void*)&obj2); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 2, sizeof(V), (void*)&obj3); BASISU_CHECK_ERR
+			return true;
+		}
+
+		template<typename T, typename U, typename V, typename W>
+		bool set_kernel_args(cl_kernel kernel, const T& obj1, const U& obj2, const V& obj3, const W& obj4)
+		{
+			cl_serializer serializer(this);
+			cl_int ret = clSetKernelArg(kernel, 0, sizeof(T), (void*)&obj1); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 1, sizeof(U), (void*)&obj2); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 2, sizeof(V), (void*)&obj3); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 3, sizeof(W), (void*)&obj4); BASISU_CHECK_ERR
+			return true;
+		}
+
+		template<typename T, typename U, typename V, typename W, typename X>
+		bool set_kernel_args(cl_kernel kernel, const T& obj1, const U& obj2, const V& obj3, const W& obj4, const X& obj5)
+		{
+			cl_serializer serializer(this);
+			cl_int ret = clSetKernelArg(kernel, 0, sizeof(T), (void*)&obj1); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 1, sizeof(U), (void*)&obj2); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 2, sizeof(V), (void*)&obj3); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 3, sizeof(W), (void*)&obj4); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 4, sizeof(X), (void*)&obj5); BASISU_CHECK_ERR
+			return true;
+		}
+
+		template<typename T, typename U, typename V, typename W, typename X, typename Y>
+		bool set_kernel_args(cl_kernel kernel, const T& obj1, const U& obj2, const V& obj3, const W& obj4, const X& obj5, const Y& obj6)
+		{
+			cl_serializer serializer(this);
+			cl_int ret = clSetKernelArg(kernel, 0, sizeof(T), (void*)&obj1); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 1, sizeof(U), (void*)&obj2); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 2, sizeof(V), (void*)&obj3); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 3, sizeof(W), (void*)&obj4); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 4, sizeof(X), (void*)&obj5); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 5, sizeof(Y), (void*)&obj6); BASISU_CHECK_ERR
+			return true;
+		}
+
+		template<typename T, typename U, typename V, typename W, typename X, typename Y, typename Z>
+		bool set_kernel_args(cl_kernel kernel, const T& obj1, const U& obj2, const V& obj3, const W& obj4, const X& obj5, const Y& obj6, const Z& obj7)
+		{
+			cl_serializer serializer(this);
+			cl_int ret = clSetKernelArg(kernel, 0, sizeof(T), (void*)&obj1); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 1, sizeof(U), (void*)&obj2); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 2, sizeof(V), (void*)&obj3); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 3, sizeof(W), (void*)&obj4); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 4, sizeof(X), (void*)&obj5); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 5, sizeof(Y), (void*)&obj6); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 6, sizeof(Z), (void*)&obj7); BASISU_CHECK_ERR
+			return true;
+		}
+
+		template<typename T, typename U, typename V, typename W, typename X, typename Y, typename Z, typename A>
+		bool set_kernel_args(cl_kernel kernel, const T& obj1, const U& obj2, const V& obj3, const W& obj4, const X& obj5, const Y& obj6, const Z& obj7, const A& obj8)
+		{
+			cl_serializer serializer(this);
+			cl_int ret = clSetKernelArg(kernel, 0, sizeof(T), (void*)&obj1); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 1, sizeof(U), (void*)&obj2); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 2, sizeof(V), (void*)&obj3); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 3, sizeof(W), (void*)&obj4); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 4, sizeof(X), (void*)&obj5); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 5, sizeof(Y), (void*)&obj6); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 6, sizeof(Z), (void*)&obj7); BASISU_CHECK_ERR
+			ret = clSetKernelArg(kernel, 7, sizeof(A), (void*)&obj8); BASISU_CHECK_ERR
+			return true;
+		}
+#undef BASISU_CHECK_ERR
+
+	private:
+		cl_device_id m_device_id = nullptr;
+		cl_context m_context = nullptr;
+		cl_command_queue m_command_queue = nullptr;
+		cl_program m_program = nullptr;
+		cl_device_fp_config m_dev_fp_config;
+		
+		bool m_use_mutex = false;
+		std::mutex m_ocl_mutex;
+
+		// This helper object is used to optionally serialize all calls to the CL driver after initialization.
+		// Currently this is only used to work around race conditions in the Windows AMD driver.
+		struct cl_serializer
+		{
+			inline cl_serializer(const cl_serializer&);
+			cl_serializer& operator= (const cl_serializer&);
+
+			inline cl_serializer(ocl *p) : m_p(p)
+			{
+				if (m_p->m_use_mutex)
+					m_p->m_ocl_mutex.lock();
+			}
+
+			inline ~cl_serializer()
+			{
+				if (m_p->m_use_mutex)
+					m_p->m_ocl_mutex.unlock();
+			}
+
+		private:
+			ocl* m_p;
+		};
+		
+		cl_image_format get_image_format(uint32_t bytes_per_pixel, bool normalized)
+		{
+			cl_image_format fmt;
+			switch (bytes_per_pixel)
+			{
+			case 1: fmt.image_channel_order = CL_LUMINANCE; break;
+			case 2: fmt.image_channel_order = CL_RG; break;
+			case 3: fmt.image_channel_order = CL_RGB; break;
+			case 4: fmt.image_channel_order = CL_RGBA; break;
+			default: assert(0); fmt.image_channel_order = CL_LUMINANCE; break;
+			}
+
+			fmt.image_channel_data_type = normalized ? CL_UNORM_INT8 : CL_UNSIGNED_INT8;
+			return fmt;
+		}
+	};
+		
+	// Library blobal state
+	ocl g_ocl;
+			
+	bool opencl_init(bool force_serialization)
+	{
+		if (g_ocl.is_initialized())
+		{
+			assert(0);
+			return false;
+		}
+
+		if (!g_ocl.init(force_serialization))
+		{
+			ocl_error_printf("opencl_init: Failed initializing OpenCL\n");
+			return false;
+		}
+
+		const char* pKernel_src = nullptr;
+		size_t kernel_src_size = 0;
+		uint8_vec kernel_src;
+
+#if BASISU_USE_OCL_KERNELS_HEADER
+		pKernel_src = reinterpret_cast<const char*>(ocl_kernels_cl);
+		kernel_src_size = ocl_kernels_cl_len;
+#else
+		if (!read_file_to_vec(BASISU_OCL_KERNELS_FILENAME, kernel_src))
+		{
+			ocl_error_printf("opencl_init: Cannot read OpenCL kernel source file \"%s\"\n", BASISU_OCL_KERNELS_FILENAME);
+			g_ocl.deinit();
+			return false;
+		}
+			
+		pKernel_src = (char*)kernel_src.data();
+		kernel_src_size = kernel_src.size();
+#endif
+		
+		if (!kernel_src_size)
+		{
+			ocl_error_printf("opencl_init: Invalid OpenCL kernel source file \"%s\"\n", BASISU_OCL_KERNELS_FILENAME);
+			g_ocl.deinit();
+			return false;
+		}
+
+		if (!g_ocl.init_program(pKernel_src, kernel_src_size))
+		{
+			ocl_error_printf("opencl_init: Failed compiling OpenCL program\n");
+			g_ocl.deinit();
+			return false;
+		}
+								
+		printf("OpenCL support initialized successfully\n");
+
+		return true;
+	}
+
+	void opencl_deinit()
+	{
+		g_ocl.deinit();
+	}
+
+	bool opencl_is_available()
+	{
+		return g_ocl.is_initialized();
+	}
+
+	struct opencl_context
+	{
+		uint32_t m_ocl_total_pixel_blocks;
+		cl_mem m_ocl_pixel_blocks;
+
+		cl_command_queue m_command_queue;
+
+		cl_kernel m_ocl_encode_etc1s_blocks_kernel;
+		cl_kernel m_ocl_refine_endpoint_clusterization_kernel;
+		cl_kernel m_ocl_encode_etc1s_from_pixel_cluster_kernel;
+		cl_kernel m_ocl_find_optimal_selector_clusters_for_each_block_kernel;
+		cl_kernel m_ocl_determine_selectors_kernel;
+	};
+
+	opencl_context_ptr opencl_create_context()
+	{
+		if (!opencl_is_available())
+		{
+			ocl_error_printf("opencl_create_context: OpenCL not initialized\n");
+			assert(0);
+			return nullptr;
+		}
+
+		interval_timer tm;
+		tm.start();
+
+		opencl_context* pContext = static_cast<opencl_context * >(calloc(sizeof(opencl_context), 1));
+		if (!pContext)
+			return nullptr;
+				
+		// To avoid driver bugs in some drivers - serialize this. Likely not necessary, we don't know.
+		// https://community.intel.com/t5/OpenCL-for-CPU/Bug-report-clCreateKernelsInProgram-is-not-thread-safe/td-p/1159771
+		
+		pContext->m_command_queue = g_ocl.create_command_queue();
+		if (!pContext->m_command_queue)
+		{
+			ocl_error_printf("opencl_create_context: Failed creating OpenCL command queue!\n");
+			opencl_destroy_context(pContext);
+			return nullptr;
+		}
+
+		pContext->m_ocl_encode_etc1s_blocks_kernel = g_ocl.create_kernel("encode_etc1s_blocks");
+		if (!pContext->m_ocl_encode_etc1s_blocks_kernel)
+		{
+			ocl_error_printf("opencl_create_context: Failed creating OpenCL kernel encode_etc1s_block\n");
+			opencl_destroy_context(pContext);
+			return nullptr;
+		}
+
+		pContext->m_ocl_refine_endpoint_clusterization_kernel = g_ocl.create_kernel("refine_endpoint_clusterization");
+		if (!pContext->m_ocl_refine_endpoint_clusterization_kernel)
+		{
+			ocl_error_printf("opencl_create_context: Failed creating OpenCL kernel refine_endpoint_clusterization\n");
+			opencl_destroy_context(pContext);
+			return nullptr;
+		}
+
+		pContext->m_ocl_encode_etc1s_from_pixel_cluster_kernel = g_ocl.create_kernel("encode_etc1s_from_pixel_cluster");
+		if (!pContext->m_ocl_encode_etc1s_from_pixel_cluster_kernel)
+		{
+			ocl_error_printf("opencl_create_context: Failed creating OpenCL kernel encode_etc1s_from_pixel_cluster\n");
+			opencl_destroy_context(pContext);
+			return nullptr;
+		}
+
+		pContext->m_ocl_find_optimal_selector_clusters_for_each_block_kernel = g_ocl.create_kernel("find_optimal_selector_clusters_for_each_block");
+		if (!pContext->m_ocl_find_optimal_selector_clusters_for_each_block_kernel)
+		{
+			ocl_error_printf("opencl_create_context: Failed creating OpenCL kernel find_optimal_selector_clusters_for_each_block\n");
+			opencl_destroy_context(pContext);
+			return nullptr;
+		}
+
+		pContext->m_ocl_determine_selectors_kernel = g_ocl.create_kernel("determine_selectors");
+		if (!pContext->m_ocl_determine_selectors_kernel)
+		{
+			ocl_error_printf("opencl_create_context: Failed creating OpenCL kernel determine_selectors\n");
+			opencl_destroy_context(pContext);
+			return nullptr;
+		}
+
+		debug_printf("opencl_create_context: Elapsed time: %f secs\n", tm.get_elapsed_secs());
+
+		return pContext;
+	}
+
+	void opencl_destroy_context(opencl_context_ptr pContext)
+	{
+		if (!pContext)
+			return;
+
+		interval_timer tm;
+		tm.start();
+
+		g_ocl.destroy_buffer(pContext->m_ocl_pixel_blocks);
+
+		g_ocl.destroy_kernel(pContext->m_ocl_determine_selectors_kernel);
+		g_ocl.destroy_kernel(pContext->m_ocl_find_optimal_selector_clusters_for_each_block_kernel);
+		g_ocl.destroy_kernel(pContext->m_ocl_encode_etc1s_from_pixel_cluster_kernel);
+		g_ocl.destroy_kernel(pContext->m_ocl_encode_etc1s_blocks_kernel);
+		g_ocl.destroy_kernel(pContext->m_ocl_refine_endpoint_clusterization_kernel);
+
+		g_ocl.destroy_command_queue(pContext->m_command_queue);
+			
+		memset(pContext, 0, sizeof(opencl_context));
+
+		free(pContext);
+
+		debug_printf("opencl_destroy_context: Elapsed time: %f secs\n", tm.get_elapsed_secs());
+	}
+
+#pragma pack(push, 1)
+	struct cl_encode_etc1s_param_struct
+	{
+		int m_total_blocks;
+		int m_perceptual;
+		int m_total_perms;
+	};
+#pragma pack(pop)
+
+	bool opencl_set_pixel_blocks(opencl_context_ptr pContext, uint32_t total_blocks, const cl_pixel_block* pPixel_blocks)
+	{
+		if (!opencl_is_available())
+			return false;
+
+		if (pContext->m_ocl_pixel_blocks)
+		{
+			g_ocl.destroy_buffer(pContext->m_ocl_pixel_blocks);
+			pContext->m_ocl_pixel_blocks = nullptr;
+		}
+
+		pContext->m_ocl_pixel_blocks = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue, pPixel_blocks, sizeof(cl_pixel_block) * total_blocks);
+		if (!pContext->m_ocl_pixel_blocks)
+			return false;
+
+		pContext->m_ocl_total_pixel_blocks = total_blocks;
+
+		return true;
+	}
+
+	bool opencl_encode_etc1s_blocks(opencl_context_ptr pContext, etc_block* pOutput_blocks, bool perceptual, uint32_t total_perms)
+	{
+		if (!opencl_is_available())
+			return false;
+
+		interval_timer tm;
+		tm.start();
+
+		assert(pContext->m_ocl_pixel_blocks);
+		if (!pContext->m_ocl_pixel_blocks)
+			return false;
+				
+		cl_encode_etc1s_param_struct ps;
+		ps.m_total_blocks = pContext->m_ocl_total_pixel_blocks;
+		ps.m_perceptual = perceptual;
+		ps.m_total_perms = total_perms;
+
+		bool status = false;
+
+		cl_mem vars = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue , &ps, sizeof(ps));
+		cl_mem block_buf = g_ocl.alloc_write_buffer(sizeof(etc_block) * pContext->m_ocl_total_pixel_blocks);
+		
+		if (!vars || !block_buf)
+			goto exit;
+
+		if (!g_ocl.set_kernel_args(pContext->m_ocl_encode_etc1s_blocks_kernel, vars, pContext->m_ocl_pixel_blocks, block_buf))
+			goto exit;
+
+		if (!g_ocl.run_2D(pContext->m_command_queue, pContext->m_ocl_encode_etc1s_blocks_kernel, pContext->m_ocl_total_pixel_blocks, 1))
+			goto exit;
+
+		if (!g_ocl.read_from_buffer(pContext->m_command_queue, block_buf, pOutput_blocks, pContext->m_ocl_total_pixel_blocks * sizeof(etc_block)))
+			goto exit;
+
+		status = true;
+
+		debug_printf("opencl_encode_etc1s_blocks: Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
+
+exit:
+		g_ocl.destroy_buffer(block_buf);
+		g_ocl.destroy_buffer(vars);
+
+		return status;
+	}
+
+	bool opencl_encode_etc1s_pixel_clusters(
+		opencl_context_ptr pContext,
+		etc_block* pOutput_blocks,
+		uint32_t total_clusters,
+		const cl_pixel_cluster* pClusters,
+		uint64_t total_pixels,
+		const color_rgba* pPixels, const uint32_t* pPixel_weights,
+		bool perceptual, uint32_t total_perms)
+	{
+		if (!opencl_is_available())
+			return false;
+
+		interval_timer tm;
+		tm.start();
+				
+		cl_encode_etc1s_param_struct ps;
+		ps.m_total_blocks = total_clusters;
+		ps.m_perceptual = perceptual;
+		ps.m_total_perms = total_perms;
+
+		bool status = false;
+
+		if (sizeof(size_t) == sizeof(uint32_t))
+		{
+			if ( ((sizeof(cl_pixel_cluster) * total_clusters) > UINT32_MAX) ||
+				 ((sizeof(color_rgba) * total_pixels) > UINT32_MAX) ||
+				 ((sizeof(uint32_t) * total_pixels) > UINT32_MAX) )
+			{
+				return false;
+			}
+		}
+				
+		cl_mem vars = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue , &ps, sizeof(ps));
+		cl_mem input_clusters = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue, pClusters, (size_t)(sizeof(cl_pixel_cluster) * total_clusters));
+		cl_mem input_pixels = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue, pPixels, (size_t)(sizeof(color_rgba) * total_pixels));
+		cl_mem weights_buf = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue, pPixel_weights, (size_t)(sizeof(uint32_t) * total_pixels));
+		cl_mem block_buf = g_ocl.alloc_write_buffer(sizeof(etc_block) * total_clusters);
+
+		if (!vars || !input_clusters || !input_pixels || !weights_buf || !block_buf)
+			goto exit;
+
+		if (!g_ocl.set_kernel_args(pContext->m_ocl_encode_etc1s_from_pixel_cluster_kernel, vars, input_clusters, input_pixels, weights_buf, block_buf))
+			goto exit;
+
+		if (!g_ocl.run_2D(pContext->m_command_queue, pContext->m_ocl_encode_etc1s_from_pixel_cluster_kernel, total_clusters, 1))
+			goto exit;
+
+		if (!g_ocl.read_from_buffer(pContext->m_command_queue, block_buf, pOutput_blocks, sizeof(etc_block) * total_clusters))
+			goto exit;
+
+		status = true;
+
+		debug_printf("opencl_encode_etc1s_pixel_clusters: Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
+
+	exit:
+		g_ocl.destroy_buffer(block_buf);
+		g_ocl.destroy_buffer(weights_buf);
+		g_ocl.destroy_buffer(input_pixels);
+		g_ocl.destroy_buffer(input_clusters);
+		g_ocl.destroy_buffer(vars);
+
+		return status;
+	}
+
+#pragma pack(push, 1)
+	struct cl_rec_param_struct
+	{
+		int m_total_blocks;
+		int m_perceptual;
+	};
+#pragma pack(pop)
+
+	bool opencl_refine_endpoint_clusterization(
+		opencl_context_ptr pContext,
+		const cl_block_info_struct* pPixel_block_info,
+		uint32_t total_clusters,
+		const cl_endpoint_cluster_struct* pCluster_info,
+		const uint32_t* pSorted_block_indices,
+		uint32_t* pOutput_cluster_indices,
+		bool perceptual)
+	{
+		if (!opencl_is_available())
+			return false;
+
+		interval_timer tm;
+		tm.start();
+
+		assert(pContext->m_ocl_pixel_blocks);
+		if (!pContext->m_ocl_pixel_blocks)
+			return false;
+				
+		cl_rec_param_struct ps;
+		ps.m_total_blocks = pContext->m_ocl_total_pixel_blocks;
+		ps.m_perceptual = perceptual;
+
+		bool status = false;
+
+		cl_mem pixel_block_info = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue, pPixel_block_info, sizeof(cl_block_info_struct) * pContext->m_ocl_total_pixel_blocks);
+		cl_mem cluster_info = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue, pCluster_info, sizeof(cl_endpoint_cluster_struct) * total_clusters);
+		cl_mem sorted_block_indices = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue, pSorted_block_indices, sizeof(uint32_t) * pContext->m_ocl_total_pixel_blocks);
+		cl_mem output_buf = g_ocl.alloc_write_buffer(sizeof(uint32_t) * pContext->m_ocl_total_pixel_blocks);
+		
+		if (!pixel_block_info || !cluster_info || !sorted_block_indices || !output_buf)
+			goto exit;
+
+		if (!g_ocl.set_kernel_args(pContext->m_ocl_refine_endpoint_clusterization_kernel, ps, pContext->m_ocl_pixel_blocks, pixel_block_info, cluster_info, sorted_block_indices, output_buf))
+			goto exit;
+
+		if (!g_ocl.run_2D(pContext->m_command_queue, pContext->m_ocl_refine_endpoint_clusterization_kernel, pContext->m_ocl_total_pixel_blocks, 1))
+			goto exit;
+
+		if (!g_ocl.read_from_buffer(pContext->m_command_queue, output_buf, pOutput_cluster_indices, pContext->m_ocl_total_pixel_blocks * sizeof(uint32_t)))
+			goto exit;
+
+		debug_printf("opencl_refine_endpoint_clusterization: Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
+		
+		status = true;
+
+exit:
+		g_ocl.destroy_buffer(pixel_block_info);
+		g_ocl.destroy_buffer(cluster_info);
+		g_ocl.destroy_buffer(sorted_block_indices);
+		g_ocl.destroy_buffer(output_buf);
+
+		return status;
+	}
+
+	bool opencl_find_optimal_selector_clusters_for_each_block(
+		opencl_context_ptr pContext,
+		const fosc_block_struct* pInput_block_info,	// one per block
+		uint32_t total_input_selectors,
+		const fosc_selector_struct* pInput_selectors,
+		const uint32_t* pSelector_cluster_indices,
+		uint32_t* pOutput_selector_cluster_indices, // one per block
+		bool perceptual)
+	{
+		if (!opencl_is_available())
+			return false;
+
+		interval_timer tm;
+		tm.start();
+
+		assert(pContext->m_ocl_pixel_blocks);
+		if (!pContext->m_ocl_pixel_blocks)
+			return false;
+
+		fosc_param_struct ps;
+		ps.m_total_blocks = pContext->m_ocl_total_pixel_blocks;
+		ps.m_perceptual = perceptual;
+		
+		bool status = false;
+
+		cl_mem input_block_info = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue, pInput_block_info, sizeof(fosc_block_struct) * pContext->m_ocl_total_pixel_blocks);
+		cl_mem input_selectors = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue, pInput_selectors, sizeof(fosc_selector_struct) * total_input_selectors);
+		cl_mem selector_cluster_indices = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue, pSelector_cluster_indices, sizeof(uint32_t) * total_input_selectors);
+		cl_mem output_selector_cluster_indices = g_ocl.alloc_write_buffer(sizeof(uint32_t) * pContext->m_ocl_total_pixel_blocks);
+
+		if (!input_block_info || !input_selectors || !selector_cluster_indices || !output_selector_cluster_indices)
+			goto exit;
+
+		if (!g_ocl.set_kernel_args(pContext->m_ocl_find_optimal_selector_clusters_for_each_block_kernel, ps, pContext->m_ocl_pixel_blocks, input_block_info, input_selectors, selector_cluster_indices, output_selector_cluster_indices))
+			goto exit;
+
+		if (!g_ocl.run_2D(pContext->m_command_queue, pContext->m_ocl_find_optimal_selector_clusters_for_each_block_kernel, pContext->m_ocl_total_pixel_blocks, 1))
+			goto exit;
+
+		if (!g_ocl.read_from_buffer(pContext->m_command_queue, output_selector_cluster_indices, pOutput_selector_cluster_indices, pContext->m_ocl_total_pixel_blocks * sizeof(uint32_t)))
+			goto exit;
+
+		debug_printf("opencl_find_optimal_selector_clusters_for_each_block: Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
+
+		status = true;
+
+	exit:
+		g_ocl.destroy_buffer(input_block_info);
+		g_ocl.destroy_buffer(input_selectors);
+		g_ocl.destroy_buffer(selector_cluster_indices);
+		g_ocl.destroy_buffer(output_selector_cluster_indices);
+
+		return status;
+	}
+
+	bool opencl_determine_selectors(
+		opencl_context_ptr pContext,
+		const color_rgba* pInput_etc_color5_and_inten,
+		etc_block* pOutput_blocks,
+		bool perceptual)
+	{
+		if (!opencl_is_available())
+			return false;
+
+		interval_timer tm;
+		tm.start();
+
+		assert(pContext->m_ocl_pixel_blocks);
+		if (!pContext->m_ocl_pixel_blocks)
+			return false;
+
+		ds_param_struct ps;
+		ps.m_total_blocks = pContext->m_ocl_total_pixel_blocks;
+		ps.m_perceptual = perceptual;
+
+		bool status = false;
+
+		cl_mem input_etc_color5_intens = g_ocl.alloc_and_init_read_buffer(pContext->m_command_queue, pInput_etc_color5_and_inten, sizeof(color_rgba) * pContext->m_ocl_total_pixel_blocks);
+		cl_mem output_blocks = g_ocl.alloc_write_buffer(sizeof(etc_block) * pContext->m_ocl_total_pixel_blocks);
+
+		if (!input_etc_color5_intens || !output_blocks)
+			goto exit;
+
+		if (!g_ocl.set_kernel_args(pContext->m_ocl_determine_selectors_kernel, ps, pContext->m_ocl_pixel_blocks, input_etc_color5_intens, output_blocks))
+			goto exit;
+
+		if (!g_ocl.run_2D(pContext->m_command_queue, pContext->m_ocl_determine_selectors_kernel, pContext->m_ocl_total_pixel_blocks, 1))
+			goto exit;
+
+		if (!g_ocl.read_from_buffer(pContext->m_command_queue, output_blocks, pOutput_blocks, pContext->m_ocl_total_pixel_blocks * sizeof(etc_block)))
+			goto exit;
+
+		debug_printf("opencl_determine_selectors: Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
+					
+		status = true;
+	
+	exit:
+		g_ocl.destroy_buffer(input_etc_color5_intens);
+		g_ocl.destroy_buffer(output_blocks);
+
+		return status;
+	}
+
+#else	
+namespace basisu
+{
+	// No OpenCL support - all dummy functions that return false;
+	bool opencl_init(bool force_serialization)
+	{
+		BASISU_NOTE_UNUSED(force_serialization);
+
+		return false;
+	}
+
+	void opencl_deinit()
+	{
+	}
+
+	bool opencl_is_available()
+	{
+		return false;
+	}
+
+	opencl_context_ptr opencl_create_context()
+	{
+		return nullptr;
+	}
+
+	void opencl_destroy_context(opencl_context_ptr context)
+	{
+		BASISU_NOTE_UNUSED(context);
+	}
+
+	bool opencl_set_pixel_blocks(opencl_context_ptr pContext, uint32_t total_blocks, const cl_pixel_block* pPixel_blocks)
+	{
+		BASISU_NOTE_UNUSED(pContext);
+		BASISU_NOTE_UNUSED(total_blocks);
+		BASISU_NOTE_UNUSED(pPixel_blocks);
+
+		return false;
+	}
+
+	bool opencl_encode_etc1s_blocks(opencl_context_ptr pContext, etc_block* pOutput_blocks, bool perceptual, uint32_t total_perms)
+	{
+		BASISU_NOTE_UNUSED(pContext);
+		BASISU_NOTE_UNUSED(pOutput_blocks);
+		BASISU_NOTE_UNUSED(perceptual);
+		BASISU_NOTE_UNUSED(total_perms);
+
+		return false;
+	}
+
+	bool opencl_encode_etc1s_pixel_clusters(
+		opencl_context_ptr pContext,
+		etc_block* pOutput_blocks,
+		uint32_t total_clusters,
+		const cl_pixel_cluster* pClusters,
+		uint64_t total_pixels,
+		const color_rgba* pPixels, const uint32_t *pPixel_weights,
+		bool perceptual, uint32_t total_perms)
+	{
+		BASISU_NOTE_UNUSED(pContext);
+		BASISU_NOTE_UNUSED(pOutput_blocks);
+		BASISU_NOTE_UNUSED(total_clusters);
+		BASISU_NOTE_UNUSED(pClusters);
+		BASISU_NOTE_UNUSED(total_pixels);
+		BASISU_NOTE_UNUSED(pPixels);
+		BASISU_NOTE_UNUSED(pPixel_weights);
+		BASISU_NOTE_UNUSED(perceptual);
+		BASISU_NOTE_UNUSED(total_perms);
+		
+		return false;
+	}
+
+	bool opencl_refine_endpoint_clusterization(
+		opencl_context_ptr pContext,
+		const cl_block_info_struct* pPixel_block_info,
+		uint32_t total_clusters,
+		const cl_endpoint_cluster_struct* pCluster_info,
+		const uint32_t* pSorted_block_indices,
+		uint32_t* pOutput_cluster_indices,
+		bool perceptual)
+	{
+		BASISU_NOTE_UNUSED(pContext);
+		BASISU_NOTE_UNUSED(pPixel_block_info);
+		BASISU_NOTE_UNUSED(total_clusters);
+		BASISU_NOTE_UNUSED(pCluster_info);
+		BASISU_NOTE_UNUSED(pSorted_block_indices);
+		BASISU_NOTE_UNUSED(pOutput_cluster_indices);
+		BASISU_NOTE_UNUSED(perceptual);
+
+		return false;
+	}
+
+	bool opencl_find_optimal_selector_clusters_for_each_block(
+		opencl_context_ptr pContext,
+		const fosc_block_struct* pInput_block_info,	// one per block
+		uint32_t total_input_selectors,
+		const fosc_selector_struct* pInput_selectors,
+		const uint32_t* pSelector_cluster_indices,
+		uint32_t* pOutput_selector_cluster_indices, // one per block
+		bool perceptual)
+	{
+		BASISU_NOTE_UNUSED(pContext);
+		BASISU_NOTE_UNUSED(pInput_block_info);
+		BASISU_NOTE_UNUSED(total_input_selectors);
+		BASISU_NOTE_UNUSED(pInput_selectors);
+		BASISU_NOTE_UNUSED(pSelector_cluster_indices);
+		BASISU_NOTE_UNUSED(pOutput_selector_cluster_indices);
+		BASISU_NOTE_UNUSED(perceptual);
+
+		return false;
+	}
+
+	bool opencl_determine_selectors(
+		opencl_context_ptr pContext,
+		const color_rgba* pInput_etc_color5_and_inten,
+		etc_block* pOutput_blocks,
+		bool perceptual)
+	{
+		BASISU_NOTE_UNUSED(pContext);
+		BASISU_NOTE_UNUSED(pInput_etc_color5_and_inten);
+		BASISU_NOTE_UNUSED(pOutput_blocks);
+		BASISU_NOTE_UNUSED(perceptual);
+
+		return false;
+	}
+
+#endif // BASISU_SUPPORT_OPENCL
+
+} // namespace basisu
diff --git a/encoder/basisu_opencl.h b/encoder/basisu_opencl.h
new file mode 100644
index 0000000..4194a08
--- /dev/null
+++ b/encoder/basisu_opencl.h
@@ -0,0 +1,143 @@
+// basisu_opencl.h
+// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
+//
+// Note: Undefine or set BASISU_SUPPORT_OPENCL to 0 to completely OpenCL support.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#pragma once
+#include "../transcoder/basisu.h"
+#include "basisu_enc.h"
+#include "basisu_etc.h"
+
+namespace basisu
+{
+	bool opencl_init(bool force_serialization);
+	void opencl_deinit();
+	bool opencl_is_available();
+
+	struct opencl_context;
+
+	// Each thread calling OpenCL should have its own opencl_context_ptr. This corresponds to a OpenCL command queue. (Confusingly, we only use a single OpenCL device "context".)
+	typedef opencl_context* opencl_context_ptr;
+
+	opencl_context_ptr opencl_create_context();
+	void opencl_destroy_context(opencl_context_ptr context);
+
+#pragma pack(push, 1)
+	struct cl_pixel_block
+	{
+		color_rgba m_pixels[16]; // [y*4+x]
+	};
+#pragma pack(pop)
+
+	// Must match BASISU_ETC1_CLUSTER_FIT_ORDER_TABLE_SIZE
+	const uint32_t OPENCL_ENCODE_ETC1S_MAX_PERMS = 165;
+
+	bool opencl_set_pixel_blocks(opencl_context_ptr pContext, uint32_t total_blocks, const cl_pixel_block* pPixel_blocks);
+
+	bool opencl_encode_etc1s_blocks(opencl_context_ptr pContext, etc_block* pOutput_blocks, bool perceptual, uint32_t total_perms);
+
+	// opencl_encode_etc1s_pixel_clusters
+
+#pragma pack(push, 1)
+	struct cl_pixel_cluster
+	{
+		uint64_t m_total_pixels;
+		uint64_t m_first_pixel_index;
+	};
+#pragma pack(pop)
+
+	bool opencl_encode_etc1s_pixel_clusters(
+		opencl_context_ptr pContext,
+		etc_block* pOutput_blocks, 
+		uint32_t total_clusters,
+		const cl_pixel_cluster *pClusters,
+		uint64_t total_pixels,
+		const color_rgba *pPixels,
+		const uint32_t *pPixel_weights,
+		bool perceptual, uint32_t total_perms);
+
+	// opencl_refine_endpoint_clusterization
+
+#pragma pack(push, 1)
+	struct cl_block_info_struct
+	{
+		uint16_t m_first_cluster_ofs;
+		uint16_t m_num_clusters;
+		uint16_t m_cur_cluster_index;
+		uint8_t m_cur_cluster_etc_inten;
+	};
+
+	struct cl_endpoint_cluster_struct
+	{
+		color_rgba m_unscaled_color;
+		uint8_t m_etc_inten;
+		uint16_t m_cluster_index;
+	};
+#pragma pack(pop)
+
+	bool opencl_refine_endpoint_clusterization(
+		opencl_context_ptr pContext,
+		const cl_block_info_struct *pPixel_block_info,
+		uint32_t total_clusters,
+		const cl_endpoint_cluster_struct *pCluster_info,
+		const uint32_t *pSorted_block_indices,
+		uint32_t* pOutput_cluster_indices, 
+		bool perceptual);
+
+	// opencl_find_optimal_selector_clusters_for_each_block
+
+#pragma pack(push, 1)
+	struct fosc_selector_struct
+	{
+		uint32_t m_packed_selectors;	// 4x4 grid of 2-bit selectors
+	};
+
+	struct fosc_block_struct
+	{
+		color_rgba m_etc_color5_inten;  // unscaled 5-bit block color in RGB, alpha has block's intensity index
+		uint32_t m_first_selector;		// offset into selector table
+		uint32_t m_num_selectors;		// number of selectors to check
+	};
+
+	struct fosc_param_struct
+	{
+		uint32_t m_total_blocks;
+		int m_perceptual;
+	};
+#pragma pack(pop)
+
+	bool opencl_find_optimal_selector_clusters_for_each_block(
+		opencl_context_ptr pContext,
+		const fosc_block_struct* pInput_block_info,	// one per block
+		uint32_t total_input_selectors,
+		const fosc_selector_struct* pInput_selectors,
+		const uint32_t* pSelector_cluster_indices,
+		uint32_t* pOutput_selector_cluster_indices, // one per block
+		bool perceptual);
+
+#pragma pack(push, 1)
+	struct ds_param_struct
+	{
+		uint32_t m_total_blocks;
+		int m_perceptual;
+	};
+#pragma pack(pop)
+
+	bool opencl_determine_selectors(
+		opencl_context_ptr pContext,
+		const color_rgba* pInput_etc_color5_and_inten,
+		etc_block* pOutput_blocks,
+		bool perceptual);
+
+} // namespace basisu
diff --git a/encoder/basisu_uastc_enc.cpp b/encoder/basisu_uastc_enc.cpp
index ca2b325..271bbc6 100644
--- a/encoder/basisu_uastc_enc.cpp
+++ b/encoder/basisu_uastc_enc.cpp
@@ -13,7 +13,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 #include "basisu_uastc_enc.h"
+
+#if BASISU_USE_ASTC_DECOMPRESS
 #include "basisu_astc_decomp.h"
+#endif
+
 #include "basisu_gpu_texture.h"
 #include "basisu_bc7enc.h"
 
@@ -509,14 +513,14 @@
 
 		if (pForce_selectors == nullptr)
 		{
-		int s0 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[0]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[2]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[4]].m_unquant;
-		int s1 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[1]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[3]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[5]].m_unquant;
-		if (s1 < s0)
-		{
-			std::swap(astc_results.m_endpoints[0], astc_results.m_endpoints[1]);
-			std::swap(astc_results.m_endpoints[2], astc_results.m_endpoints[3]);
-			std::swap(astc_results.m_endpoints[4], astc_results.m_endpoints[5]);
-			invert = true;
+			int s0 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[0]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[2]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[4]].m_unquant;
+			int s1 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[1]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[3]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[5]].m_unquant;
+			if (s1 < s0)
+			{
+				std::swap(astc_results.m_endpoints[0], astc_results.m_endpoints[1]);
+				std::swap(astc_results.m_endpoints[2], astc_results.m_endpoints[3]);
+				std::swap(astc_results.m_endpoints[4], astc_results.m_endpoints[5]);
+				invert = true;
 			}
 		}
 
@@ -3562,7 +3566,8 @@
 			basist::color32 temp_block_unpacked[4][4];
 			success = basist::unpack_uastc(temp_block, (basist::color32 *)temp_block_unpacked, false);
 			VALIDATE(success);
-				
+
+#if BASISU_USE_ASTC_DECOMPRESS
 			// Now round trip to packed ASTC and back, then decode to pixels.
 			uint32_t astc_data[4];
 			
@@ -3590,6 +3595,7 @@
 					VALIDATE(temp_block_unpacked[y][x].c[3] == decoded_uastc_block[y][x].a);
 				}
 			}
+#endif
 		}
 #endif
 
diff --git a/encoder/cppspmd_sse.h b/encoder/cppspmd_sse.h
index 9a97eeb..4c61bab 100644
--- a/encoder/cppspmd_sse.h
+++ b/encoder/cppspmd_sse.h
@@ -1,13 +1,5 @@
 // cppspmd_sse.h
-// Note for Basis Universal: All of the "cppspmd" code and headers are OPTIONAL to Basis Universal. if BASISU_SUPPORT_SSE is 0, it will never be included and does not impact compilation.
-// SSE 2 or 4.1
-// Originally written by Nicolas Guillemot, Jefferson Amstutz in the "CppSPMD" project.
-// 4/20: Richard Geldreich: Macro control flow, more SIMD instruction sets, optimizations, supports using multiple SIMD instruction sets in same executable. Still a work in progress!
-//
-// Originally Copyright 2016 Nicolas Guillemot
-// Changed from the MIT license to Apache 2.0 with permission from the author.
-//
-// Modifications/enhancements Copyright 2020-2021 Binomial LLC
+// Copyright 2020-2022 Binomial LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -20,6 +12,11 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+//
+// Notes for Basis Universal:
+// All of the "cppspmd" code and headers are OPTIONAL to Basis Universal. if BASISU_SUPPORT_SSE is 0, it will never be included and does not impact compilation.
+// The techniques used in this code were originally demonstrated for AVX2 by Nicolas Guillemot, Jefferson Amstutz in their "CppSPMD" project.
+// This is new code for use in Basis Universal, although it uses the same general SPMD techniques in SSE 2/4.
 
 #include <stdlib.h>
 #include <stdint.h>
@@ -1330,7 +1327,7 @@
 		__m128 temp = _mm_add_ps(_mm_shuffle_ps(k3210, k3210, _MM_SHUFFLE(0, 1, 2, 3)), k3210);
 		return _mm_cvtss_f32(_mm_add_ss(_mm_movehl_ps(temp, temp), temp));
 	}
-		
+
 	CPPSPMD_FORCE_INLINE int reduce_add(vint v)
 	{
 		__m128i k3210 = blendv_mask_epi32(_mm_setzero_si128(), v.m_value, m_exec.m_mask);
@@ -1668,14 +1665,16 @@
 CPPSPMD_FORCE_INLINE vint undefined_vint() { return vint{ _mm_undefined_si128() }; }
 CPPSPMD_FORCE_INLINE vfloat undefined_vfloat() { return vfloat{ _mm_undefined_ps() }; }
 
+CPPSPMD_FORCE_INLINE vint zero_vint() { return vint{ _mm_setzero_si128() }; }
+CPPSPMD_FORCE_INLINE vfloat zero_vfloat() { return vfloat{ _mm_setzero_ps() }; }
+
 CPPSPMD_FORCE_INLINE vint vint_lane_set(int v0, int v1, int v2, int v3) { return vint{ _mm_set_epi32(v3, v2, v1, v0) }; }
 CPPSPMD_FORCE_INLINE vfloat vfloat_lane_set(float v0, float v1, float v2, float v3) { return vfloat{ _mm_set_ps(v3, v2, v1, v0) }; }
-
 CPPSPMD_FORCE_INLINE vint vint_lane_set_r(int v3, int v2, int v1, int v0) { return vint{ _mm_set_epi32(v3, v2, v1, v0) }; }
 CPPSPMD_FORCE_INLINE vfloat vfloat_lane_set_r(float v3, float v2, float v1, float v0) { return vfloat{ _mm_set_ps(v3, v2, v1, v0) }; }
-
 // control is an 8-bit immediate value containing 4 2-bit indices which shuffles the int32's in each 128-bit lane.
 #define VINT_LANE_SHUFFLE_EPI32(a, control) vint(_mm_shuffle_epi32((a).m_value, control))
+#define VFLOAT_LANE_SHUFFLE_PS(a, b, control) vfloat(_mm_shuffle_ps((a).m_value, (b).m_value, control))
 
 // control is an 8-bit immediate value containing 4 2-bit indices which shuffles the int16's in either the high or low 64-bit lane.
 #define VINT_LANE_SHUFFLELO_EPI16(a, control) vint(_mm_shufflelo_epi16((a).m_value, control))
diff --git a/encoder/jpgd.cpp b/encoder/jpgd.cpp
index 563fa74..fec8b71 100644
--- a/encoder/jpgd.cpp
+++ b/encoder/jpgd.cpp
@@ -23,17 +23,6 @@
 // v1.04, May. 19, 2012: Code tweaks to fix VS2008 static code analysis warnings
 // v2.00, March 20, 2020: Fuzzed with zzuf and afl. Fixed several issues, converted most assert()'s to run-time checks. Added chroma upsampling. Removed freq. domain upsampling. gcc/clang warnings.
 //
-#ifdef _MSC_VER
-#ifndef BASISU_NO_ITERATOR_DEBUG_LEVEL
-#if defined(_DEBUG) || defined(DEBUG)
-#define _ITERATOR_DEBUG_LEVEL 1
-#define _SECURE_SCL 1
-#else
-#define _SECURE_SCL 0
-#define _ITERATOR_DEBUG_LEVEL 0
-#endif
-#endif
-#endif
 
 #include "jpgd.h"
 #include <string.h>
diff --git a/encoder/lodepng.cpp b/encoder/lodepng.cpp
deleted file mode 100644
index 63adcf4..0000000
--- a/encoder/lodepng.cpp
+++ /dev/null
@@ -1,6008 +0,0 @@
-/*
-LodePNG version 20190210
-
-Copyright (c) 2005-2019 Lode Vandevenne
-
-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.
-*/
-
-/*
-The manual and changelog are in the header file "lodepng.h"
-Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for C.
-*/
-
-#ifdef _MSC_VER
-#define _CRT_SECURE_NO_DEPRECATE
-#pragma warning (disable : 4201)
-
-#ifndef BASISU_NO_ITERATOR_DEBUG_LEVEL
-#if defined(_DEBUG) || defined(DEBUG)
-#define _ITERATOR_DEBUG_LEVEL 1
-#define _SECURE_SCL 1
-#else
-#define _SECURE_SCL 0
-#define _ITERATOR_DEBUG_LEVEL 0
-#endif
-#endif
-#endif
-
-#include "lodepng.h"
-
-#include <limits.h> /* LONG_MAX */
-#include <stdio.h> /* file handling */
-#include <stdlib.h> /* allocations */
-
-#if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/
-#pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/
-#pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/
-#endif /*_MSC_VER */
-
-const char* LODEPNG_VERSION_STRING = "20190210";
-
-/*
-This source file is built up in the following large parts. The code sections
-with the "LODEPNG_COMPILE_" #defines divide this up further in an intermixed way.
--Tools for C and common code for PNG and Zlib
--C Code for Zlib (huffman, deflate, ...)
--C Code for PNG (file format chunks, adam7, PNG filters, color conversions, ...)
--The C++ wrapper around all of the above
-*/
-
-/*The malloc, realloc and free functions defined here with "lodepng_" in front
-of the name, so that you can easily change them to others related to your
-platform if needed. Everything else in the code calls these. Pass
--DLODEPNG_NO_COMPILE_ALLOCATORS to the compiler, or comment out
-#define LODEPNG_COMPILE_ALLOCATORS in the header, to disable the ones here and
-define them in your own project's source files without needing to change
-lodepng source code. Don't forget to remove "static" if you copypaste them
-from here.*/
-
-#ifdef LODEPNG_COMPILE_ALLOCATORS
-static void* lodepng_malloc(size_t size) {
-#ifdef LODEPNG_MAX_ALLOC
-  if(size > LODEPNG_MAX_ALLOC) return 0;
-#endif
-  return malloc(size);
-}
-
-static void* lodepng_realloc(void* ptr, size_t new_size) {
-#ifdef LODEPNG_MAX_ALLOC
-  if(new_size > LODEPNG_MAX_ALLOC) return 0;
-#endif
-  return realloc(ptr, new_size);
-}
-
-static void lodepng_free(void* ptr) {
-  free(ptr);
-}
-#else /*LODEPNG_COMPILE_ALLOCATORS*/
-void* lodepng_malloc(size_t size);
-void* lodepng_realloc(void* ptr, size_t new_size);
-void lodepng_free(void* ptr);
-#endif /*LODEPNG_COMPILE_ALLOCATORS*/
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* // Tools for C, and common code for PNG and Zlib.                       // */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#define LODEPNG_MAX(a, b) (((a) > (b)) ? (a) : (b))
-#define LODEPNG_MIN(a, b) (((a) < (b)) ? (a) : (b))
-
-/*
-Often in case of an error a value is assigned to a variable and then it breaks
-out of a loop (to go to the cleanup phase of a function). This macro does that.
-It makes the error handling code shorter and more readable.
-
-Example: if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83);
-*/
-#define CERROR_BREAK(errorvar, code){\
-  errorvar = code;\
-  break;\
-}
-
-/*version of CERROR_BREAK that assumes the common case where the error variable is named "error"*/
-#define ERROR_BREAK(code) CERROR_BREAK(error, code)
-
-/*Set error var to the error code, and return it.*/
-#define CERROR_RETURN_ERROR(errorvar, code){\
-  errorvar = code;\
-  return code;\
-}
-
-/*Try the code, if it returns error, also return the error.*/
-#define CERROR_TRY_RETURN(call){\
-  unsigned error = call;\
-  if(error) return error;\
-}
-
-/*Set error var to the error code, and return from the void function.*/
-#define CERROR_RETURN(errorvar, code){\
-  errorvar = code;\
-  return;\
-}
-
-/*
-About uivector, ucvector and string:
--All of them wrap dynamic arrays or text strings in a similar way.
--LodePNG was originally written in C++. The vectors replace the std::vectors that were used in the C++ version.
--The string tools are made to avoid problems with compilers that declare things like strncat as deprecated.
--They're not used in the interface, only internally in this file as static functions.
--As with many other structs in this file, the init and cleanup functions serve as ctor and dtor.
-*/
-
-#ifdef LODEPNG_COMPILE_ZLIB
-/*dynamic vector of unsigned ints*/
-typedef struct uivector {
-  unsigned* data;
-  size_t size; /*size in number of unsigned longs*/
-  size_t allocsize; /*allocated size in bytes*/
-} uivector;
-
-static void uivector_cleanup(void* p) {
-  ((uivector*)p)->size = ((uivector*)p)->allocsize = 0;
-  lodepng_free(((uivector*)p)->data);
-  ((uivector*)p)->data = NULL;
-}
-
-/*returns 1 if success, 0 if failure ==> nothing done*/
-static unsigned uivector_reserve(uivector* p, size_t allocsize) {
-  if(allocsize > p->allocsize) {
-    size_t newsize = (allocsize > p->allocsize * 2) ? allocsize : (allocsize * 3 / 2);
-    void* data = lodepng_realloc(p->data, newsize);
-    if(data) {
-      p->allocsize = newsize;
-      p->data = (unsigned*)data;
-    }
-    else return 0; /*error: not enough memory*/
-  }
-  return 1;
-}
-
-/*returns 1 if success, 0 if failure ==> nothing done*/
-static unsigned uivector_resize(uivector* p, size_t size) {
-  if(!uivector_reserve(p, size * sizeof(unsigned))) return 0;
-  p->size = size;
-  return 1; /*success*/
-}
-
-/*resize and give all new elements the value*/
-static unsigned uivector_resizev(uivector* p, size_t size, unsigned value) {
-  size_t oldsize = p->size, i;
-  if(!uivector_resize(p, size)) return 0;
-  for(i = oldsize; i < size; ++i) p->data[i] = value;
-  return 1;
-}
-
-static void uivector_init(uivector* p) {
-  p->data = NULL;
-  p->size = p->allocsize = 0;
-}
-
-#ifdef LODEPNG_COMPILE_ENCODER
-/*returns 1 if success, 0 if failure ==> nothing done*/
-static unsigned uivector_push_back(uivector* p, unsigned c) {
-  if(!uivector_resize(p, p->size + 1)) return 0;
-  if (!p->data) return 0;
-  p->data[p->size - 1] = c;
-  return 1;
-}
-#endif /*LODEPNG_COMPILE_ENCODER*/
-#endif /*LODEPNG_COMPILE_ZLIB*/
-
-/* /////////////////////////////////////////////////////////////////////////// */
-
-/*dynamic vector of unsigned chars*/
-typedef struct ucvector {
-  unsigned char* data;
-  size_t size; /*used size*/
-  size_t allocsize; /*allocated size*/
-} ucvector;
-
-/*returns 1 if success, 0 if failure ==> nothing done*/
-static unsigned ucvector_reserve(ucvector* p, size_t allocsize) {
-  if(allocsize > p->allocsize) {
-    size_t newsize = (allocsize > p->allocsize * 2) ? allocsize : (allocsize * 3 / 2);
-    void* data = lodepng_realloc(p->data, newsize);
-    if(data) {
-      p->allocsize = newsize;
-      p->data = (unsigned char*)data;
-    }
-    else return 0; /*error: not enough memory*/
-  }
-  return 1;
-}
-
-/*returns 1 if success, 0 if failure ==> nothing done*/
-static unsigned ucvector_resize(ucvector* p, size_t size) {
-  if(!ucvector_reserve(p, size * sizeof(unsigned char))) return 0;
-  p->size = size;
-  return 1; /*success*/
-}
-
-#ifdef LODEPNG_COMPILE_PNG
-
-static void ucvector_cleanup(void* p) {
-  ((ucvector*)p)->size = ((ucvector*)p)->allocsize = 0;
-  lodepng_free(((ucvector*)p)->data);
-  ((ucvector*)p)->data = NULL;
-}
-
-static void ucvector_init(ucvector* p) {
-  p->data = NULL;
-  p->size = p->allocsize = 0;
-}
-#endif /*LODEPNG_COMPILE_PNG*/
-
-#ifdef LODEPNG_COMPILE_ZLIB
-/*you can both convert from vector to buffer&size and vica versa. If you use
-init_buffer to take over a buffer and size, it is not needed to use cleanup*/
-static void ucvector_init_buffer(ucvector* p, unsigned char* buffer, size_t size) {
-  p->data = buffer;
-  p->allocsize = p->size = size;
-}
-#endif /*LODEPNG_COMPILE_ZLIB*/
-
-#if (defined(LODEPNG_COMPILE_PNG) && defined(LODEPNG_COMPILE_ANCILLARY_CHUNKS)) || defined(LODEPNG_COMPILE_ENCODER)
-/*returns 1 if success, 0 if failure ==> nothing done*/
-static unsigned ucvector_push_back(ucvector* p, unsigned char c) {
-  if(!ucvector_resize(p, p->size + 1)) return 0;
-  p->data[p->size - 1] = c;
-  return 1;
-}
-#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/
-
-
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#ifdef LODEPNG_COMPILE_PNG
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-
-/*free string pointer and set it to NULL*/
-static void string_cleanup(char** out) {
-  lodepng_free(*out);
-  *out = NULL;
-}
-
-/* dynamically allocates a new string with a copy of the null terminated input text */
-static char* alloc_string(const char* in) {
-  size_t insize = strlen(in);
-  char* out = (char*)lodepng_malloc(insize + 1);
-  if(out) {
-    size_t i;
-    for(i = 0; i != insize; ++i) {
-      out[i] = in[i];
-    }
-    out[i] = 0;
-  }
-  return out;
-}
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-#endif /*LODEPNG_COMPILE_PNG*/
-
-/* ////////////////////////////////////////////////////////////////////////// */
-
-unsigned lodepng_read32bitInt(const unsigned char* buffer) {
-  return (unsigned)((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]);
-}
-
-#if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)
-/*buffer must have at least 4 allocated bytes available*/
-static void lodepng_set32bitInt(unsigned char* buffer, unsigned value) {
-  buffer[0] = (unsigned char)((value >> 24) & 0xff);
-  buffer[1] = (unsigned char)((value >> 16) & 0xff);
-  buffer[2] = (unsigned char)((value >>  8) & 0xff);
-  buffer[3] = (unsigned char)((value      ) & 0xff);
-}
-#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/
-
-#ifdef LODEPNG_COMPILE_ENCODER
-static void lodepng_add32bitInt(ucvector* buffer, unsigned value) {
-  ucvector_resize(buffer, buffer->size + 4); /*todo: give error if resize failed*/
-  lodepng_set32bitInt(&buffer->data[buffer->size - 4], value);
-}
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / File IO                                                                / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#ifdef LODEPNG_COMPILE_DISK
-
-/* returns negative value on error. This should be pure C compatible, so no fstat. */
-static long lodepng_filesize(const char* filename) {
-  FILE* file;
-  long size;
-  file = fopen(filename, "rb");
-  if(!file) return -1;
-
-  if(fseek(file, 0, SEEK_END) != 0) {
-    fclose(file);
-    return -1;
-  }
-
-  size = ftell(file);
-  /* It may give LONG_MAX as directory size, this is invalid for us. */
-  if(size == LONG_MAX) size = -1;
-
-  fclose(file);
-  return size;
-}
-
-/* load file into buffer that already has the correct allocated size. Returns error code.*/
-static unsigned lodepng_buffer_file(unsigned char* out, size_t size, const char* filename) {
-  FILE* file;
-  size_t readsize;
-  file = fopen(filename, "rb");
-  if(!file) return 78;
-
-  readsize = fread(out, 1, size, file);
-  fclose(file);
-
-  if (readsize != size) return 78;
-  return 0;
-}
-
-unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename) {
-  long size = lodepng_filesize(filename);
-  if (size < 0) return 78;
-  *outsize = (size_t)size;
-
-  *out = (unsigned char*)lodepng_malloc((size_t)size);
-  if(!(*out) && size > 0) return 83; /*the above malloc failed*/
-
-  return lodepng_buffer_file(*out, (size_t)size, filename);
-}
-
-/*write given buffer to the file, overwriting the file, it doesn't append to it.*/
-unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename) {
-  FILE* file;
-  file = fopen(filename, "wb" );
-  if(!file) return 79;
-  fwrite(buffer, 1, buffersize, file);
-  fclose(file);
-  return 0;
-}
-
-#endif /*LODEPNG_COMPILE_DISK*/
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* // End of common code and tools. Begin of Zlib related code.            // */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#ifdef LODEPNG_COMPILE_ZLIB
-#ifdef LODEPNG_COMPILE_ENCODER
-/*TODO: this ignores potential out of memory errors*/
-#define addBitToStream(/*size_t**/ bitpointer, /*ucvector**/ bitstream, /*unsigned char*/ bit){\
-  /*add a new byte at the end*/\
-  if(((*bitpointer) & 7) == 0) ucvector_push_back(bitstream, (unsigned char)0);\
-  /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/\
-  (bitstream->data[bitstream->size - 1]) |= (bit << ((*bitpointer) & 0x7));\
-  ++(*bitpointer);\
-}
-
-static void addBitsToStream(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) {
-  size_t i;
-  for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> i) & 1));
-}
-
-static void addBitsToStreamReversed(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) {
-  size_t i;
-  for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> (nbits - 1 - i)) & 1));
-}
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-#ifdef LODEPNG_COMPILE_DECODER
-
-#define READBIT(bitpointer, bitstream) ((bitstream[bitpointer >> 3] >> (bitpointer & 0x7)) & (unsigned char)1)
-
-static unsigned char readBitFromStream(size_t* bitpointer, const unsigned char* bitstream) {
-  unsigned char result = (unsigned char)(READBIT(*bitpointer, bitstream));
-  ++(*bitpointer);
-  return result;
-}
-
-static unsigned readBitsFromStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) {
-  unsigned result = 0, i;
-  for(i = 0; i != nbits; ++i) {
-    result += ((unsigned)READBIT(*bitpointer, bitstream)) << i;
-    ++(*bitpointer);
-  }
-  return result;
-}
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Deflate - Huffman                                                      / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#define FIRST_LENGTH_CODE_INDEX 257
-#define LAST_LENGTH_CODE_INDEX 285
-/*256 literals, the end code, some length codes, and 2 unused codes*/
-#define NUM_DEFLATE_CODE_SYMBOLS 288
-/*the distance codes have their own symbols, 30 used, 2 unused*/
-#define NUM_DISTANCE_SYMBOLS 32
-/*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/
-#define NUM_CODE_LENGTH_CODES 19
-
-/*the base lengths represented by codes 257-285*/
-static const unsigned LENGTHBASE[29]
-  = {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};
-
-/*the extra bits used by codes 257-285 (added to base length)*/
-static const unsigned LENGTHEXTRA[29]
-  = {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};
-
-/*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/
-static const unsigned DISTANCEBASE[30]
-  = {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};
-
-/*the extra bits of backwards distances (added to base)*/
-static const unsigned DISTANCEEXTRA[30]
-  = {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};
-
-/*the order in which "code length alphabet code lengths" are stored, out of this
-the huffman tree of the dynamic huffman tree lengths is generated*/
-static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES]
-  = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
-
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*
-Huffman tree struct, containing multiple representations of the tree
-*/
-typedef struct HuffmanTree {
-  unsigned* tree2d;
-  unsigned* tree1d;
-  unsigned* lengths; /*the lengths of the codes of the 1d-tree*/
-  unsigned maxbitlen; /*maximum number of bits a single code can get*/
-  unsigned numcodes; /*number of symbols in the alphabet = number of codes*/
-} HuffmanTree;
-
-/*function used for debug purposes to draw the tree in ascii art with C++*/
-/*
-static void HuffmanTree_draw(HuffmanTree* tree) {
-  std::cout << "tree. length: " << tree->numcodes << " maxbitlen: " << tree->maxbitlen << std::endl;
-  for(size_t i = 0; i != tree->tree1d.size; ++i) {
-    if(tree->lengths.data[i])
-      std::cout << i << " " << tree->tree1d.data[i] << " " << tree->lengths.data[i] << std::endl;
-  }
-  std::cout << std::endl;
-}*/
-
-static void HuffmanTree_init(HuffmanTree* tree) {
-  tree->tree2d = 0;
-  tree->tree1d = 0;
-  tree->lengths = 0;
-}
-
-static void HuffmanTree_cleanup(HuffmanTree* tree) {
-  lodepng_free(tree->tree2d);
-  lodepng_free(tree->tree1d);
-  lodepng_free(tree->lengths);
-}
-
-/*the tree representation used by the decoder. return value is error*/
-static unsigned HuffmanTree_make2DTree(HuffmanTree* tree) {
-  unsigned nodefilled = 0; /*up to which node it is filled*/
-  unsigned treepos = 0; /*position in the tree (1 of the numcodes columns)*/
-  unsigned n, i;
-
-  tree->tree2d = (unsigned*)lodepng_malloc(tree->numcodes * 2 * sizeof(unsigned));
-  if(!tree->tree2d) return 83; /*alloc fail*/
-
-  /*
-  convert tree1d[] to tree2d[][]. In the 2D array, a value of 32767 means
-  uninited, a value >= numcodes is an address to another bit, a value < numcodes
-  is a code. The 2 rows are the 2 possible bit values (0 or 1), there are as
-  many columns as codes - 1.
-  A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes.
-  Here, the internal nodes are stored (what their 0 and 1 option point to).
-  There is only memory for such good tree currently, if there are more nodes
-  (due to too long length codes), error 55 will happen
-  */
-  for(n = 0; n < tree->numcodes * 2; ++n) {
-    tree->tree2d[n] = 32767; /*32767 here means the tree2d isn't filled there yet*/
-  }
-
-  for(n = 0; n < tree->numcodes; ++n) /*the codes*/ {
-    for(i = 0; i != tree->lengths[n]; ++i) /*the bits for this code*/ {
-      unsigned char bit = (unsigned char)((tree->tree1d[n] >> (tree->lengths[n] - i - 1)) & 1);
-      /*oversubscribed, see comment in lodepng_error_text*/
-      if(treepos > 2147483647 || treepos + 2 > tree->numcodes) return 55;
-      if(tree->tree2d[2 * treepos + bit] == 32767) /*not yet filled in*/ {
-        if(i + 1 == tree->lengths[n]) /*last bit*/ {
-          tree->tree2d[2 * treepos + bit] = n; /*put the current code in it*/
-          treepos = 0;
-        } else {
-          /*put address of the next step in here, first that address has to be found of course
-          (it's just nodefilled + 1)...*/
-          ++nodefilled;
-          /*addresses encoded with numcodes added to it*/
-          tree->tree2d[2 * treepos + bit] = nodefilled + tree->numcodes;
-          treepos = nodefilled;
-        }
-      }
-      else treepos = tree->tree2d[2 * treepos + bit] - tree->numcodes;
-    }
-  }
-
-  for(n = 0; n < tree->numcodes * 2; ++n) {
-    if(tree->tree2d[n] == 32767) tree->tree2d[n] = 0; /*remove possible remaining 32767's*/
-  }
-
-  return 0;
-}
-
-/*
-Second step for the ...makeFromLengths and ...makeFromFrequencies functions.
-numcodes, lengths and maxbitlen must already be filled in correctly. return
-value is error.
-*/
-static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree) {
-  uivector blcount;
-  uivector nextcode;
-  unsigned error = 0;
-  unsigned bits, n;
-
-  uivector_init(&blcount);
-  uivector_init(&nextcode);
-
-  tree->tree1d = (unsigned*)lodepng_malloc(tree->numcodes * sizeof(unsigned));
-  if(!tree->tree1d) error = 83; /*alloc fail*/
-
-  if(!uivector_resizev(&blcount, tree->maxbitlen + 1, 0)
-  || !uivector_resizev(&nextcode, tree->maxbitlen + 1, 0))
-    error = 83; /*alloc fail*/
-
-  if(!error) {
-    /*step 1: count number of instances of each code length*/
-    for(bits = 0; bits != tree->numcodes; ++bits) ++blcount.data[tree->lengths[bits]];
-    /*step 2: generate the nextcode values*/
-    for(bits = 1; bits <= tree->maxbitlen; ++bits) {
-      nextcode.data[bits] = (nextcode.data[bits - 1] + blcount.data[bits - 1]) << 1;
-    }
-    /*step 3: generate all the codes*/
-    for(n = 0; n != tree->numcodes; ++n) {
-      if(tree->lengths[n] != 0) tree->tree1d[n] = nextcode.data[tree->lengths[n]]++;
-    }
-  }
-
-  uivector_cleanup(&blcount);
-  uivector_cleanup(&nextcode);
-
-  if(!error) return HuffmanTree_make2DTree(tree);
-  else return error;
-}
-
-/*
-given the code lengths (as stored in the PNG file), generate the tree as defined
-by Deflate. maxbitlen is the maximum bits that a code in the tree can have.
-return value is error.
-*/
-static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen,
-                                            size_t numcodes, unsigned maxbitlen) {
-  unsigned i;
-  tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned));
-  if(!tree->lengths) return 83; /*alloc fail*/
-  for(i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i];
-  tree->numcodes = (unsigned)numcodes; /*number of symbols*/
-  tree->maxbitlen = maxbitlen;
-  return HuffmanTree_makeFromLengths2(tree);
-}
-
-#ifdef LODEPNG_COMPILE_ENCODER
-
-/*BPM: Boundary Package Merge, see "A Fast and Space-Economical Algorithm for Length-Limited Coding",
-Jyrki Katajainen, Alistair Moffat, Andrew Turpin, 1995.*/
-
-/*chain node for boundary package merge*/
-typedef struct BPMNode {
-  int weight; /*the sum of all weights in this chain*/
-  unsigned index; /*index of this leaf node (called "count" in the paper)*/
-  struct BPMNode* tail; /*the next nodes in this chain (null if last)*/
-  int in_use;
-} BPMNode;
-
-/*lists of chains*/
-typedef struct BPMLists {
-  /*memory pool*/
-  unsigned memsize;
-  BPMNode* memory;
-  unsigned numfree;
-  unsigned nextfree;
-  BPMNode** freelist;
-  /*two heads of lookahead chains per list*/
-  unsigned listsize;
-  BPMNode** chains0;
-  BPMNode** chains1;
-} BPMLists;
-
-/*creates a new chain node with the given parameters, from the memory in the lists */
-static BPMNode* bpmnode_create(BPMLists* lists, int weight, unsigned index, BPMNode* tail) {
-  unsigned i;
-  BPMNode* result;
-
-  /*memory full, so garbage collect*/
-  if(lists->nextfree >= lists->numfree) {
-    /*mark only those that are in use*/
-    for(i = 0; i != lists->memsize; ++i) lists->memory[i].in_use = 0;
-    for(i = 0; i != lists->listsize; ++i) {
-      BPMNode* node;
-      for(node = lists->chains0[i]; node != 0; node = node->tail) node->in_use = 1;
-      for(node = lists->chains1[i]; node != 0; node = node->tail) node->in_use = 1;
-    }
-    /*collect those that are free*/
-    lists->numfree = 0;
-    for(i = 0; i != lists->memsize; ++i) {
-      if(!lists->memory[i].in_use) lists->freelist[lists->numfree++] = &lists->memory[i];
-    }
-    lists->nextfree = 0;
-  }
-
-  result = lists->freelist[lists->nextfree++];
-  result->weight = weight;
-  result->index = index;
-  result->tail = tail;
-  return result;
-}
-
-/*sort the leaves with stable mergesort*/
-static void bpmnode_sort(BPMNode* leaves, size_t num) {
-  BPMNode* mem = (BPMNode*)lodepng_malloc(sizeof(*leaves) * num);
-  size_t width, counter = 0;
-  for(width = 1; width < num; width *= 2) {
-    BPMNode* a = (counter & 1) ? mem : leaves;
-    BPMNode* b = (counter & 1) ? leaves : mem;
-    size_t p;
-    for(p = 0; p < num; p += 2 * width) {
-      size_t q = (p + width > num) ? num : (p + width);
-      size_t r = (p + 2 * width > num) ? num : (p + 2 * width);
-      size_t i = p, j = q, k;
-      for(k = p; k < r; k++) {
-        if(i < q && (j >= r || a[i].weight <= a[j].weight)) b[k] = a[i++];
-        else b[k] = a[j++];
-      }
-    }
-    counter++;
-  }
-  if(counter & 1) memcpy(leaves, mem, sizeof(*leaves) * num);
-  lodepng_free(mem);
-}
-
-/*Boundary Package Merge step, numpresent is the amount of leaves, and c is the current chain.*/
-static void boundaryPM(BPMLists* lists, BPMNode* leaves, size_t numpresent, int c, int num) {
-  unsigned lastindex = lists->chains1[c]->index;
-
-  if(c == 0) {
-    if(lastindex >= numpresent) return;
-    lists->chains0[c] = lists->chains1[c];
-    lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, 0);
-  } else {
-    /*sum of the weights of the head nodes of the previous lookahead chains.*/
-    int sum = lists->chains0[c - 1]->weight + lists->chains1[c - 1]->weight;
-    lists->chains0[c] = lists->chains1[c];
-    if(lastindex < numpresent && sum > leaves[lastindex].weight) {
-      lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, lists->chains1[c]->tail);
-      return;
-    }
-    lists->chains1[c] = bpmnode_create(lists, sum, lastindex, lists->chains1[c - 1]);
-    /*in the end we are only interested in the chain of the last list, so no
-    need to recurse if we're at the last one (this gives measurable speedup)*/
-    if(num + 1 < (int)(2 * numpresent - 2)) {
-      boundaryPM(lists, leaves, numpresent, c - 1, num);
-      boundaryPM(lists, leaves, numpresent, c - 1, num);
-    }
-  }
-}
-
-unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies,
-                                      size_t numcodes, unsigned maxbitlen) {
-  unsigned error = 0;
-  unsigned i;
-  size_t numpresent = 0; /*number of symbols with non-zero frequency*/
-  BPMNode* leaves; /*the symbols, only those with > 0 frequency*/
-
-  if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/
-  if((1u << maxbitlen) < (unsigned)numcodes) return 80; /*error: represent all symbols*/
-
-  leaves = (BPMNode*)lodepng_malloc(numcodes * sizeof(*leaves));
-  if(!leaves) return 83; /*alloc fail*/
-
-  for(i = 0; i != numcodes; ++i) {
-    if(frequencies[i] > 0) {
-      leaves[numpresent].weight = (int)frequencies[i];
-      leaves[numpresent].index = i;
-      ++numpresent;
-    }
-  }
-
-  for(i = 0; i != numcodes; ++i) lengths[i] = 0;
-
-  /*ensure at least two present symbols. There should be at least one symbol
-  according to RFC 1951 section 3.2.7. Some decoders incorrectly require two. To
-  make these work as well ensure there are at least two symbols. The
-  Package-Merge code below also doesn't work correctly if there's only one
-  symbol, it'd give it the theoritical 0 bits but in practice zlib wants 1 bit*/
-  if(numpresent == 0) {
-    lengths[0] = lengths[1] = 1; /*note that for RFC 1951 section 3.2.7, only lengths[0] = 1 is needed*/
-  } else if(numpresent == 1) {
-    lengths[leaves[0].index] = 1;
-    lengths[leaves[0].index == 0 ? 1 : 0] = 1;
-  } else {
-    BPMLists lists;
-    BPMNode* node;
-
-    bpmnode_sort(leaves, numpresent);
-
-    lists.listsize = maxbitlen;
-    lists.memsize = 2 * maxbitlen * (maxbitlen + 1);
-    lists.nextfree = 0;
-    lists.numfree = lists.memsize;
-    lists.memory = (BPMNode*)lodepng_malloc(lists.memsize * sizeof(*lists.memory));
-    lists.freelist = (BPMNode**)lodepng_malloc(lists.memsize * sizeof(BPMNode*));
-    lists.chains0 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*));
-    lists.chains1 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*));
-    if(!lists.memory || !lists.freelist || !lists.chains0 || !lists.chains1) error = 83; /*alloc fail*/
-
-    if(!error) {
-      for(i = 0; i != lists.memsize; ++i) lists.freelist[i] = &lists.memory[i];
-
-      bpmnode_create(&lists, leaves[0].weight, 1, 0);
-      bpmnode_create(&lists, leaves[1].weight, 2, 0);
-
-      for(i = 0; i != lists.listsize; ++i) {
-        lists.chains0[i] = &lists.memory[0];
-        lists.chains1[i] = &lists.memory[1];
-      }
-
-      /*each boundaryPM call adds one chain to the last list, and we need 2 * numpresent - 2 chains.*/
-      for(i = 2; i != 2 * numpresent - 2; ++i) boundaryPM(&lists, leaves, numpresent, (int)maxbitlen - 1, (int)i);
-
-      for(node = lists.chains1[maxbitlen - 1]; node; node = node->tail) {
-        for(i = 0; i != node->index; ++i) ++lengths[leaves[i].index];
-      }
-    }
-
-    lodepng_free(lists.memory);
-    lodepng_free(lists.freelist);
-    lodepng_free(lists.chains0);
-    lodepng_free(lists.chains1);
-  }
-
-  lodepng_free(leaves);
-  return error;
-}
-
-/*Create the Huffman tree given the symbol frequencies*/
-static unsigned HuffmanTree_makeFromFrequencies(HuffmanTree* tree, const unsigned* frequencies,
-                                                size_t mincodes, size_t numcodes, unsigned maxbitlen) {
-  unsigned error = 0;
-  while(!frequencies[numcodes - 1] && numcodes > mincodes) --numcodes; /*trim zeroes*/
-  tree->maxbitlen = maxbitlen;
-  tree->numcodes = (unsigned)numcodes; /*number of symbols*/
-  tree->lengths = (unsigned*)lodepng_realloc(tree->lengths, numcodes * sizeof(unsigned));
-  if(!tree->lengths) return 83; /*alloc fail*/
-  /*initialize all lengths to 0*/
-  memset(tree->lengths, 0, numcodes * sizeof(unsigned));
-
-  error = lodepng_huffman_code_lengths(tree->lengths, frequencies, numcodes, maxbitlen);
-  if(!error) error = HuffmanTree_makeFromLengths2(tree);
-  return error;
-}
-
-static unsigned HuffmanTree_getCode(const HuffmanTree* tree, unsigned index) {
-  return tree->tree1d[index];
-}
-
-static unsigned HuffmanTree_getLength(const HuffmanTree* tree, unsigned index) {
-  return tree->lengths[index];
-}
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-/*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/
-static unsigned generateFixedLitLenTree(HuffmanTree* tree) {
-  unsigned i, error = 0;
-  unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned));
-  if(!bitlen) return 83; /*alloc fail*/
-
-  /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/
-  for(i =   0; i <= 143; ++i) bitlen[i] = 8;
-  for(i = 144; i <= 255; ++i) bitlen[i] = 9;
-  for(i = 256; i <= 279; ++i) bitlen[i] = 7;
-  for(i = 280; i <= 287; ++i) bitlen[i] = 8;
-
-  error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15);
-
-  lodepng_free(bitlen);
-  return error;
-}
-
-/*get the distance code tree of a deflated block with fixed tree, as specified in the deflate specification*/
-static unsigned generateFixedDistanceTree(HuffmanTree* tree) {
-  unsigned i, error = 0;
-  unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned));
-  if(!bitlen) return 83; /*alloc fail*/
-
-  /*there are 32 distance codes, but 30-31 are unused*/
-  for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5;
-  error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15);
-
-  lodepng_free(bitlen);
-  return error;
-}
-
-#ifdef LODEPNG_COMPILE_DECODER
-
-/*
-returns the code, or (unsigned)(-1) if error happened
-inbitlength is the length of the complete buffer, in bits (so its byte length times 8)
-*/
-static unsigned huffmanDecodeSymbol(const unsigned char* in, size_t* bp,
-                                    const HuffmanTree* codetree, size_t inbitlength) {
-  unsigned treepos = 0, ct;
-  for(;;) {
-    if(*bp >= inbitlength) return (unsigned)(-1); /*error: end of input memory reached without endcode*/
-    /*
-    decode the symbol from the tree. The "readBitFromStream" code is inlined in
-    the expression below because this is the biggest bottleneck while decoding
-    */
-    ct = codetree->tree2d[(treepos << 1) + READBIT(*bp, in)];
-    ++(*bp);
-    if(ct < codetree->numcodes) return ct; /*the symbol is decoded, return it*/
-    else treepos = ct - codetree->numcodes; /*symbol not yet decoded, instead move tree position*/
-
-    if(treepos >= codetree->numcodes) return (unsigned)(-1); /*error: it appeared outside the codetree*/
-  }
-}
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-#ifdef LODEPNG_COMPILE_DECODER
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Inflator (Decompressor)                                                / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*get the tree of a deflated block with fixed tree, as specified in the deflate specification*/
-static void getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d) {
-  /*TODO: check for out of memory errors*/
-  generateFixedLitLenTree(tree_ll);
-  generateFixedDistanceTree(tree_d);
-}
-
-/*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/
-static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d,
-                                      const unsigned char* in, size_t* bp, size_t inlength) {
-  /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/
-  unsigned error = 0;
-  unsigned n, HLIT, HDIST, HCLEN, i;
-  size_t inbitlength = inlength * 8;
-
-  /*see comments in deflateDynamic for explanation of the context and these variables, it is analogous*/
-  unsigned* bitlen_ll = 0; /*lit,len code lengths*/
-  unsigned* bitlen_d = 0; /*dist code lengths*/
-  /*code length code lengths ("clcl"), the bit lengths of the huffman tree used to compress bitlen_ll and bitlen_d*/
-  unsigned* bitlen_cl = 0;
-  HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/
-
-  if((*bp) + 14 > (inlength << 3)) return 49; /*error: the bit pointer is or will go past the memory*/
-
-  /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/
-  HLIT =  readBitsFromStream(bp, in, 5) + 257;
-  /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/
-  HDIST = readBitsFromStream(bp, in, 5) + 1;
-  /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/
-  HCLEN = readBitsFromStream(bp, in, 4) + 4;
-
-  if((*bp) + HCLEN * 3 > (inlength << 3)) return 50; /*error: the bit pointer is or will go past the memory*/
-
-  HuffmanTree_init(&tree_cl);
-
-  while(!error) {
-    /*read the code length codes out of 3 * (amount of code length codes) bits*/
-
-    bitlen_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned));
-    if(!bitlen_cl) ERROR_BREAK(83 /*alloc fail*/);
-
-    for(i = 0; i != NUM_CODE_LENGTH_CODES; ++i) {
-      if(i < HCLEN) bitlen_cl[CLCL_ORDER[i]] = readBitsFromStream(bp, in, 3);
-      else bitlen_cl[CLCL_ORDER[i]] = 0; /*if not, it must stay 0*/
-    }
-
-    error = HuffmanTree_makeFromLengths(&tree_cl, bitlen_cl, NUM_CODE_LENGTH_CODES, 7);
-    if(error) break;
-
-    /*now we can use this tree to read the lengths for the tree that this function will return*/
-    bitlen_ll = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned));
-    bitlen_d = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned));
-    if(!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/);
-    for(i = 0; i != NUM_DEFLATE_CODE_SYMBOLS; ++i) bitlen_ll[i] = 0;
-    for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen_d[i] = 0;
-
-    /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/
-    i = 0;
-    while(i < HLIT + HDIST) {
-      unsigned code = huffmanDecodeSymbol(in, bp, &tree_cl, inbitlength);
-      if(code <= 15) /*a length code*/ {
-        if(i < HLIT) bitlen_ll[i] = code;
-        else bitlen_d[i - HLIT] = code;
-        ++i;
-      } else if(code == 16) /*repeat previous*/ {
-        unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/
-        unsigned value; /*set value to the previous code*/
-
-        if(i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/
-
-        if((*bp + 2) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/
-        replength += readBitsFromStream(bp, in, 2);
-
-        if(i < HLIT + 1) value = bitlen_ll[i - 1];
-        else value = bitlen_d[i - HLIT - 1];
-        /*repeat this value in the next lengths*/
-        for(n = 0; n < replength; ++n) {
-          if(i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/
-          if(i < HLIT) bitlen_ll[i] = value;
-          else bitlen_d[i - HLIT] = value;
-          ++i;
-        }
-      } else if(code == 17) /*repeat "0" 3-10 times*/ {
-        unsigned replength = 3; /*read in the bits that indicate repeat length*/
-        if((*bp + 3) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/
-        replength += readBitsFromStream(bp, in, 3);
-
-        /*repeat this value in the next lengths*/
-        for(n = 0; n < replength; ++n) {
-          if(i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/
-
-          if(i < HLIT) bitlen_ll[i] = 0;
-          else bitlen_d[i - HLIT] = 0;
-          ++i;
-        }
-      } else if(code == 18) /*repeat "0" 11-138 times*/ {
-        unsigned replength = 11; /*read in the bits that indicate repeat length*/
-        if((*bp + 7) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/
-        replength += readBitsFromStream(bp, in, 7);
-
-        /*repeat this value in the next lengths*/
-        for(n = 0; n < replength; ++n) {
-          if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/
-
-          if(i < HLIT) bitlen_ll[i] = 0;
-          else bitlen_d[i - HLIT] = 0;
-          ++i;
-        }
-      } else /*if(code == (unsigned)(-1))*/ /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ {
-        if(code == (unsigned)(-1)) {
-          /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol
-          (10=no endcode, 11=wrong jump outside of tree)*/
-          error = (*bp) > inbitlength ? 10 : 11;
-        }
-        else error = 16; /*unexisting code, this can never happen*/
-        break;
-      }
-    }
-    if(error) break;
-
-    if(bitlen_ll[256] == 0) ERROR_BREAK(64); /*the length of the end code 256 must be larger than 0*/
-
-    /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/
-    error = HuffmanTree_makeFromLengths(tree_ll, bitlen_ll, NUM_DEFLATE_CODE_SYMBOLS, 15);
-    if(error) break;
-    error = HuffmanTree_makeFromLengths(tree_d, bitlen_d, NUM_DISTANCE_SYMBOLS, 15);
-
-    break; /*end of error-while*/
-  }
-
-  lodepng_free(bitlen_cl);
-  lodepng_free(bitlen_ll);
-  lodepng_free(bitlen_d);
-  HuffmanTree_cleanup(&tree_cl);
-
-  return error;
-}
-
-/*inflate a block with dynamic of fixed Huffman tree*/
-static unsigned inflateHuffmanBlock(ucvector* out, const unsigned char* in, size_t* bp,
-                                    size_t* pos, size_t inlength, unsigned btype) {
-  unsigned error = 0;
-  HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/
-  HuffmanTree tree_d; /*the huffman tree for distance codes*/
-  size_t inbitlength = inlength * 8;
-
-  HuffmanTree_init(&tree_ll);
-  HuffmanTree_init(&tree_d);
-
-  if(btype == 1) getTreeInflateFixed(&tree_ll, &tree_d);
-  else if(btype == 2) error = getTreeInflateDynamic(&tree_ll, &tree_d, in, bp, inlength);
-
-  while(!error) /*decode all symbols until end reached, breaks at end code*/ {
-    /*code_ll is literal, length or end code*/
-    unsigned code_ll = huffmanDecodeSymbol(in, bp, &tree_ll, inbitlength);
-    if(code_ll <= 255) /*literal symbol*/ {
-      /*ucvector_push_back would do the same, but for some reason the two lines below run 10% faster*/
-      if(!ucvector_resize(out, (*pos) + 1)) ERROR_BREAK(83 /*alloc fail*/);
-      out->data[*pos] = (unsigned char)code_ll;
-      ++(*pos);
-    } else if(code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ {
-      unsigned code_d, distance;
-      unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/
-      size_t start, forward, backward, length;
-
-      /*part 1: get length base*/
-      length = LENGTHBASE[code_ll - FIRST_LENGTH_CODE_INDEX];
-
-      /*part 2: get extra bits and add the value of that to length*/
-      numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX];
-      if((*bp + numextrabits_l) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/
-      length += readBitsFromStream(bp, in, numextrabits_l);
-
-      /*part 3: get distance code*/
-      code_d = huffmanDecodeSymbol(in, bp, &tree_d, inbitlength);
-      if(code_d > 29) {
-        if(code_d == (unsigned)(-1)) /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ {
-          /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol
-          (10=no endcode, 11=wrong jump outside of tree)*/
-          error = (*bp) > inlength * 8 ? 10 : 11;
-        }
-        else error = 18; /*error: invalid distance code (30-31 are never used)*/
-        break;
-      }
-      distance = DISTANCEBASE[code_d];
-
-      /*part 4: get extra bits from distance*/
-      numextrabits_d = DISTANCEEXTRA[code_d];
-      if((*bp + numextrabits_d) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/
-      distance += readBitsFromStream(bp, in, numextrabits_d);
-
-      /*part 5: fill in all the out[n] values based on the length and dist*/
-      start = (*pos);
-      if(distance > start) ERROR_BREAK(52); /*too long backward distance*/
-      backward = start - distance;
-
-      if(!ucvector_resize(out, (*pos) + length)) ERROR_BREAK(83 /*alloc fail*/);
-      if (distance < length) {
-        for(forward = 0; forward < length; ++forward) {
-          out->data[(*pos)++] = out->data[backward++];
-        }
-      } else {
-        memcpy(out->data + *pos, out->data + backward, length);
-        *pos += length;
-      }
-    } else if(code_ll == 256) {
-      break; /*end code, break the loop*/
-    } else /*if(code == (unsigned)(-1))*/ /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ {
-      /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol
-      (10=no endcode, 11=wrong jump outside of tree)*/
-      error = ((*bp) > inlength * 8) ? 10 : 11;
-      break;
-    }
-  }
-
-  HuffmanTree_cleanup(&tree_ll);
-  HuffmanTree_cleanup(&tree_d);
-
-  return error;
-}
-
-static unsigned inflateNoCompression(ucvector* out, const unsigned char* in, size_t* bp, size_t* pos, size_t inlength) {
-  size_t p;
-  unsigned LEN, NLEN, n, error = 0;
-
-  /*go to first boundary of byte*/
-  while(((*bp) & 0x7) != 0) ++(*bp);
-  p = (*bp) / 8; /*byte position*/
-
-  /*read LEN (2 bytes) and NLEN (2 bytes)*/
-  if(p + 4 >= inlength) return 52; /*error, bit pointer will jump past memory*/
-  LEN = in[p] + 256u * in[p + 1]; p += 2;
-  NLEN = in[p] + 256u * in[p + 1]; p += 2;
-
-  /*check if 16-bit NLEN is really the one's complement of LEN*/
-  if(LEN + NLEN != 65535) return 21; /*error: NLEN is not one's complement of LEN*/
-
-  if(!ucvector_resize(out, (*pos) + LEN)) return 83; /*alloc fail*/
-
-  /*read the literal data: LEN bytes are now stored in the out buffer*/
-  if(p + LEN > inlength) return 23; /*error: reading outside of in buffer*/
-  for(n = 0; n < LEN; ++n) out->data[(*pos)++] = in[p++];
-
-  (*bp) = p * 8;
-
-  return error;
-}
-
-static unsigned lodepng_inflatev(ucvector* out,
-                                 const unsigned char* in, size_t insize,
-                                 const LodePNGDecompressSettings* settings) {
-  /*bit pointer in the "in" data, current byte is bp >> 3, current bit is bp & 0x7 (from lsb to msb of the byte)*/
-  size_t bp = 0;
-  unsigned BFINAL = 0;
-  size_t pos = 0; /*byte position in the out buffer*/
-  unsigned error = 0;
-
-  (void)settings;
-
-  while(!BFINAL) {
-    unsigned BTYPE;
-    if(bp + 2 >= insize * 8) return 52; /*error, bit pointer will jump past memory*/
-    BFINAL = readBitFromStream(&bp, in);
-    BTYPE = 1u * readBitFromStream(&bp, in);
-    BTYPE += 2u * readBitFromStream(&bp, in);
-
-    if(BTYPE == 3) return 20; /*error: invalid BTYPE*/
-    else if(BTYPE == 0) error = inflateNoCompression(out, in, &bp, &pos, insize); /*no compression*/
-    else error = inflateHuffmanBlock(out, in, &bp, &pos, insize, BTYPE); /*compression, BTYPE 01 or 10*/
-
-    if(error) return error;
-  }
-
-  return error;
-}
-
-unsigned lodepng_inflate(unsigned char** out, size_t* outsize,
-                         const unsigned char* in, size_t insize,
-                         const LodePNGDecompressSettings* settings) {
-  unsigned error;
-  ucvector v;
-  ucvector_init_buffer(&v, *out, *outsize);
-  error = lodepng_inflatev(&v, in, insize, settings);
-  *out = v.data;
-  *outsize = v.size;
-  return error;
-}
-
-static unsigned inflate(unsigned char** out, size_t* outsize,
-                        const unsigned char* in, size_t insize,
-                        const LodePNGDecompressSettings* settings) {
-  if(settings->custom_inflate) {
-    return settings->custom_inflate(out, outsize, in, insize, settings);
-  } else {
-    return lodepng_inflate(out, outsize, in, insize, settings);
-  }
-}
-
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-#ifdef LODEPNG_COMPILE_ENCODER
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Deflator (Compressor)                                                  / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-static const size_t MAX_SUPPORTED_DEFLATE_LENGTH = 258;
-
-/*bitlen is the size in bits of the code*/
-static void addHuffmanSymbol(size_t* bp, ucvector* compressed, unsigned code, unsigned bitlen) {
-  addBitsToStreamReversed(bp, compressed, code, bitlen);
-}
-
-/*search the index in the array, that has the largest value smaller than or equal to the given value,
-given array must be sorted (if no value is smaller, it returns the size of the given array)*/
-static size_t searchCodeIndex(const unsigned* array, size_t array_size, size_t value) {
-  /*binary search (only small gain over linear). TODO: use CPU log2 instruction for getting symbols instead*/
-  size_t left = 1;
-  size_t right = array_size - 1;
-
-  while(left <= right) {
-    size_t mid = (left + right) >> 1;
-    if (array[mid] >= value) right = mid - 1;
-    else left = mid + 1;
-  }
-  if(left >= array_size || array[left] > value) left--;
-  return left;
-}
-
-static void addLengthDistance(uivector* values, size_t length, size_t distance) {
-  /*values in encoded vector are those used by deflate:
-  0-255: literal bytes
-  256: end
-  257-285: length/distance pair (length code, followed by extra length bits, distance code, extra distance bits)
-  286-287: invalid*/
-
-  unsigned length_code = (unsigned)searchCodeIndex(LENGTHBASE, 29, length);
-  unsigned extra_length = (unsigned)(length - LENGTHBASE[length_code]);
-  unsigned dist_code = (unsigned)searchCodeIndex(DISTANCEBASE, 30, distance);
-  unsigned extra_distance = (unsigned)(distance - DISTANCEBASE[dist_code]);
-
-  uivector_push_back(values, length_code + FIRST_LENGTH_CODE_INDEX);
-  uivector_push_back(values, extra_length);
-  uivector_push_back(values, dist_code);
-  uivector_push_back(values, extra_distance);
-}
-
-/*3 bytes of data get encoded into two bytes. The hash cannot use more than 3
-bytes as input because 3 is the minimum match length for deflate*/
-static const unsigned HASH_NUM_VALUES = 65536;
-static const unsigned HASH_BIT_MASK = 65535; /*HASH_NUM_VALUES - 1, but C90 does not like that as initializer*/
-
-typedef struct Hash {
-  int* head; /*hash value to head circular pos - can be outdated if went around window*/
-  /*circular pos to prev circular pos*/
-  unsigned short* chain;
-  int* val; /*circular pos to hash value*/
-
-  /*TODO: do this not only for zeros but for any repeated byte. However for PNG
-  it's always going to be the zeros that dominate, so not important for PNG*/
-  int* headz; /*similar to head, but for chainz*/
-  unsigned short* chainz; /*those with same amount of zeros*/
-  unsigned short* zeros; /*length of zeros streak, used as a second hash chain*/
-} Hash;
-
-static unsigned hash_init(Hash* hash, unsigned windowsize) {
-  unsigned i;
-  hash->head = (int*)lodepng_malloc(sizeof(int) * HASH_NUM_VALUES);
-  hash->val = (int*)lodepng_malloc(sizeof(int) * windowsize);
-  hash->chain = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize);
-
-  hash->zeros = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize);
-  hash->headz = (int*)lodepng_malloc(sizeof(int) * (MAX_SUPPORTED_DEFLATE_LENGTH + 1));
-  hash->chainz = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize);
-
-  if(!hash->head || !hash->chain || !hash->val  || !hash->headz|| !hash->chainz || !hash->zeros) {
-    return 83; /*alloc fail*/
-  }
-
-  /*initialize hash table*/
-  for(i = 0; i != HASH_NUM_VALUES; ++i) hash->head[i] = -1;
-  for(i = 0; i != windowsize; ++i) hash->val[i] = -1;
-  for(i = 0; i != windowsize; ++i) hash->chain[i] = i; /*same value as index indicates uninitialized*/
-
-  for(i = 0; i <= MAX_SUPPORTED_DEFLATE_LENGTH; ++i) hash->headz[i] = -1;
-  for(i = 0; i != windowsize; ++i) hash->chainz[i] = i; /*same value as index indicates uninitialized*/
-
-  return 0;
-}
-
-static void hash_cleanup(Hash* hash) {
-  lodepng_free(hash->head);
-  lodepng_free(hash->val);
-  lodepng_free(hash->chain);
-
-  lodepng_free(hash->zeros);
-  lodepng_free(hash->headz);
-  lodepng_free(hash->chainz);
-}
-
-
-
-static unsigned getHash(const unsigned char* data, size_t size, size_t pos) {
-  unsigned result = 0;
-  if(pos + 2 < size) {
-    /*A simple shift and xor hash is used. Since the data of PNGs is dominated
-    by zeroes due to the filters, a better hash does not have a significant
-    effect on speed in traversing the chain, and causes more time spend on
-    calculating the hash.*/
-    result ^= (unsigned)(data[pos + 0] << 0u);
-    result ^= (unsigned)(data[pos + 1] << 4u);
-    result ^= (unsigned)(data[pos + 2] << 8u);
-  } else {
-    size_t amount, i;
-    if(pos >= size) return 0;
-    amount = size - pos;
-    for(i = 0; i != amount; ++i) result ^= (unsigned)(data[pos + i] << (i * 8u));
-  }
-  return result & HASH_BIT_MASK;
-}
-
-static unsigned countZeros(const unsigned char* data, size_t size, size_t pos) {
-  const unsigned char* start = data + pos;
-  const unsigned char* end = start + MAX_SUPPORTED_DEFLATE_LENGTH;
-  if(end > data + size) end = data + size;
-  data = start;
-  while(data != end && *data == 0) ++data;
-  /*subtracting two addresses returned as 32-bit number (max value is MAX_SUPPORTED_DEFLATE_LENGTH)*/
-  return (unsigned)(data - start);
-}
-
-/*wpos = pos & (windowsize - 1)*/
-static void updateHashChain(Hash* hash, size_t wpos, unsigned hashval, unsigned short numzeros) {
-  hash->val[wpos] = (int)hashval;
-  if(hash->head[hashval] != -1) hash->chain[wpos] = hash->head[hashval];
-  hash->head[hashval] = (int)wpos;
-
-  hash->zeros[wpos] = numzeros;
-  if(hash->headz[numzeros] != -1) hash->chainz[wpos] = hash->headz[numzeros];
-  hash->headz[numzeros] = (int)wpos;
-}
-
-/*
-LZ77-encode the data. Return value is error code. The input are raw bytes, the output
-is in the form of unsigned integers with codes representing for example literal bytes, or
-length/distance pairs.
-It uses a hash table technique to let it encode faster. When doing LZ77 encoding, a
-sliding window (of windowsize) is used, and all past bytes in that window can be used as
-the "dictionary". A brute force search through all possible distances would be slow, and
-this hash technique is one out of several ways to speed this up.
-*/
-static unsigned encodeLZ77(uivector* out, Hash* hash,
-                           const unsigned char* in, size_t inpos, size_t insize, unsigned windowsize,
-                           unsigned minmatch, unsigned nicematch, unsigned lazymatching) {
-  size_t pos;
-  unsigned i, error = 0;
-  /*for large window lengths, assume the user wants no compression loss. Otherwise, max hash chain length speedup.*/
-  unsigned maxchainlength = windowsize >= 8192 ? windowsize : windowsize / 8;
-  unsigned maxlazymatch = windowsize >= 8192 ? MAX_SUPPORTED_DEFLATE_LENGTH : 64;
-
-  unsigned usezeros = 1; /*not sure if setting it to false for windowsize < 8192 is better or worse*/
-  unsigned numzeros = 0;
-
-  unsigned offset; /*the offset represents the distance in LZ77 terminology*/
-  unsigned length;
-  unsigned lazy = 0;
-  unsigned lazylength = 0, lazyoffset = 0;
-  unsigned hashval;
-  unsigned current_offset, current_length;
-  unsigned prev_offset;
-  const unsigned char *lastptr, *foreptr, *backptr;
-  unsigned hashpos;
-
-  if(windowsize == 0 || windowsize > 32768) return 60; /*error: windowsize smaller/larger than allowed*/
-  if((windowsize & (windowsize - 1)) != 0) return 90; /*error: must be power of two*/
-
-  if(nicematch > MAX_SUPPORTED_DEFLATE_LENGTH) nicematch = MAX_SUPPORTED_DEFLATE_LENGTH;
-
-  for(pos = inpos; pos < insize; ++pos) {
-    size_t wpos = pos & (windowsize - 1); /*position for in 'circular' hash buffers*/
-    unsigned chainlength = 0;
-
-    hashval = getHash(in, insize, pos);
-
-    if(usezeros && hashval == 0) {
-      if(numzeros == 0) numzeros = countZeros(in, insize, pos);
-      else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros;
-    } else {
-      numzeros = 0;
-    }
-
-    updateHashChain(hash, wpos, hashval, numzeros);
-
-    /*the length and offset found for the current position*/
-    length = 0;
-    offset = 0;
-
-    hashpos = hash->chain[wpos];
-
-    lastptr = &in[insize < pos + MAX_SUPPORTED_DEFLATE_LENGTH ? insize : pos + MAX_SUPPORTED_DEFLATE_LENGTH];
-
-    /*search for the longest string*/
-    prev_offset = 0;
-    for(;;) {
-      if(chainlength++ >= maxchainlength) break;
-      current_offset = (unsigned)(hashpos <= wpos ? wpos - hashpos : wpos - hashpos + windowsize);
-
-      if(current_offset < prev_offset) break; /*stop when went completely around the circular buffer*/
-      prev_offset = current_offset;
-      if(current_offset > 0) {
-        /*test the next characters*/
-        foreptr = &in[pos];
-        backptr = &in[pos - current_offset];
-
-        /*common case in PNGs is lots of zeros. Quickly skip over them as a speedup*/
-        if(numzeros >= 3) {
-          unsigned skip = hash->zeros[hashpos];
-          if(skip > numzeros) skip = numzeros;
-          backptr += skip;
-          foreptr += skip;
-        }
-
-        while(foreptr != lastptr && *backptr == *foreptr) /*maximum supported length by deflate is max length*/ {
-          ++backptr;
-          ++foreptr;
-        }
-        current_length = (unsigned)(foreptr - &in[pos]);
-
-        if(current_length > length) {
-          length = current_length; /*the longest length*/
-          offset = current_offset; /*the offset that is related to this longest length*/
-          /*jump out once a length of max length is found (speed gain). This also jumps
-          out if length is MAX_SUPPORTED_DEFLATE_LENGTH*/
-          if(current_length >= nicematch) break;
-        }
-      }
-
-      if(hashpos == hash->chain[hashpos]) break;
-
-      if(numzeros >= 3 && length > numzeros) {
-        hashpos = hash->chainz[hashpos];
-        if(hash->zeros[hashpos] != numzeros) break;
-      } else {
-        hashpos = hash->chain[hashpos];
-        /*outdated hash value, happens if particular value was not encountered in whole last window*/
-        if(hash->val[hashpos] != (int)hashval) break;
-      }
-    }
-
-    if(lazymatching) {
-      if(!lazy && length >= 3 && length <= maxlazymatch && length < MAX_SUPPORTED_DEFLATE_LENGTH) {
-        lazy = 1;
-        lazylength = length;
-        lazyoffset = offset;
-        continue; /*try the next byte*/
-      }
-      if(lazy) {
-        lazy = 0;
-        if(pos == 0) ERROR_BREAK(81);
-        if(length > lazylength + 1) {
-          /*push the previous character as literal*/
-          if(!uivector_push_back(out, in[pos - 1])) ERROR_BREAK(83 /*alloc fail*/);
-        } else {
-          length = lazylength;
-          offset = lazyoffset;
-          hash->head[hashval] = -1; /*the same hashchain update will be done, this ensures no wrong alteration*/
-          hash->headz[numzeros] = -1; /*idem*/
-          --pos;
-        }
-      }
-    }
-    if(length >= 3 && offset > windowsize) ERROR_BREAK(86 /*too big (or overflown negative) offset*/);
-
-    /*encode it as length/distance pair or literal value*/
-    if(length < 3) /*only lengths of 3 or higher are supported as length/distance pair*/ {
-      if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/);
-    } else if(length < minmatch || (length == 3 && offset > 4096)) {
-      /*compensate for the fact that longer offsets have more extra bits, a
-      length of only 3 may be not worth it then*/
-      if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/);
-    } else {
-      addLengthDistance(out, length, offset);
-      for(i = 1; i < length; ++i) {
-        ++pos;
-        wpos = pos & (windowsize - 1);
-        hashval = getHash(in, insize, pos);
-        if(usezeros && hashval == 0) {
-          if(numzeros == 0) numzeros = countZeros(in, insize, pos);
-          else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros;
-        } else {
-          numzeros = 0;
-        }
-        updateHashChain(hash, wpos, hashval, numzeros);
-      }
-    }
-  } /*end of the loop through each character of input*/
-
-  return error;
-}
-
-/* /////////////////////////////////////////////////////////////////////////// */
-
-static unsigned deflateNoCompression(ucvector* out, const unsigned char* data, size_t datasize) {
-  /*non compressed deflate block data: 1 bit BFINAL,2 bits BTYPE,(5 bits): it jumps to start of next byte,
-  2 bytes LEN, 2 bytes NLEN, LEN bytes literal DATA*/
-
-  size_t i, j, numdeflateblocks = (datasize + 65534) / 65535;
-  unsigned datapos = 0;
-  for(i = 0; i != numdeflateblocks; ++i) {
-    unsigned BFINAL, BTYPE, LEN, NLEN;
-    unsigned char firstbyte;
-
-    BFINAL = (i == numdeflateblocks - 1);
-    BTYPE = 0;
-
-    firstbyte = (unsigned char)(BFINAL + ((BTYPE & 1) << 1) + ((BTYPE & 2) << 1));
-    ucvector_push_back(out, firstbyte);
-
-    LEN = 65535;
-    if(datasize - datapos < 65535) LEN = (unsigned)datasize - datapos;
-    NLEN = 65535 - LEN;
-
-    ucvector_push_back(out, (unsigned char)(LEN & 255));
-    ucvector_push_back(out, (unsigned char)(LEN >> 8));
-    ucvector_push_back(out, (unsigned char)(NLEN & 255));
-    ucvector_push_back(out, (unsigned char)(NLEN >> 8));
-
-    /*Decompressed data*/
-    for(j = 0; j < 65535 && datapos < datasize; ++j) {
-      ucvector_push_back(out, data[datapos++]);
-    }
-  }
-
-  return 0;
-}
-
-/*
-write the lz77-encoded data, which has lit, len and dist codes, to compressed stream using huffman trees.
-tree_ll: the tree for lit and len codes.
-tree_d: the tree for distance codes.
-*/
-static void writeLZ77data(size_t* bp, ucvector* out, const uivector* lz77_encoded,
-                          const HuffmanTree* tree_ll, const HuffmanTree* tree_d) {
-  size_t i = 0;
-  for(i = 0; i != lz77_encoded->size; ++i) {
-    unsigned val = lz77_encoded->data[i];
-    addHuffmanSymbol(bp, out, HuffmanTree_getCode(tree_ll, val), HuffmanTree_getLength(tree_ll, val));
-    if(val > 256) /*for a length code, 3 more things have to be added*/ {
-      unsigned length_index = val - FIRST_LENGTH_CODE_INDEX;
-      unsigned n_length_extra_bits = LENGTHEXTRA[length_index];
-      unsigned length_extra_bits = lz77_encoded->data[++i];
-
-      unsigned distance_code = lz77_encoded->data[++i];
-
-      unsigned distance_index = distance_code;
-      unsigned n_distance_extra_bits = DISTANCEEXTRA[distance_index];
-      unsigned distance_extra_bits = lz77_encoded->data[++i];
-
-      addBitsToStream(bp, out, length_extra_bits, n_length_extra_bits);
-      addHuffmanSymbol(bp, out, HuffmanTree_getCode(tree_d, distance_code),
-                       HuffmanTree_getLength(tree_d, distance_code));
-      addBitsToStream(bp, out, distance_extra_bits, n_distance_extra_bits);
-    }
-  }
-}
-
-/*Deflate for a block of type "dynamic", that is, with freely, optimally, created huffman trees*/
-static unsigned deflateDynamic(ucvector* out, size_t* bp, Hash* hash,
-                               const unsigned char* data, size_t datapos, size_t dataend,
-                               const LodePNGCompressSettings* settings, unsigned final) {
-  unsigned error = 0;
-
-  /*
-  A block is compressed as follows: The PNG data is lz77 encoded, resulting in
-  literal bytes and length/distance pairs. This is then huffman compressed with
-  two huffman trees. One huffman tree is used for the lit and len values ("ll"),
-  another huffman tree is used for the dist values ("d"). These two trees are
-  stored using their code lengths, and to compress even more these code lengths
-  are also run-length encoded and huffman compressed. This gives a huffman tree
-  of code lengths "cl". The code lenghts used to describe this third tree are
-  the code length code lengths ("clcl").
-  */
-
-  /*The lz77 encoded data, represented with integers since there will also be length and distance codes in it*/
-  uivector lz77_encoded;
-  HuffmanTree tree_ll; /*tree for lit,len values*/
-  HuffmanTree tree_d; /*tree for distance codes*/
-  HuffmanTree tree_cl; /*tree for encoding the code lengths representing tree_ll and tree_d*/
-  uivector frequencies_ll; /*frequency of lit,len codes*/
-  uivector frequencies_d; /*frequency of dist codes*/
-  uivector frequencies_cl; /*frequency of code length codes*/
-  uivector bitlen_lld; /*lit,len,dist code lenghts (int bits), literally (without repeat codes).*/
-  uivector bitlen_lld_e; /*bitlen_lld encoded with repeat codes (this is a rudemtary run length compression)*/
-  /*bitlen_cl is the code length code lengths ("clcl"). The bit lengths of codes to represent tree_cl
-  (these are written as is in the file, it would be crazy to compress these using yet another huffman
-  tree that needs to be represented by yet another set of code lengths)*/
-  uivector bitlen_cl;
-  size_t datasize = dataend - datapos;
-
-  /*
-  Due to the huffman compression of huffman tree representations ("two levels"), there are some anologies:
-  bitlen_lld is to tree_cl what data is to tree_ll and tree_d.
-  bitlen_lld_e is to bitlen_lld what lz77_encoded is to data.
-  bitlen_cl is to bitlen_lld_e what bitlen_lld is to lz77_encoded.
-  */
-
-  unsigned BFINAL = final;
-  size_t numcodes_ll, numcodes_d, i;
-  unsigned HLIT, HDIST, HCLEN;
-
-  uivector_init(&lz77_encoded);
-  HuffmanTree_init(&tree_ll);
-  HuffmanTree_init(&tree_d);
-  HuffmanTree_init(&tree_cl);
-  uivector_init(&frequencies_ll);
-  uivector_init(&frequencies_d);
-  uivector_init(&frequencies_cl);
-  uivector_init(&bitlen_lld);
-  uivector_init(&bitlen_lld_e);
-  uivector_init(&bitlen_cl);
-
-  /*This while loop never loops due to a break at the end, it is here to
-  allow breaking out of it to the cleanup phase on error conditions.*/
-  while(!error) {
-    if(settings->use_lz77) {
-      error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize,
-                         settings->minmatch, settings->nicematch, settings->lazymatching);
-      if(error) break;
-    } else {
-      if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83 /*alloc fail*/);
-      for(i = datapos; i < dataend; ++i) lz77_encoded.data[i - datapos] = data[i]; /*no LZ77, but still will be Huffman compressed*/
-    }
-
-    if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83 /*alloc fail*/);
-    if(!uivector_resizev(&frequencies_d, 30, 0)) ERROR_BREAK(83 /*alloc fail*/);
-
-    /*Count the frequencies of lit, len and dist codes*/
-    for(i = 0; i != lz77_encoded.size; ++i) {
-      unsigned symbol = lz77_encoded.data[i];
-      ++frequencies_ll.data[symbol];
-      if(symbol > 256) {
-        unsigned dist = lz77_encoded.data[i + 2];
-        ++frequencies_d.data[dist];
-        i += 3;
-      }
-    }
-    frequencies_ll.data[256] = 1; /*there will be exactly 1 end code, at the end of the block*/
-
-    /*Make both huffman trees, one for the lit and len codes, one for the dist codes*/
-    error = HuffmanTree_makeFromFrequencies(&tree_ll, frequencies_ll.data, 257, frequencies_ll.size, 15);
-    if(error) break;
-    /*2, not 1, is chosen for mincodes: some buggy PNG decoders require at least 2 symbols in the dist tree*/
-    error = HuffmanTree_makeFromFrequencies(&tree_d, frequencies_d.data, 2, frequencies_d.size, 15);
-    if(error) break;
-
-    numcodes_ll = tree_ll.numcodes; if(numcodes_ll > 286) numcodes_ll = 286;
-    numcodes_d = tree_d.numcodes; if(numcodes_d > 30) numcodes_d = 30;
-    /*store the code lengths of both generated trees in bitlen_lld*/
-    for(i = 0; i != numcodes_ll; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_ll, (unsigned)i));
-    for(i = 0; i != numcodes_d; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_d, (unsigned)i));
-
-    /*run-length compress bitlen_ldd into bitlen_lld_e by using repeat codes 16 (copy length 3-6 times),
-    17 (3-10 zeroes), 18 (11-138 zeroes)*/
-    for(i = 0; i != (unsigned)bitlen_lld.size; ++i) {
-      unsigned j = 0; /*amount of repititions*/
-      while(i + j + 1 < (unsigned)bitlen_lld.size && bitlen_lld.data[i + j + 1] == bitlen_lld.data[i]) ++j;
-
-      if(bitlen_lld.data[i] == 0 && j >= 2) /*repeat code for zeroes*/ {
-        ++j; /*include the first zero*/
-        if(j <= 10) /*repeat code 17 supports max 10 zeroes*/ {
-          uivector_push_back(&bitlen_lld_e, 17);
-          uivector_push_back(&bitlen_lld_e, j - 3);
-        } else /*repeat code 18 supports max 138 zeroes*/ {
-          if(j > 138) j = 138;
-          uivector_push_back(&bitlen_lld_e, 18);
-          uivector_push_back(&bitlen_lld_e, j - 11);
-        }
-        i += (j - 1);
-      } else if(j >= 3) /*repeat code for value other than zero*/ {
-        size_t k;
-        unsigned num = j / 6, rest = j % 6;
-        uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]);
-        for(k = 0; k < num; ++k) {
-          uivector_push_back(&bitlen_lld_e, 16);
-          uivector_push_back(&bitlen_lld_e, 6 - 3);
-        }
-        if(rest >= 3) {
-          uivector_push_back(&bitlen_lld_e, 16);
-          uivector_push_back(&bitlen_lld_e, rest - 3);
-        }
-        else j -= rest;
-        i += j;
-      } else /*too short to benefit from repeat code*/ {
-        uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]);
-      }
-    }
-
-    /*generate tree_cl, the huffmantree of huffmantrees*/
-
-    if(!uivector_resizev(&frequencies_cl, NUM_CODE_LENGTH_CODES, 0)) ERROR_BREAK(83 /*alloc fail*/);
-    for(i = 0; i != bitlen_lld_e.size; ++i) {
-      ++frequencies_cl.data[bitlen_lld_e.data[i]];
-      /*after a repeat code come the bits that specify the number of repetitions,
-      those don't need to be in the frequencies_cl calculation*/
-      if(bitlen_lld_e.data[i] >= 16) ++i;
-    }
-
-    error = HuffmanTree_makeFromFrequencies(&tree_cl, frequencies_cl.data,
-                                            frequencies_cl.size, frequencies_cl.size, 7);
-    if(error) break;
-
-    if(!uivector_resize(&bitlen_cl, tree_cl.numcodes)) ERROR_BREAK(83 /*alloc fail*/);
-    for(i = 0; i != tree_cl.numcodes; ++i) {
-      /*lenghts of code length tree is in the order as specified by deflate*/
-      bitlen_cl.data[i] = HuffmanTree_getLength(&tree_cl, CLCL_ORDER[i]);
-    }
-    while(bitlen_cl.data[bitlen_cl.size - 1] == 0 && bitlen_cl.size > 4) {
-      /*remove zeros at the end, but minimum size must be 4*/
-      if(!uivector_resize(&bitlen_cl, bitlen_cl.size - 1)) ERROR_BREAK(83 /*alloc fail*/);
-    }
-    if(error) break;
-
-    /*
-    Write everything into the output
-
-    After the BFINAL and BTYPE, the dynamic block consists out of the following:
-    - 5 bits HLIT, 5 bits HDIST, 4 bits HCLEN
-    - (HCLEN+4)*3 bits code lengths of code length alphabet
-    - HLIT + 257 code lenghts of lit/length alphabet (encoded using the code length
-      alphabet, + possible repetition codes 16, 17, 18)
-    - HDIST + 1 code lengths of distance alphabet (encoded using the code length
-      alphabet, + possible repetition codes 16, 17, 18)
-    - compressed data
-    - 256 (end code)
-    */
-
-    /*Write block type*/
-    addBitToStream(bp, out, BFINAL);
-    addBitToStream(bp, out, 0); /*first bit of BTYPE "dynamic"*/
-    addBitToStream(bp, out, 1); /*second bit of BTYPE "dynamic"*/
-
-    /*write the HLIT, HDIST and HCLEN values*/
-    HLIT = (unsigned)(numcodes_ll - 257);
-    HDIST = (unsigned)(numcodes_d - 1);
-    HCLEN = (unsigned)bitlen_cl.size - 4;
-    /*trim zeroes for HCLEN. HLIT and HDIST were already trimmed at tree creation*/
-    while(!bitlen_cl.data[HCLEN + 4 - 1] && HCLEN > 0) --HCLEN;
-    addBitsToStream(bp, out, HLIT, 5);
-    addBitsToStream(bp, out, HDIST, 5);
-    addBitsToStream(bp, out, HCLEN, 4);
-
-    /*write the code lenghts of the code length alphabet*/
-    for(i = 0; i != HCLEN + 4; ++i) addBitsToStream(bp, out, bitlen_cl.data[i], 3);
-
-    /*write the lenghts of the lit/len AND the dist alphabet*/
-    for(i = 0; i != bitlen_lld_e.size; ++i) {
-      addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_cl, bitlen_lld_e.data[i]),
-                       HuffmanTree_getLength(&tree_cl, bitlen_lld_e.data[i]));
-      /*extra bits of repeat codes*/
-      if(bitlen_lld_e.data[i] == 16) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 2);
-      else if(bitlen_lld_e.data[i] == 17) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 3);
-      else if(bitlen_lld_e.data[i] == 18) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 7);
-    }
-
-    /*write the compressed data symbols*/
-    writeLZ77data(bp, out, &lz77_encoded, &tree_ll, &tree_d);
-    /*error: the length of the end code 256 must be larger than 0*/
-    if(HuffmanTree_getLength(&tree_ll, 256) == 0) ERROR_BREAK(64);
-
-    /*write the end code*/
-    addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, 256), HuffmanTree_getLength(&tree_ll, 256));
-
-    break; /*end of error-while*/
-  }
-
-  /*cleanup*/
-  uivector_cleanup(&lz77_encoded);
-  HuffmanTree_cleanup(&tree_ll);
-  HuffmanTree_cleanup(&tree_d);
-  HuffmanTree_cleanup(&tree_cl);
-  uivector_cleanup(&frequencies_ll);
-  uivector_cleanup(&frequencies_d);
-  uivector_cleanup(&frequencies_cl);
-  uivector_cleanup(&bitlen_lld_e);
-  uivector_cleanup(&bitlen_lld);
-  uivector_cleanup(&bitlen_cl);
-
-  return error;
-}
-
-static unsigned deflateFixed(ucvector* out, size_t* bp, Hash* hash,
-                             const unsigned char* data,
-                             size_t datapos, size_t dataend,
-                             const LodePNGCompressSettings* settings, unsigned final) {
-  HuffmanTree tree_ll; /*tree for literal values and length codes*/
-  HuffmanTree tree_d; /*tree for distance codes*/
-
-  unsigned BFINAL = final;
-  unsigned error = 0;
-  size_t i;
-
-  HuffmanTree_init(&tree_ll);
-  HuffmanTree_init(&tree_d);
-
-  generateFixedLitLenTree(&tree_ll);
-  generateFixedDistanceTree(&tree_d);
-
-  addBitToStream(bp, out, BFINAL);
-  addBitToStream(bp, out, 1); /*first bit of BTYPE*/
-  addBitToStream(bp, out, 0); /*second bit of BTYPE*/
-
-  if(settings->use_lz77) /*LZ77 encoded*/ {
-    uivector lz77_encoded;
-    uivector_init(&lz77_encoded);
-    error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize,
-                       settings->minmatch, settings->nicematch, settings->lazymatching);
-    if(!error) writeLZ77data(bp, out, &lz77_encoded, &tree_ll, &tree_d);
-    uivector_cleanup(&lz77_encoded);
-  } else /*no LZ77, but still will be Huffman compressed*/ {
-    for(i = datapos; i < dataend; ++i) {
-      addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, data[i]), HuffmanTree_getLength(&tree_ll, data[i]));
-    }
-  }
-  /*add END code*/
-  if(!error) addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, 256), HuffmanTree_getLength(&tree_ll, 256));
-
-  /*cleanup*/
-  HuffmanTree_cleanup(&tree_ll);
-  HuffmanTree_cleanup(&tree_d);
-
-  return error;
-}
-
-static unsigned lodepng_deflatev(ucvector* out, const unsigned char* in, size_t insize,
-                                 const LodePNGCompressSettings* settings) {
-  unsigned error = 0;
-  size_t i, blocksize, numdeflateblocks;
-  size_t bp = 0; /*the bit pointer*/
-  Hash hash;
-
-  if(settings->btype > 2) return 61;
-  else if(settings->btype == 0) return deflateNoCompression(out, in, insize);
-  else if(settings->btype == 1) blocksize = insize;
-  else /*if(settings->btype == 2)*/ {
-    /*on PNGs, deflate blocks of 65-262k seem to give most dense encoding*/
-    blocksize = insize / 8 + 8;
-    if(blocksize < 65536) blocksize = 65536;
-    if(blocksize > 262144) blocksize = 262144;
-  }
-
-  numdeflateblocks = (insize + blocksize - 1) / blocksize;
-  if(numdeflateblocks == 0) numdeflateblocks = 1;
-
-  error = hash_init(&hash, settings->windowsize);
-  if(error) return error;
-
-  for(i = 0; i != numdeflateblocks && !error; ++i) {
-    unsigned final = (i == numdeflateblocks - 1);
-    size_t start = i * blocksize;
-    size_t end = start + blocksize;
-    if(end > insize) end = insize;
-
-    if(settings->btype == 1) error = deflateFixed(out, &bp, &hash, in, start, end, settings, final);
-    else if(settings->btype == 2) error = deflateDynamic(out, &bp, &hash, in, start, end, settings, final);
-  }
-
-  hash_cleanup(&hash);
-
-  return error;
-}
-
-unsigned lodepng_deflate(unsigned char** out, size_t* outsize,
-                         const unsigned char* in, size_t insize,
-                         const LodePNGCompressSettings* settings) {
-  unsigned error;
-  ucvector v;
-  ucvector_init_buffer(&v, *out, *outsize);
-  error = lodepng_deflatev(&v, in, insize, settings);
-  *out = v.data;
-  *outsize = v.size;
-  return error;
-}
-
-static unsigned deflate(unsigned char** out, size_t* outsize,
-                        const unsigned char* in, size_t insize,
-                        const LodePNGCompressSettings* settings) {
-  if(settings->custom_deflate) {
-    return settings->custom_deflate(out, outsize, in, insize, settings);
-  } else {
-    return lodepng_deflate(out, outsize, in, insize, settings);
-  }
-}
-
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Adler32                                                                  */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len) {
-  unsigned s1 = adler & 0xffff;
-  unsigned s2 = (adler >> 16) & 0xffff;
-
-  while(len > 0) {
-    /*at least 5552 sums can be done before the sums overflow, saving a lot of module divisions*/
-    unsigned amount = len > 5552 ? 5552 : len;
-    len -= amount;
-    while(amount > 0) {
-      s1 += (*data++);
-      s2 += s1;
-      --amount;
-    }
-    s1 %= 65521;
-    s2 %= 65521;
-  }
-
-  return (s2 << 16) | s1;
-}
-
-/*Return the adler32 of the bytes data[0..len-1]*/
-static unsigned adler32(const unsigned char* data, unsigned len) {
-  return update_adler32(1L, data, len);
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Zlib                                                                   / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#ifdef LODEPNG_COMPILE_DECODER
-
-unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in,
-                                 size_t insize, const LodePNGDecompressSettings* settings) {
-  unsigned error = 0;
-  unsigned CM, CINFO, FDICT;
-
-  if(insize < 2) return 53; /*error, size of zlib data too small*/
-  /*read information from zlib header*/
-  if((in[0] * 256 + in[1]) % 31 != 0) {
-    /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/
-    return 24;
-  }
-
-  CM = in[0] & 15;
-  CINFO = (in[0] >> 4) & 15;
-  /*FCHECK = in[1] & 31;*/ /*FCHECK is already tested above*/
-  FDICT = (in[1] >> 5) & 1;
-  /*FLEVEL = (in[1] >> 6) & 3;*/ /*FLEVEL is not used here*/
-
-  if(CM != 8 || CINFO > 7) {
-    /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/
-    return 25;
-  }
-  if(FDICT != 0) {
-    /*error: the specification of PNG says about the zlib stream:
-      "The additional flags shall not specify a preset dictionary."*/
-    return 26;
-  }
-
-  error = inflate(out, outsize, in + 2, insize - 2, settings);
-  if(error) return error;
-
-  if(!settings->ignore_adler32) {
-    unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]);
-    unsigned checksum = adler32(*out, (unsigned)(*outsize));
-    if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/
-  }
-
-  return 0; /*no error*/
-}
-
-static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in,
-                                size_t insize, const LodePNGDecompressSettings* settings) {
-  if(settings->custom_zlib) {
-    return settings->custom_zlib(out, outsize, in, insize, settings);
-  } else {
-    return lodepng_zlib_decompress(out, outsize, in, insize, settings);
-  }
-}
-
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-#ifdef LODEPNG_COMPILE_ENCODER
-
-unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in,
-                               size_t insize, const LodePNGCompressSettings* settings) {
-  /*initially, *out must be NULL and outsize 0, if you just give some random *out
-  that's pointing to a non allocated buffer, this'll crash*/
-  ucvector outv;
-  size_t i;
-  unsigned error;
-  unsigned char* deflatedata = 0;
-  size_t deflatesize = 0;
-
-  /*zlib data: 1 byte CMF (CM+CINFO), 1 byte FLG, deflate data, 4 byte ADLER32 checksum of the Decompressed data*/
-  unsigned CMF = 120; /*0b01111000: CM 8, CINFO 7. With CINFO 7, any window size up to 32768 can be used.*/
-  unsigned FLEVEL = 0;
-  unsigned FDICT = 0;
-  unsigned CMFFLG = 256 * CMF + FDICT * 32 + FLEVEL * 64;
-  unsigned FCHECK = 31 - CMFFLG % 31;
-  CMFFLG += FCHECK;
-
-  /*ucvector-controlled version of the output buffer, for dynamic array*/
-  ucvector_init_buffer(&outv, *out, *outsize);
-
-  ucvector_push_back(&outv, (unsigned char)(CMFFLG >> 8));
-  ucvector_push_back(&outv, (unsigned char)(CMFFLG & 255));
-
-  error = deflate(&deflatedata, &deflatesize, in, insize, settings);
-
-  if(!error) {
-    unsigned ADLER32 = adler32(in, (unsigned)insize);
-    for(i = 0; i != deflatesize; ++i) ucvector_push_back(&outv, deflatedata[i]);
-    lodepng_free(deflatedata);
-    lodepng_add32bitInt(&outv, ADLER32);
-  }
-
-  *out = outv.data;
-  *outsize = outv.size;
-
-  return error;
-}
-
-/* compress using the default or custom zlib function */
-static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in,
-                              size_t insize, const LodePNGCompressSettings* settings) {
-  if(settings->custom_zlib) {
-    return settings->custom_zlib(out, outsize, in, insize, settings);
-  } else {
-    return lodepng_zlib_compress(out, outsize, in, insize, settings);
-  }
-}
-
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-#else /*no LODEPNG_COMPILE_ZLIB*/
-
-#ifdef LODEPNG_COMPILE_DECODER
-static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in,
-                                size_t insize, const LodePNGDecompressSettings* settings) {
-  if(!settings->custom_zlib) return 87; /*no custom zlib function provided */
-  return settings->custom_zlib(out, outsize, in, insize, settings);
-}
-#endif /*LODEPNG_COMPILE_DECODER*/
-#ifdef LODEPNG_COMPILE_ENCODER
-static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in,
-                              size_t insize, const LodePNGCompressSettings* settings) {
-  if(!settings->custom_zlib) return 87; /*no custom zlib function provided */
-  return settings->custom_zlib(out, outsize, in, insize, settings);
-}
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-#endif /*LODEPNG_COMPILE_ZLIB*/
-
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#ifdef LODEPNG_COMPILE_ENCODER
-
-/*this is a good tradeoff between speed and compression ratio*/
-#define DEFAULT_WINDOWSIZE 2048
-
-void lodepng_compress_settings_init(LodePNGCompressSettings* settings) {
-  /*compress with dynamic huffman tree (not in the mathematical sense, just not the predefined one)*/
-  settings->btype = 2;
-  settings->use_lz77 = 1;
-  settings->windowsize = DEFAULT_WINDOWSIZE;
-  settings->minmatch = 3;
-  settings->nicematch = 128;
-  settings->lazymatching = 1;
-
-  settings->custom_zlib = 0;
-  settings->custom_deflate = 0;
-  settings->custom_context = 0;
-}
-
-const LodePNGCompressSettings lodepng_default_compress_settings = {2, 1, DEFAULT_WINDOWSIZE, 3, 128, 1, 0, 0, 0};
-
-
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-#ifdef LODEPNG_COMPILE_DECODER
-
-void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) {
-  settings->ignore_adler32 = 0;
-
-  settings->custom_zlib = 0;
-  settings->custom_inflate = 0;
-  settings->custom_context = 0;
-}
-
-const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0};
-
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* // End of Zlib related code. Begin of PNG related code.                 // */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#ifdef LODEPNG_COMPILE_PNG
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / CRC32                                                                  / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-
-#ifndef LODEPNG_NO_COMPILE_CRC
-/* CRC polynomial: 0xedb88320 */
-static unsigned lodepng_crc32_table[256] = {
-           0u, 1996959894u, 3993919788u, 2567524794u,  124634137u, 1886057615u, 3915621685u, 2657392035u,
-   249268274u, 2044508324u, 3772115230u, 2547177864u,  162941995u, 2125561021u, 3887607047u, 2428444049u,
-   498536548u, 1789927666u, 4089016648u, 2227061214u,  450548861u, 1843258603u, 4107580753u, 2211677639u,
-   325883990u, 1684777152u, 4251122042u, 2321926636u,  335633487u, 1661365465u, 4195302755u, 2366115317u,
-   997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u,
-   901097722u, 1119000684u, 3686517206u, 2898065728u,  853044451u, 1172266101u, 3705015759u, 2882616665u,
-   651767980u, 1373503546u, 3369554304u, 3218104598u,  565507253u, 1454621731u, 3485111705u, 3099436303u,
-   671266974u, 1594198024u, 3322730930u, 2970347812u,  795835527u, 1483230225u, 3244367275u, 3060149565u,
-  1994146192u,   31158534u, 2563907772u, 4023717930u, 1907459465u,  112637215u, 2680153253u, 3904427059u,
-  2013776290u,  251722036u, 2517215374u, 3775830040u, 2137656763u,  141376813u, 2439277719u, 3865271297u,
-  1802195444u,  476864866u, 2238001368u, 4066508878u, 1812370925u,  453092731u, 2181625025u, 4111451223u,
-  1706088902u,  314042704u, 2344532202u, 4240017532u, 1658658271u,  366619977u, 2362670323u, 4224994405u,
-  1303535960u,  984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u,
-  1131014506u,  879679996u, 2909243462u, 3663771856u, 1141124467u,  855842277u, 2852801631u, 3708648649u,
-  1342533948u,  654459306u, 3188396048u, 3373015174u, 1466479909u,  544179635u, 3110523913u, 3462522015u,
-  1591671054u,  702138776u, 2966460450u, 3352799412u, 1504918807u,  783551873u, 3082640443u, 3233442989u,
-  3988292384u, 2596254646u,   62317068u, 1957810842u, 3939845945u, 2647816111u,   81470997u, 1943803523u,
-  3814918930u, 2489596804u,  225274430u, 2053790376u, 3826175755u, 2466906013u,  167816743u, 2097651377u,
-  4027552580u, 2265490386u,  503444072u, 1762050814u, 4150417245u, 2154129355u,  426522225u, 1852507879u,
-  4275313526u, 2312317920u,  282753626u, 1742555852u, 4189708143u, 2394877945u,  397917763u, 1622183637u,
-  3604390888u, 2714866558u,  953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u,
-  3624741850u, 2936675148u,  906185462u, 1090812512u, 3747672003u, 2825379669u,  829329135u, 1181335161u,
-  3412177804u, 3160834842u,  628085408u, 1382605366u, 3423369109u, 3138078467u,  570562233u, 1426400815u,
-  3317316542u, 2998733608u,  733239954u, 1555261956u, 3268935591u, 3050360625u,  752459403u, 1541320221u,
-  2607071920u, 3965973030u, 1969922972u,   40735498u, 2617837225u, 3943577151u, 1913087877u,   83908371u,
-  2512341634u, 3803740692u, 2075208622u,  213261112u, 2463272603u, 3855990285u, 2094854071u,  198958881u,
-  2262029012u, 4057260610u, 1759359992u,  534414190u, 2176718541u, 4139329115u, 1873836001u,  414664567u,
-  2282248934u, 4279200368u, 1711684554u,  285281116u, 2405801727u, 4167216745u, 1634467795u,  376229701u,
-  2685067896u, 3608007406u, 1308918612u,  956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u,
-  2932959818u, 3654703836u, 1088359270u,  936918000u, 2847714899u, 3736837829u, 1202900863u,  817233897u,
-  3183342108u, 3401237130u, 1404277552u,  615818150u, 3134207493u, 3453421203u, 1423857449u,  601450431u,
-  3009837614u, 3294710456u, 1567103746u,  711928724u, 3020668471u, 3272380065u, 1510334235u,  755167117u
-};
-
-/*Return the CRC of the bytes buf[0..len-1].*/
-unsigned lodepng_crc32(const unsigned char* data, size_t length) {
-  unsigned r = 0xffffffffu;
-  size_t i;
-  for(i = 0; i < length; ++i) {
-    r = lodepng_crc32_table[(r ^ data[i]) & 0xff] ^ (r >> 8);
-  }
-  return r ^ 0xffffffffu;
-}
-#else /* !LODEPNG_NO_COMPILE_CRC */
-unsigned lodepng_crc32(const unsigned char* data, size_t length);
-#endif /* !LODEPNG_NO_COMPILE_CRC */
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Reading and writing single bits and bytes from/to stream for LodePNG   / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) {
-  unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1);
-  ++(*bitpointer);
-  return result;
-}
-
-static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) {
-  unsigned result = 0;
-  size_t i;
-  for(i = 0 ; i < nbits; ++i) {
-    result <<= 1;
-    result |= (unsigned)readBitFromReversedStream(bitpointer, bitstream);
-  }
-  return result;
-}
-
-#ifdef LODEPNG_COMPILE_DECODER
-static void setBitOfReversedStream0(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) {
-  /*the current bit in bitstream must be 0 for this to work*/
-  if(bit) {
-    /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/
-    bitstream[(*bitpointer) >> 3] |= (bit << (7 - ((*bitpointer) & 0x7)));
-  }
-  ++(*bitpointer);
-}
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) {
-  /*the current bit in bitstream may be 0 or 1 for this to work*/
-  if(bit == 0) bitstream[(*bitpointer) >> 3] &=  (unsigned char)(~(1 << (7 - ((*bitpointer) & 0x7))));
-  else         bitstream[(*bitpointer) >> 3] |=  (1 << (7 - ((*bitpointer) & 0x7)));
-  ++(*bitpointer);
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / PNG chunks                                                             / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-unsigned lodepng_chunk_length(const unsigned char* chunk) {
-  return lodepng_read32bitInt(&chunk[0]);
-}
-
-void lodepng_chunk_type(char type[5], const unsigned char* chunk) {
-  unsigned i;
-  for(i = 0; i != 4; ++i) type[i] = (char)chunk[4 + i];
-  type[4] = 0; /*null termination char*/
-}
-
-unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type) {
-  if(strlen(type) != 4) return 0;
-  return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]);
-}
-
-unsigned char lodepng_chunk_ancillary(const unsigned char* chunk) {
-  return((chunk[4] & 32) != 0);
-}
-
-unsigned char lodepng_chunk_private(const unsigned char* chunk) {
-  return((chunk[6] & 32) != 0);
-}
-
-unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk) {
-  return((chunk[7] & 32) != 0);
-}
-
-unsigned char* lodepng_chunk_data(unsigned char* chunk) {
-  return &chunk[8];
-}
-
-const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk) {
-  return &chunk[8];
-}
-
-unsigned lodepng_chunk_check_crc(const unsigned char* chunk) {
-  unsigned length = lodepng_chunk_length(chunk);
-  unsigned CRC = lodepng_read32bitInt(&chunk[length + 8]);
-  /*the CRC is taken of the data and the 4 chunk type letters, not the length*/
-  unsigned checksum = lodepng_crc32(&chunk[4], length + 4);
-  if(CRC != checksum) return 1;
-  else return 0;
-}
-
-void lodepng_chunk_generate_crc(unsigned char* chunk) {
-  unsigned length = lodepng_chunk_length(chunk);
-  unsigned CRC = lodepng_crc32(&chunk[4], length + 4);
-  lodepng_set32bitInt(chunk + 8 + length, CRC);
-}
-
-unsigned char* lodepng_chunk_next(unsigned char* chunk) {
-  if(chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47
-    && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) {
-    /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */
-    return chunk + 8;
-  } else {
-    unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12;
-    return chunk + total_chunk_length;
-  }
-}
-
-const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk) {
-  if(chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47
-    && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) {
-    /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */
-    return chunk + 8;
-  } else {
-    unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12;
-    return chunk + total_chunk_length;
-  }
-}
-
-unsigned char* lodepng_chunk_find(unsigned char* chunk, const unsigned char* end, const char type[5]) {
-  for(;;) {
-    if(chunk + 12 >= end) return 0;
-    if(lodepng_chunk_type_equals(chunk, type)) return chunk;
-    chunk = lodepng_chunk_next(chunk);
-  }
-}
-
-const unsigned char* lodepng_chunk_find_const(const unsigned char* chunk, const unsigned char* end, const char type[5]) {
-  for(;;) {
-    if(chunk + 12 >= end) return 0;
-    if(lodepng_chunk_type_equals(chunk, type)) return chunk;
-    chunk = lodepng_chunk_next_const(chunk);
-  }
-}
-
-unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk) {
-  unsigned i;
-  unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12;
-  unsigned char *chunk_start, *new_buffer;
-  size_t new_length = (*outlength) + total_chunk_length;
-  if(new_length < total_chunk_length || new_length < (*outlength)) return 77; /*integer overflow happened*/
-
-  new_buffer = (unsigned char*)lodepng_realloc(*out, new_length);
-  if(!new_buffer) return 83; /*alloc fail*/
-  (*out) = new_buffer;
-  (*outlength) = new_length;
-  chunk_start = &(*out)[new_length - total_chunk_length];
-
-  for(i = 0; i != total_chunk_length; ++i) chunk_start[i] = chunk[i];
-
-  return 0;
-}
-
-unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length,
-                              const char* type, const unsigned char* data) {
-  unsigned i;
-  unsigned char *chunk, *new_buffer;
-  size_t new_length = (*outlength) + length + 12;
-  if(new_length < length + 12 || new_length < (*outlength)) return 77; /*integer overflow happened*/
-  new_buffer = (unsigned char*)lodepng_realloc(*out, new_length);
-  if(!new_buffer) return 83; /*alloc fail*/
-  (*out) = new_buffer;
-  (*outlength) = new_length;
-  chunk = &(*out)[(*outlength) - length - 12];
-
-  /*1: length*/
-  lodepng_set32bitInt(chunk, (unsigned)length);
-
-  /*2: chunk name (4 letters)*/
-  chunk[4] = (unsigned char)type[0];
-  chunk[5] = (unsigned char)type[1];
-  chunk[6] = (unsigned char)type[2];
-  chunk[7] = (unsigned char)type[3];
-
-  /*3: the data*/
-  for(i = 0; i != length; ++i) chunk[8 + i] = data[i];
-
-  /*4: CRC (of the chunkname characters and the data)*/
-  lodepng_chunk_generate_crc(chunk);
-
-  return 0;
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Color types and such                                                   / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*return type is a LodePNG error code*/
-static unsigned checkColorValidity(LodePNGColorType colortype, unsigned bd) /*bd = bitdepth*/ {
-  switch(colortype) {
-    case 0: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; /*gray*/
-    case 2: if(!(                                 bd == 8 || bd == 16)) return 37; break; /*RGB*/
-    case 3: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8            )) return 37; break; /*palette*/
-    case 4: if(!(                                 bd == 8 || bd == 16)) return 37; break; /*gray + alpha*/
-    case 6: if(!(                                 bd == 8 || bd == 16)) return 37; break; /*RGBA*/
-    default: return 31;
-  }
-  return 0; /*allowed color type / bits combination*/
-}
-
-static unsigned getNumColorChannels(LodePNGColorType colortype) {
-  switch(colortype) {
-    case 0: return 1; /*gray*/
-    case 2: return 3; /*RGB*/
-    case 3: return 1; /*palette*/
-    case 4: return 2; /*gray + alpha*/
-    case 6: return 4; /*RGBA*/
-  }
-  return 0; /*unexisting color type*/
-}
-
-static unsigned lodepng_get_bpp_lct(LodePNGColorType colortype, unsigned bitdepth) {
-  /*bits per pixel is amount of channels * bits per channel*/
-  return getNumColorChannels(colortype) * bitdepth;
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-
-void lodepng_color_mode_init(LodePNGColorMode* info) {
-  info->key_defined = 0;
-  info->key_r = info->key_g = info->key_b = 0;
-  info->colortype = LCT_RGBA;
-  info->bitdepth = 8;
-  info->palette = 0;
-  info->palettesize = 0;
-}
-
-void lodepng_color_mode_cleanup(LodePNGColorMode* info) {
-  lodepng_palette_clear(info);
-}
-
-unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source) {
-  size_t i;
-  lodepng_color_mode_cleanup(dest);
-  *dest = *source;
-  if(source->palette) {
-    dest->palette = (unsigned char*)lodepng_malloc(1024);
-    if(!dest->palette && source->palettesize) return 83; /*alloc fail*/
-    for(i = 0; i != source->palettesize * 4; ++i) dest->palette[i] = source->palette[i];
-  }
-  return 0;
-}
-
-LodePNGColorMode lodepng_color_mode_make(LodePNGColorType colortype, unsigned bitdepth) {
-  LodePNGColorMode result;
-  lodepng_color_mode_init(&result);
-  result.colortype = colortype;
-  result.bitdepth = bitdepth;
-  return result;
-}
-
-static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b) {
-  size_t i;
-  if(a->colortype != b->colortype) return 0;
-  if(a->bitdepth != b->bitdepth) return 0;
-  if(a->key_defined != b->key_defined) return 0;
-  if(a->key_defined) {
-    if(a->key_r != b->key_r) return 0;
-    if(a->key_g != b->key_g) return 0;
-    if(a->key_b != b->key_b) return 0;
-  }
-  if(a->palettesize != b->palettesize) return 0;
-  for(i = 0; i != a->palettesize * 4; ++i) {
-    if(a->palette[i] != b->palette[i]) return 0;
-  }
-  return 1;
-}
-
-void lodepng_palette_clear(LodePNGColorMode* info) {
-  if(info->palette) lodepng_free(info->palette);
-  info->palette = 0;
-  info->palettesize = 0;
-}
-
-unsigned lodepng_palette_add(LodePNGColorMode* info,
-                             unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
-  unsigned char* data;
-  /*the same resize technique as C++ std::vectors is used, and here it's made so that for a palette with
-  the max of 256 colors, it'll have the exact alloc size*/
-  if(!info->palette) /*allocate palette if empty*/ {
-    /*room for 256 colors with 4 bytes each*/
-    data = (unsigned char*)lodepng_realloc(info->palette, 1024);
-    if(!data) return 83; /*alloc fail*/
-    else info->palette = data;
-  }
-  info->palette[4 * info->palettesize + 0] = r;
-  info->palette[4 * info->palettesize + 1] = g;
-  info->palette[4 * info->palettesize + 2] = b;
-  info->palette[4 * info->palettesize + 3] = a;
-  ++info->palettesize;
-  return 0;
-}
-
-/*calculate bits per pixel out of colortype and bitdepth*/
-unsigned lodepng_get_bpp(const LodePNGColorMode* info) {
-  return lodepng_get_bpp_lct(info->colortype, info->bitdepth);
-}
-
-unsigned lodepng_get_channels(const LodePNGColorMode* info) {
-  return getNumColorChannels(info->colortype);
-}
-
-unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info) {
-  return info->colortype == LCT_GREY || info->colortype == LCT_GREY_ALPHA;
-}
-
-unsigned lodepng_is_alpha_type(const LodePNGColorMode* info) {
-  return (info->colortype & 4) != 0; /*4 or 6*/
-}
-
-unsigned lodepng_is_palette_type(const LodePNGColorMode* info) {
-  return info->colortype == LCT_PALETTE;
-}
-
-unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info) {
-  size_t i;
-  for(i = 0; i != info->palettesize; ++i) {
-    if(info->palette[i * 4 + 3] < 255) return 1;
-  }
-  return 0;
-}
-
-unsigned lodepng_can_have_alpha(const LodePNGColorMode* info) {
-  return info->key_defined
-      || lodepng_is_alpha_type(info)
-      || lodepng_has_palette_alpha(info);
-}
-
-size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) {
-  size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth);
-  size_t n = (size_t)w * (size_t)h;
-  return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8;
-}
-
-size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) {
-  return lodepng_get_raw_size_lct(w, h, color->colortype, color->bitdepth);
-}
-
-
-#ifdef LODEPNG_COMPILE_PNG
-#ifdef LODEPNG_COMPILE_DECODER
-
-/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer,
-and in addition has one extra byte per line: the filter byte. So this gives a larger
-result than lodepng_get_raw_size. */
-static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, const LodePNGColorMode* color) {
-  size_t bpp = lodepng_get_bpp(color);
-  /* + 1 for the filter byte, and possibly plus padding bits per line */
-  size_t line = ((size_t)(w / 8) * bpp) + 1 + ((w & 7) * bpp + 7) / 8;
-  return (size_t)h * line;
-}
-
-/* Safely check if multiplying two integers will overflow (no undefined
-behavior, compiler removing the code, etc...) and output result. */
-static int lodepng_mulofl(size_t a, size_t b, size_t* result) {
-  *result = a * b; /* Unsigned multiplication is well defined and safe in C90 */
-  return (a != 0 && *result / a != b);
-}
-
-/* Safely check if adding two integers will overflow (no undefined
-behavior, compiler removing the code, etc...) and output result. */
-static int lodepng_addofl(size_t a, size_t b, size_t* result) {
-  *result = a + b; /* Unsigned addition is well defined and safe in C90 */
-  return *result < a;
-}
-
-/*Safely checks whether size_t overflow can be caused due to amount of pixels.
-This check is overcautious rather than precise. If this check indicates no overflow,
-you can safely compute in a size_t (but not an unsigned):
--(size_t)w * (size_t)h * 8
--amount of bytes in IDAT (including filter, padding and Adam7 bytes)
--amount of bytes in raw color model
-Returns 1 if overflow possible, 0 if not.
-*/
-static int lodepng_pixel_overflow(unsigned w, unsigned h,
-                                  const LodePNGColorMode* pngcolor, const LodePNGColorMode* rawcolor) {
-  size_t bpp = LODEPNG_MAX(lodepng_get_bpp(pngcolor), lodepng_get_bpp(rawcolor));
-  size_t numpixels, total;
-  size_t line; /* bytes per line in worst case */
-
-  if(lodepng_mulofl((size_t)w, (size_t)h, &numpixels)) return 1;
-  if(lodepng_mulofl(numpixels, 8, &total)) return 1; /* bit pointer with 8-bit color, or 8 bytes per channel color */
-
-  /* Bytes per scanline with the expression "(w / 8) * bpp) + ((w & 7) * bpp + 7) / 8" */
-  if(lodepng_mulofl((size_t)(w / 8), bpp, &line)) return 1;
-  if(lodepng_addofl(line, ((w & 7) * bpp + 7) / 8, &line)) return 1;
-
-  if(lodepng_addofl(line, 5, &line)) return 1; /* 5 bytes overhead per line: 1 filterbyte, 4 for Adam7 worst case */
-  if(lodepng_mulofl(line, h, &total)) return 1; /* Total bytes in worst case */
-
-  return 0; /* no overflow */
-}
-#endif /*LODEPNG_COMPILE_DECODER*/
-#endif /*LODEPNG_COMPILE_PNG*/
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-
-static void LodePNGUnknownChunks_init(LodePNGInfo* info) {
-  unsigned i;
-  for(i = 0; i != 3; ++i) info->unknown_chunks_data[i] = 0;
-  for(i = 0; i != 3; ++i) info->unknown_chunks_size[i] = 0;
-}
-
-static void LodePNGUnknownChunks_cleanup(LodePNGInfo* info) {
-  unsigned i;
-  for(i = 0; i != 3; ++i) lodepng_free(info->unknown_chunks_data[i]);
-}
-
-static unsigned LodePNGUnknownChunks_copy(LodePNGInfo* dest, const LodePNGInfo* src) {
-  unsigned i;
-
-  LodePNGUnknownChunks_cleanup(dest);
-
-  for(i = 0; i != 3; ++i) {
-    size_t j;
-    dest->unknown_chunks_size[i] = src->unknown_chunks_size[i];
-    dest->unknown_chunks_data[i] = (unsigned char*)lodepng_malloc(src->unknown_chunks_size[i]);
-    if(!dest->unknown_chunks_data[i] && dest->unknown_chunks_size[i]) return 83; /*alloc fail*/
-    for(j = 0; j < src->unknown_chunks_size[i]; ++j) {
-      dest->unknown_chunks_data[i][j] = src->unknown_chunks_data[i][j];
-    }
-  }
-
-  return 0;
-}
-
-/******************************************************************************/
-
-static void LodePNGText_init(LodePNGInfo* info) {
-  info->text_num = 0;
-  info->text_keys = NULL;
-  info->text_strings = NULL;
-}
-
-static void LodePNGText_cleanup(LodePNGInfo* info) {
-  size_t i;
-  for(i = 0; i != info->text_num; ++i) {
-    string_cleanup(&info->text_keys[i]);
-    string_cleanup(&info->text_strings[i]);
-  }
-  lodepng_free(info->text_keys);
-  lodepng_free(info->text_strings);
-}
-
-static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) {
-  size_t i = 0;
-  dest->text_keys = 0;
-  dest->text_strings = 0;
-  dest->text_num = 0;
-  for(i = 0; i != source->text_num; ++i) {
-    CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i]));
-  }
-  return 0;
-}
-
-void lodepng_clear_text(LodePNGInfo* info) {
-  LodePNGText_cleanup(info);
-}
-
-unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str) {
-  char** new_keys = (char**)(lodepng_realloc(info->text_keys, sizeof(char*) * (info->text_num + 1)));
-  char** new_strings = (char**)(lodepng_realloc(info->text_strings, sizeof(char*) * (info->text_num + 1)));
-  if(!new_keys || !new_strings) {
-    lodepng_free(new_keys);
-    lodepng_free(new_strings);
-    return 83; /*alloc fail*/
-  }
-
-  ++info->text_num;
-  info->text_keys = new_keys;
-  info->text_strings = new_strings;
-
-  info->text_keys[info->text_num - 1] = alloc_string(key);
-  info->text_strings[info->text_num - 1] = alloc_string(str);
-
-  return 0;
-}
-
-/******************************************************************************/
-
-static void LodePNGIText_init(LodePNGInfo* info) {
-  info->itext_num = 0;
-  info->itext_keys = NULL;
-  info->itext_langtags = NULL;
-  info->itext_transkeys = NULL;
-  info->itext_strings = NULL;
-}
-
-static void LodePNGIText_cleanup(LodePNGInfo* info) {
-  size_t i;
-  for(i = 0; i != info->itext_num; ++i) {
-    string_cleanup(&info->itext_keys[i]);
-    string_cleanup(&info->itext_langtags[i]);
-    string_cleanup(&info->itext_transkeys[i]);
-    string_cleanup(&info->itext_strings[i]);
-  }
-  lodepng_free(info->itext_keys);
-  lodepng_free(info->itext_langtags);
-  lodepng_free(info->itext_transkeys);
-  lodepng_free(info->itext_strings);
-}
-
-static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) {
-  size_t i = 0;
-  dest->itext_keys = 0;
-  dest->itext_langtags = 0;
-  dest->itext_transkeys = 0;
-  dest->itext_strings = 0;
-  dest->itext_num = 0;
-  for(i = 0; i != source->itext_num; ++i) {
-    CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i],
-                                        source->itext_transkeys[i], source->itext_strings[i]));
-  }
-  return 0;
-}
-
-void lodepng_clear_itext(LodePNGInfo* info) {
-  LodePNGIText_cleanup(info);
-}
-
-unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag,
-                           const char* transkey, const char* str) {
-  char** new_keys = (char**)(lodepng_realloc(info->itext_keys, sizeof(char*) * (info->itext_num + 1)));
-  char** new_langtags = (char**)(lodepng_realloc(info->itext_langtags, sizeof(char*) * (info->itext_num + 1)));
-  char** new_transkeys = (char**)(lodepng_realloc(info->itext_transkeys, sizeof(char*) * (info->itext_num + 1)));
-  char** new_strings = (char**)(lodepng_realloc(info->itext_strings, sizeof(char*) * (info->itext_num + 1)));
-  if(!new_keys || !new_langtags || !new_transkeys || !new_strings) {
-    lodepng_free(new_keys);
-    lodepng_free(new_langtags);
-    lodepng_free(new_transkeys);
-    lodepng_free(new_strings);
-    return 83; /*alloc fail*/
-  }
-
-  ++info->itext_num;
-  info->itext_keys = new_keys;
-  info->itext_langtags = new_langtags;
-  info->itext_transkeys = new_transkeys;
-  info->itext_strings = new_strings;
-
-  info->itext_keys[info->itext_num - 1] = alloc_string(key);
-  info->itext_langtags[info->itext_num - 1] = alloc_string(langtag);
-  info->itext_transkeys[info->itext_num - 1] = alloc_string(transkey);
-  info->itext_strings[info->itext_num - 1] = alloc_string(str);
-
-  return 0;
-}
-
-/* same as set but does not delete */
-static unsigned lodepng_assign_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size) {
-  info->iccp_name = alloc_string(name);
-  info->iccp_profile = (unsigned char*)lodepng_malloc(profile_size);
-
-  if(!info->iccp_name || !info->iccp_profile) return 83; /*alloc fail*/
-
-  memcpy(info->iccp_profile, profile, profile_size);
-  info->iccp_profile_size = profile_size;
-
-  return 0; /*ok*/
-}
-
-unsigned lodepng_set_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size) {
-  if(info->iccp_name) lodepng_clear_icc(info);
-  info->iccp_defined = 1;
-
-  return lodepng_assign_icc(info, name, profile, profile_size);
-}
-
-void lodepng_clear_icc(LodePNGInfo* info) {
-  string_cleanup(&info->iccp_name);
-  lodepng_free(info->iccp_profile);
-  info->iccp_profile = NULL;
-  info->iccp_profile_size = 0;
-  info->iccp_defined = 0;
-}
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-
-void lodepng_info_init(LodePNGInfo* info) {
-  lodepng_color_mode_init(&info->color);
-  info->interlace_method = 0;
-  info->compression_method = 0;
-  info->filter_method = 0;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-  info->background_defined = 0;
-  info->background_r = info->background_g = info->background_b = 0;
-
-  LodePNGText_init(info);
-  LodePNGIText_init(info);
-
-  info->time_defined = 0;
-  info->phys_defined = 0;
-
-  info->gama_defined = 0;
-  info->chrm_defined = 0;
-  info->srgb_defined = 0;
-  info->iccp_defined = 0;
-  info->iccp_name = NULL;
-  info->iccp_profile = NULL;
-
-  LodePNGUnknownChunks_init(info);
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-}
-
-void lodepng_info_cleanup(LodePNGInfo* info) {
-  lodepng_color_mode_cleanup(&info->color);
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-  LodePNGText_cleanup(info);
-  LodePNGIText_cleanup(info);
-
-  lodepng_clear_icc(info);
-
-  LodePNGUnknownChunks_cleanup(info);
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-}
-
-unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) {
-  lodepng_info_cleanup(dest);
-  *dest = *source;
-  lodepng_color_mode_init(&dest->color);
-  CERROR_TRY_RETURN(lodepng_color_mode_copy(&dest->color, &source->color));
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-  CERROR_TRY_RETURN(LodePNGText_copy(dest, source));
-  CERROR_TRY_RETURN(LodePNGIText_copy(dest, source));
-  if(source->iccp_defined) {
-    CERROR_TRY_RETURN(lodepng_assign_icc(dest, source->iccp_name, source->iccp_profile, source->iccp_profile_size));
-  }
-
-  LodePNGUnknownChunks_init(dest);
-  CERROR_TRY_RETURN(LodePNGUnknownChunks_copy(dest, source));
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-  return 0;
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to*/
-static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in) {
-  unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/
-  /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/
-  unsigned p = index & m;
-  in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/
-  in = in << (bits * (m - p));
-  if(p == 0) out[index * bits / 8] = in;
-  else out[index * bits / 8] |= in;
-}
-
-typedef struct ColorTree ColorTree;
-
-/*
-One node of a color tree
-This is the data structure used to count the number of unique colors and to get a palette
-index for a color. It's like an octree, but because the alpha channel is used too, each
-node has 16 instead of 8 children.
-*/
-struct ColorTree {
-  ColorTree* children[16]; /*up to 16 pointers to ColorTree of next level*/
-  int index; /*the payload. Only has a meaningful value if this is in the last level*/
-};
-
-static void color_tree_init(ColorTree* tree) {
-  int i;
-  for(i = 0; i != 16; ++i) tree->children[i] = 0;
-  tree->index = -1;
-}
-
-static void color_tree_cleanup(ColorTree* tree) {
-  int i;
-  for(i = 0; i != 16; ++i) {
-    if(tree->children[i]) {
-      color_tree_cleanup(tree->children[i]);
-      lodepng_free(tree->children[i]);
-    }
-  }
-}
-
-/*returns -1 if color not present, its index otherwise*/
-static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
-  int bit = 0;
-  for(bit = 0; bit < 8; ++bit) {
-    int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1);
-    if(!tree->children[i]) return -1;
-    else tree = tree->children[i];
-  }
-  return tree ? tree->index : -1;
-}
-
-#ifdef LODEPNG_COMPILE_ENCODER
-static int color_tree_has(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
-  return color_tree_get(tree, r, g, b, a) >= 0;
-}
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-/*color is not allowed to already exist.
-Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist")*/
-static void color_tree_add(ColorTree* tree,
-                           unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index) {
-  int bit;
-  for(bit = 0; bit < 8; ++bit) {
-    int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1);
-    if(!tree->children[i]) {
-      tree->children[i] = (ColorTree*)lodepng_malloc(sizeof(ColorTree));
-      color_tree_init(tree->children[i]);
-    }
-    tree = tree->children[i];
-  }
-  tree->index = (int)index;
-}
-
-/*put a pixel, given its RGBA color, into image of any color type*/
-static unsigned rgba8ToPixel(unsigned char* out, size_t i,
-                             const LodePNGColorMode* mode, ColorTree* tree /*for palette*/,
-                             unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
-  if(mode->colortype == LCT_GREY) {
-    unsigned char gray = r; /*((unsigned short)r + g + b) / 3;*/
-    if(mode->bitdepth == 8) out[i] = gray;
-    else if(mode->bitdepth == 16) out[i * 2 + 0] = out[i * 2 + 1] = gray;
-    else {
-      /*take the most significant bits of gray*/
-      gray = (gray >> (8 - mode->bitdepth)) & ((1 << mode->bitdepth) - 1);
-      addColorBits(out, i, mode->bitdepth, gray);
-    }
-  } else if(mode->colortype == LCT_RGB) {
-    if(mode->bitdepth == 8) {
-      out[i * 3 + 0] = r;
-      out[i * 3 + 1] = g;
-      out[i * 3 + 2] = b;
-    } else {
-      out[i * 6 + 0] = out[i * 6 + 1] = r;
-      out[i * 6 + 2] = out[i * 6 + 3] = g;
-      out[i * 6 + 4] = out[i * 6 + 5] = b;
-    }
-  } else if(mode->colortype == LCT_PALETTE) {
-    int index = color_tree_get(tree, r, g, b, a);
-    if(index < 0) return 82; /*color not in palette*/
-    if(mode->bitdepth == 8) out[i] = index;
-    else addColorBits(out, i, mode->bitdepth, (unsigned)index);
-  } else if(mode->colortype == LCT_GREY_ALPHA) {
-    unsigned char gray = r; /*((unsigned short)r + g + b) / 3;*/
-    if(mode->bitdepth == 8) {
-      out[i * 2 + 0] = gray;
-      out[i * 2 + 1] = a;
-    } else if(mode->bitdepth == 16) {
-      out[i * 4 + 0] = out[i * 4 + 1] = gray;
-      out[i * 4 + 2] = out[i * 4 + 3] = a;
-    }
-  } else if(mode->colortype == LCT_RGBA) {
-    if(mode->bitdepth == 8) {
-      out[i * 4 + 0] = r;
-      out[i * 4 + 1] = g;
-      out[i * 4 + 2] = b;
-      out[i * 4 + 3] = a;
-    } else {
-      out[i * 8 + 0] = out[i * 8 + 1] = r;
-      out[i * 8 + 2] = out[i * 8 + 3] = g;
-      out[i * 8 + 4] = out[i * 8 + 5] = b;
-      out[i * 8 + 6] = out[i * 8 + 7] = a;
-    }
-  }
-
-  return 0; /*no error*/
-}
-
-/*put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type*/
-static void rgba16ToPixel(unsigned char* out, size_t i,
-                         const LodePNGColorMode* mode,
-                         unsigned short r, unsigned short g, unsigned short b, unsigned short a) {
-  if(mode->colortype == LCT_GREY) {
-    unsigned short gray = r; /*((unsigned)r + g + b) / 3;*/
-    out[i * 2 + 0] = (gray >> 8) & 255;
-    out[i * 2 + 1] = gray & 255;
-  } else if(mode->colortype == LCT_RGB) {
-    out[i * 6 + 0] = (r >> 8) & 255;
-    out[i * 6 + 1] = r & 255;
-    out[i * 6 + 2] = (g >> 8) & 255;
-    out[i * 6 + 3] = g & 255;
-    out[i * 6 + 4] = (b >> 8) & 255;
-    out[i * 6 + 5] = b & 255;
-  } else if(mode->colortype == LCT_GREY_ALPHA) {
-    unsigned short gray = r; /*((unsigned)r + g + b) / 3;*/
-    out[i * 4 + 0] = (gray >> 8) & 255;
-    out[i * 4 + 1] = gray & 255;
-    out[i * 4 + 2] = (a >> 8) & 255;
-    out[i * 4 + 3] = a & 255;
-  } else if(mode->colortype == LCT_RGBA) {
-    out[i * 8 + 0] = (r >> 8) & 255;
-    out[i * 8 + 1] = r & 255;
-    out[i * 8 + 2] = (g >> 8) & 255;
-    out[i * 8 + 3] = g & 255;
-    out[i * 8 + 4] = (b >> 8) & 255;
-    out[i * 8 + 5] = b & 255;
-    out[i * 8 + 6] = (a >> 8) & 255;
-    out[i * 8 + 7] = a & 255;
-  }
-}
-
-/*Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type.*/
-static void getPixelColorRGBA8(unsigned char* r, unsigned char* g,
-                               unsigned char* b, unsigned char* a,
-                               const unsigned char* in, size_t i,
-                               const LodePNGColorMode* mode) {
-  if(mode->colortype == LCT_GREY) {
-    if(mode->bitdepth == 8) {
-      *r = *g = *b = in[i];
-      if(mode->key_defined && *r == mode->key_r) *a = 0;
-      else *a = 255;
-    } else if(mode->bitdepth == 16) {
-      *r = *g = *b = in[i * 2 + 0];
-      if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0;
-      else *a = 255;
-    } else {
-      unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/
-      size_t j = i * mode->bitdepth;
-      unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth);
-      *r = *g = *b = (value * 255) / highest;
-      if(mode->key_defined && value == mode->key_r) *a = 0;
-      else *a = 255;
-    }
-  } else if(mode->colortype == LCT_RGB) {
-    if(mode->bitdepth == 8) {
-      *r = in[i * 3 + 0]; *g = in[i * 3 + 1]; *b = in[i * 3 + 2];
-      if(mode->key_defined && *r == mode->key_r && *g == mode->key_g && *b == mode->key_b) *a = 0;
-      else *a = 255;
-    } else {
-      *r = in[i * 6 + 0];
-      *g = in[i * 6 + 2];
-      *b = in[i * 6 + 4];
-      if(mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r
-         && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g
-         && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0;
-      else *a = 255;
-    }
-  } else if(mode->colortype == LCT_PALETTE) {
-    unsigned index;
-    if(mode->bitdepth == 8) index = in[i];
-    else {
-      size_t j = i * mode->bitdepth;
-      index = readBitsFromReversedStream(&j, in, mode->bitdepth);
-    }
-
-    if(index >= mode->palettesize) {
-      /*This is an error according to the PNG spec, but common PNG decoders make it black instead.
-      Done here too, slightly faster due to no error handling needed.*/
-      *r = *g = *b = 0;
-      *a = 255;
-    } else {
-      *r = mode->palette[index * 4 + 0];
-      *g = mode->palette[index * 4 + 1];
-      *b = mode->palette[index * 4 + 2];
-      *a = mode->palette[index * 4 + 3];
-    }
-  } else if(mode->colortype == LCT_GREY_ALPHA) {
-    if(mode->bitdepth == 8) {
-      *r = *g = *b = in[i * 2 + 0];
-      *a = in[i * 2 + 1];
-    } else {
-      *r = *g = *b = in[i * 4 + 0];
-      *a = in[i * 4 + 2];
-    }
-  } else if(mode->colortype == LCT_RGBA) {
-    if(mode->bitdepth == 8) {
-      *r = in[i * 4 + 0];
-      *g = in[i * 4 + 1];
-      *b = in[i * 4 + 2];
-      *a = in[i * 4 + 3];
-    } else {
-      *r = in[i * 8 + 0];
-      *g = in[i * 8 + 2];
-      *b = in[i * 8 + 4];
-      *a = in[i * 8 + 6];
-    }
-  }
-}
-
-/*Similar to getPixelColorRGBA8, but with all the for loops inside of the color
-mode test cases, optimized to convert the colors much faster, when converting
-to RGBA or RGB with 8 bit per cannel. buffer must be RGBA or RGB output with
-enough memory, if has_alpha is true the output is RGBA. mode has the color mode
-of the input buffer.*/
-static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels,
-                                unsigned has_alpha, const unsigned char* in,
-                                const LodePNGColorMode* mode) {
-  unsigned num_channels = has_alpha ? 4 : 3;
-  size_t i;
-  if(mode->colortype == LCT_GREY) {
-    if(mode->bitdepth == 8) {
-      for(i = 0; i != numpixels; ++i, buffer += num_channels) {
-        buffer[0] = buffer[1] = buffer[2] = in[i];
-        if(has_alpha) buffer[3] = mode->key_defined && in[i] == mode->key_r ? 0 : 255;
-      }
-    } else if(mode->bitdepth == 16) {
-      for(i = 0; i != numpixels; ++i, buffer += num_channels) {
-        buffer[0] = buffer[1] = buffer[2] = in[i * 2];
-        if(has_alpha) buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255;
-      }
-    } else {
-      unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/
-      size_t j = 0;
-      for(i = 0; i != numpixels; ++i, buffer += num_channels) {
-        unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth);
-        buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest;
-        if(has_alpha) buffer[3] = mode->key_defined && value == mode->key_r ? 0 : 255;
-      }
-    }
-  } else if(mode->colortype == LCT_RGB) {
-    if(mode->bitdepth == 8) {
-      for(i = 0; i != numpixels; ++i, buffer += num_channels) {
-        buffer[0] = in[i * 3 + 0];
-        buffer[1] = in[i * 3 + 1];
-        buffer[2] = in[i * 3 + 2];
-        if(has_alpha) buffer[3] = mode->key_defined && buffer[0] == mode->key_r
-           && buffer[1]== mode->key_g && buffer[2] == mode->key_b ? 0 : 255;
-      }
-    } else {
-      for(i = 0; i != numpixels; ++i, buffer += num_channels) {
-        buffer[0] = in[i * 6 + 0];
-        buffer[1] = in[i * 6 + 2];
-        buffer[2] = in[i * 6 + 4];
-        if(has_alpha) buffer[3] = mode->key_defined
-           && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r
-           && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g
-           && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b ? 0 : 255;
-      }
-    }
-  } else if(mode->colortype == LCT_PALETTE) {
-    unsigned index;
-    size_t j = 0;
-    for(i = 0; i != numpixels; ++i, buffer += num_channels) {
-      if(mode->bitdepth == 8) index = in[i];
-      else index = readBitsFromReversedStream(&j, in, mode->bitdepth);
-
-      if(index >= mode->palettesize) {
-        /*This is an error according to the PNG spec, but most PNG decoders make it black instead.
-        Done here too, slightly faster due to no error handling needed.*/
-        buffer[0] = buffer[1] = buffer[2] = 0;
-        if(has_alpha) buffer[3] = 255;
-      } else {
-        buffer[0] = mode->palette[index * 4 + 0];
-        buffer[1] = mode->palette[index * 4 + 1];
-        buffer[2] = mode->palette[index * 4 + 2];
-        if(has_alpha) buffer[3] = mode->palette[index * 4 + 3];
-      }
-    }
-  } else if(mode->colortype == LCT_GREY_ALPHA) {
-    if(mode->bitdepth == 8) {
-      for(i = 0; i != numpixels; ++i, buffer += num_channels) {
-        buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0];
-        if(has_alpha) buffer[3] = in[i * 2 + 1];
-      }
-    } else {
-      for(i = 0; i != numpixels; ++i, buffer += num_channels) {
-        buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0];
-        if(has_alpha) buffer[3] = in[i * 4 + 2];
-      }
-    }
-  } else if(mode->colortype == LCT_RGBA) {
-    if(mode->bitdepth == 8) {
-      for(i = 0; i != numpixels; ++i, buffer += num_channels) {
-        buffer[0] = in[i * 4 + 0];
-        buffer[1] = in[i * 4 + 1];
-        buffer[2] = in[i * 4 + 2];
-        if(has_alpha) buffer[3] = in[i * 4 + 3];
-      }
-    } else {
-      for(i = 0; i != numpixels; ++i, buffer += num_channels) {
-        buffer[0] = in[i * 8 + 0];
-        buffer[1] = in[i * 8 + 2];
-        buffer[2] = in[i * 8 + 4];
-        if(has_alpha) buffer[3] = in[i * 8 + 6];
-      }
-    }
-  }
-}
-
-/*Get RGBA16 color of pixel with index i (y * width + x) from the raw image with
-given color type, but the given color type must be 16-bit itself.*/
-static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a,
-                                const unsigned char* in, size_t i, const LodePNGColorMode* mode) {
-  if(mode->colortype == LCT_GREY) {
-    *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1];
-    if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0;
-    else *a = 65535;
-  } else if(mode->colortype == LCT_RGB) {
-    *r = 256u * in[i * 6 + 0] + in[i * 6 + 1];
-    *g = 256u * in[i * 6 + 2] + in[i * 6 + 3];
-    *b = 256u * in[i * 6 + 4] + in[i * 6 + 5];
-    if(mode->key_defined
-       && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r
-       && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g
-       && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0;
-    else *a = 65535;
-  } else if(mode->colortype == LCT_GREY_ALPHA) {
-    *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1];
-    *a = 256u * in[i * 4 + 2] + in[i * 4 + 3];
-  } else if(mode->colortype == LCT_RGBA) {
-    *r = 256u * in[i * 8 + 0] + in[i * 8 + 1];
-    *g = 256u * in[i * 8 + 2] + in[i * 8 + 3];
-    *b = 256u * in[i * 8 + 4] + in[i * 8 + 5];
-    *a = 256u * in[i * 8 + 6] + in[i * 8 + 7];
-  }
-}
-
-unsigned lodepng_convert(unsigned char* out, const unsigned char* in,
-                         const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in,
-                         unsigned w, unsigned h) {
-  size_t i;
-  ColorTree tree;
-  size_t numpixels = (size_t)w * (size_t)h;
-  unsigned error = 0;
-
-  if(lodepng_color_mode_equal(mode_out, mode_in)) {
-    size_t numbytes = lodepng_get_raw_size(w, h, mode_in);
-    for(i = 0; i != numbytes; ++i) out[i] = in[i];
-    return 0;
-  }
-
-  if(mode_out->colortype == LCT_PALETTE) {
-    size_t palettesize = mode_out->palettesize;
-    const unsigned char* palette = mode_out->palette;
-    size_t palsize = (size_t)1u << mode_out->bitdepth;
-    /*if the user specified output palette but did not give the values, assume
-    they want the values of the input color type (assuming that one is palette).
-    Note that we never create a new palette ourselves.*/
-    if(palettesize == 0) {
-      palettesize = mode_in->palettesize;
-      palette = mode_in->palette;
-      /*if the input was also palette with same bitdepth, then the color types are also
-      equal, so copy literally. This to preserve the exact indices that were in the PNG
-      even in case there are duplicate colors in the palette.*/
-      if (mode_in->colortype == LCT_PALETTE && mode_in->bitdepth == mode_out->bitdepth) {
-        size_t numbytes = lodepng_get_raw_size(w, h, mode_in);
-        for(i = 0; i != numbytes; ++i) out[i] = in[i];
-        return 0;
-      }
-    }
-    if(palettesize < palsize) palsize = palettesize;
-    color_tree_init(&tree);
-    for(i = 0; i != palsize; ++i) {
-      const unsigned char* p = &palette[i * 4];
-      color_tree_add(&tree, p[0], p[1], p[2], p[3], (unsigned)i);
-    }
-  }
-
-  if(mode_in->bitdepth == 16 && mode_out->bitdepth == 16) {
-    for(i = 0; i != numpixels; ++i) {
-      unsigned short r = 0, g = 0, b = 0, a = 0;
-      getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in);
-      rgba16ToPixel(out, i, mode_out, r, g, b, a);
-    }
-  } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) {
-    getPixelColorsRGBA8(out, numpixels, 1, in, mode_in);
-  } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) {
-    getPixelColorsRGBA8(out, numpixels, 0, in, mode_in);
-  } else {
-    unsigned char r = 0, g = 0, b = 0, a = 0;
-    for(i = 0; i != numpixels; ++i) {
-      getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in);
-      error = rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a);
-      if (error) break;
-    }
-  }
-
-  if(mode_out->colortype == LCT_PALETTE) {
-    color_tree_cleanup(&tree);
-  }
-
-  return error;
-}
-
-
-/* Converts a single rgb color without alpha from one type to another, color bits truncated to
-their bitdepth. In case of single channel (gray or palette), only the r channel is used. Slow
-function, do not use to process all pixels of an image. Alpha channel not supported on purpose:
-this is for bKGD, supporting alpha may prevent it from finding a color in the palette, from the
-specification it looks like bKGD should ignore the alpha values of the palette since it can use
-any palette index but doesn't have an alpha channel. Idem with ignoring color key. */
-unsigned lodepng_convert_rgb(
-    unsigned* r_out, unsigned* g_out, unsigned* b_out,
-    unsigned r_in, unsigned g_in, unsigned b_in,
-    const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in) {
-  unsigned r = 0, g = 0, b = 0;
-  unsigned mul = 65535 / ((1u << mode_in->bitdepth) - 1u); /*65535, 21845, 4369, 257, 1*/
-  unsigned shift = 16 - mode_out->bitdepth;
-
-  if(mode_in->colortype == LCT_GREY || mode_in->colortype == LCT_GREY_ALPHA) {
-    r = g = b = r_in * mul;
-  } else if(mode_in->colortype == LCT_RGB || mode_in->colortype == LCT_RGBA) {
-    r = r_in * mul;
-    g = g_in * mul;
-    b = b_in * mul;
-  } else if(mode_in->colortype == LCT_PALETTE) {
-    if(r_in >= mode_in->palettesize) return 82;
-    r = mode_in->palette[r_in * 4 + 0] * 257u;
-    g = mode_in->palette[r_in * 4 + 1] * 257u;
-    b = mode_in->palette[r_in * 4 + 2] * 257u;
-  } else {
-    return 31;
-  }
-
-  /* now convert to output format */
-  if(mode_out->colortype == LCT_GREY || mode_out->colortype == LCT_GREY_ALPHA) {
-    *r_out = r >> shift ;
-  } else if(mode_out->colortype == LCT_RGB || mode_out->colortype == LCT_RGBA) {
-    *r_out = r >> shift ;
-    *g_out = g >> shift ;
-    *b_out = b >> shift ;
-  } else if(mode_out->colortype == LCT_PALETTE) {
-    unsigned i;
-    /* a 16-bit color cannot be in the palette */
-    if((r >> 8) != (r & 255) || (g >> 8) != (g & 255) || (b >> 8) != (b & 255)) return 82;
-    for(i = 0; i < mode_out->palettesize; i++) {
-      unsigned j = i * 4;
-      if((r >> 8) == mode_out->palette[j + 0] && (g >> 8) == mode_out->palette[j + 1] &&
-          (b >> 8) == mode_out->palette[j + 2]) {
-        *r_out = i;
-        return 0;
-      }
-    }
-    return 82;
-  } else {
-    return 31;
-  }
-
-  return 0;
-}
-
-#ifdef LODEPNG_COMPILE_ENCODER
-
-void lodepng_color_profile_init(LodePNGColorProfile* profile) {
-  profile->colored = 0;
-  profile->key = 0;
-  profile->key_r = profile->key_g = profile->key_b = 0;
-  profile->alpha = 0;
-  profile->numcolors = 0;
-  profile->bits = 1;
-  profile->numpixels = 0;
-}
-
-/*function used for debug purposes with C++*/
-/*void printColorProfile(LodePNGColorProfile* p) {
-  std::cout << "colored: " << (int)p->colored << ", ";
-  std::cout << "key: " << (int)p->key << ", ";
-  std::cout << "key_r: " << (int)p->key_r << ", ";
-  std::cout << "key_g: " << (int)p->key_g << ", ";
-  std::cout << "key_b: " << (int)p->key_b << ", ";
-  std::cout << "alpha: " << (int)p->alpha << ", ";
-  std::cout << "numcolors: " << (int)p->numcolors << ", ";
-  std::cout << "bits: " << (int)p->bits << std::endl;
-}*/
-
-/*Returns how many bits needed to represent given value (max 8 bit)*/
-static unsigned getValueRequiredBits(unsigned char value) {
-  if(value == 0 || value == 255) return 1;
-  /*The scaling of 2-bit and 4-bit values uses multiples of 85 and 17*/
-  if(value % 17 == 0) return value % 85 == 0 ? 2 : 4;
-  return 8;
-}
-
-/*profile must already have been inited.
-It's ok to set some parameters of profile to done already.*/
-unsigned lodepng_get_color_profile(LodePNGColorProfile* profile,
-                                   const unsigned char* in, unsigned w, unsigned h,
-                                   const LodePNGColorMode* mode_in) {
-  unsigned error = 0;
-  size_t i;
-  ColorTree tree;
-  size_t numpixels = (size_t)w * (size_t)h;
-
-  /* mark things as done already if it would be impossible to have a more expensive case */
-  unsigned colored_done = lodepng_is_greyscale_type(mode_in) ? 1 : 0;
-  unsigned alpha_done = lodepng_can_have_alpha(mode_in) ? 0 : 1;
-  unsigned numcolors_done = 0;
-  unsigned bpp = lodepng_get_bpp(mode_in);
-  unsigned bits_done = (profile->bits == 1 && bpp == 1) ? 1 : 0;
-  unsigned sixteen = 0; /* whether the input image is 16 bit */
-  unsigned maxnumcolors = 257;
-  if(bpp <= 8) maxnumcolors = LODEPNG_MIN(257, profile->numcolors + (1u << bpp));
-
-  profile->numpixels += numpixels;
-
-  color_tree_init(&tree);
-
-  /*If the profile was already filled in from previous data, fill its palette in tree
-  and mark things as done already if we know they are the most expensive case already*/
-  if(profile->alpha) alpha_done = 1;
-  if(profile->colored) colored_done = 1;
-  if(profile->bits == 16) numcolors_done = 1;
-  if(profile->bits >= bpp) bits_done = 1;
-  if(profile->numcolors >= maxnumcolors) numcolors_done = 1;
-
-  if(!numcolors_done) {
-    for(i = 0; i < profile->numcolors; i++) {
-      const unsigned char* color = &profile->palette[i * 4];
-      color_tree_add(&tree, color[0], color[1], color[2], color[3], (unsigned int)i);
-    }
-  }
-
-  /*Check if the 16-bit input is truly 16-bit*/
-  if(mode_in->bitdepth == 16 && !sixteen) {
-    unsigned short r, g, b, a;
-    for(i = 0; i != numpixels; ++i) {
-      getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in);
-      if((r & 255) != ((r >> 8) & 255) || (g & 255) != ((g >> 8) & 255) ||
-         (b & 255) != ((b >> 8) & 255) || (a & 255) != ((a >> 8) & 255)) /*first and second byte differ*/ {
-        profile->bits = 16;
-        sixteen = 1;
-        bits_done = 1;
-        numcolors_done = 1; /*counting colors no longer useful, palette doesn't support 16-bit*/
-        break;
-      }
-    }
-  }
-
-  if(sixteen) {
-    unsigned short r = 0, g = 0, b = 0, a = 0;
-
-    for(i = 0; i != numpixels; ++i) {
-      getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in);
-
-      if(!colored_done && (r != g || r != b)) {
-        profile->colored = 1;
-        colored_done = 1;
-      }
-
-      if(!alpha_done) {
-        unsigned matchkey = (r == profile->key_r && g == profile->key_g && b == profile->key_b);
-        if(a != 65535 && (a != 0 || (profile->key && !matchkey))) {
-          profile->alpha = 1;
-          profile->key = 0;
-          alpha_done = 1;
-        } else if(a == 0 && !profile->alpha && !profile->key) {
-          profile->key = 1;
-          profile->key_r = r;
-          profile->key_g = g;
-          profile->key_b = b;
-        } else if(a == 65535 && profile->key && matchkey) {
-          /* Color key cannot be used if an opaque pixel also has that RGB color. */
-          profile->alpha = 1;
-          profile->key = 0;
-          alpha_done = 1;
-        }
-      }
-      if(alpha_done && numcolors_done && colored_done && bits_done) break;
-    }
-
-    if(profile->key && !profile->alpha) {
-      for(i = 0; i != numpixels; ++i) {
-        getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in);
-        if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) {
-          /* Color key cannot be used if an opaque pixel also has that RGB color. */
-          profile->alpha = 1;
-          profile->key = 0;
-          alpha_done = 1;
-        }
-      }
-    }
-  } else /* < 16-bit */ {
-    unsigned char r = 0, g = 0, b = 0, a = 0;
-    for(i = 0; i != numpixels; ++i) {
-      getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in);
-
-      if(!bits_done && profile->bits < 8) {
-        /*only r is checked, < 8 bits is only relevant for grayscale*/
-        unsigned bits = getValueRequiredBits(r);
-        if(bits > profile->bits) profile->bits = bits;
-      }
-      bits_done = (profile->bits >= bpp);
-
-      if(!colored_done && (r != g || r != b)) {
-        profile->colored = 1;
-        colored_done = 1;
-        if(profile->bits < 8) profile->bits = 8; /*PNG has no colored modes with less than 8-bit per channel*/
-      }
-
-      if(!alpha_done) {
-        unsigned matchkey = (r == profile->key_r && g == profile->key_g && b == profile->key_b);
-        if(a != 255 && (a != 0 || (profile->key && !matchkey))) {
-          profile->alpha = 1;
-          profile->key = 0;
-          alpha_done = 1;
-          if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
-        } else if(a == 0 && !profile->alpha && !profile->key) {
-          profile->key = 1;
-          profile->key_r = r;
-          profile->key_g = g;
-          profile->key_b = b;
-        } else if(a == 255 && profile->key && matchkey) {
-          /* Color key cannot be used if an opaque pixel also has that RGB color. */
-          profile->alpha = 1;
-          profile->key = 0;
-          alpha_done = 1;
-          if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
-        }
-      }
-
-      if(!numcolors_done) {
-        if(!color_tree_has(&tree, r, g, b, a)) {
-          color_tree_add(&tree, r, g, b, a, profile->numcolors);
-          if(profile->numcolors < 256) {
-            unsigned char* p = profile->palette;
-            unsigned n = profile->numcolors;
-            p[n * 4 + 0] = r;
-            p[n * 4 + 1] = g;
-            p[n * 4 + 2] = b;
-            p[n * 4 + 3] = a;
-          }
-          ++profile->numcolors;
-          numcolors_done = profile->numcolors >= maxnumcolors;
-        }
-      }
-
-      if(alpha_done && numcolors_done && colored_done && bits_done) break;
-    }
-
-    if(profile->key && !profile->alpha) {
-      for(i = 0; i != numpixels; ++i) {
-        getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in);
-        if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) {
-          /* Color key cannot be used if an opaque pixel also has that RGB color. */
-          profile->alpha = 1;
-          profile->key = 0;
-          alpha_done = 1;
-          if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
-        }
-      }
-    }
-
-    /*make the profile's key always 16-bit for consistency - repeat each byte twice*/
-    profile->key_r += (profile->key_r << 8);
-    profile->key_g += (profile->key_g << 8);
-    profile->key_b += (profile->key_b << 8);
-  }
-
-  color_tree_cleanup(&tree);
-  return error;
-}
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-/*Adds a single color to the color profile. The profile must already have been inited. The color must be given as 16-bit
-(with 2 bytes repeating for 8-bit and 65535 for opaque alpha channel). This function is expensive, do not call it for
-all pixels of an image but only for a few additional values. */
-static unsigned lodepng_color_profile_add(LodePNGColorProfile* profile,
-                                          unsigned r, unsigned g, unsigned b, unsigned a) {
-  unsigned error = 0;
-  unsigned char image[8];
-  LodePNGColorMode mode;
-  lodepng_color_mode_init(&mode);
-  image[0] = r >> 8; image[1] = r; image[2] = g >> 8; image[3] = g;
-  image[4] = b >> 8; image[5] = b; image[6] = a >> 8; image[7] = a;
-  mode.bitdepth = 16;
-  mode.colortype = LCT_RGBA;
-  error = lodepng_get_color_profile(profile, image, 1, 1, &mode);
-  lodepng_color_mode_cleanup(&mode);
-  return error;
-}
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-
-/*Autochoose color model given the computed profile. mode_in is to copy palette order from
-when relevant.*/
-static unsigned auto_choose_color_from_profile(LodePNGColorMode* mode_out,
-                                               const LodePNGColorMode* mode_in,
-                                               const LodePNGColorProfile* prof) {
-  unsigned error = 0;
-  unsigned palettebits, palette_ok;
-  size_t i, n;
-  size_t numpixels = prof->numpixels;
-
-  unsigned alpha = prof->alpha;
-  unsigned key = prof->key;
-  unsigned bits = prof->bits;
-
-  mode_out->key_defined = 0;
-
-  if(key && numpixels <= 16) {
-    alpha = 1; /*too few pixels to justify tRNS chunk overhead*/
-    key = 0;
-    if(bits < 8) bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
-  }
-  n = prof->numcolors;
-  palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8));
-  palette_ok = n <= 256 && bits <= 8;
-  if(numpixels < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/
-  if(!prof->colored && bits <= palettebits) palette_ok = 0; /*gray is less overhead*/
-
-  if(palette_ok) {
-    const unsigned char* p = prof->palette;
-    lodepng_palette_clear(mode_out); /*remove potential earlier palette*/
-    for(i = 0; i != prof->numcolors; ++i) {
-      error = lodepng_palette_add(mode_out, p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3]);
-      if(error) break;
-    }
-
-    mode_out->colortype = LCT_PALETTE;
-    mode_out->bitdepth = palettebits;
-
-    if(mode_in->colortype == LCT_PALETTE && mode_in->palettesize >= mode_out->palettesize
-        && mode_in->bitdepth == mode_out->bitdepth) {
-      /*If input should have same palette colors, keep original to preserve its order and prevent conversion*/
-      lodepng_color_mode_cleanup(mode_out);
-      lodepng_color_mode_copy(mode_out, mode_in);
-    }
-  } else /*8-bit or 16-bit per channel*/ {
-    mode_out->bitdepth = bits;
-    mode_out->colortype = alpha ? (prof->colored ? LCT_RGBA : LCT_GREY_ALPHA)
-                                : (prof->colored ? LCT_RGB : LCT_GREY);
-
-    if(key) {
-      unsigned mask = (1u << mode_out->bitdepth) - 1u; /*profile always uses 16-bit, mask converts it*/
-      mode_out->key_r = prof->key_r & mask;
-      mode_out->key_g = prof->key_g & mask;
-      mode_out->key_b = prof->key_b & mask;
-      mode_out->key_defined = 1;
-    }
-  }
-
-  return error;
-}
-
-/*Automatically chooses color type that gives smallest amount of bits in the
-output image, e.g. gray if there are only grayscale pixels, palette if there
-are less than 256 colors, color key if only single transparent color, ...
-Updates values of mode with a potentially smaller color model. mode_out should
-contain the user chosen color model, but will be overwritten with the new chosen one.*/
-unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out,
-                                   const unsigned char* image, unsigned w, unsigned h,
-                                   const LodePNGColorMode* mode_in) {
-  unsigned error = 0;
-  LodePNGColorProfile prof;
-  lodepng_color_profile_init(&prof);
-  error = lodepng_get_color_profile(&prof, image, w, h, mode_in);
-  if(error) return error;
-  return auto_choose_color_from_profile(mode_out, mode_in, &prof);
-}
-
-#endif /* #ifdef LODEPNG_COMPILE_ENCODER */
-
-/*
-Paeth predicter, used by PNG filter type 4
-The parameters are of type short, but should come from unsigned chars, the shorts
-are only needed to make the paeth calculation correct.
-*/
-static unsigned char paethPredictor(short a, short b, short c) {
-  short pa = abs(b - c);
-  short pb = abs(a - c);
-  short pc = abs(a + b - c - c);
-
-  if(pc < pa && pc < pb) return (unsigned char)c;
-  else if(pb < pa) return (unsigned char)b;
-  else return (unsigned char)a;
-}
-
-/*shared values used by multiple Adam7 related functions*/
-
-static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/
-static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/
-static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/
-static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/
-
-/*
-Outputs various dimensions and positions in the image related to the Adam7 reduced images.
-passw: output containing the width of the 7 passes
-passh: output containing the height of the 7 passes
-filter_passstart: output containing the index of the start and end of each
- reduced image with filter bytes
-padded_passstart output containing the index of the start and end of each
- reduced image when without filter bytes but with padded scanlines
-passstart: output containing the index of the start and end of each reduced
- image without padding between scanlines, but still padding between the images
-w, h: width and height of non-interlaced image
-bpp: bits per pixel
-"padded" is only relevant if bpp is less than 8 and a scanline or image does not
- end at a full byte
-*/
-static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8],
-                                size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp) {
-  /*the passstart values have 8 values: the 8th one indicates the byte after the end of the 7th (= last) pass*/
-  unsigned i;
-
-  /*calculate width and height in pixels of each pass*/
-  for(i = 0; i != 7; ++i) {
-    passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i];
-    passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i];
-    if(passw[i] == 0) passh[i] = 0;
-    if(passh[i] == 0) passw[i] = 0;
-  }
-
-  filter_passstart[0] = padded_passstart[0] = passstart[0] = 0;
-  for(i = 0; i != 7; ++i) {
-    /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/
-    filter_passstart[i + 1] = filter_passstart[i]
-                            + ((passw[i] && passh[i]) ? passh[i] * (1 + (passw[i] * bpp + 7) / 8) : 0);
-    /*bits padded if needed to fill full byte at end of each scanline*/
-    padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7) / 8);
-    /*only padded at end of reduced image*/
-    passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7) / 8;
-  }
-}
-
-#ifdef LODEPNG_COMPILE_DECODER
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / PNG Decoder                                                            / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*read the information from the header and store it in the LodePNGInfo. return value is error*/
-unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state,
-                         const unsigned char* in, size_t insize) {
-  unsigned width, height;
-  LodePNGInfo* info = &state->info_png;
-  if(insize == 0 || in == 0) {
-    CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/
-  }
-  if(insize < 33) {
-    CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/
-  }
-
-  /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/
-  /* TODO: remove this. One should use a new LodePNGState for new sessions */
-  lodepng_info_cleanup(info);
-  lodepng_info_init(info);
-
-  if(in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71
-     || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) {
-    CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/
-  }
-  if(lodepng_chunk_length(in + 8) != 13) {
-    CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/
-  }
-  if(!lodepng_chunk_type_equals(in + 8, "IHDR")) {
-    CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/
-  }
-
-  /*read the values given in the header*/
-  width = lodepng_read32bitInt(&in[16]);
-  height = lodepng_read32bitInt(&in[20]);
-  info->color.bitdepth = in[24];
-  info->color.colortype = (LodePNGColorType)in[25];
-  info->compression_method = in[26];
-  info->filter_method = in[27];
-  info->interlace_method = in[28];
-
-  if(width == 0 || height == 0) {
-    CERROR_RETURN_ERROR(state->error, 93);
-  }
-
-  if(w) *w = width;
-  if(h) *h = height;
-
-  if(!state->decoder.ignore_crc) {
-    unsigned CRC = lodepng_read32bitInt(&in[29]);
-    unsigned checksum = lodepng_crc32(&in[12], 17);
-    if(CRC != checksum) {
-      CERROR_RETURN_ERROR(state->error, 57); /*invalid CRC*/
-    }
-  }
-
-  /*error: only compression method 0 is allowed in the specification*/
-  if(info->compression_method != 0) CERROR_RETURN_ERROR(state->error, 32);
-  /*error: only filter method 0 is allowed in the specification*/
-  if(info->filter_method != 0) CERROR_RETURN_ERROR(state->error, 33);
-  /*error: only interlace methods 0 and 1 exist in the specification*/
-  if(info->interlace_method > 1) CERROR_RETURN_ERROR(state->error, 34);
-
-  state->error = checkColorValidity(info->color.colortype, info->color.bitdepth);
-  return state->error;
-}
-
-static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon,
-                                 size_t bytewidth, unsigned char filterType, size_t length) {
-  /*
-  For PNG filter method 0
-  unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte,
-  the filter works byte per byte (bytewidth = 1)
-  precon is the previous unfiltered scanline, recon the result, scanline the current one
-  the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead
-  recon and scanline MAY be the same memory address! precon must be disjoint.
-  */
-
-  size_t i;
-  switch(filterType) {
-    case 0:
-      for(i = 0; i != length; ++i) recon[i] = scanline[i];
-      break;
-    case 1:
-      for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
-      for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth];
-      break;
-    case 2:
-      if(precon) {
-        for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i];
-      } else {
-        for(i = 0; i != length; ++i) recon[i] = scanline[i];
-      }
-      break;
-    case 3:
-      if(precon) {
-        for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1);
-        for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1);
-      } else {
-        for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
-        for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1);
-      }
-      break;
-    case 4:
-      if(precon) {
-        for(i = 0; i != bytewidth; ++i) {
-          recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/
-        }
-        for(i = bytewidth; i < length; ++i) {
-          recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]));
-        }
-      } else {
-        for(i = 0; i != bytewidth; ++i) {
-          recon[i] = scanline[i];
-        }
-        for(i = bytewidth; i < length; ++i) {
-          /*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/
-          recon[i] = (scanline[i] + recon[i - bytewidth]);
-        }
-      }
-      break;
-    default: return 36; /*error: unexisting filter type given*/
-  }
-  return 0;
-}
-
-static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) {
-  /*
-  For PNG filter method 0
-  this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 seven times)
-  out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline
-  w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel
-  in and out are allowed to be the same memory address (but aren't the same size since in has the extra filter bytes)
-  */
-
-  unsigned y;
-  unsigned char* prevline = 0;
-
-  /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/
-  size_t bytewidth = (bpp + 7) / 8;
-  size_t linebytes = (w * bpp + 7) / 8;
-
-  for(y = 0; y < h; ++y) {
-    size_t outindex = linebytes * y;
-    size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/
-    unsigned char filterType = in[inindex];
-
-    CERROR_TRY_RETURN(unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes));
-
-    prevline = &out[outindex];
-  }
-
-  return 0;
-}
-
-/*
-in: Adam7 interlaced image, with no padding bits between scanlines, but between
- reduced images so that each reduced image starts at a byte.
-out: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h
-bpp: bits per pixel
-out has the following size in bits: w * h * bpp.
-in is possibly bigger due to padding bits between reduced images.
-out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation
-(because that's likely a little bit faster)
-NOTE: comments about padding bits are only relevant if bpp < 8
-*/
-static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) {
-  unsigned passw[7], passh[7];
-  size_t filter_passstart[8], padded_passstart[8], passstart[8];
-  unsigned i;
-
-  Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
-
-  if(bpp >= 8) {
-    for(i = 0; i != 7; ++i) {
-      unsigned x, y, b;
-      size_t bytewidth = bpp / 8;
-      for(y = 0; y < passh[i]; ++y)
-      for(x = 0; x < passw[i]; ++x) {
-        size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth;
-        size_t pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth;
-        for(b = 0; b < bytewidth; ++b) {
-          out[pixeloutstart + b] = in[pixelinstart + b];
-        }
-      }
-    }
-  } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ {
-    for(i = 0; i != 7; ++i) {
-      unsigned x, y, b;
-      unsigned ilinebits = bpp * passw[i];
-      unsigned olinebits = bpp * w;
-      size_t obp, ibp; /*bit pointers (for out and in buffer)*/
-      for(y = 0; y < passh[i]; ++y)
-      for(x = 0; x < passw[i]; ++x) {
-        ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp);
-        obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp;
-        for(b = 0; b < bpp; ++b) {
-          unsigned char bit = readBitFromReversedStream(&ibp, in);
-          /*note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise*/
-          setBitOfReversedStream0(&obp, out, bit);
-        }
-      }
-    }
-  }
-}
-
-static void removePaddingBits(unsigned char* out, const unsigned char* in,
-                              size_t olinebits, size_t ilinebits, unsigned h) {
-  /*
-  After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need
-  to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers
-  for the Adam7 code, the color convert code and the output to the user.
-  in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must
-  have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits
-  also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7
-  only useful if (ilinebits - olinebits) is a value in the range 1..7
-  */
-  unsigned y;
-  size_t diff = ilinebits - olinebits;
-  size_t ibp = 0, obp = 0; /*input and output bit pointers*/
-  for(y = 0; y < h; ++y) {
-    size_t x;
-    for(x = 0; x < olinebits; ++x) {
-      unsigned char bit = readBitFromReversedStream(&ibp, in);
-      setBitOfReversedStream(&obp, out, bit);
-    }
-    ibp += diff;
-  }
-}
-
-/*out must be buffer big enough to contain full image, and in must contain the full decompressed data from
-the IDAT chunks (with filter index bytes and possible padding bits)
-return value is error*/
-static unsigned postProcessScanlines(unsigned char* out, unsigned char* in,
-                                     unsigned w, unsigned h, const LodePNGInfo* info_png) {
-  /*
-  This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype.
-  Steps:
-  *) if no Adam7: 1) unfilter 2) remove padding bits (= posible extra bits per scanline if bpp < 8)
-  *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace
-  NOTE: the in buffer will be overwritten with intermediate data!
-  */
-  unsigned bpp = lodepng_get_bpp(&info_png->color);
-  if(bpp == 0) return 31; /*error: invalid colortype*/
-
-  if(info_png->interlace_method == 0) {
-    if(bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) {
-      CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp));
-      removePaddingBits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h);
-    }
-    /*we can immediately filter into the out buffer, no other steps needed*/
-    else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp));
-  } else /*interlace_method is 1 (Adam7)*/ {
-    unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8];
-    unsigned i;
-
-    Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
-
-    for(i = 0; i != 7; ++i) {
-      CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp));
-      /*TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline,
-      move bytes instead of bits or move not at all*/
-      if(bpp < 8) {
-        /*remove padding bits in scanlines; after this there still may be padding
-        bits between the different reduced images: each reduced image still starts nicely at a byte*/
-        removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp,
-                          ((passw[i] * bpp + 7) / 8) * 8, passh[i]);
-      }
-    }
-
-    Adam7_deinterlace(out, in, w, h, bpp);
-  }
-
-  return 0;
-}
-
-static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) {
-  unsigned pos = 0, i;
-  if(color->palette) lodepng_free(color->palette);
-  color->palettesize = chunkLength / 3;
-  color->palette = (unsigned char*)lodepng_malloc(4 * color->palettesize);
-  if(!color->palette && color->palettesize) {
-    color->palettesize = 0;
-    return 83; /*alloc fail*/
-  }
-  if(color->palettesize > 256) return 38; /*error: palette too big*/
-
-  for(i = 0; i != color->palettesize; ++i) {
-    color->palette[4 * i + 0] = data[pos++]; /*R*/
-    color->palette[4 * i + 1] = data[pos++]; /*G*/
-    color->palette[4 * i + 2] = data[pos++]; /*B*/
-    color->palette[4 * i + 3] = 255; /*alpha*/
-  }
-
-  return 0; /* OK */
-}
-
-static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) {
-  unsigned i;
-  if(color->colortype == LCT_PALETTE) {
-    /*error: more alpha values given than there are palette entries*/
-    if(chunkLength > color->palettesize) return 39;
-
-    for(i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i];
-  } else if(color->colortype == LCT_GREY) {
-    /*error: this chunk must be 2 bytes for grayscale image*/
-    if(chunkLength != 2) return 30;
-
-    color->key_defined = 1;
-    color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1];
-  } else if(color->colortype == LCT_RGB) {
-    /*error: this chunk must be 6 bytes for RGB image*/
-    if(chunkLength != 6) return 41;
-
-    color->key_defined = 1;
-    color->key_r = 256u * data[0] + data[1];
-    color->key_g = 256u * data[2] + data[3];
-    color->key_b = 256u * data[4] + data[5];
-  }
-  else return 42; /*error: tRNS chunk not allowed for other color models*/
-
-  return 0; /* OK */
-}
-
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-/*background color chunk (bKGD)*/
-static unsigned readChunk_bKGD(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
-  if(info->color.colortype == LCT_PALETTE) {
-    /*error: this chunk must be 1 byte for indexed color image*/
-    if(chunkLength != 1) return 43;
-
-    /*error: invalid palette index, or maybe this chunk appeared before PLTE*/
-    if(data[0] >= info->color.palettesize) return 103;
-
-    info->background_defined = 1;
-    info->background_r = info->background_g = info->background_b = data[0];
-  } else if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) {
-    /*error: this chunk must be 2 bytes for grayscale image*/
-    if(chunkLength != 2) return 44;
-
-    /*the values are truncated to bitdepth in the PNG file*/
-    info->background_defined = 1;
-    info->background_r = info->background_g = info->background_b = 256u * data[0] + data[1];
-  } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) {
-    /*error: this chunk must be 6 bytes for grayscale image*/
-    if(chunkLength != 6) return 45;
-
-    /*the values are truncated to bitdepth in the PNG file*/
-    info->background_defined = 1;
-    info->background_r = 256u * data[0] + data[1];
-    info->background_g = 256u * data[2] + data[3];
-    info->background_b = 256u * data[4] + data[5];
-  }
-
-  return 0; /* OK */
-}
-
-/*text chunk (tEXt)*/
-static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
-  unsigned error = 0;
-  char *key = 0, *str = 0;
-  unsigned i;
-
-  while(!error) /*not really a while loop, only used to break on error*/ {
-    unsigned length, string2_begin;
-
-    length = 0;
-    while(length < chunkLength && data[length] != 0) ++length;
-    /*even though it's not allowed by the standard, no error is thrown if
-    there's no null termination char, if the text is empty*/
-    if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/
-
-    key = (char*)lodepng_malloc(length + 1);
-    if(!key) CERROR_BREAK(error, 83); /*alloc fail*/
-
-    key[length] = 0;
-    for(i = 0; i != length; ++i) key[i] = (char)data[i];
-
-    string2_begin = length + 1; /*skip keyword null terminator*/
-
-    length = (unsigned)(chunkLength < string2_begin ? 0 : chunkLength - string2_begin);
-    str = (char*)lodepng_malloc(length + 1);
-    if(!str) CERROR_BREAK(error, 83); /*alloc fail*/
-
-    str[length] = 0;
-    for(i = 0; i != length; ++i) str[i] = (char)data[string2_begin + i];
-
-    error = lodepng_add_text(info, key, str);
-
-    break;
-  }
-
-  lodepng_free(key);
-  lodepng_free(str);
-
-  return error;
-}
-
-/*compressed text chunk (zTXt)*/
-static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings,
-                               const unsigned char* data, size_t chunkLength) {
-  unsigned error = 0;
-  unsigned i;
-
-  unsigned length, string2_begin;
-  char *key = 0;
-  ucvector decoded;
-
-  ucvector_init(&decoded);
-
-  while(!error) /*not really a while loop, only used to break on error*/ {
-    for(length = 0; length < chunkLength && data[length] != 0; ++length) ;
-    if(length + 2 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/
-    if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/
-
-    key = (char*)lodepng_malloc(length + 1);
-    if(!key) CERROR_BREAK(error, 83); /*alloc fail*/
-
-    key[length] = 0;
-    for(i = 0; i != length; ++i) key[i] = (char)data[i];
-
-    if(data[length + 1] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/
-
-    string2_begin = length + 2;
-    if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/
-
-    length = (unsigned)chunkLength - string2_begin;
-    /*will fail if zlib error, e.g. if length is too small*/
-    error = zlib_decompress(&decoded.data, &decoded.size,
-                            (unsigned char*)(&data[string2_begin]),
-                            length, zlibsettings);
-    if(error) break;
-    ucvector_push_back(&decoded, 0);
-
-    error = lodepng_add_text(info, key, (char*)decoded.data);
-
-    break;
-  }
-
-  lodepng_free(key);
-  ucvector_cleanup(&decoded);
-
-  return error;
-}
-
-/*international text chunk (iTXt)*/
-static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings,
-                               const unsigned char* data, size_t chunkLength) {
-  unsigned error = 0;
-  unsigned i;
-
-  unsigned length, begin, compressed;
-  char *key = 0, *langtag = 0, *transkey = 0;
-  ucvector decoded;
-  ucvector_init(&decoded); /* TODO: only use in case of compressed text */
-
-  while(!error) /*not really a while loop, only used to break on error*/ {
-    /*Quick check if the chunk length isn't too small. Even without check
-    it'd still fail with other error checks below if it's too short. This just gives a different error code.*/
-    if(chunkLength < 5) CERROR_BREAK(error, 30); /*iTXt chunk too short*/
-
-    /*read the key*/
-    for(length = 0; length < chunkLength && data[length] != 0; ++length) ;
-    if(length + 3 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination char, corrupt?*/
-    if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/
-
-    key = (char*)lodepng_malloc(length + 1);
-    if(!key) CERROR_BREAK(error, 83); /*alloc fail*/
-
-    key[length] = 0;
-    for(i = 0; i != length; ++i) key[i] = (char)data[i];
-
-    /*read the compression method*/
-    compressed = data[length + 1];
-    if(data[length + 2] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/
-
-    /*even though it's not allowed by the standard, no error is thrown if
-    there's no null termination char, if the text is empty for the next 3 texts*/
-
-    /*read the langtag*/
-    begin = length + 3;
-    length = 0;
-    for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length;
-
-    langtag = (char*)lodepng_malloc(length + 1);
-    if(!langtag) CERROR_BREAK(error, 83); /*alloc fail*/
-
-    langtag[length] = 0;
-    for(i = 0; i != length; ++i) langtag[i] = (char)data[begin + i];
-
-    /*read the transkey*/
-    begin += length + 1;
-    length = 0;
-    for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length;
-
-    transkey = (char*)lodepng_malloc(length + 1);
-    if(!transkey) CERROR_BREAK(error, 83); /*alloc fail*/
-
-    transkey[length] = 0;
-    for(i = 0; i != length; ++i) transkey[i] = (char)data[begin + i];
-
-    /*read the actual text*/
-    begin += length + 1;
-
-    length = (unsigned)chunkLength < begin ? 0 : (unsigned)chunkLength - begin;
-
-    if(compressed) {
-      /*will fail if zlib error, e.g. if length is too small*/
-      error = zlib_decompress(&decoded.data, &decoded.size,
-                              (unsigned char*)(&data[begin]),
-                              length, zlibsettings);
-      if(error) break;
-      if(decoded.allocsize < decoded.size) decoded.allocsize = decoded.size;
-      ucvector_push_back(&decoded, 0);
-    } else {
-      if(!ucvector_resize(&decoded, length + 1)) CERROR_BREAK(error, 83 /*alloc fail*/);
-
-      decoded.data[length] = 0;
-      for(i = 0; i != length; ++i) decoded.data[i] = data[begin + i];
-    }
-
-    error = lodepng_add_itext(info, key, langtag, transkey, (char*)decoded.data);
-
-    break;
-  }
-
-  lodepng_free(key);
-  lodepng_free(langtag);
-  lodepng_free(transkey);
-  ucvector_cleanup(&decoded);
-
-  return error;
-}
-
-static unsigned readChunk_tIME(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
-  if(chunkLength != 7) return 73; /*invalid tIME chunk size*/
-
-  info->time_defined = 1;
-  info->time.year = 256u * data[0] + data[1];
-  info->time.month = data[2];
-  info->time.day = data[3];
-  info->time.hour = data[4];
-  info->time.minute = data[5];
-  info->time.second = data[6];
-
-  return 0; /* OK */
-}
-
-static unsigned readChunk_pHYs(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
-  if(chunkLength != 9) return 74; /*invalid pHYs chunk size*/
-
-  info->phys_defined = 1;
-  info->phys_x = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3];
-  info->phys_y = 16777216u * data[4] + 65536u * data[5] + 256u * data[6] + data[7];
-  info->phys_unit = data[8];
-
-  return 0; /* OK */
-}
-
-static unsigned readChunk_gAMA(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
-  if(chunkLength != 4) return 96; /*invalid gAMA chunk size*/
-
-  info->gama_defined = 1;
-  info->gama_gamma = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3];
-
-  return 0; /* OK */
-}
-
-static unsigned readChunk_cHRM(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
-  if(chunkLength != 32) return 97; /*invalid cHRM chunk size*/
-
-  info->chrm_defined = 1;
-  info->chrm_white_x = 16777216u * data[ 0] + 65536u * data[ 1] + 256u * data[ 2] + data[ 3];
-  info->chrm_white_y = 16777216u * data[ 4] + 65536u * data[ 5] + 256u * data[ 6] + data[ 7];
-  info->chrm_red_x   = 16777216u * data[ 8] + 65536u * data[ 9] + 256u * data[10] + data[11];
-  info->chrm_red_y   = 16777216u * data[12] + 65536u * data[13] + 256u * data[14] + data[15];
-  info->chrm_green_x = 16777216u * data[16] + 65536u * data[17] + 256u * data[18] + data[19];
-  info->chrm_green_y = 16777216u * data[20] + 65536u * data[21] + 256u * data[22] + data[23];
-  info->chrm_blue_x  = 16777216u * data[24] + 65536u * data[25] + 256u * data[26] + data[27];
-  info->chrm_blue_y  = 16777216u * data[28] + 65536u * data[29] + 256u * data[30] + data[31];
-
-  return 0; /* OK */
-}
-
-static unsigned readChunk_sRGB(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
-  if(chunkLength != 1) return 98; /*invalid sRGB chunk size (this one is never ignored)*/
-
-  info->srgb_defined = 1;
-  info->srgb_intent = data[0];
-
-  return 0; /* OK */
-}
-
-static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings,
-                               const unsigned char* data, size_t chunkLength) {
-  unsigned error = 0;
-  unsigned i;
-
-  unsigned length, string2_begin;
-  ucvector decoded;
-
-  info->iccp_defined = 1;
-  if(info->iccp_name) lodepng_clear_icc(info);
-
-  for(length = 0; length < chunkLength && data[length] != 0; ++length) ;
-  if(length + 2 >= chunkLength) return 75; /*no null termination, corrupt?*/
-  if(length < 1 || length > 79) return 89; /*keyword too short or long*/
-
-  info->iccp_name = (char*)lodepng_malloc(length + 1);
-  if(!info->iccp_name) return 83; /*alloc fail*/
-
-  info->iccp_name[length] = 0;
-  for(i = 0; i != length; ++i) info->iccp_name[i] = (char)data[i];
-
-  if(data[length + 1] != 0) return 72; /*the 0 byte indicating compression must be 0*/
-
-  string2_begin = length + 2;
-  if(string2_begin > chunkLength) return 75; /*no null termination, corrupt?*/
-
-  length = (unsigned)chunkLength - string2_begin;
-  ucvector_init(&decoded);
-  error = zlib_decompress(&decoded.data, &decoded.size,
-                          (unsigned char*)(&data[string2_begin]),
-                          length, zlibsettings);
-  if(!error) {
-    info->iccp_profile_size = (unsigned int)decoded.size;
-    info->iccp_profile = (unsigned char*)lodepng_malloc(decoded.size);
-    if(info->iccp_profile) {
-      memcpy(info->iccp_profile, decoded.data, decoded.size);
-    } else {
-      error = 83; /* alloc fail */
-    }
-  }
-  ucvector_cleanup(&decoded);
-  return error;
-}
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-
-unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos,
-                               const unsigned char* in, size_t insize) {
-  const unsigned char* chunk = in + pos;
-  unsigned chunkLength;
-  const unsigned char* data;
-  unsigned unhandled = 0;
-  unsigned error = 0;
-
-  if (pos + 4 > insize) return 30;
-  chunkLength = lodepng_chunk_length(chunk);
-  if(chunkLength > 2147483647) return 63;
-  data = lodepng_chunk_data_const(chunk);
-  if(data + chunkLength + 4 > in + insize) return 30;
-
-  if(lodepng_chunk_type_equals(chunk, "PLTE")) {
-    error = readChunk_PLTE(&state->info_png.color, data, chunkLength);
-  } else if(lodepng_chunk_type_equals(chunk, "tRNS")) {
-    error = readChunk_tRNS(&state->info_png.color, data, chunkLength);
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-  } else if(lodepng_chunk_type_equals(chunk, "bKGD")) {
-    error = readChunk_bKGD(&state->info_png, data, chunkLength);
-  } else if(lodepng_chunk_type_equals(chunk, "tEXt")) {
-    error = readChunk_tEXt(&state->info_png, data, chunkLength);
-  } else if(lodepng_chunk_type_equals(chunk, "zTXt")) {
-    error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
-  } else if(lodepng_chunk_type_equals(chunk, "iTXt")) {
-    error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
-  } else if(lodepng_chunk_type_equals(chunk, "tIME")) {
-    error = readChunk_tIME(&state->info_png, data, chunkLength);
-  } else if(lodepng_chunk_type_equals(chunk, "pHYs")) {
-    error = readChunk_pHYs(&state->info_png, data, chunkLength);
-  } else if(lodepng_chunk_type_equals(chunk, "gAMA")) {
-    error = readChunk_gAMA(&state->info_png, data, chunkLength);
-  } else if(lodepng_chunk_type_equals(chunk, "cHRM")) {
-    error = readChunk_cHRM(&state->info_png, data, chunkLength);
-  } else if(lodepng_chunk_type_equals(chunk, "sRGB")) {
-    error = readChunk_sRGB(&state->info_png, data, chunkLength);
-  } else if(lodepng_chunk_type_equals(chunk, "iCCP")) {
-    error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-  } else {
-    /* unhandled chunk is ok (is not an error) */
-    unhandled = 1;
-  }
-
-  if(!error && !unhandled && !state->decoder.ignore_crc) {
-    if(lodepng_chunk_check_crc(chunk)) return 57; /*invalid CRC*/
-  }
-
-  return error;
-}
-
-/*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/
-static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
-                          LodePNGState* state,
-                          const unsigned char* in, size_t insize) {
-  unsigned char IEND = 0;
-  const unsigned char* chunk;
-  size_t i;
-  ucvector idat; /*the data from idat chunks*/
-  ucvector scanlines;
-  size_t predict;
-  size_t outsize = 0;
-
-  /*for unknown chunk order*/
-  unsigned unknown = 0;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-  unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-
-
-  /* safe output values in case error happens */
-  *out = 0;
-  *w = *h = 0;
-
-  state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/
-  if(state->error) return;
-
-  if(lodepng_pixel_overflow(*w, *h, &state->info_png.color, &state->info_raw)) {
-    CERROR_RETURN(state->error, 92); /*overflow possible due to amount of pixels*/
-  }
-
-  ucvector_init(&idat);
-  chunk = &in[33]; /*first byte of the first chunk after the header*/
-
-  /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk.
-  IDAT data is put at the start of the in buffer*/
-  while(!IEND && !state->error) {
-    unsigned chunkLength;
-    const unsigned char* data; /*the data in the chunk*/
-
-    /*error: size of the in buffer too small to contain next chunk*/
-    if((size_t)((chunk - in) + 12) > insize || chunk < in) {
-      if(state->decoder.ignore_end) break; /*other errors may still happen though*/
-      CERROR_BREAK(state->error, 30);
-    }
-
-    /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/
-    chunkLength = lodepng_chunk_length(chunk);
-    /*error: chunk length larger than the max PNG chunk size*/
-    if(chunkLength > 2147483647) {
-      if(state->decoder.ignore_end) break; /*other errors may still happen though*/
-      CERROR_BREAK(state->error, 63);
-    }
-
-    if((size_t)((chunk - in) + chunkLength + 12) > insize || (chunk + chunkLength + 12) < in) {
-      CERROR_BREAK(state->error, 64); /*error: size of the in buffer too small to contain next chunk*/
-    }
-
-    data = lodepng_chunk_data_const(chunk);
-
-    unknown = 0;
-
-    /*IDAT chunk, containing compressed image data*/
-    if(lodepng_chunk_type_equals(chunk, "IDAT")) {
-      size_t oldsize = idat.size;
-      size_t newsize;
-      if(lodepng_addofl(oldsize, chunkLength, &newsize)) CERROR_BREAK(state->error, 95);
-      if(!ucvector_resize(&idat, newsize)) CERROR_BREAK(state->error, 83 /*alloc fail*/);
-      for(i = 0; i != chunkLength; ++i) idat.data[oldsize + i] = data[i];
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-      critical_pos = 3;
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-    } else if(lodepng_chunk_type_equals(chunk, "IEND")) {
-      /*IEND chunk*/
-      IEND = 1;
-    } else if(lodepng_chunk_type_equals(chunk, "PLTE")) {
-      /*palette chunk (PLTE)*/
-      state->error = readChunk_PLTE(&state->info_png.color, data, chunkLength);
-      if(state->error) break;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-      critical_pos = 2;
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-    } else if(lodepng_chunk_type_equals(chunk, "tRNS")) {
-      /*palette transparency chunk (tRNS). Even though this one is an ancillary chunk , it is still compiled
-      in without 'LODEPNG_COMPILE_ANCILLARY_CHUNKS' because it contains essential color information that
-      affects the alpha channel of pixels. */
-      state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength);
-      if(state->error) break;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-      /*background color chunk (bKGD)*/
-    } else if(lodepng_chunk_type_equals(chunk, "bKGD")) {
-      state->error = readChunk_bKGD(&state->info_png, data, chunkLength);
-      if(state->error) break;
-    } else if(lodepng_chunk_type_equals(chunk, "tEXt")) {
-      /*text chunk (tEXt)*/
-      if(state->decoder.read_text_chunks) {
-        state->error = readChunk_tEXt(&state->info_png, data, chunkLength);
-        if(state->error) break;
-      }
-    } else if(lodepng_chunk_type_equals(chunk, "zTXt")) {
-      /*compressed text chunk (zTXt)*/
-      if(state->decoder.read_text_chunks) {
-        state->error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
-        if(state->error) break;
-      }
-    } else if(lodepng_chunk_type_equals(chunk, "iTXt")) {
-      /*international text chunk (iTXt)*/
-      if(state->decoder.read_text_chunks) {
-        state->error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
-        if(state->error) break;
-      }
-    } else if(lodepng_chunk_type_equals(chunk, "tIME")) {
-      state->error = readChunk_tIME(&state->info_png, data, chunkLength);
-      if(state->error) break;
-    } else if(lodepng_chunk_type_equals(chunk, "pHYs")) {
-      state->error = readChunk_pHYs(&state->info_png, data, chunkLength);
-      if(state->error) break;
-    } else if(lodepng_chunk_type_equals(chunk, "gAMA")) {
-      state->error = readChunk_gAMA(&state->info_png, data, chunkLength);
-      if(state->error) break;
-    } else if(lodepng_chunk_type_equals(chunk, "cHRM")) {
-      state->error = readChunk_cHRM(&state->info_png, data, chunkLength);
-      if(state->error) break;
-    } else if(lodepng_chunk_type_equals(chunk, "sRGB")) {
-      state->error = readChunk_sRGB(&state->info_png, data, chunkLength);
-      if(state->error) break;
-    } else if(lodepng_chunk_type_equals(chunk, "iCCP")) {
-      state->error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
-      if(state->error) break;
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-    } else /*it's not an implemented chunk type, so ignore it: skip over the data*/ {
-      /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/
-      if(!state->decoder.ignore_critical && !lodepng_chunk_ancillary(chunk)) {
-        CERROR_BREAK(state->error, 69);
-      }
-
-      unknown = 1;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-      if(state->decoder.remember_unknown_chunks) {
-        state->error = lodepng_chunk_append(&state->info_png.unknown_chunks_data[critical_pos - 1],
-                                            &state->info_png.unknown_chunks_size[critical_pos - 1], chunk);
-        if(state->error) break;
-      }
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-    }
-
-    if(!state->decoder.ignore_crc && !unknown) /*check CRC if wanted, only on known chunk types*/ {
-      if(lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/
-    }
-
-    if(!IEND) chunk = lodepng_chunk_next_const(chunk);
-  }
-
-  ucvector_init(&scanlines);
-  /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation.
-  If the decompressed size does not match the prediction, the image must be corrupt.*/
-  if(state->info_png.interlace_method == 0) {
-    predict = lodepng_get_raw_size_idat(*w, *h, &state->info_png.color);
-  } else {
-    /*Adam-7 interlaced: predicted size is the sum of the 7 sub-images sizes*/
-    const LodePNGColorMode* color = &state->info_png.color;
-    predict = 0;
-    predict += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, color);
-    if(*w > 4) predict += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, color);
-    predict += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, color);
-    if(*w > 2) predict += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, color);
-    predict += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, color);
-    if(*w > 1) predict += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, color);
-    predict += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, color);
-  }
-  if(!state->error && !ucvector_reserve(&scanlines, predict)) state->error = 83; /*alloc fail*/
-  if(!state->error) {
-    state->error = zlib_decompress(&scanlines.data, &scanlines.size, idat.data,
-                                   idat.size, &state->decoder.zlibsettings);
-    if(!state->error && scanlines.size != predict) state->error = 91; /*decompressed size doesn't match prediction*/
-  }
-  ucvector_cleanup(&idat);
-
-  if(!state->error) {
-    outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color);
-    *out = (unsigned char*)lodepng_malloc(outsize);
-    if(!*out) state->error = 83; /*alloc fail*/
-  }
-  if(!state->error) {
-    for(i = 0; i < outsize; i++) (*out)[i] = 0;
-    state->error = postProcessScanlines(*out, scanlines.data, *w, *h, &state->info_png);
-  }
-  ucvector_cleanup(&scanlines);
-}
-
-unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h,
-                        LodePNGState* state,
-                        const unsigned char* in, size_t insize) {
-  *out = 0;
-  decodeGeneric(out, w, h, state, in, insize);
-  if(state->error) return state->error;
-  if(!state->decoder.color_convert || lodepng_color_mode_equal(&state->info_raw, &state->info_png.color)) {
-    /*same color type, no copying or converting of data needed*/
-    /*store the info_png color settings on the info_raw so that the info_raw still reflects what colortype
-    the raw image has to the end user*/
-    if(!state->decoder.color_convert) {
-      state->error = lodepng_color_mode_copy(&state->info_raw, &state->info_png.color);
-      if(state->error) return state->error;
-    }
-  } else {
-    /*color conversion needed; sort of copy of the data*/
-    unsigned char* data = *out;
-    size_t outsize;
-
-    /*TODO: check if this works according to the statement in the documentation: "The converter can convert
-    from grayscale input color type, to 8-bit grayscale or grayscale with alpha"*/
-    if(!(state->info_raw.colortype == LCT_RGB || state->info_raw.colortype == LCT_RGBA)
-       && !(state->info_raw.bitdepth == 8)) {
-      return 56; /*unsupported color mode conversion*/
-    }
-
-    outsize = lodepng_get_raw_size(*w, *h, &state->info_raw);
-    *out = (unsigned char*)lodepng_malloc(outsize);
-    if(!(*out)) {
-      state->error = 83; /*alloc fail*/
-    }
-    else state->error = lodepng_convert(*out, data, &state->info_raw,
-                                        &state->info_png.color, *w, *h);
-    lodepng_free(data);
-  }
-  return state->error;
-}
-
-unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in,
-                               size_t insize, LodePNGColorType colortype, unsigned bitdepth) {
-  unsigned error;
-  LodePNGState state;
-  lodepng_state_init(&state);
-  state.info_raw.colortype = colortype;
-  state.info_raw.bitdepth = bitdepth;
-  error = lodepng_decode(out, w, h, &state, in, insize);
-  lodepng_state_cleanup(&state);
-  return error;
-}
-
-unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) {
-  return lodepng_decode_memory(out, w, h, in, insize, LCT_RGBA, 8);
-}
-
-unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) {
-  return lodepng_decode_memory(out, w, h, in, insize, LCT_RGB, 8);
-}
-
-#ifdef LODEPNG_COMPILE_DISK
-unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename,
-                             LodePNGColorType colortype, unsigned bitdepth) {
-  unsigned char* buffer = 0;
-  size_t buffersize;
-  unsigned error;
-  /* safe output values in case error happens */
-  *out = 0;
-  *w = *h = 0;
-  error = lodepng_load_file(&buffer, &buffersize, filename);
-  if(!error) error = lodepng_decode_memory(out, w, h, buffer, buffersize, colortype, bitdepth);
-  lodepng_free(buffer);
-  return error;
-}
-
-unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) {
-  return lodepng_decode_file(out, w, h, filename, LCT_RGBA, 8);
-}
-
-unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) {
-  return lodepng_decode_file(out, w, h, filename, LCT_RGB, 8);
-}
-#endif /*LODEPNG_COMPILE_DISK*/
-
-void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) {
-  settings->color_convert = 1;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-  settings->read_text_chunks = 1;
-  settings->remember_unknown_chunks = 0;
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-  settings->ignore_crc = 0;
-  settings->ignore_critical = 0;
-  settings->ignore_end = 0;
-  lodepng_decompress_settings_init(&settings->zlibsettings);
-}
-
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER)
-
-void lodepng_state_init(LodePNGState* state) {
-#ifdef LODEPNG_COMPILE_DECODER
-  lodepng_decoder_settings_init(&state->decoder);
-#endif /*LODEPNG_COMPILE_DECODER*/
-#ifdef LODEPNG_COMPILE_ENCODER
-  lodepng_encoder_settings_init(&state->encoder);
-#endif /*LODEPNG_COMPILE_ENCODER*/
-  lodepng_color_mode_init(&state->info_raw);
-  lodepng_info_init(&state->info_png);
-  state->error = 1;
-}
-
-void lodepng_state_cleanup(LodePNGState* state) {
-  lodepng_color_mode_cleanup(&state->info_raw);
-  lodepng_info_cleanup(&state->info_png);
-}
-
-void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source) {
-  lodepng_state_cleanup(dest);
-  *dest = *source;
-  lodepng_color_mode_init(&dest->info_raw);
-  lodepng_info_init(&dest->info_png);
-  dest->error = lodepng_color_mode_copy(&dest->info_raw, &source->info_raw); if(dest->error) return;
-  dest->error = lodepng_info_copy(&dest->info_png, &source->info_png); if(dest->error) return;
-}
-
-#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */
-
-#ifdef LODEPNG_COMPILE_ENCODER
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / PNG Encoder                                                            / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*chunkName must be string of 4 characters*/
-static unsigned addChunk(ucvector* out, const char* chunkName, const unsigned char* data, size_t length) {
-  CERROR_TRY_RETURN(lodepng_chunk_create(&out->data, &out->size, (unsigned)length, chunkName, data));
-  out->allocsize = out->size; /*fix the allocsize again*/
-  return 0;
-}
-
-static void writeSignature(ucvector* out) {
-  /*8 bytes PNG signature, aka the magic bytes*/
-  ucvector_push_back(out, 137);
-  ucvector_push_back(out, 80);
-  ucvector_push_back(out, 78);
-  ucvector_push_back(out, 71);
-  ucvector_push_back(out, 13);
-  ucvector_push_back(out, 10);
-  ucvector_push_back(out, 26);
-  ucvector_push_back(out, 10);
-}
-
-static unsigned addChunk_IHDR(ucvector* out, unsigned w, unsigned h,
-                              LodePNGColorType colortype, unsigned bitdepth, unsigned interlace_method) {
-  unsigned error = 0;
-  ucvector header;
-  ucvector_init(&header);
-
-  lodepng_add32bitInt(&header, w); /*width*/
-  lodepng_add32bitInt(&header, h); /*height*/
-  ucvector_push_back(&header, (unsigned char)bitdepth); /*bit depth*/
-  ucvector_push_back(&header, (unsigned char)colortype); /*color type*/
-  ucvector_push_back(&header, 0); /*compression method*/
-  ucvector_push_back(&header, 0); /*filter method*/
-  ucvector_push_back(&header, interlace_method); /*interlace method*/
-
-  error = addChunk(out, "IHDR", header.data, header.size);
-  ucvector_cleanup(&header);
-
-  return error;
-}
-
-static unsigned addChunk_PLTE(ucvector* out, const LodePNGColorMode* info) {
-  unsigned error = 0;
-  size_t i;
-  ucvector PLTE;
-  ucvector_init(&PLTE);
-  for(i = 0; i != info->palettesize * 4; ++i) {
-    /*add all channels except alpha channel*/
-    if(i % 4 != 3) ucvector_push_back(&PLTE, info->palette[i]);
-  }
-  error = addChunk(out, "PLTE", PLTE.data, PLTE.size);
-  ucvector_cleanup(&PLTE);
-
-  return error;
-}
-
-static unsigned addChunk_tRNS(ucvector* out, const LodePNGColorMode* info) {
-  unsigned error = 0;
-  size_t i;
-  ucvector tRNS;
-  ucvector_init(&tRNS);
-  if(info->colortype == LCT_PALETTE) {
-    size_t amount = info->palettesize;
-    /*the tail of palette values that all have 255 as alpha, does not have to be encoded*/
-    for(i = info->palettesize; i != 0; --i) {
-      if(info->palette[4 * (i - 1) + 3] == 255) --amount;
-      else break;
-    }
-    /*add only alpha channel*/
-    for(i = 0; i != amount; ++i) ucvector_push_back(&tRNS, info->palette[4 * i + 3]);
-  } else if(info->colortype == LCT_GREY) {
-    if(info->key_defined) {
-      ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8));
-      ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255));
-    }
-  } else if(info->colortype == LCT_RGB) {
-    if(info->key_defined) {
-      ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8));
-      ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255));
-      ucvector_push_back(&tRNS, (unsigned char)(info->key_g >> 8));
-      ucvector_push_back(&tRNS, (unsigned char)(info->key_g & 255));
-      ucvector_push_back(&tRNS, (unsigned char)(info->key_b >> 8));
-      ucvector_push_back(&tRNS, (unsigned char)(info->key_b & 255));
-    }
-  }
-
-  error = addChunk(out, "tRNS", tRNS.data, tRNS.size);
-  ucvector_cleanup(&tRNS);
-
-  return error;
-}
-
-static unsigned addChunk_IDAT(ucvector* out, const unsigned char* data, size_t datasize,
-                              LodePNGCompressSettings* zlibsettings) {
-  ucvector zlibdata;
-  unsigned error = 0;
-
-  /*compress with the Zlib compressor*/
-  ucvector_init(&zlibdata);
-  error = zlib_compress(&zlibdata.data, &zlibdata.size, data, datasize, zlibsettings);
-  if(!error) error = addChunk(out, "IDAT", zlibdata.data, zlibdata.size);
-  ucvector_cleanup(&zlibdata);
-
-  return error;
-}
-
-static unsigned addChunk_IEND(ucvector* out) {
-  unsigned error = 0;
-  error = addChunk(out, "IEND", 0, 0);
-  return error;
-}
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-
-static unsigned addChunk_tEXt(ucvector* out, const char* keyword, const char* textstring) {
-  unsigned error = 0;
-  size_t i;
-  ucvector text;
-  ucvector_init(&text);
-  for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)keyword[i]);
-  if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/
-  ucvector_push_back(&text, 0); /*0 termination char*/
-  for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)textstring[i]);
-  error = addChunk(out, "tEXt", text.data, text.size);
-  ucvector_cleanup(&text);
-
-  return error;
-}
-
-static unsigned addChunk_zTXt(ucvector* out, const char* keyword, const char* textstring,
-                              LodePNGCompressSettings* zlibsettings) {
-  unsigned error = 0;
-  ucvector data, compressed;
-  size_t i, textsize = strlen(textstring);
-
-  ucvector_init(&data);
-  ucvector_init(&compressed);
-  for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]);
-  if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/
-  ucvector_push_back(&data, 0); /*0 termination char*/
-  ucvector_push_back(&data, 0); /*compression method: 0*/
-
-  error = zlib_compress(&compressed.data, &compressed.size,
-                        (unsigned char*)textstring, textsize, zlibsettings);
-  if(!error) {
-    for(i = 0; i != compressed.size; ++i) ucvector_push_back(&data, compressed.data[i]);
-    error = addChunk(out, "zTXt", data.data, data.size);
-  }
-
-  ucvector_cleanup(&compressed);
-  ucvector_cleanup(&data);
-  return error;
-}
-
-static unsigned addChunk_iTXt(ucvector* out, unsigned compressed, const char* keyword, const char* langtag,
-                              const char* transkey, const char* textstring, LodePNGCompressSettings* zlibsettings) {
-  unsigned error = 0;
-  ucvector data;
-  size_t i, textsize = strlen(textstring);
-
-  ucvector_init(&data);
-
-  for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]);
-  if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/
-  ucvector_push_back(&data, 0); /*null termination char*/
-  ucvector_push_back(&data, compressed ? 1 : 0); /*compression flag*/
-  ucvector_push_back(&data, 0); /*compression method*/
-  for(i = 0; langtag[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)langtag[i]);
-  ucvector_push_back(&data, 0); /*null termination char*/
-  for(i = 0; transkey[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)transkey[i]);
-  ucvector_push_back(&data, 0); /*null termination char*/
-
-  if(compressed) {
-    ucvector compressed_data;
-    ucvector_init(&compressed_data);
-    error = zlib_compress(&compressed_data.data, &compressed_data.size,
-                          (unsigned char*)textstring, textsize, zlibsettings);
-    if(!error) {
-      for(i = 0; i != compressed_data.size; ++i) ucvector_push_back(&data, compressed_data.data[i]);
-    }
-    ucvector_cleanup(&compressed_data);
-  } else /*not compressed*/ {
-    for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)textstring[i]);
-  }
-
-  if(!error) error = addChunk(out, "iTXt", data.data, data.size);
-  ucvector_cleanup(&data);
-  return error;
-}
-
-static unsigned addChunk_bKGD(ucvector* out, const LodePNGInfo* info) {
-  unsigned error = 0;
-  ucvector bKGD;
-  ucvector_init(&bKGD);
-  if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) {
-    ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8));
-    ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255));
-  } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) {
-    ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8));
-    ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255));
-    ucvector_push_back(&bKGD, (unsigned char)(info->background_g >> 8));
-    ucvector_push_back(&bKGD, (unsigned char)(info->background_g & 255));
-    ucvector_push_back(&bKGD, (unsigned char)(info->background_b >> 8));
-    ucvector_push_back(&bKGD, (unsigned char)(info->background_b & 255));
-  } else if(info->color.colortype == LCT_PALETTE) {
-    ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); /*palette index*/
-  }
-
-  error = addChunk(out, "bKGD", bKGD.data, bKGD.size);
-  ucvector_cleanup(&bKGD);
-
-  return error;
-}
-
-static unsigned addChunk_tIME(ucvector* out, const LodePNGTime* time) {
-  unsigned error = 0;
-  unsigned char* data = (unsigned char*)lodepng_malloc(7);
-  if(!data) return 83; /*alloc fail*/
-  data[0] = (unsigned char)(time->year >> 8);
-  data[1] = (unsigned char)(time->year & 255);
-  data[2] = (unsigned char)time->month;
-  data[3] = (unsigned char)time->day;
-  data[4] = (unsigned char)time->hour;
-  data[5] = (unsigned char)time->minute;
-  data[6] = (unsigned char)time->second;
-  error = addChunk(out, "tIME", data, 7);
-  lodepng_free(data);
-  return error;
-}
-
-static unsigned addChunk_pHYs(ucvector* out, const LodePNGInfo* info) {
-  unsigned error = 0;
-  ucvector data;
-  ucvector_init(&data);
-
-  lodepng_add32bitInt(&data, info->phys_x);
-  lodepng_add32bitInt(&data, info->phys_y);
-  ucvector_push_back(&data, info->phys_unit);
-
-  error = addChunk(out, "pHYs", data.data, data.size);
-  ucvector_cleanup(&data);
-
-  return error;
-}
-
-static unsigned addChunk_gAMA(ucvector* out, const LodePNGInfo* info) {
-  unsigned error = 0;
-  ucvector data;
-  ucvector_init(&data);
-
-  lodepng_add32bitInt(&data, info->gama_gamma);
-
-  error = addChunk(out, "gAMA", data.data, data.size);
-  ucvector_cleanup(&data);
-
-  return error;
-}
-
-static unsigned addChunk_cHRM(ucvector* out, const LodePNGInfo* info) {
-  unsigned error = 0;
-  ucvector data;
-  ucvector_init(&data);
-
-  lodepng_add32bitInt(&data, info->chrm_white_x);
-  lodepng_add32bitInt(&data, info->chrm_white_y);
-  lodepng_add32bitInt(&data, info->chrm_red_x);
-  lodepng_add32bitInt(&data, info->chrm_red_y);
-  lodepng_add32bitInt(&data, info->chrm_green_x);
-  lodepng_add32bitInt(&data, info->chrm_green_y);
-  lodepng_add32bitInt(&data, info->chrm_blue_x);
-  lodepng_add32bitInt(&data, info->chrm_blue_y);
-
-  error = addChunk(out, "cHRM", data.data, data.size);
-  ucvector_cleanup(&data);
-
-  return error;
-}
-
-static unsigned addChunk_sRGB(ucvector* out, const LodePNGInfo* info) {
-  unsigned char data = info->srgb_intent;
-  return addChunk(out, "sRGB", &data, 1);
-}
-
-static unsigned addChunk_iCCP(ucvector* out, const LodePNGInfo* info, LodePNGCompressSettings* zlibsettings) {
-  unsigned error = 0;
-  ucvector data, compressed;
-  size_t i;
-
-  ucvector_init(&data);
-  ucvector_init(&compressed);
-  for(i = 0; info->iccp_name[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)info->iccp_name[i]);
-  if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/
-  ucvector_push_back(&data, 0); /*0 termination char*/
-  ucvector_push_back(&data, 0); /*compression method: 0*/
-
-  error = zlib_compress(&compressed.data, &compressed.size,
-                        info->iccp_profile, info->iccp_profile_size, zlibsettings);
-  if(!error) {
-    for(i = 0; i != compressed.size; ++i) ucvector_push_back(&data, compressed.data[i]);
-    error = addChunk(out, "iCCP", data.data, data.size);
-  }
-
-  ucvector_cleanup(&compressed);
-  ucvector_cleanup(&data);
-  return error;
-}
-
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-
-static void filterScanline(unsigned char* out, const unsigned char* scanline, const unsigned char* prevline,
-                           size_t length, size_t bytewidth, unsigned char filterType) {
-  size_t i;
-  switch(filterType) {
-    case 0: /*None*/
-      for(i = 0; i != length; ++i) out[i] = scanline[i];
-      break;
-    case 1: /*Sub*/
-      for(i = 0; i != bytewidth; ++i) out[i] = scanline[i];
-      for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - scanline[i - bytewidth];
-      break;
-    case 2: /*Up*/
-      if(prevline) {
-        for(i = 0; i != length; ++i) out[i] = scanline[i] - prevline[i];
-      } else {
-        for(i = 0; i != length; ++i) out[i] = scanline[i];
-      }
-      break;
-    case 3: /*Average*/
-      if(prevline) {
-        for(i = 0; i != bytewidth; ++i) out[i] = scanline[i] - (prevline[i] >> 1);
-        for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) >> 1);
-      } else {
-        for(i = 0; i != bytewidth; ++i) out[i] = scanline[i];
-        for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - (scanline[i - bytewidth] >> 1);
-      }
-      break;
-    case 4: /*Paeth*/
-      if(prevline) {
-        /*paethPredictor(0, prevline[i], 0) is always prevline[i]*/
-        for(i = 0; i != bytewidth; ++i) out[i] = (scanline[i] - prevline[i]);
-        for(i = bytewidth; i < length; ++i) {
-          out[i] = (scanline[i] - paethPredictor(scanline[i - bytewidth], prevline[i], prevline[i - bytewidth]));
-        }
-      } else {
-        for(i = 0; i != bytewidth; ++i) out[i] = scanline[i];
-        /*paethPredictor(scanline[i - bytewidth], 0, 0) is always scanline[i - bytewidth]*/
-        for(i = bytewidth; i < length; ++i) out[i] = (scanline[i] - scanline[i - bytewidth]);
-      }
-      break;
-    default: return; /*unexisting filter type given*/
-  }
-}
-
-/* log2 approximation. A slight bit faster than std::log. */
-static float flog2(float f) {
-  float result = 0;
-  while(f > 32) { result += 4; f /= 16; }
-  while(f > 2) { ++result; f /= 2; }
-  return result + 1.442695f * (f * f * f / 3 - 3 * f * f / 2 + 3 * f - 1.83333f);
-}
-
-static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h,
-                       const LodePNGColorMode* info, const LodePNGEncoderSettings* settings) {
-  /*
-  For PNG filter method 0
-  out must be a buffer with as size: h + (w * h * bpp + 7) / 8, because there are
-  the scanlines with 1 extra byte per scanline
-  */
-
-  unsigned bpp = lodepng_get_bpp(info);
-  /*the width of a scanline in bytes, not including the filter type*/
-  size_t linebytes = (w * bpp + 7) / 8;
-  /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/
-  size_t bytewidth = (bpp + 7) / 8;
-  const unsigned char* prevline = 0;
-  unsigned x, y;
-  unsigned error = 0;
-  LodePNGFilterStrategy strategy = settings->filter_strategy;
-
-  /*
-  There is a heuristic called the minimum sum of absolute differences heuristic, suggested by the PNG standard:
-   *  If the image type is Palette, or the bit depth is smaller than 8, then do not filter the image (i.e.
-      use fixed filtering, with the filter None).
-   * (The other case) If the image type is Grayscale or RGB (with or without Alpha), and the bit depth is
-     not smaller than 8, then use adaptive filtering heuristic as follows: independently for each row, apply
-     all five filters and select the filter that produces the smallest sum of absolute values per row.
-  This heuristic is used if filter strategy is LFS_MINSUM and filter_palette_zero is true.
-
-  If filter_palette_zero is true and filter_strategy is not LFS_MINSUM, the above heuristic is followed,
-  but for "the other case", whatever strategy filter_strategy is set to instead of the minimum sum
-  heuristic is used.
-  */
-  if(settings->filter_palette_zero &&
-     (info->colortype == LCT_PALETTE || info->bitdepth < 8)) strategy = LFS_ZERO;
-
-  if(bpp == 0) return 31; /*error: invalid color type*/
-
-  if(strategy == LFS_ZERO) {
-    for(y = 0; y != h; ++y) {
-      size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/
-      size_t inindex = linebytes * y;
-      out[outindex] = 0; /*filter type byte*/
-      filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, 0);
-      prevline = &in[inindex];
-    }
-  } else if(strategy == LFS_MINSUM) {
-    /*adaptive filtering*/
-    size_t sum[5];
-    unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/
-    size_t smallest = 0;
-    unsigned char type, bestType = 0;
-
-    for(type = 0; type != 5; ++type) {
-      attempt[type] = (unsigned char*)lodepng_malloc(linebytes);
-      if(!attempt[type]) return 83; /*alloc fail*/
-    }
-
-    if(!error) {
-      for(y = 0; y != h; ++y) {
-        /*try the 5 filter types*/
-        for(type = 0; type != 5; ++type) {
-          filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type);
-
-          /*calculate the sum of the result*/
-          sum[type] = 0;
-          if(type == 0) {
-            for(x = 0; x != linebytes; ++x) sum[type] += (unsigned char)(attempt[type][x]);
-          } else {
-            for(x = 0; x != linebytes; ++x) {
-              /*For differences, each byte should be treated as signed, values above 127 are negative
-              (converted to signed char). Filtertype 0 isn't a difference though, so use unsigned there.
-              This means filtertype 0 is almost never chosen, but that is justified.*/
-              unsigned char s = attempt[type][x];
-              sum[type] += s < 128 ? s : (255U - s);
-            }
-          }
-
-          /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/
-          if(type == 0 || sum[type] < smallest) {
-            bestType = type;
-            smallest = sum[type];
-          }
-        }
-
-        prevline = &in[y * linebytes];
-
-        /*now fill the out values*/
-        out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/
-        for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x];
-      }
-    }
-
-    for(type = 0; type != 5; ++type) lodepng_free(attempt[type]);
-  } else if(strategy == LFS_ENTROPY) {
-    float sum[5];
-    unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/
-    float smallest = 0;
-    unsigned type, bestType = 0;
-    unsigned count[256];
-
-    for(type = 0; type != 5; ++type) {
-      attempt[type] = (unsigned char*)lodepng_malloc(linebytes);
-      if(!attempt[type]) return 83; /*alloc fail*/
-    }
-
-    for(y = 0; y != h; ++y) {
-      /*try the 5 filter types*/
-      for(type = 0; type != 5; ++type) {
-        filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type);
-        for(x = 0; x != 256; ++x) count[x] = 0;
-        for(x = 0; x != linebytes; ++x) ++count[attempt[type][x]];
-        ++count[type]; /*the filter type itself is part of the scanline*/
-        sum[type] = 0;
-        for(x = 0; x != 256; ++x) {
-          float p = count[x] / (float)(linebytes + 1);
-          sum[type] += count[x] == 0 ? 0 : flog2(1 / p) * p;
-        }
-        /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/
-        if(type == 0 || sum[type] < smallest) {
-          bestType = type;
-          smallest = sum[type];
-        }
-      }
-
-      prevline = &in[y * linebytes];
-
-      /*now fill the out values*/
-      out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/
-      for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x];
-    }
-
-    for(type = 0; type != 5; ++type) lodepng_free(attempt[type]);
-  } else if(strategy == LFS_PREDEFINED) {
-    for(y = 0; y != h; ++y) {
-      size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/
-      size_t inindex = linebytes * y;
-      unsigned char type = settings->predefined_filters[y];
-      out[outindex] = type; /*filter type byte*/
-      filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type);
-      prevline = &in[inindex];
-    }
-  } else if(strategy == LFS_BRUTE_FORCE) {
-    /*brute force filter chooser.
-    deflate the scanline after every filter attempt to see which one deflates best.
-    This is very slow and gives only slightly smaller, sometimes even larger, result*/
-    size_t size[5];
-    unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/
-    size_t smallest = 0;
-    unsigned type = 0, bestType = 0;
-    unsigned char* dummy;
-    LodePNGCompressSettings zlibsettings = settings->zlibsettings;
-    /*use fixed tree on the attempts so that the tree is not adapted to the filtertype on purpose,
-    to simulate the true case where the tree is the same for the whole image. Sometimes it gives
-    better result with dynamic tree anyway. Using the fixed tree sometimes gives worse, but in rare
-    cases better compression. It does make this a bit less slow, so it's worth doing this.*/
-    zlibsettings.btype = 1;
-    /*a custom encoder likely doesn't read the btype setting and is optimized for complete PNG
-    images only, so disable it*/
-    zlibsettings.custom_zlib = 0;
-    zlibsettings.custom_deflate = 0;
-    for(type = 0; type != 5; ++type) {
-      attempt[type] = (unsigned char*)lodepng_malloc(linebytes);
-      if(!attempt[type]) return 83; /*alloc fail*/
-    }
-    for(y = 0; y != h; ++y) /*try the 5 filter types*/ {
-      for(type = 0; type != 5; ++type) {
-        unsigned testsize = (unsigned)linebytes;
-        /*if(testsize > 8) testsize /= 8;*/ /*it already works good enough by testing a part of the row*/
-
-        filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type);
-        size[type] = 0;
-        dummy = 0;
-        zlib_compress(&dummy, &size[type], attempt[type], testsize, &zlibsettings);
-        lodepng_free(dummy);
-        /*check if this is smallest size (or if type == 0 it's the first case so always store the values)*/
-        if(type == 0 || size[type] < smallest) {
-          bestType = type;
-          smallest = size[type];
-        }
-      }
-      prevline = &in[y * linebytes];
-      out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/
-      for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x];
-    }
-    for(type = 0; type != 5; ++type) lodepng_free(attempt[type]);
-  }
-  else return 88; /* unknown filter strategy */
-
-  return error;
-}
-
-static void addPaddingBits(unsigned char* out, const unsigned char* in,
-                           size_t olinebits, size_t ilinebits, unsigned h) {
-  /*The opposite of the removePaddingBits function
-  olinebits must be >= ilinebits*/
-  unsigned y;
-  size_t diff = olinebits - ilinebits;
-  size_t obp = 0, ibp = 0; /*bit pointers*/
-  for(y = 0; y != h; ++y) {
-    size_t x;
-    for(x = 0; x < ilinebits; ++x) {
-      unsigned char bit = readBitFromReversedStream(&ibp, in);
-      setBitOfReversedStream(&obp, out, bit);
-    }
-    /*obp += diff; --> no, fill in some value in the padding bits too, to avoid
-    "Use of uninitialised value of size ###" warning from valgrind*/
-    for(x = 0; x != diff; ++x) setBitOfReversedStream(&obp, out, 0);
-  }
-}
-
-/*
-in: non-interlaced image with size w*h
-out: the same pixels, but re-ordered according to PNG's Adam7 interlacing, with
- no padding bits between scanlines, but between reduced images so that each
- reduced image starts at a byte.
-bpp: bits per pixel
-there are no padding bits, not between scanlines, not between reduced images
-in has the following size in bits: w * h * bpp.
-out is possibly bigger due to padding bits between reduced images
-NOTE: comments about padding bits are only relevant if bpp < 8
-*/
-static void Adam7_interlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) {
-  unsigned passw[7], passh[7];
-  size_t filter_passstart[8], padded_passstart[8], passstart[8];
-  unsigned i;
-
-  Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
-
-  if(bpp >= 8) {
-    for(i = 0; i != 7; ++i) {
-      unsigned x, y, b;
-      size_t bytewidth = bpp / 8;
-      for(y = 0; y < passh[i]; ++y)
-      for(x = 0; x < passw[i]; ++x) {
-        size_t pixelinstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth;
-        size_t pixeloutstart = passstart[i] + (y * passw[i] + x) * bytewidth;
-        for(b = 0; b < bytewidth; ++b) {
-          out[pixeloutstart + b] = in[pixelinstart + b];
-        }
-      }
-    }
-  } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ {
-    for(i = 0; i != 7; ++i) {
-      unsigned x, y, b;
-      unsigned ilinebits = bpp * passw[i];
-      unsigned olinebits = bpp * w;
-      size_t obp, ibp; /*bit pointers (for out and in buffer)*/
-      for(y = 0; y < passh[i]; ++y)
-      for(x = 0; x < passw[i]; ++x) {
-        ibp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp;
-        obp = (8 * passstart[i]) + (y * ilinebits + x * bpp);
-        for(b = 0; b < bpp; ++b) {
-          unsigned char bit = readBitFromReversedStream(&ibp, in);
-          setBitOfReversedStream(&obp, out, bit);
-        }
-      }
-    }
-  }
-}
-
-/*out must be buffer big enough to contain uncompressed IDAT chunk data, and in must contain the full image.
-return value is error**/
-static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const unsigned char* in,
-                                    unsigned w, unsigned h,
-                                    const LodePNGInfo* info_png, const LodePNGEncoderSettings* settings) {
-  /*
-  This function converts the pure 2D image with the PNG's colortype, into filtered-padded-interlaced data. Steps:
-  *) if no Adam7: 1) add padding bits (= posible extra bits per scanline if bpp < 8) 2) filter
-  *) if adam7: 1) Adam7_interlace 2) 7x add padding bits 3) 7x filter
-  */
-  unsigned bpp = lodepng_get_bpp(&info_png->color);
-  unsigned error = 0;
-
-  if(info_png->interlace_method == 0) {
-    *outsize = h + (h * ((w * bpp + 7) / 8)); /*image size plus an extra byte per scanline + possible padding bits*/
-    *out = (unsigned char*)lodepng_malloc(*outsize);
-    if(!(*out) && (*outsize)) error = 83; /*alloc fail*/
-
-    if(!error) {
-      /*non multiple of 8 bits per scanline, padding bits needed per scanline*/
-      if(bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) {
-        unsigned char* padded = (unsigned char*)lodepng_malloc(h * ((w * bpp + 7) / 8));
-        if(!padded) error = 83; /*alloc fail*/
-        if(!error) {
-          addPaddingBits(padded, in, ((w * bpp + 7) / 8) * 8, w * bpp, h);
-          error = filter(*out, padded, w, h, &info_png->color, settings);
-        }
-        lodepng_free(padded);
-      } else {
-        /*we can immediately filter into the out buffer, no other steps needed*/
-        error = filter(*out, in, w, h, &info_png->color, settings);
-      }
-    }
-  } else /*interlace_method is 1 (Adam7)*/ {
-    unsigned passw[7], passh[7];
-    size_t filter_passstart[8], padded_passstart[8], passstart[8];
-    unsigned char* adam7;
-
-    Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
-
-    *outsize = filter_passstart[7]; /*image size plus an extra byte per scanline + possible padding bits*/
-    *out = (unsigned char*)lodepng_malloc(*outsize);
-    if(!(*out)) error = 83; /*alloc fail*/
-
-    adam7 = (unsigned char*)lodepng_malloc(passstart[7]);
-    if(!adam7 && passstart[7]) error = 83; /*alloc fail*/
-
-    if(!error) {
-      unsigned i;
-
-      Adam7_interlace(adam7, in, w, h, bpp);
-      for(i = 0; i != 7; ++i) {
-        if(bpp < 8) {
-          unsigned char* padded = (unsigned char*)lodepng_malloc(padded_passstart[i + 1] - padded_passstart[i]);
-          if(!padded) ERROR_BREAK(83); /*alloc fail*/
-          addPaddingBits(padded, &adam7[passstart[i]],
-                         ((passw[i] * bpp + 7) / 8) * 8, passw[i] * bpp, passh[i]);
-          error = filter(&(*out)[filter_passstart[i]], padded,
-                         passw[i], passh[i], &info_png->color, settings);
-          lodepng_free(padded);
-        } else {
-          error = filter(&(*out)[filter_passstart[i]], &adam7[padded_passstart[i]],
-                         passw[i], passh[i], &info_png->color, settings);
-        }
-
-        if(error) break;
-      }
-    }
-
-    lodepng_free(adam7);
-  }
-
-  return error;
-}
-
-/*
-palette must have 4 * palettesize bytes allocated, and given in format RGBARGBARGBARGBA...
-returns 0 if the palette is opaque,
-returns 1 if the palette has a single color with alpha 0 ==> color key
-returns 2 if the palette is semi-translucent.
-*/
-static unsigned getPaletteTranslucency(const unsigned char* palette, size_t palettesize) {
-  size_t i;
-  unsigned key = 0;
-  unsigned r = 0, g = 0, b = 0; /*the value of the color with alpha 0, so long as color keying is possible*/
-  for(i = 0; i != palettesize; ++i) {
-    if(!key && palette[4 * i + 3] == 0) {
-      r = palette[4 * i + 0]; g = palette[4 * i + 1]; b = palette[4 * i + 2];
-      key = 1;
-      i = (size_t)(-1); /*restart from beginning, to detect earlier opaque colors with key's value*/
-    }
-    else if(palette[4 * i + 3] != 255) return 2;
-    /*when key, no opaque RGB may have key's RGB*/
-    else if(key && r == palette[i * 4 + 0] && g == palette[i * 4 + 1] && b == palette[i * 4 + 2]) return 2;
-  }
-  return key;
-}
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-static unsigned addUnknownChunks(ucvector* out, unsigned char* data, size_t datasize) {
-  unsigned char* inchunk = data;
-  while((size_t)(inchunk - data) < datasize) {
-    CERROR_TRY_RETURN(lodepng_chunk_append(&out->data, &out->size, inchunk));
-    out->allocsize = out->size; /*fix the allocsize again*/
-    inchunk = lodepng_chunk_next(inchunk);
-  }
-  return 0;
-}
-
-static unsigned isGrayICCProfile(const unsigned char* profile, unsigned size) {
-  /*
-  It is a gray profile if bytes 16-19 are "GRAY", rgb profile if bytes 16-19
-  are "RGB ". We do not perform any full parsing of the ICC profile here, other
-  than check those 4 bytes to grayscale profile. Other than that, validity of
-  the profile is not checked. This is needed only because the PNG specification
-  requires using a non-gray color model if there is an ICC profile with "RGB "
-  (sadly limiting compression opportunities if the input data is grayscale RGB
-  data), and requires using a gray color model if it is "GRAY".
-  */
-  if(size < 20) return 0;
-  return profile[16] == 'G' &&  profile[17] == 'R' &&  profile[18] == 'A' &&  profile[19] == 'Y';
-}
-
-static unsigned isRGBICCProfile(const unsigned char* profile, unsigned size) {
-  /* See comment in isGrayICCProfile*/
-  if(size < 20) return 0;
-  return profile[16] == 'R' &&  profile[17] == 'G' &&  profile[18] == 'B' &&  profile[19] == ' ';
-}
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-
-unsigned lodepng_encode(unsigned char** out, size_t* outsize,
-                        const unsigned char* image, unsigned w, unsigned h,
-                        LodePNGState* state) {
-  unsigned char* data = 0; /*uncompressed version of the IDAT chunk data*/
-  size_t datasize = 0;
-  ucvector outv;
-  LodePNGInfo info;
-
-  ucvector_init(&outv);
-  lodepng_info_init(&info);
-
-  /*provide some proper output values if error will happen*/
-  *out = 0;
-  *outsize = 0;
-  state->error = 0;
-
-  /*check input values validity*/
-  if((state->info_png.color.colortype == LCT_PALETTE || state->encoder.force_palette)
-      && (state->info_png.color.palettesize == 0 || state->info_png.color.palettesize > 256)) {
-    state->error = 68; /*invalid palette size, it is only allowed to be 1-256*/
-    goto cleanup;
-  }
-  if(state->encoder.zlibsettings.btype > 2) {
-    state->error = 61; /*error: unexisting btype*/
-    goto cleanup;
-  }
-  if(state->info_png.interlace_method > 1) {
-    state->error = 71; /*error: unexisting interlace mode*/
-    goto cleanup;
-  }
-  state->error = checkColorValidity(state->info_png.color.colortype, state->info_png.color.bitdepth);
-  if(state->error) goto cleanup; /*error: unexisting color type given*/
-  state->error = checkColorValidity(state->info_raw.colortype, state->info_raw.bitdepth);
-  if(state->error) goto cleanup; /*error: unexisting color type given*/
-
-  /* color convert and compute scanline filter types */
-  lodepng_info_copy(&info, &state->info_png);
-  if(state->encoder.auto_convert) {
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-    if(state->info_png.background_defined) {
-      unsigned bg_r = state->info_png.background_r;
-      unsigned bg_g = state->info_png.background_g;
-      unsigned bg_b = state->info_png.background_b;
-      unsigned r = 0, g = 0, b = 0;
-      LodePNGColorProfile prof;
-      LodePNGColorMode mode16 = lodepng_color_mode_make(LCT_RGB, 16);
-      lodepng_convert_rgb(&r, &g, &b, bg_r, bg_g, bg_b, &mode16, &state->info_png.color);
-      lodepng_color_profile_init(&prof);
-      state->error = lodepng_get_color_profile(&prof, image, w, h, &state->info_raw);
-      if(state->error) goto cleanup;
-      lodepng_color_profile_add(&prof, r, g, b, 65535);
-      state->error = auto_choose_color_from_profile(&info.color, &state->info_raw, &prof);
-      if(state->error) goto cleanup;
-      if(lodepng_convert_rgb(&info.background_r, &info.background_g, &info.background_b,
-          bg_r, bg_g, bg_b, &info.color, &state->info_png.color)) {
-        state->error = 104;
-        goto cleanup;
-      }
-    }
-    else
-#endif /* LODEPNG_COMPILE_ANCILLARY_CHUNKS */
-    {
-      state->error = lodepng_auto_choose_color(&info.color, image, w, h, &state->info_raw);
-      if(state->error) goto cleanup;
-    }
-  }
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-  if(state->info_png.iccp_defined) {
-    unsigned gray_icc = isGrayICCProfile(state->info_png.iccp_profile, state->info_png.iccp_profile_size);
-    unsigned gray_png = info.color.colortype == LCT_GREY || info.color.colortype == LCT_GREY_ALPHA;
-    /* TODO: perhaps instead of giving errors or less optimal compression, we can automatically modify
-    the ICC profile here to say "GRAY" or "RGB " to match the PNG color type, unless this will require
-    non trivial changes to the rest of the ICC profile */
-    if(!gray_icc && !isRGBICCProfile(state->info_png.iccp_profile, state->info_png.iccp_profile_size)) {
-      state->error = 100; /* Disallowed profile color type for PNG */
-      goto cleanup;
-    }
-    if(!state->encoder.auto_convert && gray_icc != gray_png) {
-      /* Non recoverable: encoder not allowed to convert color type, and requested color type not
-      compatible with ICC color type */
-      state->error = 101;
-      goto cleanup;
-    }
-    if(gray_icc && !gray_png) {
-      /* Non recoverable: trying to set grayscale ICC profile while colored pixels were given */
-      state->error = 102;
-      goto cleanup;
-      /* NOTE: this relies on the fact that lodepng_auto_choose_color never returns palette for grayscale pixels */
-    }
-    if(!gray_icc && gray_png) {
-      /* Recoverable but an unfortunate loss in compression density: We have grayscale pixels but
-      are forced to store them in more expensive RGB format that will repeat each value 3 times
-      because the PNG spec does not allow an RGB ICC profile with internal grayscale color data */
-      if(info.color.colortype == LCT_GREY) info.color.colortype = LCT_RGB;
-      if(info.color.colortype == LCT_GREY_ALPHA) info.color.colortype = LCT_RGBA;
-      if(info.color.bitdepth < 8) info.color.bitdepth = 8;
-    }
-  }
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-  if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) {
-    unsigned char* converted;
-    size_t size = ((size_t)w * (size_t)h * (size_t)lodepng_get_bpp(&info.color) + 7) / 8;
-
-    converted = (unsigned char*)lodepng_malloc(size);
-    if(!converted && size) state->error = 83; /*alloc fail*/
-    if(!state->error) {
-      state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h);
-    }
-    if(!state->error) preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder);
-    lodepng_free(converted);
-    if(state->error) goto cleanup;
-  }
-  else preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder);
-
-  /* output all PNG chunks */ {
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-    size_t i;
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-    /*write signature and chunks*/
-    writeSignature(&outv);
-    /*IHDR*/
-    addChunk_IHDR(&outv, w, h, info.color.colortype, info.color.bitdepth, info.interlace_method);
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-    /*unknown chunks between IHDR and PLTE*/
-    if(info.unknown_chunks_data[0]) {
-      state->error = addUnknownChunks(&outv, info.unknown_chunks_data[0], info.unknown_chunks_size[0]);
-      if(state->error) goto cleanup;
-    }
-    /*color profile chunks must come before PLTE */
-    if(info.iccp_defined) addChunk_iCCP(&outv, &info, &state->encoder.zlibsettings);
-    if(info.srgb_defined) addChunk_sRGB(&outv, &info);
-    if(info.gama_defined) addChunk_gAMA(&outv, &info);
-    if(info.chrm_defined) addChunk_cHRM(&outv, &info);
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-    /*PLTE*/
-    if(info.color.colortype == LCT_PALETTE) {
-      addChunk_PLTE(&outv, &info.color);
-    }
-    if(state->encoder.force_palette && (info.color.colortype == LCT_RGB || info.color.colortype == LCT_RGBA)) {
-      addChunk_PLTE(&outv, &info.color);
-    }
-    /*tRNS*/
-    if(info.color.colortype == LCT_PALETTE && getPaletteTranslucency(info.color.palette, info.color.palettesize) != 0) {
-      addChunk_tRNS(&outv, &info.color);
-    }
-    if((info.color.colortype == LCT_GREY || info.color.colortype == LCT_RGB) && info.color.key_defined) {
-      addChunk_tRNS(&outv, &info.color);
-    }
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-    /*bKGD (must come between PLTE and the IDAt chunks*/
-    if(info.background_defined) {
-      state->error = addChunk_bKGD(&outv, &info);
-      if(state->error) goto cleanup;
-    }
-    /*pHYs (must come before the IDAT chunks)*/
-    if(info.phys_defined) addChunk_pHYs(&outv, &info);
-
-    /*unknown chunks between PLTE and IDAT*/
-    if(info.unknown_chunks_data[1]) {
-      state->error = addUnknownChunks(&outv, info.unknown_chunks_data[1], info.unknown_chunks_size[1]);
-      if(state->error) goto cleanup;
-    }
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-    /*IDAT (multiple IDAT chunks must be consecutive)*/
-    state->error = addChunk_IDAT(&outv, data, datasize, &state->encoder.zlibsettings);
-    if(state->error) goto cleanup;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-    /*tIME*/
-    if(info.time_defined) addChunk_tIME(&outv, &info.time);
-    /*tEXt and/or zTXt*/
-    for(i = 0; i != info.text_num; ++i) {
-      if(strlen(info.text_keys[i]) > 79) {
-        state->error = 66; /*text chunk too large*/
-        goto cleanup;
-      }
-      if(strlen(info.text_keys[i]) < 1) {
-        state->error = 67; /*text chunk too small*/
-        goto cleanup;
-      }
-      if(state->encoder.text_compression) {
-        addChunk_zTXt(&outv, info.text_keys[i], info.text_strings[i], &state->encoder.zlibsettings);
-      } else {
-        addChunk_tEXt(&outv, info.text_keys[i], info.text_strings[i]);
-      }
-    }
-    /*LodePNG version id in text chunk*/
-    if(state->encoder.add_id) {
-      unsigned already_added_id_text = 0;
-      for(i = 0; i != info.text_num; ++i) {
-        if(!strcmp(info.text_keys[i], "LodePNG")) {
-          already_added_id_text = 1;
-          break;
-        }
-      }
-      if(already_added_id_text == 0) {
-        addChunk_tEXt(&outv, "LodePNG", LODEPNG_VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/
-      }
-    }
-    /*iTXt*/
-    for(i = 0; i != info.itext_num; ++i) {
-      if(strlen(info.itext_keys[i]) > 79) {
-        state->error = 66; /*text chunk too large*/
-        goto cleanup;
-      }
-      if(strlen(info.itext_keys[i]) < 1) {
-        state->error = 67; /*text chunk too small*/
-        goto cleanup;
-      }
-      addChunk_iTXt(&outv, state->encoder.text_compression,
-                    info.itext_keys[i], info.itext_langtags[i], info.itext_transkeys[i], info.itext_strings[i],
-                    &state->encoder.zlibsettings);
-    }
-
-    /*unknown chunks between IDAT and IEND*/
-    if(info.unknown_chunks_data[2]) {
-      state->error = addUnknownChunks(&outv, info.unknown_chunks_data[2], info.unknown_chunks_size[2]);
-      if(state->error) goto cleanup;
-    }
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-    addChunk_IEND(&outv);
-  }
-
-cleanup:
-  lodepng_info_cleanup(&info);
-  lodepng_free(data);
-
-  /*instead of cleaning the vector up, give it to the output*/
-  *out = outv.data;
-  *outsize = outv.size;
-
-  return state->error;
-}
-
-unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, const unsigned char* image,
-                               unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) {
-  unsigned error;
-  LodePNGState state;
-  lodepng_state_init(&state);
-  state.info_raw.colortype = colortype;
-  state.info_raw.bitdepth = bitdepth;
-  state.info_png.color.colortype = colortype;
-  state.info_png.color.bitdepth = bitdepth;
-  lodepng_encode(out, outsize, image, w, h, &state);
-  error = state.error;
-  lodepng_state_cleanup(&state);
-  return error;
-}
-
-unsigned lodepng_encode32(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) {
-  return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGBA, 8);
-}
-
-unsigned lodepng_encode24(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) {
-  return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGB, 8);
-}
-
-#ifdef LODEPNG_COMPILE_DISK
-unsigned lodepng_encode_file(const char* filename, const unsigned char* image, unsigned w, unsigned h,
-                             LodePNGColorType colortype, unsigned bitdepth) {
-  unsigned char* buffer;
-  size_t buffersize;
-  unsigned error = lodepng_encode_memory(&buffer, &buffersize, image, w, h, colortype, bitdepth);
-  if(!error) error = lodepng_save_file(buffer, buffersize, filename);
-  lodepng_free(buffer);
-  return error;
-}
-
-unsigned lodepng_encode32_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) {
-  return lodepng_encode_file(filename, image, w, h, LCT_RGBA, 8);
-}
-
-unsigned lodepng_encode24_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) {
-  return lodepng_encode_file(filename, image, w, h, LCT_RGB, 8);
-}
-#endif /*LODEPNG_COMPILE_DISK*/
-
-void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings) {
-  lodepng_compress_settings_init(&settings->zlibsettings);
-  settings->filter_palette_zero = 1;
-  settings->filter_strategy = LFS_MINSUM;
-  settings->auto_convert = 1;
-  settings->force_palette = 0;
-  settings->predefined_filters = 0;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-  settings->add_id = 0;
-  settings->text_compression = 1;
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-}
-
-#endif /*LODEPNG_COMPILE_ENCODER*/
-#endif /*LODEPNG_COMPILE_PNG*/
-
-#ifdef LODEPNG_COMPILE_ERROR_TEXT
-/*
-This returns the description of a numerical error code in English. This is also
-the documentation of all the error codes.
-*/
-const char* lodepng_error_text(unsigned code) {
-  switch(code) {
-    case 0: return "no error, everything went ok";
-    case 1: return "nothing done yet"; /*the Encoder/Decoder has done nothing yet, error checking makes no sense yet*/
-    case 10: return "end of input memory reached without huffman end code"; /*while huffman decoding*/
-    case 11: return "error in code tree made it jump outside of huffman tree"; /*while huffman decoding*/
-    case 13: return "problem while processing dynamic deflate block";
-    case 14: return "problem while processing dynamic deflate block";
-    case 15: return "problem while processing dynamic deflate block";
-    case 16: return "unexisting code while processing dynamic deflate block";
-    case 17: return "end of out buffer memory reached while inflating";
-    case 18: return "invalid distance code while inflating";
-    case 19: return "end of out buffer memory reached while inflating";
-    case 20: return "invalid deflate block BTYPE encountered while decoding";
-    case 21: return "NLEN is not ones complement of LEN in a deflate block";
-
-    /*end of out buffer memory reached while inflating:
-    This can happen if the inflated deflate data is longer than the amount of bytes required to fill up
-    all the pixels of the image, given the color depth and image dimensions. Something that doesn't
-    happen in a normal, well encoded, PNG image.*/
-    case 22: return "end of out buffer memory reached while inflating";
-    case 23: return "end of in buffer memory reached while inflating";
-    case 24: return "invalid FCHECK in zlib header";
-    case 25: return "invalid compression method in zlib header";
-    case 26: return "FDICT encountered in zlib header while it's not used for PNG";
-    case 27: return "PNG file is smaller than a PNG header";
-    /*Checks the magic file header, the first 8 bytes of the PNG file*/
-    case 28: return "incorrect PNG signature, it's no PNG or corrupted";
-    case 29: return "first chunk is not the header chunk";
-    case 30: return "chunk length too large, chunk broken off at end of file";
-    case 31: return "illegal PNG color type or bpp";
-    case 32: return "illegal PNG compression method";
-    case 33: return "illegal PNG filter method";
-    case 34: return "illegal PNG interlace method";
-    case 35: return "chunk length of a chunk is too large or the chunk too small";
-    case 36: return "illegal PNG filter type encountered";
-    case 37: return "illegal bit depth for this color type given";
-    case 38: return "the palette is too big"; /*more than 256 colors*/
-    case 39: return "tRNS chunk before PLTE or has more entries than palette size";
-    case 40: return "tRNS chunk has wrong size for grayscale image";
-    case 41: return "tRNS chunk has wrong size for RGB image";
-    case 42: return "tRNS chunk appeared while it was not allowed for this color type";
-    case 43: return "bKGD chunk has wrong size for palette image";
-    case 44: return "bKGD chunk has wrong size for grayscale image";
-    case 45: return "bKGD chunk has wrong size for RGB image";
-    case 48: return "empty input buffer given to decoder. Maybe caused by non-existing file?";
-    case 49: return "jumped past memory while generating dynamic huffman tree";
-    case 50: return "jumped past memory while generating dynamic huffman tree";
-    case 51: return "jumped past memory while inflating huffman block";
-    case 52: return "jumped past memory while inflating";
-    case 53: return "size of zlib data too small";
-    case 54: return "repeat symbol in tree while there was no value symbol yet";
-    /*jumped past tree while generating huffman tree, this could be when the
-    tree will have more leaves than symbols after generating it out of the
-    given lenghts. They call this an oversubscribed dynamic bit lengths tree in zlib.*/
-    case 55: return "jumped past tree while generating huffman tree";
-    case 56: return "given output image colortype or bitdepth not supported for color conversion";
-    case 57: return "invalid CRC encountered (checking CRC can be disabled)";
-    case 58: return "invalid ADLER32 encountered (checking ADLER32 can be disabled)";
-    case 59: return "requested color conversion not supported";
-    case 60: return "invalid window size given in the settings of the encoder (must be 0-32768)";
-    case 61: return "invalid BTYPE given in the settings of the encoder (only 0, 1 and 2 are allowed)";
-    /*LodePNG leaves the choice of RGB to grayscale conversion formula to the user.*/
-    case 62: return "conversion from color to grayscale not supported";
-    /*(2^31-1)*/
-    case 63: return "length of a chunk too long, max allowed for PNG is 2147483647 bytes per chunk";
-    /*this would result in the inability of a deflated block to ever contain an end code. It must be at least 1.*/
-    case 64: return "the length of the END symbol 256 in the Huffman tree is 0";
-    case 66: return "the length of a text chunk keyword given to the encoder is longer than the maximum of 79 bytes";
-    case 67: return "the length of a text chunk keyword given to the encoder is smaller than the minimum of 1 byte";
-    case 68: return "tried to encode a PLTE chunk with a palette that has less than 1 or more than 256 colors";
-    case 69: return "unknown chunk type with 'critical' flag encountered by the decoder";
-    case 71: return "unexisting interlace mode given to encoder (must be 0 or 1)";
-    case 72: return "while decoding, unexisting compression method encountering in zTXt or iTXt chunk (it must be 0)";
-    case 73: return "invalid tIME chunk size";
-    case 74: return "invalid pHYs chunk size";
-    /*length could be wrong, or data chopped off*/
-    case 75: return "no null termination char found while decoding text chunk";
-    case 76: return "iTXt chunk too short to contain required bytes";
-    case 77: return "integer overflow in buffer size";
-    case 78: return "failed to open file for reading"; /*file doesn't exist or couldn't be opened for reading*/
-    case 79: return "failed to open file for writing";
-    case 80: return "tried creating a tree of 0 symbols";
-    case 81: return "lazy matching at pos 0 is impossible";
-    case 82: return "color conversion to palette requested while a color isn't in palette, or index out of bounds";
-    case 83: return "memory allocation failed";
-    case 84: return "given image too small to contain all pixels to be encoded";
-    case 86: return "impossible offset in lz77 encoding (internal bug)";
-    case 87: return "must provide custom zlib function pointer if LODEPNG_COMPILE_ZLIB is not defined";
-    case 88: return "invalid filter strategy given for LodePNGEncoderSettings.filter_strategy";
-    case 89: return "text chunk keyword too short or long: must have size 1-79";
-    /*the windowsize in the LodePNGCompressSettings. Requiring POT(==> & instead of %) makes encoding 12% faster.*/
-    case 90: return "windowsize must be a power of two";
-    case 91: return "invalid decompressed idat size";
-    case 92: return "integer overflow due to too many pixels";
-    case 93: return "zero width or height is invalid";
-    case 94: return "header chunk must have a size of 13 bytes";
-    case 95: return "integer overflow with combined idat chunk size";
-    case 96: return "invalid gAMA chunk size";
-    case 97: return "invalid cHRM chunk size";
-    case 98: return "invalid sRGB chunk size";
-    case 99: return "invalid sRGB rendering intent";
-    case 100: return "invalid ICC profile color type, the PNG specification only allows RGB or GRAY";
-    case 101: return "PNG specification does not allow RGB ICC profile on gray color types and vice versa";
-    case 102: return "not allowed to set grayscale ICC profile with colored pixels by PNG specification";
-    case 103: return "invalid palette index in bKGD chunk. Maybe it came before PLTE chunk?";
-    case 104: return "invalid bKGD color while encoding (e.g. palette index out of range)";
-  }
-  return "unknown error code";
-}
-#endif /*LODEPNG_COMPILE_ERROR_TEXT*/
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* // C++ Wrapper                                                          // */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#ifdef LODEPNG_COMPILE_CPP
-namespace lodepng {
-
-#ifdef LODEPNG_COMPILE_DISK
-unsigned load_file(std::vector<unsigned char>& buffer, const std::string& filename) {
-  long size = lodepng_filesize(filename.c_str());
-  if(size < 0) return 78;
-  buffer.resize((size_t)size);
-  return size == 0 ? 0 : lodepng_buffer_file(&buffer[0], (size_t)size, filename.c_str());
-}
-
-/*write given buffer to the file, overwriting the file, it doesn't append to it.*/
-unsigned save_file(const std::vector<unsigned char>& buffer, const std::string& filename) {
-  return lodepng_save_file(buffer.empty() ? 0 : &buffer[0], buffer.size(), filename.c_str());
-}
-#endif /* LODEPNG_COMPILE_DISK */
-
-#ifdef LODEPNG_COMPILE_ZLIB
-#ifdef LODEPNG_COMPILE_DECODER
-unsigned decompress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize,
-                    const LodePNGDecompressSettings& settings) {
-  unsigned char* buffer = 0;
-  size_t buffersize = 0;
-  unsigned error = zlib_decompress(&buffer, &buffersize, in, insize, &settings);
-  if(buffer) {
-    out.insert(out.end(), &buffer[0], &buffer[buffersize]);
-    lodepng_free(buffer);
-  }
-  return error;
-}
-
-unsigned decompress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in,
-                    const LodePNGDecompressSettings& settings) {
-  return decompress(out, in.empty() ? 0 : &in[0], in.size(), settings);
-}
-#endif /* LODEPNG_COMPILE_DECODER */
-
-#ifdef LODEPNG_COMPILE_ENCODER
-unsigned compress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize,
-                  const LodePNGCompressSettings& settings) {
-  unsigned char* buffer = 0;
-  size_t buffersize = 0;
-  unsigned error = zlib_compress(&buffer, &buffersize, in, insize, &settings);
-  if(buffer) {
-    out.insert(out.end(), &buffer[0], &buffer[buffersize]);
-    lodepng_free(buffer);
-  }
-  return error;
-}
-
-unsigned compress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in,
-                  const LodePNGCompressSettings& settings) {
-  return compress(out, in.empty() ? 0 : &in[0], in.size(), settings);
-}
-#endif /* LODEPNG_COMPILE_ENCODER */
-#endif /* LODEPNG_COMPILE_ZLIB */
-
-
-#ifdef LODEPNG_COMPILE_PNG
-
-State::State() {
-  lodepng_state_init(this);
-}
-
-State::State(const State& other) {
-  lodepng_state_init(this);
-  lodepng_state_copy(this, &other);
-}
-
-State::~State() {
-  lodepng_state_cleanup(this);
-}
-
-State& State::operator=(const State& other) {
-  lodepng_state_copy(this, &other);
-  return *this;
-}
-
-#ifdef LODEPNG_COMPILE_DECODER
-
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, const unsigned char* in,
-                size_t insize, LodePNGColorType colortype, unsigned bitdepth) {
-  unsigned char* buffer;
-  unsigned error = lodepng_decode_memory(&buffer, &w, &h, in, insize, colortype, bitdepth);
-  if(buffer && !error) {
-    State state;
-    state.info_raw.colortype = colortype;
-    state.info_raw.bitdepth = bitdepth;
-    size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw);
-    out.insert(out.end(), &buffer[0], &buffer[buffersize]);
-    lodepng_free(buffer);
-  }
-  return error;
-}
-
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
-                const std::vector<unsigned char>& in, LodePNGColorType colortype, unsigned bitdepth) {
-  return decode(out, w, h, in.empty() ? 0 : &in[0], (unsigned)in.size(), colortype, bitdepth);
-}
-
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
-                State& state,
-                const unsigned char* in, size_t insize) {
-  unsigned char* buffer = NULL;
-  unsigned error = lodepng_decode(&buffer, &w, &h, &state, in, insize);
-  if(buffer && !error) {
-    size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw);
-    out.insert(out.end(), &buffer[0], &buffer[buffersize]);
-  }
-  lodepng_free(buffer);
-  return error;
-}
-
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
-                State& state,
-                const std::vector<unsigned char>& in) {
-  return decode(out, w, h, state, in.empty() ? 0 : &in[0], in.size());
-}
-
-#ifdef LODEPNG_COMPILE_DISK
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, const std::string& filename,
-                LodePNGColorType colortype, unsigned bitdepth) {
-  std::vector<unsigned char> buffer;
-  /* safe output values in case error happens */
-  w = h = 0;
-  unsigned error = load_file(buffer, filename);
-  if(error) return error;
-  return decode(out, w, h, buffer, colortype, bitdepth);
-}
-#endif /* LODEPNG_COMPILE_DECODER */
-#endif /* LODEPNG_COMPILE_DISK */
-
-#ifdef LODEPNG_COMPILE_ENCODER
-unsigned encode(std::vector<unsigned char>& out, const unsigned char* in, unsigned w, unsigned h,
-                LodePNGColorType colortype, unsigned bitdepth) {
-  unsigned char* buffer;
-  size_t buffersize;
-  unsigned error = lodepng_encode_memory(&buffer, &buffersize, in, w, h, colortype, bitdepth);
-  if(buffer) {
-    out.insert(out.end(), &buffer[0], &buffer[buffersize]);
-    lodepng_free(buffer);
-  }
-  return error;
-}
-
-unsigned encode(std::vector<unsigned char>& out,
-                const std::vector<unsigned char>& in, unsigned w, unsigned h,
-                LodePNGColorType colortype, unsigned bitdepth) {
-  if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84;
-  return encode(out, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth);
-}
-
-unsigned encode(std::vector<unsigned char>& out,
-                const unsigned char* in, unsigned w, unsigned h,
-                State& state) {
-  unsigned char* buffer;
-  size_t buffersize;
-  unsigned error = lodepng_encode(&buffer, &buffersize, in, w, h, &state);
-  if(buffer) {
-    out.insert(out.end(), &buffer[0], &buffer[buffersize]);
-    lodepng_free(buffer);
-  }
-  return error;
-}
-
-unsigned encode(std::vector<unsigned char>& out,
-                const std::vector<unsigned char>& in, unsigned w, unsigned h,
-                State& state) {
-  if(lodepng_get_raw_size(w, h, &state.info_raw) > in.size()) return 84;
-  return encode(out, in.empty() ? 0 : &in[0], w, h, state);
-}
-
-#ifdef LODEPNG_COMPILE_DISK
-unsigned encode(const std::string& filename,
-                const unsigned char* in, unsigned w, unsigned h,
-                LodePNGColorType colortype, unsigned bitdepth) {
-  std::vector<unsigned char> buffer;
-  unsigned error = encode(buffer, in, w, h, colortype, bitdepth);
-  if(!error) error = save_file(buffer, filename);
-  return error;
-}
-
-unsigned encode(const std::string& filename,
-                const std::vector<unsigned char>& in, unsigned w, unsigned h,
-                LodePNGColorType colortype, unsigned bitdepth) {
-  if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84;
-  return encode(filename, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth);
-}
-#endif /* LODEPNG_COMPILE_DISK */
-#endif /* LODEPNG_COMPILE_ENCODER */
-#endif /* LODEPNG_COMPILE_PNG */
-} /* namespace lodepng */
-#endif /*LODEPNG_COMPILE_CPP*/
diff --git a/encoder/lodepng.h b/encoder/lodepng.h
deleted file mode 100644
index 476a206..0000000
--- a/encoder/lodepng.h
+++ /dev/null
@@ -1,1930 +0,0 @@
-/*
-LodePNG version 20190210
-
-Copyright (c) 2005-2019 Lode Vandevenne
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-    1. The origin of this software must not be misrepresented; you must not
-    claim that you wrote the original software. If you use this software
-    in a product, an acknowledgment in the product documentation would be
-    appreciated but is not required.
-
-    2. Altered source versions must be plainly marked as such, and must not be
-    misrepresented as being the original software.
-
-    3. This notice may not be removed or altered from any source
-    distribution.
-*/
-
-#ifndef LODEPNG_H
-#define LODEPNG_H
-
-#include <string.h> /*for size_t*/
-
-extern const char* LODEPNG_VERSION_STRING;
-
-/*
-The following #defines are used to create code sections. They can be disabled
-to disable code sections, which can give faster compile time and smaller binary.
-The "NO_COMPILE" defines are designed to be used to pass as defines to the
-compiler command to disable them without modifying this header, e.g.
--DLODEPNG_NO_COMPILE_ZLIB for gcc.
-In addition to those below, you can also define LODEPNG_NO_COMPILE_CRC to
-allow implementing a custom lodepng_crc32.
-*/
-/*deflate & zlib. If disabled, you must specify alternative zlib functions in
-the custom_zlib field of the compress and decompress settings*/
-#ifndef LODEPNG_NO_COMPILE_ZLIB
-#define LODEPNG_COMPILE_ZLIB
-#endif
-
-/*png encoder and png decoder*/
-#ifndef LODEPNG_NO_COMPILE_PNG
-#define LODEPNG_COMPILE_PNG
-#endif
-
-/*deflate&zlib decoder and png decoder*/
-#ifndef LODEPNG_NO_COMPILE_DECODER
-#define LODEPNG_COMPILE_DECODER
-#endif
-
-/*deflate&zlib encoder and png encoder*/
-#ifndef LODEPNG_NO_COMPILE_ENCODER
-#define LODEPNG_COMPILE_ENCODER
-#endif
-
-/*the optional built in harddisk file loading and saving functions*/
-#ifndef LODEPNG_NO_COMPILE_DISK
-#define LODEPNG_COMPILE_DISK
-#endif
-
-/*support for chunks other than IHDR, IDAT, PLTE, tRNS, IEND: ancillary and unknown chunks*/
-#ifndef LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS
-#define LODEPNG_COMPILE_ANCILLARY_CHUNKS
-#endif
-
-/*ability to convert error numerical codes to English text string*/
-#ifndef LODEPNG_NO_COMPILE_ERROR_TEXT
-#define LODEPNG_COMPILE_ERROR_TEXT
-#endif
-
-/*Compile the default allocators (C's free, malloc and realloc). If you disable this,
-you can define the functions lodepng_free, lodepng_malloc and lodepng_realloc in your
-source files with custom allocators.*/
-#ifndef LODEPNG_NO_COMPILE_ALLOCATORS
-#define LODEPNG_COMPILE_ALLOCATORS
-#endif
-
-/*compile the C++ version (you can disable the C++ wrapper here even when compiling for C++)*/
-#ifdef __cplusplus
-#ifndef LODEPNG_NO_COMPILE_CPP
-#define LODEPNG_COMPILE_CPP
-#endif
-#endif
-
-#ifdef LODEPNG_COMPILE_CPP
-#include <vector>
-#include <string>
-#endif /*LODEPNG_COMPILE_CPP*/
-
-#ifdef LODEPNG_COMPILE_PNG
-/*The PNG color types (also used for raw).*/
-typedef enum LodePNGColorType {
-  LCT_GREY = 0, /*grayscale: 1,2,4,8,16 bit*/
-  LCT_RGB = 2, /*RGB: 8,16 bit*/
-  LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/
-  LCT_GREY_ALPHA = 4, /*grayscale with alpha: 8,16 bit*/
-  LCT_RGBA = 6 /*RGB with alpha: 8,16 bit*/
-} LodePNGColorType;
-
-#ifdef LODEPNG_COMPILE_DECODER
-/*
-Converts PNG data in memory to raw pixel data.
-out: Output parameter. Pointer to buffer that will contain the raw pixel data.
-     After decoding, its size is w * h * (bytes per pixel) bytes larger than
-     initially. Bytes per pixel depends on colortype and bitdepth.
-     Must be freed after usage with free(*out).
-     Note: for 16-bit per channel colors, uses big endian format like PNG does.
-w: Output parameter. Pointer to width of pixel data.
-h: Output parameter. Pointer to height of pixel data.
-in: Memory buffer with the PNG file.
-insize: size of the in buffer.
-colortype: the desired color type for the raw output image. See explanation on PNG color types.
-bitdepth: the desired bit depth for the raw output image. See explanation on PNG color types.
-Return value: LodePNG error code (0 means no error).
-*/
-unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h,
-                               const unsigned char* in, size_t insize,
-                               LodePNGColorType colortype, unsigned bitdepth);
-
-/*Same as lodepng_decode_memory, but always decodes to 32-bit RGBA raw image*/
-unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h,
-                          const unsigned char* in, size_t insize);
-
-/*Same as lodepng_decode_memory, but always decodes to 24-bit RGB raw image*/
-unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h,
-                          const unsigned char* in, size_t insize);
-
-#ifdef LODEPNG_COMPILE_DISK
-/*
-Load PNG from disk, from file with given name.
-Same as the other decode functions, but instead takes a filename as input.
-*/
-unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h,
-                             const char* filename,
-                             LodePNGColorType colortype, unsigned bitdepth);
-
-/*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.*/
-unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h,
-                               const char* filename);
-
-/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.*/
-unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h,
-                               const char* filename);
-#endif /*LODEPNG_COMPILE_DISK*/
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-
-#ifdef LODEPNG_COMPILE_ENCODER
-/*
-Converts raw pixel data into a PNG image in memory. The colortype and bitdepth
-  of the output PNG image cannot be chosen, they are automatically determined
-  by the colortype, bitdepth and content of the input pixel data.
-  Note: for 16-bit per channel colors, needs big endian format like PNG does.
-out: Output parameter. Pointer to buffer that will contain the PNG image data.
-     Must be freed after usage with free(*out).
-outsize: Output parameter. Pointer to the size in bytes of the out buffer.
-image: The raw pixel data to encode. The size of this buffer should be
-       w * h * (bytes per pixel), bytes per pixel depends on colortype and bitdepth.
-w: width of the raw pixel data in pixels.
-h: height of the raw pixel data in pixels.
-colortype: the color type of the raw input image. See explanation on PNG color types.
-bitdepth: the bit depth of the raw input image. See explanation on PNG color types.
-Return value: LodePNG error code (0 means no error).
-*/
-unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize,
-                               const unsigned char* image, unsigned w, unsigned h,
-                               LodePNGColorType colortype, unsigned bitdepth);
-
-/*Same as lodepng_encode_memory, but always encodes from 32-bit RGBA raw image.*/
-unsigned lodepng_encode32(unsigned char** out, size_t* outsize,
-                          const unsigned char* image, unsigned w, unsigned h);
-
-/*Same as lodepng_encode_memory, but always encodes from 24-bit RGB raw image.*/
-unsigned lodepng_encode24(unsigned char** out, size_t* outsize,
-                          const unsigned char* image, unsigned w, unsigned h);
-
-#ifdef LODEPNG_COMPILE_DISK
-/*
-Converts raw pixel data into a PNG file on disk.
-Same as the other encode functions, but instead takes a filename as output.
-NOTE: This overwrites existing files without warning!
-*/
-unsigned lodepng_encode_file(const char* filename,
-                             const unsigned char* image, unsigned w, unsigned h,
-                             LodePNGColorType colortype, unsigned bitdepth);
-
-/*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.*/
-unsigned lodepng_encode32_file(const char* filename,
-                               const unsigned char* image, unsigned w, unsigned h);
-
-/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.*/
-unsigned lodepng_encode24_file(const char* filename,
-                               const unsigned char* image, unsigned w, unsigned h);
-#endif /*LODEPNG_COMPILE_DISK*/
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-
-#ifdef LODEPNG_COMPILE_CPP
-namespace lodepng {
-#ifdef LODEPNG_COMPILE_DECODER
-/*Same as lodepng_decode_memory, but decodes to an std::vector. The colortype
-is the format to output the pixels to. Default is RGBA 8-bit per channel.*/
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
-                const unsigned char* in, size_t insize,
-                LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
-                const std::vector<unsigned char>& in,
-                LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
-#ifdef LODEPNG_COMPILE_DISK
-/*
-Converts PNG file from disk to raw pixel data in memory.
-Same as the other decode functions, but instead takes a filename as input.
-*/
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
-                const std::string& filename,
-                LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
-#endif /* LODEPNG_COMPILE_DISK */
-#endif /* LODEPNG_COMPILE_DECODER */
-
-#ifdef LODEPNG_COMPILE_ENCODER
-/*Same as lodepng_encode_memory, but encodes to an std::vector. colortype
-is that of the raw input data. The output PNG color type will be auto chosen.*/
-unsigned encode(std::vector<unsigned char>& out,
-                const unsigned char* in, unsigned w, unsigned h,
-                LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
-unsigned encode(std::vector<unsigned char>& out,
-                const std::vector<unsigned char>& in, unsigned w, unsigned h,
-                LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
-#ifdef LODEPNG_COMPILE_DISK
-/*
-Converts 32-bit RGBA raw pixel data into a PNG file on disk.
-Same as the other encode functions, but instead takes a filename as output.
-NOTE: This overwrites existing files without warning!
-*/
-unsigned encode(const std::string& filename,
-                const unsigned char* in, unsigned w, unsigned h,
-                LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
-unsigned encode(const std::string& filename,
-                const std::vector<unsigned char>& in, unsigned w, unsigned h,
-                LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
-#endif /* LODEPNG_COMPILE_DISK */
-#endif /* LODEPNG_COMPILE_ENCODER */
-} /* namespace lodepng */
-#endif /*LODEPNG_COMPILE_CPP*/
-#endif /*LODEPNG_COMPILE_PNG*/
-
-#ifdef LODEPNG_COMPILE_ERROR_TEXT
-/*Returns an English description of the numerical error code.*/
-const char* lodepng_error_text(unsigned code);
-#endif /*LODEPNG_COMPILE_ERROR_TEXT*/
-
-#ifdef LODEPNG_COMPILE_DECODER
-/*Settings for zlib decompression*/
-typedef struct LodePNGDecompressSettings LodePNGDecompressSettings;
-struct LodePNGDecompressSettings {
-  /* Check LodePNGDecoderSettings for more ignorable errors such as ignore_crc */
-  unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/
-
-  /*use custom zlib decoder instead of built in one (default: null)*/
-  unsigned (*custom_zlib)(unsigned char**, size_t*,
-                          const unsigned char*, size_t,
-                          const LodePNGDecompressSettings*);
-  /*use custom deflate decoder instead of built in one (default: null)
-  if custom_zlib is used, custom_deflate is ignored since only the built in
-  zlib function will call custom_deflate*/
-  unsigned (*custom_inflate)(unsigned char**, size_t*,
-                             const unsigned char*, size_t,
-                             const LodePNGDecompressSettings*);
-
-  const void* custom_context; /*optional custom settings for custom functions*/
-};
-
-extern const LodePNGDecompressSettings lodepng_default_decompress_settings;
-void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings);
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-#ifdef LODEPNG_COMPILE_ENCODER
-/*
-Settings for zlib compression. Tweaking these settings tweaks the balance
-between speed and compression ratio.
-*/
-typedef struct LodePNGCompressSettings LodePNGCompressSettings;
-struct LodePNGCompressSettings /*deflate = compress*/ {
-  /*LZ77 related settings*/
-  unsigned btype; /*the block type for LZ (0, 1, 2 or 3, see zlib standard). Should be 2 for proper compression.*/
-  unsigned use_lz77; /*whether or not to use LZ77. Should be 1 for proper compression.*/
-  unsigned windowsize; /*must be a power of two <= 32768. higher compresses more but is slower. Default value: 2048.*/
-  unsigned minmatch; /*mininum lz77 length. 3 is normally best, 6 can be better for some PNGs. Default: 0*/
-  unsigned nicematch; /*stop searching if >= this length found. Set to 258 for best compression. Default: 128*/
-  unsigned lazymatching; /*use lazy matching: better compression but a bit slower. Default: true*/
-
-  /*use custom zlib encoder instead of built in one (default: null)*/
-  unsigned (*custom_zlib)(unsigned char**, size_t*,
-                          const unsigned char*, size_t,
-                          const LodePNGCompressSettings*);
-  /*use custom deflate encoder instead of built in one (default: null)
-  if custom_zlib is used, custom_deflate is ignored since only the built in
-  zlib function will call custom_deflate*/
-  unsigned (*custom_deflate)(unsigned char**, size_t*,
-                             const unsigned char*, size_t,
-                             const LodePNGCompressSettings*);
-
-  const void* custom_context; /*optional custom settings for custom functions*/
-};
-
-extern const LodePNGCompressSettings lodepng_default_compress_settings;
-void lodepng_compress_settings_init(LodePNGCompressSettings* settings);
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-#ifdef LODEPNG_COMPILE_PNG
-/*
-Color mode of an image. Contains all information required to decode the pixel
-bits to RGBA colors. This information is the same as used in the PNG file
-format, and is used both for PNG and raw image data in LodePNG.
-*/
-typedef struct LodePNGColorMode {
-  /*header (IHDR)*/
-  LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/
-  unsigned bitdepth;  /*bits per sample, see PNG standard or documentation further in this header file*/
-
-  /*
-  palette (PLTE and tRNS)
-
-  Dynamically allocated with the colors of the palette, including alpha.
-  When encoding a PNG, to store your colors in the palette of the LodePNGColorMode, first use
-  lodepng_palette_clear, then for each color use lodepng_palette_add.
-  If you encode an image without alpha with palette, don't forget to put value 255 in each A byte of the palette.
-
-  When decoding, by default you can ignore this palette, since LodePNG already
-  fills the palette colors in the pixels of the raw RGBA output.
-
-  The palette is only supported for color type 3.
-  */
-  unsigned char* palette; /*palette in RGBARGBA... order. When allocated, must be either 0, or have size 1024*/
-  size_t palettesize; /*palette size in number of colors (amount of bytes is 4 * palettesize)*/
-
-  /*
-  transparent color key (tRNS)
-
-  This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit.
-  For grayscale PNGs, r, g and b will all 3 be set to the same.
-
-  When decoding, by default you can ignore this information, since LodePNG sets
-  pixels with this key to transparent already in the raw RGBA output.
-
-  The color key is only supported for color types 0 and 2.
-  */
-  unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/
-  unsigned key_r;       /*red/grayscale component of color key*/
-  unsigned key_g;       /*green component of color key*/
-  unsigned key_b;       /*blue component of color key*/
-} LodePNGColorMode;
-
-/*init, cleanup and copy functions to use with this struct*/
-void lodepng_color_mode_init(LodePNGColorMode* info);
-void lodepng_color_mode_cleanup(LodePNGColorMode* info);
-/*return value is error code (0 means no error)*/
-unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source);
-/* Makes a temporary LodePNGColorMode that does not need cleanup (no palette) */
-LodePNGColorMode lodepng_color_mode_make(LodePNGColorType colortype, unsigned bitdepth);
-
-void lodepng_palette_clear(LodePNGColorMode* info);
-/*add 1 color to the palette*/
-unsigned lodepng_palette_add(LodePNGColorMode* info,
-                             unsigned char r, unsigned char g, unsigned char b, unsigned char a);
-
-/*get the total amount of bits per pixel, based on colortype and bitdepth in the struct*/
-unsigned lodepng_get_bpp(const LodePNGColorMode* info);
-/*get the amount of color channels used, based on colortype in the struct.
-If a palette is used, it counts as 1 channel.*/
-unsigned lodepng_get_channels(const LodePNGColorMode* info);
-/*is it a grayscale type? (only colortype 0 or 4)*/
-unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info);
-/*has it got an alpha channel? (only colortype 2 or 6)*/
-unsigned lodepng_is_alpha_type(const LodePNGColorMode* info);
-/*has it got a palette? (only colortype 3)*/
-unsigned lodepng_is_palette_type(const LodePNGColorMode* info);
-/*only returns true if there is a palette and there is a value in the palette with alpha < 255.
-Loops through the palette to check this.*/
-unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info);
-/*
-Check if the given color info indicates the possibility of having non-opaque pixels in the PNG image.
-Returns true if the image can have translucent or invisible pixels (it still be opaque if it doesn't use such pixels).
-Returns false if the image can only have opaque pixels.
-In detail, it returns true only if it's a color type with alpha, or has a palette with non-opaque values,
-or if "key_defined" is true.
-*/
-unsigned lodepng_can_have_alpha(const LodePNGColorMode* info);
-/*Returns the byte size of a raw image buffer with given width, height and color mode*/
-size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color);
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-/*The information of a Time chunk in PNG.*/
-typedef struct LodePNGTime {
-  unsigned year;    /*2 bytes used (0-65535)*/
-  unsigned month;   /*1-12*/
-  unsigned day;     /*1-31*/
-  unsigned hour;    /*0-23*/
-  unsigned minute;  /*0-59*/
-  unsigned second;  /*0-60 (to allow for leap seconds)*/
-} LodePNGTime;
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-
-/*Information about the PNG image, except pixels, width and height.*/
-typedef struct LodePNGInfo {
-  /*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/
-  unsigned compression_method;/*compression method of the original file. Always 0.*/
-  unsigned filter_method;     /*filter method of the original file*/
-  unsigned interlace_method;  /*interlace method of the original file: 0=none, 1=Adam7*/
-  LodePNGColorMode color;     /*color type and bits, palette and transparency of the PNG file*/
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-  /*
-  Suggested background color chunk (bKGD)
-
-  This uses the same color mode and bit depth as the PNG (except no alpha channel),
-  with values truncated to the bit depth in the unsigned integer.
-
-  For grayscale and palette PNGs, the value is stored in background_r. The values
-  in background_g and background_b are then unused.
-
-  So when decoding, you may get these in a different color mode than the one you requested
-  for the raw pixels.
-
-  When encoding with auto_convert, you must use the color model defined in info_png.color for
-  these values. The encoder normally ignores info_png.color when auto_convert is on, but will
-  use it to interpret these values (and convert copies of them to its chosen color model).
-
-  When encoding, avoid setting this to an expensive color, such as a non-gray value
-  when the image is gray, or the compression will be worse since it will be forced to
-  write the PNG with a more expensive color mode (when auto_convert is on).
-
-  The decoder does not use this background color to edit the color of pixels. This is a
-  completely optional metadata feature.
-  */
-  unsigned background_defined; /*is a suggested background color given?*/
-  unsigned background_r;       /*red/gray/palette component of suggested background color*/
-  unsigned background_g;       /*green component of suggested background color*/
-  unsigned background_b;       /*blue component of suggested background color*/
-
-  /*
-  non-international text chunks (tEXt and zTXt)
-
-  The char** arrays each contain num strings. The actual messages are in
-  text_strings, while text_keys are keywords that give a short description what
-  the actual text represents, e.g. Title, Author, Description, or anything else.
-
-  All the string fields below including keys, names and language tags are null terminated.
-  The PNG specification uses null characters for the keys, names and tags, and forbids null
-  characters to appear in the main text which is why we can use null termination everywhere here.
-
-  A keyword is minimum 1 character and maximum 79 characters long. It's
-  discouraged to use a single line length longer than 79 characters for texts.
-
-  Don't allocate these text buffers yourself. Use the init/cleanup functions
-  correctly and use lodepng_add_text and lodepng_clear_text.
-  */
-  size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/
-  char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/
-  char** text_strings; /*the actual text*/
-
-  /*
-  international text chunks (iTXt)
-  Similar to the non-international text chunks, but with additional strings
-  "langtags" and "transkeys".
-  */
-  size_t itext_num; /*the amount of international texts in this PNG*/
-  char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/
-  char** itext_langtags; /*language tag for this text's language, ISO/IEC 646 string, e.g. ISO 639 language tag*/
-  char** itext_transkeys; /*keyword translated to the international language - UTF-8 string*/
-  char** itext_strings; /*the actual international text - UTF-8 string*/
-
-  /*time chunk (tIME)*/
-  unsigned time_defined; /*set to 1 to make the encoder generate a tIME chunk*/
-  LodePNGTime time;
-
-  /*phys chunk (pHYs)*/
-  unsigned phys_defined; /*if 0, there is no pHYs chunk and the values below are undefined, if 1 else there is one*/
-  unsigned phys_x; /*pixels per unit in x direction*/
-  unsigned phys_y; /*pixels per unit in y direction*/
-  unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/
-
-  /*
-  Color profile related chunks: gAMA, cHRM, sRGB, iCPP
-
-  LodePNG does not apply any color conversions on pixels in the encoder or decoder and does not interpret these color
-  profile values. It merely passes on the information. If you wish to use color profiles and convert colors, please
-  use these values with a color management library.
-
-  See the PNG, ICC and sRGB specifications for more information about the meaning of these values.
-  */
-
-  /* gAMA chunk: optional, overridden by sRGB or iCCP if those are present. */
-  unsigned gama_defined; /* Whether a gAMA chunk is present (0 = not present, 1 = present). */
-  unsigned gama_gamma;   /* Gamma exponent times 100000 */
-
-  /* cHRM chunk: optional, overridden by sRGB or iCCP if those are present. */
-  unsigned chrm_defined; /* Whether a cHRM chunk is present (0 = not present, 1 = present). */
-  unsigned chrm_white_x; /* White Point x times 100000 */
-  unsigned chrm_white_y; /* White Point y times 100000 */
-  unsigned chrm_red_x;   /* Red x times 100000 */
-  unsigned chrm_red_y;   /* Red y times 100000 */
-  unsigned chrm_green_x; /* Green x times 100000 */
-  unsigned chrm_green_y; /* Green y times 100000 */
-  unsigned chrm_blue_x;  /* Blue x times 100000 */
-  unsigned chrm_blue_y;  /* Blue y times 100000 */
-
-  /*
-  sRGB chunk: optional. May not appear at the same time as iCCP.
-  If gAMA is also present gAMA must contain value 45455.
-  If cHRM is also present cHRM must contain respectively 31270,32900,64000,33000,30000,60000,15000,6000.
-  */
-  unsigned srgb_defined; /* Whether an sRGB chunk is present (0 = not present, 1 = present). */
-  unsigned srgb_intent;  /* Rendering intent: 0=perceptual, 1=rel. colorimetric, 2=saturation, 3=abs. colorimetric */
-
-  /*
-  iCCP chunk: optional. May not appear at the same time as sRGB.
-
-  LodePNG does not parse or use the ICC profile (except its color space header field for an edge case), a
-  separate library to handle the ICC data (not included in LodePNG) format is needed to use it for color
-  management and conversions.
-
-  For encoding, if iCCP is present, gAMA and cHRM are recommended to be added as well with values that match the ICC
-  profile as closely as possible, if you wish to do this you should provide the correct values for gAMA and cHRM and
-  enable their '_defined' flags since LodePNG will not automatically compute them from the ICC profile.
-
-  For encoding, the ICC profile is required by the PNG specification to be an "RGB" profile for non-gray
-  PNG color types and a "GRAY" profile for gray PNG color types. If you disable auto_convert, you must ensure
-  the ICC profile type matches your requested color type, else the encoder gives an error. If auto_convert is
-  enabled (the default), and the ICC profile is not a good match for the pixel data, this will result in an encoder
-  error if the pixel data has non-gray pixels for a GRAY profile, or a silent less-optimal compression of the pixel
-  data if the pixels could be encoded as grayscale but the ICC profile is RGB.
-
-  To avoid this do not set an ICC profile in the image unless there is a good reason for it, and when doing so
-  make sure you compute it carefully to avoid the above problems.
-  */
-  unsigned iccp_defined;      /* Whether an iCCP chunk is present (0 = not present, 1 = present). */
-  char* iccp_name;            /* Null terminated string with profile name, 1-79 bytes */
-  /*
-  The ICC profile in iccp_profile_size bytes.
-  Don't allocate this buffer yourself. Use the init/cleanup functions
-  correctly and use lodepng_set_icc and lodepng_clear_icc.
-  */
-  unsigned char* iccp_profile;
-  unsigned iccp_profile_size; /* The size of iccp_profile in bytes */
-
-  /* End of color profile related chunks */
-
-
-  /*
-  unknown chunks: chunks not known by LodePNG, passed on byte for byte.
-
-  There are 3 buffers, one for each position in the PNG where unknown chunks can appear.
-  Each buffer contains all unknown chunks for that position consecutively.
-  The 3 positions are:
-  0: between IHDR and PLTE, 1: between PLTE and IDAT, 2: between IDAT and IEND.
-
-  For encoding, do not store critical chunks or known chunks that are enabled with a "_defined" flag
-  above in here, since the encoder will blindly follow this and could then encode an invalid PNG file
-  (such as one with two IHDR chunks or the disallowed combination of sRGB with iCCP). But do use
-  this if you wish to store an ancillary chunk that is not supported by LodePNG (such as sPLT or hIST),
-  or any non-standard PNG chunk.
-
-  Do not allocate or traverse this data yourself. Use the chunk traversing functions declared
-  later, such as lodepng_chunk_next and lodepng_chunk_append, to read/write this struct.
-  */
-  unsigned char* unknown_chunks_data[3];
-  size_t unknown_chunks_size[3]; /*size in bytes of the unknown chunks, given for protection*/
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-} LodePNGInfo;
-
-/*init, cleanup and copy functions to use with this struct*/
-void lodepng_info_init(LodePNGInfo* info);
-void lodepng_info_cleanup(LodePNGInfo* info);
-/*return value is error code (0 means no error)*/
-unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source);
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str); /*push back both texts at once*/
-void lodepng_clear_text(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/
-
-unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag,
-                           const char* transkey, const char* str); /*push back the 4 texts of 1 chunk at once*/
-void lodepng_clear_itext(LodePNGInfo* info); /*use this to clear the itexts again after you filled them in*/
-
-/*replaces if exists*/
-unsigned lodepng_set_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size);
-void lodepng_clear_icc(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-
-/*
-Converts raw buffer from one color type to another color type, based on
-LodePNGColorMode structs to describe the input and output color type.
-See the reference manual at the end of this header file to see which color conversions are supported.
-return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported)
-The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel
-of the output color type (lodepng_get_bpp).
-For < 8 bpp images, there should not be padding bits at the end of scanlines.
-For 16-bit per channel colors, uses big endian format like PNG does.
-Return value is LodePNG error code
-*/
-unsigned lodepng_convert(unsigned char* out, const unsigned char* in,
-                         const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in,
-                         unsigned w, unsigned h);
-
-#ifdef LODEPNG_COMPILE_DECODER
-/*
-Settings for the decoder. This contains settings for the PNG and the Zlib
-decoder, but not the Info settings from the Info structs.
-*/
-typedef struct LodePNGDecoderSettings {
-  LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/
-
-  /* Check LodePNGDecompressSettings for more ignorable errors such as ignore_adler32 */
-  unsigned ignore_crc; /*ignore CRC checksums*/
-  unsigned ignore_critical; /*ignore unknown critical chunks*/
-  unsigned ignore_end; /*ignore issues at end of file if possible (missing IEND chunk, too large chunk, ...)*/
-  /* TODO: make a system involving warnings with levels and a strict mode instead. Other potentially recoverable
-     errors: srgb rendering intent value, size of content of ancillary chunks, more than 79 characters for some
-     strings, placement/combination rules for ancillary chunks, crc of unknown chunks, allowed characters
-     in string keys, etc... */
-
-  unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-  unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/
-  /*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/
-  unsigned remember_unknown_chunks;
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-} LodePNGDecoderSettings;
-
-void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings);
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-#ifdef LODEPNG_COMPILE_ENCODER
-/*automatically use color type with less bits per pixel if losslessly possible. Default: AUTO*/
-typedef enum LodePNGFilterStrategy {
-  /*every filter at zero*/
-  LFS_ZERO,
-  /*Use filter that gives minimum sum, as described in the official PNG filter heuristic.*/
-  LFS_MINSUM,
-  /*Use the filter type that gives smallest Shannon entropy for this scanline. Depending
-  on the image, this is better or worse than minsum.*/
-  LFS_ENTROPY,
-  /*
-  Brute-force-search PNG filters by compressing each filter for each scanline.
-  Experimental, very slow, and only rarely gives better compression than MINSUM.
-  */
-  LFS_BRUTE_FORCE,
-  /*use predefined_filters buffer: you specify the filter type for each scanline*/
-  LFS_PREDEFINED
-} LodePNGFilterStrategy;
-
-/*Gives characteristics about the integer RGBA colors of the image (count, alpha channel usage, bit depth, ...),
-which helps decide which color model to use for encoding.
-Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms.
-NOTE: This is not related to the ICC color profile, search "iccp_profile" instead to find the ICC/chromacity/...
-fields in this header file.*/
-typedef struct LodePNGColorProfile {
-  unsigned colored; /*not grayscale*/
-  unsigned key; /*image is not opaque and color key is possible instead of full alpha*/
-  unsigned short key_r; /*key values, always as 16-bit, in 8-bit case the byte is duplicated, e.g. 65535 means 255*/
-  unsigned short key_g;
-  unsigned short key_b;
-  unsigned alpha; /*image is not opaque and alpha channel or alpha palette required*/
-  unsigned numcolors; /*amount of colors, up to 257. Not valid if bits == 16.*/
-  unsigned char palette[1024]; /*Remembers up to the first 256 RGBA colors, in no particular order*/
-  unsigned bits; /*bits per channel (not for palette). 1,2 or 4 for grayscale only. 16 if 16-bit per channel required.*/
-  size_t numpixels;
-} LodePNGColorProfile;
-
-void lodepng_color_profile_init(LodePNGColorProfile* profile);
-
-/*Get a LodePNGColorProfile of the image. The profile must already have been inited.
-NOTE: This is not related to the ICC color profile, search "iccp_profile" instead to find the ICC/chromacity/...
-fields in this header file.*/
-unsigned lodepng_get_color_profile(LodePNGColorProfile* profile,
-                                   const unsigned char* image, unsigned w, unsigned h,
-                                   const LodePNGColorMode* mode_in);
-/*The function LodePNG uses internally to decide the PNG color with auto_convert.
-Chooses an optimal color model, e.g. gray if only gray pixels, palette if < 256 colors, ...*/
-unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out,
-                                   const unsigned char* image, unsigned w, unsigned h,
-                                   const LodePNGColorMode* mode_in);
-
-/*Settings for the encoder.*/
-typedef struct LodePNGEncoderSettings {
-  LodePNGCompressSettings zlibsettings; /*settings for the zlib encoder, such as window size, ...*/
-
-  unsigned auto_convert; /*automatically choose output PNG color type. Default: true*/
-
-  /*If true, follows the official PNG heuristic: if the PNG uses a palette or lower than
-  8 bit depth, set all filters to zero. Otherwise use the filter_strategy. Note that to
-  completely follow the official PNG heuristic, filter_palette_zero must be true and
-  filter_strategy must be LFS_MINSUM*/
-  unsigned filter_palette_zero;
-  /*Which filter strategy to use when not using zeroes due to filter_palette_zero.
-  Set filter_palette_zero to 0 to ensure always using your chosen strategy. Default: LFS_MINSUM*/
-  LodePNGFilterStrategy filter_strategy;
-  /*used if filter_strategy is LFS_PREDEFINED. In that case, this must point to a buffer with
-  the same length as the amount of scanlines in the image, and each value must <= 5. You
-  have to cleanup this buffer, LodePNG will never free it. Don't forget that filter_palette_zero
-  must be set to 0 to ensure this is also used on palette or low bitdepth images.*/
-  const unsigned char* predefined_filters;
-
-  /*force creating a PLTE chunk if colortype is 2 or 6 (= a suggested palette).
-  If colortype is 3, PLTE is _always_ created.*/
-  unsigned force_palette;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-  /*add LodePNG identifier and version as a text chunk, for debugging*/
-  unsigned add_id;
-  /*encode text chunks as zTXt chunks instead of tEXt chunks, and use compression in iTXt chunks*/
-  unsigned text_compression;
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-} LodePNGEncoderSettings;
-
-void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings);
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-
-#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER)
-/*The settings, state and information for extended encoding and decoding.*/
-typedef struct LodePNGState {
-#ifdef LODEPNG_COMPILE_DECODER
-  LodePNGDecoderSettings decoder; /*the decoding settings*/
-#endif /*LODEPNG_COMPILE_DECODER*/
-#ifdef LODEPNG_COMPILE_ENCODER
-  LodePNGEncoderSettings encoder; /*the encoding settings*/
-#endif /*LODEPNG_COMPILE_ENCODER*/
-  LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/
-  LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/
-  unsigned error;
-#ifdef LODEPNG_COMPILE_CPP
-  /* For the lodepng::State subclass. */
-  virtual ~LodePNGState(){}
-#endif
-} LodePNGState;
-
-/*init, cleanup and copy functions to use with this struct*/
-void lodepng_state_init(LodePNGState* state);
-void lodepng_state_cleanup(LodePNGState* state);
-void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source);
-#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */
-
-#ifdef LODEPNG_COMPILE_DECODER
-/*
-Same as lodepng_decode_memory, but uses a LodePNGState to allow custom settings and
-getting much more information about the PNG image and color mode.
-*/
-unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h,
-                        LodePNGState* state,
-                        const unsigned char* in, size_t insize);
-
-/*
-Read the PNG header, but not the actual data. This returns only the information
-that is in the IHDR chunk of the PNG, such as width, height and color type. The
-information is placed in the info_png field of the LodePNGState.
-*/
-unsigned lodepng_inspect(unsigned* w, unsigned* h,
-                         LodePNGState* state,
-                         const unsigned char* in, size_t insize);
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-/*
-Reads one metadata chunk (other than IHDR) of the PNG file and outputs what it
-read in the state. Returns error code on failure.
-Use lodepng_inspect first with a new state, then e.g. lodepng_chunk_find_const
-to find the desired chunk type, and if non null use lodepng_inspect_chunk (with
-chunk_pointer - start_of_file as pos).
-Supports most metadata chunks from the PNG standard (gAMA, bKGD, tEXt, ...).
-Ignores unsupported, unknown, non-metadata or IHDR chunks (without error).
-Requirements: &in[pos] must point to start of a chunk, must use regular
-lodepng_inspect first since format of most other chunks depends on IHDR, and if
-there is a PLTE chunk, that one must be inspected before tRNS or bKGD.
-*/
-unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos,
-                               const unsigned char* in, size_t insize);
-
-#ifdef LODEPNG_COMPILE_ENCODER
-/*This function allocates the out buffer with standard malloc and stores the size in *outsize.*/
-unsigned lodepng_encode(unsigned char** out, size_t* outsize,
-                        const unsigned char* image, unsigned w, unsigned h,
-                        LodePNGState* state);
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-/*
-The lodepng_chunk functions are normally not needed, except to traverse the
-unknown chunks stored in the LodePNGInfo struct, or add new ones to it.
-It also allows traversing the chunks of an encoded PNG file yourself.
-
-The chunk pointer always points to the beginning of the chunk itself, that is
-the first byte of the 4 length bytes.
-
-In the PNG file format, chunks have the following format:
--4 bytes length: length of the data of the chunk in bytes (chunk itself is 12 bytes longer)
--4 bytes chunk type (ASCII a-z,A-Z only, see below)
--length bytes of data (may be 0 bytes if length was 0)
--4 bytes of CRC, computed on chunk name + data
-
-The first chunk starts at the 8th byte of the PNG file, the entire rest of the file
-exists out of concatenated chunks with the above format.
-
-PNG standard chunk ASCII naming conventions:
--First byte: uppercase = critical, lowercase = ancillary
--Second byte: uppercase = public, lowercase = private
--Third byte: must be uppercase
--Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy
-*/
-
-/*
-Gets the length of the data of the chunk. Total chunk length has 12 bytes more.
-There must be at least 4 bytes to read from. If the result value is too large,
-it may be corrupt data.
-*/
-unsigned lodepng_chunk_length(const unsigned char* chunk);
-
-/*puts the 4-byte type in null terminated string*/
-void lodepng_chunk_type(char type[5], const unsigned char* chunk);
-
-/*check if the type is the given type*/
-unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type);
-
-/*0: it's one of the critical chunk types, 1: it's an ancillary chunk (see PNG standard)*/
-unsigned char lodepng_chunk_ancillary(const unsigned char* chunk);
-
-/*0: public, 1: private (see PNG standard)*/
-unsigned char lodepng_chunk_private(const unsigned char* chunk);
-
-/*0: the chunk is unsafe to copy, 1: the chunk is safe to copy (see PNG standard)*/
-unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk);
-
-/*get pointer to the data of the chunk, where the input points to the header of the chunk*/
-unsigned char* lodepng_chunk_data(unsigned char* chunk);
-const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk);
-
-/*returns 0 if the crc is correct, 1 if it's incorrect (0 for OK as usual!)*/
-unsigned lodepng_chunk_check_crc(const unsigned char* chunk);
-
-/*generates the correct CRC from the data and puts it in the last 4 bytes of the chunk*/
-void lodepng_chunk_generate_crc(unsigned char* chunk);
-
-/*
-Iterate to next chunks, allows iterating through all chunks of the PNG file.
-Input must be at the beginning of a chunk (result of a previous lodepng_chunk_next call,
-or the 8th byte of a PNG file which always has the first chunk), or alternatively may
-point to the first byte of the PNG file (which is not a chunk but the magic header, the
-function will then skip over it and return the first real chunk).
-Expects at least 8 readable bytes of memory in the input pointer.
-Will output pointer to the start of the next chunk or the end of the file if there
-is no more chunk after this. Start this process at the 8th byte of the PNG file.
-In a non-corrupt PNG file, the last chunk should have name "IEND".
-*/
-unsigned char* lodepng_chunk_next(unsigned char* chunk);
-const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk);
-
-/*Finds the first chunk with the given type in the range [chunk, end), or returns NULL if not found.*/
-unsigned char* lodepng_chunk_find(unsigned char* chunk, const unsigned char* end, const char type[5]);
-const unsigned char* lodepng_chunk_find_const(const unsigned char* chunk, const unsigned char* end, const char type[5]);
-
-/*
-Appends chunk to the data in out. The given chunk should already have its chunk header.
-The out variable and outlength are updated to reflect the new reallocated buffer.
-Returns error code (0 if it went ok)
-*/
-unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk);
-
-/*
-Appends new chunk to out. The chunk to append is given by giving its length, type
-and data separately. The type is a 4-letter string.
-The out variable and outlength are updated to reflect the new reallocated buffer.
-Returne error code (0 if it went ok)
-*/
-unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length,
-                              const char* type, const unsigned char* data);
-
-
-/*Calculate CRC32 of buffer*/
-unsigned lodepng_crc32(const unsigned char* buf, size_t len);
-#endif /*LODEPNG_COMPILE_PNG*/
-
-
-#ifdef LODEPNG_COMPILE_ZLIB
-/*
-This zlib part can be used independently to zlib compress and decompress a
-buffer. It cannot be used to create gzip files however, and it only supports the
-part of zlib that is required for PNG, it does not support dictionaries.
-*/
-
-#ifdef LODEPNG_COMPILE_DECODER
-/*Inflate a buffer. Inflate is the decompression step of deflate. Out buffer must be freed after use.*/
-unsigned lodepng_inflate(unsigned char** out, size_t* outsize,
-                         const unsigned char* in, size_t insize,
-                         const LodePNGDecompressSettings* settings);
-
-/*
-Decompresses Zlib data. Reallocates the out buffer and appends the data. The
-data must be according to the zlib specification.
-Either, *out must be NULL and *outsize must be 0, or, *out must be a valid
-buffer and *outsize its size in bytes. out must be freed by user after usage.
-*/
-unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize,
-                                 const unsigned char* in, size_t insize,
-                                 const LodePNGDecompressSettings* settings);
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-#ifdef LODEPNG_COMPILE_ENCODER
-/*
-Compresses data with Zlib. Reallocates the out buffer and appends the data.
-Zlib adds a small header and trailer around the deflate data.
-The data is output in the format of the zlib specification.
-Either, *out must be NULL and *outsize must be 0, or, *out must be a valid
-buffer and *outsize its size in bytes. out must be freed by user after usage.
-*/
-unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize,
-                               const unsigned char* in, size_t insize,
-                               const LodePNGCompressSettings* settings);
-
-/*
-Find length-limited Huffman code for given frequencies. This function is in the
-public interface only for tests, it's used internally by lodepng_deflate.
-*/
-unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies,
-                                      size_t numcodes, unsigned maxbitlen);
-
-/*Compress a buffer with deflate. See RFC 1951. Out buffer must be freed after use.*/
-unsigned lodepng_deflate(unsigned char** out, size_t* outsize,
-                         const unsigned char* in, size_t insize,
-                         const LodePNGCompressSettings* settings);
-
-#endif /*LODEPNG_COMPILE_ENCODER*/
-#endif /*LODEPNG_COMPILE_ZLIB*/
-
-#ifdef LODEPNG_COMPILE_DISK
-/*
-Load a file from disk into buffer. The function allocates the out buffer, and
-after usage you should free it.
-out: output parameter, contains pointer to loaded buffer.
-outsize: output parameter, size of the allocated out buffer
-filename: the path to the file to load
-return value: error code (0 means ok)
-*/
-unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename);
-
-/*
-Save a file from buffer to disk. Warning, if it exists, this function overwrites
-the file without warning!
-buffer: the buffer to write
-buffersize: size of the buffer to write
-filename: the path to the file to save to
-return value: error code (0 means ok)
-*/
-unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename);
-#endif /*LODEPNG_COMPILE_DISK*/
-
-#ifdef LODEPNG_COMPILE_CPP
-/* The LodePNG C++ wrapper uses std::vectors instead of manually allocated memory buffers. */
-namespace lodepng {
-#ifdef LODEPNG_COMPILE_PNG
-class State : public LodePNGState {
-  public:
-    State();
-    State(const State& other);
-    virtual ~State();
-    State& operator=(const State& other);
-};
-
-#ifdef LODEPNG_COMPILE_DECODER
-/* Same as other lodepng::decode, but using a State for more settings and information. */
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
-                State& state,
-                const unsigned char* in, size_t insize);
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
-                State& state,
-                const std::vector<unsigned char>& in);
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-#ifdef LODEPNG_COMPILE_ENCODER
-/* Same as other lodepng::encode, but using a State for more settings and information. */
-unsigned encode(std::vector<unsigned char>& out,
-                const unsigned char* in, unsigned w, unsigned h,
-                State& state);
-unsigned encode(std::vector<unsigned char>& out,
-                const std::vector<unsigned char>& in, unsigned w, unsigned h,
-                State& state);
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-#ifdef LODEPNG_COMPILE_DISK
-/*
-Load a file from disk into an std::vector.
-return value: error code (0 means ok)
-*/
-unsigned load_file(std::vector<unsigned char>& buffer, const std::string& filename);
-
-/*
-Save the binary data in an std::vector to a file on disk. The file is overwritten
-without warning.
-*/
-unsigned save_file(const std::vector<unsigned char>& buffer, const std::string& filename);
-#endif /* LODEPNG_COMPILE_DISK */
-#endif /* LODEPNG_COMPILE_PNG */
-
-#ifdef LODEPNG_COMPILE_ZLIB
-#ifdef LODEPNG_COMPILE_DECODER
-/* Zlib-decompress an unsigned char buffer */
-unsigned decompress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize,
-                    const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings);
-
-/* Zlib-decompress an std::vector */
-unsigned decompress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in,
-                    const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings);
-#endif /* LODEPNG_COMPILE_DECODER */
-
-#ifdef LODEPNG_COMPILE_ENCODER
-/* Zlib-compress an unsigned char buffer */
-unsigned compress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize,
-                  const LodePNGCompressSettings& settings = lodepng_default_compress_settings);
-
-/* Zlib-compress an std::vector */
-unsigned compress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in,
-                  const LodePNGCompressSettings& settings = lodepng_default_compress_settings);
-#endif /* LODEPNG_COMPILE_ENCODER */
-#endif /* LODEPNG_COMPILE_ZLIB */
-} /* namespace lodepng */
-#endif /*LODEPNG_COMPILE_CPP*/
-
-/*
-TODO:
-[.] test if there are no memory leaks or security exploits - done a lot but needs to be checked often
-[.] check compatibility with various compilers  - done but needs to be redone for every newer version
-[X] converting color to 16-bit per channel types
-[X] support color profile chunk types (but never let them touch RGB values by default)
-[ ] support all public PNG chunk types (almost done except sBIT, sPLT and hIST)
-[ ] make sure encoder generates no chunks with size > (2^31)-1
-[ ] partial decoding (stream processing)
-[X] let the "isFullyOpaque" function check color keys and transparent palettes too
-[X] better name for the variables "codes", "codesD", "codelengthcodes", "clcl" and "lldl"
-[ ] allow treating some errors like warnings, when image is recoverable (e.g. 69, 57, 58)
-[ ] make warnings like: oob palette, checksum fail, data after iend, wrong/unknown crit chunk, no null terminator in text, ...
-[ ] error messages with line numbers (and version)
-[ ] errors in state instead of as return code?
-[ ] new errors/warnings like suspiciously big decompressed ztxt or iccp chunk
-[ ] let the C++ wrapper catch exceptions coming from the standard library and return LodePNG error codes
-[ ] allow user to provide custom color conversion functions, e.g. for premultiplied alpha, padding bits or not, ...
-[ ] allow user to give data (void*) to custom allocator
-[ ] provide alternatives for C library functions not present on some platforms (memcpy, ...)
-[ ] rename "grey" to "gray" everywhere since "color" also uses US spelling (keep "grey" copies for backwards compatibility)
-*/
-
-#endif /*LODEPNG_H inclusion guard*/
-
-/*
-LodePNG Documentation
----------------------
-
-0. table of contents
---------------------
-
-  1. about
-   1.1. supported features
-   1.2. features not supported
-  2. C and C++ version
-  3. security
-  4. decoding
-  5. encoding
-  6. color conversions
-    6.1. PNG color types
-    6.2. color conversions
-    6.3. padding bits
-    6.4. A note about 16-bits per channel and endianness
-  7. error values
-  8. chunks and PNG editing
-  9. compiler support
-  10. examples
-   10.1. decoder C++ example
-   10.2. decoder C example
-  11. state settings reference
-  12. changes
-  13. contact information
-
-
-1. about
---------
-
-PNG is a file format to store raster images losslessly with good compression,
-supporting different color types and alpha channel.
-
-LodePNG is a PNG codec according to the Portable Network Graphics (PNG)
-Specification (Second Edition) - W3C Recommendation 10 November 2003.
-
-The specifications used are:
-
-*) Portable Network Graphics (PNG) Specification (Second Edition):
-     http://www.w3.org/TR/2003/REC-PNG-20031110
-*) RFC 1950 ZLIB Compressed Data Format version 3.3:
-     http://www.gzip.org/zlib/rfc-zlib.html
-*) RFC 1951 DEFLATE Compressed Data Format Specification ver 1.3:
-     http://www.gzip.org/zlib/rfc-deflate.html
-
-The most recent version of LodePNG can currently be found at
-http://lodev.org/lodepng/
-
-LodePNG works both in C (ISO C90) and C++, with a C++ wrapper that adds
-extra functionality.
-
-LodePNG exists out of two files:
--lodepng.h: the header file for both C and C++
--lodepng.c(pp): give it the name lodepng.c or lodepng.cpp (or .cc) depending on your usage
-
-If you want to start using LodePNG right away without reading this doc, get the
-examples from the LodePNG website to see how to use it in code, or check the
-smaller examples in chapter 13 here.
-
-LodePNG is simple but only supports the basic requirements. To achieve
-simplicity, the following design choices were made: There are no dependencies
-on any external library. There are functions to decode and encode a PNG with
-a single function call, and extended versions of these functions taking a
-LodePNGState struct allowing to specify or get more information. By default
-the colors of the raw image are always RGB or RGBA, no matter what color type
-the PNG file uses. To read and write files, there are simple functions to
-convert the files to/from buffers in memory.
-
-This all makes LodePNG suitable for loading textures in games, demos and small
-programs, ... It's less suitable for full fledged image editors, loading PNGs
-over network (it requires all the image data to be available before decoding can
-begin), life-critical systems, ...
-
-1.1. supported features
------------------------
-
-The following features are supported by the decoder:
-
-*) decoding of PNGs with any color type, bit depth and interlace mode, to a 24- or 32-bit color raw image,
-   or the same color type as the PNG
-*) encoding of PNGs, from any raw image to 24- or 32-bit color, or the same color type as the raw image
-*) Adam7 interlace and deinterlace for any color type
-*) loading the image from harddisk or decoding it from a buffer from other sources than harddisk
-*) support for alpha channels, including RGBA color model, translucent palettes and color keying
-*) zlib decompression (inflate)
-*) zlib compression (deflate)
-*) CRC32 and ADLER32 checksums
-*) colorimetric color profile conversions: currently experimentally available in lodepng_util.cpp only,
-   plus alternatively ability to pass on chroma/gamma/ICC profile information to other color management system.
-*) handling of unknown chunks, allowing making a PNG editor that stores custom and unknown chunks.
-*) the following chunks are supported by both encoder and decoder:
-    IHDR: header information
-    PLTE: color palette
-    IDAT: pixel data
-    IEND: the final chunk
-    tRNS: transparency for palettized images
-    tEXt: textual information
-    zTXt: compressed textual information
-    iTXt: international textual information
-    bKGD: suggested background color
-    pHYs: physical dimensions
-    tIME: modification time
-    cHRM: RGB chromaticities
-    gAMA: RGB gamma correction
-    iCCP: ICC color profile
-    sRGB: rendering intent
-
-1.2. features not supported
----------------------------
-
-The following features are _not_ supported:
-
-*) some features needed to make a conformant PNG-Editor might be still missing.
-*) partial loading/stream processing. All data must be available and is processed in one call.
-*) The following public chunks are not (yet) supported but treated as unknown chunks by LodePNG:
-    sBIT
-    hIST
-    sPLT
-
-
-2. C and C++ version
---------------------
-
-The C version uses buffers allocated with alloc that you need to free()
-yourself. You need to use init and cleanup functions for each struct whenever
-using a struct from the C version to avoid exploits and memory leaks.
-
-The C++ version has extra functions with std::vectors in the interface and the
-lodepng::State class which is a LodePNGState with constructor and destructor.
-
-These files work without modification for both C and C++ compilers because all
-the additional C++ code is in "#ifdef __cplusplus" blocks that make C-compilers
-ignore it, and the C code is made to compile both with strict ISO C90 and C++.
-
-To use the C++ version, you need to rename the source file to lodepng.cpp
-(instead of lodepng.c), and compile it with a C++ compiler.
-
-To use the C version, you need to rename the source file to lodepng.c (instead
-of lodepng.cpp), and compile it with a C compiler.
-
-
-3. Security
------------
-
-Even if carefully designed, it's always possible that LodePNG contains possible
-exploits. If you discover one, please let me know, and it will be fixed.
-
-When using LodePNG, care has to be taken with the C version of LodePNG, as well
-as the C-style structs when working with C++. The following conventions are used
-for all C-style structs:
-
--if a struct has a corresponding init function, always call the init function when making a new one
--if a struct has a corresponding cleanup function, call it before the struct disappears to avoid memory leaks
--if a struct has a corresponding copy function, use the copy function instead of "=".
- The destination must also be inited already.
-
-
-4. Decoding
------------
-
-Decoding converts a PNG compressed image to a raw pixel buffer.
-
-Most documentation on using the decoder is at its declarations in the header
-above. For C, simple decoding can be done with functions such as
-lodepng_decode32, and more advanced decoding can be done with the struct
-LodePNGState and lodepng_decode. For C++, all decoding can be done with the
-various lodepng::decode functions, and lodepng::State can be used for advanced
-features.
-
-When using the LodePNGState, it uses the following fields for decoding:
-*) LodePNGInfo info_png: it stores extra information about the PNG (the input) in here
-*) LodePNGColorMode info_raw: here you can say what color mode of the raw image (the output) you want to get
-*) LodePNGDecoderSettings decoder: you can specify a few extra settings for the decoder to use
-
-LodePNGInfo info_png
---------------------
-
-After decoding, this contains extra information of the PNG image, except the actual
-pixels, width and height because these are already gotten directly from the decoder
-functions.
-
-It contains for example the original color type of the PNG image, text comments,
-suggested background color, etc... More details about the LodePNGInfo struct are
-at its declaration documentation.
-
-LodePNGColorMode info_raw
--------------------------
-
-When decoding, here you can specify which color type you want
-the resulting raw image to be. If this is different from the colortype of the
-PNG, then the decoder will automatically convert the result. This conversion
-always works, except if you want it to convert a color PNG to grayscale or to
-a palette with missing colors.
-
-By default, 32-bit color is used for the result.
-
-LodePNGDecoderSettings decoder
-------------------------------
-
-The settings can be used to ignore the errors created by invalid CRC and Adler32
-chunks, and to disable the decoding of tEXt chunks.
-
-There's also a setting color_convert, true by default. If false, no conversion
-is done, the resulting data will be as it was in the PNG (after decompression)
-and you'll have to puzzle the colors of the pixels together yourself using the
-color type information in the LodePNGInfo.
-
-
-5. Encoding
------------
-
-Encoding converts a raw pixel buffer to a PNG compressed image.
-
-Most documentation on using the encoder is at its declarations in the header
-above. For C, simple encoding can be done with functions such as
-lodepng_encode32, and more advanced decoding can be done with the struct
-LodePNGState and lodepng_encode. For C++, all encoding can be done with the
-various lodepng::encode functions, and lodepng::State can be used for advanced
-features.
-
-Like the decoder, the encoder can also give errors. However it gives less errors
-since the encoder input is trusted, the decoder input (a PNG image that could
-be forged by anyone) is not trusted.
-
-When using the LodePNGState, it uses the following fields for encoding:
-*) LodePNGInfo info_png: here you specify how you want the PNG (the output) to be.
-*) LodePNGColorMode info_raw: here you say what color type of the raw image (the input) has
-*) LodePNGEncoderSettings encoder: you can specify a few settings for the encoder to use
-
-LodePNGInfo info_png
---------------------
-
-When encoding, you use this the opposite way as when decoding: for encoding,
-you fill in the values you want the PNG to have before encoding. By default it's
-not needed to specify a color type for the PNG since it's automatically chosen,
-but it's possible to choose it yourself given the right settings.
-
-The encoder will not always exactly match the LodePNGInfo struct you give,
-it tries as close as possible. Some things are ignored by the encoder. The
-encoder uses, for example, the following settings from it when applicable:
-colortype and bitdepth, text chunks, time chunk, the color key, the palette, the
-background color, the interlace method, unknown chunks, ...
-
-When encoding to a PNG with colortype 3, the encoder will generate a PLTE chunk.
-If the palette contains any colors for which the alpha channel is not 255 (so
-there are translucent colors in the palette), it'll add a tRNS chunk.
-
-LodePNGColorMode info_raw
--------------------------
-
-You specify the color type of the raw image that you give to the input here,
-including a possible transparent color key and palette you happen to be using in
-your raw image data.
-
-By default, 32-bit color is assumed, meaning your input has to be in RGBA
-format with 4 bytes (unsigned chars) per pixel.
-
-LodePNGEncoderSettings encoder
-------------------------------
-
-The following settings are supported (some are in sub-structs):
-*) auto_convert: when this option is enabled, the encoder will
-automatically choose the smallest possible color mode (including color key) that
-can encode the colors of all pixels without information loss.
-*) btype: the block type for LZ77. 0 = uncompressed, 1 = fixed huffman tree,
-   2 = dynamic huffman tree (best compression). Should be 2 for proper
-   compression.
-*) use_lz77: whether or not to use LZ77 for compressed block types. Should be
-   true for proper compression.
-*) windowsize: the window size used by the LZ77 encoder (1 - 32768). Has value
-   2048 by default, but can be set to 32768 for better, but slow, compression.
-*) force_palette: if colortype is 2 or 6, you can make the encoder write a PLTE
-   chunk if force_palette is true. This can used as suggested palette to convert
-   to by viewers that don't support more than 256 colors (if those still exist)
-*) add_id: add text chunk "Encoder: LodePNG <version>" to the image.
-*) text_compression: default 1. If 1, it'll store texts as zTXt instead of tEXt chunks.
-  zTXt chunks use zlib compression on the text. This gives a smaller result on
-  large texts but a larger result on small texts (such as a single program name).
-  It's all tEXt or all zTXt though, there's no separate setting per text yet.
-
-
-6. color conversions
---------------------
-
-An important thing to note about LodePNG, is that the color type of the PNG, and
-the color type of the raw image, are completely independent. By default, when
-you decode a PNG, you get the result as a raw image in the color type you want,
-no matter whether the PNG was encoded with a palette, grayscale or RGBA color.
-And if you encode an image, by default LodePNG will automatically choose the PNG
-color type that gives good compression based on the values of colors and amount
-of colors in the image. It can be configured to let you control it instead as
-well, though.
-
-To be able to do this, LodePNG does conversions from one color mode to another.
-It can convert from almost any color type to any other color type, except the
-following conversions: RGB to grayscale is not supported, and converting to a
-palette when the palette doesn't have a required color is not supported. This is
-not supported on purpose: this is information loss which requires a color
-reduction algorithm that is beyong the scope of a PNG encoder (yes, RGB to gray
-is easy, but there are multiple ways if you want to give some channels more
-weight).
-
-By default, when decoding, you get the raw image in 32-bit RGBA or 24-bit RGB
-color, no matter what color type the PNG has. And by default when encoding,
-LodePNG automatically picks the best color model for the output PNG, and expects
-the input image to be 32-bit RGBA or 24-bit RGB. So, unless you want to control
-the color format of the images yourself, you can skip this chapter.
-
-6.1. PNG color types
---------------------
-
-A PNG image can have many color types, ranging from 1-bit color to 64-bit color,
-as well as palettized color modes. After the zlib decompression and unfiltering
-in the PNG image is done, the raw pixel data will have that color type and thus
-a certain amount of bits per pixel. If you want the output raw image after
-decoding to have another color type, a conversion is done by LodePNG.
-
-The PNG specification gives the following color types:
-
-0: grayscale, bit depths 1, 2, 4, 8, 16
-2: RGB, bit depths 8 and 16
-3: palette, bit depths 1, 2, 4 and 8
-4: grayscale with alpha, bit depths 8 and 16
-6: RGBA, bit depths 8 and 16
-
-Bit depth is the amount of bits per pixel per color channel. So the total amount
-of bits per pixel is: amount of channels * bitdepth.
-
-6.2. color conversions
-----------------------
-
-As explained in the sections about the encoder and decoder, you can specify
-color types and bit depths in info_png and info_raw to change the default
-behaviour.
-
-If, when decoding, you want the raw image to be something else than the default,
-you need to set the color type and bit depth you want in the LodePNGColorMode,
-or the parameters colortype and bitdepth of the simple decoding function.
-
-If, when encoding, you use another color type than the default in the raw input
-image, you need to specify its color type and bit depth in the LodePNGColorMode
-of the raw image, or use the parameters colortype and bitdepth of the simple
-encoding function.
-
-If, when encoding, you don't want LodePNG to choose the output PNG color type
-but control it yourself, you need to set auto_convert in the encoder settings
-to false, and specify the color type you want in the LodePNGInfo of the
-encoder (including palette: it can generate a palette if auto_convert is true,
-otherwise not).
-
-If the input and output color type differ (whether user chosen or auto chosen),
-LodePNG will do a color conversion, which follows the rules below, and may
-sometimes result in an error.
-
-To avoid some confusion:
--the decoder converts from PNG to raw image
--the encoder converts from raw image to PNG
--the colortype and bitdepth in LodePNGColorMode info_raw, are those of the raw image
--the colortype and bitdepth in the color field of LodePNGInfo info_png, are those of the PNG
--when encoding, the color type in LodePNGInfo is ignored if auto_convert
- is enabled, it is automatically generated instead
--when decoding, the color type in LodePNGInfo is set by the decoder to that of the original
- PNG image, but it can be ignored since the raw image has the color type you requested instead
--if the color type of the LodePNGColorMode and PNG image aren't the same, a conversion
- between the color types is done if the color types are supported. If it is not
- supported, an error is returned. If the types are the same, no conversion is done.
--even though some conversions aren't supported, LodePNG supports loading PNGs from any
- colortype and saving PNGs to any colortype, sometimes it just requires preparing
- the raw image correctly before encoding.
--both encoder and decoder use the same color converter.
-
-The function lodepng_convert does the color conversion. It is available in the
-interface but normally isn't needed since the encoder and decoder already call
-it.
-
-Non supported color conversions:
--color to grayscale when non-gray pixels are present: no error is thrown, but
-the result will look ugly because only the red channel is taken (it assumes all
-three channels are the same in this case so ignores green and blue). The reason
-no error is given is to allow converting from three-channel grayscale images to
-one-channel even if there are numerical imprecisions.
--anything to palette when the palette does not have an exact match for a from-color
-in it: in this case an error is thrown
-
-Supported color conversions:
--anything to 8-bit RGB, 8-bit RGBA, 16-bit RGB, 16-bit RGBA
--any gray or gray+alpha, to gray or gray+alpha
--anything to a palette, as long as the palette has the requested colors in it
--removing alpha channel
--higher to smaller bitdepth, and vice versa
-
-If you want no color conversion to be done (e.g. for speed or control):
--In the encoder, you can make it save a PNG with any color type by giving the
-raw color mode and LodePNGInfo the same color mode, and setting auto_convert to
-false.
--In the decoder, you can make it store the pixel data in the same color type
-as the PNG has, by setting the color_convert setting to false. Settings in
-info_raw are then ignored.
-
-6.3. padding bits
------------------
-
-In the PNG file format, if a less than 8-bit per pixel color type is used and the scanlines
-have a bit amount that isn't a multiple of 8, then padding bits are used so that each
-scanline starts at a fresh byte. But that is NOT true for the LodePNG raw input and output.
-The raw input image you give to the encoder, and the raw output image you get from the decoder
-will NOT have these padding bits, e.g. in the case of a 1-bit image with a width
-of 7 pixels, the first pixel of the second scanline will the the 8th bit of the first byte,
-not the first bit of a new byte.
-
-6.4. A note about 16-bits per channel and endianness
-----------------------------------------------------
-
-LodePNG uses unsigned char arrays for 16-bit per channel colors too, just like
-for any other color format. The 16-bit values are stored in big endian (most
-significant byte first) in these arrays. This is the opposite order of the
-little endian used by x86 CPU's.
-
-LodePNG always uses big endian because the PNG file format does so internally.
-Conversions to other formats than PNG uses internally are not supported by
-LodePNG on purpose, there are myriads of formats, including endianness of 16-bit
-colors, the order in which you store R, G, B and A, and so on. Supporting and
-converting to/from all that is outside the scope of LodePNG.
-
-This may mean that, depending on your use case, you may want to convert the big
-endian output of LodePNG to little endian with a for loop. This is certainly not
-always needed, many applications and libraries support big endian 16-bit colors
-anyway, but it means you cannot simply cast the unsigned char* buffer to an
-unsigned short* buffer on x86 CPUs.
-
-
-7. error values
----------------
-
-All functions in LodePNG that return an error code, return 0 if everything went
-OK, or a non-zero code if there was an error.
-
-The meaning of the LodePNG error values can be retrieved with the function
-lodepng_error_text: given the numerical error code, it returns a description
-of the error in English as a string.
-
-Check the implementation of lodepng_error_text to see the meaning of each code.
-
-
-8. chunks and PNG editing
--------------------------
-
-If you want to add extra chunks to a PNG you encode, or use LodePNG for a PNG
-editor that should follow the rules about handling of unknown chunks, or if your
-program is able to read other types of chunks than the ones handled by LodePNG,
-then that's possible with the chunk functions of LodePNG.
-
-A PNG chunk has the following layout:
-
-4 bytes length
-4 bytes type name
-length bytes data
-4 bytes CRC
-
-8.1. iterating through chunks
------------------------------
-
-If you have a buffer containing the PNG image data, then the first chunk (the
-IHDR chunk) starts at byte number 8 of that buffer. The first 8 bytes are the
-signature of the PNG and are not part of a chunk. But if you start at byte 8
-then you have a chunk, and can check the following things of it.
-
-NOTE: none of these functions check for memory buffer boundaries. To avoid
-exploits, always make sure the buffer contains all the data of the chunks.
-When using lodepng_chunk_next, make sure the returned value is within the
-allocated memory.
-
-unsigned lodepng_chunk_length(const unsigned char* chunk):
-
-Get the length of the chunk's data. The total chunk length is this length + 12.
-
-void lodepng_chunk_type(char type[5], const unsigned char* chunk):
-unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type):
-
-Get the type of the chunk or compare if it's a certain type
-
-unsigned char lodepng_chunk_critical(const unsigned char* chunk):
-unsigned char lodepng_chunk_private(const unsigned char* chunk):
-unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk):
-
-Check if the chunk is critical in the PNG standard (only IHDR, PLTE, IDAT and IEND are).
-Check if the chunk is private (public chunks are part of the standard, private ones not).
-Check if the chunk is safe to copy. If it's not, then, when modifying data in a critical
-chunk, unsafe to copy chunks of the old image may NOT be saved in the new one if your
-program doesn't handle that type of unknown chunk.
-
-unsigned char* lodepng_chunk_data(unsigned char* chunk):
-const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk):
-
-Get a pointer to the start of the data of the chunk.
-
-unsigned lodepng_chunk_check_crc(const unsigned char* chunk):
-void lodepng_chunk_generate_crc(unsigned char* chunk):
-
-Check if the crc is correct or generate a correct one.
-
-unsigned char* lodepng_chunk_next(unsigned char* chunk):
-const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk):
-
-Iterate to the next chunk. This works if you have a buffer with consecutive chunks. Note that these
-functions do no boundary checking of the allocated data whatsoever, so make sure there is enough
-data available in the buffer to be able to go to the next chunk.
-
-unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk):
-unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length,
-                              const char* type, const unsigned char* data):
-
-These functions are used to create new chunks that are appended to the data in *out that has
-length *outlength. The append function appends an existing chunk to the new data. The create
-function creates a new chunk with the given parameters and appends it. Type is the 4-letter
-name of the chunk.
-
-8.2. chunks in info_png
------------------------
-
-The LodePNGInfo struct contains fields with the unknown chunk in it. It has 3
-buffers (each with size) to contain 3 types of unknown chunks:
-the ones that come before the PLTE chunk, the ones that come between the PLTE
-and the IDAT chunks, and the ones that come after the IDAT chunks.
-It's necessary to make the distionction between these 3 cases because the PNG
-standard forces to keep the ordering of unknown chunks compared to the critical
-chunks, but does not force any other ordering rules.
-
-info_png.unknown_chunks_data[0] is the chunks before PLTE
-info_png.unknown_chunks_data[1] is the chunks after PLTE, before IDAT
-info_png.unknown_chunks_data[2] is the chunks after IDAT
-
-The chunks in these 3 buffers can be iterated through and read by using the same
-way described in the previous subchapter.
-
-When using the decoder to decode a PNG, you can make it store all unknown chunks
-if you set the option settings.remember_unknown_chunks to 1. By default, this
-option is off (0).
-
-The encoder will always encode unknown chunks that are stored in the info_png.
-If you need it to add a particular chunk that isn't known by LodePNG, you can
-use lodepng_chunk_append or lodepng_chunk_create to the chunk data in
-info_png.unknown_chunks_data[x].
-
-Chunks that are known by LodePNG should not be added in that way. E.g. to make
-LodePNG add a bKGD chunk, set background_defined to true and add the correct
-parameters there instead.
-
-
-9. compiler support
--------------------
-
-No libraries other than the current standard C library are needed to compile
-LodePNG. For the C++ version, only the standard C++ library is needed on top.
-Add the files lodepng.c(pp) and lodepng.h to your project, include
-lodepng.h where needed, and your program can read/write PNG files.
-
-It is compatible with C90 and up, and C++03 and up.
-
-If performance is important, use optimization when compiling! For both the
-encoder and decoder, this makes a large difference.
-
-Make sure that LodePNG is compiled with the same compiler of the same version
-and with the same settings as the rest of the program, or the interfaces with
-std::vectors and std::strings in C++ can be incompatible.
-
-CHAR_BITS must be 8 or higher, because LodePNG uses unsigned chars for octets.
-
-*) gcc and g++
-
-LodePNG is developed in gcc so this compiler is natively supported. It gives no
-warnings with compiler options "-Wall -Wextra -pedantic -ansi", with gcc and g++
-version 4.7.1 on Linux, 32-bit and 64-bit.
-
-*) Clang
-
-Fully supported and warning-free.
-
-*) Mingw
-
-The Mingw compiler (a port of gcc for Windows) should be fully supported by
-LodePNG.
-
-*) Visual Studio and Visual C++ Express Edition
-
-LodePNG should be warning-free with warning level W4. Two warnings were disabled
-with pragmas though: warning 4244 about implicit conversions, and warning 4996
-where it wants to use a non-standard function fopen_s instead of the standard C
-fopen.
-
-Visual Studio may want "stdafx.h" files to be included in each source file and
-give an error "unexpected end of file while looking for precompiled header".
-This is not standard C++ and will not be added to the stock LodePNG. You can
-disable it for lodepng.cpp only by right clicking it, Properties, C/C++,
-Precompiled Headers, and set it to Not Using Precompiled Headers there.
-
-NOTE: Modern versions of VS should be fully supported, but old versions, e.g.
-VS6, are not guaranteed to work.
-
-*) Compilers on Macintosh
-
-LodePNG has been reported to work both with gcc and LLVM for Macintosh, both for
-C and C++.
-
-*) Other Compilers
-
-If you encounter problems on any compilers, feel free to let me know and I may
-try to fix it if the compiler is modern and standards complient.
-
-
-10. examples
-------------
-
-This decoder example shows the most basic usage of LodePNG. More complex
-examples can be found on the LodePNG website.
-
-10.1. decoder C++ example
--------------------------
-
-#include "lodepng.h"
-#include <iostream>
-
-int main(int argc, char *argv[]) {
-  const char* filename = argc > 1 ? argv[1] : "test.png";
-
-  //load and decode
-  std::vector<unsigned char> image;
-  unsigned width, height;
-  unsigned error = lodepng::decode(image, width, height, filename);
-
-  //if there's an error, display it
-  if(error) std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl;
-
-  //the pixels are now in the vector "image", 4 bytes per pixel, ordered RGBARGBA..., use it as texture, draw it, ...
-}
-
-10.2. decoder C example
------------------------
-
-#include "lodepng.h"
-
-int main(int argc, char *argv[]) {
-  unsigned error;
-  unsigned char* image;
-  size_t width, height;
-  const char* filename = argc > 1 ? argv[1] : "test.png";
-
-  error = lodepng_decode32_file(&image, &width, &height, filename);
-
-  if(error) printf("decoder error %u: %s\n", error, lodepng_error_text(error));
-
-  / * use image here * /
-
-  free(image);
-  return 0;
-}
-
-11. state settings reference
-----------------------------
-
-A quick reference of some settings to set on the LodePNGState
-
-For decoding:
-
-state.decoder.zlibsettings.ignore_adler32: ignore ADLER32 checksums
-state.decoder.zlibsettings.custom_...: use custom inflate function
-state.decoder.ignore_crc: ignore CRC checksums
-state.decoder.ignore_critical: ignore unknown critical chunks
-state.decoder.ignore_end: ignore missing IEND chunk. May fail if this corruption causes other errors
-state.decoder.color_convert: convert internal PNG color to chosen one
-state.decoder.read_text_chunks: whether to read in text metadata chunks
-state.decoder.remember_unknown_chunks: whether to read in unknown chunks
-state.info_raw.colortype: desired color type for decoded image
-state.info_raw.bitdepth: desired bit depth for decoded image
-state.info_raw....: more color settings, see struct LodePNGColorMode
-state.info_png....: no settings for decoder but ouput, see struct LodePNGInfo
-
-For encoding:
-
-state.encoder.zlibsettings.btype: disable compression by setting it to 0
-state.encoder.zlibsettings.use_lz77: use LZ77 in compression
-state.encoder.zlibsettings.windowsize: tweak LZ77 windowsize
-state.encoder.zlibsettings.minmatch: tweak min LZ77 length to match
-state.encoder.zlibsettings.nicematch: tweak LZ77 match where to stop searching
-state.encoder.zlibsettings.lazymatching: try one more LZ77 matching
-state.encoder.zlibsettings.custom_...: use custom deflate function
-state.encoder.auto_convert: choose optimal PNG color type, if 0 uses info_png
-state.encoder.filter_palette_zero: PNG filter strategy for palette
-state.encoder.filter_strategy: PNG filter strategy to encode with
-state.encoder.force_palette: add palette even if not encoding to one
-state.encoder.add_id: add LodePNG identifier and version as a text chunk
-state.encoder.text_compression: use compressed text chunks for metadata
-state.info_raw.colortype: color type of raw input image you provide
-state.info_raw.bitdepth: bit depth of raw input image you provide
-state.info_raw: more color settings, see struct LodePNGColorMode
-state.info_png.color.colortype: desired color type if auto_convert is false
-state.info_png.color.bitdepth: desired bit depth if auto_convert is false
-state.info_png.color....: more color settings, see struct LodePNGColorMode
-state.info_png....: more PNG related settings, see struct LodePNGInfo
-
-
-12. changes
------------
-
-The version number of LodePNG is the date of the change given in the format
-yyyymmdd.
-
-Some changes aren't backwards compatible. Those are indicated with a (!)
-symbol.
-
-*) 30 dec 2018: code style changes only: removed newlines before opening braces.
-*) 10 sep 2018: added way to inspect metadata chunks without full decoding.
-*) 19 aug 2018 (!): fixed color mode bKGD is encoded with and made it use
-   palette index in case of palette.
-*) 10 aug 2018 (!): added support for gAMA, cHRM, sRGB and iCCP chunks. This
-   change is backwards compatible unless you relied on unknown_chunks for those.
-*) 11 jun 2018: less restrictive check for pixel size integer overflow
-*) 14 jan 2018: allow optionally ignoring a few more recoverable errors
-*) 17 sep 2017: fix memory leak for some encoder input error cases
-*) 27 nov 2016: grey+alpha auto color model detection bugfix
-*) 18 apr 2016: Changed qsort to custom stable sort (for platforms w/o qsort).
-*) 09 apr 2016: Fixed colorkey usage detection, and better file loading (within
-   the limits of pure C90).
-*) 08 dec 2015: Made load_file function return error if file can't be opened.
-*) 24 okt 2015: Bugfix with decoding to palette output.
-*) 18 apr 2015: Boundary PM instead of just package-merge for faster encoding.
-*) 23 aug 2014: Reduced needless memory usage of decoder.
-*) 28 jun 2014: Removed fix_png setting, always support palette OOB for
-    simplicity. Made ColorProfile public.
-*) 09 jun 2014: Faster encoder by fixing hash bug and more zeros optimization.
-*) 22 dec 2013: Power of two windowsize required for optimization.
-*) 15 apr 2013: Fixed bug with LAC_ALPHA and color key.
-*) 25 mar 2013: Added an optional feature to ignore some PNG errors (fix_png).
-*) 11 mar 2013 (!): Bugfix with custom free. Changed from "my" to "lodepng_"
-    prefix for the custom allocators and made it possible with a new #define to
-    use custom ones in your project without needing to change lodepng's code.
-*) 28 jan 2013: Bugfix with color key.
-*) 27 okt 2012: Tweaks in text chunk keyword length error handling.
-*) 8 okt 2012 (!): Added new filter strategy (entropy) and new auto color mode.
-    (no palette). Better deflate tree encoding. New compression tweak settings.
-    Faster color conversions while decoding. Some internal cleanups.
-*) 23 sep 2012: Reduced warnings in Visual Studio a little bit.
-*) 1 sep 2012 (!): Removed #define's for giving custom (de)compression functions
-    and made it work with function pointers instead.
-*) 23 jun 2012: Added more filter strategies. Made it easier to use custom alloc
-    and free functions and toggle #defines from compiler flags. Small fixes.
-*) 6 may 2012 (!): Made plugging in custom zlib/deflate functions more flexible.
-*) 22 apr 2012 (!): Made interface more consistent, renaming a lot. Removed
-    redundant C++ codec classes. Reduced amount of structs. Everything changed,
-    but it is cleaner now imho and functionality remains the same. Also fixed
-    several bugs and shrunk the implementation code. Made new samples.
-*) 6 nov 2011 (!): By default, the encoder now automatically chooses the best
-    PNG color model and bit depth, based on the amount and type of colors of the
-    raw image. For this, autoLeaveOutAlphaChannel replaced by auto_choose_color.
-*) 9 okt 2011: simpler hash chain implementation for the encoder.
-*) 8 sep 2011: lz77 encoder lazy matching instead of greedy matching.
-*) 23 aug 2011: tweaked the zlib compression parameters after benchmarking.
-    A bug with the PNG filtertype heuristic was fixed, so that it chooses much
-    better ones (it's quite significant). A setting to do an experimental, slow,
-    brute force search for PNG filter types is added.
-*) 17 aug 2011 (!): changed some C zlib related function names.
-*) 16 aug 2011: made the code less wide (max 120 characters per line).
-*) 17 apr 2011: code cleanup. Bugfixes. Convert low to 16-bit per sample colors.
-*) 21 feb 2011: fixed compiling for C90. Fixed compiling with sections disabled.
-*) 11 dec 2010: encoding is made faster, based on suggestion by Peter Eastman
-    to optimize long sequences of zeros.
-*) 13 nov 2010: added LodePNG_InfoColor_hasPaletteAlpha and
-    LodePNG_InfoColor_canHaveAlpha functions for convenience.
-*) 7 nov 2010: added LodePNG_error_text function to get error code description.
-*) 30 okt 2010: made decoding slightly faster
-*) 26 okt 2010: (!) changed some C function and struct names (more consistent).
-     Reorganized the documentation and the declaration order in the header.
-*) 08 aug 2010: only changed some comments and external samples.
-*) 05 jul 2010: fixed bug thanks to warnings in the new gcc version.
-*) 14 mar 2010: fixed bug where too much memory was allocated for char buffers.
-*) 02 sep 2008: fixed bug where it could create empty tree that linux apps could
-    read by ignoring the problem but windows apps couldn't.
-*) 06 jun 2008: added more error checks for out of memory cases.
-*) 26 apr 2008: added a few more checks here and there to ensure more safety.
-*) 06 mar 2008: crash with encoding of strings fixed
-*) 02 feb 2008: support for international text chunks added (iTXt)
-*) 23 jan 2008: small cleanups, and #defines to divide code in sections
-*) 20 jan 2008: support for unknown chunks allowing using LodePNG for an editor.
-*) 18 jan 2008: support for tIME and pHYs chunks added to encoder and decoder.
-*) 17 jan 2008: ability to encode and decode compressed zTXt chunks added
-    Also various fixes, such as in the deflate and the padding bits code.
-*) 13 jan 2008: Added ability to encode Adam7-interlaced images. Improved
-    filtering code of encoder.
-*) 07 jan 2008: (!) changed LodePNG to use ISO C90 instead of C++. A
-    C++ wrapper around this provides an interface almost identical to before.
-    Having LodePNG be pure ISO C90 makes it more portable. The C and C++ code
-    are together in these files but it works both for C and C++ compilers.
-*) 29 dec 2007: (!) changed most integer types to unsigned int + other tweaks
-*) 30 aug 2007: bug fixed which makes this Borland C++ compatible
-*) 09 aug 2007: some VS2005 warnings removed again
-*) 21 jul 2007: deflate code placed in new namespace separate from zlib code
-*) 08 jun 2007: fixed bug with 2- and 4-bit color, and small interlaced images
-*) 04 jun 2007: improved support for Visual Studio 2005: crash with accessing
-    invalid std::vector element [0] fixed, and level 3 and 4 warnings removed
-*) 02 jun 2007: made the encoder add a tag with version by default
-*) 27 may 2007: zlib and png code separated (but still in the same file),
-    simple encoder/decoder functions added for more simple usage cases
-*) 19 may 2007: minor fixes, some code cleaning, new error added (error 69),
-    moved some examples from here to lodepng_examples.cpp
-*) 12 may 2007: palette decoding bug fixed
-*) 24 apr 2007: changed the license from BSD to the zlib license
-*) 11 mar 2007: very simple addition: ability to encode bKGD chunks.
-*) 04 mar 2007: (!) tEXt chunk related fixes, and support for encoding
-    palettized PNG images. Plus little interface change with palette and texts.
-*) 03 mar 2007: Made it encode dynamic Huffman shorter with repeat codes.
-    Fixed a bug where the end code of a block had length 0 in the Huffman tree.
-*) 26 feb 2007: Huffman compression with dynamic trees (BTYPE 2) now implemented
-    and supported by the encoder, resulting in smaller PNGs at the output.
-*) 27 jan 2007: Made the Adler-32 test faster so that a timewaste is gone.
-*) 24 jan 2007: gave encoder an error interface. Added color conversion from any
-    greyscale type to 8-bit greyscale with or without alpha.
-*) 21 jan 2007: (!) Totally changed the interface. It allows more color types
-    to convert to and is more uniform. See the manual for how it works now.
-*) 07 jan 2007: Some cleanup & fixes, and a few changes over the last days:
-    encode/decode custom tEXt chunks, separate classes for zlib & deflate, and
-    at last made the decoder give errors for incorrect Adler32 or Crc.
-*) 01 jan 2007: Fixed bug with encoding PNGs with less than 8 bits per channel.
-*) 29 dec 2006: Added support for encoding images without alpha channel, and
-    cleaned out code as well as making certain parts faster.
-*) 28 dec 2006: Added "Settings" to the encoder.
-*) 26 dec 2006: The encoder now does LZ77 encoding and produces much smaller files now.
-    Removed some code duplication in the decoder. Fixed little bug in an example.
-*) 09 dec 2006: (!) Placed output parameters of public functions as first parameter.
-    Fixed a bug of the decoder with 16-bit per color.
-*) 15 okt 2006: Changed documentation structure
-*) 09 okt 2006: Encoder class added. It encodes a valid PNG image from the
-    given image buffer, however for now it's not compressed.
-*) 08 sep 2006: (!) Changed to interface with a Decoder class
-*) 30 jul 2006: (!) LodePNG_InfoPng , width and height are now retrieved in different
-    way. Renamed decodePNG to decodePNGGeneric.
-*) 29 jul 2006: (!) Changed the interface: image info is now returned as a
-    struct of type LodePNG::LodePNG_Info, instead of a vector, which was a bit clumsy.
-*) 28 jul 2006: Cleaned the code and added new error checks.
-    Corrected terminology "deflate" into "inflate".
-*) 23 jun 2006: Added SDL example in the documentation in the header, this
-    example allows easy debugging by displaying the PNG and its transparency.
-*) 22 jun 2006: (!) Changed way to obtain error value. Added
-    loadFile function for convenience. Made decodePNG32 faster.
-*) 21 jun 2006: (!) Changed type of info vector to unsigned.
-    Changed position of palette in info vector. Fixed an important bug that
-    happened on PNGs with an uncompressed block.
-*) 16 jun 2006: Internally changed unsigned into unsigned where
-    needed, and performed some optimizations.
-*) 07 jun 2006: (!) Renamed functions to decodePNG and placed them
-    in LodePNG namespace. Changed the order of the parameters. Rewrote the
-    documentation in the header. Renamed files to lodepng.cpp and lodepng.h
-*) 22 apr 2006: Optimized and improved some code
-*) 07 sep 2005: (!) Changed to std::vector interface
-*) 12 aug 2005: Initial release (C++, decoder only)
-
-
-13. contact information
------------------------
-
-Feel free to contact me with suggestions, problems, comments, ... concerning
-LodePNG. If you encounter a PNG image that doesn't work properly with this
-decoder, feel free to send it and I'll use it to find and fix the problem.
-
-My email address is (puzzle the account and domain together with an @ symbol):
-Domain: gmail dot com.
-Account: lode dot vandevenne.
-
-
-Copyright (c) 2005-2019 Lode Vandevenne
-*/
diff --git a/encoder/pvpngreader.cpp b/encoder/pvpngreader.cpp
new file mode 100644
index 0000000..46639f2
--- /dev/null
+++ b/encoder/pvpngreader.cpp
@@ -0,0 +1,2662 @@
+// pngreader.cpp - Public Domain - see unlicense at bottom of file.
+//
+// Notes: 
+// This is ancient code from ~1995 ported to C++. It was originally written for a 
+// DOS app with very limited memory. It's not as fast as it should be, but it works. 
+// The low-level PNG reader class was written assuming the PNG file could not fit 
+// entirely into memory, which dictated how it was written/structured.
+// It has been modified to use either zlib or miniz.
+// It supports all PNG color types/bit depths/interlacing, however 16-bit/component 
+// images are converted to 8-bit.
+// TRNS chunks are converted to alpha as needed.
+// GAMA chunk is read, but not applied.
+
+#include "../transcoder/basisu.h"
+
+#define MINIZ_HEADER_FILE_ONLY
+#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
+#include "basisu_miniz.h"
+
+#include "pvpngreader.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <vector>
+#include <assert.h>
+
+#define PVPNG_IDAT_CRC_CHECKING (1)
+#define PVPNG_ADLER32_CHECKING (1)
+
+namespace pv_png
+{
+
+const uint32_t MIN_PNG_SIZE = 8 + 13 + 8 + 1 + 4 + 12;
+
+template <typename S> inline S maximum(S a, S b) { return (a > b) ? a : b; }
+template <typename S> inline S minimum(S a, S b) { return (a < b) ? a : b; }
+
+template <typename T> inline void clear_obj(T& obj) { memset(&obj, 0, sizeof(obj)); }
+
+#define MAX_SUPPORTED_RES (32768)
+#define FALSE (0)
+#define TRUE (1)
+#define PNG_MAX_ALLOC_BLOCKS (16)
+
+enum
+{
+	PNG_DECERROR = -3,
+	PNG_ALLDONE = -5,
+	PNG_READPASTEOF = -11,
+	PNG_UNKNOWNTYPE = -16,
+	PNG_FILEREADERROR = -17,
+	PNG_NOTENOUGHMEM = -108,
+	PNG_BAD_CHUNK_CRC32 = -13000,
+	PNG_NO_IHDR = -13001,
+	PNG_BAD_WIDTH = -13002,
+	PNG_BAD_HEIGHT = -13003,
+	PNG_UNS_COMPRESSION = -13004,
+	PNG_UNS_FILTER = -13005,
+	PNG_UNS_ILACE = -13006,
+	PNG_UNS_COLOR_TYPE = -13007,
+	PNG_BAD_BIT_DEPTH = -13008,
+	PNG_BAD_CHUNK_SIZE = -13009,
+	PNG_UNS_CRITICAL_CHUNK = -13010,
+	PNG_BAD_TRNS_CHUNK = -13011,
+	PNG_BAD_PLTE_CHUNK = -13012,
+	PNG_UNS_RESOLUTION = -13013,
+	PNG_INVALID_DATA_STREAM = -13014,
+	PNG_MISSING_PALETTE = -13015,
+	PNG_UNS_PREDICTOR = -13016,
+	PNG_INCOMPLETE_IMAGE = -13017,
+	PNG_TOO_MUCH_DATA = -13018
+};
+
+#define PNG_COLOR_TYPE_PAL_MASK        (1)
+#define PNG_COLOR_TYPE_COL_MASK        (2)
+#define PNG_COLOR_TYPE_ALP_MASK        (4)
+
+#define PNG_INFLATE_SRC_BUF_SIZE       (4096)
+
+struct ihdr_struct
+{
+	uint32_t m_width;
+	uint32_t m_height;
+	uint8_t m_bit_depth;
+	uint8_t m_color_type;
+	uint8_t m_comp_type;
+	uint8_t m_filter_type;
+	uint8_t m_ilace_type;
+};
+
+class png_file
+{
+public:
+	png_file() { }
+	virtual ~png_file() { }
+
+	virtual bool resize(uint64_t new_size) = 0;
+	virtual uint64_t get_size() = 0;
+	virtual uint64_t tell() = 0;
+	virtual bool seek(uint64_t ofs) = 0;
+	virtual size_t write(const void* pBuf, size_t len) = 0;
+	virtual size_t read(void* pBuf, size_t len) = 0;
+};
+
+class png_memory_file : public png_file
+{
+public:
+	std::vector<uint8_t> m_buf;
+	uint64_t m_ofs;
+	
+	png_memory_file() : 
+		png_file(),
+		m_ofs(0)
+	{ 
+	}
+	
+	virtual ~png_memory_file()
+	{ 
+	}
+
+	std::vector<uint8_t>& get_buf() { return m_buf; }
+	const std::vector<uint8_t>& get_buf() const { return m_buf; }
+	 
+	void init()
+	{
+		m_ofs = 0;
+		m_buf.resize(0);
+	}
+
+	virtual bool resize(uint64_t new_size)
+	{
+		if ((sizeof(size_t) == sizeof(uint32_t)) && (new_size >= 0x7FFFFFFF))
+			return false;
+
+		m_buf.resize((size_t)new_size);
+		m_ofs = m_buf.size();
+
+		return true;
+	}
+
+	virtual uint64_t get_size()
+	{
+		return m_buf.size();
+	}
+
+	virtual uint64_t tell()
+	{
+		return m_ofs;
+	}
+
+	virtual bool seek(uint64_t ofs)
+	{
+		m_ofs = ofs;
+		return true;
+	}
+
+	virtual size_t write(const void* pBuf, size_t len)
+	{
+		uint64_t new_size = m_ofs + len;
+		if (new_size > m_buf.size())
+		{
+			if ((sizeof(size_t) == sizeof(uint32_t)) && (new_size > 0x7FFFFFFFUL))
+				return 0;
+			m_buf.resize(new_size);
+		}
+
+		memcpy(&m_buf[(size_t)m_ofs], pBuf, len);
+		m_ofs += len;
+
+		return len;
+	}
+
+	virtual size_t read(void* pBuf, size_t len)
+	{
+		if (m_ofs >= m_buf.size())
+			return 0;
+
+		uint64_t max_bytes = minimum<uint64_t>(len, m_buf.size() - m_ofs);
+		memcpy(pBuf, &m_buf[(size_t)m_ofs], max_bytes);
+
+		m_ofs += max_bytes;
+
+		return max_bytes;
+	}
+};
+
+class png_readonly_memory_file : public png_file
+{
+public:
+	const uint8_t* m_pBuf;
+	size_t m_buf_size;
+	uint64_t m_ofs;
+
+	png_readonly_memory_file() :
+		png_file(),
+		m_pBuf(nullptr),
+		m_buf_size(0),
+		m_ofs(0)
+	{
+	}
+
+	virtual ~png_readonly_memory_file()
+	{
+	}
+
+	void init(const void *pBuf, size_t buf_size)
+	{
+		m_pBuf = static_cast<const uint8_t*>(pBuf);
+		m_buf_size = buf_size;
+		m_ofs = 0;
+	}
+
+	virtual bool resize(uint64_t new_size)
+	{
+		(void)new_size;
+		assert(0);
+		return false;
+	}
+
+	virtual uint64_t get_size()
+	{
+		return m_buf_size;
+	}
+
+	virtual uint64_t tell()
+	{
+		return m_ofs;
+	}
+
+	virtual bool seek(uint64_t ofs)
+	{
+		m_ofs = ofs;
+		return true;
+	}
+
+	virtual size_t write(const void* pBuf, size_t len)
+	{
+		(void)pBuf;
+		(void)len;
+		assert(0);
+		return 0;
+	}
+
+	virtual size_t read(void* pBuf, size_t len)
+	{
+		if (m_ofs >= m_buf_size)
+			return 0;
+
+		uint64_t max_bytes = minimum<uint64_t>(len, m_buf_size - m_ofs);
+		memcpy(pBuf, &m_pBuf[(size_t)m_ofs], max_bytes);
+
+		m_ofs += max_bytes;
+
+		return max_bytes;
+	}
+};
+
+#ifdef _MSC_VER
+#define ftell64 _ftelli64
+#define fseek64 _fseeki64
+#else
+#define ftell64 ftello
+#define fseek64 fseeko
+#endif
+
+class png_cfile : public png_file
+{
+public:
+	FILE* m_pFile;
+	
+	png_cfile() : 
+		png_file(),
+		m_pFile(nullptr)
+	{
+	}
+
+	virtual ~png_cfile()
+	{
+		close();
+	}
+
+	bool init(const char *pFilename, const char *pMode)
+	{
+		close();
+		
+		m_pFile = nullptr;
+		
+#ifdef _MSC_VER
+		fopen_s(&m_pFile, pFilename, pMode);
+#else
+		m_pFile = fopen(pFilename, pMode);
+#endif
+
+		return m_pFile != nullptr;
+	}
+
+	bool close()
+	{
+		bool status = true;
+		if (m_pFile)
+		{
+			if (fclose(m_pFile) == EOF)
+				status = false;
+			m_pFile = nullptr;
+		}
+		return status;
+	}
+
+	virtual bool resize(uint64_t new_size)
+	{
+		if (new_size)
+		{
+			if (!seek(new_size - 1))
+				return false;
+
+			int v = 0;
+			if (write(&v, 1) != 1)
+				return false;
+		}
+		else
+		{
+			if (!seek(0))
+				return false;
+		}
+
+		return true;
+	}
+
+	virtual uint64_t get_size()
+	{
+		int64_t cur_ofs = ftell64(m_pFile);
+		if (cur_ofs < 0)
+			return 0;
+		
+		if (fseek64(m_pFile, 0, SEEK_END) != 0)
+			return 0;
+		
+		const int64_t cur_size = ftell64(m_pFile);
+		if (cur_size < 0)
+			return 0;
+
+		if (fseek64(m_pFile, cur_ofs, SEEK_SET) != 0)
+			return 0;
+		
+		return cur_size;
+	}
+
+	virtual uint64_t tell()
+	{
+		int64_t cur_ofs = ftell64(m_pFile);
+		if (cur_ofs < 0)
+			return 0;
+
+		return cur_ofs;
+	}
+
+	virtual bool seek(uint64_t ofs)
+	{
+		return fseek64(m_pFile, ofs, SEEK_SET) == 0;
+	}
+
+	virtual size_t write(const void* pBuf, size_t len)
+	{
+		return (size_t)fwrite(pBuf, 1, len, m_pFile);
+	}
+
+	virtual size_t read(void* pBuf, size_t len)
+	{
+		return (size_t)fread(pBuf, 1, len, m_pFile);
+	}
+};
+
+// This low-level helper class handles the actual decoding of PNG files.
+class png_decoder
+{
+public:
+	png_decoder();
+	~png_decoder();
+
+	// Scans the PNG file, but doesn't decode the IDAT data. 
+	// Returns 0 on success, or an error code.
+	// If the returned status is non-zero, or m_img_supported_flag==FALSE the image either the image is corrupted/not PNG or is unsupported in some way.
+	int png_scan(png_file *pFile);
+
+	// Decodes a single scanline of PNG image data.
+	// Returns a pointer to the scanline's pixel data and its size in bytes. 
+	// This data is only minimally processed from the internal PNG pixel data.
+	// The caller must use the ihdr, trns_flag and values, and the palette to actually decode the pixel data.
+	//
+	// Possible returned pixel formats is somewhat complex due to the history of this code:
+	// 8-bit RGBA, always 4 bytes/pixel - 24bpp PNG's are converted to 32bpp and TRNS processing is done automatically (8/16bpp RGB or RGBA PNG files)
+	// 1/2/4/8-bit grayscale, 1 byte per pixel - must convert to [0,255] using the palette or some other means, must optionally use the TRNS chunk for alpha (1/2/4/8 Grayscale PNG files - not 16bpp though!)
+	// 1/2/4/8-bit palettized, 1 byte per pixel - must convert to RGB using the 24bpp palette and optionally the TRNS chunk for alpha (1/2/4/8bpp palettized PNG files)
+	// 8-bit grayscale with alpha, 2 bytes per pixel - TRNS processing will be done for you on 16bpp images (there's a special case here for 16bpp Grey files) (8/16bpp Gray-Alpha *or 16bpp Grayscale* PNG files)
+	//
+	// Returns 0 on success, a non-zero error code, or PNG_ALLDONE.
+	int png_decode(void** ppImg_ptr, uint32_t* pImg_len);
+	
+	// Starts decoding. Returns 0 on success, otherwise an error code.
+	int png_decode_start();
+	
+	// Deinitializes the decoder, freeing all allocations.
+	void png_decode_end();
+
+	png_file* m_pFile;
+		
+	// Image's 24bpp palette - 3 bytes per entry
+	uint8_t m_plte_flag;
+	uint8_t m_img_pal[768];
+		
+	int m_img_supported_flag;
+		
+	ihdr_struct m_ihdr;
+
+	uint8_t m_chunk_flag;
+	uint32_t m_chunk_size;
+	uint32_t m_chunk_left;
+	uint32_t m_chunk_crc32;
+	uint8_t m_chunk_name[4];
+
+	uint8_t m_end_of_idat_chunks;
+
+	void* m_pMalloc_blocks[PNG_MAX_ALLOC_BLOCKS];
+
+	uint32_t m_dec_bytes_per_pixel; // bytes per pixel decoded from the PNG file (minimum 1 for 1/2/4 bpp), factors in the PNG 8/16 bit/component bit depth, may be up to 8 bytes (2*4)
+	uint32_t m_dst_bytes_per_pixel; // bytes per pixel returned to the caller (1-4), always has alpha if the PNG has alpha, 16-bit components always converted to 8-bits/component
+
+	uint32_t m_dec_bytes_per_line;	// bytes per line decoded from the PNG file (before 1/2/4 expansion), +1 for the filter byte
+	uint32_t m_src_bytes_per_line;	// decoded PNG bytes per line, before 1/2/4 bpp expansion, not counting the filter byte, updated during adam7 deinterlacing
+	uint32_t m_dst_bytes_per_line;	// bytes per line returned to the caller (1-4 times width)
+
+	int (*m_pProcess_func)(uint8_t* src, uint8_t* dst, int pixels, png_decoder* pwi);
+
+	uint8_t* m_pPre_line_buf;
+	uint8_t* m_pCur_line_buf;
+	uint8_t* m_pPro_line_buf;
+
+	uint8_t m_bkgd_flag;
+	uint32_t  m_bkgd_value[3];
+
+	uint8_t m_gama_flag;
+	uint32_t m_gama_value;
+			
+	uint8_t m_trns_flag;
+	uint32_t m_trns_value[256];
+
+	buminiz::mz_stream m_inflator;
+
+	uint8_t inflate_src_buf[PNG_INFLATE_SRC_BUF_SIZE];
+
+	uint32_t m_inflate_src_buf_ofs;
+	uint32_t m_inflate_src_buf_size;
+	uint32_t m_inflate_dst_buf_ofs;
+
+	int m_inflate_eof_flag;
+		
+	uint8_t m_gamma_table[256];
+
+	int m_pass_x_size;
+	int m_pass_y_left;
+
+	int m_adam7_pass_num;
+	int m_adam7_pass_y;
+	int m_adam7_pass_size_x[7];
+	int m_adam7_pass_size_y[7];
+
+	std::vector<uint8_t> m_adam7_image_buf;
+		
+	int m_adam7_decoded_flag;
+	
+	bool m_scanned_flag;
+		
+	int m_terminate_status;
+		
+#define TEMP_BUF_SIZE (384)
+	uint8_t m_temp_buf[TEMP_BUF_SIZE * 4];
+			
+	void clear();
+	void uninitialize();
+	int terminate(int status);
+	void* png_malloc(uint32_t i);
+	void* png_calloc(uint32_t i);
+	int block_read(void* buf, uint32_t len);
+	int64_t block_read_dword();
+	int fetch_next_chunk_data(uint8_t* buf, int bytes);
+	int fetch_next_chunk_byte();
+	int fetch_next_chunk_word();
+	int64_t fetch_next_chunk_dword();
+	int fetch_next_chunk_init();
+	int unchunk_data(uint8_t* buf, uint32_t bytes, uint32_t* ptr_bytes_read);
+	inline void adam7_write_pixel_8(int x, int y, int c);
+	inline void adam7_write_pixel_16(int x, int y, int r, int g);
+	inline void adam7_write_pixel_24(int x, int y, int r, int g, int b);
+	inline void adam7_write_pixel_32(int x, int y, int r, int g, int b, int a);
+	void unpredict_sub(uint8_t* lst, uint8_t* cur, uint32_t bytes, int bpp);
+	void unpredict_up(uint8_t* lst, uint8_t* cur, uint32_t bytes, int bpp);
+	void unpredict_average(uint8_t* lst, uint8_t* cur, uint32_t bytes, int bpp);
+	inline uint8_t paeth_predictor(int a, int b, int c);
+	void unpredict_paeth(uint8_t* lst, uint8_t* cur, uint32_t bytes, int bpp);
+	int adam7_pass_size(int size, int start, int step);
+	int decompress_line(uint32_t* bytes_decoded);
+	int find_iend_chunk();
+	void calc_gamma_table();
+	void create_grey_palette();
+	int read_signature();
+	int read_ihdr_chunk();
+	int read_bkgd_chunk();
+	int read_gama_chunk();
+	int read_trns_chunk();
+	int read_plte_chunk();
+	int find_idat_chunk();
+};
+
+void png_decoder::uninitialize()
+{
+	m_pFile = nullptr;
+				
+	for (int i = 0; i < PNG_MAX_ALLOC_BLOCKS; i++)
+	{
+		free(m_pMalloc_blocks[i]);
+		m_pMalloc_blocks[i] = nullptr;
+	}
+
+	mz_inflateEnd(&m_inflator);
+}
+
+int png_decoder::terminate(int status)
+{
+	if (m_terminate_status == 0)
+		m_terminate_status = status;
+
+	uninitialize();
+	return status;
+}
+
+void* png_decoder::png_malloc(uint32_t len)
+{
+	if (!len)
+		len++;
+
+	void* p = malloc(len);
+
+	if (!p)
+		return nullptr;
+
+	int j;
+	for (j = 0; j < PNG_MAX_ALLOC_BLOCKS; j++)
+		if (!m_pMalloc_blocks[j])
+			break;
+
+	if (j == PNG_MAX_ALLOC_BLOCKS)
+		return nullptr;
+
+	m_pMalloc_blocks[j] = p;
+
+	return p;
+}
+
+void* png_decoder::png_calloc(uint32_t len)
+{
+	void* p = png_malloc(len);
+	if (!p)
+		return nullptr;
+
+	if (p)
+		memset(p, 0, len);
+
+	return p;
+}
+
+int png_decoder::block_read(void* buf, uint32_t len)
+{
+	size_t bytes_read = m_pFile->read(buf, len);
+	if (bytes_read != len)
+		return terminate(PNG_READPASTEOF);
+	return 0;
+}
+
+int64_t png_decoder::block_read_dword()
+{
+	uint8_t buf[4];
+
+	int status = block_read(buf, 4);
+	if (status != 0)
+		return status;
+
+	uint32_t v = buf[3] + ((uint32_t)buf[2] << 8) + ((uint32_t)buf[1] << 16) + ((uint32_t)buf[0] << 24);
+	return (int64_t)v;
+}
+
+int png_decoder::fetch_next_chunk_data(uint8_t* buf, int bytes)
+{
+	if (!m_chunk_flag)
+		return 0;
+
+	bytes = minimum<int>(bytes, m_chunk_left);
+
+	int status = block_read(buf, bytes);
+	if (status != 0)
+		return status;
+				
+#if PVPNG_IDAT_CRC_CHECKING
+	bool check_crc32 = true;
+#else
+	const bool is_idat = (m_chunk_name[0] == 'I') && (m_chunk_name[1] == 'D') && (m_chunk_name[2] == 'A') && (m_chunk_name[3] == 'T');
+	bool check_crc32 = !is_idat;
+#endif
+
+	if (check_crc32)
+		m_chunk_crc32 = buminiz::mz_crc32(m_chunk_crc32, buf, bytes);
+
+	if ((m_chunk_left -= bytes) == 0)
+	{
+		int64_t res = block_read_dword();
+		if (res < 0)
+			return (int)res;
+
+		if (check_crc32)
+		{
+			if (m_chunk_crc32 != (uint32_t)res)
+				return terminate(PNG_BAD_CHUNK_CRC32);
+		}
+
+		m_chunk_flag = FALSE;
+	}
+
+	return bytes;
+}
+
+int png_decoder::fetch_next_chunk_byte()
+{
+	uint8_t buf[1];
+
+	int status = fetch_next_chunk_data(buf, 1);
+	if (status < 0)
+		return status;
+
+	if (status != 1)
+		return terminate(PNG_BAD_CHUNK_SIZE);
+
+	return buf[0];
+}
+
+int png_decoder::fetch_next_chunk_word()
+{
+	uint8_t buf[2];
+
+	int status = fetch_next_chunk_data(buf, 2);
+	if (status < 0)
+		return status;
+
+	if (status != 2)
+		return terminate(PNG_BAD_CHUNK_SIZE);
+
+	return buf[1] + ((uint32_t)buf[0] << 8);
+}
+
+int64_t png_decoder::fetch_next_chunk_dword()
+{
+	uint8_t buf[4];
+
+	int status = fetch_next_chunk_data(buf, 4);
+	if (status < 0)
+		return status;
+
+	if (status != 4)
+		terminate(PNG_BAD_CHUNK_SIZE);
+
+	uint32_t v = buf[3] + ((uint32_t)buf[2] << 8) + ((uint32_t)buf[1] << 16) + ((uint32_t)buf[0] << 24);
+	return (int64_t)v;
+}
+
+int png_decoder::fetch_next_chunk_init()
+{
+	while (m_chunk_flag)
+	{
+		int status = fetch_next_chunk_data(m_temp_buf, TEMP_BUF_SIZE * 4);
+		if (status != 0)
+			return status;
+	}
+	
+	int64_t n = block_read_dword();
+	if (n < 0)
+		return (int)n;
+
+	m_chunk_size = (uint32_t)n;
+
+	m_chunk_flag = TRUE;
+	m_chunk_left = m_chunk_size + 4;
+	m_chunk_crc32 = 0;
+
+	int status = fetch_next_chunk_data(m_chunk_name, 4);
+	if (status < 0)
+		return status;
+
+	return 0;
+}
+
+int png_decoder::unchunk_data(uint8_t* buf, uint32_t bytes, uint32_t* ptr_bytes_read)
+{
+	uint32_t bytes_read = 0;
+
+	if ((!bytes) || (m_end_of_idat_chunks))
+	{
+		*ptr_bytes_read = 0;
+		return TRUE;
+	}
+
+	while (bytes_read != bytes)
+	{
+		if (!m_chunk_flag)
+		{
+			int res = fetch_next_chunk_init();
+			if (res < 0)
+				return res;
+
+			if ((m_chunk_name[0] != 'I') ||
+				(m_chunk_name[1] != 'D') ||
+				(m_chunk_name[2] != 'A') ||
+				(m_chunk_name[3] != 'T'))
+			{
+				*ptr_bytes_read = bytes_read;
+				m_end_of_idat_chunks = TRUE;
+				return TRUE;
+			}
+		}
+
+		int res = fetch_next_chunk_data(buf + bytes_read, bytes - bytes_read);
+		if (res < 0)
+			return res;
+
+		bytes_read += (uint32_t)res;
+	}
+
+	*ptr_bytes_read = bytes_read;
+
+	return FALSE;
+}
+
+inline void png_decoder::adam7_write_pixel_8(int x, int y, int c)
+{
+	m_adam7_image_buf[x + y * m_dst_bytes_per_line] = (uint8_t)c;
+}
+
+inline void png_decoder::adam7_write_pixel_16(int x, int y, int r, int g)
+{
+	uint32_t ofs = x * 2 + y * m_dst_bytes_per_line;
+	m_adam7_image_buf[ofs + 0] = (uint8_t)r;
+	m_adam7_image_buf[ofs + 1] = (uint8_t)g;
+}
+
+inline void png_decoder::adam7_write_pixel_24(int x, int y, int r, int g, int b)
+{
+	uint32_t ofs = x * 3 + y * m_dst_bytes_per_line;
+	m_adam7_image_buf[ofs + 0] = (uint8_t)r;
+	m_adam7_image_buf[ofs + 1] = (uint8_t)g;
+	m_adam7_image_buf[ofs + 2] = (uint8_t)b;
+}
+
+inline void png_decoder::adam7_write_pixel_32(int x, int y, int r, int g, int b, int a)
+{
+	uint32_t ofs = x * 4 + y * m_dst_bytes_per_line;
+	m_adam7_image_buf[ofs + 0] = (uint8_t)r;
+	m_adam7_image_buf[ofs + 1] = (uint8_t)g;
+	m_adam7_image_buf[ofs + 2] = (uint8_t)b;
+	m_adam7_image_buf[ofs + 3] = (uint8_t)a;
+}
+
+static void PixelDePack2(void* src, void* dst, int numbytes)
+{
+	uint8_t* src8 = (uint8_t*)src;
+	uint8_t* dst8 = (uint8_t*)dst;
+
+	while (numbytes)
+	{
+		uint8_t v = *src8++;
+		
+		for (uint32_t i = 0; i < 8; i++)
+			dst8[7 - i] = (v >> i) & 1;
+
+		dst8 += 8;
+		numbytes--;
+	}
+}
+
+static void PixelDePack16(void* src, void* dst, int numbytes)
+{
+	uint8_t* src8 = (uint8_t*)src;
+	uint8_t* dst8 = (uint8_t*)dst;
+
+	while (numbytes)
+	{
+		uint8_t v = *src8++;
+
+		dst8[0] = (uint8_t)v >> 4;
+		dst8[1] = (uint8_t)v & 0xF;
+		dst8 += 2;
+
+		numbytes--;
+	}
+}
+
+static int unpack_grey_1(uint8_t* src, uint8_t* dst, int pixels, png_decoder *pwi)
+{
+	(void)pwi;
+	PixelDePack2(src, dst, pixels >> 3);
+
+	dst += (pixels & 0xFFF8);
+
+	if ((pixels & 7) != 0)
+	{
+		uint8_t c = src[pixels >> 3];
+
+		pixels &= 7;
+
+		while (pixels--)
+		{
+			*dst++ = ((c & 128) >> 7);
+
+			c <<= 1;
+		}
+	}
+
+	return TRUE;
+}
+
+static int unpack_grey_2(uint8_t* src, uint8_t* dst, int pixels, png_decoder* pwi)
+{
+	(void)pwi;
+	int i = pixels;
+	uint8_t c;
+
+	while (i >= 4)
+	{
+		c = *src++;
+
+		*dst++ = (c >> 6);
+		*dst++ = (c >> 4) & 3;
+		*dst++ = (c >> 2) & 3;
+		*dst++ = (c) & 3;
+
+		i -= 4;
+	}
+
+	if (i)
+	{
+		c = *src;
+
+		while (i--)
+		{
+			*dst++ = (c >> 6);
+
+			c <<= 2;
+		}
+	}
+
+	return TRUE;
+}
+
+static int unpack_grey_4(uint8_t* src, uint8_t* dst, int pixels, png_decoder* pwi)
+{
+	(void)pwi;
+
+	PixelDePack16(src, dst, pixels >> 1);
+
+	if (pixels & 1)
+		dst[pixels & 0xFFFE] = (src[pixels >> 1] >> 4);
+
+	return TRUE;
+}
+
+static int unpack_grey_8(uint8_t* src, uint8_t* dst, int pixels, png_decoder* pwi)
+{
+	(void)src;
+	(void)dst;
+	(void)pixels;
+	(void)pwi;
+	return FALSE;
+}
+
+static int unpack_grey_16(uint8_t* src, uint8_t* dst, int pixels, png_decoder* pwi)
+{
+	(void)pwi;
+	while (pixels--)
+	{
+		*dst++ = *src++;
+
+		src++;
+	}
+
+	return TRUE;
+}
+
+static int unpack_grey_16_2(uint8_t* src, uint8_t* dst, int pixels, png_decoder* pwi)
+{
+	if (pwi->m_trns_flag)
+	{
+		while (pixels--)
+		{
+			uint32_t v = (src[0] << 8) + src[1];
+			src += 2;
+
+			*dst++ = (uint8_t)(v >> 8);
+			*dst++ = (v == pwi->m_trns_value[0]) ? 0 : 255;
+		}
+	}
+	else
+	{
+		while (pixels--)
+		{
+			*dst++ = *src++;
+			*dst++ = 0xFF;
+
+			src++;
+		}
+	}
+
+	return TRUE;
+}
+
+static int unpack_true_8(uint8_t* src, uint8_t* dst, int pixels, png_decoder* pwi)
+{
+	if (pwi->m_trns_flag)
+	{
+		const uint32_t tr = pwi->m_trns_value[0];
+		const uint32_t tg = pwi->m_trns_value[1];
+		const uint32_t tb = pwi->m_trns_value[2];
+
+		for (int i = 0; i < pixels; i++)
+		{
+			uint8_t r = src[i * 3 + 0];
+			uint8_t g = src[i * 3 + 1];
+			uint8_t b = src[i * 3 + 2];
+			
+			dst[i * 4 + 0] = r;
+			dst[i * 4 + 1] = g;
+			dst[i * 4 + 2] = b;
+			dst[i * 4 + 3] = ((r == tr) && (g == tg) && (b == tb)) ? 0 : 255;
+		}
+	}
+	else
+	{
+		for (int i = 0; i < pixels; i++)
+		{
+			dst[i * 4 + 0] = src[i * 3 + 0];
+			dst[i * 4 + 1] = src[i * 3 + 1];
+			dst[i * 4 + 2] = src[i * 3 + 2];
+			dst[i * 4 + 3] = 255;
+		}
+	}
+
+	return TRUE;
+}
+
+static int unpack_true_16(uint8_t* src, uint8_t* dst, int pixels, png_decoder* pwi)
+{
+	if (pwi->m_trns_flag)
+	{
+		const uint32_t tr = pwi->m_trns_value[0];
+		const uint32_t tg = pwi->m_trns_value[1];
+		const uint32_t tb = pwi->m_trns_value[2];
+
+		for (int i = 0; i < pixels; i++)
+		{
+			uint32_t r = (src[i * 6 + 0] << 8) + src[i * 6 + 1];
+			uint32_t g = (src[i * 6 + 2] << 8) + src[i * 6 + 3];
+			uint32_t b = (src[i * 6 + 4] << 8) + src[i * 6 + 5];
+
+			dst[i * 4 + 0] = (uint8_t)(r >> 8);
+			dst[i * 4 + 1] = (uint8_t)(g >> 8);
+			dst[i * 4 + 2] = (uint8_t)(b >> 8);
+			dst[i * 4 + 3] = ((r == tr) && (g == tg) && (b == tb)) ? 0 : 255;
+		}
+	}
+	else
+	{
+		while (pixels--)
+		{
+			dst[0] = src[0];
+			dst[1] = src[2];
+			dst[2] = src[4];
+			dst[3] = 255;
+			
+			dst += 4;
+			src += 6;
+		}
+	}
+
+	return TRUE;
+}
+
+static int unpack_grey_alpha_8(uint8_t* src, uint8_t* dst, int pixels, png_decoder* pwi)
+{
+	(void)pwi;
+	while (pixels--)
+	{
+		dst[0] = src[0];
+		dst[1] = src[1];
+		dst += 2;
+		src += 2;
+	}
+
+	return TRUE;
+}
+
+static int unpack_grey_alpha_16(uint8_t* src, uint8_t* dst, int pixels, png_decoder* pwi)
+{
+	(void)pwi;
+	while (pixels--)
+	{
+		dst[0] = src[0];
+		dst[1] = src[2];
+		dst += 2;
+		src += 4;
+	}
+
+	return TRUE;
+}
+
+static int unpack_true_alpha_8(uint8_t* src, uint8_t* dst, int pixels, png_decoder* pwi)
+{
+	(void)src;
+	(void)dst;
+	(void)pixels;
+	(void)pwi;
+	return FALSE;
+}
+
+static int unpack_true_alpha_16(uint8_t* src, uint8_t* dst, int pixels, png_decoder* pwi)
+{
+	(void)pwi;
+	while (pixels--)
+	{
+		dst[0] = src[0];
+		dst[1] = src[2];
+		dst[2] = src[4];
+		dst[3] = src[6];
+		dst += 4;
+		src += 8;
+	}
+
+	return TRUE;
+}
+
+void png_decoder::unpredict_sub(uint8_t* lst, uint8_t* cur, uint32_t bytes, int bpp)
+{
+	(void)lst;
+	if (bytes == (uint32_t)bpp)
+		return;
+
+	cur += bpp;
+	bytes -= bpp;
+
+	while (bytes--)
+	{
+		*cur += *(cur - bpp);
+		cur++;
+	}
+}
+
+void png_decoder::unpredict_up(uint8_t* lst, uint8_t* cur, uint32_t bytes, int bpp)
+{
+	(void)bpp;
+	while (bytes--)
+		*cur++ += *lst++;
+}
+
+void png_decoder::unpredict_average(uint8_t* lst, uint8_t* cur, uint32_t bytes, int bpp)
+{
+	int i;
+
+	for (i = 0; i < bpp; i++)
+		*cur++ += (*lst++ >> 1);
+
+	if (bytes == (uint32_t)bpp)
+		return;
+
+	bytes -= bpp;
+
+	while (bytes--)
+	{
+		*cur += ((*lst++ + *(cur - bpp)) >> 1);
+		cur++;
+	}
+}
+
+inline uint8_t png_decoder::paeth_predictor(int a, int b, int c)
+{
+	int p, pa, pb, pc;
+
+	/* a = left, b = above, c = upper left */
+
+	p = a + b - c;
+
+	pa = abs(p - a);
+	pb = abs(p - b);
+	pc = abs(p - c);
+
+	if ((pa <= pb) && (pa <= pc))
+		return (uint8_t)a;
+	else if (pb <= pc)
+		return (uint8_t)b;
+	else
+		return (uint8_t)c;
+}
+
+void png_decoder::unpredict_paeth(uint8_t* lst, uint8_t* cur, uint32_t bytes, int bpp)
+{
+	int i;
+
+	for (i = 0; i < bpp; i++)
+		*cur++ += paeth_predictor(0, *lst++, 0);
+
+	if (bytes == (uint32_t)bpp)
+		return;
+
+	bytes -= bpp;
+
+	while (bytes--)
+	{
+		int p, a, b, c, pa, pb, pc;
+
+		a = *(cur - bpp);
+		b = *lst;
+		c = *(lst - bpp);
+
+		p = a + b - c;
+
+		pa = abs(p - a);
+		pb = abs(p - b);
+		pc = abs(p - c);
+
+		if ((pa <= pb) && (pa <= pc))
+			*cur++ += (uint8_t)a;
+		else if (pb <= pc)
+			*cur++ += (uint8_t)b;
+		else
+			*cur++ += (uint8_t)c;
+
+		lst++;
+	}
+}
+
+int png_decoder::adam7_pass_size(int size, int start, int step)
+{
+	if (size > start)
+		return 1 + ((size - 1) - start) / step;
+	else
+		return 0;
+}
+
+// TRUE if no more data, negative on error, FALSE if OK
+int png_decoder::decompress_line(uint32_t* bytes_decoded)
+{
+	int status;
+	uint32_t temp, src_bytes_left, dst_bytes_left;
+
+	m_inflate_dst_buf_ofs = 0;
+
+	for (; ; )
+	{
+		if (m_inflate_src_buf_ofs == PNG_INFLATE_SRC_BUF_SIZE)
+		{
+			int res = unchunk_data(inflate_src_buf, PNG_INFLATE_SRC_BUF_SIZE, &temp);
+			if (res < 0)
+				return res;
+			m_inflate_eof_flag = res;
+
+			m_inflate_src_buf_size = temp;
+
+			m_inflate_src_buf_ofs = 0;
+		}
+
+		for (; ; )
+		{
+			src_bytes_left = m_inflate_src_buf_size - m_inflate_src_buf_ofs;
+			dst_bytes_left = m_dec_bytes_per_line - m_inflate_dst_buf_ofs;
+
+			m_inflator.next_in = inflate_src_buf + m_inflate_src_buf_ofs;
+			m_inflator.avail_in = src_bytes_left;
+			
+			m_inflator.next_out = m_pCur_line_buf + m_inflate_dst_buf_ofs;
+			m_inflator.avail_out = dst_bytes_left;
+						
+			status = buminiz::mz_inflate2(&m_inflator, buminiz::MZ_NO_FLUSH, PVPNG_ADLER32_CHECKING);
+
+			const uint32_t src_bytes_consumed = src_bytes_left - m_inflator.avail_in;
+			const uint32_t dst_bytes_written = dst_bytes_left - m_inflator.avail_out;
+			
+			m_inflate_src_buf_ofs += src_bytes_consumed;
+			m_inflate_dst_buf_ofs += dst_bytes_written;
+
+			if (status != buminiz::MZ_OK)
+			{
+				if (status != buminiz::MZ_STREAM_END)
+					return terminate(PNG_INVALID_DATA_STREAM);
+
+				if (bytes_decoded)
+					*bytes_decoded = m_inflate_dst_buf_ofs;
+
+				return TRUE;
+			}
+
+			if (m_inflate_dst_buf_ofs == m_dec_bytes_per_line)
+			{
+				if (bytes_decoded)
+					*bytes_decoded = m_inflate_dst_buf_ofs;
+
+				return FALSE;
+			}
+
+			if ((m_inflate_src_buf_ofs == m_inflate_src_buf_size) &&
+				(m_inflate_eof_flag == FALSE))
+				break;
+		}
+	}
+}
+
+int png_decoder::find_iend_chunk()
+{
+	uint32_t dummy;
+
+	while (!m_end_of_idat_chunks)
+	{
+		int res = unchunk_data(m_temp_buf, TEMP_BUF_SIZE * 4, &dummy);
+		if (res < 0)
+			return res;
+	}
+
+	for (; ; )
+	{
+		if ((m_chunk_name[0] == 'I') &&
+			(m_chunk_name[1] == 'E') &&
+			(m_chunk_name[2] == 'N') &&
+			(m_chunk_name[3] == 'D'))
+			break;
+
+		int res = fetch_next_chunk_init();
+		if (res < 0)
+			return res;
+	}
+
+	return 0;
+}
+
+int png_decoder::png_decode(void** ppImg_ptr, uint32_t* pImg_len)
+{
+	int status;
+	uint8_t* decoded_line;
+	uint32_t bytes_decoded;
+
+	if (m_adam7_decoded_flag)
+	{
+		if (m_pass_y_left == 0)
+			return PNG_ALLDONE;
+				
+		*ppImg_ptr = &m_adam7_image_buf[(m_ihdr.m_height - m_pass_y_left) * m_dst_bytes_per_line];
+		*pImg_len = m_dst_bytes_per_line;
+				
+		m_pass_y_left--;
+
+		return 0;
+	}
+
+	if (m_pass_y_left == 0)
+	{
+		if (m_ihdr.m_ilace_type == 0)
+		{
+			status = find_iend_chunk();
+			if (status < 0)
+				return status;
+
+			return PNG_ALLDONE;
+		}
+
+		for (; ; )
+		{
+			if (++m_adam7_pass_num == 7)
+			{
+				status = find_iend_chunk();
+				if (status < 0)
+					return status;
+								
+				return PNG_ALLDONE;
+			}
+
+			if (((m_pass_y_left = m_adam7_pass_size_y[m_adam7_pass_num]) != 0) &&
+				((m_pass_x_size = m_adam7_pass_size_x[m_adam7_pass_num]) != 0))
+				break;
+		}
+
+		switch (m_adam7_pass_num)
+		{
+		case 0:
+		case 1:
+		case 3:
+		case 5:
+			m_adam7_pass_y = 0;
+			break;
+		case 2:
+			m_adam7_pass_y = 4;
+			break;
+		case 4:
+			m_adam7_pass_y = 2;
+			break;
+		case 6:
+			m_adam7_pass_y = 1;
+			break;
+		}
+
+		switch (m_ihdr.m_color_type)
+		{
+		case PNG_COLOR_TYPE_GREYSCALE:
+		case PNG_COLOR_TYPE_PALETTIZED:
+		{
+			m_src_bytes_per_line = (((uint32_t)m_pass_x_size * m_ihdr.m_bit_depth) + 7) / 8;
+			break;
+		}
+		case PNG_COLOR_TYPE_TRUECOLOR:
+		{
+			m_src_bytes_per_line = ((uint32_t)m_pass_x_size * m_dec_bytes_per_pixel);
+			break;
+		}
+		case PNG_COLOR_TYPE_GREYSCALE_ALPHA:
+		{
+			m_src_bytes_per_line = ((uint32_t)m_pass_x_size * m_dec_bytes_per_pixel);
+			break;
+		}
+		case PNG_COLOR_TYPE_TRUECOLOR_ALPHA:
+		{
+			m_src_bytes_per_line = ((uint32_t)m_pass_x_size * m_dec_bytes_per_pixel);
+			break;
+		}
+		}
+
+		m_dec_bytes_per_line = m_src_bytes_per_line + 1;
+
+		memset(m_pPre_line_buf, 0, m_src_bytes_per_line);
+	}
+
+	int res = decompress_line(&bytes_decoded);
+	if (res < 0)
+		return terminate(res);
+
+	if (res)
+	{
+		if (m_ihdr.m_ilace_type == 0)
+		{
+			if (m_pass_y_left != 1)
+				return terminate(PNG_INCOMPLETE_IMAGE);
+		}
+		else
+		{
+			if ((m_pass_y_left != 1) && (m_adam7_pass_num != 6))
+				return terminate(PNG_INCOMPLETE_IMAGE);
+		}
+	}
+
+	if (bytes_decoded != m_dec_bytes_per_line)
+		return terminate(PNG_INCOMPLETE_IMAGE);
+
+	decoded_line = &m_pCur_line_buf[1];
+
+	switch (m_pCur_line_buf[0])
+	{
+	case 0:
+		break;
+	case 1:
+	{
+		unpredict_sub(m_pPre_line_buf, m_pCur_line_buf + 1, m_src_bytes_per_line, m_dec_bytes_per_pixel);
+		break;
+	}
+	case 2:
+	{
+		unpredict_up(m_pPre_line_buf, m_pCur_line_buf + 1, m_src_bytes_per_line, m_dec_bytes_per_pixel);
+		break;
+	}
+	case 3:
+	{
+		unpredict_average(m_pPre_line_buf, m_pCur_line_buf + 1, m_src_bytes_per_line, m_dec_bytes_per_pixel);
+		break;
+	}
+	case 4:
+	{
+		unpredict_paeth(m_pPre_line_buf, m_pCur_line_buf + 1, m_src_bytes_per_line, m_dec_bytes_per_pixel);
+		break;
+	}
+	default:
+		return terminate(PNG_UNS_PREDICTOR);
+	}
+
+	memmove(m_pPre_line_buf, m_pCur_line_buf + 1, m_src_bytes_per_line);
+
+	if (m_pProcess_func)
+	{
+		if ((*m_pProcess_func)(m_pCur_line_buf + 1, m_pPro_line_buf, m_pass_x_size, this))
+			decoded_line = m_pPro_line_buf;
+	}
+		
+	if (m_ihdr.m_ilace_type == 0)
+	{
+		*ppImg_ptr = decoded_line;
+		*pImg_len = m_dst_bytes_per_line;
+
+		if (--m_pass_y_left == 0)
+		{
+			res = decompress_line(&bytes_decoded);
+			if (res < 0)
+				return terminate(res);
+
+			if (res == FALSE)
+				return terminate(PNG_TOO_MUCH_DATA);
+
+			if (bytes_decoded)
+				return terminate(PNG_TOO_MUCH_DATA);
+		}
+	}
+	else
+	{
+		int i, x_ofs = 0, y_ofs = 0, x_stp = 0;
+		uint8_t* p = decoded_line;
+
+		switch (m_adam7_pass_num)
+		{
+		case 0: { x_ofs = 0; x_stp = 8; break; }
+		case 1: { x_ofs = 4; x_stp = 8; break; }
+		case 2: { x_ofs = 0; x_stp = 4; break; }
+		case 3: { x_ofs = 2; x_stp = 4; break; }
+		case 4: { x_ofs = 0; x_stp = 2; break; }
+		case 5: { x_ofs = 1; x_stp = 2; break; }
+		case 6: { x_ofs = 0; x_stp = 1; break; }
+		}
+
+		y_ofs = m_adam7_pass_y;
+
+		assert(x_ofs < (int)m_ihdr.m_width);
+		assert(y_ofs < (int)m_ihdr.m_height);
+
+		if (m_dst_bytes_per_pixel == 1)
+		{
+			for (i = m_pass_x_size; i > 0; i--, x_ofs += x_stp)
+				adam7_write_pixel_8(x_ofs, y_ofs, *p++);
+		}
+		else if (m_dst_bytes_per_pixel == 2)
+		{
+			for (i = m_pass_x_size; i > 0; i--, x_ofs += x_stp, p += 2)
+				adam7_write_pixel_16(x_ofs, y_ofs, p[0], p[1]);
+		}
+		else if (m_dst_bytes_per_pixel == 3)
+		{
+			for (i = m_pass_x_size; i > 0; i--, x_ofs += x_stp, p += 3)
+				adam7_write_pixel_24(x_ofs, y_ofs, p[0], p[1], p[2]);
+		}
+		else if (m_dst_bytes_per_pixel == 4)
+		{
+			for (i = m_pass_x_size; i > 0; i--, x_ofs += x_stp, p += 4)
+				adam7_write_pixel_32(x_ofs, y_ofs, p[0], p[1], p[2], p[3]);
+		}
+		else
+		{
+			assert(0);
+		}
+
+		switch (m_adam7_pass_num)
+		{
+		case 0:
+		case 1:
+		case 2: { m_adam7_pass_y += 8; break; }
+		case 3:
+		case 4: { m_adam7_pass_y += 4; break; }
+		case 5:
+		case 6: { m_adam7_pass_y += 2; break; }
+		}
+
+		if ((--m_pass_y_left == 0) && (m_adam7_pass_num == 6))
+		{
+			res = decompress_line(&bytes_decoded);
+			if (res < 0)
+				return terminate(res);
+
+			if (res == FALSE)
+				return terminate(PNG_TOO_MUCH_DATA);
+
+			if (bytes_decoded)
+				return terminate(PNG_TOO_MUCH_DATA);
+		}
+	}
+
+	return 0;
+}
+
+void png_decoder::png_decode_end()
+{
+	uninitialize();
+}
+
+int png_decoder::png_decode_start()
+{
+	int status;
+	
+	if (m_img_supported_flag != TRUE)
+		return terminate(m_img_supported_flag);
+		
+	switch (m_ihdr.m_color_type)
+	{
+	case PNG_COLOR_TYPE_GREYSCALE:
+	{
+		if (m_ihdr.m_bit_depth == 16)
+		{
+			// This is a special case. We can't pass back 8-bit samples and let the caller decide on transparency because the PNG is 16-bits. 
+			// So we expand to 8-bit Gray-Alpha and handle transparency during decoding.
+			// We don't do this with all grayscale cases because that would require more code to deal with 1/2/4bpp expansion.
+			m_dec_bytes_per_pixel = (m_ihdr.m_bit_depth + 7) / 8;
+			m_dst_bytes_per_pixel = 2;
+
+			m_src_bytes_per_line = (((uint32_t)m_ihdr.m_width * m_ihdr.m_bit_depth) + 7) / 8;
+			m_dst_bytes_per_line = 2 * m_ihdr.m_width;
+
+			m_pProcess_func = unpack_grey_16_2;
+		}
+		else
+		{
+			m_dec_bytes_per_pixel = (m_ihdr.m_bit_depth + 7) / 8;
+			m_dst_bytes_per_pixel = 1;
+
+			m_src_bytes_per_line = (((uint32_t)m_ihdr.m_width * m_ihdr.m_bit_depth) + 7) / 8;
+			m_dst_bytes_per_line = m_ihdr.m_width;
+
+			if (m_ihdr.m_bit_depth == 1)
+				m_pProcess_func = unpack_grey_1;
+			else if (m_ihdr.m_bit_depth == 2)
+				m_pProcess_func = unpack_grey_2;
+			else if (m_ihdr.m_bit_depth == 4)
+				m_pProcess_func = unpack_grey_4;
+			else 
+				m_pProcess_func = unpack_grey_8;
+		}
+
+		break;
+	}
+	case PNG_COLOR_TYPE_PALETTIZED:
+	{
+		m_dec_bytes_per_pixel = (m_ihdr.m_bit_depth + 7) / 8;
+		m_dst_bytes_per_pixel = 1;
+
+		m_src_bytes_per_line = (((uint32_t)m_ihdr.m_width * m_ihdr.m_bit_depth) + 7) / 8;
+		m_dst_bytes_per_line = m_ihdr.m_width;
+
+		if (m_ihdr.m_bit_depth == 1)
+			m_pProcess_func = unpack_grey_1;
+		else if (m_ihdr.m_bit_depth == 2)
+			m_pProcess_func = unpack_grey_2;
+		else if (m_ihdr.m_bit_depth == 4)
+			m_pProcess_func = unpack_grey_4;
+		else if (m_ihdr.m_bit_depth == 8)
+			m_pProcess_func = unpack_grey_8;
+		else if (m_ihdr.m_bit_depth == 16)
+			m_pProcess_func = unpack_grey_16;
+
+		break;
+	}
+	case PNG_COLOR_TYPE_TRUECOLOR:
+	{
+		// We always pass back alpha with transparency handling.
+		m_dec_bytes_per_pixel = 3 * (m_ihdr.m_bit_depth / 8);
+		m_dst_bytes_per_pixel = 4;
+
+		m_src_bytes_per_line = ((uint32_t)m_ihdr.m_width * m_dec_bytes_per_pixel);
+		m_dst_bytes_per_line = 4 * m_ihdr.m_width;
+
+		if (m_ihdr.m_bit_depth == 8)
+			m_pProcess_func = unpack_true_8;
+		else if (m_ihdr.m_bit_depth == 16)
+			m_pProcess_func = unpack_true_16;
+
+		break;
+	}
+	case PNG_COLOR_TYPE_GREYSCALE_ALPHA:
+	{
+		m_dec_bytes_per_pixel = 2 * (m_ihdr.m_bit_depth / 8);
+		m_dst_bytes_per_pixel = 2;
+
+		m_src_bytes_per_line = ((uint32_t)m_ihdr.m_width * m_dec_bytes_per_pixel);
+		m_dst_bytes_per_line = m_ihdr.m_width * 2;
+
+		if (m_ihdr.m_bit_depth == 8)
+			m_pProcess_func = unpack_grey_alpha_8;
+		else if (m_ihdr.m_bit_depth == 16)
+			m_pProcess_func = unpack_grey_alpha_16;
+
+		break;
+	}
+	case PNG_COLOR_TYPE_TRUECOLOR_ALPHA:
+	{
+		m_dec_bytes_per_pixel = 4 * (m_ihdr.m_bit_depth / 8);
+		m_dst_bytes_per_pixel = 4;
+
+		m_src_bytes_per_line = ((uint32_t)m_ihdr.m_width * m_dec_bytes_per_pixel);
+		m_dst_bytes_per_line = 4 * m_ihdr.m_width;
+
+		if (m_ihdr.m_bit_depth == 8)
+			m_pProcess_func = unpack_true_alpha_8;
+		else
+			m_pProcess_func = unpack_true_alpha_16;
+
+		break;
+	}
+	}
+
+	m_dec_bytes_per_line = m_src_bytes_per_line + 1;
+
+	m_pPre_line_buf = (uint8_t*)png_calloc(m_src_bytes_per_line);
+	m_pCur_line_buf = (uint8_t*)png_calloc(m_dec_bytes_per_line);
+	m_pPro_line_buf = (uint8_t*)png_calloc(m_dst_bytes_per_line);
+
+	if (!m_pPre_line_buf || !m_pCur_line_buf || !m_pPro_line_buf)
+		return terminate(PNG_NOTENOUGHMEM);
+
+	m_inflate_src_buf_ofs = PNG_INFLATE_SRC_BUF_SIZE;
+
+	int res = mz_inflateInit(&m_inflator);
+	if (res != 0)
+		return terminate(PNG_DECERROR);
+
+	if (m_ihdr.m_ilace_type == 1)
+	{
+		int i;
+		uint32_t total_lines, lines_processed;
+
+		m_adam7_pass_size_x[0] = adam7_pass_size(m_ihdr.m_width, 0, 8);
+		m_adam7_pass_size_x[1] = adam7_pass_size(m_ihdr.m_width, 4, 8);
+		m_adam7_pass_size_x[2] = adam7_pass_size(m_ihdr.m_width, 0, 4);
+		m_adam7_pass_size_x[3] = adam7_pass_size(m_ihdr.m_width, 2, 4);
+		m_adam7_pass_size_x[4] = adam7_pass_size(m_ihdr.m_width, 0, 2);
+		m_adam7_pass_size_x[5] = adam7_pass_size(m_ihdr.m_width, 1, 2);
+		m_adam7_pass_size_x[6] = adam7_pass_size(m_ihdr.m_width, 0, 1);
+
+		m_adam7_pass_size_y[0] = adam7_pass_size(m_ihdr.m_height, 0, 8);
+		m_adam7_pass_size_y[1] = adam7_pass_size(m_ihdr.m_height, 0, 8);
+		m_adam7_pass_size_y[2] = adam7_pass_size(m_ihdr.m_height, 4, 8);
+		m_adam7_pass_size_y[3] = adam7_pass_size(m_ihdr.m_height, 0, 4);
+		m_adam7_pass_size_y[4] = adam7_pass_size(m_ihdr.m_height, 2, 4);
+		m_adam7_pass_size_y[5] = adam7_pass_size(m_ihdr.m_height, 0, 2);
+		m_adam7_pass_size_y[6] = adam7_pass_size(m_ihdr.m_height, 1, 2);
+				
+		m_adam7_image_buf.resize(m_dst_bytes_per_line * m_ihdr.m_height);
+								
+		m_adam7_pass_num = -1;
+
+		m_pass_y_left = 0;
+
+		total_lines = lines_processed = 0;
+
+		for (i = 0; i < 7; i++)
+			total_lines += m_adam7_pass_size_y[i];
+
+		for (; ; )
+		{
+			void* dummy_ptr = nullptr;
+			uint32_t dummy_len = 0;
+
+			status = png_decode(&dummy_ptr, &dummy_len);
+
+			if (status)
+			{
+				if (status == PNG_ALLDONE)
+					break;
+				else
+				{
+					uninitialize();
+
+					return status;
+				}
+			}
+
+			lines_processed++;
+		}
+
+		m_adam7_decoded_flag = TRUE;
+		m_pass_y_left = m_ihdr.m_height;
+	}
+	else
+	{
+		m_pass_x_size = m_ihdr.m_width;
+		m_pass_y_left = m_ihdr.m_height;
+	}
+		
+	return 0;
+}
+
+void png_decoder::calc_gamma_table()
+{
+	if (m_gama_value == 45000)
+	{
+		for (int i = 0; i < 256; i++)
+			m_gamma_table[i] = (uint8_t)i;
+		return;
+	}
+
+	float gamma = (float)(m_gama_value) / 100000.0f;
+
+	gamma = 1.0f / (gamma * 2.2f);
+
+	for (int i = 0; i < 256; i++)
+	{
+		float temp = powf((float)(i) / 255.0f, gamma) * 255.0f;
+
+		int j = (int)(temp + .5f);
+
+		if (j < 0)
+			j = 0;
+		else if (j > 255)
+			j = 255;
+
+		m_gamma_table[i] = (uint8_t)j;
+	}
+}
+
+void png_decoder::create_grey_palette()
+{
+	int i, j;
+	uint8_t* p = m_img_pal;
+
+	const int img_colors = minimum(256, 1 << m_ihdr.m_bit_depth);
+	for (i = 0; i < img_colors; i++)
+	{
+		j = ((uint32_t)255 * (uint32_t)i) / (img_colors - 1);
+
+		*p++ = (uint8_t)j;
+		*p++ = (uint8_t)j;
+		*p++ = (uint8_t)j;
+	}
+}
+
+int png_decoder::read_signature()
+{
+	if (m_pFile->read(m_temp_buf, 8) != 8)
+		return terminate(PNG_UNKNOWNTYPE);
+
+	if ((m_temp_buf[0] != 137) ||
+		(m_temp_buf[1] != 80) ||
+		(m_temp_buf[2] != 78) ||
+		(m_temp_buf[3] != 71) ||
+		(m_temp_buf[4] != 13) ||
+		(m_temp_buf[5] != 10) ||
+		(m_temp_buf[6] != 26) ||
+		(m_temp_buf[7] != 10))
+	{
+		return terminate(PNG_UNKNOWNTYPE);
+	}
+
+	return 0;
+}
+
+int png_decoder::read_ihdr_chunk()
+{
+	int res = fetch_next_chunk_init();
+	if (res < 0)
+		return res;
+
+	if ((m_chunk_name[0] != 'I') || (m_chunk_name[1] != 'H') || (m_chunk_name[2] != 'D') || (m_chunk_name[3] != 'R') || (m_chunk_size != 13))
+		return terminate(PNG_NO_IHDR);
+
+	int64_t v64 = fetch_next_chunk_dword();
+	if (v64 < 0)
+		return (int)v64;
+	m_ihdr.m_width = (uint32_t)v64;
+
+	v64 = fetch_next_chunk_dword();
+	if (v64 < 0)
+		return (int)v64;
+	m_ihdr.m_height = (uint32_t)v64;
+
+	if ((m_ihdr.m_width == 0) || (m_ihdr.m_width > MAX_SUPPORTED_RES))
+		return terminate(PNG_BAD_WIDTH);
+
+	if ((m_ihdr.m_height == 0) || (m_ihdr.m_height > MAX_SUPPORTED_RES))
+		return terminate(PNG_BAD_HEIGHT);
+
+	int v = fetch_next_chunk_byte(); 
+	if (v < 0) 
+		return v;
+	m_ihdr.m_bit_depth = (uint8_t)v;
+
+	v = fetch_next_chunk_byte(); 
+	if (v < 0) 
+		return v;
+	m_ihdr.m_color_type = (uint8_t)v;
+
+	v = fetch_next_chunk_byte();
+	if (v < 0)
+		return v;
+	m_ihdr.m_comp_type = (uint8_t)v;
+
+	v = fetch_next_chunk_byte();
+	if (v < 0)
+		return v;
+	m_ihdr.m_filter_type = (uint8_t)v;
+
+	v = fetch_next_chunk_byte();
+	if (v < 0)
+		return v;
+	m_ihdr.m_ilace_type = (uint8_t)v;
+
+	if (m_ihdr.m_comp_type != 0)
+		m_img_supported_flag = PNG_UNS_COMPRESSION;
+
+	if (m_ihdr.m_filter_type != 0)
+		m_img_supported_flag = PNG_UNS_FILTER;
+
+	if (m_ihdr.m_ilace_type > 1)
+		m_img_supported_flag = PNG_UNS_ILACE;
+		
+	switch (m_ihdr.m_color_type)
+	{
+	case PNG_COLOR_TYPE_GREYSCALE:
+	{
+		switch (m_ihdr.m_bit_depth)
+		{
+		case 1:
+		case 2:
+		case 4:
+		case 8:
+		case 16:
+		{
+			break;
+		}
+		default:
+			return terminate(PNG_BAD_BIT_DEPTH);
+		}
+
+		break;
+	}
+	case PNG_COLOR_TYPE_PALETTIZED:
+	{
+		switch (m_ihdr.m_bit_depth)
+		{
+		case 1:
+		case 2:
+		case 4:
+		case 8:
+		{
+			break;
+		}
+		default:
+			return terminate(PNG_BAD_BIT_DEPTH);
+		}
+
+		break;
+	}
+	case PNG_COLOR_TYPE_TRUECOLOR:
+	case PNG_COLOR_TYPE_GREYSCALE_ALPHA:
+	case PNG_COLOR_TYPE_TRUECOLOR_ALPHA:
+	{
+		switch (m_ihdr.m_bit_depth)
+		{
+		case 8:
+		case 16:
+		{
+			break;
+		}
+		default:
+			return terminate(PNG_BAD_BIT_DEPTH);
+		}
+
+		break;
+	}
+	default:
+		return terminate(PNG_UNS_COLOR_TYPE);
+	}
+
+	return 0;
+}
+
+int png_decoder::read_bkgd_chunk()
+{
+	m_bkgd_flag = TRUE;
+
+	if (m_ihdr.m_color_type == PNG_COLOR_TYPE_PALETTIZED)
+	{
+		int v = fetch_next_chunk_byte();
+		if (v < 0)
+			return v;
+		m_bkgd_value[0] = v;
+	}
+	else if ((m_ihdr.m_color_type == PNG_COLOR_TYPE_GREYSCALE) || (m_ihdr.m_color_type == PNG_COLOR_TYPE_GREYSCALE_ALPHA))
+	{
+		int v = fetch_next_chunk_word();
+		if (v < 0)
+			return v;
+		m_bkgd_value[0] = v;
+	}
+	else if ((m_ihdr.m_color_type == PNG_COLOR_TYPE_TRUECOLOR) || (m_ihdr.m_color_type == PNG_COLOR_TYPE_TRUECOLOR_ALPHA))
+	{
+		int v = fetch_next_chunk_word();
+		if (v < 0)
+			return v;
+		m_bkgd_value[0] = v;
+
+		v = fetch_next_chunk_word();
+		if (v < 0)
+			return v;
+		m_bkgd_value[1] = v;
+		
+		v = fetch_next_chunk_word();
+		if (v < 0)
+			return v;
+		m_bkgd_value[2] = v;
+	}
+
+	return 0;
+}
+
+int png_decoder::read_gama_chunk()
+{
+	m_gama_flag = TRUE;
+
+	int64_t v = fetch_next_chunk_dword();
+	if (v < 0)
+		return (int)v;
+
+	m_gama_value = (uint32_t)v;
+	
+	return 0;
+}
+
+int png_decoder::read_trns_chunk()
+{
+	int i;
+
+	m_trns_flag = TRUE;
+
+	if (m_ihdr.m_color_type == PNG_COLOR_TYPE_PALETTIZED)
+	{
+		for (i = 0; i < 256; i++)
+			m_trns_value[i] = 255;
+
+		const uint32_t img_colors = 1 << m_ihdr.m_bit_depth;
+		if (m_chunk_size > (uint32_t)img_colors)
+			return terminate(PNG_BAD_TRNS_CHUNK);
+
+		for (i = 0; i < (int)m_chunk_size; i++)
+		{
+			int v = fetch_next_chunk_byte();
+			if (v < 0)
+				return v;
+			m_trns_value[i] = v;
+		}
+	}
+	else if (m_ihdr.m_color_type == PNG_COLOR_TYPE_GREYSCALE)
+	{
+		int v = fetch_next_chunk_word();
+		if (v < 0)
+			return v;
+		m_trns_value[0] = v;
+	}
+	else if (m_ihdr.m_color_type == PNG_COLOR_TYPE_TRUECOLOR)
+	{
+		int v = fetch_next_chunk_word();
+		if (v < 0)
+			return v;
+		m_trns_value[0] = v;
+		
+		v = fetch_next_chunk_word();
+		if (v < 0)
+			return v;
+		m_trns_value[1] = v;
+		
+		v = fetch_next_chunk_word();
+		if (v < 0)
+			return v;
+		m_trns_value[2] = v;
+	}
+	else
+	{
+		return terminate(PNG_BAD_TRNS_CHUNK);
+	}
+	return 0;
+}
+
+int png_decoder::read_plte_chunk()
+{
+	int i, j;
+	uint8_t* p;
+
+	if (m_plte_flag)
+		return terminate(PNG_BAD_PLTE_CHUNK);
+
+	m_plte_flag = TRUE;
+
+	memset(m_img_pal, 0, 768);
+
+	if (m_chunk_size % 3)
+		return terminate(PNG_BAD_PLTE_CHUNK);
+
+	j = m_chunk_size / 3;
+
+	const int img_colors = minimum(256, 1 << m_ihdr.m_bit_depth);
+	if (j > img_colors)
+		return terminate(PNG_BAD_PLTE_CHUNK);
+
+	if ((m_ihdr.m_color_type == PNG_COLOR_TYPE_GREYSCALE) ||
+		(m_ihdr.m_color_type == PNG_COLOR_TYPE_GREYSCALE_ALPHA))
+		return terminate(PNG_BAD_PLTE_CHUNK);
+
+	p = m_img_pal;
+
+	for (i = 0; i < j; i++)
+	{
+		int v = fetch_next_chunk_byte();
+		if (v < 0)
+			return v;
+		*p++ = (uint8_t)v;
+		
+		v = fetch_next_chunk_byte();
+		if (v < 0)
+			return v;
+		*p++ = (uint8_t)v;
+
+		v = fetch_next_chunk_byte();
+		if (v < 0)
+			return v;
+		*p++ = (uint8_t)v;
+	}
+	
+	return 0;
+}
+
+int png_decoder::find_idat_chunk()
+{
+	for (; ; )
+	{
+		int res = fetch_next_chunk_init();
+		if (res < 0)
+			return res;
+
+		if (m_chunk_name[0] & 32)  /* ancillary? */
+		{
+			if ((m_chunk_name[0] == 'b') && (m_chunk_name[1] == 'K') && (m_chunk_name[2] == 'G') && (m_chunk_name[3] == 'D'))
+			{
+				res = read_bkgd_chunk();
+				if (res < 0)
+					return res;
+			}
+			else if ((m_chunk_name[0] == 'g') && (m_chunk_name[1] == 'A') && (m_chunk_name[2] == 'M') && (m_chunk_name[3] == 'A'))
+			{
+				res = read_gama_chunk();
+				if (res < 0)
+					return res;
+			}
+			else if ((m_chunk_name[0] == 't') && (m_chunk_name[1] == 'R') && (m_chunk_name[2] == 'N') && (m_chunk_name[3] == 'S'))
+			{
+				res = read_trns_chunk();
+				if (res < 0)
+					return res;
+			}
+		}
+		else
+		{
+			if ((m_chunk_name[0] == 'P') && (m_chunk_name[1] == 'L') && (m_chunk_name[2] == 'T') && (m_chunk_name[3] == 'E'))
+			{
+				res = read_plte_chunk();
+				if (res < 0)
+					return res;
+			}
+			else if ((m_chunk_name[0] == 'I') && (m_chunk_name[1] == 'D') && (m_chunk_name[2] == 'A') && (m_chunk_name[3] == 'T'))
+			{
+				break;
+			}
+			else
+			{
+				m_img_supported_flag = PNG_UNS_CRITICAL_CHUNK;
+			}
+		}
+	}
+
+	return 0;
+}
+
+png_decoder::png_decoder()
+{
+	clear();
+}
+
+png_decoder::~png_decoder()
+{
+	uninitialize();
+}
+
+void png_decoder::clear()
+{
+	clear_obj(m_pMalloc_blocks);
+
+	m_pFile = nullptr;
+
+	clear_obj(m_img_pal);
+
+	m_img_supported_flag = FALSE;
+
+	m_adam7_image_buf.clear();
+
+	clear_obj(m_ihdr);
+
+	m_chunk_flag = FALSE;
+	m_chunk_size = 0;
+	m_chunk_left = 0;
+	m_chunk_crc32 = 0;
+	clear_obj(m_chunk_name);
+
+	m_end_of_idat_chunks = 0;
+
+	m_dec_bytes_per_pixel = 0;
+	m_dst_bytes_per_pixel = 0;
+
+	m_dec_bytes_per_line = 0;
+	m_src_bytes_per_line = 0;
+	m_dst_bytes_per_line = 0;
+
+	m_pProcess_func = nullptr;
+
+	m_pPre_line_buf = nullptr;
+	m_pCur_line_buf = nullptr;
+	m_pPro_line_buf = nullptr;
+
+	m_bkgd_flag = FALSE;
+	clear_obj(m_bkgd_value);
+
+	m_gama_flag = FALSE;
+	m_gama_value = 0;
+
+	m_plte_flag = FALSE;
+
+	m_trns_flag = FALSE;
+	clear_obj(m_trns_value);
+
+	clear_obj(m_inflator);
+
+	m_inflate_src_buf_ofs = 0;
+	m_inflate_src_buf_size = 0;
+	m_inflate_dst_buf_ofs = 0;
+
+	m_inflate_eof_flag = FALSE;
+		
+	clear_obj(m_trns_value);
+
+	m_pass_x_size = 0;
+	m_pass_y_left = 0;
+
+	m_adam7_pass_num = 0;
+	m_adam7_pass_y = 0;
+	clear_obj(m_adam7_pass_size_x);
+	clear_obj(m_adam7_pass_size_y);
+		
+	m_adam7_decoded_flag = FALSE;
+	
+	m_scanned_flag = false;
+				
+	m_terminate_status = 0;
+}
+
+int png_decoder::png_scan(png_file *pFile)
+{
+	m_pFile = pFile;
+		
+	m_img_supported_flag = TRUE;
+	m_terminate_status = 0;
+
+	int res = read_signature();
+	if (res != 0)
+		return res;
+
+	res = read_ihdr_chunk();
+	if (res != 0)
+		return res;
+		
+	res = find_idat_chunk();
+	if (res != 0)
+		return res;
+				
+	if (m_gama_flag)
+		calc_gamma_table();
+
+	if (m_ihdr.m_color_type == PNG_COLOR_TYPE_PALETTIZED)
+	{
+		if (!m_plte_flag)
+			return terminate(PNG_MISSING_PALETTE);
+	}
+	else if ((m_ihdr.m_color_type == PNG_COLOR_TYPE_GREYSCALE) || (m_ihdr.m_color_type == PNG_COLOR_TYPE_GREYSCALE_ALPHA))
+	{
+		create_grey_palette();
+	}
+
+	m_scanned_flag = true;
+
+	return 0;
+}
+
+static inline uint8_t get_709_luma(uint32_t r, uint32_t g, uint32_t b)
+{ 
+	return (uint8_t)((13938U * r + 46869U * g + 4729U * b + 32768U) >> 16U);
+}
+
+bool get_png_info(const void* pImage_buf, size_t buf_size, png_info &info)
+{
+	memset(&info, 0, sizeof(info));
+		
+	if ((!pImage_buf) || (buf_size < MIN_PNG_SIZE))
+		return false;
+
+	png_readonly_memory_file mf;
+	mf.init(pImage_buf, buf_size);
+
+	png_decoder dec;
+
+	int status = dec.png_scan(&mf);
+	if ((status != 0) || (dec.m_img_supported_flag != TRUE))
+		return false;
+
+	info.m_width = dec.m_ihdr.m_width;
+	info.m_height = dec.m_ihdr.m_height;
+	info.m_bit_depth = dec.m_ihdr.m_bit_depth;
+	info.m_color_type = dec.m_ihdr.m_color_type;
+	info.m_has_gamma = dec.m_gama_flag != 0;
+	info.m_gamma_value = dec.m_gama_value;
+	info.m_has_trns = dec.m_trns_flag != 0;
+
+	switch (dec.m_ihdr.m_color_type)
+	{
+	case PNG_COLOR_TYPE_GREYSCALE:
+		info.m_num_chans = dec.m_trns_flag ? 2 : 1;
+		break;
+	case PNG_COLOR_TYPE_GREYSCALE_ALPHA:
+		info.m_num_chans = 2;
+		break;
+	case PNG_COLOR_TYPE_PALETTIZED:
+	case PNG_COLOR_TYPE_TRUECOLOR:
+		info.m_num_chans = dec.m_trns_flag ? 4 : 3;
+		break;
+	case PNG_COLOR_TYPE_TRUECOLOR_ALPHA:
+		info.m_num_chans = 4;
+		break;
+	default:
+		assert(0);
+		break;
+	}
+
+	return true;
+}
+
+void* load_png(const void* pImage_buf, size_t buf_size, uint32_t desired_chans, uint32_t& width, uint32_t& height, uint32_t& num_chans)
+{
+	width = 0;
+	height = 0;
+	num_chans = 0;
+		
+	if ((!pImage_buf) || (buf_size < MIN_PNG_SIZE))
+	{
+		assert(0);
+		return nullptr;
+	}
+
+	if (desired_chans > 4)
+	{
+		assert(0);
+		return nullptr;
+	}
+
+	png_readonly_memory_file mf;
+	mf.init(pImage_buf, buf_size);
+
+	png_decoder dec;
+		
+	int status = dec.png_scan(&mf);
+	if ((status != 0) || (dec.m_img_supported_flag != TRUE))
+		return nullptr;
+
+	uint32_t colortype = dec.m_ihdr.m_color_type;
+	switch (colortype)
+	{
+	case PNG_COLOR_TYPE_GREYSCALE:
+		num_chans = dec.m_trns_flag ? 2 : 1;
+		break;
+	case PNG_COLOR_TYPE_GREYSCALE_ALPHA:
+		num_chans = 2;
+		break;
+	case PNG_COLOR_TYPE_PALETTIZED:
+	case PNG_COLOR_TYPE_TRUECOLOR:
+		num_chans = dec.m_trns_flag ? 4 : 3;
+		break;
+	case PNG_COLOR_TYPE_TRUECOLOR_ALPHA:
+		num_chans = 4;
+		break;
+	default:
+		assert(0);
+		break;
+	}
+
+	if (!desired_chans)
+		desired_chans = num_chans;
+
+#if 0
+	printf("lode_png: %ux%u bitdepth: %u colortype: %u trns: %u ilace: %u\n",
+		dec.m_ihdr.m_width,
+		dec.m_ihdr.m_height,
+		dec.m_ihdr.m_bit_depth,
+		dec.m_ihdr.m_color_type,
+		dec.m_trns_flag,
+		dec.m_ihdr.m_ilace_type);
+#endif
+
+	width = dec.m_ihdr.m_width;
+	height = dec.m_ihdr.m_height;
+	uint32_t bitdepth = dec.m_ihdr.m_bit_depth;
+	uint32_t pitch = width * desired_chans;
+
+	uint64_t total_size = (uint64_t)pitch * height;
+	if (total_size > 0x7FFFFFFFULL)
+		return nullptr;
+	
+	uint8_t* pBuf = (uint8_t*)malloc((size_t)total_size);
+	if (!pBuf)
+		return nullptr;
+	
+	if (dec.png_decode_start() != 0)
+	{
+		free(pBuf);
+		return nullptr;
+	}
+
+	uint8_t* pDst = pBuf;
+
+	for (uint32_t y = 0; y < height; y++, pDst += pitch)
+	{
+		uint8_t* pLine;
+		uint32_t line_bytes;
+		if (dec.png_decode((void**)&pLine, &line_bytes) != 0)
+		{
+			free(pBuf);
+			return nullptr;
+		}
+
+		// This conversion matrix handles converting RGB->Luma, converting grayscale samples to 8-bit samples, converting palettized images, and PNG transparency.
+		switch (colortype)
+		{
+		case PNG_COLOR_TYPE_GREYSCALE:
+		{
+			uint32_t trans_value = dec.m_trns_value[0];
+
+			switch (desired_chans)
+			{
+			case 1:
+				if (bitdepth == 16)
+				{
+					assert(line_bytes == width * 2);
+
+					for (uint32_t i = 0; i < width; i++)
+						pDst[i] = dec.m_img_pal[pLine[i * 2 + 0] * 3];
+				}
+				else if (bitdepth == 8)
+				{
+					assert(line_bytes == width);
+					memcpy(pDst, pLine, pitch);
+				}
+				else
+				{
+					assert(line_bytes == width);
+					for (uint32_t i = 0; i < width; i++)
+						pDst[i] = dec.m_img_pal[pLine[i] * 3];
+				}
+				break;
+			case 2:
+				if (bitdepth == 16)
+				{
+					assert(line_bytes == width * 2);
+					for (uint32_t i = 0; i < width; i++)
+					{
+						pDst[i * 2 + 0] = dec.m_img_pal[pLine[i * 2 + 0] * 3];
+						pDst[i * 2 + 1] = pLine[i * 2 + 1];
+					}
+				}
+				else if (dec.m_trns_flag)
+				{
+					assert(line_bytes == width);
+					for (uint32_t i = 0; i < width; i++)
+					{
+						pDst[i * 2 + 0] = dec.m_img_pal[pLine[i] * 3];
+						pDst[i * 2 + 1] = (pLine[i] == trans_value) ? 0 : 255;
+					}
+				}
+				else
+				{
+					assert(line_bytes == width);
+					for (uint32_t i = 0; i < width; i++)
+					{
+						pDst[i * 2 + 0] = dec.m_img_pal[pLine[i] * 3];
+						pDst[i * 2 + 1] = 255;
+					}
+				}
+				break;
+			case 3:
+				if (bitdepth == 16)
+				{
+					assert(line_bytes == width * 2);
+					for (uint32_t i = 0; i < width; i++)
+					{
+						uint8_t c = dec.m_img_pal[pLine[i * 2 + 0] * 3];
+						pDst[i * 3 + 0] = c;
+						pDst[i * 3 + 1] = c;
+						pDst[i * 3 + 2] = c;
+					}
+				}
+				else
+				{
+					assert(line_bytes == width);
+					for (uint32_t i = 0; i < width; i++)
+					{
+						uint8_t c = dec.m_img_pal[pLine[i] * 3];
+						pDst[i * 3 + 0] = c;
+						pDst[i * 3 + 1] = c;
+						pDst[i * 3 + 2] = c;
+					}
+				}
+				break;
+			case 4:
+				if (bitdepth == 16)
+				{
+					assert(line_bytes == width * 2);
+					for (uint32_t i = 0; i < width; i++)
+					{
+						uint8_t c = dec.m_img_pal[pLine[i * 2 + 0] * 3];
+						pDst[i * 4 + 0] = c;
+						pDst[i * 4 + 1] = c;
+						pDst[i * 4 + 2] = c;
+						pDst[i * 4 + 3] = pLine[i * 2 + 1];
+					}
+				}
+				else if (dec.m_trns_flag)
+				{
+					assert(line_bytes == width);
+					for (uint32_t i = 0; i < width; i++)
+					{
+						uint8_t c = dec.m_img_pal[pLine[i] * 3];
+						pDst[i * 4 + 0] = c;
+						pDst[i * 4 + 1] = c;
+						pDst[i * 4 + 2] = c;
+						pDst[i * 4 + 3] = (pLine[i] == trans_value) ? 0 : 255;
+					}
+				}
+				else
+				{
+					assert(line_bytes == width);
+					for (uint32_t i = 0; i < width; i++)
+					{
+						uint8_t c = dec.m_img_pal[pLine[i] * 3];
+						pDst[i * 4 + 0] = c;
+						pDst[i * 4 + 1] = c;
+						pDst[i * 4 + 2] = c;
+						pDst[i * 4 + 3] = 255;
+					}
+				}
+				break;
+			}
+
+			break;
+		}
+		case PNG_COLOR_TYPE_GREYSCALE_ALPHA:
+		{
+			assert(line_bytes == width * 2);
+
+			switch (desired_chans)
+			{
+			case 1:
+				for (uint32_t i = 0; i < width; i++)
+					pDst[i] = dec.m_img_pal[pLine[i * 2 + 0] * 3];
+				break;
+			case 2:
+				assert(line_bytes == pitch);
+				if (bitdepth >= 8)
+					memcpy(pDst, pLine, pitch);
+				else
+				{
+					for (uint32_t i = 0; i < width; i++)
+					{
+						pDst[i * 2 + 0] = dec.m_img_pal[pLine[i * 2 + 0] * 3];
+						pDst[i * 2 + 1] = pLine[i * 2 + 1];
+					}
+				}
+				break;
+			case 3:
+				for (uint32_t i = 0; i < width; i++)
+				{
+					uint8_t c = dec.m_img_pal[pLine[i * 2 + 0] * 3];
+					pDst[i * 3 + 0] = c;
+					pDst[i * 3 + 1] = c;
+					pDst[i * 3 + 2] = c;
+				}
+				break;
+			case 4:
+				for (uint32_t i = 0; i < width; i++)
+				{
+					uint8_t c = dec.m_img_pal[pLine[i * 2 + 0] * 3];
+					pDst[i * 4 + 0] = c;
+					pDst[i * 4 + 1] = c;
+					pDst[i * 4 + 2] = c;
+					pDst[i * 4 + 3] = pLine[i * 2 + 1];
+				}
+				break;
+			}
+
+			break;
+		}
+		case PNG_COLOR_TYPE_PALETTIZED:
+		{
+			assert(line_bytes == width);
+
+			switch (desired_chans)
+			{
+			case 1:
+				for (uint32_t i = 0; i < width; i++)
+				{
+					const uint8_t* p = &dec.m_img_pal[pLine[i] * 3];
+					pDst[i] = get_709_luma(p[0], p[1], p[2]);
+				}
+				break;
+			case 2:
+				if (dec.m_trns_flag)
+				{
+					for (uint32_t i = 0; i < width; i++)
+					{
+						const uint8_t* p = &dec.m_img_pal[pLine[i] * 3];
+						pDst[i * 2 + 0] = get_709_luma(p[0], p[1], p[2]);
+						pDst[i * 2 + 1] = (uint8_t)dec.m_trns_value[pLine[i]];
+					}
+				}
+				else
+				{
+					for (uint32_t i = 0; i < width; i++)
+					{
+						const uint8_t* p = &dec.m_img_pal[pLine[i] * 3];
+						pDst[i * 2 + 0] = get_709_luma(p[0], p[1], p[2]);
+						pDst[i * 2 + 1] = 255;
+					}
+				}
+				break;
+			case 3:
+				for (uint32_t i = 0; i < width; i++)
+				{
+					const uint8_t* p = &dec.m_img_pal[pLine[i] * 3];
+					pDst[i * 3 + 0] = p[0];
+					pDst[i * 3 + 1] = p[1];
+					pDst[i * 3 + 2] = p[2];
+				}
+				break;
+			case 4:
+				if (dec.m_trns_flag)
+				{
+					for (uint32_t i = 0; i < width; i++)
+					{
+						const uint8_t* p = &dec.m_img_pal[pLine[i] * 3];
+						pDst[i * 4 + 0] = p[0];
+						pDst[i * 4 + 1] = p[1];
+						pDst[i * 4 + 2] = p[2];
+						pDst[i * 4 + 3] = (uint8_t)dec.m_trns_value[pLine[i]];
+					}
+				}
+				else
+				{
+					for (uint32_t i = 0; i < width; i++)
+					{
+						const uint8_t* p = &dec.m_img_pal[pLine[i] * 3];
+						pDst[i * 4 + 0] = p[0];
+						pDst[i * 4 + 1] = p[1];
+						pDst[i * 4 + 2] = p[2];
+						pDst[i * 4 + 3] = 255;
+					}
+				}
+				break;
+			}
+
+			break;
+		}
+		case PNG_COLOR_TYPE_TRUECOLOR:
+		case PNG_COLOR_TYPE_TRUECOLOR_ALPHA:
+		{
+			assert(line_bytes == width * 4);
+
+			switch (desired_chans)
+			{
+			case 1:
+				for (uint32_t i = 0; i < width; i++)
+				{
+					const uint8_t* p = &pLine[i * 4];
+					pDst[i] = get_709_luma(p[0], p[1], p[2]);
+				}
+				break;
+			case 2:
+				for (uint32_t i = 0; i < width; i++)
+				{
+					const uint8_t* p = &pLine[i * 4];
+					pDst[i * 2 + 0] = get_709_luma(p[0], p[1], p[2]);
+					pDst[i * 2 + 1] = p[3];
+				}
+				break;
+			case 3:
+				for (uint32_t i = 0; i < width; i++)
+				{
+					const uint8_t* p = &pLine[i * 4];
+					pDst[i * 3 + 0] = p[0];
+					pDst[i * 3 + 1] = p[1];
+					pDst[i * 3 + 2] = p[2];
+				}
+				break;
+			case 4:
+				memcpy(pDst, pLine, pitch);
+				break;
+			}
+
+			break;
+		}
+		default:
+			assert(0);
+			break;
+		}
+
+	} // y
+
+	return pBuf;
+}
+
+} // namespace pv_png
+
+/*
+	This is free and unencumbered software released into the public domain.
+
+	Anyone is free to copy, modify, publish, use, compile, sell, or
+	distribute this software, either in source code form or as a compiled
+	binary, for any purpose, commercial or non-commercial, and by any
+	means.
+
+	In jurisdictions that recognize copyright laws, the author or authors
+	of this software dedicate any and all copyright interest in the
+	software to the public domain. We make this dedication for the benefit
+	of the public at large and to the detriment of our heirs and
+	successors. We intend this dedication to be an overt act of
+	relinquishment in perpetuity of all present and future rights to this
+	software under copyright law.
+
+	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+	IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+	OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+	ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+	OTHER DEALINGS IN THE SOFTWARE.
+
+	For more information, please refer to <http://unlicense.org/>
+
+	Richard Geldreich, Jr.
+	1/20/2022
+*/
diff --git a/encoder/pvpngreader.h b/encoder/pvpngreader.h
new file mode 100644
index 0000000..4f3fe46
--- /dev/null
+++ b/encoder/pvpngreader.h
@@ -0,0 +1,48 @@
+// pngreader.h - Public Domain - see unlicense at bottom of pvpngreader.cpp
+#pragma once
+#include <stdint.h>
+
+namespace pv_png
+{
+	// PNG color types
+	enum
+	{
+		PNG_COLOR_TYPE_GREYSCALE = 0,
+		PNG_COLOR_TYPE_TRUECOLOR = 2,
+		PNG_COLOR_TYPE_PALETTIZED = 3,
+		PNG_COLOR_TYPE_GREYSCALE_ALPHA = 4,
+		PNG_COLOR_TYPE_TRUECOLOR_ALPHA = 6
+	};
+
+	// PNG file description
+	struct png_info
+	{
+		uint32_t m_width;
+		uint32_t m_height;
+				
+		uint32_t m_num_chans;	// The number of channels, factoring in transparency. Ranges from [1-4].
+
+		uint32_t m_bit_depth;	// PNG ihdr bit depth: 1, 2, 4, 8 or 16
+		uint32_t m_color_type;	// PNG ihdr color type, PNG_COLOR_TYPE_GRAYSCALE etc.
+
+		bool m_has_gamma;		// true if the PNG file had a GAMA chunk
+		uint32_t m_gamma_value; // PNG GAMA chunk value, scaled by 100000
+
+		bool m_has_trns;		// true if the PNG file used colorkey transparency
+	};
+
+	// Retrieved information about the PNG file.
+	// Returns false on any errors.
+	bool get_png_info(const void* pImage_buf, size_t buf_size, png_info& info);
+
+	// Input parameters:
+	// pImage_buf, buf_size - pointer to PNG image data
+	// desired_chans - desired number of output channels. 0=auto, 1=grayscale, 2=grayscale alpha, 3=24bpp RGB, 4=32bpp RGBA
+	//
+	// Output parameters:
+	// width, height - PNG image resolution
+	// num_chans - actual number of channels in PNG, from [1,4] (factoring in transparency)
+	//
+	// Returns nullptr on any errors.
+	void* load_png(const void* pImage_buf, size_t buf_size, uint32_t desired_chans, uint32_t &width, uint32_t &height, uint32_t& num_chans);
+}
diff --git a/format.sh b/format.sh
index 5d90393..5760aec 100755
--- a/format.sh
+++ b/format.sh
@@ -14,6 +14,8 @@
         continue
     elif [[ $f == *"min.js" ]]; then
         continue
+    elif [[ $f == *"coder.js" ]]; then
+        continue
     fi
     # Ensures that files are UTF-8 formatted.
     recode UTF-8 $f 2> /dev/null
diff --git a/test_files/alpha0.png b/test_files/alpha0.png
new file mode 100644
index 0000000..ea22fb2
--- /dev/null
+++ b/test_files/alpha0.png
Binary files differ
diff --git a/test_files/black_1x1.png b/test_files/black_1x1.png
new file mode 100644
index 0000000..7b435bc
--- /dev/null
+++ b/test_files/black_1x1.png
Binary files differ
diff --git a/test_files/kodim01.png b/test_files/kodim01.png
new file mode 100644
index 0000000..14317f0
--- /dev/null
+++ b/test_files/kodim01.png
Binary files differ
diff --git a/test_files/kodim02.png b/test_files/kodim02.png
new file mode 100644
index 0000000..20c7206
--- /dev/null
+++ b/test_files/kodim02.png
Binary files differ
diff --git a/test_files/kodim03.png b/test_files/kodim03.png
new file mode 100644
index 0000000..d57e8bd
--- /dev/null
+++ b/test_files/kodim03.png
Binary files differ
diff --git a/test_files/kodim04.png b/test_files/kodim04.png
new file mode 100644
index 0000000..de0991c
--- /dev/null
+++ b/test_files/kodim04.png
Binary files differ
diff --git a/test_files/kodim05.png b/test_files/kodim05.png
new file mode 100644
index 0000000..400e4cf
--- /dev/null
+++ b/test_files/kodim05.png
Binary files differ
diff --git a/test_files/kodim06.png b/test_files/kodim06.png
new file mode 100644
index 0000000..949d1e2
--- /dev/null
+++ b/test_files/kodim06.png
Binary files differ
diff --git a/test_files/kodim07.png b/test_files/kodim07.png
new file mode 100644
index 0000000..d04374d
--- /dev/null
+++ b/test_files/kodim07.png
Binary files differ
diff --git a/test_files/kodim08.png b/test_files/kodim08.png
new file mode 100644
index 0000000..d628801
--- /dev/null
+++ b/test_files/kodim08.png
Binary files differ
diff --git a/test_files/kodim09.png b/test_files/kodim09.png
new file mode 100644
index 0000000..1d47188
--- /dev/null
+++ b/test_files/kodim09.png
Binary files differ
diff --git a/test_files/kodim10.png b/test_files/kodim10.png
new file mode 100644
index 0000000..2e4b18b
--- /dev/null
+++ b/test_files/kodim10.png
Binary files differ
diff --git a/test_files/kodim11.png b/test_files/kodim11.png
new file mode 100644
index 0000000..3c66526
--- /dev/null
+++ b/test_files/kodim11.png
Binary files differ
diff --git a/test_files/kodim12.png b/test_files/kodim12.png
new file mode 100644
index 0000000..55db018
--- /dev/null
+++ b/test_files/kodim12.png
Binary files differ
diff --git a/test_files/kodim13.png b/test_files/kodim13.png
new file mode 100644
index 0000000..b35c9d3
--- /dev/null
+++ b/test_files/kodim13.png
Binary files differ
diff --git a/test_files/kodim14.png b/test_files/kodim14.png
new file mode 100644
index 0000000..e7b3283
--- /dev/null
+++ b/test_files/kodim14.png
Binary files differ
diff --git a/test_files/kodim15.png b/test_files/kodim15.png
new file mode 100644
index 0000000..9c5eacf
--- /dev/null
+++ b/test_files/kodim15.png
Binary files differ
diff --git a/test_files/kodim16.png b/test_files/kodim16.png
new file mode 100644
index 0000000..54b0d0d
--- /dev/null
+++ b/test_files/kodim16.png
Binary files differ
diff --git a/test_files/kodim17.png b/test_files/kodim17.png
new file mode 100644
index 0000000..5e4583e
--- /dev/null
+++ b/test_files/kodim17.png
Binary files differ
diff --git a/test_files/kodim18.png b/test_files/kodim18.png
new file mode 100644
index 0000000..48097fc
--- /dev/null
+++ b/test_files/kodim18.png
Binary files differ
diff --git a/test_files/kodim19.png b/test_files/kodim19.png
new file mode 100644
index 0000000..e359a32
--- /dev/null
+++ b/test_files/kodim19.png
Binary files differ
diff --git a/test_files/kodim20.png b/test_files/kodim20.png
new file mode 100644
index 0000000..86e2837
--- /dev/null
+++ b/test_files/kodim20.png
Binary files differ
diff --git a/test_files/kodim21.png b/test_files/kodim21.png
new file mode 100644
index 0000000..ccd4c3f
--- /dev/null
+++ b/test_files/kodim21.png
Binary files differ
diff --git a/test_files/kodim22.png b/test_files/kodim22.png
new file mode 100644
index 0000000..52bcd47
--- /dev/null
+++ b/test_files/kodim22.png
Binary files differ
diff --git a/test_files/kodim23.png b/test_files/kodim23.png
new file mode 100644
index 0000000..ff22e83
--- /dev/null
+++ b/test_files/kodim23.png
Binary files differ
diff --git a/test_files/kodim24.png b/test_files/kodim24.png
new file mode 100644
index 0000000..85bd64f
--- /dev/null
+++ b/test_files/kodim24.png
Binary files differ
diff --git a/test_files/white_1x1.png b/test_files/white_1x1.png
new file mode 100644
index 0000000..7fbca9a
--- /dev/null
+++ b/test_files/white_1x1.png
Binary files differ
diff --git a/test_files/wikipedia.png b/test_files/wikipedia.png
new file mode 100644
index 0000000..4190fcc
--- /dev/null
+++ b/test_files/wikipedia.png
Binary files differ
diff --git a/transcoder/basisu.h b/transcoder/basisu.h
index f33baf6..1230b59 100644
--- a/transcoder/basisu.h
+++ b/transcoder/basisu.h
@@ -21,6 +21,11 @@
 	#pragma warning (disable : 4127) // warning C4127: conditional expression is constant
 	#pragma warning (disable : 4530) // C++ exception handler used, but unwind semantics are not enabled.
 
+	// Slamming this off always for v1.16 because we've gotten rid of most std containers.
+	#ifndef BASISU_NO_ITERATOR_DEBUG_LEVEL
+		#define BASISU_NO_ITERATOR_DEBUG_LEVEL (1)
+	#endif
+
 	#ifndef BASISU_NO_ITERATOR_DEBUG_LEVEL
 		//#define _HAS_ITERATOR_DEBUGGING 0
 
@@ -165,6 +170,10 @@
 	{ 
 		if (n)
 		{
+			if (vec.size())
+			{
+				assert((pObjs + n) <= vec.begin() || (pObjs >= vec.end()));
+			}
 			const size_t cur_s = vec.size();
 			vec.resize(cur_s + n);
 			memcpy(&vec[cur_s], pObjs, sizeof(R) * n);
@@ -173,6 +182,7 @@
 
 	template<typename T> inline void append_vector(T &vec, const T &other_vec)
 	{
+		assert(&vec != &other_vec);
 		if (other_vec.size())
 			append_vector(vec, &other_vec[0], other_vec.size());
 	}
diff --git a/transcoder/basisu_containers.h b/transcoder/basisu_containers.h
index 1ca4bab..d3e1436 100644
--- a/transcoder/basisu_containers.h
+++ b/transcoder/basisu_containers.h
@@ -12,6 +12,12 @@
 #define HAS_MALLOC_USABLE_SIZE 1
 #endif
 
+// Set to 1 to always check vector operator[], front(), and back() even in release.
+#define BASISU_VECTOR_FORCE_CHECKING 0
+
+// If 1, the vector container will not query the CRT to get the size of resized memory blocks.
+#define BASISU_VECTOR_DETERMINISTIC 1
+
 #ifdef _MSC_VER
 #define BASISU_FORCE_INLINE __forceinline
 #else
@@ -279,7 +285,10 @@
          m_size = other.m_size;
 
          if (BASISU_IS_BITWISE_COPYABLE(T))
-            memcpy(m_p, other.m_p, m_size * sizeof(T));
+         {
+             if ((m_p) && (other.m_p))
+                memcpy(m_p, other.m_p, m_size * sizeof(T));
+         }
          else
          {
             T* pDst = m_p;
@@ -320,7 +329,10 @@
          }
 
          if (BASISU_IS_BITWISE_COPYABLE(T))
-            memcpy(m_p, other.m_p, other.m_size * sizeof(T));
+         {
+             if ((m_p) && (other.m_p))
+                memcpy(m_p, other.m_p, other.m_size * sizeof(T));
+         }
          else
          {
             T* pDst = m_p;
@@ -348,20 +360,81 @@
       // operator[] will assert on out of range indices, but in final builds there is (and will never be) any range checking on this method.
       //BASISU_FORCE_INLINE const T& operator[] (uint32_t i) const { assert(i < m_size); return m_p[i]; }
       //BASISU_FORCE_INLINE T& operator[] (uint32_t i) { assert(i < m_size); return m_p[i]; }
-
+            
+#if !BASISU_VECTOR_FORCE_CHECKING
       BASISU_FORCE_INLINE const T& operator[] (size_t i) const { assert(i < m_size); return m_p[i]; }
       BASISU_FORCE_INLINE T& operator[] (size_t i) { assert(i < m_size); return m_p[i]; }
+#else
+      BASISU_FORCE_INLINE const T& operator[] (size_t i) const 
+      { 
+          if (i >= m_size)
+          {
+              fprintf(stderr, "operator[] invalid index: %u, max entries %u, type size %u\n", (uint32_t)i, m_size, (uint32_t)sizeof(T));
+              abort();
+          }
+          return m_p[i]; 
+      }
+      BASISU_FORCE_INLINE T& operator[] (size_t i) 
+      { 
+          if (i >= m_size)
+          {
+              fprintf(stderr, "operator[] invalid index: %u, max entries %u, type size %u\n", (uint32_t)i, m_size, (uint32_t)sizeof(T));
+              abort();
+          }
+          return m_p[i]; 
+      }
+#endif
 
       // at() always includes range checking, even in final builds, unlike operator [].
       // The first element is returned if the index is out of range.
       BASISU_FORCE_INLINE const T& at(size_t i) const { assert(i < m_size); return (i >= m_size) ? m_p[0] : m_p[i]; }
       BASISU_FORCE_INLINE T& at(size_t i) { assert(i < m_size); return (i >= m_size) ? m_p[0] : m_p[i]; }
-
+            
+#if !BASISU_VECTOR_FORCE_CHECKING
       BASISU_FORCE_INLINE const T& front() const { assert(m_size); return m_p[0]; }
       BASISU_FORCE_INLINE T& front() { assert(m_size); return m_p[0]; }
 
       BASISU_FORCE_INLINE const T& back() const { assert(m_size); return m_p[m_size - 1]; }
       BASISU_FORCE_INLINE T& back() { assert(m_size); return m_p[m_size - 1]; }
+#else
+      BASISU_FORCE_INLINE const T& front() const 
+      { 
+          if (!m_size)
+          {
+              fprintf(stderr, "front: vector is empty, type size %u\n", (uint32_t)sizeof(T));
+              abort();
+          }
+          return m_p[0]; 
+      }
+      BASISU_FORCE_INLINE T& front() 
+      { 
+          if (!m_size)
+          {
+              fprintf(stderr, "front: vector is empty, type size %u\n", (uint32_t)sizeof(T));
+              abort();
+          }
+          return m_p[0]; 
+      }
+
+      BASISU_FORCE_INLINE const T& back() const 
+      { 
+          if(!m_size)
+          {
+              fprintf(stderr, "back: vector is empty, type size %u\n", (uint32_t)sizeof(T));
+              abort();
+          }
+          return m_p[m_size - 1]; 
+      }
+      BASISU_FORCE_INLINE T& back() 
+      { 
+          if (!m_size)
+          {
+              fprintf(stderr, "back: vector is empty, type size %u\n", (uint32_t)sizeof(T));
+              abort();
+          }
+          return m_p[m_size - 1]; 
+      }
+#endif
 
       BASISU_FORCE_INLINE const T* get_ptr() const { return m_p; }
       BASISU_FORCE_INLINE T* get_ptr() { return m_p; }
@@ -952,6 +1025,8 @@
 
       // Caller is granting ownership of the indicated heap block.
       // Block must have size constructed elements, and have enough room for capacity elements.
+      // The block must have been allocated using malloc().
+      // Important: This method is used in Basis Universal. If you change how this container allocates memory, you'll need to change any users of this method.
       inline bool grant_ownership(T* p, uint32_t size, uint32_t capacity)
       {
          // To to prevent the caller from obviously shooting themselves in the foot.
diff --git a/transcoder/basisu_containers_impl.h b/transcoder/basisu_containers_impl.h
index 6555171..d5cb615 100644
--- a/transcoder/basisu_containers_impl.h
+++ b/transcoder/basisu_containers_impl.h
@@ -55,7 +55,9 @@
             abort();
          }
 
-#ifdef _MSC_VER
+#if BASISU_VECTOR_DETERMINISTIC
+         actual_size = desired_size;
+#elif defined(_MSC_VER)
          actual_size = _msize(new_p);
 #elif HAS_MALLOC_USABLE_SIZE
          actual_size = malloc_usable_size(new_p);
@@ -82,7 +84,9 @@
             abort();
          }
 
-#ifdef _MSC_VER
+#if BASISU_VECTOR_DETERMINISTIC
+         actual_size = desired_size;
+#elif defined(_MSC_VER)
          actual_size = _msize(new_p);
 #elif HAS_MALLOC_USABLE_SIZE
          actual_size = malloc_usable_size(new_p);
diff --git a/transcoder/basisu_global_selector_cb.h b/transcoder/basisu_global_selector_cb.h
deleted file mode 100644
index 8ab5098..0000000
--- a/transcoder/basisu_global_selector_cb.h
+++ /dev/null
@@ -1,272 +0,0 @@
-// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-{
-0x0, 0x505, 0x5555, 0x5F5F, 0x5055050, 0x5055F5F, 0x50AAA551, 0xFAA5A5AA, 0x6AAA5095, 0x41E6FBAB, 0x19AE99F5, 0x1057AAA4, 0x54005A1A, 0x4459AEAF, 0x56015B, 0xBAA9A554,
-0x4335E5E0, 0xD9FE5FBB, 0x2525256A, 0x9AE892, 0xC0D5FAF5, 0x5BA5E641, 0x7EDEC8B8, 0xBB671211, 0x4C9844EE, 0xEE042415, 0xE5663EAE, 0x90909091, 0xAAA45AFF, 0x15556E1D, 0xA6959195, 0x4BFF8BF,
-0x5166AAF, 0x15490065, 0x6F5BAFAF, 0xFF00FF00, 0xD96956AA, 0x15AF6B, 0xFF5A00AA, 0xE0E557AA, 0x1A6F19BD, 0x69655555, 0xD0500158, 0xEEEDD894, 0xE4E4FE, 0xC71B7B10, 0x55AA5AAF, 0x50AA59BE,
-0xE4E990E4, 0x5353B63B, 0xFEE5D0E4, 0x96AF051A, 0x3CC95A6, 0x70B5A40D, 0x9504196E, 0x4A0BD7A3, 0x11B89592, 0xAAFF4095, 0x55A5D4E0, 0xBBA55050, 0x1111666, 0xA5544000, 0xED994444, 0x5A56BF,
-0x94A954B9, 0xFB651000, 0x604E633E, 0x14291A15, 0x56965956, 0xB8E0D0C0, 0x5A565A55, 0x65A61A6A, 0xE490F990, 0xCA87AAF5, 0x6060A0B, 0x24C23143, 0x55AA9A40, 0x505E1605, 0xCEC0486E, 0x156E55EA,
-0x79978B0B, 0x4595F53C, 0x405C4AF7, 0xC1897D75, 0xD5F40BA6, 0x95444017, 0x14AD6935, 0x87C7A7BD, 0x4A4E8597, 0xFF1D7E55, 0x451400F9, 0x1112277B, 0x9A0590F8, 0x53E3492E, 0xE590E995, 0x7E730A9A,
-0x929697E7, 0x2E781609, 0xE22317A1, 0xEDE9D884, 0xDDD75CDD, 0xAF1B6F1A, 0xE6909047, 0xA77DAD5D, 0x184C0D5D, 0xFAB56010, 0x5EA4F1D0, 0x11166B6B, 0xF51A7AD6, 0xF79950F4, 0x1B6B1B11, 0x9A6D6469,
-0x441997E, 0x4546869A, 0x95AA6965, 0x155A6A, 0x6E68B0E6, 0x5A55A665, 0x1B051605, 0x601D8BE6, 0xBD2F1B06, 0x409A429B, 0x23272721, 0xB07454A9, 0x7E66A3A1, 0x1B6A5500, 0xA0E0F5A6, 0xBF5A0500,
-0x55A5A9A9, 0x99D9E995, 0xE440566F, 0x6550BE99, 0x2267777B, 0xFA50FE50, 0xA657B441, 0xB4E29343, 0x555090E5, 0x45465B6B, 0xE654E6, 0xEA90469B, 0x2E05D2F4, 0x99594444, 0xF1C20746, 0x295AD2E0,
-0xF990EA95, 0x804459AE, 0xA9999894, 0x1F41E4A5, 0x4040E5E5, 0x5481E1F2, 0x2AFF59F1, 0x6B6B1712, 0xA7131051, 0xF9406F16, 0x1B2B5B9E, 0x587E0F2F, 0x547E1919, 0xD0F5645B, 0xB1B1B1B, 0x5756A4FE,
-0x46A9B965, 0x1529F99D, 0xE490E490, 0x4495FE, 0x985E0B06, 0x5FD2D23A, 0x5D0E95A, 0xF69103F4, 0x4029790, 0x1B062F1B, 0xEE594500, 0xB6539B5A, 0x106165BA, 0xD26B7C8D, 0x8B2A25A5, 0x55EAD5E3,
-0x431FB8E1, 0xBEB4E646, 0x9A5A4545, 0x5015A6B, 0x90D4B83D, 0xDB8A99A4, 0x9E894905, 0xDD7D1101, 0xA95E00BF, 0x579FA5A5, 0xA292D145, 0x93292C96, 0xF9A995A5, 0xBFE8A450, 0xB990D15B, 0x45D1E01A,
-0x4BD3F767, 0xF243479A, 0x7E420927, 0xF9E5E090, 0xA1C869F, 0x253A36, 0x9BAB569A, 0x4147031F, 0xA059AFE, 0xE0D6590F, 0xD5EAD5E6, 0x9A4B4641, 0x5AAA4406, 0x55EA90E4, 0x10179BC4, 0x44485999,
-0x5156253E, 0x1F29E054, 0xCDDAA773, 0x5601AB05, 0x94FC94C0, 0x116166BB, 0xBF964006, 0x414196EB, 0x8498D9ED, 0xB5E08687, 0xBD564150, 0x2B8D9DF8, 0x7F12017E, 0x90904747, 0x50B56AB, 0xDBD19490,
-0xBB5A5659, 0xBAF40E4, 0x6D649014, 0x1D29166F, 0x414F3D75, 0x6F929540, 0x565AAF05, 0xBD9884E5, 0xF5342A25, 0x157915AE, 0x1A055A55, 0x9019A19F, 0x64B96A05, 0x35689CCC, 0x996012E2, 0x5252677B,
-0x156AA401, 0x25BCE483, 0xAA665555, 0xD6AF4B0F, 0x3F4BBDE0, 0x9404A9AF, 0xA590F9E4, 0x8191A5FD, 0x568190B4, 0x591A6616, 0x92C11D3E, 0x97D2E5FC, 0xF5A55A6F, 0xBEE0969B, 0x8918B4CA, 0xE0915397,
-0x5243472F, 0x95EA4055, 0x55E6E0A4, 0x9AEBD181, 0xF4A25357, 0x11115666, 0xFE45FF0A, 0x8BC7D2E1, 0x800556BB, 0x757D6A96, 0xFA909A5B, 0x68962FDB, 0xEB0056AA, 0x69970241, 0xAA58AD64, 0xC4D9DED5,
-0x5A5BF2F0, 0xBD0905B4, 0x197D7801, 0x8987EDC4, 0xFF40565A, 0x460978A4, 0xE4067FE4, 0x5DA23153, 0xB90565AE, 0x5E14B946, 0x4E35879F, 0xC72F8666, 0x1816472F, 0x9A5A4949, 0x64A0D1E5, 0xC7025B1A,
-0x1B061B55, 0xFFAA051B, 0xAF5DEDA1, 0xAA955094, 0x6659965A, 0x99A95DAD, 0x9450A5A5, 0xA550A595, 0x6914B950, 0xEF454944, 0x906BB990, 0xD680944B, 0xE091461B, 0x5363B7BB, 0xF0743906, 0x66566A69,
-0x4B85D0BC, 0x40E494A5, 0x1161B6B6, 0x519BD59, 0x5998401, 0x1651F26B, 0x5709BB1B, 0x6AE1D1B9, 0xD19297BF, 0x1A69FEE4, 0x6066B5A, 0x74A56491, 0xB4661151, 0x559191A4, 0x96756A68, 0xF5C791A6,
-0x20297A15, 0x6B660100, 0x313177A2, 0x55054150, 0x6A969669, 0xF0B82111, 0x555A6996, 0xB666295A, 0x1EA95441, 0x6A166BA5, 0x8C18566D, 0x2797278A, 0x82A552BD, 0xF964BD14, 0x41540668, 0x5078785A,
-0x5754FE, 0xF9E0E5FA, 0x15453D3F, 0x5A9699A9, 0xD9854147, 0x849494E9, 0x1DC39734, 0x67E797B3, 0x107066F, 0xAED9986C, 0xAB564140, 0x9B51A6B7, 0x5FD3E2F4, 0x5A5429F9, 0xF9A05161, 0x5A5A6965,
-0xDDD88484, 0xFA50FA55, 0x90E5E4FA, 0x6BF166B, 0x6566665A, 0xE450A6E5, 0xEB45AA04, 0xDA9A4646, 0xD7A37235, 0x11431B97, 0xD41D6E64, 0xD3D3A1A0, 0x5D540E9, 0x627777BB, 0x5054A4BE, 0x593A05ED,
-0x2EBE454B, 0x1ABA1015, 0x7C64B460, 0xC358B47F, 0x176F4293, 0xA6E417AB, 0xF611756E, 0x1F40D499, 0x84885D5F, 0x2F0B9B9B, 0x14BE05, 0xE5919590, 0x101B146E, 0x7B261190, 0xDC96F8B0, 0xF460257E,
-0x34B0AFC0, 0xEB9140FE, 0xC5C589DD, 0x1F6D6865, 0xF5100195, 0xAF560607, 0x505066B5, 0x7E590999, 0x13E190E4, 0xA56ABD59, 0xC21B68D7, 0xE594E4, 0xF6576E50, 0xFFA751D1, 0x19A179CB, 0x2726797,
-0xA1931C7C, 0xE1D90F1B, 0x7F2B2510, 0x6AF90055, 0x5F1E4C88, 0xE410757A, 0x95702212, 0x7B762100, 0x1B05BF6A, 0x16F05AB, 0xDDC5C9C9, 0x72BE594, 0xE490E555, 0xC5E50106, 0x816DAC16, 0x5540FA90,
-0x156605FF, 0x3B372621, 0x2B57A67D, 0x6C661E16, 0x1E97A917, 0xE6E2D383, 0x1B40F91B, 0xD9A63333, 0x34E18629, 0xA71616E9, 0x84946D99, 0x1B6906AF, 0xEFDE8904, 0x88F52470, 0x50E990F8, 0x4182E1B4,
-0xBAE1865B, 0xF48E4F4, 0x64A0517F, 0xA1F45902, 0x12177BF5, 0x465EBD91, 0x37A747, 0xF0A5106, 0x4C4E8A5, 0x62779E65, 0xDE494989, 0x7B6796D1, 0xC5C5C58A, 0xE4786B07, 0x6F07E0F9, 0x5554A550,
-0x95559333, 0x747A6B5, 0xA4A45500, 0xE998444, 0xF5966371, 0x111116BB, 0x783A679, 0x95409AFF, 0xFF9690E4, 0x60743EBD, 0x1C5A90FD, 0x2B051EE9, 0x5B7A1624, 0xEB415701, 0x1B6B0155, 0x9BCB8586,
-0x599E5C51, 0x510064BE, 0x50FA6060, 0x16066B5B, 0x54DA89D5, 0xA01468B5, 0xC1655E5, 0x55FF6657, 0xE4985E9, 0xD738BE27, 0x6938D450, 0x47D0E4FE, 0x4858986E, 0xE793431E, 0x1A05FFFD, 0x18939141,
-0x15EE4620, 0x79E45151, 0x663AA556, 0xD1266DD9, 0x7E0655E0, 0xB6A7676D, 0x54A96AA5, 0x1664092B, 0x56517AA0, 0xD6402CB8, 0x40A7773C, 0x554F0646, 0x488D5F2F, 0xE4E49095, 0x1C7CB4E0, 0x7C27529A,
-0xF6FAA151, 0xCC7358D6, 0xE8406D15, 0x6E074B5F, 0x638359F7, 0xD4E9A88C, 0xE888050F, 0xE6546A0B, 0xB9904EBD, 0x755061AF, 0xA371285C, 0xE95A1904, 0xCADD042D, 0x757F6ED6, 0xE4A91F06, 0x6D5D0909,
-0xE49559B8, 0xF4B0569B, 0x8454B5B9, 0x2161B5B6, 0x855AADEE, 0x575B0544, 0xBFE4D086, 0xE484CBEB, 0xF9F5426F, 0xCC653366, 0xA3524656, 0x9A5989E4, 0x10451466, 0x71F1655, 0x9B90A4ED, 0x14599FF,
-0x9666AA91, 0x5A99A945, 0x9685CD8F, 0xB8506A91, 0xB427E0F8, 0x50A990FE, 0xA5FA9090, 0x60D4DA80, 0x28E35CB3, 0x55E4AA, 0xD20B55A4, 0xE15F86, 0x36E6995E, 0x54036FFF, 0xA79D2250, 0xBA11A500,
-0x404603AC, 0x641065A0, 0x9DD84A0A, 0x969B061B, 0x36737313, 0x7B65631A, 0xA4E4C099, 0x9590448F, 0xD57F0680, 0x6094D86D, 0x15D8E3BD, 0x757F7DD7, 0xB45B854, 0x6560FA98, 0x7A805637, 0xD68416BB,
-0x7B767131, 0x90F9E8FE, 0xA4E54045, 0xE0411F6E, 0xD57D7DDD, 0x33CB1C33, 0x58ADD010, 0x9B1FA5C6, 0xA401BE95, 0xA950F994, 0xA851971E, 0x33CC33CC, 0x10F0B164, 0x151A6F01, 0x78B5660C, 0x33333225,
-0x41162402, 0x5F0506CB, 0xFD96166F, 0xE4417643, 0x56A51A94, 0x5323BFEA, 0xD12DD12D, 0xA999959A, 0x547C6482, 0x499EE652, 0x4AC7D1E5, 0x2D3DAD07, 0x6B171201, 0xAF065854, 0xD6C4891D, 0xCC739CE7,
-0x9D692663, 0x3E41597C, 0xF38314BB, 0x1150A4F4, 0xE1E50FA, 0xF4D60B6F, 0x5A54E590, 0x227AB5F0, 0x73A3D7FC, 0xD7420A59, 0x12015A59, 0x4F1999D5, 0xA90EE44A, 0x1065B9B5, 0xD10533E3, 0xBA918409,
-0xE5409FEF, 0x4549047F, 0x6B57A6A5, 0xE94691AA, 0x111A6E7E, 0x45496BA, 0x49FD999, 0x414D5B8D, 0xAB10EF5E, 0xE9878505, 0x8C910499, 0xC0C5DA3E, 0x6F1B7298, 0x177D78D0, 0x687B5665, 0x3F470353,
-0x1441A590, 0xE1965F6F, 0x5A5B4A8D, 0x47D7C98, 0xD1404115, 0xB89A053F, 0x8C4095FE, 0x4861E055, 0x3B417607, 0xF9E0E4E0, 0x65B0506, 0x93633236, 0xAA07A5E4, 0x77747080, 0x776160F0, 0x1672B05,
-0xA54E0428, 0x520A9625, 0xE581065, 0x90C76D76, 0x2157B2B3, 0x5C5BE06, 0x151A5A01, 0xA9D5C081, 0xCBCD9854, 0xFDD1061F, 0xB66111B1, 0x9DC3D7B0, 0x650A7642, 0x8095734D, 0xD052011B, 0xE0A1479B,
-0x9501BFF8, 0xE9D9BD0D, 0x7A017925, 0x69A67373, 0x41E0E557, 0x5F844124, 0xEAB0695E, 0x566B5040, 0xCCC9D693, 0xA79684DE, 0x6B5BC3C1, 0x9595667B, 0x9C33CA5C, 0x8984C5C5, 0x459BBDE9, 0x1F10A5F4,
-0x22A55AA, 0x97C3430D, 0xAA569A55, 0x552E1E00, 0xD3C3C78B, 0x82C7521E, 0x5B0605EA, 0x5FF69268, 0xD081460B, 0xE4517F06, 0x4448C9CE, 0x2F69F940, 0x476DA470, 0x9F96FE12, 0x4D8D9E8E, 0x6A6A16B5,
-0x1D05BE66, 0x84F5BD, 0x691E1B41, 0xE0939B17, 0x159059AA, 0x1E5792B9, 0x25A701A5, 0x439162E, 0xE994077C, 0x5CC396AA, 0x1D0D9AA5, 0x4A4A598D, 0x1B6F156B, 0x1A1B0F40, 0x34CB34CB, 0x6F542E,
-0x32CC739C, 0x94EB9669, 0xDA8D4E1D, 0xC6C5C46E, 0x10152B3F, 0x8787F9F9, 0x5E42D064, 0x699B05E9, 0x7030295E, 0x495E09BE, 0xEE191016, 0x801D2D56, 0x3A0099F9, 0xEA09059E, 0x5BAB5100, 0x393D49C,
-0x10E15DC2, 0xB056DD4, 0x3536915, 0xE0C18719, 0xEB964090, 0x6172727, 0xFD5900FA, 0xD10B78D1, 0x33332626, 0x50F990F9, 0x78600A5B, 0xE2B5401B, 0xAE5E9404, 0xF2CF0C0, 0x9E9D8080, 0x84E4F4F9,
-0x41F0E59F, 0x90787E12, 0xE4E19143, 0x761D6706, 0x6560BCE5, 0x134A9BD3, 0x23768995, 0x22ADF6, 0x434A5C72, 0xD4985444, 0x70936BFF, 0xAB54E0E7, 0x45E7A682, 0x7A786D90, 0xF8546A00, 0x5F5E4540,
-0x999A651E, 0xF9E297FD, 0xAF86E5, 0xD00B6E54, 0x5442878A, 0x50E940A5, 0x61F6AF1, 0x479701AA, 0xAE455E5D, 0x6560123E, 0x22D17625, 0x83071B64, 0xF9460251, 0x5F4B064A, 0x8742417C, 0x5F89C51A,
-0x14A29F50, 0x5013BF6B, 0x76395676, 0x54A590F9, 0x40915AA7, 0xEB95E041, 0x7E560504, 0x65B9E4D1, 0x3F63A594, 0x17448216, 0x1A4F87F1, 0xF990E696, 0xECE89A50, 0x2266B17, 0x6A959A98, 0x50F5001A,
-0xBF056A55, 0x74470FFE, 0x65251011, 0x9F7D6597, 0x51BB962, 0xA0D04297, 0xA257F0D1, 0x5B1569D5, 0x4F40959E, 0xEC5D1D0D, 0x51A1A, 0x1DF56462, 0xC4491A6F, 0x4B4A55EF, 0xFD741D5F, 0xE1526713,
-0x875153E2, 0x9752A2E2, 0xEFDA8504, 0xF0E84756, 0xE0A196E9, 0x5FAF5C40, 0x9A3359CC, 0xE056062E, 0xB07B71D7, 0x5966475D, 0x66161100, 0x444A0151, 0xDAC7D6F5, 0xFBE8E314, 0x35098512, 0x1A7F7690,
-0xAF970158, 0x666A6996, 0xD1D10938, 0x742969B, 0x4542A5FE, 0x6EBE50A1, 0x816E7955, 0x64E1D0C1, 0x105156AA, 0x6A9AD0F5, 0xB4909E2E, 0x55A6A959, 0x45B4999F, 0x3266CC26, 0x9B915EE, 0x9769E58B,
-0x2EF59968, 0x3F2F0711, 0x79798469, 0x6161B6B6, 0xA79504B1, 0x9B92A351, 0x61C08573, 0xAB1B656F, 0x37271601, 0xE4840979, 0x45D1C1DA, 0xA4C4961B, 0x59A7F2F0, 0xEA9FC147, 0x635362B0, 0x9561EAF,
-0x6B6762A1, 0x585A43D3, 0x8484819F, 0xD1C30D5D, 0x2123101D, 0xA0F8E4F9, 0x63676220, 0x17EB6A5, 0x90E63F27, 0xDD256045, 0x7B66A1A0, 0x64143F6, 0x41D479D7, 0xF1520F82, 0x12B44687, 0x1504BE1A,
-0x90E45401, 0xC4C98E4F, 0x919097C, 0xA7A52919, 0xB9B62313, 0x9695C089, 0x30C5E6BD, 0xAA55669B, 0xD19F0645, 0x1150E2FF, 0x36213121, 0x1F1F0A05, 0x2A315099, 0x2A1E0414, 0xA3E3D04, 0xD5992851,
-0x19A56A45, 0x5D0669E5, 0xA7C1F8C0, 0x84D1E5AA, 0x7292A464, 0x9040F5E4, 0xF185405, 0x1FAE4509, 0xF91690BE, 0x5540A540, 0xA1D2874B, 0x560B65F8, 0xC207E1E6, 0x646D0F9, 0x5A1440, 0xBB454116,
-0x13597242, 0x413A4504, 0x66E7D2D2, 0x61DA6950, 0x519DF0A0, 0xD2926EB4, 0xA583060B, 0x247E1587, 0xE50590BD, 0xEFF50146, 0x6252B722, 0x4B9AF552, 0x42445A5, 0x5D0844D0, 0xD7C1D18, 0x6B53900,
-0x7DD68434, 0xE6964247, 0xE0A50B5, 0x72635347, 0x669A6B06, 0x91549A65, 0x8F097CA5, 0x849458EC, 0xF9B09275, 0x71390D5A, 0x478BC9D6, 0x5D579AA5, 0x9ED08605, 0xCA1C35D3, 0x1029669, 0x1344FEA7,
-0x5B468B87, 0xA7F29990, 0x60BDB855, 0x3430B574, 0x544461FF, 0xC5C9550E, 0x69E716A7, 0x112336, 0x3F11D2D7, 0x2F0796E4, 0xB5250B00, 0x33CC33DD, 0x20357676, 0x7B6F9272, 0x114B09BB, 0xA7F6C987,
-0x32959833, 0x40D25BB6, 0x13170353, 0xD52E5949, 0x93626538, 0x43449A56, 0x655890BA, 0x2F56811B, 0xE5E4C88, 0xA6079500, 0xA4F90507, 0x6460A055, 0xE990906D, 0x156F56AC, 0x54CF00, 0x181D5A0A,
-0x7C09E947, 0xAD9E898C, 0xFF914212, 0x6933A7CC, 0xB2935B2E, 0x4454D8A2, 0xA6A560B6, 0x519E2075, 0x575FA6A5, 0xB8B06916, 0x598B471B, 0x10686AD1, 0x45EA0170, 0xD0470B9B, 0x3B511E0B, 0x53D79D0,
-0xCBCAC5C5, 0xDAD54CD0, 0x3542EE79, 0xB4AD4FD, 0x642DFF01, 0xB99109B9, 0xE1919B9F, 0x97B84162, 0xE995460A, 0x1060F5F5, 0x166DBCF1, 0x4214957A, 0x6C60626, 0x50FE4F0B, 0xB466470A, 0x808596E2,
-0x70D1440D, 0x818617B6, 0xC8E8DDED, 0x40443474, 0x103E0750, 0x1559A9, 0x16E29FF, 0x54FE0447, 0x34CAB25C, 0x9B30756A, 0xB0E74B05, 0xE19051D, 0x402E7450, 0xF5E0D1AB, 0x87979B5F, 0x8707BA71,
-0x90B4A491, 0x1A2F5301, 0x6C44D318, 0x8AC0A1F4, 0x5A6F0306, 0xE1159090, 0xF9A54183, 0x4CC7321C, 0x7E64868B, 0xFDE60582, 0x4BE77014, 0x1B902D01, 0x104D8FA7, 0x16A7CD, 0x94693912, 0x62E759A,
-0x594BA906, 0x5D023747, 0xDF9757AD, 0x97364CCA, 0xFA011265, 0x12E16116, 0x7A615600, 0x501196F9, 0x5067E247, 0x2A75B070, 0xBC0196BE, 0x19FD8907, 0xCA8511AE, 0x7B671210, 0xB8F0966F, 0x600AE5F4,
-0x4146858E, 0xA579C124, 0x19F26C13, 0x2320776, 0x595BF900, 0xFB059055, 0x6FD6E460, 0x86CAD5D, 0x948153A5, 0xC6C546FF, 0xE199AD5A, 0x656A566A, 0x81256994, 0x7C285400, 0x37CD6A37, 0x4CF4E1B,
-0xD181E0B5, 0x90F89F46, 0x5AD2D072, 0xF1F44D4, 0xB5E091C6, 0xFF90E764, 0x656B9965, 0x833471C7, 0xE6470700, 0x521A517, 0x56620BF, 0x7A6458C9, 0x566959A6, 0x5A5FF3D2, 0xD050063F, 0x9AC17C39,
-0xC1F03D19, 0xE7939343, 0x35312404, 0x76671223, 0xA0D05804, 0x7B773262, 0x5E2E6465, 0xE6860519, 0xDE909B5A, 0xB5C094E4, 0xAF019B15, 0x1A57027F, 0x7874E7D3, 0xB35674A, 0xD0854FB6, 0x916509FD,
-0x431F91FF, 0x9B605420, 0x566978B4, 0xE8D1042D, 0x2533074, 0xEC904443, 0xD404A4D1, 0xB9984945, 0x435181E6, 0xDFD0520A, 0x37FC61D, 0x1540FA94, 0x876DB853, 0x9D686C9D, 0x5D7DE642, 0x556A6669,
-0x6B166F05, 0xF0F06616, 0xE490051F, 0x147B0606, 0xFD76D9D9, 0x3B814E5D, 0x16E6460, 0x91F05406, 0x37444D34, 0x1B17BF00, 0xA8465A05, 0x12429D1C, 0x79753935, 0x639291F4, 0x6761F0F4, 0xBC789460,
-0xF890D79E, 0x54780743, 0x1131367B, 0xD6487C64, 0x8E582E4F, 0x6A972A65, 0x1BA6D0E5, 0x17D6007A, 0x82590727, 0x95D0FA, 0x1540E47E, 0x56B91A0B, 0x8A85C4D4, 0x9F8205E4, 0x80D4C58B, 0x75D3E647,
-0x5956D966, 0x74ED4500, 0x167EA440, 0x255E191, 0x31811515, 0x82999DDF, 0x11670BB8, 0x2BDAD965, 0x5AA5669A, 0x55BF105A, 0x88496E59, 0x5AF56600, 0x4858E751, 0xF4811BB9, 0xB501A7B0, 0x11B26DB,
-0x767C9887, 0x602D7703, 0x1219F8FD, 0x464297D, 0xFF06DB95, 0x156A04BF, 0x5050A39C, 0x35CA4F94, 0x7F00EF1B, 0x68353273, 0x150663B6, 0x79666190, 0xDA650647, 0xA962959A, 0x96E596FF, 0x537E17A3,
-0x57F9E440, 0x101458FF, 0xA4D4E441, 0x1898C4E0, 0x7E189481, 0xB6C71904, 0x9A95EDDD, 0x944449FF, 0x61E4C997, 0x52DF8F5, 0x6A51F46E, 0x9145AD, 0xD9A8DDD2, 0x8784E63D, 0xFF5B906E, 0x2998A559,
-0xCCCC6633, 0xB954C0D0, 0x70B5663B, 0x531C8B25, 0xFFBA0191, 0xF4E35B90, 0x40FF7150, 0x1F075AE5, 0xD0015BFF, 0xDCC3D6DB, 0x4E54A07F, 0x7E9647A, 0xA19D4E1, 0x51504404, 0xE7D68A8A, 0xAF10A450,
-0x71B71184, 0x79940A0E, 0x821B196E, 0x50413A5B, 0x5707962D, 0xD1B63962, 0x819DEDE4, 0xEEC5CB54, 0x251DBAD, 0x50BD6D1D, 0x20976E74, 0xDDC98A4F, 0x451079E9, 0x69146E, 0x68590311, 0x8045A9F4,
-0xEEC58B96, 0x98CCC996, 0x94784451, 0xE6D6015B, 0x3035B95, 0x12E39F6, 0x50EE4058, 0x4D1C74A0, 0xA4291505, 0x936B67D4, 0x2AC1449D, 0xA4015A6D, 0xAFCB4414, 0x74A50038, 0xDED051C8, 0x347B76DA,
-0x817ED01B, 0xDD2D79D2, 0x5A1A011B, 0xA040F556, 0x540246B8, 0xF2B45A06, 0x6A4999B4, 0x4B67D0BF, 0x31614701, 0x456C84C1, 0xB8F4814C, 0xFF009669, 0x4F4A4999, 0x907D95AB, 0xB7A49402, 0x526E61D,
-0x5A9542D3, 0xF8792606, 0xA913569, 0x3193534B, 0x7A61D074, 0x51A452E3, 0x40E490E5, 0x4106377, 0x404A1709, 0x1562727, 0xC0B89996, 0x4440781E, 0x78FA9053, 0x5D1E00E8, 0x1C3C75D0, 0xD581AB05,
-0x85C58A4, 0x44E490E4, 0xCD94CDCA, 0xB252E6D6, 0x1FC345AB, 0x40C5B905, 0x26693851, 0xEC3741D, 0x1B5869B, 0xA161510, 0xE061977B, 0x8A580510, 0xD960D554, 0xB53E091, 0x14B900FA, 0x3E094659,
-0x6090906, 0xE6B47C17, 0xECEC9840, 0xF9A405FA, 0x90F994FA, 0x2B750A5, 0x803B3D25, 0x14AE405F, 0x6F97E0FD, 0xCD34E38C, 0xDED0D4AF, 0x96D1C038, 0x51E78187, 0x93D7CACD, 0xD4D052A7, 0xE6558B4F,
-0xF6025766, 0xE54074B0, 0x6613252C, 0x257A75, 0x1B1266B5, 0xF956E0B6, 0x44D3E3B9, 0xC5C5E9E6, 0xEBD69599, 0x9F91D0F8, 0xB0A05253, 0x6E0F1761, 0x425FE480, 0xA5A051FF, 0xB1384DC7, 0x1CE31CD3,
-0xEBF6701B, 0x6B152998, 0x35A62510, 0xD140E5E6, 0x9070791D, 0x3CA6D1, 0xDAC98985, 0x90917E97, 0x19BCF91, 0xD0C7CBC7, 0xB5466B37, 0xB111D25B, 0x9A29978C, 0x3196C50, 0xCAC5C1FF, 0x4F4A4192,
-0xB14E708B, 0xD5E958D3, 0x73747E24, 0xEDE0D6A2, 0x1B91436E, 0x79252511, 0xCBC58C44, 0x7E64F890, 0x9F05B9F4, 0x1B55E0D0, 0x21D1E969, 0xF4558028, 0xF9E5C3C2, 0x1974325B, 0x6A94E0F0, 0xD101A5BD,
-0x1A17075, 0x5B2D78E1, 0x17194807, 0xF5C24B1D, 0xFA40E655, 0x7A095515, 0xE106F993, 0x565A1103, 0x5A54F6E0, 0xF5E0016E, 0xD6CBC5C1, 0xF940E696, 0xAE316D90, 0x6A146A00, 0x9B96E9E4, 0x6351D86F,
-0x5A466995, 0xD4460B8A, 0x2CC3744E, 0x99666696, 0x20257ABF, 0xA3F2955, 0xD5D0919A, 0x54444859, 0xFC5C606F, 0xA6653749, 0x306E696, 0x2528BD70, 0xA07BCE5, 0xF0A46662, 0xDC649440, 0x99C7874A,
-0x656CBEB, 0x5151A67B, 0x60DB4B05, 0xD1444107, 0x514B74, 0x165025A, 0x5B5A1101, 0x7101179A, 0xD09A070B, 0x50A096BF, 0x47A9521D, 0x4B2D7492, 0x3F0F1B53, 0x1941ABFF, 0x7B666111, 0xD3C345BA,
-0x1CA9D6, 0xE18359B9, 0x1590E6F8, 0xF4285902, 0xBF1B92D0, 0x1BE76D, 0x2A5582C3, 0x8979164B, 0x3C721B40, 0x33C0CF30, 0x2DD21EE1, 0xE9D30707, 0x192DF65, 0x1B92E7C5, 0x33333669, 0x1E0560F4,
-0xFB53034B, 0x966A6699, 0x6F9797A7, 0xEF469BDB, 0x1943F5F6, 0x1DA7C70A, 0x74741E9E, 0xF5709967, 0x520B74F1, 0x4741FBAB, 0xDA6712B9, 0xCBC6C1D0, 0x67E2D64, 0x5E03B625, 0xC088D9D5, 0xF89005FA,
-0x5A2516AA, 0xE5C7D9D6, 0x69FE5096, 0x435BE0D1, 0x7193DB8, 0x9CCC9967, 0x54613301, 0x461211BC, 0x730C6FAB, 0x6050A5B1, 0x7EB91141, 0x6A152F00, 0x69665AE5, 0xAE5780F8, 0x7D06F90, 0x2032D510,
-0x4E0746D1, 0xA69C33CC, 0x1A462616, 0x36C6C6C6, 0xFFA59D1F, 0x9B6D74F1, 0x1197907E, 0x656A9995, 0x91742E48, 0xD00538F5, 0x441542, 0x40D2469B, 0xFF5999, 0x15A9966A, 0x94841EF9, 0xA5651D1F,
-0xCC9C6633, 0x2F7D107E, 0x9B81411F, 0x9E59A669, 0x5E970007, 0x7E2E1F03, 0x29B5F244, 0x86C5D48, 0x548581B4, 0xF955CF44, 0xE35CB2CD, 0xA956B6D7, 0xE0E69125, 0x1969C59D, 0x550099EF, 0x68D99F24,
-0x5FA65010, 0x2B1256FD, 0xF8244147, 0x1D56470C, 0x2162A76, 0x62F16510, 0xAA556995, 0x540669, 0x14C29726, 0x4790FC0, 0x2CC19B44, 0x6CD7759D, 0xD7F64140, 0x425F81E4, 0x5034348D, 0x65095966,
-0x7261B0F5, 0xAF5681D1, 0x935A4051, 0x5262767B, 0x595EC2E8, 0xDB743847, 0x3C60A513, 0xC21F3991, 0xCB34C738, 0xD19AE995, 0xB9171781, 0x1107AB66, 0x5FC0D790, 0x2070F1F6, 0x6E5E1A09, 0xB5407E5E,
-0x505494EA, 0xE4703556, 0xF890999E, 0xA65BF595, 0x2919A7F, 0x49DD3880, 0x94EF960A, 0x183D1906, 0x1500F9F9, 0x9DEE4509, 0xF5FA6313, 0xE0526850, 0xBFE60005, 0xF16E685, 0xF90065A5, 0x916AF859,
-0xFF608454, 0xE6BF5094, 0x81D1E0FA, 0xA19FF91, 0x60D59CD9, 0x39524274, 0xD7994F4, 0x36295C8D, 0xE0503945, 0x2D85C545, 0xD1500658, 0x22172635, 0x338C5626, 0xDE812506, 0xDB57E8D0, 0xE9D955F6,
-0x9F6C94E6, 0x9636CC33, 0x2A1A3542, 0xF95D80F5, 0xD4266F42, 0x1C9E5F9, 0x74A1C30E, 0x6B16A050, 0xFB03506A, 0x4218B469, 0x94D1422D, 0x427D3531, 0xC25BAD40, 0x6692B292, 0x5A50E9A5, 0x717CD4C3,
-0x71938F59, 0x5147400A, 0x41BC6EB5, 0xD4A0D4E4, 0x1D0E5FA, 0xF6869F06, 0xA7E35252, 0xF4D1421B, 0xA4894DE7, 0x2D946B1A, 0xD0FFC684, 0x17C291E4, 0x7F525000, 0x15848139, 0x669669A5, 0xA7A76353,
-0xA996966A, 0x2FE155C0, 0x1E1E42D6, 0x4CB19976, 0x1103BE69, 0x57699082, 0x71624AF, 0xE1445A09, 0x969A4504, 0x5602955E, 0x5A1548E4, 0x5B074314, 0x74A6CE66, 0x9038152E, 0xD8C4554F, 0x3EBB0657,
-0xA8A65669, 0xB9B56010, 0x96B92D86, 0xE9D9CCA4, 0x33DE69A7, 0x5C8CD8E4, 0xF5D1401A, 0x4B59A46B, 0xCD258D07, 0xF3C3A475, 0x105DFC2E, 0xF7929140, 0xA4A450FE, 0x5B4598, 0x17137B75, 0x1B05F082,
-0xD62474D1, 0x673AB500, 0xA9460B5E, 0x47025AD1, 0x6E6695D9, 0x1262B6F6, 0x6AE65190, 0x66A69499, 0xD0993A76, 0x22321727, 0x55C0C56F, 0x6F7D1E43, 0x352F53E, 0x6AD6662F, 0x62713623, 0x948484F9,
-0x27161E04, 0xE996065B, 0xFED0411E, 0x8BC59A4B, 0x689E4B01, 0x40467E7E, 0x550E0F8, 0x1832D78D, 0x6CEC9580, 0x6A9E8F6E, 0x5340D4B1, 0x1449C6D, 0x22163530, 0xB4F6C3C6, 0xAD01DA95, 0x656F051E,
-0x8151167B, 0xA0C3966B, 0x40F85B15, 0xDD4B8D2A, 0x24BB667, 0x52E193F, 0x56A7019B, 0x12625504, 0x11A16297, 0x4B59066C, 0x59E990A0, 0xA45C0B00, 0x92C34B3F, 0xD06F6325, 0xC68D18B9, 0x74783C30,
-0xEA58C5CD, 0x142151F7, 0xDA649E04, 0xDA950602, 0x6E641510, 0xA7249144, 0xF46A1FF, 0xA950F611, 0x752E1FC, 0x7A460551, 0xB8E0D6D1, 0xE9D09151, 0xC745A104, 0x2072E687, 0x9F870105, 0x65A0F1FB,
-0xD0D500F9, 0x1A901FE1, 0x6F4E9401, 0x33322558, 0x52532732, 0xDB16B782, 0xBAA56046, 0xBCA19396, 0x411B39B7, 0xDC847184, 0x31C8B364, 0x6A66906E, 0x3F85D8F0, 0x36EB4393, 0xD518985, 0x34392F47,
-0x1FCF4E96, 0xF4A05E06, 0xBD5B102C, 0xFF056A65, 0x561BB601, 0xACC50A51, 0xA3733235, 0x7E1B53E2, 0x5251A0E5, 0xE078156F, 0x1F56E790, 0xA9965D59, 0xE0949C7D, 0xD87D560A, 0xD0649CED, 0x1065A47E,
-0x257CE4C3, 0xE2409597, 0x71D49220, 0x253929F, 0x47673ECA, 0x716B51F3, 0x4C1C48C5, 0xC7C1520B, 0x7B593060, 0x4F88491E, 0xA566A956, 0x7767A34, 0x65149A00, 0xB6D75A5F, 0x96A757A6, 0xA050617B,
-0xB007D141, 0x12A377B0, 0xC4E8D5F5, 0xE0C74B95, 0xC13C06F9, 0x7996213D, 0x9AC68F45, 0x65460B95, 0x99A7C292, 0x889C9C6C, 0xA4EDD440, 0xB0693DF, 0xFF50E490, 0x507A977, 0xE4147ED0, 0x80F19505,
-0xFCC5066E, 0x74B64319, 0x103A1EC6, 0xB288507E, 0x56A779A0, 0x7C9056AA, 0x381294FD, 0x5D194EC1, 0xF0C19B1F, 0xD60A1DE1, 0x94C0C4DF, 0x7E66824B, 0x69670667, 0x461520C7, 0x7466071F, 0x96FD1A00,
-0x10C4EA65, 0x93DE045, 0x6F01E2E5, 0xB196946, 0x504246AD, 0xB45E0F4, 0xABF1194, 0x84C94D5A, 0x111D27E, 0xF6951BBD, 0xE081166B, 0x56699965, 0xA1F16C54, 0x46107A64, 0x821C3491, 0x88D86E5B,
-0xE5D2C7CB, 0x5203432F, 0xE616332B, 0xF9854938, 0xD2736742, 0x671061F7, 0x9248BDB8, 0x544106E2, 0x6134280D, 0xC65E091D, 0x5599E860, 0xECC245B4, 0xFE4564E0, 0xD500F4F4, 0x59FDC0ED, 0x44530376,
-0xB152767E, 0x6A074351, 0xC565DE, 0xAD995844, 0x57F1D099, 0xB252D484, 0xD1984D8E, 0x5196A66, 0x2C59E167, 0xC0E9055F, 0xFD196F06, 0xF5F0C1C7, 0xFAD05152, 0x2126E0F5, 0xE9F42911, 0xC8339895,
-0x8D405BD, 0x50E94377, 0x443AF945, 0x5011293C, 0x76A297AD, 0x1EE440C5, 0x35DAD9A0, 0xA4419590, 0x165BA619, 0xF60B9A65, 0x1F89444A, 0x71B0255, 0x476DF0A6, 0x61D4A1A, 0x469F9DB0, 0x32355C8C,
-0x86091D46, 0xE795060B, 0x2296305C, 0x6E41F80, 0xF490E594, 0xF4E25699, 0xBF65AC50, 0x4A7E75DB, 0x1015F458, 0xB04ACD9E, 0xEE5478F4, 0x7F630C53, 0x4E49F5F0, 0xCDA3319C, 0xBE116D20, 0x4589D9E9,
-0x7435434B, 0xFDD96656, 0x5F681E4, 0x99AD1C50, 0x552A5401, 0x68F04549, 0x73B66D2, 0x1F86D101, 0xD041160B, 0xF906FFA5, 0x689676DE, 0x5F4780BD, 0xF0939AA5, 0x1F0B5F40, 0x501B3F, 0xA8C193B5,
-0x8C73CC33, 0xE24F7CD3, 0x9090A565, 0xA990D59A, 0xA1C14877, 0x946D8180, 0xF8343124, 0x14C1E915, 0x4919CBA5, 0xFF55D001, 0xF4E152A2, 0x46687EF0, 0xF05ABD48, 0xB7B37460, 0x1474C58, 0x94E490E8,
-0xB9E44183, 0x78D32DE1, 0xC8E4489F, 0x5BDAA45B, 0xB5610147, 0x5162A5F9, 0x5D6191C8, 0x4595C0A6, 0xA57DC996, 0x6D9D0D1E, 0xF58BC767, 0x1A0E5509, 0x409BD2D7, 0xFF5C135, 0x33B7579, 0xEF035600,
-0x779152B, 0x50B091B7, 0x363A3162, 0x491069DB, 0x9C4E0C5F, 0x945A1B12, 0x5490FF00, 0xB5A0B400, 0x1F82D64A, 0xB2C7426, 0x329CDA73, 0xA9995AA5, 0xF65876B4, 0x1E4E890E, 0x857AB576, 0xA52E054,
-0x1D11D88C, 0xA35895B0, 0x6F196890, 0xC7143E70, 0x6BE401FE, 0x111BA961, 0x66070AF5, 0x5F07130F, 0xA15061B6, 0x8C73CD32, 0x6C6481D6, 0x6AD5A6C1, 0x14A5872C, 0x46B90BE4, 0xE5586946, 0x3163C58C,
-0x1431B550, 0x91E94F09, 0x66F5C24B, 0xEE40461E, 0x97593992, 0x19E059F4, 0x94F92E05, 0x8CC445EA, 0x403D64A7, 0xB0FE5D8, 0x9C9B5C00, 0x7EDED9A4, 0x7CD393B5, 0xF1A50DA, 0x372B4077, 0xBF9046E7,
-0x56261E6D, 0x51232570, 0x40153CAB, 0x9B6F6712, 0x51C0160A, 0xB990052F, 0x3788B955, 0x555064A0, 0x6F0B5B05, 0x9776666B, 0xAA069B45, 0xAFF81440, 0x91E4DE1A, 0x80D5C9C6, 0x1E9E5DA4, 0x2B65B450,
-0x1213362B, 0xD766654A, 0x461346C, 0x197B2441, 0x5F9547EB, 0x15AF0506, 0x464C8D9D, 0x4021957A, 0xFE54E413, 0x13295035, 0x145E0D2, 0x4D4854E9, 0xBE50919B, 0xA76CF940, 0x91E1406E, 0x9A655A9A,
-0x3E65005A, 0x1C0B6666, 0x2611AC57, 0xBE7D1442, 0x311E3FC, 0xA4858185, 0x6E54D2A1, 0x9140A5DE, 0x844EC85, 0xF4A81540, 0x1159BF9, 0x2896D07D, 0xD5E690EA, 0x40A47D47, 0x6F672C16, 0xF1611217,
-0xE4096F65, 0x621D05EF, 0x9040979F, 0xC1C5CA46, 0x5B9606B5, 0x5101767A, 0x448C9568, 0xE4983D12, 0xCBC8B5E9, 0x1B15E402, 0xE1C60E15, 0x44E68419, 0x90D48DC, 0x7A9042BB, 0x151285D, 0x676B2613,
-0x121644B4, 0x920F52E3, 0x56DE09E, 0x6E6607A4, 0x5E560B0E, 0xC20B7616, 0xAF464146, 0x8C5C2835, 0x474BC2C6, 0xBC6419CA, 0x5C4353F0, 0x31392547, 0x504295AA, 0xCC593363, 0x1C0C6DBA, 0x4D71B7F,
-0xCC593633, 0xE71A250B, 0xE8CC599D, 0x894FA695, 0x4EC3513E, 0x39C0C645, 0x7B526034, 0xF91E2E06, 0x1A663699, 0x202599EF, 0xC1D1E195, 0x75E600FF, 0xD0D1ECDC, 0xD6DB6050, 0xE0E5016F, 0xF0910B15,
-0xD68C6917, 0x6F1A3D00, 0xF5D61BA6, 0x336D1CA5, 0x7888941D, 0x78D0912D, 0xE1D1C34C, 0x592E4C41, 0x1539F804, 0xFB019650, 0xD5E50609, 0xE8C553F9, 0x411BEBD, 0xE580D1DA, 0x1CACD693, 0xAF15BCE1,
-0x1104909C, 0x4B3B4686, 0xB3A31251, 0x50F5665, 0x1D4EC1D3, 0x2830D712, 0x401F0475, 0xC089D4DD, 0xB2425701, 0x40076F65, 0xA950F5A4, 0x9F9990BF, 0x70214431, 0x22753C31, 0xCC7369D9, 0xE9919061,
-0xF45A1F1, 0xBD0690D0, 0x79A6C180, 0x2FDD105A, 0x584296CA, 0x116F906E, 0x58484C5C, 0xDF9B10E6, 0x5184486D, 0xE9D89D90, 0x69B05ED2, 0xD9841D2B, 0xC2C6C5E6, 0xF88947CD, 0x90696469, 0xC5C0C6DF,
-0x677B0640, 0x4B479BC5, 0x83D1B811, 0x5B47E440, 0x1A419DEA, 0x3DD8D4A9, 0x8752B0F3, 0xD1D18189, 0xCCA632CC, 0x725CCB73, 0x6325CC33, 0x14464A25, 0x5410303F, 0xF8C12F85, 0x39D205E5, 0xFE850549,
-0xC68710B5, 0x471F7842, 0xD34A4C6C, 0x4742D188, 0xBFD50A44, 0x1987777C, 0x5B61C12, 0xB2530677, 0xD995A916, 0x75B496A0, 0x31E61E7, 0x53939DED, 0x51705B00, 0xBA5A4046, 0xFF14A4, 0x2599DCF8,
-0x7D47F02E, 0x9B959064, 0xDCD4E014, 0x38C22DD1, 0x65647C88, 0xD4606066, 0x3551E0D2, 0xD06157FE, 0x1131260B, 0x8B81E454, 0x1E1865FF, 0x1966A524, 0x4B4684D8, 0x3450F1B6, 0xE4FE41D1, 0x1D31F1E6,
-0xC47C1F4A, 0x94656AB, 0x45D101B, 0xB681B712, 0x9BD09215, 0x77924154, 0xFCECD4A0, 0xF0F1C72D, 0x74D0834F, 0xE4908117, 0x65E0365C, 0x955A09EF, 0x6E744349, 0xF4692B13, 0x34750F6E, 0x94D198A9,
-0x839C2DB2, 0xED929580, 0xEA609E65, 0x1A15F8D1, 0xFE00D6AD, 0xD5DC0141, 0xD90D3995, 0x6E6640ED, 0xA9B443CF, 0xE50681D9, 0x3F705659, 0x5F5E4844, 0x9A254A5B, 0x26618195, 0x8B945DAD, 0xDFD4E490,
-0x674352C0, 0x9967C1C0, 0x5990E995, 0xDEC544BA, 0xA7DA444, 0x16457E8, 0x142DFE04, 0xA4D5C084, 0x13172539, 0x84FC3590, 0xA9734748, 0xFE07F451, 0x42119AED, 0x8F087916, 0x13A65D1, 0x82856E75,
-0x3511B1B9, 0xA61BD018, 0x8619B893, 0x40BD89E5, 0x9A15F640, 0x4052464F, 0x9D87C1E0, 0x972C58F0, 0x84D8ED54, 0xE64D9C5D, 0xF421502F, 0xF24864, 0x59B8A154, 0x9A2596DF, 0x1441E6FA, 0x1BE46F90,
-0xA8915051, 0xA5B62611, 0xF582841D, 0x825362B7, 0x55F367E, 0x415263BD, 0x3E463930, 0x68754A06, 0x1117F280, 0x8B16B855, 0xEC75E1C4, 0xA0771117, 0x669DB850, 0x6D171238, 0x47167213, 0x90B21746,
-0xD92649C8, 0xCAC6C6F1, 0xE392C769, 0x60E7939B, 0x14130475, 0x56001A7D, 0xF657028F, 0x6F6819E, 0x29974C33, 0x49A57C5C, 0xD590E890, 0x6FF1116, 0x132B7665, 0xA0C60B56, 0x9844DF41, 0xA1B103E,
-0x44845EEF, 0xA6D2F5D5, 0x55FA4600, 0xBF611264, 0x6AD53610, 0xED594A11, 0xCFC15015, 0x96264247, 0xA4F1D3DA, 0x257EC166, 0xB8D6C114, 0xEA90CF4F, 0x6D5A804, 0x3F0695E8, 0x9C90BCE9, 0xAB4701D1,
-0x136357B5, 0x994D9E0E, 0x121652B2, 0x6D9FD261, 0x5CCC9733, 0x873B95E5, 0x905E9C80, 0xA051BA6F, 0x5F478589, 0x197AD19A, 0x50015DE9, 0x5BAE412F, 0x4111162B, 0x738F386, 0xE4835BAE, 0x5B55A2D0,
-0x5E4B038, 0xF9F91014, 0x8FCBD1E1, 0xD98B4743, 0xB9955DAF, 0xC5373DCD, 0xBD1C8114, 0x250532F1, 0x24504E0E, 0x62D7C50, 0xE8D44154, 0xA8944044, 0x9D669995, 0x530C6FFA, 0x65167BA7, 0xE641BE,
-0x811AB5D2, 0xD18A9D09, 0x6F064045, 0xBF902991, 0xC54F07FF, 0xF6962B11, 0xA4819FDB, 0x60F46A54, 0xE451815B, 0x7C197B, 0x9D44DA0C, 0x869640FF, 0x18C44D18, 0x40247F66, 0x5221F711, 0x510196FF,
-0x353261EF, 0x44490E0E, 0xF994E956, 0x926C45BF, 0x7D96855F, 0x84D4C98A, 0xE06BF456, 0xC21B1641, 0x4B5CB493, 0xDA90D366, 0xE8964D44, 0x197C75DA, 0xD6D46333, 0x465E78A1, 0x1942461E, 0xC1356656,
-0x58D631CC, 0xD773384C, 0x4A9940E5, 0xA0F97414, 0x53532327, 0x78107AB5, 0x3C091BA5, 0xA6469144, 0x44E45841, 0xC50D5842, 0x4A199678, 0x46365DF8, 0x5463E87, 0x8D2C1151, 0xFF5895, 0x2FD231C8,
-0x6C9CD2B3, 0xD74243B2, 0xC4409CDE, 0xB8E0411F, 0x26670F05, 0x5B6697A9, 0x55464A2B, 0x2E0F569A, 0x4660E25B, 0xFE500715, 0x9C683532, 0xCB0C5949, 0x806C6592, 0x679B0156, 0xFE005F15, 0xA540FE50,
-0x1F281510, 0x906D4F00, 0x57052C80, 0xA95BC0B5, 0x6C98C5CB, 0x44581E4E, 0xAD9D4846, 0x8352C7DB, 0xB21E50D9, 0xEB4607B7, 0x99AC9C77, 0x1509D9FC, 0x5460133E, 0x4244EE5A, 0xEF5A259A, 0x3439461B,
-0x6B940B05, 0x59E94484, 0x5062B45E, 0x859B9363, 0xFDE48194, 0x325362D2, 0xE460FB46, 0xD78B98E7, 0x64A3D346, 0xE5B06277, 0x9F655A41, 0x676B5302, 0xEA950B7D, 0xFD158A, 0x775362B4, 0x75C21938,
-0x861EB9D2, 0x589532CC, 0x1025190B, 0xD46B06FF, 0xB50E7C44, 0xCDD7A372, 0xB5B96100, 0xF2B491A1, 0x5E9960F4, 0xF1CC582E, 0x89C99575, 0x7AA1475F, 0x7B66C124, 0xD0D681EF, 0x44156E08, 0x8F1F1056,
-0x6E67D240, 0x9A4C4951, 0xD451E4A7, 0xEA941B13, 0x7E470070, 0x85422E7F, 0x5B9690FE, 0xDE06E626, 0xC8854A15, 0x5A05ED4D, 0x80F46E1B, 0x9011F302, 0x4493D2D8, 0xE50D9D68, 0xFB764400, 0x64D1816F,
-0x9965A616, 0xBF011811, 0x155B0252, 0x3D9DA411, 0x58D96D90, 0x5FAC14F, 0x34E09F47, 0x7470303D, 0x35316E5B, 0x84782D19, 0x6074B114, 0xDF85124F, 0x5B47B723, 0x91327673, 0x31A3C548, 0x6D04F9,
-0x65DA62CC, 0x779B6270, 0x9037699, 0xD59A061B, 0xD140F5D0, 0x546F04E5, 0x197A6193, 0x150F46FA, 0xE0592151, 0x54A9D0DB, 0xAA55D851, 0x50616F64, 0x35307994, 0x1198C72F, 0x472E79D1, 0xFF01981,
-0xF6195AFF, 0x58E66219, 0x69D3D67C, 0x3395C832, 0x5DA31DFF, 0xD66E7583, 0x802979D7, 0x1C419805, 0xA596916A, 0x6A464414, 0x64702547, 0xD31AB704, 0x91E6C21B, 0x41AF55, 0xC600DF64, 0x2D162960,
-0xD0D9AC64, 0x921C6378, 0xD0051C18, 0xCC26969C, 0x3C856899, 0x56A9D3A3, 0xC8CCD966, 0xB42552E2, 0xFF055B5B, 0x4AC7E641, 0xE747D0DD, 0x45A5F75D, 0xFD960205, 0xA60391FD, 0x5EAD8484, 0x13D0E563,
-0x11011BBF, 0xF4A4414, 0xCB5D0939, 0x1D6DE804, 0x2D6DF850, 0xB7E11431, 0x8B253410, 0x24D7A918, 0xA6591F6A, 0xFA0545, 0x10D3421A, 0x4E4CE453, 0x99C48C3D, 0x71D1C0F, 0xB4B94045, 0xE0815D05,
-0x9D854214, 0xF5F06136, 0x1A58FD15, 0x4742A650, 0x7E66464A, 0xCAC5D1F2, 0x99A91441, 0xB06115FB, 0xC0F43522, 0x9CE9CDC8, 0x59EA404E, 0x5B42A707, 0x16914BD, 0x872DF087, 0x4114AE9E, 0x3B1284FE,
-0x5E5E5808, 0x4680D66C, 0x364246C2, 0x65C2462E, 0x421C78A1, 0x1EB8D5EA, 0x28F24C55, 0x5021670B, 0x90BD7875, 0x71385CCA, 0xE7F07411, 0x1F11A366, 0x5FDFB852, 0x4C4DE05A, 0x9E0D9AD6, 0xE0953C84,
-0xE5CC0641, 0xE01BF406, 0x51E4072A, 0x4D66B8B5, 0x272DAD04, 0xF2C3986D, 0x6D680158, 0x464B0755, 0x550047FB, 0x74B41D1, 0xAF58676C, 0x7A655241, 0x64B9505B, 0x4D1D4D8D, 0xFFA5909B, 0x2F0754C1,
-0x4052256D, 0xB9D68609, 0x66615EB, 0x8A3D64D1, 0x2392D1E5, 0xF0A15ABD, 0xF7375B01, 0x699291A1, 0x607D0154, 0x6F19CDC6, 0x869DAC51, 0x11164726, 0xB0B50B1A, 0x9550E320, 0x257FF450, 0x3264669A,
-0xDF5D405B, 0x819855E7, 0x6B97C0A4, 0x88394945, 0xBCF14411, 0x6ED499, 0x570193E3, 0xFA346401, 0xF479095, 0xE6E6850B, 0xB2F1615, 0x95E2160, 0x144D8A3D, 0x3932D186, 0x5B64F801, 0x1F0746E7,
-0x1443EF98, 0xE8444E49, 0x4740EB50, 0xB9035B58, 0xD3D21B3E, 0xB8767699, 0x67D78366, 0xBD1187CB, 0x6C5C0C1C, 0x906D3E13, 0x9E1E6C49, 0x400598ED, 0x529106FD, 0xB4C10D2D, 0x740DC334, 0xE99458A0,
-0x94C8D572, 0xD7D18228, 0x13E566E, 0x1BC3D629, 0x63136172, 0x51A1B10, 0xF305F4E0, 0x679F6813, 0xD38356EA, 0xD669C856, 0xC5CE5A4, 0xC738CB74, 0x567DB480, 0xA65F9440, 0xE207176B, 0x9D5E4A88,
-0x1EFD6440, 0x1095738A, 0xF005646E, 0x19E1870, 0xEC481545, 0x2E598105, 0x55F7439F, 0xDBDB41A1, 0xE64F995, 0x6A9164A9, 0xA5191E04, 0xF4D1413C, 0x646EA056, 0xE3911263, 0x20117A65, 0xF9059304,
-0x48366754, 0x12166B67, 0x868A677, 0x46DB80C0, 0x1878D69A, 0x60D26DC1, 0x405EE804, 0x84D990BF, 0xDAD54606, 0xD93365CC, 0x93B2971B, 0x31269151, 0x5F0E58C2, 0x19B96390, 0xA1AD444, 0x90286D94,
-0xB1D10B7E, 0x40FAA451, 0x8276815E, 0x8669F400, 0xB5A61B12, 0x65FC9B8, 0x446AA55, 0xA11D8378, 0xAD9F4468, 0x21266E1E, 0x868544FC, 0x67520318, 0x858DD0EB, 0x86D4906F, 0x51D3A9A6, 0x9767857E,
-0xD6C99C68, 0x33CC2667, 0x660795F4, 0xD1F10155, 0x86D90D16, 0xE66A1603, 0xF5F86440, 0xE98E4504, 0x17424B9A, 0xB0839F59, 0xE70438F0, 0x432A7560, 0x92D89FC5, 0x37C42399, 0x11617570, 0x249605BE,
-0x101BE265, 0xEE905053, 0x4540DA59, 0x9729A4FF, 0xEE850607, 0x9FD15248, 0x50E5B323, 0x7579F946, 0x498459AF, 0x919C2C1E, 0x6AD78374, 0xB1647421, 0x9EE0B603, 0xA6D35E9, 0x96696C9E, 0x587A5003,
-0x49454A9, 0x6D6D9393, 0xC98677E9, 0x4095C2EF, 0x9C608505, 0x15940C2C, 0x5B60D2DB, 0x1C8151A9, 0x60F491D2, 0x2B670453, 0x4F1964BA, 0xB4430B51, 0xA9860454, 0xB0E41E46, 0x1406B7E3, 0x562701E4,
-0x20D1F6A1, 0x5352A966, 0x64E1D62F, 0x92C6ED07, 0xF882561B, 0x99195A04, 0xC934E3AC, 0xD9B91810, 0x474B04F4, 0x3D9A66, 0xCA9C10AF, 0xE1942916, 0xA125355, 0x32295C8C, 0x675A64E1, 0x9D906494,
-0x6C60EB1F, 0x7874C1BC, 0x1CB53038, 0x56D0F9D1, 0xB1EB954, 0x7C781540, 0x5323619E, 0x6FE055A9, 0xE7440D22, 0x7431A347, 0x6F1BE146, 0x1015BF00, 0x62381905, 0x7025B440, 0x176AF050, 0x5D270EF1,
-0x46AF4C18, 0x4B86D9ED, 0x8B177010, 0xFAD09606, 0xBFA15E50, 0x4701D72D, 0xA298A55E, 0x865CA950, 0x9B5B1227, 0xE0B9175, 0xF481F1FC, 0x7E5D0440, 0x1A697FE5, 0xDE84C5F0, 0xF9975303, 0x4CB46C54,
-0x3ED0D666, 0xE6B07065, 0xF0661263, 0x117C93A0, 0xD56DB4E1, 0x2E5E0E5D, 0x873DE11F, 0x5512413E, 0x5E9CE404, 0xB4C10B7F, 0xF990E956, 0x84C95E95, 0xB5E04B1F, 0x86815BA5, 0x7A5CB26C, 0x42F41F91,
-0xF4592E03, 0xF51F1300, 0x99D7810F, 0xB16051A7, 0x5B57B03E, 0xEE590B55, 0xC014FDD6, 0xF9C04505, 0x1A669669, 0x984C5F84, 0x1A6FD2E3, 0x1A075E5, 0xD80E450C, 0xD66942B6, 0xD050E31F, 0xBA605440,
-0x50E7856A, 0x406E53EF, 0xF01191B, 0xBF055243, 0x9B25E9E7, 0xF892A157, 0x2D598957, 0x85896B6, 0xEF44584B, 0x170667FB, 0xD1017F38, 0xCEC58687, 0x443C6C99, 0xB5B83D3, 0xFD05D91B, 0xA6D72DD3,
-0x6C98D9D0, 0xD66990E0, 0xDE850B0F, 0xE9964609, 0x8605B94B, 0x6DC0D196, 0xB194A5, 0x6F13424D, 0x9ED13215, 0x7064249F, 0x5751E0B0, 0x166F26D6, 0x559EC2F8, 0xE12C5C71, 0x59898D15, 0xBC015A05,
-0x90D18B0F, 0xB0E8649C, 0x170B98D0, 0x8181F491, 0xD990498C, 0x76312925, 0x93B53402, 0xCC1C4B54, 0x2F3F5006, 0x56EC4F10, 0x1AE501E4, 0x6F4D40DB, 0xA3525D69, 0x1329E491, 0x400AF995, 0x2D5C4806,
-0xB091667D, 0xCC6C1627, 0x425CD9EC, 0xF552931A, 0xA6D46580, 0xF4AE50E0, 0x1979909A, 0xF1750058, 0x3365CCD9, 0xBA166500, 0x86779739, 0x67F2611, 0x55109A0B, 0x51016A65, 0x1160BBF, 0x5A6C1401,
-0xC4DC9984, 0x33534244, 0x4057113A, 0xF1F431C1, 0xF8A44BE5, 0x1A976851, 0xB9B5C28F, 0xFE099454, 0xB065B8B5, 0xC0217707, 0xF3831BD1, 0x9757401B, 0x1D293580, 0x7A951200, 0x20615AF5, 0x6272757,
-0xE0FC2907, 0xD860B5C1, 0x985F89C, 0xC25C2C5F, 0x7F941C01, 0xA95A450A, 0x44D89A95, 0xCE1C0C58, 0x194184F9, 0x579BB371, 0x90111F40, 0x5F037075, 0xBB516996, 0x4EC9791, 0xCCEC9C44, 0x5D8404F8,
-0x48FE9D1, 0x4792E3E2, 0x6F35E381, 0x5F0350FB, 0xFF6C56, 0x6050A5F7, 0x589625C9, 0x8F79061D, 0x11D0421F, 0x9061F184, 0x504662BE, 0x8FC7D190, 0xB4404627, 0xEA1F1B03, 0xAF59D0EA, 0x79E50E04,
-0xCF80445D, 0x4191D38B, 0x8F51328, 0x6653E282, 0x8C5C3573, 0x90E05929, 0xC5910D2B, 0xAE479D46, 0x2F7C1610, 0x24B1637E, 0xCB001F55, 0x118384B5, 0xA3421710, 0xAA0725F4, 0xE9C0959A, 0xE5E74114,
-0x4A44CB25, 0x1763BD90, 0x1B095404, 0xCF09D095, 0x5B8344AC, 0xC5C0F9D1, 0xA7615441, 0xE990474B, 0x63536723, 0x1E305BA6, 0xE56B066B, 0xCA817916, 0x9F798578, 0xD7692E06, 0x56F01481, 0x37215100,
-0x17111401, 0xF890F955, 0xA68D68D6, 0xD64A06F0, 0x9F65D9D2, 0xBE90095F, 0x9967072F, 0xE01C947B, 0x12235709, 0x589DFD, 0xBB154A41, 0x2D5DC01F, 0xC58B5C08, 0xCB64DC4C, 0x9F9393AC, 0x7303467E,
-0x599EC984, 0x46E111FC, 0x7A679404, 0xA90647FB, 0x40EE949A, 0x77076E08, 0x10736423, 0x53D90AB, 0x1F510BB, 0x85CC253A, 0xAA905666, 0x45EC0B4, 0x835121F1, 0x2E5A4247, 0x1D8664A0, 0x51442CE0,
-0xDD88451E, 0xE85C0113, 0xFA409601, 0xA4211627, 0x459F50BA, 0x984C458A, 0xE65A60D7, 0x958143B2, 0xD3835BFA, 0xF727F110, 0x4F074354, 0x11113D3D, 0x1558312B, 0xFF08E6D6, 0x89BC5910, 0x99F99287,
-0x40D4183F, 0xDA66A550, 0xA45CE279, 0x7F97A251, 0xB1B025F, 0x70772265, 0xF0E21E16, 0xE79959B5, 0x496669C6, 0xECD00716, 0x5619ECE1, 0x6E5C11F, 0x1101FFF8, 0x55A01DCB, 0xA5053970, 0x3D448855,
-0x9F92E540, 0x35B8A4D0, 0x84119CA7, 0x5429406F, 0x90E05B56, 0x3752E1D8, 0xE1B42D87, 0x9454B0F1, 0xDD382505, 0x1904ED6F, 0xD94D0C58, 0x7C52031F, 0x14B06706, 0x79928799, 0x40149BC7, 0xE9DCD6D0,
-0x104F4D1, 0xD9663448, 0xBD85D003, 0x358CCDA3, 0x66F5412B, 0x6E65066A, 0x6E19B161, 0x55D1AA50, 0x785C9D30, 0x162530BE, 0x7AE64548, 0xA51B9B0E, 0xF9A56400, 0x32CE35DB, 0x17285370, 0xB0117B76,
-0x7CE04741, 0x9D0C51A4, 0xBF004D55, 0xAAD5AC11, 0x6599CB41, 0xFBD09444, 0x936291E5, 0x2D5EA056, 0x1129656A, 0x7FC09506, 0xD44149C8, 0xBA750352, 0xD2431943, 0x7967E182, 0xB813C497, 0x24219465,
-0xC43C9169, 0x6FD81483, 0x6152E3C, 0x59947B6, 0x27F90D12, 0x54046D2A, 0xD521309B, 0xE6653921, 0x9164F4F1, 0x1521FB4A, 0x61C54C38, 0x416484BF, 0x2776B11F, 0x59A9D884, 0xED806B54, 0x1BB0D59B,
-0x4E8D91A1, 0x782FD131, 0xC05A0377, 0x7A121126, 0xDFC5D0E0, 0xEF859E05, 0x7BB60515, 0x5DD26D89, 0x2E291157, 0x9F2F3530, 0xC08156A9, 0x6D91CDC5, 0xF1548117, 0xB52517FE, 0x2053E947, 0xAB5785C5,
-0x479A91E0, 0x9F90FE01, 0x312484DE, 0x441BC1A5, 0x66B501F5, 0xC4617A5B, 0x916BB0F4, 0x20657C7A, 0xE25390FF, 0x1F3861B4, 0x550586AF, 0x6B8599D4, 0x9F4294EC, 0xF4D48147, 0xD0965233, 0x1E05CFED,
-0x59176903, 0x2D2976C, 0xCD33338C, 0x1441E4AD, 0x4196C088, 0xF6079403, 0x969DC030, 0x64990D29, 0x2453F372, 0x8658909B, 0x458046FB, 0x11A185C4, 0x58FC1144, 0x31E641A, 0x8E47709E, 0xF1611277,
-0x61F907A, 0xB7100F5C, 0x5A170B19, 0x160557E, 0x55B39D0, 0xC0C556EF, 0xBBF50299, 0xD1D23E12, 0x69588543, 0x4546890, 0x90FA4115, 0x11C5ED8, 0x68B74789, 0xC06F0117, 0x172790AD, 0x606DAC54,
-0x8D488154, 0x8444EC98, 0x9A6592D3, 0xE5E15040, 0xDB031762, 0x2F099450, 0x404F0F5A, 0x1D09E9F4, 0x56045E1F, 0xF9C99F00, 0x136C6599, 0xB0C36E91, 0x1C2835B0, 0x60BE6B5, 0x462FB4D1, 0xCB19B6D7,
-0x6C48C4D9, 0xD0D19B69, 0xA5E544FC, 0xE4851E31, 0x41E464B8, 0x59C6F9D4, 0xF0A6527E, 0x5333D1E0, 0x4862F606, 0x72370631, 0xC2B490C5, 0x4643DE5C, 0x54F9C489, 0xFC526C40, 0xB0A56895, 0x45C1C134,
-0x768F1981, 0x6E295005, 0x950B6964, 0x4B4CD5E9, 0xEB131245, 0x447F844, 0x131B1D76, 0x99981A7, 0x500B1F0A, 0x91011B1B, 0x64E64709, 0xA45C8145, 0xB53001D1, 0xDB462885, 0xBE06D015, 0x4858541B,
-0x6C86C3B5, 0x70A1967B, 0x969B0414, 0xEF485C04, 0x7FE151C1, 0xEBE7F444, 0x5341990B, 0x71D58060, 0xF5A61E10, 0x79F2C53, 0xE5E41F00, 0xBFF4505, 0xE01BB4E9, 0xBFE3905D, 0xDB7664D0, 0x956A065B,
-0x12894E1, 0xBF14B401, 0x30F7A97, 0xA85C97E0, 0xF40643E9, 0xF8506706, 0x9B9B06AB, 0x5AA05995, 0x45448E3D, 0xE1D1907F, 0x24F4C22C, 0x82C2959A, 0xBF065243, 0x8785812D, 0x9A94A916, 0x315B2C60,
-0x523291D9, 0xF2991609, 0x2830579C, 0x999973CC, 0x4B14C098, 0xC1C5053, 0xD1C1C68A, 0x64B67F3, 0xD09B801D, 0x6A7C1044, 0x48580954, 0x859F095A, 0x46533620, 0x592C852D, 0xA5419141, 0x4D16A1D0,
-0xB0B153B, 0xE1944E4A, 0x21371633, 0xCEC9C4E4, 0xA5D70E08, 0x4E4C4DD, 0x591CAC04, 0x5B021DBC, 0x9967CC33, 0x81C0E5FD, 0xF651C104, 0x966760C9, 0x95E1303C, 0xEC5C6440, 0x50BF9F4, 0x3561C31E,
-0x45A096AD, 0xFF92500B, 0xF6598904, 0x19F9C50B, 0x60C64D18, 0xD640584F, 0x2194E1F3, 0xF031163D, 0x101B56E0, 0xDE5921DA, 0xBF01FD1, 0x706FD94, 0x34CB30CE, 0xB857061C, 0x5225B609, 0xC43C3135,
-0xC03B9350, 0xF001676F, 0xF0C18767, 0x6611391B, 0x4F89A46, 0x1F42441A, 0x649F0F46, 0xB5E0015E, 0x7966830E, 0x9740F4F7, 0xC8CC4451, 0x59BD404B, 0xCC9435E1, 0x3B15B2A0, 0x123C1607, 0x59468D90,
-0x40E0D749, 0x14C1F46E, 0x448BD6A9, 0x659AF556, 0x50E7435B, 0xAED2933C, 0x56051308, 0xCE146E50, 0x1C904F10, 0xEC485E0D, 0x169A936C, 0x51633605, 0xDB57A830, 0x62957AB, 0xF4F14448, 0x45CAD5D,
-0x695CC006, 0x104606B9, 0x81D1D86D, 0x8649F050, 0x20D1DB56, 0xE5E53CC0, 0xCB470256, 0xC629E494, 0x49385F8F, 0x65056E31, 0x9C86F095, 0xC8CC9865, 0x869D8118, 0xD1021F11, 0x87DAD1E2, 0xAF04D500,
-0x4518464, 0xB857B085, 0x4505E834, 0xB1E25F04, 0xD8DD9084, 0x35978C33, 0x11A64257, 0x991A1E41, 0x66B06752, 0xBAF41443, 0xBD4A4544, 0xD6753B66, 0xCAC6C639, 0x154480FD, 0x99D19BC, 0x546EE442,
-0xD0C0193E, 0x7491E21F, 0x24B0B3C7, 0xF981D501, 0x74438B58, 0x54C47391, 0x9EDD1180, 0xE697874F, 0x2E41F8E5, 0x468FDD9, 0x4F1E439C, 0x5542EB81, 0xA554FAE0, 0x1E43E2B0, 0x606D5898, 0x6587C4E8,
-0x6B86C511, 0x6050A6D3, 0x1C523485, 0xF2530396, 0x4A2D5884, 0x6FD09193, 0x29978F84, 0xFE490999, 0x9189675E, 0x99277401, 0xDD6F2494, 0x8CD39344, 0xB5783D9, 0xC859EE, 0xAC701503, 0x8FC1D7E1,
-0xA0854317, 0xD09E8444, 0x4510F2BD, 0xED484CD4, 0xDE5D0C1C, 0xC0854367, 0xD1642134, 0x503876E, 0x7C046A5E, 0xE5644035, 0x3D043173, 0x5DE8991, 0xA603D6D0, 0x70644B1B, 0x41C3F55, 0x799EC50,
-0x5C1D005A, 0xB54027BB, 0x501703FB, 0xCE5E2D84, 0xC248776B, 0x34C31DB1, 0xD08146F6, 0x6194D488, 0x902D95DF, 0x56239699, 0x79566996, 0x46366797, 0x2075B449, 0xE619904B, 0xF1FC491D, 0x4026925F,
-0x85CE994, 0xC62D651B, 0xE0F50B4, 0x60976F55, 0x5B7706A6, 0x49471F41, 0xF6D91352, 0x551A062F, 0x4145CFCA, 0x55B14234, 0xABF12531, 0xB915F0F4, 0x8AC7E275, 0x4679C12F, 0xE21353EC, 0x693602A5,
-0x4BC256AB, 0x463D64A6, 0x5E7C6B8, 0x143983F0, 0x3646871A, 0x601D1FE, 0x4D4601FC, 0xB489D0DF, 0xF7814046, 0xE5A50BA7, 0xB34A116, 0x707C4151, 0x804466BB, 0xCF450C5C, 0x1329B9F4, 0xFE400655,
-0xF4A1431B, 0x51D3202D, 0x74B0A444, 0xA6F1612, 0x6C584C8C, 0xC014F665, 0xD3982957, 0x36C136D1, 0x5C45CCD0, 0x9F459882, 0x91A753A2, 0xA9752C4C, 0x421B4297, 0x776A2313, 0x35397906, 0x48065762,
-0x1129B440, 0xAF410B5D, 0x7F9582D2, 0x4B51E650, 0x891D0D77, 0xE59909E5, 0x705D234B, 0xC3DA9055, 0xF1B11501, 0x44BC94D9, 0x1FA55C48, 0x2A422657, 0xC41091A1, 0x18970E74, 0x7699099, 0x10616AD7,
-0x1AE19F10, 0x475A6C31, 0xA27257E9, 0xC484582C, 0xD699665A, 0x17C94C18, 0xE1569B, 0x11855ABF, 0xA580D607, 0x64C18B7E, 0x7030A605, 0x2B3660D0, 0x165BE103, 0x809E1867, 0x47407FD7, 0x34E4858B,
-0x90ED740B, 0x1B43C2F5, 0xF451527C, 0x5723E1C5, 0xE1D1065B, 0xA51A9DF4, 0x7F099A85, 0x4157030B, 0x1D9F60B8, 0xC11491FD, 0xFE850A55, 0xB990431F, 0xA0C78D16, 0x6F5A4D40, 0xFE4411E, 0x34426B56,
-0x5014FE0F, 0xBD5DC51A, 0xC63637C3, 0xE1B459B2, 0xFA871B05, 0x4544D0AD, 0xE5E5093D, 0x3306C5DD, 0x47C33484, 0x6F1E146, 0xE4974F1, 0xDD48050F, 0xAB151E12, 0x5169FC92, 0x5055B27A, 0xD142AD5C,
-0x4B16B0F4, 0x312BC955, 0x75B10FC, 0x4A9E0458, 0xA65D34CE, 0x90D366CF, 0x10099D29, 0x8B358419, 0x87D2B5DA, 0x31162EF5, 0xF61989C5, 0x1F4341F8, 0xBC4C6D9C, 0x11A759, 0x9553A21, 0xCFC41530,
-0x1F05B610, 0x5B2F64C0, 0x117F1124, 0xF3D24D9, 0x44447874, 0x74351A06, 0x64C48979, 0x63C185B, 0x91F094A4, 0x4E1D1C48, 0x969103AE, 0x9CFC6067, 0x2FC94045, 0xED3844C4, 0x676C935B, 0x6E2091B5,
-0x2D9744EC, 0x6A111227, 0x78DB90E5, 0x8045587E, 0xC4E4591E, 0x479BE1E0, 0x5B667499, 0x50E0493E, 0xFB54819C, 0x431491F8, 0xD3EF50E0, 0x7363926, 0x50FF6440, 0x44DC6C9D, 0xF6D9965A, 0x548499,
-0x150A1403, 0xC7324CC7, 0xAFF0474B, 0xB479966E, 0xAD9E5451, 0xF9913C41, 0x1F09B46, 0x3E35461E, 0xA0C66C10, 0xA4D1D103, 0xA05470B4, 0x6F1A3531, 0x4047B2F5, 0x11472CE0, 0x3AE41631, 0xFD36A905,
-0xF4436F91, 0x4A419766, 0x50699E67, 0x5408D93D, 0x112711AF, 0xF500642F, 0x160BD66A, 0x11B0BFE, 0xC2C55CA1, 0x31CF859, 0xC6C95401, 0xD9C0E5C1, 0xE46056AE, 0xF5E30710, 0xA5FF0150, 0x4454203F,
-0xB6660959, 0x2522776, 0x2D586970, 0x7370757, 0x60953317, 0x81E7072D, 0x68149E6, 0x653834, 0x363D9D81, 0xB429113F, 0x566C0258, 0x51F92F00, 0xE19C5812, 0x11FB150, 0x59A65200, 0x2775E0F4,
-0x106752A3, 0x7574326, 0x5CC34739, 0xDAD54805, 0x5E936BC7, 0xCFCAC5C9, 0x2FC42555, 0xD06994C0, 0x5CAC7400, 0x5D84D074, 0x6D668604, 0xE50B19BD, 0xE7969F55, 0xFF925005, 0x65B266B, 0x4743035E,
-0x972C44C7, 0x5AB47260, 0x77123580, 0xD92DB404, 0x451BB8D1, 0xB94B4447, 0x9B955210, 0xA253815, 0x79AC5184, 0xE3D10C5C, 0x452410FB, 0xDFD3A148, 0xE0336E46, 0x4E287599, 0x12B750A9, 0xDAC0DE47,
-0xC18459A9, 0xC764CD30, 0x2334675E, 0xF6490DB8, 0x49172679, 0x659B6994, 0x7A912691, 0xB2B76074, 0xB9E58704, 0xCC33D9A9, 0x207F6A7, 0xC405F801, 0x560297CF, 0x48140E5F, 0xC44E676A, 0xB191DED,
-0x11035F60, 0x5B1F60B4, 0xA4EB1443, 0x5A6C06E6, 0x1915AE9, 0x33CD2696, 0x879F4444, 0xD442170B, 0xC9C9D9D6, 0xA1D4C124, 0xC40366B7, 0x22497E9, 0xCD18D7F9, 0x99FD111A, 0x9F6B53A1, 0xE051ABFC,
-0xBC10071D, 0xA566A759, 0x6DBC5E5, 0x4A9DA851, 0x2266B15, 0x123D6E9B, 0x66E05449, 0xD10D3870, 0x1706E462, 0xAF0B9B51, 0xE0109D6C, 0xF144861B, 0x813ED01B, 0xB6496C10, 0x1ED00768, 0x60747C10,
-0x46494376, 0x5E4C10B0, 0x573D5906, 0x5A02E5FE, 0xB3E005F2, 0xC05C4F45, 0xDBD58982, 0x6D13171F, 0x9741FA00, 0x61DB852, 0x8D46D299, 0x47421E4D, 0x74A94E6, 0x4DA31CD3, 0x681602FF, 0xC67962D7,
-0x1560BBA, 0xF8849D85, 0x7DD6D5B8, 0x5B6B4640, 0x441A349F, 0x61B3601F, 0xD550E2D3, 0xCF18111B, 0x97673333, 0xC59909E4, 0x57DAE840, 0xBB910E5, 0x144484F9, 0xA85C2511, 0x4092D5EA, 0x89910391,
-0x7D385500, 0x3D1D2176, 0x802FD41B, 0xA5D94890, 0x656E9099, 0xE66906E6, 0x4331A2D5, 0x4478E25B, 0xE78C41D, 0x3565D979, 0x4147133B, 0x89C6C64A, 0x45C9CDCF, 0x532315FA, 0x3E5A0D04, 0xB111DBFC,
-0x63162FF7, 0x9E0B9CD0, 0xA758E46B, 0xA097421F, 0xAE544262, 0xA59A5DDD, 0x3732261D, 0x71736790, 0x8895A173, 0x687470B1, 0x57E564FE, 0x6A765404, 0xF5E21482, 0x667150E1, 0xC0A764D9, 0x89950C14,
-0x3756912B, 0x3913499, 0x6FB553C3, 0x29920995, 0x750EC738, 0xC35C6992, 0xAA55E404, 0xCB4B0753, 0x20396156, 0xDB04F4C1, 0x7876825F, 0xD562319A, 0x592D121B, 0xF9360956, 0x47811B47, 0xD9BD5884,
-0x170A35E, 0x844EE59A, 0x8684EDDA, 0x405F1000, 0x813641A7, 0x176681A2, 0x7DBE6440, 0x87C6A54, 0xC485D905, 0x1AB0811D, 0xDBA59291, 0xFA52817C, 0x3981E5F0, 0x5109FEE5, 0xC1284557, 0x573B2703,
-0x30640659, 0xF1D3E997, 0x906253B7, 0x9076C3FD, 0xCA34D35C, 0xF481491C, 0x5976A4C0, 0x3FF1144, 0x26170649, 0x196B9065, 0x90693D09, 0xF0211A17, 0xAF016DD4, 0x4E449C48, 0x6F9A84F4, 0xB4449818,
-0x606DB144, 0x916B97A6, 0xEBD0FD19, 0xAA08D541, 0xD71BE505, 0x1E6507AF, 0x3E4B1361, 0xF391619, 0x4264BC50, 0x60563321, 0x4A370617, 0x436452E7, 0xBE107604, 0xDDA9C18E, 0xBF5B0371, 0x906F1107,
-0x55C5C83C, 0x31364782, 0x68DD9F1, 0xF99607E6, 0x2996367E, 0x5AF4458, 0x96582D30, 0xC4E4D98A, 0xE68719BE, 0x20C56C64, 0x5A550246, 0xB759B079, 0x64439BFB, 0xB1240D2E, 0x271350B2, 0x4314AF56,
-0xA9998D72, 0x5E8D1481, 0xB65D7253, 0x669392B8, 0xF0919B6C, 0x9F9790DD, 0x60979838, 0x3D625168, 0x12196D58, 0x59E9448A, 0x30547C92, 0x8E317590, 0x7B954843, 0x323D4907, 0x5A4D5308, 0xD10490DB,
-0x92CE4511, 0xB9353125, 0xA91F4050, 0xDC485D08, 0x2F11742, 0xB9B4090F, 0xD09891FF, 0x9503936, 0x80E3505F, 0xED99405F, 0xEA9058DE, 0x55019FC0, 0x9A1F412C, 0x471660DA, 0xF4D0C505, 0x4926666D,
-0x4191C3EF, 0x1A0526F5, 0x55F889C7, 0xD33CC738, 0x7A66C601, 0xF701C662, 0x6E953906, 0x6E6559AD, 0x1626C57A, 0xE0075266, 0x67069A5B, 0xA5F70401, 0x47786090, 0x8111C53B, 0xB4640B1A, 0x269D5DAE,
-0x50C4A474, 0x2A94987D, 0x44C05226, 0x6114423F, 0xB8474F1D, 0x8E0E05D8, 0x87B40FD2, 0x1F4392FD, 0x170E995, 0x1563DE0, 0x6F9AC5CA, 0x752A0D1D, 0xFF0A44D0, 0x7FD0D366, 0x51A62E05, 0xD0D5E8DE,
-0x56113F30, 0xE4056D87, 0xAB3B5100, 0x46539C6, 0x7F676290, 0xB851130D, 0xF5E09183, 0x64AC5D2D, 0x55F9404B, 0xE256F977, 0x70617760, 0xEE48E450, 0xDD5E35C8, 0x94D400F8, 0x446A0667, 0xEA935184,
-0x464B07F8, 0x8C74B6D4, 0x8D9D59A5, 0xCEC6ADD5, 0x2D0956A3, 0xA5BA5103, 0x31353123, 0x55FF4140, 0xA2440F15, 0x89266792, 0x44E1E154, 0xE5B41338, 0x2FCE9901, 0x95E6811B, 0x475C871, 0xAE9E4A44,
-0x5E0D141, 0xE702D66B, 0x474707E2, 0x6486CBC5, 0x7476016E, 0xB1D29B56, 0x7A34C605, 0x51427906, 0xE7967F97, 0x65D29C90, 0x55081AD, 0xD6E64045, 0x67134368, 0x9101E6DD, 0x68957841, 0x3D3D5909,
-0xE0525BAF, 0x134A4D3C, 0xC1B11909, 0x16493B76, 0x6B96D0A0, 0xBF0E79D6, 0x2C7096F9, 0x90FC04D6, 0x1742E472, 0x531CA1AC, 0xDAD08646, 0x9066669, 0xA7588D86, 0x913C54C2, 0x742A4514, 0x17111ABC,
-0xDDD7EDE5, 0x6699C498, 0x75313135, 0xE4995F64, 0x2582667E, 0x9C5C4C48, 0x1D0D10D9, 0x5B4C164, 0x46E616CB, 0xD18444BF, 0xE1D4695C, 0x40095E99, 0xECE02955, 0xFF40A411, 0x54D2421B, 0xE1C7DB05,
-0x80E5095D, 0x6745E907, 0xA6197007, 0x760674F9, 0x1522170A, 0x65181FF, 0xE0FDA45, 0x521C7867, 0x6B91D382, 0x657A83C1, 0x73C98C35, 0xB3CC31D6, 0x164B257B, 0x6F9605F9, 0x52F49EB5, 0x87592907,
-0x1B10D6F8, 0x949FD54, 0x910315BA, 0x40F09B47, 0xB761C144, 0x6D039D5, 0x59B113F, 0xE5C6D9, 0x176990FB, 0x90F8A465, 0x6DB8D144, 0x4552E392, 0xC94396B9, 0x36315A97, 0x7E874215, 0x40116F6F,
-0xDF193401, 0x70689317, 0x7E021740, 0xD5C044BD, 0xF1636E94, 0x5B0D34A1, 0x1027F689, 0x44A45590, 0xD443035F, 0xD7A4418C, 0xD949F480, 0x84C1E154, 0x6090966E, 0x40B56C1E, 0x9669C540, 0xA6305505,
-0x1667E244, 0xC245885D, 0xD504A5E, 0xA4F0C549, 0x358472A8, 0xC414BF20, 0x43369729, 0xB1112711, 0x78644352, 0x811CB856, 0x6F9792F0, 0x1E1E4195, 0x62C58C14, 0x2619D3A2, 0x998056BD, 0x653F3410,
-0x919184E4, 0x64906D6C, 0xE3D7996, 0x47660563, 0x50A91F03, 0x49F8C5CB, 0xFE15235E, 0x9783451, 0x716257B7, 0x1AB5351, 0x1710DC84, 0x3FA01D5A, 0x9844AD64, 0xE4D901BC, 0xEE546352, 0xF9547987,
-0xF6834B47, 0xB651F6D, 0xE09C9045, 0xE491424E, 0x94F9050B, 0x7DA6091D, 0xB5C14D74, 0x123A699D, 0x1F413CC3, 0x9F434158, 0x55D8C8C4, 0x51106FF, 0xD36CB1C, 0x1541FCE1, 0xC3344ED3, 0x3E29D905,
-0x150A6579, 0x502475A, 0x454B0F04, 0x65902722, 0x9EA055, 0x26568CC5, 0x7EB1443, 0x42186F96, 0x132357A9, 0x74091F13, 0xEE55480F, 0x7A0B4A45, 0xF01A8637, 0x937D68D5, 0xE2611115, 0x8A853663,
-0x60D295AB, 0x1946461A, 0xBB5190A1, 0x6D514218, 0x7C6482CF, 0x8759ACC8, 0x7E86C805, 0x42D3755F, 0xF4C10529, 0x471F1400, 0x5394238C, 0x112D54A8, 0x55E450F8, 0xDC2191BA, 0xA5F93011, 0x1A2159F5,
-0x4051A297, 0x66066999, 0xA644C058, 0x54EC9148, 0xFAFB0146, 0x1150FCEC, 0x4141D171, 0xF09F0444, 0x488E5E0E, 0x3D190F10, 0x406E405F, 0xFE9904AD, 0x101F5C6F, 0x454B093B, 0x40946C1F, 0xF0B966D,
-0xC16464DB, 0x91E2D6F9, 0x97789074, 0x537C5C40, 0x44A533C3, 0x74102B33, 0x40D425C1, 0xD748C936, 0x786619C9, 0x40917985, 0x66594C1C, 0x2F05D2D2, 0x8C711B4, 0xF9602757, 0x1013854A, 0x7D6296D0,
-0x3D99B8F0, 0x495D3243, 0x916864BE, 0x4E43B947, 0xE756A110, 0xA8D09D7D, 0xBF625095, 0xC1D48F87, 0x8BD47099, 0xDB631315, 0xE54139A0, 0xE4DC9C63, 0x846C2544, 0x4F0E04D1, 0xE0B15B26, 0x3249499D,
-
-}
diff --git a/transcoder/basisu_global_selector_palette.h b/transcoder/basisu_global_selector_palette.h
deleted file mode 100644
index 8bedf94..0000000
--- a/transcoder/basisu_global_selector_palette.h
+++ /dev/null
@@ -1,675 +0,0 @@
-// basisu_global_selector_palette.h
-// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
-// 
-// TODO: NONE of this is used in .basis/.ktx2 files. It will be deleted soon.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-#pragma once
-#include "basisu_transcoder_internal.h"
-#include <algorithm>
-
-namespace basist
-{
-	class etc1_global_palette_entry_modifier
-	{
-	public:
-		enum { cTotalBits = 15, cTotalValues = 1 << cTotalBits };
-
-		etc1_global_palette_entry_modifier(uint32_t index = 0)
-		{
-#ifdef _DEBUG
-			static bool s_tested;
-			if (!s_tested)
-			{
-				s_tested = true;
-				for (uint32_t i = 0; i < cTotalValues; i++)
-				{
-					etc1_global_palette_entry_modifier m(i);
-					etc1_global_palette_entry_modifier n = m;
-
-					assert(n.get_index() == i);
-				}
-			}
-#endif
-
-			set_index(index);
-		}
-
-		void set_index(uint32_t index)
-		{
-			assert(index < cTotalValues);
-			m_rot = index & 3;
-			m_flip = (index >> 2) & 1;
-			m_inv = (index >> 3) & 1;
-			m_contrast = (index >> 4) & 3;
-			m_shift = (index >> 6) & 1;
-			m_median = (index >> 7) & 1;
-			m_div = (index >> 8) & 1;
-			m_rand = (index >> 9) & 1;
-			m_dilate = (index >> 10) & 1;
-			m_shift_x = (index >> 11) & 1;
-			m_shift_y = (index >> 12) & 1;
-			m_erode = (index >> 13) & 1;
-			m_high_pass = (index >> 14) & 1;
-		}
-
-		uint32_t get_index() const
-		{
-			return m_rot | (m_flip << 2) | (m_inv << 3) | (m_contrast << 4) | (m_shift << 6) | (m_median << 7) | (m_div << 8) | (m_rand << 9) | (m_dilate << 10) | (m_shift_x << 11) | (m_shift_y << 12) | (m_erode << 13) | (m_high_pass << 14);
-		}
-
-		void clear()
-		{
-			basisu::clear_obj(*this);
-		}
-
-		uint8_t m_contrast;
-		bool m_rand;
-		bool m_median;
-		bool m_div;
-		bool m_shift;
-		bool m_inv;
-		bool m_flip;
-		bool m_dilate;
-		bool m_shift_x;
-		bool m_shift_y;
-		bool m_erode;
-		bool m_high_pass;
-		uint8_t m_rot;
-	};
-
-	enum modifier_types
-	{
-		cModifierContrast,
-		cModifierRand,
-		cModifierMedian,
-		cModifierDiv,
-		cModifierShift,
-		cModifierInv,
-		cModifierFlippedAndRotated,
-		cModifierDilate,
-		cModifierShiftX,
-		cModifierShiftY,
-		cModifierErode,
-		cModifierHighPass,
-		cTotalModifiers
-	};
-
-#define ETC1_GLOBAL_SELECTOR_CODEBOOK_MAX_MOD_BITS (etc1_global_palette_entry_modifier::cTotalBits)
-
-	struct etc1_selector_palette_entry
-	{
-		etc1_selector_palette_entry()
-		{
-			clear();
-		}
-
-		void clear()
-		{
-			basisu::clear_obj(*this);
-		}
-
-		uint8_t operator[] (uint32_t i) const { assert(i < 16); return m_selectors[i]; }
-		uint8_t&operator[] (uint32_t i) { assert(i < 16); return m_selectors[i]; }
-
-		void set_uint32(uint32_t v)
-		{
-			for (uint32_t byte_index = 0; byte_index < 4; byte_index++)
-			{
-				uint32_t b = (v >> (byte_index * 8)) & 0xFF;
-
-				m_selectors[byte_index * 4 + 0] = b & 3;
-				m_selectors[byte_index * 4 + 1] = (b >> 2) & 3;
-				m_selectors[byte_index * 4 + 2] = (b >> 4) & 3;
-				m_selectors[byte_index * 4 + 3] = (b >> 6) & 3;
-			}
-		}
-
-		uint32_t get_uint32() const
-		{
-			return get_byte(0) | (get_byte(1) << 8) | (get_byte(2) << 16) | (get_byte(3) << 24);
-		}
-
-		uint32_t get_byte(uint32_t byte_index) const
-		{
-			assert(byte_index < 4);
-
-			return m_selectors[byte_index * 4 + 0] |
-				(m_selectors[byte_index * 4 + 1] << 2) |
-				(m_selectors[byte_index * 4 + 2] << 4) |
-				(m_selectors[byte_index * 4 + 3] << 6);
-		}
-
-		uint8_t operator()(uint32_t x, uint32_t y) const { assert((x < 4) && (y < 4)); return m_selectors[x + y * 4]; }
-		uint8_t&operator()(uint32_t x, uint32_t y) { assert((x < 4) && (y < 4)); return m_selectors[x + y * 4]; }
-
-		uint32_t calc_distance(const etc1_selector_palette_entry &other) const
-		{
-			uint32_t dist = 0;
-			for (uint32_t i = 0; i < 8; i++)
-			{
-				int delta = static_cast<int>(m_selectors[i]) - static_cast<int>(other.m_selectors[i]);
-				dist += delta * delta;
-			}
-			return dist;
-		}
-
-#if 0
-		uint32_t calc_hamming_dist(const etc1_selector_palette_entry &other) const
-		{
-			uint32_t dist = 0;
-			for (uint32_t i = 0; i < 4; i++)
-				dist += g_hamming_dist[get_byte(i) ^ other.get_byte(i)];
-			return dist;
-		}
-#endif
-
-		etc1_selector_palette_entry get_inverted() const
-		{
-			etc1_selector_palette_entry result;
-
-			for (uint32_t i = 0; i < 16; i++)
-				result.m_selectors[i] = 3 - m_selectors[i];
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_divided() const
-		{
-			etc1_selector_palette_entry result;
-
-			const uint8_t div_selector[4] = { 2, 0, 3, 1 };
-
-			for (uint32_t i = 0; i < 16; i++)
-				result.m_selectors[i] = div_selector[m_selectors[i]];
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_shifted(int delta) const
-		{
-			etc1_selector_palette_entry result;
-
-			for (uint32_t i = 0; i < 16; i++)
-				result.m_selectors[i] = static_cast<uint8_t>(basisu::clamp<int>(m_selectors[i] + delta, 0, 3));
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_randomized() const
-		{
-			uint32_t seed = get_uint32();
-
-			etc1_selector_palette_entry result;
-
-			for (uint32_t y = 0; y < 4; y++)
-			{
-				for (uint32_t x = 0; x < 4; x++)
-				{
-					int s = (*this)(x, y);
-
-					// between 0 and 10
-					uint32_t i = basisd_urand(seed, 6) + basisd_urand(seed, 6);
-					if (i == 0)
-						s -= 2;
-					else if (i == 10)
-						s += 2;
-					else if (i < 3)
-						s -= 1;
-					else if (i > 7)
-						s += 1;
-
-					result(x, y) = static_cast<uint8_t>(basisu::clamp<int>(s, 0, 3));
-				}
-			}
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_contrast(int table_index) const
-		{
-			assert(table_index < 4);
-
-			etc1_selector_palette_entry result;
-
-			static const uint8_t s_contrast_tables[4][4] =
-			{
-				{ 0, 1, 2, 3 }, // not used
-				{ 0, 0, 3, 3 },
-				{ 1, 1, 2, 2 },
-				{ 1, 1, 3, 3 }
-			};
-
-			for (uint32_t i = 0; i < 16; i++)
-			{
-				result[i] = s_contrast_tables[table_index][(*this)[i]];
-			}
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_dilated() const
-		{
-			etc1_selector_palette_entry result;
-
-			for (uint32_t y = 0; y < 4; y++)
-			{
-				for (uint32_t x = 0; x < 4; x++)
-				{
-					uint32_t max_selector = 0;
-
-					for (int yd = -1; yd <= 1; yd++)
-					{
-						int fy = y + yd;
-						if ((fy < 0) || (fy > 3))
-							continue;
-
-						for (int xd = -1; xd <= 1; xd++)
-						{
-							int fx = x + xd;
-							if ((fx < 0) || (fx > 3))
-								continue;
-
-							max_selector = basisu::maximum<uint32_t>(max_selector, (*this)(fx, fy));
-						}
-					}
-
-					result(x, y) = static_cast<uint8_t>(max_selector);
-				}
-			}
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_eroded() const
-		{
-			etc1_selector_palette_entry result;
-
-			for (uint32_t y = 0; y < 4; y++)
-			{
-				for (uint32_t x = 0; x < 4; x++)
-				{
-					uint32_t min_selector = 99;
-
-					for (int yd = -1; yd <= 1; yd++)
-					{
-						int fy = y + yd;
-						if ((fy < 0) || (fy > 3))
-							continue;
-
-						for (int xd = -1; xd <= 1; xd++)
-						{
-							int fx = x + xd;
-							if ((fx < 0) || (fx > 3))
-								continue;
-
-							min_selector = basisu::minimum<uint32_t>(min_selector, (*this)(fx, fy));
-						}
-					}
-
-					result(x, y) = static_cast<uint8_t>(min_selector);
-				}
-			}
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_shift_x() const
-		{
-			etc1_selector_palette_entry result;
-
-			for (uint32_t y = 0; y < 4; y++)
-			{
-				for (uint32_t x = 0; x < 4; x++)
-				{
-					int sx = x - 1;
-					if (sx < 0)
-						sx = 0;
-
-					result(x, y) = (*this)(sx, y);
-				}
-			}
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_shift_y() const
-		{
-			etc1_selector_palette_entry result;
-
-			for (uint32_t y = 0; y < 4; y++)
-			{
-				int sy = y - 1;
-				if (sy < 0)
-					sy = 3;
-
-				for (uint32_t x = 0; x < 4; x++)
-					result(x, y) = (*this)(x, sy);
-			}
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_median() const
-		{
-			etc1_selector_palette_entry result;
-
-			for (uint32_t y = 0; y < 4; y++)
-			{
-				for (uint32_t x = 0; x < 4; x++)
-				{
-					// ABC
-					// D F
-					// GHI
-
-					uint8_t selectors[8];
-					uint32_t n = 0;
-
-					for (int yd = -1; yd <= 1; yd++)
-					{
-						int fy = y + yd;
-						if ((fy < 0) || (fy > 3))
-							continue;
-
-						for (int xd = -1; xd <= 1; xd++)
-						{
-							if ((xd | yd) == 0)
-								continue;
-
-							int fx = x + xd;
-							if ((fx < 0) || (fx > 3))
-								continue;
-
-							selectors[n++] = (*this)(fx, fy);
-						}
-					}
-
-					std::sort(selectors, selectors + n);
-
-					result(x, y) = selectors[n / 2];
-				}
-			}
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_high_pass() const
-		{
-			etc1_selector_palette_entry result;
-
-			static const int kernel[3][3] =
-			{
-				{ 0,  -1,  0 },
-				{ -1,  8, -1 },
-				{ 0,  -1,  0 }
-			};
-
-			for (uint32_t y = 0; y < 4; y++)
-			{
-				for (uint32_t x = 0; x < 4; x++)
-				{
-					// ABC
-					// D F
-					// GHI
-
-					int sum = 0;
-
-					for (int yd = -1; yd <= 1; yd++)
-					{
-						int fy = y + yd;
-						fy = basisu::clamp<int>(fy, 0, 3);
-
-						for (int xd = -1; xd <= 1; xd++)
-						{
-							int fx = x + xd;
-							fx = basisu::clamp<int>(fx, 0, 3);
-
-							int k = (*this)(fx, fy);
-							sum += k * kernel[yd + 1][xd + 1];
-						}
-					}
-
-					sum = sum / 4;
-
-					result(x, y) = static_cast<uint8_t>(basisu::clamp<int>(sum, 0, 3));
-				}
-			}
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_flipped_and_rotated(bool flip, uint32_t rotation_index) const
-		{
-			etc1_selector_palette_entry temp;
-
-			if (flip)
-			{
-				for (uint32_t y = 0; y < 4; y++)
-					for (uint32_t x = 0; x < 4; x++)
-						temp(x, y) = (*this)(x, 3 - y);
-			}
-			else
-			{
-				temp = *this;
-			}
-
-			etc1_selector_palette_entry result;
-
-			switch (rotation_index)
-			{
-			case 0:
-				result = temp;
-				break;
-			case 1:
-				for (uint32_t y = 0; y < 4; y++)
-					for (uint32_t x = 0; x < 4; x++)
-						result(x, y) = temp(y, 3 - x);
-				break;
-			case 2:
-				for (uint32_t y = 0; y < 4; y++)
-					for (uint32_t x = 0; x < 4; x++)
-						result(x, y) = temp(3 - x, 3 - y);
-				break;
-			case 3:
-				for (uint32_t y = 0; y < 4; y++)
-					for (uint32_t x = 0; x < 4; x++)
-						result(x, y) = temp(3 - y, x);
-				break;
-			default:
-				assert(0);
-				break;
-			}
-
-			return result;
-		}
-
-		etc1_selector_palette_entry get_modified(const etc1_global_palette_entry_modifier &modifier) const
-		{
-			etc1_selector_palette_entry r(*this);
-
-			if (modifier.m_shift_x)
-				r = r.get_shift_x();
-
-			if (modifier.m_shift_y)
-				r = r.get_shift_y();
-
-			r = r.get_flipped_and_rotated(modifier.m_flip != 0, modifier.m_rot);
-
-			if (modifier.m_dilate)
-				r = r.get_dilated();
-
-			if (modifier.m_erode)
-				r = r.get_eroded();
-
-			if (modifier.m_high_pass)
-				r = r.get_high_pass();
-
-			if (modifier.m_rand)
-				r = r.get_randomized();
-
-			if (modifier.m_div)
-				r = r.get_divided();
-
-			if (modifier.m_shift)
-				r = r.get_shifted(1);
-
-			if (modifier.m_contrast)
-				r = r.get_contrast(modifier.m_contrast);
-
-			if (modifier.m_inv)
-				r = r.get_inverted();
-
-			if (modifier.m_median)
-				r = r.get_median();
-
-			return r;
-		}
-
-		etc1_selector_palette_entry apply_modifier(modifier_types mod_type, const etc1_global_palette_entry_modifier &modifier) const
-		{
-			switch (mod_type)
-			{
-			case cModifierContrast:
-				return get_contrast(modifier.m_contrast);
-			case cModifierRand:
-				return get_randomized();
-			case cModifierMedian:
-				return get_median();
-			case cModifierDiv:
-				return get_divided();
-			case cModifierShift:
-				return get_shifted(1);
-			case cModifierInv:
-				return get_inverted();
-			case cModifierFlippedAndRotated:
-				return get_flipped_and_rotated(modifier.m_flip != 0, modifier.m_rot);
-			case cModifierDilate:
-				return get_dilated();
-			case cModifierShiftX:
-				return get_shift_x();
-			case cModifierShiftY:
-				return get_shift_y();
-			case cModifierErode:
-				return get_eroded();
-			case cModifierHighPass:
-				return get_high_pass();
-			default:
-				assert(0);
-				break;
-			}
-
-			return *this;
-		}
-
-		etc1_selector_palette_entry get_modified(const etc1_global_palette_entry_modifier &modifier, uint32_t num_order, const modifier_types *pOrder) const
-		{
-			etc1_selector_palette_entry r(*this);
-
-			for (uint32_t i = 0; i < num_order; i++)
-			{
-				r = r.apply_modifier(pOrder[i], modifier);
-			}
-
-			return r;
-		}
-
-		bool operator< (const etc1_selector_palette_entry &other) const
-		{
-			for (uint32_t i = 0; i < 16; i++)
-			{
-				if (m_selectors[i] < other.m_selectors[i])
-					return true;
-				else if (m_selectors[i] != other.m_selectors[i])
-					return false;
-			}
-
-			return false;
-		}
-
-		bool operator== (const etc1_selector_palette_entry &other) const
-		{
-			for (uint32_t i = 0; i < 16; i++)
-			{
-				if (m_selectors[i] != other.m_selectors[i])
-					return false;
-			}
-
-			return true;
-		}
-
-	private:
-		uint8_t m_selectors[16];
-	};
-
-	typedef basisu::vector<etc1_selector_palette_entry> etc1_selector_palette_entry_vec;
-
-	extern const uint32_t g_global_selector_cb[];
-	extern const uint32_t g_global_selector_cb_size;
-
-#define ETC1_GLOBAL_SELECTOR_CODEBOOK_MAX_PAL_BITS (12)
-
-	struct etc1_global_selector_codebook_entry_id
-	{
-		uint32_t m_palette_index;
-		etc1_global_palette_entry_modifier m_modifier;
-
-		etc1_global_selector_codebook_entry_id(uint32_t palette_index, const etc1_global_palette_entry_modifier &modifier) : m_palette_index(palette_index), m_modifier(modifier) { }
-
-		etc1_global_selector_codebook_entry_id() { }
-
-		void set(uint32_t palette_index, const etc1_global_palette_entry_modifier &modifier) { m_palette_index = palette_index; m_modifier = modifier; }
-	};
-
-	typedef basisu::vector<etc1_global_selector_codebook_entry_id> etc1_global_selector_codebook_entry_id_vec;
-
-	class etc1_global_selector_codebook
-	{
-	public:
-		etc1_global_selector_codebook() { }
-		etc1_global_selector_codebook(uint32_t N, const uint32_t *pEntries) { init(N, pEntries); }
-
-		void init(uint32_t N, const uint32_t* pEntries);
-
-		void print_code(FILE *pFile);
-
-		void clear()
-		{
-			m_palette.clear();
-		}
-
-		uint32_t size() const { return (uint32_t)m_palette.size(); }
-
-		const etc1_selector_palette_entry_vec &get_palette() const
-		{
-			return m_palette;
-		}
-
-		etc1_selector_palette_entry get_entry(uint32_t palette_index) const
-		{
-			return m_palette[palette_index];
-		}
-
-		etc1_selector_palette_entry get_entry(uint32_t palette_index, const etc1_global_palette_entry_modifier &modifier) const
-		{
-			return m_palette[palette_index].get_modified(modifier);
-		}
-
-		etc1_selector_palette_entry get_entry(const etc1_global_selector_codebook_entry_id &id) const
-		{
-			return m_palette[id.m_palette_index].get_modified(id.m_modifier);
-		}
-
-		etc1_selector_palette_entry_vec m_palette;
-	};
-
-} // namespace basist
diff --git a/transcoder/basisu_transcoder.cpp b/transcoder/basisu_transcoder.cpp
index 0b37333..6307319 100644
--- a/transcoder/basisu_transcoder.cpp
+++ b/transcoder/basisu_transcoder.cpp
@@ -37,6 +37,14 @@
 	#endif
 #endif
 
+// Using unaligned loads and stores causes errors when using UBSan. Jam it off.
+#if defined(__has_feature)
+#if __has_feature(undefined_behavior_sanitizer)
+#undef BASISD_USE_UNALIGNED_WORD_READS
+#define BASISD_USE_UNALIGNED_WORD_READS 0
+#endif
+#endif
+
 #define BASISD_SUPPORTED_BASIS_VERSION (0x13)
 
 #ifndef BASISD_SUPPORT_KTX2
@@ -224,32 +232,7 @@
 
 		return static_cast<uint16_t>(~crc);
 	}
-
-	const uint32_t g_global_selector_cb[] =
-#include "basisu_global_selector_cb.h"
-		;
-
-	const uint32_t g_global_selector_cb_size = sizeof(g_global_selector_cb) / sizeof(g_global_selector_cb[0]);
-
-	void etc1_global_selector_codebook::init(uint32_t N, const uint32_t* pEntries)
-	{
-		m_palette.resize(N);
-		for (uint32_t i = 0; i < N; i++)
-			m_palette[i].set_uint32(pEntries[i]);
-	}
-
-	void etc1_global_selector_codebook::print_code(FILE* pFile)
-	{
-		fprintf(pFile, "{\n");
-		for (uint32_t i = 0; i < m_palette.size(); i++)
-		{
-			fprintf(pFile, "0x%X,", m_palette[i].get_uint32());
-			if ((i & 15) == 15)
-				fprintf(pFile, "\n");
-		}
-		fprintf(pFile, "\n}\n");
-	}
-
+		
 	enum etc_constants
 	{
 		cETC1BytesPerBlock = 8U,
@@ -7532,9 +7515,8 @@
 	}
 #endif // BASISD_SUPPORT_PVRTC2
 
-	basisu_lowlevel_etc1s_transcoder::basisu_lowlevel_etc1s_transcoder(const etc1_global_selector_codebook* pGlobal_sel_codebook) :
+	basisu_lowlevel_etc1s_transcoder::basisu_lowlevel_etc1s_transcoder() :
 		m_pGlobal_codebook(nullptr),
-		m_pGlobal_sel_codebook(pGlobal_sel_codebook),
 		m_selector_history_buf_size(0)
 	{
 	}
@@ -7641,50 +7623,8 @@
 
 		if (used_global_selector_cb)
 		{
-			// global selector palette
-			uint32_t pal_bits = sym_codec.get_bits(4);
-			uint32_t mod_bits = sym_codec.get_bits(4);
-
-			basist::huffman_decoding_table mod_model;
-			if (mod_bits)
-			{
-				if (!sym_codec.read_huffman_table(mod_model))
-				{
-					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 6\n");
-					return false;
-				}
-				if (!mod_model.is_valid())
-				{
-					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 6a\n");
-					return false;
-				}
-			}
-
-			for (uint32_t i = 0; i < num_selectors; i++)
-			{
-				uint32_t pal_index = 0;
-				if (pal_bits)
-					pal_index = sym_codec.get_bits(pal_bits);
-
-				uint32_t mod_index = 0;
-				if (mod_bits)
-					mod_index = sym_codec.decode_huffman(mod_model);
-
-				if (pal_index >= m_pGlobal_sel_codebook->size())
-				{
-					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 7z\n");
-					return false;
-				}
-
-				const etc1_selector_palette_entry e(m_pGlobal_sel_codebook->get_entry(pal_index, etc1_global_palette_entry_modifier(mod_index)));
-
-				// TODO: Optimize this
-				for (uint32_t y = 0; y < 4; y++)
-					for (uint32_t x = 0; x < 4; x++)
-						m_local_selectors[i].set_selector(x, y, e[x + y * 4]);
-
-				m_local_selectors[i].init_flags();
-			}
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: global selector codebooks are unsupported\n");
+			return false;
 		}
 		else
 		{
@@ -7692,146 +7632,70 @@
 
 			if (used_hybrid_selector_cb)
 			{
-				const uint32_t pal_bits = sym_codec.get_bits(4);
-				const uint32_t mod_bits = sym_codec.get_bits(4);
+				BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: hybrid global selector codebooks are unsupported\n");
+				return false;
+			}
+				
+			const bool used_raw_encoding = (sym_codec.get_bits(1) == 1);
 
-				basist::huffman_decoding_table uses_global_cb_bitflags_model;
-				if (!sym_codec.read_huffman_table(uses_global_cb_bitflags_model))
+			if (used_raw_encoding)
+			{
+				for (uint32_t i = 0; i < num_selectors; i++)
 				{
-					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 7\n");
-					return false;
-				}
-				if (!uses_global_cb_bitflags_model.is_valid())
-				{
-					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 7a\n");
-					return false;
-				}
-
-				basist::huffman_decoding_table global_mod_indices_model;
-				if (mod_bits)
-				{
-					if (!sym_codec.read_huffman_table(global_mod_indices_model))
+					for (uint32_t j = 0; j < 4; j++)
 					{
-						BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 8\n");
-						return false;
-					}
-					if (!global_mod_indices_model.is_valid())
-					{
-						BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 8a\n");
-						return false;
-					}
-				}
+						uint32_t cur_byte = sym_codec.get_bits(8);
 
-				uint32_t cur_uses_global_cb_bitflags = 0;
-				uint32_t uses_global_cb_bitflags_remaining = 0;
-
-				for (uint32_t q = 0; q < num_selectors; q++)
-				{
-					if (!uses_global_cb_bitflags_remaining)
-					{
-						cur_uses_global_cb_bitflags = sym_codec.decode_huffman(uses_global_cb_bitflags_model);
-
-						uses_global_cb_bitflags_remaining = 8;
-					}
-					uses_global_cb_bitflags_remaining--;
-
-					const bool used_global_cb_flag = (cur_uses_global_cb_bitflags & 1) != 0;
-					cur_uses_global_cb_bitflags >>= 1;
-
-					if (used_global_cb_flag)
-					{
-						const uint32_t pal_index = pal_bits ? sym_codec.get_bits(pal_bits) : 0;
-						const uint32_t mod_index = mod_bits ? sym_codec.decode_huffman(global_mod_indices_model) : 0;
-
-						if (pal_index >= m_pGlobal_sel_codebook->size())
-						{
-							BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 8b\n");
-							return false;
-						}
-
-						const etc1_selector_palette_entry e(m_pGlobal_sel_codebook->get_entry(pal_index, etc1_global_palette_entry_modifier(mod_index)));
-
-						for (uint32_t y = 0; y < 4; y++)
-							for (uint32_t x = 0; x < 4; x++)
-								m_local_selectors[q].set_selector(x, y, e[x + y * 4]);
-					}
-					else
-					{
-						for (uint32_t j = 0; j < 4; j++)
-						{
-							uint32_t cur_byte = sym_codec.get_bits(8);
-
-							for (uint32_t k = 0; k < 4; k++)
-								m_local_selectors[q].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
-						}
+						for (uint32_t k = 0; k < 4; k++)
+							m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
 					}
 
-					m_local_selectors[q].init_flags();
+					m_local_selectors[i].init_flags();
 				}
 			}
 			else
 			{
-				const bool used_raw_encoding = (sym_codec.get_bits(1) == 1);
-
-				if (used_raw_encoding)
+				if (!sym_codec.read_huffman_table(delta_selector_pal_model))
 				{
-					for (uint32_t i = 0; i < num_selectors; i++)
+					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 10\n");
+					return false;
+				}
+
+				if ((num_selectors > 1) && (!delta_selector_pal_model.is_valid()))
+				{
+					BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 10a\n");
+					return false;
+				}
+
+				uint8_t prev_bytes[4] = { 0, 0, 0, 0 };
+
+				for (uint32_t i = 0; i < num_selectors; i++)
+				{
+					if (!i)
 					{
 						for (uint32_t j = 0; j < 4; j++)
 						{
 							uint32_t cur_byte = sym_codec.get_bits(8);
-
-							for (uint32_t k = 0; k < 4; k++)
-								m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
-						}
-
-						m_local_selectors[i].init_flags();
-					}
-				}
-				else
-				{
-					if (!sym_codec.read_huffman_table(delta_selector_pal_model))
-					{
-						BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 10\n");
-						return false;
-					}
-
-					if ((num_selectors > 1) && (!delta_selector_pal_model.is_valid()))
-					{
-						BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 10a\n");
-						return false;
-					}
-
-					uint8_t prev_bytes[4] = { 0, 0, 0, 0 };
-
-					for (uint32_t i = 0; i < num_selectors; i++)
-					{
-						if (!i)
-						{
-							for (uint32_t j = 0; j < 4; j++)
-							{
-								uint32_t cur_byte = sym_codec.get_bits(8);
-								prev_bytes[j] = static_cast<uint8_t>(cur_byte);
-
-								for (uint32_t k = 0; k < 4; k++)
-									m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
-							}
-							m_local_selectors[i].init_flags();
-							continue;
-						}
-
-						for (uint32_t j = 0; j < 4; j++)
-						{
-							int delta_byte = sym_codec.decode_huffman(delta_selector_pal_model);
-
-							uint32_t cur_byte = delta_byte ^ prev_bytes[j];
 							prev_bytes[j] = static_cast<uint8_t>(cur_byte);
 
 							for (uint32_t k = 0; k < 4; k++)
 								m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
 						}
 						m_local_selectors[i].init_flags();
+						continue;
 					}
+
+					for (uint32_t j = 0; j < 4; j++)
+					{
+						int delta_byte = sym_codec.decode_huffman(delta_selector_pal_model);
+
+						uint32_t cur_byte = delta_byte ^ prev_bytes[j];
+						prev_bytes[j] = static_cast<uint8_t>(cur_byte);
+
+						for (uint32_t k = 0; k < 4; k++)
+							m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
+					}
+					m_local_selectors[i].init_flags();
 				}
 			}
 		}
@@ -7899,6 +7763,12 @@
 		}
 
 		m_selector_history_buf_size = sym_codec.get_bits(13);
+		// Check for bogus values.
+		if (!m_selector_history_buf_size)
+		{
+			BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 5\n");
+			return false;
+		}
 
 		sym_codec.stop();
 
@@ -7979,8 +7849,11 @@
 
 		decoder_etc_block block;
 		memset(&block, 0, sizeof(block));
+				
+		//block.set_flip_bit(true);
+		// Setting the flip bit to false to be compatible with the Khronos KDFS.
+		block.set_flip_bit(false);
 
-		block.set_flip_bit(true);
 		block.set_diff_bit(true);
 
 		void* pPVRTC_work_mem = nullptr;
@@ -8741,7 +8614,7 @@
 			if (!output_row_pitch_in_blocks_or_pixels)
 				output_row_pitch_in_blocks_or_pixels = orig_width;
 
-			if (!output_rows_in_pixels)
+			if (!output_rows_in_pixels) 
 				output_rows_in_pixels = orig_height;
 
 			// Now make sure the output buffer is large enough, or we'll overwrite memory.
@@ -9440,6 +9313,12 @@
 				{
 					switch (fmt)
 					{
+					case block_format::cUASTC_4x4:
+					{
+						memcpy(pDst_block, pSource_block, sizeof(uastc_block));
+						status = true;
+						break;
+					}
 					case block_format::cETC1:
 					{
 						if (from_alpha)
@@ -9906,8 +9785,7 @@
 		return status;
 	}
 	
-	basisu_transcoder::basisu_transcoder(const etc1_global_selector_codebook* pGlobal_sel_codebook) :
-		m_lowlevel_etc1s_decoder(pGlobal_sel_codebook),
+	basisu_transcoder::basisu_transcoder() :
 		m_ready_to_transcode(false)
 	{
 	}
@@ -10778,6 +10656,8 @@
 			return false;
 		}
 
+		//const bool transcode_alpha_data_to_opaque_formats = (decode_flags & cDecodeFlagsTranscodeAlphaDataToOpaqueFormats) != 0;
+
 		if (decode_flags & cDecodeFlagsPVRTCDecodeToNextPow2)
 		{
 			BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: cDecodeFlagsPVRTCDecodeToNextPow2 currently unsupported\n");
@@ -11001,6 +10881,7 @@
 		case block_format::cRGB565: return "RGB565";
 		case block_format::cBGR565: return "BGR565";
 		case block_format::cRGBA4444: return "RGBA4444";
+		case block_format::cUASTC_4x4: return "UASTC_4x4";
 		case block_format::cFXT1_RGB: return "FXT1_RGB";
 		case block_format::cPVRTC2_4_RGB: return "PVRTC2_4_RGB";
 		case block_format::cPVRTC2_4_RGBA: return "PVRTC2_4_RGBA";
@@ -12567,12 +12448,8 @@
 				bits = read_bits64(blk.m_bytes, bit_ofs, basisu::minimum<int>(64, 128 - (int)bit_ofs));
 			else
 			{
-#ifdef __EMSCRIPTEN__
 				bits = blk.m_dwords[2];
 				bits |= (((uint64_t)blk.m_dwords[3]) << 32U);
-#else
-				bits = blk.m_qwords[1];
-#endif
 				
 				if (bit_ofs >= 64U)
 					bits >>= (bit_ofs - 64U);
@@ -16722,8 +16599,8 @@
 #if BASISD_SUPPORT_KTX2
 	const uint8_t g_ktx2_file_identifier[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
 
-	ktx2_transcoder::ktx2_transcoder(basist::etc1_global_selector_codebook* pGlobal_sel_codebook) :
-		m_etc1s_transcoder(pGlobal_sel_codebook)
+	ktx2_transcoder::ktx2_transcoder() :
+		m_etc1s_transcoder()
 	{
 		clear();
 	}
@@ -17334,6 +17211,7 @@
 		
 	bool ktx2_transcoder::decompress_level_data(uint32_t level_index, basisu::uint8_vec& uncomp_data)
 	{
+		const uint8_t* pComp_data = m_levels[level_index].m_byte_offset + m_pData;
 		const uint64_t comp_size = m_levels[level_index].m_byte_length;
 		
 		const uint64_t uncomp_size = m_levels[level_index].m_uncompressed_byte_length;
@@ -17349,7 +17227,7 @@
 			return false;
 		}
 
-		if (!uncomp_data.try_resize(uncomp_size))
+		if (!uncomp_data.try_resize((size_t)uncomp_size))
 		{
 			BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: Out of memory\n");
 			return false;
@@ -17358,7 +17236,6 @@
 		if (m_header.m_supercompression_scheme == KTX2_SS_ZSTANDARD)
 		{
 #if BASISD_SUPPORT_KTX2_ZSTD
-			const uint8_t* pComp_data = m_levels[level_index].m_byte_offset + m_pData;
 			size_t actualUncompSize = ZSTD_decompress(uncomp_data.data(), (size_t)uncomp_size, pComp_data, (size_t)comp_size);
 			if (ZSTD_isError(actualUncompSize))
 			{
diff --git a/transcoder/basisu_transcoder.h b/transcoder/basisu_transcoder.h
index bf3aed3..3327e8d 100644
--- a/transcoder/basisu_transcoder.h
+++ b/transcoder/basisu_transcoder.h
@@ -34,7 +34,6 @@
 
 #include "basisu_transcoder_internal.h"
 #include "basisu_transcoder_uastc.h"
-#include "basisu_global_selector_palette.h"
 #include "basisu_file_headers.h"
 
 namespace basist
@@ -190,7 +189,7 @@
 		friend class basisu_transcoder;
 
 	public:
-		basisu_lowlevel_etc1s_transcoder(const basist::etc1_global_selector_codebook* pGlobal_sel_codebook);
+		basisu_lowlevel_etc1s_transcoder();
 
 		void set_global_codebooks(const basisu_lowlevel_etc1s_transcoder* pGlobal_codebook) { m_pGlobal_codebook = pGlobal_codebook; }
 		const basisu_lowlevel_etc1s_transcoder* get_global_codebooks() const { return m_pGlobal_codebook; }
@@ -248,17 +247,13 @@
 
 		typedef basisu::vector<selector> selector_vec;
 		const selector_vec& get_selectors() const { return m_local_selectors; }
-
-		const etc1_global_selector_codebook* get_global_sel_codebook() const { return m_pGlobal_sel_codebook; }
-
+				
 	private:
 		const basisu_lowlevel_etc1s_transcoder* m_pGlobal_codebook;
 
 		endpoint_vec m_local_endpoints;
 		selector_vec m_local_selectors;
-
-		const etc1_global_selector_codebook* m_pGlobal_sel_codebook;
-
+				
 		huffman_decoding_table m_endpoint_pred_model, m_delta_endpoint_model, m_selector_model, m_selector_history_buf_rle_model;
 
 		uint32_t m_selector_history_buf_size;
@@ -442,7 +437,7 @@
 		basisu_transcoder& operator= (const basisu_transcoder&);
 
 	public:
-		basisu_transcoder(const etc1_global_selector_codebook* pGlobal_sel_codebook);
+		basisu_transcoder();
 
 		// Validates the .basis file. This computes a crc16 over the entire file, so it's slow.
 		bool validate_file_checksums(const void* pData, uint32_t data_size, bool full_validation) const;
@@ -770,7 +765,7 @@
 	class ktx2_transcoder
 	{
 	public:
-		ktx2_transcoder(basist::etc1_global_selector_codebook* pGlobal_sel_codebook);
+		ktx2_transcoder();
 
 		// Frees all allocations, resets object.
 		void clear();
diff --git a/transcoder/basisu_transcoder_internal.h b/transcoder/basisu_transcoder_internal.h
index 2422d78..776a998 100644
--- a/transcoder/basisu_transcoder_internal.h
+++ b/transcoder/basisu_transcoder_internal.h
@@ -20,8 +20,8 @@
 #pragma warning (disable: 4127) //  conditional expression is constant
 #endif
 
-#define BASISD_LIB_VERSION 115
-#define BASISD_VERSION_STRING "01.15"
+#define BASISD_LIB_VERSION 116
+#define BASISD_VERSION_STRING "01.16"
 
 #ifdef _DEBUG
 #define BASISD_BUILD_DEBUG
@@ -46,18 +46,18 @@
 	{
 		cETC1,								// ETC1S RGB 
 		cETC2_RGBA,							// full ETC2 EAC RGBA8 block
-		cBC1,									// DXT1 RGB 
-		cBC3,									// BC4 block followed by a four color BC1 block
-		cBC4,									// DXT5A (alpha block only)
-		cBC5,									// two BC4 blocks
+		cBC1,								// DXT1 RGB 
+		cBC3,								// BC4 block followed by a four color BC1 block
+		cBC4,								// DXT5A (alpha block only)
+		cBC5,								// two BC4 blocks
 		cPVRTC1_4_RGB,						// opaque-only PVRTC1 4bpp
-		cPVRTC1_4_RGBA,					// PVRTC1 4bpp RGBA
-		cBC7,									// Full BC7 block, any mode
+		cPVRTC1_4_RGBA,						// PVRTC1 4bpp RGBA
+		cBC7,								// Full BC7 block, any mode
 		cBC7_M5_COLOR,						// RGB BC7 mode 5 color (writes an opaque mode 5 block)
 		cBC7_M5_ALPHA,						// alpha portion of BC7 mode 5 (cBC7_M5_COLOR output data must have been written to the output buffer first to set the mode/rot fields etc.)
 		cETC2_EAC_A8,						// alpha block of ETC2 EAC (first 8 bytes of the 16-bit ETC2 EAC RGBA format)
 		cASTC_4x4,							// ASTC 4x4 (either color-only or color+alpha). Note that the transcoder always currently assumes sRGB is not enabled when outputting ASTC 
-												// data. If you use a sRGB ASTC format you'll get ~1 LSB of additional error, because of the different way ASTC decoders scale 8-bit endpoints to 16-bits during unpacking.
+											// data. If you use a sRGB ASTC format you'll get ~1 LSB of additional error, because of the different way ASTC decoders scale 8-bit endpoints to 16-bits during unpacking.
 		
 		cATC_RGB,
 		cATC_RGBA_INTERPOLATED_ALPHA,
@@ -72,8 +72,8 @@
 		cIndices,							// Used internally: Write 16-bit endpoint and selector indices directly to output (output block must be at least 32-bits)
 
 		cRGB32,								// Writes RGB components to 32bpp output pixels
-		cRGBA32,								// Writes RGB255 components to 32bpp output pixels
-		cA32,									// Writes alpha component to 32bpp output pixels
+		cRGBA32,							// Writes RGB255 components to 32bpp output pixels
+		cA32,								// Writes alpha component to 32bpp output pixels
 				
 		cRGB565,
 		cBGR565,
@@ -82,6 +82,8 @@
 		cRGBA4444_ALPHA,
 		cRGBA4444_COLOR_OPAQUE,
 		cRGBA4444,
+
+		cUASTC_4x4,
 						
 		cTotalBlockFormats
 	};
diff --git a/transcoder/basisu_transcoder_uastc.h b/transcoder/basisu_transcoder_uastc.h
index d501a2a..f91314f 100644
--- a/transcoder/basisu_transcoder_uastc.h
+++ b/transcoder/basisu_transcoder_uastc.h
@@ -205,10 +205,6 @@
 		{
 			uint8_t m_bytes[16];
 			uint32_t m_dwords[4];
-
-#ifndef __EMSCRIPTEN__
-			uint64_t m_qwords[2];
-#endif
 		};
 	};
 
diff --git a/webgl/encoder/CMakeLists.txt b/webgl/encoder/CMakeLists.txt
index e365e1b..588d91b 100644
--- a/webgl/encoder/CMakeLists.txt
+++ b/webgl/encoder/CMakeLists.txt
@@ -20,18 +20,16 @@
 	../../encoder/basisu_enc.cpp                             
 	../../encoder/basisu_etc.cpp                             
 	../../encoder/basisu_frontend.cpp                        
-	../../encoder/basisu_global_selector_palette_helpers.cpp 
 	../../encoder/basisu_gpu_texture.cpp                     
 	../../encoder/basisu_pvrtc1_4.cpp                        
 	../../encoder/basisu_resampler.cpp                       
 	../../encoder/basisu_resample_filters.cpp                
 	../../encoder/basisu_ssim.cpp                            
-	../../encoder/basisu_astc_decomp.cpp                     
 	../../encoder/basisu_uastc_enc.cpp                       
 	../../encoder/basisu_bc7enc.cpp
 	../../encoder/basisu_kernels_sse.cpp
-	../../encoder/lodepng.cpp                                
-	../../encoder/apg_bmp.c                                  
+	../../encoder/basisu_opencl.cpp
+	../../encoder/pvpngreader.cpp
 	../../encoder/jpgd.cpp                                   
   )
 
diff --git a/webgl/encoder/build/basis_encoder.js b/webgl/encoder/build/basis_encoder.js
index 2820331..c52dba2 100644
--- a/webgl/encoder/build/basis_encoder.js
+++ b/webgl/encoder/build/basis_encoder.js
@@ -6,7 +6,7 @@
 function(BASIS) {
   BASIS = BASIS || {};
 
-var Module=typeof BASIS!=="undefined"?BASIS:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var STACK_ALIGN=16;function alignMemory(size,factor){if(!factor)factor=STACK_ALIGN;return Math.ceil(size/factor)*factor}var tempRet0=0;var setTempRet0=function(value){tempRet0=value};var getTempRet0=function(){return tempRet0};var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx<endPtr){var u0=heap[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heap[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heap[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heap[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function UTF16ToString(ptr,maxBytesToRead){var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder){return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr))}else{var str="";for(var i=0;!(i>=maxBytesToRead/2);++i){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)break;str+=String.fromCharCode(codeUnit)}return str}}function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite<str.length*2?maxBytesToWrite/2:str.length;for(var i=0;i<numCharsToWrite;++i){var codeUnit=str.charCodeAt(i);HEAP16[outPtr>>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}function lengthBytesUTF16(str){return str.length*2}function UTF32ToString(ptr,maxBytesToRead){var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str}function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr}function lengthBytesUTF32(str){var len=0;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343)++i;len+=4}return len}function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||299958272;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();TTY.init();callRuntimeCallbacks(__ATINIT__)}function preMain(){FS.ignorePermissions=false;callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";function isFileURI(filename){return hasPrefix(filename,fileURIPrefix)}var wasmBinaryFile="basis_encoder.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return Promise.resolve().then(getBinary)}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["Z"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["_"];removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}var tempDouble;var tempI64;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var ExceptionInfoAttrs={DESTRUCTOR_OFFSET:0,REFCOUNT_OFFSET:4,TYPE_OFFSET:8,CAUGHT_OFFSET:12,RETHROWN_OFFSET:13,SIZE:16};function ___cxa_allocate_exception(size){return _malloc(size+ExceptionInfoAttrs.SIZE)+ExceptionInfoAttrs.SIZE}function ExceptionInfo(excPtr){this.excPtr=excPtr;this.ptr=excPtr-ExceptionInfoAttrs.SIZE;this.set_type=function(type){HEAP32[this.ptr+ExceptionInfoAttrs.TYPE_OFFSET>>2]=type};this.get_type=function(){return HEAP32[this.ptr+ExceptionInfoAttrs.TYPE_OFFSET>>2]};this.set_destructor=function(destructor){HEAP32[this.ptr+ExceptionInfoAttrs.DESTRUCTOR_OFFSET>>2]=destructor};this.get_destructor=function(){return HEAP32[this.ptr+ExceptionInfoAttrs.DESTRUCTOR_OFFSET>>2]};this.set_refcount=function(refcount){HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=refcount};this.set_caught=function(caught){caught=caught?1:0;HEAP8[this.ptr+ExceptionInfoAttrs.CAUGHT_OFFSET>>0]=caught};this.get_caught=function(){return HEAP8[this.ptr+ExceptionInfoAttrs.CAUGHT_OFFSET>>0]!=0};this.set_rethrown=function(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+ExceptionInfoAttrs.RETHROWN_OFFSET>>0]=rethrown};this.get_rethrown=function(){return HEAP8[this.ptr+ExceptionInfoAttrs.RETHROWN_OFFSET>>0]!=0};this.init=function(type,destructor){this.set_type(type);this.set_destructor(destructor);this.set_refcount(0);this.set_caught(false);this.set_rethrown(false)};this.add_ref=function(){var value=HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2];HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=value+1};this.release_ref=function(){var prev=HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2];HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=prev-1;return prev===1}}var exceptionLast=0;var uncaughtExceptionCount=0;function ___cxa_throw(ptr,type,destructor){var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw ptr}function setErrNo(value){HEAP32[___errno_location()>>2]=value;return value}var PATH={splitPath:function(filename){var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:function(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:function(path){var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:function(path){if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},extname:function(path){return PATH.splitPath(path)[3]},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:function(l,r){return PATH.normalize(l+"/"+r)}};function getRandomDevice(){if(typeof crypto==="object"&&typeof crypto["getRandomValues"]==="function"){var randomBuffer=new Uint8Array(1);return function(){crypto.getRandomValues(randomBuffer);return randomBuffer[0]}}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");return function(){return crypto_module["randomBytes"](1)[0]}}catch(e){}}return function(){abort("randomDevice")}}var PATH_FS={resolve:function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:FS.cwd();if(typeof path!=="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:function(from,to){from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start<arr.length;start++){if(arr[start]!=="")break}var end=arr.length-1;for(;end>=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i<length;i++){if(fromParts[i]!==toParts[i]){samePartsLength=i;break}}var outputParts=[];for(var i=samePartsLength;i<fromParts.length;i++){outputParts.push("..")}outputParts=outputParts.concat(toParts.slice(samePartsLength));return outputParts.join("/")}};var TTY={ttys:[],init:function(){},shutdown:function(){},register:function(dev,ops){TTY.ttys[dev]={input:[],output:[],ops:ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open:function(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close:function(stream){stream.tty.ops.flush(stream.tty)},flush:function(stream){stream.tty.ops.flush(stream.tty)},read:function(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i<length;i++){var result;try{result=stream.tty.ops.get_char(stream.tty)}catch(e){throw new FS.ErrnoError(29)}if(result===undefined&&bytesRead===0){throw new FS.ErrnoError(6)}if(result===null||result===undefined)break;bytesRead++;buffer[offset+i]=result}if(bytesRead){stream.node.timestamp=Date.now()}return bytesRead},write:function(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.put_char){throw new FS.ErrnoError(60)}try{for(var i=0;i<length;i++){stream.tty.ops.put_char(stream.tty,buffer[offset+i])}}catch(e){throw new FS.ErrnoError(29)}if(length){stream.node.timestamp=Date.now()}return i}},default_tty_ops:{get_char:function(tty){if(!tty.input.length){var result=null;if(ENVIRONMENT_IS_NODE){var BUFSIZE=256;var buf=Buffer.alloc?Buffer.alloc(BUFSIZE):new Buffer(BUFSIZE);var bytesRead=0;try{bytesRead=nodeFS.readSync(process.stdin.fd,buf,0,BUFSIZE,null)}catch(e){if(e.toString().indexOf("EOF")!=-1)bytesRead=0;else throw e}if(bytesRead>0){result=buf.slice(0,bytesRead).toString("utf-8")}else{result=null}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else if(typeof readline=="function"){result=readline();if(result!==null){result+="\n"}}if(!result){return null}tty.input=intArrayFromString(result,true)}return tty.input.shift()},put_char:function(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}}},default_tty1_ops:{put_char:function(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};function mmapAlloc(size){var alignedSize=alignMemory(size,16384);var ptr=_malloc(alignedSize);while(size<alignedSize)HEAP8[ptr+size++]=0;return ptr}var MEMFS={ops_table:null,mount:function(mount){return MEMFS.createNode(null,"/",16384|511,0)},createNode:function(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}if(!MEMFS.ops_table){MEMFS.ops_table={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,allocate:MEMFS.stream_ops.allocate,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}}}var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.timestamp=Date.now();if(parent){parent.contents[name]=node}return node},getFileDataAsRegularArray:function(node){if(node.contents&&node.contents.subarray){var arr=[];for(var i=0;i<node.usedBytes;++i)arr.push(node.contents[i]);return arr}return node.contents},getFileDataAsTypedArray:function(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage:function(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity<CAPACITY_DOUBLING_MAX?2:1.125)>>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0);return},resizeFileStorage:function(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0;return}if(!node.contents||node.contents.subarray){var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize;return}if(!node.contents)node.contents=[];if(node.contents.length>newSize)node.contents.length=newSize;else while(node.contents.length<newSize)node.contents.push(0);node.usedBytes=newSize},node_ops:{getattr:function(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr:function(node,attr){if(attr.mode!==undefined){node.mode=attr.mode}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup:function(parent,name){throw FS.genericErrors[44]},mknod:function(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename:function(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.name=new_name;new_dir.contents[new_name]=old_node;old_node.parent=new_dir},unlink:function(parent,name){delete parent.contents[name]},rmdir:function(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name]},readdir:function(node){var entries=[".",".."];for(var key in node.contents){if(!node.contents.hasOwnProperty(key)){continue}entries.push(key)}return entries},symlink:function(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink:function(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read:function(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i<size;i++)buffer[offset+i]=contents[position+i]}return size},write:function(stream,buffer,offset,length,position,canOwn){if(buffer.buffer===HEAP8.buffer){canOwn=false}if(!length)return 0;var node=stream.node;node.timestamp=Date.now();if(buffer.subarray&&(!node.contents||node.contents.subarray)){if(canOwn){node.contents=buffer.subarray(offset,offset+length);node.usedBytes=length;return length}else if(node.usedBytes===0&&position===0){node.contents=buffer.slice(offset,offset+length);node.usedBytes=length;return length}else if(position+length<=node.usedBytes){node.contents.set(buffer.subarray(offset,offset+length),position);return length}}MEMFS.expandFileStorage(node,position+length);if(node.contents.subarray&&buffer.subarray){node.contents.set(buffer.subarray(offset,offset+length),position)}else{for(var i=0;i<length;i++){node.contents[position+i]=buffer[offset+i]}}node.usedBytes=Math.max(node.usedBytes,position+length);return length},llseek:function(stream,offset,whence){var position=offset;if(whence===1){position+=stream.position}else if(whence===2){if(FS.isFile(stream.node.mode)){position+=stream.node.usedBytes}}if(position<0){throw new FS.ErrnoError(28)}return position},allocate:function(stream,offset,length){MEMFS.expandFileStorage(stream.node,offset+length);stream.node.usedBytes=Math.max(stream.node.usedBytes,offset+length)},mmap:function(stream,address,length,position,prot,flags){assert(address===0);if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}var ptr;var allocated;var contents=stream.node.contents;if(!(flags&2)&&contents.buffer===buffer){allocated=false;ptr=contents.byteOffset}else{if(position>0||position+length<contents.length){if(contents.subarray){contents=contents.subarray(position,position+length)}else{contents=Array.prototype.slice.call(contents,position,position+length)}}allocated=true;ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}HEAP8.set(contents,ptr)}return{ptr:ptr,allocated:allocated}},msync:function(stream,buffer,offset,length,mmapFlags){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(mmapFlags&2){return 0}var bytesWritten=MEMFS.stream_ops.write(stream,buffer,0,length,offset,false);return 0}}};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,trackingDelegate:{},tracking:{openFlags:{READ:1,WRITE:2}},ErrnoError:null,genericErrors:{},filesystems:null,syncFSRequests:0,lookupPath:function(path,opts){path=PATH_FS.resolve(FS.cwd(),path);opts=opts||{};if(!path)return{path:"",node:null};var defaults={follow_mount:true,recurse_count:0};for(var key in defaults){if(opts[key]===undefined){opts[key]=defaults[key]}}if(opts.recurse_count>8){throw new FS.ErrnoError(32)}var parts=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),false);var current=FS.root;var current_path="/";for(var i=0;i<parts.length;i++){var islast=i===parts.length-1;if(islast&&opts.parent){break}current=FS.lookupNode(current,parts[i]);current_path=PATH.join2(current_path,parts[i]);if(FS.isMountpoint(current)){if(!islast||islast&&opts.follow_mount){current=current.mounted.root}}if(!islast||opts.follow){var count=0;while(FS.isLink(current.mode)){var link=FS.readlink(current_path);current_path=PATH_FS.resolve(PATH.dirname(current_path),link);var lookup=FS.lookupPath(current_path,{recurse_count:opts.recurse_count});current=lookup.node;if(count++>40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath:function(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?mount+"/"+path:mount+path}path=path?node.name+"/"+path:node.name;node=node.parent}},hashName:function(parentid,name){var hash=0;for(var i=0;i<name.length;i++){hash=(hash<<5)-hash+name.charCodeAt(i)|0}return(parentid+hash>>>0)%FS.nameTable.length},hashAddNode:function(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode:function(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode:function(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode,parent)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode:function(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode:function(node){FS.hashRemoveNode(node)},isRoot:function(node){return node===node.parent},isMountpoint:function(node){return!!node.mounted},isFile:function(mode){return(mode&61440)===32768},isDir:function(mode){return(mode&61440)===16384},isLink:function(mode){return(mode&61440)===40960},isChrdev:function(mode){return(mode&61440)===8192},isBlkdev:function(mode){return(mode&61440)===24576},isFIFO:function(mode){return(mode&61440)===4096},isSocket:function(mode){return(mode&49152)===49152},flagModes:{"r":0,"r+":2,"w":577,"w+":578,"a":1089,"a+":1090},modeStringToFlags:function(str){var flags=FS.flagModes[str];if(typeof flags==="undefined"){throw new Error("Unknown file open mode: "+str)}return flags},flagsToPermissionString:function(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions:function(node,perms){if(FS.ignorePermissions){return 0}if(perms.indexOf("r")!==-1&&!(node.mode&292)){return 2}else if(perms.indexOf("w")!==-1&&!(node.mode&146)){return 2}else if(perms.indexOf("x")!==-1&&!(node.mode&73)){return 2}return 0},mayLookup:function(dir){var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate:function(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete:function(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen:function(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd:function(fd_start,fd_end){fd_start=fd_start||0;fd_end=fd_end||FS.MAX_OPEN_FDS;for(var fd=fd_start;fd<=fd_end;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStream:function(fd){return FS.streams[fd]},createStream:function(stream,fd_start,fd_end){if(!FS.FSStream){FS.FSStream=function(){};FS.FSStream.prototype={object:{get:function(){return this.node},set:function(val){this.node=val}},isRead:{get:function(){return(this.flags&2097155)!==1}},isWrite:{get:function(){return(this.flags&2097155)!==0}},isAppend:{get:function(){return this.flags&1024}}}}var newStream=new FS.FSStream;for(var p in stream){newStream[p]=stream[p]}stream=newStream;var fd=FS.nextfd(fd_start,fd_end);stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream:function(fd){FS.streams[fd]=null},chrdev_stream_ops:{open:function(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;if(stream.stream_ops.open){stream.stream_ops.open(stream)}},llseek:function(){throw new FS.ErrnoError(70)}},major:function(dev){return dev>>8},minor:function(dev){return dev&255},makedev:function(ma,mi){return ma<<8|mi},registerDevice:function(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:function(dev){return FS.devices[dev]},getMounts:function(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push.apply(check,m.mounts)}return mounts},syncfs:function(populate,callback){if(typeof populate==="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err("warning: "+FS.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work")}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(function(mount){if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount:function(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount:function(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(function(hash){var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.indexOf(current.mount)!==-1){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup:function(parent,name){return parent.node_ops.lookup(parent,name)},mknod:function(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create:function(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir:function(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree:function(path,mode){var dirs=path.split("/");var d="";for(var i=0;i<dirs.length;++i){if(!dirs[i])continue;d+="/"+dirs[i];try{FS.mkdir(d,mode)}catch(e){if(e.errno!=20)throw e}}},mkdev:function(path,mode,dev){if(typeof dev==="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink:function(oldpath,newpath){if(!PATH_FS.resolve(oldpath)){throw new FS.ErrnoError(44)}var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.symlink){throw new FS.ErrnoError(63)}return parent.node_ops.symlink(parent,newname,oldpath)},rename:function(old_path,new_path){var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node;if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount){throw new FS.ErrnoError(75)}var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(28)}relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(55)}var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node){return}var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode){throw new FS.ErrnoError(errCode)}errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode){throw new FS.ErrnoError(errCode)}if(!old_dir.node_ops.rename){throw new FS.ErrnoError(63)}if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node)){throw new FS.ErrnoError(10)}if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode){throw new FS.ErrnoError(errCode)}}try{if(FS.trackingDelegate["willMovePath"]){FS.trackingDelegate["willMovePath"](old_path,new_path)}}catch(e){err("FS.trackingDelegate['willMovePath']('"+old_path+"', '"+new_path+"') threw an exception: "+e.message)}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name)}catch(e){throw e}finally{FS.hashAddNode(old_node)}try{if(FS.trackingDelegate["onMovePath"])FS.trackingDelegate["onMovePath"](old_path,new_path)}catch(e){err("FS.trackingDelegate['onMovePath']('"+old_path+"', '"+new_path+"') threw an exception: "+e.message)}},rmdir:function(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.rmdir){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}try{if(FS.trackingDelegate["willDeletePath"]){FS.trackingDelegate["willDeletePath"](path)}}catch(e){err("FS.trackingDelegate['willDeletePath']('"+path+"') threw an exception: "+e.message)}parent.node_ops.rmdir(parent,name);FS.destroyNode(node);try{if(FS.trackingDelegate["onDeletePath"])FS.trackingDelegate["onDeletePath"](path)}catch(e){err("FS.trackingDelegate['onDeletePath']('"+path+"') threw an exception: "+e.message)}},readdir:function(path){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;if(!node.node_ops.readdir){throw new FS.ErrnoError(54)}return node.node_ops.readdir(node)},unlink:function(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.unlink){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}try{if(FS.trackingDelegate["willDeletePath"]){FS.trackingDelegate["willDeletePath"](path)}}catch(e){err("FS.trackingDelegate['willDeletePath']('"+path+"') threw an exception: "+e.message)}parent.node_ops.unlink(parent,name);FS.destroyNode(node);try{if(FS.trackingDelegate["onDeletePath"])FS.trackingDelegate["onDeletePath"](path)}catch(e){err("FS.trackingDelegate['onDeletePath']('"+path+"') threw an exception: "+e.message)}},readlink:function(path){var lookup=FS.lookupPath(path);var link=lookup.node;if(!link){throw new FS.ErrnoError(44)}if(!link.node_ops.readlink){throw new FS.ErrnoError(28)}return PATH_FS.resolve(FS.getPath(link.parent),link.node_ops.readlink(link))},stat:function(path,dontFollow){var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;if(!node){throw new FS.ErrnoError(44)}if(!node.node_ops.getattr){throw new FS.ErrnoError(63)}return node.node_ops.getattr(node)},lstat:function(path){return FS.stat(path,true)},chmod:function(path,mode,dontFollow){var node;if(typeof path==="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}node.node_ops.setattr(node,{mode:mode&4095|node.mode&~4095,timestamp:Date.now()})},lchmod:function(path,mode){FS.chmod(path,mode,true)},fchmod:function(fd,mode){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}FS.chmod(stream.node,mode)},chown:function(path,uid,gid,dontFollow){var node;if(typeof path==="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}node.node_ops.setattr(node,{timestamp:Date.now()})},lchown:function(path,uid,gid){FS.chown(path,uid,gid,true)},fchown:function(fd,uid,gid){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}FS.chown(stream.node,uid,gid)},truncate:function(path,len){if(len<0){throw new FS.ErrnoError(28)}var node;if(typeof path==="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}if(FS.isDir(node.mode)){throw new FS.ErrnoError(31)}if(!FS.isFile(node.mode)){throw new FS.ErrnoError(28)}var errCode=FS.nodePermissions(node,"w");if(errCode){throw new FS.ErrnoError(errCode)}node.node_ops.setattr(node,{size:len,timestamp:Date.now()})},ftruncate:function(fd,len){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(28)}FS.truncate(stream.node,len)},utime:function(path,atime,mtime){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;node.node_ops.setattr(node,{timestamp:Math.max(atime,mtime)})},open:function(path,flags,mode,fd_start,fd_end){if(path===""){throw new FS.ErrnoError(44)}flags=typeof flags==="string"?FS.modeStringToFlags(flags):flags;mode=typeof mode==="undefined"?438:mode;if(flags&64){mode=mode&4095|32768}else{mode=0}var node;if(typeof path==="object"){node=path}else{path=PATH.normalize(path);try{var lookup=FS.lookupPath(path,{follow:!(flags&131072)});node=lookup.node}catch(e){}}var created=false;if(flags&64){if(node){if(flags&128){throw new FS.ErrnoError(20)}}else{node=FS.mknod(path,mode,0);created=true}}if(!node){throw new FS.ErrnoError(44)}if(FS.isChrdev(node.mode)){flags&=~512}if(flags&65536&&!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}if(!created){var errCode=FS.mayOpen(node,flags);if(errCode){throw new FS.ErrnoError(errCode)}}if(flags&512){FS.truncate(node,0)}flags&=~(128|512|131072);var stream=FS.createStream({node:node,path:FS.getPath(node),flags:flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false},fd_start,fd_end);if(stream.stream_ops.open){stream.stream_ops.open(stream)}if(Module["logReadFiles"]&&!(flags&1)){if(!FS.readFiles)FS.readFiles={};if(!(path in FS.readFiles)){FS.readFiles[path]=1;err("FS.trackingDelegate error on read file: "+path)}}try{if(FS.trackingDelegate["onOpenFile"]){var trackingFlags=0;if((flags&2097155)!==1){trackingFlags|=FS.tracking.openFlags.READ}if((flags&2097155)!==0){trackingFlags|=FS.tracking.openFlags.WRITE}FS.trackingDelegate["onOpenFile"](path,trackingFlags)}}catch(e){err("FS.trackingDelegate['onOpenFile']('"+path+"', flags) threw an exception: "+e.message)}return stream},close:function(stream){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(stream.getdents)stream.getdents=null;try{if(stream.stream_ops.close){stream.stream_ops.close(stream)}}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed:function(stream){return stream.fd===null},llseek:function(stream,offset,whence){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(!stream.seekable||!stream.stream_ops.llseek){throw new FS.ErrnoError(70)}if(whence!=0&&whence!=1&&whence!=2){throw new FS.ErrnoError(28)}stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read:function(stream,buffer,offset,length,position){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.read){throw new FS.ErrnoError(28)}var seeking=typeof position!=="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write:function(stream,buffer,offset,length,position,canOwn){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.write){throw new FS.ErrnoError(28)}if(stream.seekable&&stream.flags&1024){FS.llseek(stream,0,2)}var seeking=typeof position!=="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;try{if(stream.path&&FS.trackingDelegate["onWriteToFile"])FS.trackingDelegate["onWriteToFile"](stream.path)}catch(e){err("FS.trackingDelegate['onWriteToFile']('"+stream.path+"') threw an exception: "+e.message)}return bytesWritten},allocate:function(stream,offset,length){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(offset<0||length<=0){throw new FS.ErrnoError(28)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(!FS.isFile(stream.node.mode)&&!FS.isDir(stream.node.mode)){throw new FS.ErrnoError(43)}if(!stream.stream_ops.allocate){throw new FS.ErrnoError(138)}stream.stream_ops.allocate(stream,offset,length)},mmap:function(stream,address,length,position,prot,flags){if((prot&2)!==0&&(flags&2)===0&&(stream.flags&2097155)!==2){throw new FS.ErrnoError(2)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(2)}if(!stream.stream_ops.mmap){throw new FS.ErrnoError(43)}return stream.stream_ops.mmap(stream,address,length,position,prot,flags)},msync:function(stream,buffer,offset,length,mmapFlags){if(!stream||!stream.stream_ops.msync){return 0}return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},munmap:function(stream){return 0},ioctl:function(stream,cmd,arg){if(!stream.stream_ops.ioctl){throw new FS.ErrnoError(59)}return stream.stream_ops.ioctl(stream,cmd,arg)},readFile:function(path,opts){opts=opts||{};opts.flags=opts.flags||0;opts.encoding=opts.encoding||"binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary"){throw new Error('Invalid encoding type "'+opts.encoding+'"')}var ret;var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8"){ret=UTF8ArrayToString(buf,0)}else if(opts.encoding==="binary"){ret=buf}FS.close(stream);return ret},writeFile:function(path,data,opts){opts=opts||{};opts.flags=opts.flags||577;var stream=FS.open(path,opts.flags,opts.mode);if(typeof data==="string"){var buf=new Uint8Array(lengthBytesUTF8(data)+1);var actualNumBytes=stringToUTF8Array(data,buf,0,buf.length);FS.write(stream,buf,0,actualNumBytes,undefined,opts.canOwn)}else if(ArrayBuffer.isView(data)){FS.write(stream,data,0,data.byteLength,undefined,opts.canOwn)}else{throw new Error("Unsupported data type")}FS.close(stream)},cwd:function(){return FS.currentPath},chdir:function(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories:function(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices:function(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:function(){return 0},write:function(stream,buffer,offset,length,pos){return length}});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var random_device=getRandomDevice();FS.createDevice("/dev","random",random_device);FS.createDevice("/dev","urandom",random_device);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories:function(){FS.mkdir("/proc");FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount:function(){var node=FS.createNode("/proc/self","fd",16384|511,73);node.node_ops={lookup:function(parent,name){var fd=+name;var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:function(){return stream.path}}};ret.parent=ret;return ret}};return node}},{},"/proc/self/fd")},createStandardStreams:function(){if(Module["stdin"]){FS.createDevice("/dev","stdin",Module["stdin"])}else{FS.symlink("/dev/tty","/dev/stdin")}if(Module["stdout"]){FS.createDevice("/dev","stdout",null,Module["stdout"])}else{FS.symlink("/dev/tty","/dev/stdout")}if(Module["stderr"]){FS.createDevice("/dev","stderr",null,Module["stderr"])}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},ensureErrnoError:function(){if(FS.ErrnoError)return;FS.ErrnoError=function ErrnoError(errno,node){this.node=node;this.setErrno=function(errno){this.errno=errno};this.setErrno(errno);this.message="FS error"};FS.ErrnoError.prototype=new Error;FS.ErrnoError.prototype.constructor=FS.ErrnoError;[44].forEach(function(code){FS.genericErrors[code]=new FS.ErrnoError(code);FS.genericErrors[code].stack="<generic error, no stack>"})},staticInit:function(){FS.ensureErrnoError();FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={"MEMFS":MEMFS}},init:function(input,output,error){FS.init.initialized=true;FS.ensureErrnoError();Module["stdin"]=input||Module["stdin"];Module["stdout"]=output||Module["stdout"];Module["stderr"]=error||Module["stderr"];FS.createStandardStreams()},quit:function(){FS.init.initialized=false;var fflush=Module["_fflush"];if(fflush)fflush(0);for(var i=0;i<FS.streams.length;i++){var stream=FS.streams[i];if(!stream){continue}FS.close(stream)}},getMode:function(canRead,canWrite){var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode},findObject:function(path,dontResolveLastLink){var ret=FS.analyzePath(path,dontResolveLastLink);if(ret.exists){return ret.object}else{return null}},analyzePath:function(path,dontResolveLastLink){try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{var lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createPath:function(parent,path,canRead,canWrite){parent=typeof parent==="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){}parent=current}return current},createFile:function(parent,name,properties,canRead,canWrite){var path=PATH.join2(typeof parent==="string"?parent:FS.getPath(parent),name);var mode=FS.getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile:function(parent,name,data,canRead,canWrite,canOwn){var path=name?PATH.join2(typeof parent==="string"?parent:FS.getPath(parent),name):parent;var mode=FS.getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){if(typeof data==="string"){var arr=new Array(data.length);for(var i=0,len=data.length;i<len;++i)arr[i]=data.charCodeAt(i);data=arr}FS.chmod(node,mode|146);var stream=FS.open(node,577);FS.write(stream,data,0,data.length,0,canOwn);FS.close(stream);FS.chmod(node,mode)}return node},createDevice:function(parent,name,input,output){var path=PATH.join2(typeof parent==="string"?parent:FS.getPath(parent),name);var mode=FS.getMode(!!input,!!output);if(!FS.createDevice.major)FS.createDevice.major=64;var dev=FS.makedev(FS.createDevice.major++,0);FS.registerDevice(dev,{open:function(stream){stream.seekable=false},close:function(stream){if(output&&output.buffer&&output.buffer.length){output(10)}},read:function(stream,buffer,offset,length,pos){var bytesRead=0;for(var i=0;i<length;i++){var result;try{result=input()}catch(e){throw new FS.ErrnoError(29)}if(result===undefined&&bytesRead===0){throw new FS.ErrnoError(6)}if(result===null||result===undefined)break;bytesRead++;buffer[offset+i]=result}if(bytesRead){stream.node.timestamp=Date.now()}return bytesRead},write:function(stream,buffer,offset,length,pos){for(var i=0;i<length;i++){try{output(buffer[offset+i])}catch(e){throw new FS.ErrnoError(29)}}if(length){stream.node.timestamp=Date.now()}return i}});return FS.mkdev(path,mode,dev)},forceLoadFile:function(obj){if(obj.isDevice||obj.isFolder||obj.link||obj.contents)return true;if(typeof XMLHttpRequest!=="undefined"){throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.")}else if(read_){try{obj.contents=intArrayFromString(read_(obj.url),true);obj.usedBytes=obj.contents.length}catch(e){throw new FS.ErrnoError(29)}}else{throw new Error("Cannot load without read() or XMLHttpRequest.")}},createLazyFile:function(parent,name,url,canRead,canWrite){function LazyUint8Array(){this.lengthKnown=false;this.chunks=[]}LazyUint8Array.prototype.get=function LazyUint8Array_get(idx){if(idx>this.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]};LazyUint8Array.prototype.setDataGetter=function LazyUint8Array_setDataGetter(getter){this.getter=getter};LazyUint8Array.prototype.cacheLength=function LazyUint8Array_cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=function(from,to){if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);if(typeof Uint8Array!="undefined")xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}else{return intArrayFromString(xhr.responseText||"",true)}};var lazyArray=this;lazyArray.setDataGetter(function(chunkNum){var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]==="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]==="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true};if(typeof XMLHttpRequest!=="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;Object.defineProperties(lazyArray,{length:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._length}},chunkSize:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}});var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(function(key){var fn=node.stream_ops[key];stream_ops[key]=function forceLoadLazyFile(){FS.forceLoadFile(node);return fn.apply(null,arguments)}});stream_ops.read=function stream_ops_read(stream,buffer,offset,length,position){FS.forceLoadFile(node);var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i<size;i++){buffer[offset+i]=contents[position+i]}}else{for(var i=0;i<size;i++){buffer[offset+i]=contents.get(position+i)}}return size};node.stream_ops=stream_ops;return node},createPreloadedFile:function(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish){Browser.init();var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency("cp "+fullname);function processData(byteArray){function finish(byteArray){if(preFinish)preFinish();if(!dontCreateFile){FS.createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}if(onload)onload();removeRunDependency(dep)}var handled=false;Module["preloadPlugins"].forEach(function(plugin){if(handled)return;if(plugin["canHandle"](fullname)){plugin["handle"](byteArray,fullname,finish,function(){if(onerror)onerror();removeRunDependency(dep)});handled=true}});if(!handled)finish(byteArray)}addRunDependency(dep);if(typeof url=="string"){Browser.asyncLoad(url,function(byteArray){processData(byteArray)},onerror)}else{processData(url)}},indexedDB:function(){return window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB},DB_NAME:function(){return"EM_FS_"+window.location.pathname},DB_VERSION:20,DB_STORE_NAME:"FILE_DATA",saveFilesToDB:function(paths,onload,onerror){onload=onload||function(){};onerror=onerror||function(){};var indexedDB=FS.indexedDB();try{var openRequest=indexedDB.open(FS.DB_NAME(),FS.DB_VERSION)}catch(e){return onerror(e)}openRequest.onupgradeneeded=function openRequest_onupgradeneeded(){out("creating db");var db=openRequest.result;db.createObjectStore(FS.DB_STORE_NAME)};openRequest.onsuccess=function openRequest_onsuccess(){var db=openRequest.result;var transaction=db.transaction([FS.DB_STORE_NAME],"readwrite");var files=transaction.objectStore(FS.DB_STORE_NAME);var ok=0,fail=0,total=paths.length;function finish(){if(fail==0)onload();else onerror()}paths.forEach(function(path){var putRequest=files.put(FS.analyzePath(path).object.contents,path);putRequest.onsuccess=function putRequest_onsuccess(){ok++;if(ok+fail==total)finish()};putRequest.onerror=function putRequest_onerror(){fail++;if(ok+fail==total)finish()}});transaction.onerror=onerror};openRequest.onerror=onerror},loadFilesFromDB:function(paths,onload,onerror){onload=onload||function(){};onerror=onerror||function(){};var indexedDB=FS.indexedDB();try{var openRequest=indexedDB.open(FS.DB_NAME(),FS.DB_VERSION)}catch(e){return onerror(e)}openRequest.onupgradeneeded=onerror;openRequest.onsuccess=function openRequest_onsuccess(){var db=openRequest.result;try{var transaction=db.transaction([FS.DB_STORE_NAME],"readonly")}catch(e){onerror(e);return}var files=transaction.objectStore(FS.DB_STORE_NAME);var ok=0,fail=0,total=paths.length;function finish(){if(fail==0)onload();else onerror()}paths.forEach(function(path){var getRequest=files.get(path);getRequest.onsuccess=function getRequest_onsuccess(){if(FS.analyzePath(path).exists){FS.unlink(path)}FS.createDataFile(PATH.dirname(path),PATH.basename(path),getRequest.result,true,true,true);ok++;if(ok+fail==total)finish()};getRequest.onerror=function getRequest_onerror(){fail++;if(ok+fail==total)finish()}});transaction.onerror=onerror};openRequest.onerror=onerror}};var SYSCALLS={mappings:{},DEFAULT_POLLMASK:5,umask:511,calculateAt:function(dirfd,path){if(path[0]!=="/"){var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=FS.getStream(dirfd);if(!dirstream)throw new FS.ErrnoError(8);dir=dirstream.path}path=PATH.join2(dir,path)}return path},doStat:function(func,path,buf){try{var stat=func(path)}catch(e){if(e&&e.node&&PATH.normalize(path)!==PATH.normalize(FS.getPath(e.node))){return-54}throw e}HEAP32[buf>>2]=stat.dev;HEAP32[buf+4>>2]=0;HEAP32[buf+8>>2]=stat.ino;HEAP32[buf+12>>2]=stat.mode;HEAP32[buf+16>>2]=stat.nlink;HEAP32[buf+20>>2]=stat.uid;HEAP32[buf+24>>2]=stat.gid;HEAP32[buf+28>>2]=stat.rdev;HEAP32[buf+32>>2]=0;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAP32[buf+48>>2]=4096;HEAP32[buf+52>>2]=stat.blocks;HEAP32[buf+56>>2]=stat.atime.getTime()/1e3|0;HEAP32[buf+60>>2]=0;HEAP32[buf+64>>2]=stat.mtime.getTime()/1e3|0;HEAP32[buf+68>>2]=0;HEAP32[buf+72>>2]=stat.ctime.getTime()/1e3|0;HEAP32[buf+76>>2]=0;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+80>>2]=tempI64[0],HEAP32[buf+84>>2]=tempI64[1];return 0},doMsync:function(addr,stream,len,flags,offset){var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},doMkdir:function(path,mode){path=PATH.normalize(path);if(path[path.length-1]==="/")path=path.substr(0,path.length-1);FS.mkdir(path,mode,0);return 0},doMknod:function(path,mode,dev){switch(mode&61440){case 32768:case 8192:case 24576:case 4096:case 49152:break;default:return-28}FS.mknod(path,mode,dev);return 0},doReadlink:function(path,buf,bufsize){if(bufsize<=0)return-28;var ret=FS.readlink(path);var len=Math.min(bufsize,lengthBytesUTF8(ret));var endChar=HEAP8[buf+len];stringToUTF8(ret,buf,bufsize+1);HEAP8[buf+len]=endChar;return len},doAccess:function(path,amode){if(amode&~7){return-28}var node;var lookup=FS.lookupPath(path,{follow:true});node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0},doDup:function(path,flags,suggestFD){var suggest=FS.getStream(suggestFD);if(suggest)FS.close(suggest);return FS.open(path,flags,0,suggestFD,suggestFD).fd},doReadv:function(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i<iovcnt;i++){var ptr=HEAP32[iov+i*8>>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr<len)break}return ret},doWritev:function(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i<iovcnt;i++){var ptr=HEAP32[iov+i*8>>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr}return ret},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},getStreamFromFD:function(fd){var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);return stream},get64:function(low,high){return low}};function ___sys_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=SYSCALLS.get();if(arg<0){return-28}var newStream;newStream=FS.open(stream.path,stream.flags,0,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=SYSCALLS.get();stream.flags|=arg;return 0}case 12:{var arg=SYSCALLS.get();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0;case 16:case 8:return-28;case 9:setErrNo(28);return-1;default:{return-28}}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:case 21505:{if(!stream.tty)return-59;return 0}case 21510:case 21511:case 21512:case 21506:case 21507:case 21508:{if(!stream.tty)return-59;return 0}case 21519:{if(!stream.tty)return-59;var argp=SYSCALLS.get();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=SYSCALLS.get();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;return 0}case 21524:{if(!stream.tty)return-59;return 0}default:abort("bad ioctl syscall "+op)}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_open(path,flags,varargs){SYSCALLS.varargs=varargs;try{var pathname=SYSCALLS.getStr(path);var mode=SYSCALLS.get();var stream=FS.open(pathname,flags,mode);return stream.fd}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}var structRegistrations={};function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function makeLegalFunctionName(name){if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return"_"+name}else{return name}}function createNamedFunction(name,body){name=makeLegalFunctionName(name);return new Function("body","return function "+name+"() {\n"+'    "use strict";'+"    return body.apply(this, arguments);\n"+"};\n")(body)}function extendError(baseErrorType,errorName){var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return this.name+": "+this.message}};return errorClass}var InternalError=undefined;function throwInternalError(message){throw new InternalError(message)}function whenDependentTypesAreResolved(myTypes,dependentTypes,getTypeConverters){myTypes.forEach(function(type){typeDependencies[type]=dependentTypes});function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i<myTypes.length;++i){registerType(myTypes[i],myTypeConverters[i])}}var typeConverters=new Array(dependentTypes.length);var unregisteredTypes=[];var registered=0;dependentTypes.forEach(function(dt,i){if(registeredTypes.hasOwnProperty(dt)){typeConverters[i]=registeredTypes[dt]}else{unregisteredTypes.push(dt);if(!awaitingDependencies.hasOwnProperty(dt)){awaitingDependencies[dt]=[]}awaitingDependencies[dt].push(function(){typeConverters[i]=registeredTypes[dt];++registered;if(registered===unregisteredTypes.length){onComplete(typeConverters)}})}});if(0===unregisteredTypes.length){onComplete(typeConverters)}}function __embind_finalize_value_object(structType){var reg=structRegistrations[structType];delete structRegistrations[structType];var rawConstructor=reg.rawConstructor;var rawDestructor=reg.rawDestructor;var fieldRecords=reg.fields;var fieldTypes=fieldRecords.map(function(field){return field.getterReturnType}).concat(fieldRecords.map(function(field){return field.setterArgumentType}));whenDependentTypesAreResolved([structType],fieldTypes,function(fieldTypes){var fields={};fieldRecords.forEach(function(field,i){var fieldName=field.fieldName;var getterReturnType=fieldTypes[i];var getter=field.getter;var getterContext=field.getterContext;var setterArgumentType=fieldTypes[i+fieldRecords.length];var setter=field.setter;var setterContext=field.setterContext;fields[fieldName]={read:function(ptr){return getterReturnType["fromWireType"](getter(getterContext,ptr))},write:function(ptr,o){var destructors=[];setter(setterContext,ptr,setterArgumentType["toWireType"](destructors,o));runDestructors(destructors)}}});return[{name:reg.name,"fromWireType":function(ptr){var rv={};for(var i in fields){rv[i]=fields[i].read(ptr)}rawDestructor(ptr);return rv},"toWireType":function(destructors,o){for(var fieldName in fields){if(!(fieldName in o)){throw new TypeError('Missing field:  "'+fieldName+'"')}}var ptr=rawConstructor();for(fieldName in fields){fields[fieldName].write(ptr,o[fieldName])}if(destructors!==null){destructors.push(rawDestructor,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:rawDestructor}]})}function getShiftFromSize(size){switch(size){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+size)}}function embind_init_charCodes(){var codes=new Array(256);for(var i=0;i<256;++i){codes[i]=String.fromCharCode(i)}embind_charCodes=codes}var embind_charCodes=undefined;function readLatin1String(ptr){var ret="";var c=ptr;while(HEAPU8[c]){ret+=embind_charCodes[HEAPU8[c++]]}return ret}var BindingError=undefined;function throwBindingError(message){throw new BindingError(message)}function registerType(rawType,registeredInstance,options){options=options||{};if(!("argPackAdvance"in registeredInstance)){throw new TypeError("registerType registeredInstance requires argPackAdvance")}var name=registeredInstance.name;if(!rawType){throwBindingError('type "'+name+'" must have a positive integer typeid pointer')}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError("Cannot register type '"+name+"' twice")}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(function(cb){cb()})}}function __embind_register_bool(rawType,name,size,trueValue,falseValue){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(wt){return!!wt},"toWireType":function(destructors,o){return o?trueValue:falseValue},"argPackAdvance":8,"readValueFromPointer":function(pointer){var heap;if(size===1){heap=HEAP8}else if(size===2){heap=HEAP16}else if(size===4){heap=HEAP32}else{throw new TypeError("Unknown boolean type size: "+name)}return this["fromWireType"](heap[pointer>>shift])},destructorFunction:null})}function ClassHandle_isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right}function shallowCopyInternalPointer(o){return{count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType}}function throwInstanceAlreadyDeleted(obj){function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")}var finalizationGroup=false;function detachFinalizer(handle){}function runDestructor($$){if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}}function releaseClassHandle($$){$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}}function attachFinalizer(handle){if("undefined"===typeof FinalizationGroup){attachFinalizer=function(handle){return handle};return handle}finalizationGroup=new FinalizationGroup(function(iter){for(var result=iter.next();!result.done;result=iter.next()){var $$=result.value;if(!$$.ptr){console.warn("object already deleted: "+$$.ptr)}else{releaseClassHandle($$)}}});attachFinalizer=function(handle){finalizationGroup.register(handle,handle.$$,handle.$$);return handle};detachFinalizer=function(handle){finalizationGroup.unregister(handle.$$)};return attachFinalizer(handle)}function ClassHandle_clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}}function ClassHandle_delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}}function ClassHandle_isDeleted(){return!this.$$.ptr}var delayFunction=undefined;var deletionQueue=[];function flushPendingDeletes(){while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}}function ClassHandle_deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}function init_ClassHandle(){ClassHandle.prototype["isAliasOf"]=ClassHandle_isAliasOf;ClassHandle.prototype["clone"]=ClassHandle_clone;ClassHandle.prototype["delete"]=ClassHandle_delete;ClassHandle.prototype["isDeleted"]=ClassHandle_isDeleted;ClassHandle.prototype["deleteLater"]=ClassHandle_deleteLater}function ClassHandle(){}var registeredPointers={};function ensureOverloadTable(proto,methodName,humanName){if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(){if(!proto[methodName].overloadTable.hasOwnProperty(arguments.length)){throwBindingError("Function '"+humanName+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+proto[methodName].overloadTable+")!")}return proto[methodName].overloadTable[arguments.length].apply(this,arguments)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}}function exposePublicSymbol(name,value,numArguments){if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError("Cannot register public name '"+name+"' twice")}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError("Cannot register multiple overloads of a function with the same number of arguments ("+numArguments+")!")}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}}function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}function upcastPointer(ptr,ptrClass,desiredClass){while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError("Expected null or instance of "+desiredClass.name+", got an instance of "+ptrClass.name)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr}function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,__emval_register(function(){clonedHandle["delete"]()}));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupporting sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+handle.$$.ptrType.name+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function RegisteredPointer_getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr}function RegisteredPointer_destructor(ptr){if(this.rawDestructor){this.rawDestructor(ptr)}}function RegisteredPointer_deleteObject(handle){if(handle!==null){handle["delete"]()}}function downcastPointer(ptr,ptrClass,desiredClass){if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)}function getInheritedInstanceCount(){return Object.keys(registeredInstances).length}function getLiveInheritedInstances(){var rv=[];for(var k in registeredInstances){if(registeredInstances.hasOwnProperty(k)){rv.push(registeredInstances[k])}}return rv}function setDelayFunction(fn){delayFunction=fn;if(deletionQueue.length&&delayFunction){delayFunction(flushPendingDeletes)}}function init_embind(){Module["getInheritedInstanceCount"]=getInheritedInstanceCount;Module["getLiveInheritedInstances"]=getLiveInheritedInstances;Module["flushPendingDeletes"]=flushPendingDeletes;Module["setDelayFunction"]=setDelayFunction}var registeredInstances={};function getBasestPointer(class_,ptr){if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr}function getInheritedInstance(class_,ptr){ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]}function makeClassHandle(prototype,record){if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record}}))}function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr:ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}function init_RegisteredPointer(){RegisteredPointer.prototype.getPointee=RegisteredPointer_getPointee;RegisteredPointer.prototype.destructor=RegisteredPointer_destructor;RegisteredPointer.prototype["argPackAdvance"]=8;RegisteredPointer.prototype["readValueFromPointer"]=simpleReadValueFromPointer;RegisteredPointer.prototype["deleteObject"]=RegisteredPointer_deleteObject;RegisteredPointer.prototype["fromWireType"]=RegisteredPointer_fromWireType}function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&&registeredClass.baseClass===undefined){if(isConst){this["toWireType"]=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this["toWireType"]=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this["toWireType"]=genericPointerToWireType}}function replacePublicSymbol(name,value,numArguments){if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistant public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}}function dynCallLegacy(sig,ptr,args){if(args&&args.length){return Module["dynCall_"+sig].apply(null,[ptr].concat(args))}return Module["dynCall_"+sig].call(null,ptr)}function dynCall(sig,ptr,args){if(sig.indexOf("j")!=-1){return dynCallLegacy(sig,ptr,args)}return wasmTable.get(ptr).apply(null,args)}function getDynCaller(sig,ptr){assert(sig.indexOf("j")>=0,"getDynCaller should only be called with i64 sigs");var argCache=[];return function(){argCache.length=arguments.length;for(var i=0;i<arguments.length;i++){argCache[i]=arguments[i]}return dynCall(sig,ptr,argCache)}}function embind__requireFunction(signature,rawFunction){signature=readLatin1String(signature);function makeDynCaller(){if(signature.indexOf("j")!=-1){return getDynCaller(signature,rawFunction)}return wasmTable.get(rawFunction)}var fp=makeDynCaller();if(typeof fp!=="function"){throwBindingError("unknown function pointer with signature "+signature+": "+rawFunction)}return fp}var UnboundTypeError=undefined;function getTypeName(type){var ptr=___getTypeName(type);var rv=readLatin1String(ptr);_free(ptr);return rv}function throwUnboundTypeError(message,types){var unboundTypes=[];var seen={};function visit(type){if(seen[type]){return}if(registeredTypes[type]){return}if(typeDependencies[type]){typeDependencies[type].forEach(visit);return}unboundTypes.push(type);seen[type]=true}types.forEach(visit);throw new UnboundTypeError(message+": "+unboundTypes.map(getTypeName).join([", "]))}function __embind_register_class(rawType,rawPointerType,rawConstPointerType,baseClassRawType,getActualTypeSignature,getActualType,upcastSignature,upcast,downcastSignature,downcast,name,destructorSignature,rawDestructor){name=readLatin1String(name);getActualType=embind__requireFunction(getActualTypeSignature,getActualType);if(upcast){upcast=embind__requireFunction(upcastSignature,upcast)}if(downcast){downcast=embind__requireFunction(downcastSignature,downcast)}rawDestructor=embind__requireFunction(destructorSignature,rawDestructor);var legalFunctionName=makeLegalFunctionName(name);exposePublicSymbol(legalFunctionName,function(){throwUnboundTypeError("Cannot construct "+name+" due to unbound types",[baseClassRawType])});whenDependentTypesAreResolved([rawType,rawPointerType,rawConstPointerType],baseClassRawType?[baseClassRawType]:[],function(base){base=base[0];var baseClass;var basePrototype;if(baseClassRawType){baseClass=base.registeredClass;basePrototype=baseClass.instancePrototype}else{basePrototype=ClassHandle.prototype}var constructor=createNamedFunction(legalFunctionName,function(){if(Object.getPrototypeOf(this)!==instancePrototype){throw new BindingError("Use 'new' to construct "+name)}if(undefined===registeredClass.constructor_body){throw new BindingError(name+" has no accessible constructor")}var body=registeredClass.constructor_body[arguments.length];if(undefined===body){throw new BindingError("Tried to invoke ctor of "+name+" with invalid number of parameters ("+arguments.length+") - expected ("+Object.keys(registeredClass.constructor_body).toString()+") parameters instead!")}return body.apply(this,arguments)});var instancePrototype=Object.create(basePrototype,{constructor:{value:constructor}});constructor.prototype=instancePrototype;var registeredClass=new RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast);var referenceConverter=new RegisteredPointer(name,registeredClass,true,false,false);var pointerConverter=new RegisteredPointer(name+"*",registeredClass,false,false,false);var constPointerConverter=new RegisteredPointer(name+" const*",registeredClass,false,true,false);registeredPointers[rawType]={pointerType:pointerConverter,constPointerType:constPointerConverter};replacePublicSymbol(legalFunctionName,constructor);return[referenceConverter,pointerConverter,constPointerConverter]})}function heap32VectorToArray(count,firstElement){var array=[];for(var i=0;i<count;i++){array.push(HEAP32[(firstElement>>2)+i])}return array}function __embind_register_class_constructor(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor){assert(argCount>0);var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);var args=[rawConstructor];var destructors=[];whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName="constructor "+classType.name;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError("Cannot register multiple constructors with identical number of parameters ("+(argCount-1)+") for class '"+classType.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!")}classType.registeredClass.constructor_body[argCount-1]=function unboundTypeHandler(){throwUnboundTypeError("Cannot construct "+classType.name+" due to unbound types",rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){classType.registeredClass.constructor_body[argCount-1]=function constructor_body(){if(arguments.length!==argCount-1){throwBindingError(humanName+" called with "+arguments.length+" arguments, expected "+(argCount-1))}destructors.length=0;args.length=argCount;for(var i=1;i<argCount;++i){args[i]=argTypes[i]["toWireType"](destructors,arguments[i-1])}var ptr=invoker.apply(null,args);runDestructors(destructors);return argTypes[0]["fromWireType"](ptr)};return[]});return[]})}function new_(constructor,argumentList){if(!(constructor instanceof Function)){throw new TypeError("new_ called with constructor type "+typeof constructor+" which is not a function")}var dummy=createNamedFunction(constructor.name||"unknownFunctionName",function(){});dummy.prototype=constructor.prototype;var obj=new dummy;var r=constructor.apply(obj,argumentList);return r instanceof Object?r:obj}function craftInvokerFunction(humanName,argTypes,classType,cppInvokerFunc,cppTargetFunc){var argCount=argTypes.length;if(argCount<2){throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!")}var isClassMethodFunc=argTypes[1]!==null&&classType!==null;var needsDestructorStack=false;for(var i=1;i<argTypes.length;++i){if(argTypes[i]!==null&&argTypes[i].destructorFunction===undefined){needsDestructorStack=true;break}}var returns=argTypes[0].name!=="void";var argsList="";var argsListWired="";for(var i=0;i<argCount-2;++i){argsList+=(i!==0?", ":"")+"arg"+i;argsListWired+=(i!==0?", ":"")+"arg"+i+"Wired"}var invokerFnBody="return function "+makeLegalFunctionName(humanName)+"("+argsList+") {\n"+"if (arguments.length !== "+(argCount-2)+") {\n"+"throwBindingError('function "+humanName+" called with ' + arguments.length + ' arguments, expected "+(argCount-2)+" args!');\n"+"}\n";if(needsDestructorStack){invokerFnBody+="var destructors = [];\n"}var dtorStack=needsDestructorStack?"destructors":"null";var args1=["throwBindingError","invoker","fn","runDestructors","retType","classParam"];var args2=[throwBindingError,cppInvokerFunc,cppTargetFunc,runDestructors,argTypes[0],argTypes[1]];if(isClassMethodFunc){invokerFnBody+="var thisWired = classParam.toWireType("+dtorStack+", this);\n"}for(var i=0;i<argCount-2;++i){invokerFnBody+="var arg"+i+"Wired = argType"+i+".toWireType("+dtorStack+", arg"+i+"); // "+argTypes[i+2].name+"\n";args1.push("argType"+i);args2.push(argTypes[i+2])}if(isClassMethodFunc){argsListWired="thisWired"+(argsListWired.length>0?", ":"")+argsListWired}invokerFnBody+=(returns?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\n"}else{for(var i=isClassMethodFunc?1:2;i<argTypes.length;++i){var paramName=i===1?"thisWired":"arg"+(i-2)+"Wired";if(argTypes[i].destructorFunction!==null){invokerFnBody+=paramName+"_dtor("+paramName+"); // "+argTypes[i].name+"\n";args1.push(paramName+"_dtor");args2.push(argTypes[i].destructorFunction)}}}if(returns){invokerFnBody+="var ret = retType.fromWireType(rv);\n"+"return ret;\n"}else{}invokerFnBody+="}\n";args1.push(invokerFnBody);var invokerFunction=new_(Function,args1).apply(null,args2);return invokerFunction}function __embind_register_class_function(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,context,isPureVirtual){var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=readLatin1String(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker);whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName=classType.name+"."+methodName;if(isPureVirtual){classType.registeredClass.pureVirtualFunctions.push(methodName)}function unboundTypesHandler(){throwUnboundTypeError("Cannot call "+humanName+" due to unbound types",rawArgTypes)}var proto=classType.registeredClass.instancePrototype;var method=proto[methodName];if(undefined===method||undefined===method.overloadTable&&method.className!==classType.name&&method.argCount===argCount-2){unboundTypesHandler.argCount=argCount-2;unboundTypesHandler.className=classType.name;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-2]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){var memberFunction=craftInvokerFunction(humanName,argTypes,classType,rawInvoker,context);if(undefined===proto[methodName].overloadTable){memberFunction.argCount=argCount-2;proto[methodName]=memberFunction}else{proto[methodName].overloadTable[argCount-2]=memberFunction}return[]});return[]})}function __embind_register_constant(name,type,value){name=readLatin1String(name);whenDependentTypesAreResolved([],[type],function(type){type=type[0];Module[name]=type["fromWireType"](value);return[]})}var emval_free_list=[];var emval_handle_array=[{},{value:undefined},{value:null},{value:true},{value:false}];function __emval_decref(handle){if(handle>4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i<emval_handle_array.length;++i){if(emval_handle_array[i]!==undefined){++count}}return count}function get_first_emval(){for(var i=5;i<emval_handle_array.length;++i){if(emval_handle_array[i]!==undefined){return emval_handle_array[i]}}return null}function init_emval(){Module["count_emval_handles"]=count_emval_handles;Module["get_first_emval"]=get_first_emval}function __emval_register(value){switch(value){case undefined:{return 1}case null:{return 2}case true:{return 3}case false:{return 4}default:{var handle=emval_free_list.length?emval_free_list.pop():emval_handle_array.length;emval_handle_array[handle]={refcount:1,value:value};return handle}}}function __embind_register_emval(rawType,name){name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(handle){var rv=emval_handle_array[handle].value;__emval_decref(handle);return rv},"toWireType":function(destructors,value){return __emval_register(value)},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:null})}function enumReadValueFromPointer(name,shift,signed){switch(shift){case 0:return function(pointer){var heap=signed?HEAP8:HEAPU8;return this["fromWireType"](heap[pointer])};case 1:return function(pointer){var heap=signed?HEAP16:HEAPU16;return this["fromWireType"](heap[pointer>>1])};case 2:return function(pointer){var heap=signed?HEAP32:HEAPU32;return this["fromWireType"](heap[pointer>>2])};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_enum(rawType,name,size,isSigned){var shift=getShiftFromSize(size);name=readLatin1String(name);function ctor(){}ctor.values={};registerType(rawType,{name:name,constructor:ctor,"fromWireType":function(c){return this.constructor.values[c]},"toWireType":function(destructors,c){return c.value},"argPackAdvance":8,"readValueFromPointer":enumReadValueFromPointer(name,shift,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor)}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __embind_register_enum_value(rawEnumType,name,enumValue){var enumType=requireRegisteredType(rawEnumType,"enum");name=readLatin1String(name);var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(enumType.name+"_"+name,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value}function _embind_repr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function __embind_register_function(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn){var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError("Cannot call "+name+" due to unbound types",argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn),argCount-1);return[]})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=function(value){return value};if(minRange===0){var bitshift=32-8*size;fromWireType=function(value){return value<<bitshift>>>bitshift}}var isUnsignedType=name.indexOf("unsigned")!=-1;registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}if(value<minRange||value>maxRange){throw new TypeError('Passing a number "'+_embind_repr(value)+'" from JS side to C/C++ side to an argument of type "'+name+'", which is outside the valid range ['+minRange+", "+maxRange+"]!")}return isUnsignedType?value>>>0:value|0},"argPackAdvance":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var str;if(stdStringIsUTF8){var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i;if(i==length||HEAPU8[currentBytePtr]==0){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i<length;++i){a[i]=String.fromCharCode(HEAPU8[value+4+i])}str=a.join("")}_free(value);return str},"toWireType":function(destructors,value){if(value instanceof ArrayBuffer){value=new Uint8Array(value)}var getLength;var valueIsOfTypeString=typeof value==="string";if(!(valueIsOfTypeString||value instanceof Uint8Array||value instanceof Uint8ClampedArray||value instanceof Int8Array)){throwBindingError("Cannot pass non-string to std::string")}if(stdStringIsUTF8&&valueIsOfTypeString){getLength=function(){return lengthBytesUTF8(value)}}else{getLength=function(){return value.length}}var length=getLength();var ptr=_malloc(4+length+1);HEAPU32[ptr>>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr+4,length+1)}else{if(valueIsOfTypeString){for(var i=0;i<length;++i){var charCode=value.charCodeAt(i);if(charCode>255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+4+i]=charCode}}else{for(var i=0;i<length;++i){HEAPU8[ptr+4+i]=value[i]}}}if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_std_wstring(rawType,charSize,name){name=readLatin1String(name);var decodeString,encodeString,getHeap,lengthBytesUTF,shift;if(charSize===2){decodeString=UTF16ToString;encodeString=stringToUTF16;lengthBytesUTF=lengthBytesUTF16;getHeap=function(){return HEAPU16};shift=1}else if(charSize===4){decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32;getHeap=function(){return HEAPU32};shift=2}registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var HEAP=getHeap();var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||HEAP[currentBytePtr>>shift]==0){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_free(value);return str},"toWireType":function(destructors,value){if(!(typeof value==="string")){throwBindingError("Cannot pass non-string to C++ string type "+name)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length>>shift;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_value_object(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor){structRegistrations[rawType]={name:readLatin1String(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),fields:[]}}function __embind_register_value_object_field(structType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){structRegistrations[structType].fields.push({fieldName:readLatin1String(fieldName),getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}function requireHandle(handle){if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value}function __emval_as(handle,returnType,destructorsRef){handle=requireHandle(handle);returnType=requireRegisteredType(returnType,"emval::as");var destructors=[];var rd=__emval_register(destructors);HEAP32[destructorsRef>>2]=rd;return returnType["toWireType"](destructors,handle)}var emval_symbols={};function getStringOrSymbol(address){var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}else{return symbol}}var emval_methodCallers=[];function __emval_call_void_method(caller,handle,methodName,args){caller=emval_methodCallers[caller];handle=requireHandle(handle);methodName=getStringOrSymbol(methodName);caller(handle,methodName,null,args)}function emval_get_global(){if(typeof globalThis==="object"){return globalThis}return function(){return Function}()("return this")()}function __emval_get_global(name){if(name===0){return __emval_register(emval_get_global())}else{name=getStringOrSymbol(name);return __emval_register(emval_get_global()[name])}}function __emval_addMethodCaller(caller){var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id}function __emval_lookupTypes(argCount,argTypes){var a=new Array(argCount);for(var i=0;i<argCount;++i){a[i]=requireRegisteredType(HEAP32[(argTypes>>2)+i],"parameter "+i)}return a}function __emval_get_method_caller(argCount,argTypes){var types=__emval_lookupTypes(argCount,argTypes);var retType=types[0];var signatureName=retType.name+"_$"+types.slice(1).map(function(t){return t.name}).join("_")+"$";var params=["retType"];var args=[retType];var argsList="";for(var i=0;i<argCount-1;++i){argsList+=(i!==0?", ":"")+"arg"+i;params.push("argType"+i);args.push(types[1+i])}var functionName=makeLegalFunctionName("methodCaller_"+signatureName);var functionBody="return function "+functionName+"(handle, name, destructors, args) {\n";var offset=0;for(var i=0;i<argCount-1;++i){functionBody+="    var arg"+i+" = argType"+i+".readValueFromPointer(args"+(offset?"+"+offset:"")+");\n";offset+=types[i+1]["argPackAdvance"]}functionBody+="    var rv = handle[name]("+argsList+");\n";for(var i=0;i<argCount-1;++i){if(types[i+1]["deleteObject"]){functionBody+="    argType"+i+".deleteObject(arg"+i+");\n"}}if(!retType.isVoid){functionBody+="    return retType.toWireType(destructors, rv);\n"}functionBody+="};\n";params.push(functionBody);var invokerFunction=new_(Function,params).apply(null,args);return __emval_addMethodCaller(invokerFunction)}function __emval_get_module_property(name){name=getStringOrSymbol(name);return __emval_register(Module[name])}function __emval_get_property(handle,key){handle=requireHandle(handle);key=requireHandle(key);return __emval_register(handle[key])}function __emval_incref(handle){if(handle>4){emval_handle_array[handle].refcount+=1}}function craftEmvalAllocator(argCount){var argsList="";for(var i=0;i<argCount;++i){argsList+=(i!==0?", ":"")+"arg"+i}var functionBody="return function emval_allocator_"+argCount+"(constructor, argTypes, args) {\n";for(var i=0;i<argCount;++i){functionBody+="var argType"+i+" = requireRegisteredType(Module['HEAP32'][(argTypes >>> 2) + "+i+'], "parameter '+i+'");\n'+"var arg"+i+" = argType"+i+".readValueFromPointer(args);\n"+"args += argType"+i+"['argPackAdvance'];\n"}functionBody+="var obj = new constructor("+argsList+");\n"+"return __emval_register(obj);\n"+"}\n";return new Function("requireRegisteredType","Module","__emval_register",functionBody)(requireRegisteredType,Module,__emval_register)}var emval_newers={};function __emval_new(handle,argCount,argTypes,args){handle=requireHandle(handle);var newer=emval_newers[argCount];if(!newer){newer=craftEmvalAllocator(argCount);emval_newers[argCount]=newer}return newer(handle,argTypes,args)}function __emval_new_cstring(v){return __emval_register(getStringOrSymbol(v))}function __emval_run_destructors(handle){var destructors=emval_handle_array[handle].value;runDestructors(destructors);__emval_decref(handle)}function _abort(){abort()}function _longjmp(env,value){_setThrew(env,value||1);throw"longjmp"}function _emscripten_longjmp(a0,a1){return _longjmp(a0,a1)}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_size(){return HEAPU8.length}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;var oldSize=_emscripten_get_heap_size();var maxHeapSize=2147483648;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_read(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doReadv(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){try{var stream=SYSCALLS.getStreamFromFD(fd);var HIGH_OFFSET=4294967296;var offset=offset_high*HIGH_OFFSET+(offset_low>>>0);var DOUBLE_LIMIT=9007199254740992;if(offset<=-DOUBLE_LIMIT||offset>=DOUBLE_LIMIT){return-61}FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doWritev(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _getTempRet0(){return getTempRet0()|0}function _gettimeofday(ptr){var now=Date.now();HEAP32[ptr>>2]=now/1e3|0;HEAP32[ptr+4>>2]=now%1e3*1e3|0;return 0}function _pthread_join(){return 28}function _setTempRet0($i){setTempRet0($i|0)}function _sysconf(name){switch(name){case 30:return 16384;case 85:var maxHeapSize=2147483648;return maxHeapSize/16384;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 80:case 81:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:case 79:return 200809;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1e3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:{if(typeof navigator==="object")return navigator["hardwareConcurrency"]||1;return 1}}setErrNo(28);return-1}var FSNode=function(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev};var readMode=292|73;var writeMode=146;Object.defineProperties(FSNode.prototype,{read:{get:function(){return(this.mode&readMode)===readMode},set:function(val){val?this.mode|=readMode:this.mode&=~readMode}},write:{get:function(){return(this.mode&writeMode)===writeMode},set:function(val){val?this.mode|=writeMode:this.mode&=~writeMode}},isFolder:{get:function(){return FS.isDir(this.mode)}},isDevice:{get:function(){return FS.isChrdev(this.mode)}}});FS.FSNode=FSNode;FS.staticInit();InternalError=Module["InternalError"]=extendError(Error,"InternalError");embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");init_ClassHandle();init_RegisteredPointer();init_embind();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");init_emval();function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}__ATINIT__.push({func:function(){___wasm_call_ctors()}});var asmLibraryArg={"K":___cxa_allocate_exception,"J":___cxa_throw,"G":___sys_fcntl64,"R":___sys_ioctl,"S":___sys_open,"x":__embind_finalize_value_object,"U":__embind_register_bool,"A":__embind_register_class,"z":__embind_register_class_constructor,"a":__embind_register_class_function,"e":__embind_register_constant,"T":__embind_register_emval,"q":__embind_register_enum,"c":__embind_register_enum_value,"H":__embind_register_float,"m":__embind_register_function,"n":__embind_register_integer,"k":__embind_register_memory_view,"I":__embind_register_std_string,"B":__embind_register_std_wstring,"y":__embind_register_value_object,"d":__embind_register_value_object_field,"V":__embind_register_void,"p":__emval_as,"r":__emval_call_void_method,"b":__emval_decref,"D":__emval_get_global,"s":__emval_get_method_caller,"u":__emval_get_module_property,"f":__emval_get_property,"j":__emval_incref,"t":__emval_new,"g":__emval_new_cstring,"o":__emval_run_destructors,"h":_abort,"w":_emscripten_longjmp,"M":_emscripten_memcpy_big,"N":_emscripten_resize_heap,"F":_fd_close,"Q":_fd_read,"L":_fd_seek,"E":_fd_write,"l":_getTempRet0,"C":_gettimeofday,"X":invoke_ii,"v":invoke_vi,"W":invoke_vii,"Y":invoke_viii,"P":_pthread_join,"i":_setTempRet0,"O":_sysconf};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["$"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["aa"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["ba"]).apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return(___getTypeName=Module["___getTypeName"]=Module["asm"]["ca"]).apply(null,arguments)};var ___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=function(){return(___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=Module["asm"]["da"]).apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return(___errno_location=Module["___errno_location"]=Module["asm"]["ea"]).apply(null,arguments)};var stackSave=Module["stackSave"]=function(){return(stackSave=Module["stackSave"]=Module["asm"]["fa"]).apply(null,arguments)};var stackRestore=Module["stackRestore"]=function(){return(stackRestore=Module["stackRestore"]=Module["asm"]["ga"]).apply(null,arguments)};var _setThrew=Module["_setThrew"]=function(){return(_setThrew=Module["_setThrew"]=Module["asm"]["ha"]).apply(null,arguments)};var dynCall_jiiii=Module["dynCall_jiiii"]=function(){return(dynCall_jiiii=Module["dynCall_jiiii"]=Module["asm"]["ia"]).apply(null,arguments)};var dynCall_jiji=Module["dynCall_jiji"]=function(){return(dynCall_jiji=Module["dynCall_jiji"]=Module["asm"]["ja"]).apply(null,arguments)};function invoke_vi(index,a1){var sp=stackSave();try{wasmTable.get(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viii(index,a1,a2,a3){var sp=stackSave();try{wasmTable.get(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_ii(index,a1){var sp=stackSave();try{return wasmTable.get(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{wasmTable.get(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run();
+var Module=typeof BASIS!=="undefined"?BASIS:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var tempRet0=0;var setTempRet0=function(value){tempRet0=value};var getTempRet0=function(){return tempRet0};var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){err("no native wasm support detected")}var wasmMemory;var wasmTable=new WebAssembly.Table({"initial":325,"maximum":325+0,"element":"anyfunc"});var ABORT=false;var EXITSTATUS=0;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx<endPtr){var u0=heap[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heap[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heap[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heap[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function UTF16ToString(ptr,maxBytesToRead){var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder){return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr))}else{var i=0;var str="";while(1){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0||i==maxBytesToRead/2)return str;++i;str+=String.fromCharCode(codeUnit)}}}function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite<str.length*2?maxBytesToWrite/2:str.length;for(var i=0;i<numCharsToWrite;++i){var codeUnit=str.charCodeAt(i);HEAP16[outPtr>>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}function lengthBytesUTF16(str){return str.length*2}function UTF32ToString(ptr,maxBytesToRead){var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str}function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr}function lengthBytesUTF32(str){var len=0;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343)++i;len+=4}return len}var WASM_PAGE_SIZE=65536;function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var DYNAMIC_BASE=5650320,DYNAMICTOP_PTR=407280;var INITIAL_INITIAL_MEMORY=Module["INITIAL_MEMORY"]||299958272;if(Module["wasmMemory"]){wasmMemory=Module["wasmMemory"]}else{wasmMemory=new WebAssembly.Memory({"initial":INITIAL_INITIAL_MEMORY/WASM_PAGE_SIZE,"maximum":2147483648/WASM_PAGE_SIZE})}if(wasmMemory){buffer=wasmMemory.buffer}INITIAL_INITIAL_MEMORY=buffer.byteLength;updateGlobalBufferAndViews(buffer);HEAP32[DYNAMICTOP_PTR>>2]=DYNAMIC_BASE;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Module["dynCall_v"](func)}else{Module["dynCall_vi"](func,callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();TTY.init();callRuntimeCallbacks(__ATINIT__)}function preMain(){FS.ignorePermissions=false;callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var Math_abs=Math.abs;var Math_ceil=Math.ceil;var Math_floor=Math.floor;var Math_min=Math.min;var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";out(what);err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";throw new WebAssembly.RuntimeError(what)}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";function isFileURI(filename){return hasPrefix(filename,fileURIPrefix)}var wasmBinaryFile="basis_encoder.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return new Promise(function(resolve,reject){resolve(getBinary())})}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync();return{}}var tempDouble;var tempI64;__ATINIT__.push({func:function(){___wasm_call_ctors()}});function demangle(func){return func}function demangleAll(text){var regex=/\b_Z[\w\d_]+/g;return text.replace(regex,function(x){var y=demangle(x);return x===y?x:y+" ["+x+"]"})}function jsStackTrace(){var err=new Error;if(!err.stack){try{throw new Error}catch(e){err=e}if(!err.stack){return"(no stack trace available)"}}return err.stack.toString()}function stackTrace(){var js=jsStackTrace();if(Module["extraStackTrace"])js+="\n"+Module["extraStackTrace"]();return demangleAll(js)}function ___cxa_allocate_exception(size){return _malloc(size)}var ___exception_infos={};var ___exception_last=0;function __ZSt18uncaught_exceptionv(){return __ZSt18uncaught_exceptionv.uncaught_exceptions>0}function ___cxa_throw(ptr,type,destructor){___exception_infos[ptr]={ptr:ptr,adjusted:[ptr],type:type,destructor:destructor,refcount:0,caught:false,rethrown:false};___exception_last=ptr;if(!("uncaught_exception"in __ZSt18uncaught_exceptionv)){__ZSt18uncaught_exceptionv.uncaught_exceptions=1}else{__ZSt18uncaught_exceptionv.uncaught_exceptions++}throw ptr}function setErrNo(value){HEAP32[___errno_location()>>2]=value;return value}var PATH={splitPath:function(filename){var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:function(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:function(path){var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:function(path){if(path==="/")return"/";var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},extname:function(path){return PATH.splitPath(path)[3]},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:function(l,r){return PATH.normalize(l+"/"+r)}};var PATH_FS={resolve:function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:FS.cwd();if(typeof path!=="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:function(from,to){from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start<arr.length;start++){if(arr[start]!=="")break}var end=arr.length-1;for(;end>=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i<length;i++){if(fromParts[i]!==toParts[i]){samePartsLength=i;break}}var outputParts=[];for(var i=samePartsLength;i<fromParts.length;i++){outputParts.push("..")}outputParts=outputParts.concat(toParts.slice(samePartsLength));return outputParts.join("/")}};var TTY={ttys:[],init:function(){},shutdown:function(){},register:function(dev,ops){TTY.ttys[dev]={input:[],output:[],ops:ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open:function(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close:function(stream){stream.tty.ops.flush(stream.tty)},flush:function(stream){stream.tty.ops.flush(stream.tty)},read:function(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i<length;i++){var result;try{result=stream.tty.ops.get_char(stream.tty)}catch(e){throw new FS.ErrnoError(29)}if(result===undefined&&bytesRead===0){throw new FS.ErrnoError(6)}if(result===null||result===undefined)break;bytesRead++;buffer[offset+i]=result}if(bytesRead){stream.node.timestamp=Date.now()}return bytesRead},write:function(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.put_char){throw new FS.ErrnoError(60)}try{for(var i=0;i<length;i++){stream.tty.ops.put_char(stream.tty,buffer[offset+i])}}catch(e){throw new FS.ErrnoError(29)}if(length){stream.node.timestamp=Date.now()}return i}},default_tty_ops:{get_char:function(tty){if(!tty.input.length){var result=null;if(ENVIRONMENT_IS_NODE){var BUFSIZE=256;var buf=Buffer.alloc?Buffer.alloc(BUFSIZE):new Buffer(BUFSIZE);var bytesRead=0;try{bytesRead=nodeFS.readSync(process.stdin.fd,buf,0,BUFSIZE,null)}catch(e){if(e.toString().indexOf("EOF")!=-1)bytesRead=0;else throw e}if(bytesRead>0){result=buf.slice(0,bytesRead).toString("utf-8")}else{result=null}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else if(typeof readline=="function"){result=readline();if(result!==null){result+="\n"}}if(!result){return null}tty.input=intArrayFromString(result,true)}return tty.input.shift()},put_char:function(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}}},default_tty1_ops:{put_char:function(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};var MEMFS={ops_table:null,mount:function(mount){return MEMFS.createNode(null,"/",16384|511,0)},createNode:function(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}if(!MEMFS.ops_table){MEMFS.ops_table={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,allocate:MEMFS.stream_ops.allocate,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}}}var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.timestamp=Date.now();if(parent){parent.contents[name]=node}return node},getFileDataAsRegularArray:function(node){if(node.contents&&node.contents.subarray){var arr=[];for(var i=0;i<node.usedBytes;++i)arr.push(node.contents[i]);return arr}return node.contents},getFileDataAsTypedArray:function(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage:function(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity<CAPACITY_DOUBLING_MAX?2:1.125)>>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0);return},resizeFileStorage:function(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0;return}if(!node.contents||node.contents.subarray){var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize;return}if(!node.contents)node.contents=[];if(node.contents.length>newSize)node.contents.length=newSize;else while(node.contents.length<newSize)node.contents.push(0);node.usedBytes=newSize},node_ops:{getattr:function(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr:function(node,attr){if(attr.mode!==undefined){node.mode=attr.mode}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup:function(parent,name){throw FS.genericErrors[44]},mknod:function(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename:function(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.name=new_name;new_dir.contents[new_name]=old_node;old_node.parent=new_dir},unlink:function(parent,name){delete parent.contents[name]},rmdir:function(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name]},readdir:function(node){var entries=[".",".."];for(var key in node.contents){if(!node.contents.hasOwnProperty(key)){continue}entries.push(key)}return entries},symlink:function(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink:function(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read:function(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i<size;i++)buffer[offset+i]=contents[position+i]}return size},write:function(stream,buffer,offset,length,position,canOwn){if(buffer.buffer===HEAP8.buffer){canOwn=false}if(!length)return 0;var node=stream.node;node.timestamp=Date.now();if(buffer.subarray&&(!node.contents||node.contents.subarray)){if(canOwn){node.contents=buffer.subarray(offset,offset+length);node.usedBytes=length;return length}else if(node.usedBytes===0&&position===0){node.contents=buffer.slice(offset,offset+length);node.usedBytes=length;return length}else if(position+length<=node.usedBytes){node.contents.set(buffer.subarray(offset,offset+length),position);return length}}MEMFS.expandFileStorage(node,position+length);if(node.contents.subarray&&buffer.subarray)node.contents.set(buffer.subarray(offset,offset+length),position);else{for(var i=0;i<length;i++){node.contents[position+i]=buffer[offset+i]}}node.usedBytes=Math.max(node.usedBytes,position+length);return length},llseek:function(stream,offset,whence){var position=offset;if(whence===1){position+=stream.position}else if(whence===2){if(FS.isFile(stream.node.mode)){position+=stream.node.usedBytes}}if(position<0){throw new FS.ErrnoError(28)}return position},allocate:function(stream,offset,length){MEMFS.expandFileStorage(stream.node,offset+length);stream.node.usedBytes=Math.max(stream.node.usedBytes,offset+length)},mmap:function(stream,buffer,offset,length,position,prot,flags){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}var ptr;var allocated;var contents=stream.node.contents;if(!(flags&2)&&contents.buffer===buffer.buffer){allocated=false;ptr=contents.byteOffset}else{if(position>0||position+length<contents.length){if(contents.subarray){contents=contents.subarray(position,position+length)}else{contents=Array.prototype.slice.call(contents,position,position+length)}}allocated=true;var fromHeap=buffer.buffer==HEAP8.buffer;ptr=_malloc(length);if(!ptr){throw new FS.ErrnoError(48)}(fromHeap?HEAP8:buffer).set(contents,ptr)}return{ptr:ptr,allocated:allocated}},msync:function(stream,buffer,offset,length,mmapFlags){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(mmapFlags&2){return 0}var bytesWritten=MEMFS.stream_ops.write(stream,buffer,0,length,offset,false);return 0}}};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,trackingDelegate:{},tracking:{openFlags:{READ:1,WRITE:2}},ErrnoError:null,genericErrors:{},filesystems:null,syncFSRequests:0,handleFSError:function(e){if(!(e instanceof FS.ErrnoError))throw e+" : "+stackTrace();return setErrNo(e.errno)},lookupPath:function(path,opts){path=PATH_FS.resolve(FS.cwd(),path);opts=opts||{};if(!path)return{path:"",node:null};var defaults={follow_mount:true,recurse_count:0};for(var key in defaults){if(opts[key]===undefined){opts[key]=defaults[key]}}if(opts.recurse_count>8){throw new FS.ErrnoError(32)}var parts=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),false);var current=FS.root;var current_path="/";for(var i=0;i<parts.length;i++){var islast=i===parts.length-1;if(islast&&opts.parent){break}current=FS.lookupNode(current,parts[i]);current_path=PATH.join2(current_path,parts[i]);if(FS.isMountpoint(current)){if(!islast||islast&&opts.follow_mount){current=current.mounted.root}}if(!islast||opts.follow){var count=0;while(FS.isLink(current.mode)){var link=FS.readlink(current_path);current_path=PATH_FS.resolve(PATH.dirname(current_path),link);var lookup=FS.lookupPath(current_path,{recurse_count:opts.recurse_count});current=lookup.node;if(count++>40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath:function(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?mount+"/"+path:mount+path}path=path?node.name+"/"+path:node.name;node=node.parent}},hashName:function(parentid,name){var hash=0;for(var i=0;i<name.length;i++){hash=(hash<<5)-hash+name.charCodeAt(i)|0}return(parentid+hash>>>0)%FS.nameTable.length},hashAddNode:function(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode:function(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode:function(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode,parent)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode:function(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode:function(node){FS.hashRemoveNode(node)},isRoot:function(node){return node===node.parent},isMountpoint:function(node){return!!node.mounted},isFile:function(mode){return(mode&61440)===32768},isDir:function(mode){return(mode&61440)===16384},isLink:function(mode){return(mode&61440)===40960},isChrdev:function(mode){return(mode&61440)===8192},isBlkdev:function(mode){return(mode&61440)===24576},isFIFO:function(mode){return(mode&61440)===4096},isSocket:function(mode){return(mode&49152)===49152},flagModes:{"r":0,"rs":1052672,"r+":2,"w":577,"wx":705,"xw":705,"w+":578,"wx+":706,"xw+":706,"a":1089,"ax":1217,"xa":1217,"a+":1090,"ax+":1218,"xa+":1218},modeStringToFlags:function(str){var flags=FS.flagModes[str];if(typeof flags==="undefined"){throw new Error("Unknown file open mode: "+str)}return flags},flagsToPermissionString:function(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions:function(node,perms){if(FS.ignorePermissions){return 0}if(perms.indexOf("r")!==-1&&!(node.mode&292)){return 2}else if(perms.indexOf("w")!==-1&&!(node.mode&146)){return 2}else if(perms.indexOf("x")!==-1&&!(node.mode&73)){return 2}return 0},mayLookup:function(dir){var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate:function(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete:function(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen:function(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd:function(fd_start,fd_end){fd_start=fd_start||0;fd_end=fd_end||FS.MAX_OPEN_FDS;for(var fd=fd_start;fd<=fd_end;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStream:function(fd){return FS.streams[fd]},createStream:function(stream,fd_start,fd_end){if(!FS.FSStream){FS.FSStream=function(){};FS.FSStream.prototype={object:{get:function(){return this.node},set:function(val){this.node=val}},isRead:{get:function(){return(this.flags&2097155)!==1}},isWrite:{get:function(){return(this.flags&2097155)!==0}},isAppend:{get:function(){return this.flags&1024}}}}var newStream=new FS.FSStream;for(var p in stream){newStream[p]=stream[p]}stream=newStream;var fd=FS.nextfd(fd_start,fd_end);stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream:function(fd){FS.streams[fd]=null},chrdev_stream_ops:{open:function(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;if(stream.stream_ops.open){stream.stream_ops.open(stream)}},llseek:function(){throw new FS.ErrnoError(70)}},major:function(dev){return dev>>8},minor:function(dev){return dev&255},makedev:function(ma,mi){return ma<<8|mi},registerDevice:function(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:function(dev){return FS.devices[dev]},getMounts:function(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push.apply(check,m.mounts)}return mounts},syncfs:function(populate,callback){if(typeof populate==="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err("warning: "+FS.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work")}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(function(mount){if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount:function(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount:function(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(function(hash){var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.indexOf(current.mount)!==-1){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup:function(parent,name){return parent.node_ops.lookup(parent,name)},mknod:function(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create:function(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir:function(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree:function(path,mode){var dirs=path.split("/");var d="";for(var i=0;i<dirs.length;++i){if(!dirs[i])continue;d+="/"+dirs[i];try{FS.mkdir(d,mode)}catch(e){if(e.errno!=20)throw e}}},mkdev:function(path,mode,dev){if(typeof dev==="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink:function(oldpath,newpath){if(!PATH_FS.resolve(oldpath)){throw new FS.ErrnoError(44)}var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.symlink){throw new FS.ErrnoError(63)}return parent.node_ops.symlink(parent,newname,oldpath)},rename:function(old_path,new_path){var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;try{lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node}catch(e){throw new FS.ErrnoError(10)}if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount){throw new FS.ErrnoError(75)}var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(28)}relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(55)}var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node){return}var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode){throw new FS.ErrnoError(errCode)}errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode){throw new FS.ErrnoError(errCode)}if(!old_dir.node_ops.rename){throw new FS.ErrnoError(63)}if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node)){throw new FS.ErrnoError(10)}if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode){throw new FS.ErrnoError(errCode)}}try{if(FS.trackingDelegate["willMovePath"]){FS.trackingDelegate["willMovePath"](old_path,new_path)}}catch(e){err("FS.trackingDelegate['willMovePath']('"+old_path+"', '"+new_path+"') threw an exception: "+e.message)}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name)}catch(e){throw e}finally{FS.hashAddNode(old_node)}try{if(FS.trackingDelegate["onMovePath"])FS.trackingDelegate["onMovePath"](old_path,new_path)}catch(e){err("FS.trackingDelegate['onMovePath']('"+old_path+"', '"+new_path+"') threw an exception: "+e.message)}},rmdir:function(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.rmdir){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}try{if(FS.trackingDelegate["willDeletePath"]){FS.trackingDelegate["willDeletePath"](path)}}catch(e){err("FS.trackingDelegate['willDeletePath']('"+path+"') threw an exception: "+e.message)}parent.node_ops.rmdir(parent,name);FS.destroyNode(node);try{if(FS.trackingDelegate["onDeletePath"])FS.trackingDelegate["onDeletePath"](path)}catch(e){err("FS.trackingDelegate['onDeletePath']('"+path+"') threw an exception: "+e.message)}},readdir:function(path){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;if(!node.node_ops.readdir){throw new FS.ErrnoError(54)}return node.node_ops.readdir(node)},unlink:function(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.unlink){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}try{if(FS.trackingDelegate["willDeletePath"]){FS.trackingDelegate["willDeletePath"](path)}}catch(e){err("FS.trackingDelegate['willDeletePath']('"+path+"') threw an exception: "+e.message)}parent.node_ops.unlink(parent,name);FS.destroyNode(node);try{if(FS.trackingDelegate["onDeletePath"])FS.trackingDelegate["onDeletePath"](path)}catch(e){err("FS.trackingDelegate['onDeletePath']('"+path+"') threw an exception: "+e.message)}},readlink:function(path){var lookup=FS.lookupPath(path);var link=lookup.node;if(!link){throw new FS.ErrnoError(44)}if(!link.node_ops.readlink){throw new FS.ErrnoError(28)}return PATH_FS.resolve(FS.getPath(link.parent),link.node_ops.readlink(link))},stat:function(path,dontFollow){var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;if(!node){throw new FS.ErrnoError(44)}if(!node.node_ops.getattr){throw new FS.ErrnoError(63)}return node.node_ops.getattr(node)},lstat:function(path){return FS.stat(path,true)},chmod:function(path,mode,dontFollow){var node;if(typeof path==="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}node.node_ops.setattr(node,{mode:mode&4095|node.mode&~4095,timestamp:Date.now()})},lchmod:function(path,mode){FS.chmod(path,mode,true)},fchmod:function(fd,mode){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}FS.chmod(stream.node,mode)},chown:function(path,uid,gid,dontFollow){var node;if(typeof path==="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}node.node_ops.setattr(node,{timestamp:Date.now()})},lchown:function(path,uid,gid){FS.chown(path,uid,gid,true)},fchown:function(fd,uid,gid){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}FS.chown(stream.node,uid,gid)},truncate:function(path,len){if(len<0){throw new FS.ErrnoError(28)}var node;if(typeof path==="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}if(FS.isDir(node.mode)){throw new FS.ErrnoError(31)}if(!FS.isFile(node.mode)){throw new FS.ErrnoError(28)}var errCode=FS.nodePermissions(node,"w");if(errCode){throw new FS.ErrnoError(errCode)}node.node_ops.setattr(node,{size:len,timestamp:Date.now()})},ftruncate:function(fd,len){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(28)}FS.truncate(stream.node,len)},utime:function(path,atime,mtime){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;node.node_ops.setattr(node,{timestamp:Math.max(atime,mtime)})},open:function(path,flags,mode,fd_start,fd_end){if(path===""){throw new FS.ErrnoError(44)}flags=typeof flags==="string"?FS.modeStringToFlags(flags):flags;mode=typeof mode==="undefined"?438:mode;if(flags&64){mode=mode&4095|32768}else{mode=0}var node;if(typeof path==="object"){node=path}else{path=PATH.normalize(path);try{var lookup=FS.lookupPath(path,{follow:!(flags&131072)});node=lookup.node}catch(e){}}var created=false;if(flags&64){if(node){if(flags&128){throw new FS.ErrnoError(20)}}else{node=FS.mknod(path,mode,0);created=true}}if(!node){throw new FS.ErrnoError(44)}if(FS.isChrdev(node.mode)){flags&=~512}if(flags&65536&&!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}if(!created){var errCode=FS.mayOpen(node,flags);if(errCode){throw new FS.ErrnoError(errCode)}}if(flags&512){FS.truncate(node,0)}flags&=~(128|512|131072);var stream=FS.createStream({node:node,path:FS.getPath(node),flags:flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false},fd_start,fd_end);if(stream.stream_ops.open){stream.stream_ops.open(stream)}if(Module["logReadFiles"]&&!(flags&1)){if(!FS.readFiles)FS.readFiles={};if(!(path in FS.readFiles)){FS.readFiles[path]=1;err("FS.trackingDelegate error on read file: "+path)}}try{if(FS.trackingDelegate["onOpenFile"]){var trackingFlags=0;if((flags&2097155)!==1){trackingFlags|=FS.tracking.openFlags.READ}if((flags&2097155)!==0){trackingFlags|=FS.tracking.openFlags.WRITE}FS.trackingDelegate["onOpenFile"](path,trackingFlags)}}catch(e){err("FS.trackingDelegate['onOpenFile']('"+path+"', flags) threw an exception: "+e.message)}return stream},close:function(stream){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(stream.getdents)stream.getdents=null;try{if(stream.stream_ops.close){stream.stream_ops.close(stream)}}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed:function(stream){return stream.fd===null},llseek:function(stream,offset,whence){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(!stream.seekable||!stream.stream_ops.llseek){throw new FS.ErrnoError(70)}if(whence!=0&&whence!=1&&whence!=2){throw new FS.ErrnoError(28)}stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read:function(stream,buffer,offset,length,position){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.read){throw new FS.ErrnoError(28)}var seeking=typeof position!=="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write:function(stream,buffer,offset,length,position,canOwn){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.write){throw new FS.ErrnoError(28)}if(stream.seekable&&stream.flags&1024){FS.llseek(stream,0,2)}var seeking=typeof position!=="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;try{if(stream.path&&FS.trackingDelegate["onWriteToFile"])FS.trackingDelegate["onWriteToFile"](stream.path)}catch(e){err("FS.trackingDelegate['onWriteToFile']('"+stream.path+"') threw an exception: "+e.message)}return bytesWritten},allocate:function(stream,offset,length){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(offset<0||length<=0){throw new FS.ErrnoError(28)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(!FS.isFile(stream.node.mode)&&!FS.isDir(stream.node.mode)){throw new FS.ErrnoError(43)}if(!stream.stream_ops.allocate){throw new FS.ErrnoError(138)}stream.stream_ops.allocate(stream,offset,length)},mmap:function(stream,buffer,offset,length,position,prot,flags){if((prot&2)!==0&&(flags&2)===0&&(stream.flags&2097155)!==2){throw new FS.ErrnoError(2)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(2)}if(!stream.stream_ops.mmap){throw new FS.ErrnoError(43)}return stream.stream_ops.mmap(stream,buffer,offset,length,position,prot,flags)},msync:function(stream,buffer,offset,length,mmapFlags){if(!stream||!stream.stream_ops.msync){return 0}return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},munmap:function(stream){return 0},ioctl:function(stream,cmd,arg){if(!stream.stream_ops.ioctl){throw new FS.ErrnoError(59)}return stream.stream_ops.ioctl(stream,cmd,arg)},readFile:function(path,opts){opts=opts||{};opts.flags=opts.flags||"r";opts.encoding=opts.encoding||"binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary"){throw new Error('Invalid encoding type "'+opts.encoding+'"')}var ret;var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8"){ret=UTF8ArrayToString(buf,0)}else if(opts.encoding==="binary"){ret=buf}FS.close(stream);return ret},writeFile:function(path,data,opts){opts=opts||{};opts.flags=opts.flags||"w";var stream=FS.open(path,opts.flags,opts.mode);if(typeof data==="string"){var buf=new Uint8Array(lengthBytesUTF8(data)+1);var actualNumBytes=stringToUTF8Array(data,buf,0,buf.length);FS.write(stream,buf,0,actualNumBytes,undefined,opts.canOwn)}else if(ArrayBuffer.isView(data)){FS.write(stream,data,0,data.byteLength,undefined,opts.canOwn)}else{throw new Error("Unsupported data type")}FS.close(stream)},cwd:function(){return FS.currentPath},chdir:function(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories:function(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices:function(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:function(){return 0},write:function(stream,buffer,offset,length,pos){return length}});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var random_device;if(typeof crypto==="object"&&typeof crypto["getRandomValues"]==="function"){var randomBuffer=new Uint8Array(1);random_device=function(){crypto.getRandomValues(randomBuffer);return randomBuffer[0]}}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");random_device=function(){return crypto_module["randomBytes"](1)[0]}}catch(e){}}else{}if(!random_device){random_device=function(){abort("random_device")}}FS.createDevice("/dev","random",random_device);FS.createDevice("/dev","urandom",random_device);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories:function(){FS.mkdir("/proc");FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount:function(){var node=FS.createNode("/proc/self","fd",16384|511,73);node.node_ops={lookup:function(parent,name){var fd=+name;var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:function(){return stream.path}}};ret.parent=ret;return ret}};return node}},{},"/proc/self/fd")},createStandardStreams:function(){if(Module["stdin"]){FS.createDevice("/dev","stdin",Module["stdin"])}else{FS.symlink("/dev/tty","/dev/stdin")}if(Module["stdout"]){FS.createDevice("/dev","stdout",null,Module["stdout"])}else{FS.symlink("/dev/tty","/dev/stdout")}if(Module["stderr"]){FS.createDevice("/dev","stderr",null,Module["stderr"])}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin","r");var stdout=FS.open("/dev/stdout","w");var stderr=FS.open("/dev/stderr","w")},ensureErrnoError:function(){if(FS.ErrnoError)return;FS.ErrnoError=function ErrnoError(errno,node){this.node=node;this.setErrno=function(errno){this.errno=errno};this.setErrno(errno);this.message="FS error"};FS.ErrnoError.prototype=new Error;FS.ErrnoError.prototype.constructor=FS.ErrnoError;[44].forEach(function(code){FS.genericErrors[code]=new FS.ErrnoError(code);FS.genericErrors[code].stack="<generic error, no stack>"})},staticInit:function(){FS.ensureErrnoError();FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={"MEMFS":MEMFS}},init:function(input,output,error){FS.init.initialized=true;FS.ensureErrnoError();Module["stdin"]=input||Module["stdin"];Module["stdout"]=output||Module["stdout"];Module["stderr"]=error||Module["stderr"];FS.createStandardStreams()},quit:function(){FS.init.initialized=false;var fflush=Module["_fflush"];if(fflush)fflush(0);for(var i=0;i<FS.streams.length;i++){var stream=FS.streams[i];if(!stream){continue}FS.close(stream)}},getMode:function(canRead,canWrite){var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode},joinPath:function(parts,forceRelative){var path=PATH.join.apply(null,parts);if(forceRelative&&path[0]=="/")path=path.substr(1);return path},absolutePath:function(relative,base){return PATH_FS.resolve(base,relative)},standardizePath:function(path){return PATH.normalize(path)},findObject:function(path,dontResolveLastLink){var ret=FS.analyzePath(path,dontResolveLastLink);if(ret.exists){return ret.object}else{setErrNo(ret.error);return null}},analyzePath:function(path,dontResolveLastLink){try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{var lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createFolder:function(parent,name,canRead,canWrite){var path=PATH.join2(typeof parent==="string"?parent:FS.getPath(parent),name);var mode=FS.getMode(canRead,canWrite);return FS.mkdir(path,mode)},createPath:function(parent,path,canRead,canWrite){parent=typeof parent==="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){}parent=current}return current},createFile:function(parent,name,properties,canRead,canWrite){var path=PATH.join2(typeof parent==="string"?parent:FS.getPath(parent),name);var mode=FS.getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile:function(parent,name,data,canRead,canWrite,canOwn){var path=name?PATH.join2(typeof parent==="string"?parent:FS.getPath(parent),name):parent;var mode=FS.getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){if(typeof data==="string"){var arr=new Array(data.length);for(var i=0,len=data.length;i<len;++i)arr[i]=data.charCodeAt(i);data=arr}FS.chmod(node,mode|146);var stream=FS.open(node,"w");FS.write(stream,data,0,data.length,0,canOwn);FS.close(stream);FS.chmod(node,mode)}return node},createDevice:function(parent,name,input,output){var path=PATH.join2(typeof parent==="string"?parent:FS.getPath(parent),name);var mode=FS.getMode(!!input,!!output);if(!FS.createDevice.major)FS.createDevice.major=64;var dev=FS.makedev(FS.createDevice.major++,0);FS.registerDevice(dev,{open:function(stream){stream.seekable=false},close:function(stream){if(output&&output.buffer&&output.buffer.length){output(10)}},read:function(stream,buffer,offset,length,pos){var bytesRead=0;for(var i=0;i<length;i++){var result;try{result=input()}catch(e){throw new FS.ErrnoError(29)}if(result===undefined&&bytesRead===0){throw new FS.ErrnoError(6)}if(result===null||result===undefined)break;bytesRead++;buffer[offset+i]=result}if(bytesRead){stream.node.timestamp=Date.now()}return bytesRead},write:function(stream,buffer,offset,length,pos){for(var i=0;i<length;i++){try{output(buffer[offset+i])}catch(e){throw new FS.ErrnoError(29)}}if(length){stream.node.timestamp=Date.now()}return i}});return FS.mkdev(path,mode,dev)},createLink:function(parent,name,target,canRead,canWrite){var path=PATH.join2(typeof parent==="string"?parent:FS.getPath(parent),name);return FS.symlink(target,path)},forceLoadFile:function(obj){if(obj.isDevice||obj.isFolder||obj.link||obj.contents)return true;var success=true;if(typeof XMLHttpRequest!=="undefined"){throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.")}else if(read_){try{obj.contents=intArrayFromString(read_(obj.url),true);obj.usedBytes=obj.contents.length}catch(e){success=false}}else{throw new Error("Cannot load without read() or XMLHttpRequest.")}if(!success)setErrNo(29);return success},createLazyFile:function(parent,name,url,canRead,canWrite){function LazyUint8Array(){this.lengthKnown=false;this.chunks=[]}LazyUint8Array.prototype.get=function LazyUint8Array_get(idx){if(idx>this.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]};LazyUint8Array.prototype.setDataGetter=function LazyUint8Array_setDataGetter(getter){this.getter=getter};LazyUint8Array.prototype.cacheLength=function LazyUint8Array_cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=function(from,to){if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);if(typeof Uint8Array!="undefined")xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}else{return intArrayFromString(xhr.responseText||"",true)}};var lazyArray=this;lazyArray.setDataGetter(function(chunkNum){var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]==="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]==="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true};if(typeof XMLHttpRequest!=="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;Object.defineProperties(lazyArray,{length:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._length}},chunkSize:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}});var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(function(key){var fn=node.stream_ops[key];stream_ops[key]=function forceLoadLazyFile(){if(!FS.forceLoadFile(node)){throw new FS.ErrnoError(29)}return fn.apply(null,arguments)}});stream_ops.read=function stream_ops_read(stream,buffer,offset,length,position){if(!FS.forceLoadFile(node)){throw new FS.ErrnoError(29)}var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i<size;i++){buffer[offset+i]=contents[position+i]}}else{for(var i=0;i<size;i++){buffer[offset+i]=contents.get(position+i)}}return size};node.stream_ops=stream_ops;return node},createPreloadedFile:function(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish){Browser.init();var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency("cp "+fullname);function processData(byteArray){function finish(byteArray){if(preFinish)preFinish();if(!dontCreateFile){FS.createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}if(onload)onload();removeRunDependency(dep)}var handled=false;Module["preloadPlugins"].forEach(function(plugin){if(handled)return;if(plugin["canHandle"](fullname)){plugin["handle"](byteArray,fullname,finish,function(){if(onerror)onerror();removeRunDependency(dep)});handled=true}});if(!handled)finish(byteArray)}addRunDependency(dep);if(typeof url=="string"){Browser.asyncLoad(url,function(byteArray){processData(byteArray)},onerror)}else{processData(url)}},indexedDB:function(){return window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB},DB_NAME:function(){return"EM_FS_"+window.location.pathname},DB_VERSION:20,DB_STORE_NAME:"FILE_DATA",saveFilesToDB:function(paths,onload,onerror){onload=onload||function(){};onerror=onerror||function(){};var indexedDB=FS.indexedDB();try{var openRequest=indexedDB.open(FS.DB_NAME(),FS.DB_VERSION)}catch(e){return onerror(e)}openRequest.onupgradeneeded=function openRequest_onupgradeneeded(){out("creating db");var db=openRequest.result;db.createObjectStore(FS.DB_STORE_NAME)};openRequest.onsuccess=function openRequest_onsuccess(){var db=openRequest.result;var transaction=db.transaction([FS.DB_STORE_NAME],"readwrite");var files=transaction.objectStore(FS.DB_STORE_NAME);var ok=0,fail=0,total=paths.length;function finish(){if(fail==0)onload();else onerror()}paths.forEach(function(path){var putRequest=files.put(FS.analyzePath(path).object.contents,path);putRequest.onsuccess=function putRequest_onsuccess(){ok++;if(ok+fail==total)finish()};putRequest.onerror=function putRequest_onerror(){fail++;if(ok+fail==total)finish()}});transaction.onerror=onerror};openRequest.onerror=onerror},loadFilesFromDB:function(paths,onload,onerror){onload=onload||function(){};onerror=onerror||function(){};var indexedDB=FS.indexedDB();try{var openRequest=indexedDB.open(FS.DB_NAME(),FS.DB_VERSION)}catch(e){return onerror(e)}openRequest.onupgradeneeded=onerror;openRequest.onsuccess=function openRequest_onsuccess(){var db=openRequest.result;try{var transaction=db.transaction([FS.DB_STORE_NAME],"readonly")}catch(e){onerror(e);return}var files=transaction.objectStore(FS.DB_STORE_NAME);var ok=0,fail=0,total=paths.length;function finish(){if(fail==0)onload();else onerror()}paths.forEach(function(path){var getRequest=files.get(path);getRequest.onsuccess=function getRequest_onsuccess(){if(FS.analyzePath(path).exists){FS.unlink(path)}FS.createDataFile(PATH.dirname(path),PATH.basename(path),getRequest.result,true,true,true);ok++;if(ok+fail==total)finish()};getRequest.onerror=function getRequest_onerror(){fail++;if(ok+fail==total)finish()}});transaction.onerror=onerror};openRequest.onerror=onerror}};var SYSCALLS={mappings:{},DEFAULT_POLLMASK:5,umask:511,calculateAt:function(dirfd,path){if(path[0]!=="/"){var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=FS.getStream(dirfd);if(!dirstream)throw new FS.ErrnoError(8);dir=dirstream.path}path=PATH.join2(dir,path)}return path},doStat:function(func,path,buf){try{var stat=func(path)}catch(e){if(e&&e.node&&PATH.normalize(path)!==PATH.normalize(FS.getPath(e.node))){return-54}throw e}HEAP32[buf>>2]=stat.dev;HEAP32[buf+4>>2]=0;HEAP32[buf+8>>2]=stat.ino;HEAP32[buf+12>>2]=stat.mode;HEAP32[buf+16>>2]=stat.nlink;HEAP32[buf+20>>2]=stat.uid;HEAP32[buf+24>>2]=stat.gid;HEAP32[buf+28>>2]=stat.rdev;HEAP32[buf+32>>2]=0;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAP32[buf+48>>2]=4096;HEAP32[buf+52>>2]=stat.blocks;HEAP32[buf+56>>2]=stat.atime.getTime()/1e3|0;HEAP32[buf+60>>2]=0;HEAP32[buf+64>>2]=stat.mtime.getTime()/1e3|0;HEAP32[buf+68>>2]=0;HEAP32[buf+72>>2]=stat.ctime.getTime()/1e3|0;HEAP32[buf+76>>2]=0;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+80>>2]=tempI64[0],HEAP32[buf+84>>2]=tempI64[1];return 0},doMsync:function(addr,stream,len,flags,offset){var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},doMkdir:function(path,mode){path=PATH.normalize(path);if(path[path.length-1]==="/")path=path.substr(0,path.length-1);FS.mkdir(path,mode,0);return 0},doMknod:function(path,mode,dev){switch(mode&61440){case 32768:case 8192:case 24576:case 4096:case 49152:break;default:return-28}FS.mknod(path,mode,dev);return 0},doReadlink:function(path,buf,bufsize){if(bufsize<=0)return-28;var ret=FS.readlink(path);var len=Math.min(bufsize,lengthBytesUTF8(ret));var endChar=HEAP8[buf+len];stringToUTF8(ret,buf,bufsize+1);HEAP8[buf+len]=endChar;return len},doAccess:function(path,amode){if(amode&~7){return-28}var node;var lookup=FS.lookupPath(path,{follow:true});node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0},doDup:function(path,flags,suggestFD){var suggest=FS.getStream(suggestFD);if(suggest)FS.close(suggest);return FS.open(path,flags,0,suggestFD,suggestFD).fd},doReadv:function(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i<iovcnt;i++){var ptr=HEAP32[iov+i*8>>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr<len)break}return ret},doWritev:function(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i<iovcnt;i++){var ptr=HEAP32[iov+i*8>>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr}return ret},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},getStreamFromFD:function(fd){var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);return stream},get64:function(low,high){return low}};function ___sys_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=SYSCALLS.get();if(arg<0){return-28}var newStream;newStream=FS.open(stream.path,stream.flags,0,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=SYSCALLS.get();stream.flags|=arg;return 0}case 12:{var arg=SYSCALLS.get();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0;case 16:case 8:return-28;case 9:setErrNo(28);return-1;default:{return-28}}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:case 21505:{if(!stream.tty)return-59;return 0}case 21510:case 21511:case 21512:case 21506:case 21507:case 21508:{if(!stream.tty)return-59;return 0}case 21519:{if(!stream.tty)return-59;var argp=SYSCALLS.get();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=SYSCALLS.get();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;return 0}case 21524:{if(!stream.tty)return-59;return 0}default:abort("bad ioctl syscall "+op)}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_open(path,flags,varargs){SYSCALLS.varargs=varargs;try{var pathname=SYSCALLS.getStr(path);var mode=SYSCALLS.get();var stream=FS.open(pathname,flags,mode);return stream.fd}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}var structRegistrations={};function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function makeLegalFunctionName(name){if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return"_"+name}else{return name}}function createNamedFunction(name,body){name=makeLegalFunctionName(name);return new Function("body","return function "+name+"() {\n"+'    "use strict";'+"    return body.apply(this, arguments);\n"+"};\n")(body)}function extendError(baseErrorType,errorName){var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return this.name+": "+this.message}};return errorClass}var InternalError=undefined;function throwInternalError(message){throw new InternalError(message)}function whenDependentTypesAreResolved(myTypes,dependentTypes,getTypeConverters){myTypes.forEach(function(type){typeDependencies[type]=dependentTypes});function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i<myTypes.length;++i){registerType(myTypes[i],myTypeConverters[i])}}var typeConverters=new Array(dependentTypes.length);var unregisteredTypes=[];var registered=0;dependentTypes.forEach(function(dt,i){if(registeredTypes.hasOwnProperty(dt)){typeConverters[i]=registeredTypes[dt]}else{unregisteredTypes.push(dt);if(!awaitingDependencies.hasOwnProperty(dt)){awaitingDependencies[dt]=[]}awaitingDependencies[dt].push(function(){typeConverters[i]=registeredTypes[dt];++registered;if(registered===unregisteredTypes.length){onComplete(typeConverters)}})}});if(0===unregisteredTypes.length){onComplete(typeConverters)}}function __embind_finalize_value_object(structType){var reg=structRegistrations[structType];delete structRegistrations[structType];var rawConstructor=reg.rawConstructor;var rawDestructor=reg.rawDestructor;var fieldRecords=reg.fields;var fieldTypes=fieldRecords.map(function(field){return field.getterReturnType}).concat(fieldRecords.map(function(field){return field.setterArgumentType}));whenDependentTypesAreResolved([structType],fieldTypes,function(fieldTypes){var fields={};fieldRecords.forEach(function(field,i){var fieldName=field.fieldName;var getterReturnType=fieldTypes[i];var getter=field.getter;var getterContext=field.getterContext;var setterArgumentType=fieldTypes[i+fieldRecords.length];var setter=field.setter;var setterContext=field.setterContext;fields[fieldName]={read:function(ptr){return getterReturnType["fromWireType"](getter(getterContext,ptr))},write:function(ptr,o){var destructors=[];setter(setterContext,ptr,setterArgumentType["toWireType"](destructors,o));runDestructors(destructors)}}});return[{name:reg.name,"fromWireType":function(ptr){var rv={};for(var i in fields){rv[i]=fields[i].read(ptr)}rawDestructor(ptr);return rv},"toWireType":function(destructors,o){for(var fieldName in fields){if(!(fieldName in o)){throw new TypeError("Missing field")}}var ptr=rawConstructor();for(fieldName in fields){fields[fieldName].write(ptr,o[fieldName])}if(destructors!==null){destructors.push(rawDestructor,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:rawDestructor}]})}function getShiftFromSize(size){switch(size){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+size)}}function embind_init_charCodes(){var codes=new Array(256);for(var i=0;i<256;++i){codes[i]=String.fromCharCode(i)}embind_charCodes=codes}var embind_charCodes=undefined;function readLatin1String(ptr){var ret="";var c=ptr;while(HEAPU8[c]){ret+=embind_charCodes[HEAPU8[c++]]}return ret}var BindingError=undefined;function throwBindingError(message){throw new BindingError(message)}function registerType(rawType,registeredInstance,options){options=options||{};if(!("argPackAdvance"in registeredInstance)){throw new TypeError("registerType registeredInstance requires argPackAdvance")}var name=registeredInstance.name;if(!rawType){throwBindingError('type "'+name+'" must have a positive integer typeid pointer')}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError("Cannot register type '"+name+"' twice")}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(function(cb){cb()})}}function __embind_register_bool(rawType,name,size,trueValue,falseValue){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(wt){return!!wt},"toWireType":function(destructors,o){return o?trueValue:falseValue},"argPackAdvance":8,"readValueFromPointer":function(pointer){var heap;if(size===1){heap=HEAP8}else if(size===2){heap=HEAP16}else if(size===4){heap=HEAP32}else{throw new TypeError("Unknown boolean type size: "+name)}return this["fromWireType"](heap[pointer>>shift])},destructorFunction:null})}function ClassHandle_isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right}function shallowCopyInternalPointer(o){return{count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType}}function throwInstanceAlreadyDeleted(obj){function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")}var finalizationGroup=false;function detachFinalizer(handle){}function runDestructor($$){if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}}function releaseClassHandle($$){$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}}function attachFinalizer(handle){if("undefined"===typeof FinalizationGroup){attachFinalizer=function(handle){return handle};return handle}finalizationGroup=new FinalizationGroup(function(iter){for(var result=iter.next();!result.done;result=iter.next()){var $$=result.value;if(!$$.ptr){console.warn("object already deleted: "+$$.ptr)}else{releaseClassHandle($$)}}});attachFinalizer=function(handle){finalizationGroup.register(handle,handle.$$,handle.$$);return handle};detachFinalizer=function(handle){finalizationGroup.unregister(handle.$$)};return attachFinalizer(handle)}function ClassHandle_clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}}function ClassHandle_delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}}function ClassHandle_isDeleted(){return!this.$$.ptr}var delayFunction=undefined;var deletionQueue=[];function flushPendingDeletes(){while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}}function ClassHandle_deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}function init_ClassHandle(){ClassHandle.prototype["isAliasOf"]=ClassHandle_isAliasOf;ClassHandle.prototype["clone"]=ClassHandle_clone;ClassHandle.prototype["delete"]=ClassHandle_delete;ClassHandle.prototype["isDeleted"]=ClassHandle_isDeleted;ClassHandle.prototype["deleteLater"]=ClassHandle_deleteLater}function ClassHandle(){}var registeredPointers={};function ensureOverloadTable(proto,methodName,humanName){if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(){if(!proto[methodName].overloadTable.hasOwnProperty(arguments.length)){throwBindingError("Function '"+humanName+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+proto[methodName].overloadTable+")!")}return proto[methodName].overloadTable[arguments.length].apply(this,arguments)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}}function exposePublicSymbol(name,value,numArguments){if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError("Cannot register public name '"+name+"' twice")}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError("Cannot register multiple overloads of a function with the same number of arguments ("+numArguments+")!")}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}}function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}function upcastPointer(ptr,ptrClass,desiredClass){while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError("Expected null or instance of "+desiredClass.name+", got an instance of "+ptrClass.name)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr}function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,__emval_register(function(){clonedHandle["delete"]()}));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupporting sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+handle.$$.ptrType.name+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function RegisteredPointer_getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr}function RegisteredPointer_destructor(ptr){if(this.rawDestructor){this.rawDestructor(ptr)}}function RegisteredPointer_deleteObject(handle){if(handle!==null){handle["delete"]()}}function downcastPointer(ptr,ptrClass,desiredClass){if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)}function getInheritedInstanceCount(){return Object.keys(registeredInstances).length}function getLiveInheritedInstances(){var rv=[];for(var k in registeredInstances){if(registeredInstances.hasOwnProperty(k)){rv.push(registeredInstances[k])}}return rv}function setDelayFunction(fn){delayFunction=fn;if(deletionQueue.length&&delayFunction){delayFunction(flushPendingDeletes)}}function init_embind(){Module["getInheritedInstanceCount"]=getInheritedInstanceCount;Module["getLiveInheritedInstances"]=getLiveInheritedInstances;Module["flushPendingDeletes"]=flushPendingDeletes;Module["setDelayFunction"]=setDelayFunction}var registeredInstances={};function getBasestPointer(class_,ptr){if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr}function getInheritedInstance(class_,ptr){ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]}function makeClassHandle(prototype,record){if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record}}))}function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr:ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}function init_RegisteredPointer(){RegisteredPointer.prototype.getPointee=RegisteredPointer_getPointee;RegisteredPointer.prototype.destructor=RegisteredPointer_destructor;RegisteredPointer.prototype["argPackAdvance"]=8;RegisteredPointer.prototype["readValueFromPointer"]=simpleReadValueFromPointer;RegisteredPointer.prototype["deleteObject"]=RegisteredPointer_deleteObject;RegisteredPointer.prototype["fromWireType"]=RegisteredPointer_fromWireType}function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&&registeredClass.baseClass===undefined){if(isConst){this["toWireType"]=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this["toWireType"]=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this["toWireType"]=genericPointerToWireType}}function replacePublicSymbol(name,value,numArguments){if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistant public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}}function embind__requireFunction(signature,rawFunction){signature=readLatin1String(signature);function makeDynCaller(dynCall){var args=[];for(var i=1;i<signature.length;++i){args.push("a"+i)}var name="dynCall_"+signature+"_"+rawFunction;var body="return function "+name+"("+args.join(", ")+") {\n";body+="    return dynCall(rawFunction"+(args.length?", ":"")+args.join(", ")+");\n";body+="};\n";return new Function("dynCall","rawFunction",body)(dynCall,rawFunction)}var dc=Module["dynCall_"+signature];var fp=makeDynCaller(dc);if(typeof fp!=="function"){throwBindingError("unknown function pointer with signature "+signature+": "+rawFunction)}return fp}var UnboundTypeError=undefined;function getTypeName(type){var ptr=___getTypeName(type);var rv=readLatin1String(ptr);_free(ptr);return rv}function throwUnboundTypeError(message,types){var unboundTypes=[];var seen={};function visit(type){if(seen[type]){return}if(registeredTypes[type]){return}if(typeDependencies[type]){typeDependencies[type].forEach(visit);return}unboundTypes.push(type);seen[type]=true}types.forEach(visit);throw new UnboundTypeError(message+": "+unboundTypes.map(getTypeName).join([", "]))}function __embind_register_class(rawType,rawPointerType,rawConstPointerType,baseClassRawType,getActualTypeSignature,getActualType,upcastSignature,upcast,downcastSignature,downcast,name,destructorSignature,rawDestructor){name=readLatin1String(name);getActualType=embind__requireFunction(getActualTypeSignature,getActualType);if(upcast){upcast=embind__requireFunction(upcastSignature,upcast)}if(downcast){downcast=embind__requireFunction(downcastSignature,downcast)}rawDestructor=embind__requireFunction(destructorSignature,rawDestructor);var legalFunctionName=makeLegalFunctionName(name);exposePublicSymbol(legalFunctionName,function(){throwUnboundTypeError("Cannot construct "+name+" due to unbound types",[baseClassRawType])});whenDependentTypesAreResolved([rawType,rawPointerType,rawConstPointerType],baseClassRawType?[baseClassRawType]:[],function(base){base=base[0];var baseClass;var basePrototype;if(baseClassRawType){baseClass=base.registeredClass;basePrototype=baseClass.instancePrototype}else{basePrototype=ClassHandle.prototype}var constructor=createNamedFunction(legalFunctionName,function(){if(Object.getPrototypeOf(this)!==instancePrototype){throw new BindingError("Use 'new' to construct "+name)}if(undefined===registeredClass.constructor_body){throw new BindingError(name+" has no accessible constructor")}var body=registeredClass.constructor_body[arguments.length];if(undefined===body){throw new BindingError("Tried to invoke ctor of "+name+" with invalid number of parameters ("+arguments.length+") - expected ("+Object.keys(registeredClass.constructor_body).toString()+") parameters instead!")}return body.apply(this,arguments)});var instancePrototype=Object.create(basePrototype,{constructor:{value:constructor}});constructor.prototype=instancePrototype;var registeredClass=new RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast);var referenceConverter=new RegisteredPointer(name,registeredClass,true,false,false);var pointerConverter=new RegisteredPointer(name+"*",registeredClass,false,false,false);var constPointerConverter=new RegisteredPointer(name+" const*",registeredClass,false,true,false);registeredPointers[rawType]={pointerType:pointerConverter,constPointerType:constPointerConverter};replacePublicSymbol(legalFunctionName,constructor);return[referenceConverter,pointerConverter,constPointerConverter]})}function heap32VectorToArray(count,firstElement){var array=[];for(var i=0;i<count;i++){array.push(HEAP32[(firstElement>>2)+i])}return array}function __embind_register_class_constructor(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor){assert(argCount>0);var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);var args=[rawConstructor];var destructors=[];whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName="constructor "+classType.name;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError("Cannot register multiple constructors with identical number of parameters ("+(argCount-1)+") for class '"+classType.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!")}classType.registeredClass.constructor_body[argCount-1]=function unboundTypeHandler(){throwUnboundTypeError("Cannot construct "+classType.name+" due to unbound types",rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){classType.registeredClass.constructor_body[argCount-1]=function constructor_body(){if(arguments.length!==argCount-1){throwBindingError(humanName+" called with "+arguments.length+" arguments, expected "+(argCount-1))}destructors.length=0;args.length=argCount;for(var i=1;i<argCount;++i){args[i]=argTypes[i]["toWireType"](destructors,arguments[i-1])}var ptr=invoker.apply(null,args);runDestructors(destructors);return argTypes[0]["fromWireType"](ptr)};return[]});return[]})}function new_(constructor,argumentList){if(!(constructor instanceof Function)){throw new TypeError("new_ called with constructor type "+typeof constructor+" which is not a function")}var dummy=createNamedFunction(constructor.name||"unknownFunctionName",function(){});dummy.prototype=constructor.prototype;var obj=new dummy;var r=constructor.apply(obj,argumentList);return r instanceof Object?r:obj}function craftInvokerFunction(humanName,argTypes,classType,cppInvokerFunc,cppTargetFunc){var argCount=argTypes.length;if(argCount<2){throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!")}var isClassMethodFunc=argTypes[1]!==null&&classType!==null;var needsDestructorStack=false;for(var i=1;i<argTypes.length;++i){if(argTypes[i]!==null&&argTypes[i].destructorFunction===undefined){needsDestructorStack=true;break}}var returns=argTypes[0].name!=="void";var argsList="";var argsListWired="";for(var i=0;i<argCount-2;++i){argsList+=(i!==0?", ":"")+"arg"+i;argsListWired+=(i!==0?", ":"")+"arg"+i+"Wired"}var invokerFnBody="return function "+makeLegalFunctionName(humanName)+"("+argsList+") {\n"+"if (arguments.length !== "+(argCount-2)+") {\n"+"throwBindingError('function "+humanName+" called with ' + arguments.length + ' arguments, expected "+(argCount-2)+" args!');\n"+"}\n";if(needsDestructorStack){invokerFnBody+="var destructors = [];\n"}var dtorStack=needsDestructorStack?"destructors":"null";var args1=["throwBindingError","invoker","fn","runDestructors","retType","classParam"];var args2=[throwBindingError,cppInvokerFunc,cppTargetFunc,runDestructors,argTypes[0],argTypes[1]];if(isClassMethodFunc){invokerFnBody+="var thisWired = classParam.toWireType("+dtorStack+", this);\n"}for(var i=0;i<argCount-2;++i){invokerFnBody+="var arg"+i+"Wired = argType"+i+".toWireType("+dtorStack+", arg"+i+"); // "+argTypes[i+2].name+"\n";args1.push("argType"+i);args2.push(argTypes[i+2])}if(isClassMethodFunc){argsListWired="thisWired"+(argsListWired.length>0?", ":"")+argsListWired}invokerFnBody+=(returns?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\n"}else{for(var i=isClassMethodFunc?1:2;i<argTypes.length;++i){var paramName=i===1?"thisWired":"arg"+(i-2)+"Wired";if(argTypes[i].destructorFunction!==null){invokerFnBody+=paramName+"_dtor("+paramName+"); // "+argTypes[i].name+"\n";args1.push(paramName+"_dtor");args2.push(argTypes[i].destructorFunction)}}}if(returns){invokerFnBody+="var ret = retType.fromWireType(rv);\n"+"return ret;\n"}else{}invokerFnBody+="}\n";args1.push(invokerFnBody);var invokerFunction=new_(Function,args1).apply(null,args2);return invokerFunction}function __embind_register_class_function(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,context,isPureVirtual){var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=readLatin1String(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker);whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName=classType.name+"."+methodName;if(isPureVirtual){classType.registeredClass.pureVirtualFunctions.push(methodName)}function unboundTypesHandler(){throwUnboundTypeError("Cannot call "+humanName+" due to unbound types",rawArgTypes)}var proto=classType.registeredClass.instancePrototype;var method=proto[methodName];if(undefined===method||undefined===method.overloadTable&&method.className!==classType.name&&method.argCount===argCount-2){unboundTypesHandler.argCount=argCount-2;unboundTypesHandler.className=classType.name;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-2]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){var memberFunction=craftInvokerFunction(humanName,argTypes,classType,rawInvoker,context);if(undefined===proto[methodName].overloadTable){memberFunction.argCount=argCount-2;proto[methodName]=memberFunction}else{proto[methodName].overloadTable[argCount-2]=memberFunction}return[]});return[]})}function __embind_register_constant(name,type,value){name=readLatin1String(name);whenDependentTypesAreResolved([],[type],function(type){type=type[0];Module[name]=type["fromWireType"](value);return[]})}var emval_free_list=[];var emval_handle_array=[{},{value:undefined},{value:null},{value:true},{value:false}];function __emval_decref(handle){if(handle>4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i<emval_handle_array.length;++i){if(emval_handle_array[i]!==undefined){++count}}return count}function get_first_emval(){for(var i=5;i<emval_handle_array.length;++i){if(emval_handle_array[i]!==undefined){return emval_handle_array[i]}}return null}function init_emval(){Module["count_emval_handles"]=count_emval_handles;Module["get_first_emval"]=get_first_emval}function __emval_register(value){switch(value){case undefined:{return 1}case null:{return 2}case true:{return 3}case false:{return 4}default:{var handle=emval_free_list.length?emval_free_list.pop():emval_handle_array.length;emval_handle_array[handle]={refcount:1,value:value};return handle}}}function __embind_register_emval(rawType,name){name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(handle){var rv=emval_handle_array[handle].value;__emval_decref(handle);return rv},"toWireType":function(destructors,value){return __emval_register(value)},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:null})}function enumReadValueFromPointer(name,shift,signed){switch(shift){case 0:return function(pointer){var heap=signed?HEAP8:HEAPU8;return this["fromWireType"](heap[pointer])};case 1:return function(pointer){var heap=signed?HEAP16:HEAPU16;return this["fromWireType"](heap[pointer>>1])};case 2:return function(pointer){var heap=signed?HEAP32:HEAPU32;return this["fromWireType"](heap[pointer>>2])};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_enum(rawType,name,size,isSigned){var shift=getShiftFromSize(size);name=readLatin1String(name);function ctor(){}ctor.values={};registerType(rawType,{name:name,constructor:ctor,"fromWireType":function(c){return this.constructor.values[c]},"toWireType":function(destructors,c){return c.value},"argPackAdvance":8,"readValueFromPointer":enumReadValueFromPointer(name,shift,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor)}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __embind_register_enum_value(rawEnumType,name,enumValue){var enumType=requireRegisteredType(rawEnumType,"enum");name=readLatin1String(name);var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(enumType.name+"_"+name,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value}function _embind_repr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function __embind_register_function(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn){var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError("Cannot call "+name+" due to unbound types",argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn),argCount-1);return[]})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=function(value){return value};if(minRange===0){var bitshift=32-8*size;fromWireType=function(value){return value<<bitshift>>>bitshift}}var isUnsignedType=name.indexOf("unsigned")!=-1;registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}if(value<minRange||value>maxRange){throw new TypeError('Passing a number "'+_embind_repr(value)+'" from JS side to C/C++ side to an argument of type "'+name+'", which is outside the valid range ['+minRange+", "+maxRange+"]!")}return isUnsignedType?value>>>0:value|0},"argPackAdvance":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var str;if(stdStringIsUTF8){var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i;if(HEAPU8[currentBytePtr]==0||i==length){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i<length;++i){a[i]=String.fromCharCode(HEAPU8[value+4+i])}str=a.join("")}_free(value);return str},"toWireType":function(destructors,value){if(value instanceof ArrayBuffer){value=new Uint8Array(value)}var getLength;var valueIsOfTypeString=typeof value==="string";if(!(valueIsOfTypeString||value instanceof Uint8Array||value instanceof Uint8ClampedArray||value instanceof Int8Array)){throwBindingError("Cannot pass non-string to std::string")}if(stdStringIsUTF8&&valueIsOfTypeString){getLength=function(){return lengthBytesUTF8(value)}}else{getLength=function(){return value.length}}var length=getLength();var ptr=_malloc(4+length+1);HEAPU32[ptr>>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr+4,length+1)}else{if(valueIsOfTypeString){for(var i=0;i<length;++i){var charCode=value.charCodeAt(i);if(charCode>255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+4+i]=charCode}}else{for(var i=0;i<length;++i){HEAPU8[ptr+4+i]=value[i]}}}if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_std_wstring(rawType,charSize,name){name=readLatin1String(name);var decodeString,encodeString,getHeap,lengthBytesUTF,shift;if(charSize===2){decodeString=UTF16ToString;encodeString=stringToUTF16;lengthBytesUTF=lengthBytesUTF16;getHeap=function(){return HEAPU16};shift=1}else if(charSize===4){decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32;getHeap=function(){return HEAPU32};shift=2}registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var HEAP=getHeap();var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(HEAP[currentBytePtr>>shift]==0||i==length){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_free(value);return str},"toWireType":function(destructors,value){if(!(typeof value==="string")){throwBindingError("Cannot pass non-string to C++ string type "+name)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length>>shift;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_value_object(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor){structRegistrations[rawType]={name:readLatin1String(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),fields:[]}}function __embind_register_value_object_field(structType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){structRegistrations[structType].fields.push({fieldName:readLatin1String(fieldName),getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}function requireHandle(handle){if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value}function __emval_as(handle,returnType,destructorsRef){handle=requireHandle(handle);returnType=requireRegisteredType(returnType,"emval::as");var destructors=[];var rd=__emval_register(destructors);HEAP32[destructorsRef>>2]=rd;return returnType["toWireType"](destructors,handle)}var emval_symbols={};function getStringOrSymbol(address){var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}else{return symbol}}var emval_methodCallers=[];function __emval_call_void_method(caller,handle,methodName,args){caller=emval_methodCallers[caller];handle=requireHandle(handle);methodName=getStringOrSymbol(methodName);caller(handle,methodName,null,args)}function emval_get_global(){if(typeof globalThis==="object"){return globalThis}return function(){return Function}()("return this")()}function __emval_get_global(name){if(name===0){return __emval_register(emval_get_global())}else{name=getStringOrSymbol(name);return __emval_register(emval_get_global()[name])}}function __emval_addMethodCaller(caller){var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id}function __emval_lookupTypes(argCount,argTypes){var a=new Array(argCount);for(var i=0;i<argCount;++i){a[i]=requireRegisteredType(HEAP32[(argTypes>>2)+i],"parameter "+i)}return a}function __emval_get_method_caller(argCount,argTypes){var types=__emval_lookupTypes(argCount,argTypes);var retType=types[0];var signatureName=retType.name+"_$"+types.slice(1).map(function(t){return t.name}).join("_")+"$";var params=["retType"];var args=[retType];var argsList="";for(var i=0;i<argCount-1;++i){argsList+=(i!==0?", ":"")+"arg"+i;params.push("argType"+i);args.push(types[1+i])}var functionName=makeLegalFunctionName("methodCaller_"+signatureName);var functionBody="return function "+functionName+"(handle, name, destructors, args) {\n";var offset=0;for(var i=0;i<argCount-1;++i){functionBody+="    var arg"+i+" = argType"+i+".readValueFromPointer(args"+(offset?"+"+offset:"")+");\n";offset+=types[i+1]["argPackAdvance"]}functionBody+="    var rv = handle[name]("+argsList+");\n";for(var i=0;i<argCount-1;++i){if(types[i+1]["deleteObject"]){functionBody+="    argType"+i+".deleteObject(arg"+i+");\n"}}if(!retType.isVoid){functionBody+="    return retType.toWireType(destructors, rv);\n"}functionBody+="};\n";params.push(functionBody);var invokerFunction=new_(Function,params).apply(null,args);return __emval_addMethodCaller(invokerFunction)}function __emval_get_module_property(name){name=getStringOrSymbol(name);return __emval_register(Module[name])}function __emval_get_property(handle,key){handle=requireHandle(handle);key=requireHandle(key);return __emval_register(handle[key])}function __emval_incref(handle){if(handle>4){emval_handle_array[handle].refcount+=1}}function craftEmvalAllocator(argCount){var argsList="";for(var i=0;i<argCount;++i){argsList+=(i!==0?", ":"")+"arg"+i}var functionBody="return function emval_allocator_"+argCount+"(constructor, argTypes, args) {\n";for(var i=0;i<argCount;++i){functionBody+="var argType"+i+" = requireRegisteredType(Module['HEAP32'][(argTypes >>> 2) + "+i+'], "parameter '+i+'");\n'+"var arg"+i+" = argType"+i+".readValueFromPointer(args);\n"+"args += argType"+i+"['argPackAdvance'];\n"}functionBody+="var obj = new constructor("+argsList+");\n"+"return __emval_register(obj);\n"+"}\n";return new Function("requireRegisteredType","Module","__emval_register",functionBody)(requireRegisteredType,Module,__emval_register)}var emval_newers={};function __emval_new(handle,argCount,argTypes,args){handle=requireHandle(handle);var newer=emval_newers[argCount];if(!newer){newer=craftEmvalAllocator(argCount);emval_newers[argCount]=newer}return newer(handle,argTypes,args)}function __emval_new_cstring(v){return __emval_register(getStringOrSymbol(v))}function __emval_run_destructors(handle){var destructors=emval_handle_array[handle].value;runDestructors(destructors);__emval_decref(handle)}function _abort(){abort()}var setjmpId=0;function _saveSetjmp(env,label,table,size){env=env|0;label=label|0;table=table|0;size=size|0;var i=0;setjmpId=setjmpId+1|0;HEAP32[env>>2]=setjmpId;while((i|0)<(size|0)){if((HEAP32[table+(i<<3)>>2]|0)==0){HEAP32[table+(i<<3)>>2]=setjmpId;HEAP32[table+((i<<3)+4)>>2]=label;HEAP32[table+((i<<3)+8)>>2]=0;setTempRet0(size|0);return table|0}i=i+1|0}size=size*2|0;table=_realloc(table|0,8*(size+1|0)|0)|0;table=_saveSetjmp(env|0,label|0,table|0,size|0)|0;setTempRet0(size|0);return table|0}function _testSetjmp(id,table,size){id=id|0;table=table|0;size=size|0;var i=0,curr=0;while((i|0)<(size|0)){curr=HEAP32[table+(i<<3)>>2]|0;if((curr|0)==0)break;if((curr|0)==(id|0)){return HEAP32[table+((i<<3)+4)>>2]|0}i=i+1|0}return 0}function _longjmp(env,value){_setThrew(env,value||1);throw"longjmp"}function _emscripten_longjmp(env,value){_longjmp(env,value)}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_size(){return HEAPU8.length}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;var oldSize=_emscripten_get_heap_size();var PAGE_MULTIPLE=65536;var maxHeapSize=2147483648;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),PAGE_MULTIPLE));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_read(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doReadv(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){try{var stream=SYSCALLS.getStreamFromFD(fd);var HIGH_OFFSET=4294967296;var offset=offset_high*HIGH_OFFSET+(offset_low>>>0);var DOUBLE_LIMIT=9007199254740992;if(offset<=-DOUBLE_LIMIT||offset>=DOUBLE_LIMIT){return-61}FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doWritev(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _getTempRet0(){return getTempRet0()|0}function _gettimeofday(ptr){var now=Date.now();HEAP32[ptr>>2]=now/1e3|0;HEAP32[ptr+4>>2]=now%1e3*1e3|0;return 0}function _pthread_join(){}function _roundf(d){d=+d;return d>=+0?+Math_floor(d+ +.5):+Math_ceil(d-+.5)}function _setTempRet0($i){setTempRet0($i|0)}function _sysconf(name){switch(name){case 30:return 16384;case 85:var maxHeapSize=2147483648;return maxHeapSize/16384;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 80:case 81:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:case 79:return 200809;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1e3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:{if(typeof navigator==="object")return navigator["hardwareConcurrency"]||1;return 1}}setErrNo(28);return-1}var FSNode=function(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev};var readMode=292|73;var writeMode=146;Object.defineProperties(FSNode.prototype,{read:{get:function(){return(this.mode&readMode)===readMode},set:function(val){val?this.mode|=readMode:this.mode&=~readMode}},write:{get:function(){return(this.mode&writeMode)===writeMode},set:function(val){val?this.mode|=writeMode:this.mode&=~writeMode}},isFolder:{get:function(){return FS.isDir(this.mode)}},isDevice:{get:function(){return FS.isChrdev(this.mode)}}});FS.FSNode=FSNode;FS.staticInit();InternalError=Module["InternalError"]=extendError(Error,"InternalError");embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");init_ClassHandle();init_RegisteredPointer();init_embind();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");init_emval();function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var asmLibraryArg={"$":___cxa_allocate_exception,"_":___cxa_throw,"J":___sys_fcntl64,"S":___sys_ioctl,"T":___sys_open,"A":__embind_finalize_value_object,"V":__embind_register_bool,"D":__embind_register_class,"C":__embind_register_class_constructor,"a":__embind_register_class_function,"e":__embind_register_constant,"U":__embind_register_emval,"s":__embind_register_enum,"c":__embind_register_enum_value,"K":__embind_register_float,"m":__embind_register_function,"n":__embind_register_integer,"k":__embind_register_memory_view,"L":__embind_register_std_string,"E":__embind_register_std_wstring,"B":__embind_register_value_object,"d":__embind_register_value_object_field,"W":__embind_register_void,"r":__emval_as,"t":__emval_call_void_method,"b":__emval_decref,"G":__emval_get_global,"u":__emval_get_method_caller,"w":__emval_get_module_property,"f":__emval_get_property,"i":__emval_incref,"v":__emval_new,"g":__emval_new_cstring,"q":__emval_run_destructors,"h":_abort,"x":_emscripten_longjmp,"N":_emscripten_memcpy_big,"O":_emscripten_resize_heap,"H":_fd_close,"R":_fd_read,"M":_fd_seek,"I":_fd_write,"l":_getTempRet0,"z":_gettimeofday,"Y":invoke_ii,"y":invoke_vi,"X":invoke_vii,"Z":invoke_viii,"memory":wasmMemory,"Q":_pthread_join,"p":_roundf,"F":_saveSetjmp,"j":_setTempRet0,"P":_sysconf,"table":wasmTable,"o":_testSetjmp};var asm=createWasm();Module["asm"]=asm;var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["aa"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["ba"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["ca"]).apply(null,arguments)};var _realloc=Module["_realloc"]=function(){return(_realloc=Module["_realloc"]=Module["asm"]["da"]).apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return(___getTypeName=Module["___getTypeName"]=Module["asm"]["ea"]).apply(null,arguments)};var ___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=function(){return(___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=Module["asm"]["fa"]).apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return(___errno_location=Module["___errno_location"]=Module["asm"]["ga"]).apply(null,arguments)};var _setThrew=Module["_setThrew"]=function(){return(_setThrew=Module["_setThrew"]=Module["asm"]["ha"]).apply(null,arguments)};var dynCall_vi=Module["dynCall_vi"]=function(){return(dynCall_vi=Module["dynCall_vi"]=Module["asm"]["ia"]).apply(null,arguments)};var dynCall_vii=Module["dynCall_vii"]=function(){return(dynCall_vii=Module["dynCall_vii"]=Module["asm"]["ja"]).apply(null,arguments)};var dynCall_viii=Module["dynCall_viii"]=function(){return(dynCall_viii=Module["dynCall_viii"]=Module["asm"]["ka"]).apply(null,arguments)};var dynCall_ii=Module["dynCall_ii"]=function(){return(dynCall_ii=Module["dynCall_ii"]=Module["asm"]["la"]).apply(null,arguments)};var stackSave=Module["stackSave"]=function(){return(stackSave=Module["stackSave"]=Module["asm"]["ma"]).apply(null,arguments)};var stackRestore=Module["stackRestore"]=function(){return(stackRestore=Module["stackRestore"]=Module["asm"]["na"]).apply(null,arguments)};var dynCall_v=Module["dynCall_v"]=function(){return(dynCall_v=Module["dynCall_v"]=Module["asm"]["oa"]).apply(null,arguments)};var dynCall_i=Module["dynCall_i"]=function(){return(dynCall_i=Module["dynCall_i"]=Module["asm"]["pa"]).apply(null,arguments)};var dynCall_iii=Module["dynCall_iii"]=function(){return(dynCall_iii=Module["dynCall_iii"]=Module["asm"]["qa"]).apply(null,arguments)};var dynCall_iiii=Module["dynCall_iiii"]=function(){return(dynCall_iiii=Module["dynCall_iiii"]=Module["asm"]["ra"]).apply(null,arguments)};var dynCall_iiiii=Module["dynCall_iiiii"]=function(){return(dynCall_iiiii=Module["dynCall_iiiii"]=Module["asm"]["sa"]).apply(null,arguments)};var dynCall_iiiiii=Module["dynCall_iiiiii"]=function(){return(dynCall_iiiiii=Module["dynCall_iiiiii"]=Module["asm"]["ta"]).apply(null,arguments)};var dynCall_iiiiiiii=Module["dynCall_iiiiiiii"]=function(){return(dynCall_iiiiiiii=Module["dynCall_iiiiiiii"]=Module["asm"]["ua"]).apply(null,arguments)};var dynCall_iiiiiiiii=Module["dynCall_iiiiiiiii"]=function(){return(dynCall_iiiiiiiii=Module["dynCall_iiiiiiiii"]=Module["asm"]["va"]).apply(null,arguments)};var dynCall_viiii=Module["dynCall_viiii"]=function(){return(dynCall_viiii=Module["dynCall_viiii"]=Module["asm"]["wa"]).apply(null,arguments)};var dynCall_iiiiiii=Module["dynCall_iiiiiii"]=function(){return(dynCall_iiiiiii=Module["dynCall_iiiiiii"]=Module["asm"]["xa"]).apply(null,arguments)};var dynCall_iiiiiiiiiiiiiiiiiiii=Module["dynCall_iiiiiiiiiiiiiiiiiiii"]=function(){return(dynCall_iiiiiiiiiiiiiiiiiiii=Module["dynCall_iiiiiiiiiiiiiiiiiiii"]=Module["asm"]["ya"]).apply(null,arguments)};var dynCall_iiiiiiiiiiiiiiiiiiiii=Module["dynCall_iiiiiiiiiiiiiiiiiiiii"]=function(){return(dynCall_iiiiiiiiiiiiiiiiiiiii=Module["dynCall_iiiiiiiiiiiiiiiiiiiii"]=Module["asm"]["za"]).apply(null,arguments)};var dynCall_iiiiiiiiiiiiiiiiiii=Module["dynCall_iiiiiiiiiiiiiiiiiii"]=function(){return(dynCall_iiiiiiiiiiiiiiiiiii=Module["dynCall_iiiiiiiiiiiiiiiiiii"]=Module["asm"]["Aa"]).apply(null,arguments)};var dynCall_viiiii=Module["dynCall_viiiii"]=function(){return(dynCall_viiiii=Module["dynCall_viiiii"]=Module["asm"]["Ba"]).apply(null,arguments)};var dynCall_iiiiiiiiii=Module["dynCall_iiiiiiiiii"]=function(){return(dynCall_iiiiiiiiii=Module["dynCall_iiiiiiiiii"]=Module["asm"]["Ca"]).apply(null,arguments)};var dynCall_iiiiiiiiiii=Module["dynCall_iiiiiiiiiii"]=function(){return(dynCall_iiiiiiiiiii=Module["dynCall_iiiiiiiiiii"]=Module["asm"]["Da"]).apply(null,arguments)};var dynCall_viiiiii=Module["dynCall_viiiiii"]=function(){return(dynCall_viiiiii=Module["dynCall_viiiiii"]=Module["asm"]["Ea"]).apply(null,arguments)};var dynCall_vif=Module["dynCall_vif"]=function(){return(dynCall_vif=Module["dynCall_vif"]=Module["asm"]["Fa"]).apply(null,arguments)};var dynCall_viif=Module["dynCall_viif"]=function(){return(dynCall_viif=Module["dynCall_viif"]=Module["asm"]["Ga"]).apply(null,arguments)};var dynCall_ff=Module["dynCall_ff"]=function(){return(dynCall_ff=Module["dynCall_ff"]=Module["asm"]["Ha"]).apply(null,arguments)};var dynCall_jiiii=Module["dynCall_jiiii"]=function(){return(dynCall_jiiii=Module["dynCall_jiiii"]=Module["asm"]["Ia"]).apply(null,arguments)};var dynCall_iij=Module["dynCall_iij"]=function(){return(dynCall_iij=Module["dynCall_iij"]=Module["asm"]["Ja"]).apply(null,arguments)};var dynCall_ji=Module["dynCall_ji"]=function(){return(dynCall_ji=Module["dynCall_ji"]=Module["asm"]["Ka"]).apply(null,arguments)};var dynCall_jiji=Module["dynCall_jiji"]=function(){return(dynCall_jiji=Module["dynCall_jiji"]=Module["asm"]["La"]).apply(null,arguments)};var dynCall_iidiiii=Module["dynCall_iidiiii"]=function(){return(dynCall_iidiiii=Module["dynCall_iidiiii"]=Module["asm"]["Ma"]).apply(null,arguments)};function invoke_vi(index,a1){var sp=stackSave();try{dynCall_vi(index,a1)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viii(index,a1,a2,a3){var sp=stackSave();try{dynCall_viii(index,a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_ii(index,a1){var sp=stackSave();try{return dynCall_ii(index,a1)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{dynCall_vii(index,a1,a2)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}Module["asm"]=asm;var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run();
 
 
   return BASIS.ready
@@ -14,8 +14,9 @@
 );
 })();
 if (typeof exports === 'object' && typeof module === 'object')
-  module.exports = BASIS;
-else if (typeof define === 'function' && define['amd'])
-  define([], function() { return BASIS; });
-else if (typeof exports === 'object')
-  exports["BASIS"] = BASIS;
+      module.exports = BASIS;
+    else if (typeof define === 'function' && define['amd'])
+      define([], function() { return BASIS; });
+    else if (typeof exports === 'object')
+      exports["BASIS"] = BASIS;
+    
\ No newline at end of file
diff --git a/webgl/encoder/build/basis_encoder.wasm b/webgl/encoder/build/basis_encoder.wasm
index b0539c5..72799b6 100644
--- a/webgl/encoder/build/basis_encoder.wasm
+++ b/webgl/encoder/build/basis_encoder.wasm
Binary files differ
diff --git a/webgl/transcoder/basis_wrappers.cpp b/webgl/transcoder/basis_wrappers.cpp
index a1c6428..739f092 100644
--- a/webgl/transcoder/basis_wrappers.cpp
+++ b/webgl/transcoder/basis_wrappers.cpp
@@ -41,7 +41,7 @@
 using namespace basist;
 using namespace basisu;
 
-static basist::etc1_global_selector_codebook* g_pGlobal_codebook;
+static bool g_basis_initialized_flag;
 
 void basis_init()
 {
@@ -49,7 +49,7 @@
 	printf("basis_init()\n");
 #endif   
 
-	if (g_pGlobal_codebook)
+	if (g_basis_initialized_flag)
 		return;
 
 #if BASISU_SUPPORT_ENCODING
@@ -58,7 +58,7 @@
 
 	basisu_transcoder_init();
 
-	g_pGlobal_codebook = new basist::etc1_global_selector_codebook(g_global_selector_cb_size, g_global_selector_cb);
+	g_basis_initialized_flag = true;
 }
 
 static void copy_from_jsbuffer(const emscripten::val& srcBuffer, basisu::vector<uint8_t>& dstVec)
@@ -174,10 +174,9 @@
 		: m_file([&]() {
 		size_t byteLength = jsBuffer["byteLength"].as<size_t>();
 		return basisu::vector<uint8_t>(byteLength);
-			}()),
-		m_transcoder(g_pGlobal_codebook)
+			}())
 	{
-		if (!g_pGlobal_codebook)
+		if (!g_basis_initialized_flag)
 		{
 #if BASISU_DEBUG_PRINTF   
 			printf("basis_file::basis_file: Must call basis_init() first!\n");
@@ -532,10 +531,9 @@
 		: m_file([&]() {
 		size_t byteLength = jsBuffer["byteLength"].as<size_t>();
 		return basisu::vector<uint8_t>(byteLength);
-			}()),
-		m_transcoder(g_pGlobal_codebook)
+			}())
 	{
-		if (!g_pGlobal_codebook)
+		if (!g_basis_initialized_flag)
 		{
 #if BASISU_DEBUG_PRINTF   
 			printf("basis_file::basis_file: Must call basis_init() first!\n");
@@ -1050,7 +1048,7 @@
 	
 	uint32_t encode(const emscripten::val& dst_basis_file_js_val)
 	{
-		if (!g_pGlobal_codebook)
+		if (!g_basis_initialized_flag)
 		{
 #if BASISU_DEBUG_PRINTF   
 			printf("basis_encoder::encode: Must call basis_init() first!\n");
@@ -1059,8 +1057,6 @@
 			return 0;
 		}
 
-		basist::etc1_global_selector_codebook sel_codebook(basist::g_global_selector_cb_size, basist::g_global_selector_cb);
-
 		// We don't use threading for now, but the compressor needs a job pool.
 		job_pool jpool(1);
 
@@ -1076,7 +1072,6 @@
 
 		params.m_read_source_images = false;
 		params.m_write_output_basis_files = false;
-		params.m_pSel_codebook = &sel_codebook;
 
 		basis_compressor comp;
 
@@ -1135,8 +1130,7 @@
 	basisu_transcoder_state m_state;
 	
 public:
-	lowlevel_etc1s_image_transcoder() : 
-		basisu_lowlevel_etc1s_transcoder(g_pGlobal_codebook)
+	lowlevel_etc1s_image_transcoder()
 	{
 	}
 	
@@ -1195,7 +1189,7 @@
 		uint32_t output_row_pitch_in_blocks_or_pixels,
 		uint32_t output_rows_in_pixels)
 	{
-		if (!g_pGlobal_codebook)
+		if (!g_basis_initialized_flag)
 		{
 #if BASISU_DEBUG_PRINTF   
 			printf("transcode_etc1s_image: basis_init() must be called first\n");
@@ -1273,7 +1267,7 @@
 {
 	transcoder_texture_format target_format = static_cast<transcoder_texture_format>(target_format_int);
 	
-	if (!g_pGlobal_codebook)
+	if (!g_basis_initialized_flag)
 	{
 #if BASISU_DEBUG_PRINTF   
 		printf("transcode_uastc_image: basis_init() must be called first\n");
diff --git a/webgl/transcoder/build/basis_transcoder.js b/webgl/transcoder/build/basis_transcoder.js
index 9e285dd..b42d977 100644
--- a/webgl/transcoder/build/basis_transcoder.js
+++ b/webgl/transcoder/build/basis_transcoder.js
@@ -6,7 +6,7 @@
 function(BASIS) {
   BASIS = BASIS || {};
 
-var Module=typeof BASIS!=="undefined"?BASIS:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var tempRet0=0;var setTempRet0=function(value){tempRet0=value};var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx<endPtr){var u0=heap[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heap[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heap[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heap[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function UTF16ToString(ptr,maxBytesToRead){var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder){return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr))}else{var str="";for(var i=0;!(i>=maxBytesToRead/2);++i){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)break;str+=String.fromCharCode(codeUnit)}return str}}function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite<str.length*2?maxBytesToWrite/2:str.length;for(var i=0;i<numCharsToWrite;++i){var codeUnit=str.charCodeAt(i);HEAP16[outPtr>>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}function lengthBytesUTF16(str){return str.length*2}function UTF32ToString(ptr,maxBytesToRead){var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str}function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr}function lengthBytesUTF32(str){var len=0;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343)++i;len+=4}return len}function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";function isFileURI(filename){return hasPrefix(filename,fileURIPrefix)}var wasmBinaryFile="basis_transcoder.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return Promise.resolve().then(getBinary)}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["K"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["L"];removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var structRegistrations={};function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function makeLegalFunctionName(name){if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return"_"+name}else{return name}}function createNamedFunction(name,body){name=makeLegalFunctionName(name);return new Function("body","return function "+name+"() {\n"+'    "use strict";'+"    return body.apply(this, arguments);\n"+"};\n")(body)}function extendError(baseErrorType,errorName){var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return this.name+": "+this.message}};return errorClass}var InternalError=undefined;function throwInternalError(message){throw new InternalError(message)}function whenDependentTypesAreResolved(myTypes,dependentTypes,getTypeConverters){myTypes.forEach(function(type){typeDependencies[type]=dependentTypes});function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i<myTypes.length;++i){registerType(myTypes[i],myTypeConverters[i])}}var typeConverters=new Array(dependentTypes.length);var unregisteredTypes=[];var registered=0;dependentTypes.forEach(function(dt,i){if(registeredTypes.hasOwnProperty(dt)){typeConverters[i]=registeredTypes[dt]}else{unregisteredTypes.push(dt);if(!awaitingDependencies.hasOwnProperty(dt)){awaitingDependencies[dt]=[]}awaitingDependencies[dt].push(function(){typeConverters[i]=registeredTypes[dt];++registered;if(registered===unregisteredTypes.length){onComplete(typeConverters)}})}});if(0===unregisteredTypes.length){onComplete(typeConverters)}}function __embind_finalize_value_object(structType){var reg=structRegistrations[structType];delete structRegistrations[structType];var rawConstructor=reg.rawConstructor;var rawDestructor=reg.rawDestructor;var fieldRecords=reg.fields;var fieldTypes=fieldRecords.map(function(field){return field.getterReturnType}).concat(fieldRecords.map(function(field){return field.setterArgumentType}));whenDependentTypesAreResolved([structType],fieldTypes,function(fieldTypes){var fields={};fieldRecords.forEach(function(field,i){var fieldName=field.fieldName;var getterReturnType=fieldTypes[i];var getter=field.getter;var getterContext=field.getterContext;var setterArgumentType=fieldTypes[i+fieldRecords.length];var setter=field.setter;var setterContext=field.setterContext;fields[fieldName]={read:function(ptr){return getterReturnType["fromWireType"](getter(getterContext,ptr))},write:function(ptr,o){var destructors=[];setter(setterContext,ptr,setterArgumentType["toWireType"](destructors,o));runDestructors(destructors)}}});return[{name:reg.name,"fromWireType":function(ptr){var rv={};for(var i in fields){rv[i]=fields[i].read(ptr)}rawDestructor(ptr);return rv},"toWireType":function(destructors,o){for(var fieldName in fields){if(!(fieldName in o)){throw new TypeError('Missing field:  "'+fieldName+'"')}}var ptr=rawConstructor();for(fieldName in fields){fields[fieldName].write(ptr,o[fieldName])}if(destructors!==null){destructors.push(rawDestructor,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:rawDestructor}]})}function getShiftFromSize(size){switch(size){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+size)}}function embind_init_charCodes(){var codes=new Array(256);for(var i=0;i<256;++i){codes[i]=String.fromCharCode(i)}embind_charCodes=codes}var embind_charCodes=undefined;function readLatin1String(ptr){var ret="";var c=ptr;while(HEAPU8[c]){ret+=embind_charCodes[HEAPU8[c++]]}return ret}var BindingError=undefined;function throwBindingError(message){throw new BindingError(message)}function registerType(rawType,registeredInstance,options){options=options||{};if(!("argPackAdvance"in registeredInstance)){throw new TypeError("registerType registeredInstance requires argPackAdvance")}var name=registeredInstance.name;if(!rawType){throwBindingError('type "'+name+'" must have a positive integer typeid pointer')}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError("Cannot register type '"+name+"' twice")}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(function(cb){cb()})}}function __embind_register_bool(rawType,name,size,trueValue,falseValue){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(wt){return!!wt},"toWireType":function(destructors,o){return o?trueValue:falseValue},"argPackAdvance":8,"readValueFromPointer":function(pointer){var heap;if(size===1){heap=HEAP8}else if(size===2){heap=HEAP16}else if(size===4){heap=HEAP32}else{throw new TypeError("Unknown boolean type size: "+name)}return this["fromWireType"](heap[pointer>>shift])},destructorFunction:null})}function ClassHandle_isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right}function shallowCopyInternalPointer(o){return{count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType}}function throwInstanceAlreadyDeleted(obj){function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")}var finalizationGroup=false;function detachFinalizer(handle){}function runDestructor($$){if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}}function releaseClassHandle($$){$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}}function attachFinalizer(handle){if("undefined"===typeof FinalizationGroup){attachFinalizer=function(handle){return handle};return handle}finalizationGroup=new FinalizationGroup(function(iter){for(var result=iter.next();!result.done;result=iter.next()){var $$=result.value;if(!$$.ptr){console.warn("object already deleted: "+$$.ptr)}else{releaseClassHandle($$)}}});attachFinalizer=function(handle){finalizationGroup.register(handle,handle.$$,handle.$$);return handle};detachFinalizer=function(handle){finalizationGroup.unregister(handle.$$)};return attachFinalizer(handle)}function ClassHandle_clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}}function ClassHandle_delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}}function ClassHandle_isDeleted(){return!this.$$.ptr}var delayFunction=undefined;var deletionQueue=[];function flushPendingDeletes(){while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}}function ClassHandle_deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}function init_ClassHandle(){ClassHandle.prototype["isAliasOf"]=ClassHandle_isAliasOf;ClassHandle.prototype["clone"]=ClassHandle_clone;ClassHandle.prototype["delete"]=ClassHandle_delete;ClassHandle.prototype["isDeleted"]=ClassHandle_isDeleted;ClassHandle.prototype["deleteLater"]=ClassHandle_deleteLater}function ClassHandle(){}var registeredPointers={};function ensureOverloadTable(proto,methodName,humanName){if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(){if(!proto[methodName].overloadTable.hasOwnProperty(arguments.length)){throwBindingError("Function '"+humanName+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+proto[methodName].overloadTable+")!")}return proto[methodName].overloadTable[arguments.length].apply(this,arguments)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}}function exposePublicSymbol(name,value,numArguments){if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError("Cannot register public name '"+name+"' twice")}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError("Cannot register multiple overloads of a function with the same number of arguments ("+numArguments+")!")}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}}function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}function upcastPointer(ptr,ptrClass,desiredClass){while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError("Expected null or instance of "+desiredClass.name+", got an instance of "+ptrClass.name)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr}function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,__emval_register(function(){clonedHandle["delete"]()}));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupporting sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+handle.$$.ptrType.name+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function RegisteredPointer_getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr}function RegisteredPointer_destructor(ptr){if(this.rawDestructor){this.rawDestructor(ptr)}}function RegisteredPointer_deleteObject(handle){if(handle!==null){handle["delete"]()}}function downcastPointer(ptr,ptrClass,desiredClass){if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)}function getInheritedInstanceCount(){return Object.keys(registeredInstances).length}function getLiveInheritedInstances(){var rv=[];for(var k in registeredInstances){if(registeredInstances.hasOwnProperty(k)){rv.push(registeredInstances[k])}}return rv}function setDelayFunction(fn){delayFunction=fn;if(deletionQueue.length&&delayFunction){delayFunction(flushPendingDeletes)}}function init_embind(){Module["getInheritedInstanceCount"]=getInheritedInstanceCount;Module["getLiveInheritedInstances"]=getLiveInheritedInstances;Module["flushPendingDeletes"]=flushPendingDeletes;Module["setDelayFunction"]=setDelayFunction}var registeredInstances={};function getBasestPointer(class_,ptr){if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr}function getInheritedInstance(class_,ptr){ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]}function makeClassHandle(prototype,record){if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record}}))}function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr:ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}function init_RegisteredPointer(){RegisteredPointer.prototype.getPointee=RegisteredPointer_getPointee;RegisteredPointer.prototype.destructor=RegisteredPointer_destructor;RegisteredPointer.prototype["argPackAdvance"]=8;RegisteredPointer.prototype["readValueFromPointer"]=simpleReadValueFromPointer;RegisteredPointer.prototype["deleteObject"]=RegisteredPointer_deleteObject;RegisteredPointer.prototype["fromWireType"]=RegisteredPointer_fromWireType}function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&&registeredClass.baseClass===undefined){if(isConst){this["toWireType"]=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this["toWireType"]=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this["toWireType"]=genericPointerToWireType}}function replacePublicSymbol(name,value,numArguments){if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistant public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}}function dynCallLegacy(sig,ptr,args){if(args&&args.length){return Module["dynCall_"+sig].apply(null,[ptr].concat(args))}return Module["dynCall_"+sig].call(null,ptr)}function dynCall(sig,ptr,args){if(sig.indexOf("j")!=-1){return dynCallLegacy(sig,ptr,args)}return wasmTable.get(ptr).apply(null,args)}function getDynCaller(sig,ptr){assert(sig.indexOf("j")>=0,"getDynCaller should only be called with i64 sigs");var argCache=[];return function(){argCache.length=arguments.length;for(var i=0;i<arguments.length;i++){argCache[i]=arguments[i]}return dynCall(sig,ptr,argCache)}}function embind__requireFunction(signature,rawFunction){signature=readLatin1String(signature);function makeDynCaller(){if(signature.indexOf("j")!=-1){return getDynCaller(signature,rawFunction)}return wasmTable.get(rawFunction)}var fp=makeDynCaller();if(typeof fp!=="function"){throwBindingError("unknown function pointer with signature "+signature+": "+rawFunction)}return fp}var UnboundTypeError=undefined;function getTypeName(type){var ptr=___getTypeName(type);var rv=readLatin1String(ptr);_free(ptr);return rv}function throwUnboundTypeError(message,types){var unboundTypes=[];var seen={};function visit(type){if(seen[type]){return}if(registeredTypes[type]){return}if(typeDependencies[type]){typeDependencies[type].forEach(visit);return}unboundTypes.push(type);seen[type]=true}types.forEach(visit);throw new UnboundTypeError(message+": "+unboundTypes.map(getTypeName).join([", "]))}function __embind_register_class(rawType,rawPointerType,rawConstPointerType,baseClassRawType,getActualTypeSignature,getActualType,upcastSignature,upcast,downcastSignature,downcast,name,destructorSignature,rawDestructor){name=readLatin1String(name);getActualType=embind__requireFunction(getActualTypeSignature,getActualType);if(upcast){upcast=embind__requireFunction(upcastSignature,upcast)}if(downcast){downcast=embind__requireFunction(downcastSignature,downcast)}rawDestructor=embind__requireFunction(destructorSignature,rawDestructor);var legalFunctionName=makeLegalFunctionName(name);exposePublicSymbol(legalFunctionName,function(){throwUnboundTypeError("Cannot construct "+name+" due to unbound types",[baseClassRawType])});whenDependentTypesAreResolved([rawType,rawPointerType,rawConstPointerType],baseClassRawType?[baseClassRawType]:[],function(base){base=base[0];var baseClass;var basePrototype;if(baseClassRawType){baseClass=base.registeredClass;basePrototype=baseClass.instancePrototype}else{basePrototype=ClassHandle.prototype}var constructor=createNamedFunction(legalFunctionName,function(){if(Object.getPrototypeOf(this)!==instancePrototype){throw new BindingError("Use 'new' to construct "+name)}if(undefined===registeredClass.constructor_body){throw new BindingError(name+" has no accessible constructor")}var body=registeredClass.constructor_body[arguments.length];if(undefined===body){throw new BindingError("Tried to invoke ctor of "+name+" with invalid number of parameters ("+arguments.length+") - expected ("+Object.keys(registeredClass.constructor_body).toString()+") parameters instead!")}return body.apply(this,arguments)});var instancePrototype=Object.create(basePrototype,{constructor:{value:constructor}});constructor.prototype=instancePrototype;var registeredClass=new RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast);var referenceConverter=new RegisteredPointer(name,registeredClass,true,false,false);var pointerConverter=new RegisteredPointer(name+"*",registeredClass,false,false,false);var constPointerConverter=new RegisteredPointer(name+" const*",registeredClass,false,true,false);registeredPointers[rawType]={pointerType:pointerConverter,constPointerType:constPointerConverter};replacePublicSymbol(legalFunctionName,constructor);return[referenceConverter,pointerConverter,constPointerConverter]})}function heap32VectorToArray(count,firstElement){var array=[];for(var i=0;i<count;i++){array.push(HEAP32[(firstElement>>2)+i])}return array}function __embind_register_class_constructor(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor){assert(argCount>0);var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);var args=[rawConstructor];var destructors=[];whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName="constructor "+classType.name;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError("Cannot register multiple constructors with identical number of parameters ("+(argCount-1)+") for class '"+classType.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!")}classType.registeredClass.constructor_body[argCount-1]=function unboundTypeHandler(){throwUnboundTypeError("Cannot construct "+classType.name+" due to unbound types",rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){classType.registeredClass.constructor_body[argCount-1]=function constructor_body(){if(arguments.length!==argCount-1){throwBindingError(humanName+" called with "+arguments.length+" arguments, expected "+(argCount-1))}destructors.length=0;args.length=argCount;for(var i=1;i<argCount;++i){args[i]=argTypes[i]["toWireType"](destructors,arguments[i-1])}var ptr=invoker.apply(null,args);runDestructors(destructors);return argTypes[0]["fromWireType"](ptr)};return[]});return[]})}function new_(constructor,argumentList){if(!(constructor instanceof Function)){throw new TypeError("new_ called with constructor type "+typeof constructor+" which is not a function")}var dummy=createNamedFunction(constructor.name||"unknownFunctionName",function(){});dummy.prototype=constructor.prototype;var obj=new dummy;var r=constructor.apply(obj,argumentList);return r instanceof Object?r:obj}function craftInvokerFunction(humanName,argTypes,classType,cppInvokerFunc,cppTargetFunc){var argCount=argTypes.length;if(argCount<2){throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!")}var isClassMethodFunc=argTypes[1]!==null&&classType!==null;var needsDestructorStack=false;for(var i=1;i<argTypes.length;++i){if(argTypes[i]!==null&&argTypes[i].destructorFunction===undefined){needsDestructorStack=true;break}}var returns=argTypes[0].name!=="void";var argsList="";var argsListWired="";for(var i=0;i<argCount-2;++i){argsList+=(i!==0?", ":"")+"arg"+i;argsListWired+=(i!==0?", ":"")+"arg"+i+"Wired"}var invokerFnBody="return function "+makeLegalFunctionName(humanName)+"("+argsList+") {\n"+"if (arguments.length !== "+(argCount-2)+") {\n"+"throwBindingError('function "+humanName+" called with ' + arguments.length + ' arguments, expected "+(argCount-2)+" args!');\n"+"}\n";if(needsDestructorStack){invokerFnBody+="var destructors = [];\n"}var dtorStack=needsDestructorStack?"destructors":"null";var args1=["throwBindingError","invoker","fn","runDestructors","retType","classParam"];var args2=[throwBindingError,cppInvokerFunc,cppTargetFunc,runDestructors,argTypes[0],argTypes[1]];if(isClassMethodFunc){invokerFnBody+="var thisWired = classParam.toWireType("+dtorStack+", this);\n"}for(var i=0;i<argCount-2;++i){invokerFnBody+="var arg"+i+"Wired = argType"+i+".toWireType("+dtorStack+", arg"+i+"); // "+argTypes[i+2].name+"\n";args1.push("argType"+i);args2.push(argTypes[i+2])}if(isClassMethodFunc){argsListWired="thisWired"+(argsListWired.length>0?", ":"")+argsListWired}invokerFnBody+=(returns?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\n"}else{for(var i=isClassMethodFunc?1:2;i<argTypes.length;++i){var paramName=i===1?"thisWired":"arg"+(i-2)+"Wired";if(argTypes[i].destructorFunction!==null){invokerFnBody+=paramName+"_dtor("+paramName+"); // "+argTypes[i].name+"\n";args1.push(paramName+"_dtor");args2.push(argTypes[i].destructorFunction)}}}if(returns){invokerFnBody+="var ret = retType.fromWireType(rv);\n"+"return ret;\n"}else{}invokerFnBody+="}\n";args1.push(invokerFnBody);var invokerFunction=new_(Function,args1).apply(null,args2);return invokerFunction}function __embind_register_class_function(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,context,isPureVirtual){var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=readLatin1String(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker);whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName=classType.name+"."+methodName;if(isPureVirtual){classType.registeredClass.pureVirtualFunctions.push(methodName)}function unboundTypesHandler(){throwUnboundTypeError("Cannot call "+humanName+" due to unbound types",rawArgTypes)}var proto=classType.registeredClass.instancePrototype;var method=proto[methodName];if(undefined===method||undefined===method.overloadTable&&method.className!==classType.name&&method.argCount===argCount-2){unboundTypesHandler.argCount=argCount-2;unboundTypesHandler.className=classType.name;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-2]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){var memberFunction=craftInvokerFunction(humanName,argTypes,classType,rawInvoker,context);if(undefined===proto[methodName].overloadTable){memberFunction.argCount=argCount-2;proto[methodName]=memberFunction}else{proto[methodName].overloadTable[argCount-2]=memberFunction}return[]});return[]})}function __embind_register_constant(name,type,value){name=readLatin1String(name);whenDependentTypesAreResolved([],[type],function(type){type=type[0];Module[name]=type["fromWireType"](value);return[]})}var emval_free_list=[];var emval_handle_array=[{},{value:undefined},{value:null},{value:true},{value:false}];function __emval_decref(handle){if(handle>4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i<emval_handle_array.length;++i){if(emval_handle_array[i]!==undefined){++count}}return count}function get_first_emval(){for(var i=5;i<emval_handle_array.length;++i){if(emval_handle_array[i]!==undefined){return emval_handle_array[i]}}return null}function init_emval(){Module["count_emval_handles"]=count_emval_handles;Module["get_first_emval"]=get_first_emval}function __emval_register(value){switch(value){case undefined:{return 1}case null:{return 2}case true:{return 3}case false:{return 4}default:{var handle=emval_free_list.length?emval_free_list.pop():emval_handle_array.length;emval_handle_array[handle]={refcount:1,value:value};return handle}}}function __embind_register_emval(rawType,name){name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(handle){var rv=emval_handle_array[handle].value;__emval_decref(handle);return rv},"toWireType":function(destructors,value){return __emval_register(value)},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:null})}function enumReadValueFromPointer(name,shift,signed){switch(shift){case 0:return function(pointer){var heap=signed?HEAP8:HEAPU8;return this["fromWireType"](heap[pointer])};case 1:return function(pointer){var heap=signed?HEAP16:HEAPU16;return this["fromWireType"](heap[pointer>>1])};case 2:return function(pointer){var heap=signed?HEAP32:HEAPU32;return this["fromWireType"](heap[pointer>>2])};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_enum(rawType,name,size,isSigned){var shift=getShiftFromSize(size);name=readLatin1String(name);function ctor(){}ctor.values={};registerType(rawType,{name:name,constructor:ctor,"fromWireType":function(c){return this.constructor.values[c]},"toWireType":function(destructors,c){return c.value},"argPackAdvance":8,"readValueFromPointer":enumReadValueFromPointer(name,shift,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor)}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __embind_register_enum_value(rawEnumType,name,enumValue){var enumType=requireRegisteredType(rawEnumType,"enum");name=readLatin1String(name);var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(enumType.name+"_"+name,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value}function _embind_repr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function __embind_register_function(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn){var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError("Cannot call "+name+" due to unbound types",argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn),argCount-1);return[]})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=function(value){return value};if(minRange===0){var bitshift=32-8*size;fromWireType=function(value){return value<<bitshift>>>bitshift}}var isUnsignedType=name.indexOf("unsigned")!=-1;registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}if(value<minRange||value>maxRange){throw new TypeError('Passing a number "'+_embind_repr(value)+'" from JS side to C/C++ side to an argument of type "'+name+'", which is outside the valid range ['+minRange+", "+maxRange+"]!")}return isUnsignedType?value>>>0:value|0},"argPackAdvance":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var str;if(stdStringIsUTF8){var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i;if(i==length||HEAPU8[currentBytePtr]==0){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i<length;++i){a[i]=String.fromCharCode(HEAPU8[value+4+i])}str=a.join("")}_free(value);return str},"toWireType":function(destructors,value){if(value instanceof ArrayBuffer){value=new Uint8Array(value)}var getLength;var valueIsOfTypeString=typeof value==="string";if(!(valueIsOfTypeString||value instanceof Uint8Array||value instanceof Uint8ClampedArray||value instanceof Int8Array)){throwBindingError("Cannot pass non-string to std::string")}if(stdStringIsUTF8&&valueIsOfTypeString){getLength=function(){return lengthBytesUTF8(value)}}else{getLength=function(){return value.length}}var length=getLength();var ptr=_malloc(4+length+1);HEAPU32[ptr>>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr+4,length+1)}else{if(valueIsOfTypeString){for(var i=0;i<length;++i){var charCode=value.charCodeAt(i);if(charCode>255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+4+i]=charCode}}else{for(var i=0;i<length;++i){HEAPU8[ptr+4+i]=value[i]}}}if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_std_wstring(rawType,charSize,name){name=readLatin1String(name);var decodeString,encodeString,getHeap,lengthBytesUTF,shift;if(charSize===2){decodeString=UTF16ToString;encodeString=stringToUTF16;lengthBytesUTF=lengthBytesUTF16;getHeap=function(){return HEAPU16};shift=1}else if(charSize===4){decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32;getHeap=function(){return HEAPU32};shift=2}registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var HEAP=getHeap();var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||HEAP[currentBytePtr>>shift]==0){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_free(value);return str},"toWireType":function(destructors,value){if(!(typeof value==="string")){throwBindingError("Cannot pass non-string to C++ string type "+name)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length>>shift;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_value_object(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor){structRegistrations[rawType]={name:readLatin1String(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),fields:[]}}function __embind_register_value_object_field(structType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){structRegistrations[structType].fields.push({fieldName:readLatin1String(fieldName),getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}function requireHandle(handle){if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value}function __emval_as(handle,returnType,destructorsRef){handle=requireHandle(handle);returnType=requireRegisteredType(returnType,"emval::as");var destructors=[];var rd=__emval_register(destructors);HEAP32[destructorsRef>>2]=rd;return returnType["toWireType"](destructors,handle)}var emval_symbols={};function getStringOrSymbol(address){var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}else{return symbol}}var emval_methodCallers=[];function __emval_call_void_method(caller,handle,methodName,args){caller=emval_methodCallers[caller];handle=requireHandle(handle);methodName=getStringOrSymbol(methodName);caller(handle,methodName,null,args)}function emval_get_global(){if(typeof globalThis==="object"){return globalThis}return function(){return Function}()("return this")()}function __emval_get_global(name){if(name===0){return __emval_register(emval_get_global())}else{name=getStringOrSymbol(name);return __emval_register(emval_get_global()[name])}}function __emval_addMethodCaller(caller){var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id}function __emval_lookupTypes(argCount,argTypes){var a=new Array(argCount);for(var i=0;i<argCount;++i){a[i]=requireRegisteredType(HEAP32[(argTypes>>2)+i],"parameter "+i)}return a}function __emval_get_method_caller(argCount,argTypes){var types=__emval_lookupTypes(argCount,argTypes);var retType=types[0];var signatureName=retType.name+"_$"+types.slice(1).map(function(t){return t.name}).join("_")+"$";var params=["retType"];var args=[retType];var argsList="";for(var i=0;i<argCount-1;++i){argsList+=(i!==0?", ":"")+"arg"+i;params.push("argType"+i);args.push(types[1+i])}var functionName=makeLegalFunctionName("methodCaller_"+signatureName);var functionBody="return function "+functionName+"(handle, name, destructors, args) {\n";var offset=0;for(var i=0;i<argCount-1;++i){functionBody+="    var arg"+i+" = argType"+i+".readValueFromPointer(args"+(offset?"+"+offset:"")+");\n";offset+=types[i+1]["argPackAdvance"]}functionBody+="    var rv = handle[name]("+argsList+");\n";for(var i=0;i<argCount-1;++i){if(types[i+1]["deleteObject"]){functionBody+="    argType"+i+".deleteObject(arg"+i+");\n"}}if(!retType.isVoid){functionBody+="    return retType.toWireType(destructors, rv);\n"}functionBody+="};\n";params.push(functionBody);var invokerFunction=new_(Function,params).apply(null,args);return __emval_addMethodCaller(invokerFunction)}function __emval_get_module_property(name){name=getStringOrSymbol(name);return __emval_register(Module[name])}function __emval_get_property(handle,key){handle=requireHandle(handle);key=requireHandle(key);return __emval_register(handle[key])}function __emval_incref(handle){if(handle>4){emval_handle_array[handle].refcount+=1}}function craftEmvalAllocator(argCount){var argsList="";for(var i=0;i<argCount;++i){argsList+=(i!==0?", ":"")+"arg"+i}var functionBody="return function emval_allocator_"+argCount+"(constructor, argTypes, args) {\n";for(var i=0;i<argCount;++i){functionBody+="var argType"+i+" = requireRegisteredType(Module['HEAP32'][(argTypes >>> 2) + "+i+'], "parameter '+i+'");\n'+"var arg"+i+" = argType"+i+".readValueFromPointer(args);\n"+"args += argType"+i+"['argPackAdvance'];\n"}functionBody+="var obj = new constructor("+argsList+");\n"+"return __emval_register(obj);\n"+"}\n";return new Function("requireRegisteredType","Module","__emval_register",functionBody)(requireRegisteredType,Module,__emval_register)}var emval_newers={};function __emval_new(handle,argCount,argTypes,args){handle=requireHandle(handle);var newer=emval_newers[argCount];if(!newer){newer=craftEmvalAllocator(argCount);emval_newers[argCount]=newer}return newer(handle,argTypes,args)}function __emval_new_cstring(v){return __emval_register(getStringOrSymbol(v))}function __emval_run_destructors(handle){var destructors=emval_handle_array[handle].value;runDestructors(destructors);__emval_decref(handle)}function _abort(){abort()}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_size(){return HEAPU8.length}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;var oldSize=_emscripten_get_heap_size();var maxHeapSize=2147483648;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var SYSCALLS={mappings:{},buffers:[null,[],[]],printChar:function(stream,curr){var buffer=SYSCALLS.buffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},get64:function(low,high){return low}};function _fd_close(fd){return 0}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){}function _fd_write(fd,iov,iovcnt,pnum){var num=0;for(var i=0;i<iovcnt;i++){var ptr=HEAP32[iov+i*8>>2];var len=HEAP32[iov+(i*8+4)>>2];for(var j=0;j<len;j++){SYSCALLS.printChar(fd,HEAPU8[ptr+j])}num+=len}HEAP32[pnum>>2]=num;return 0}function _setTempRet0($i){setTempRet0($i|0)}InternalError=Module["InternalError"]=extendError(Error,"InternalError");embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");init_ClassHandle();init_RegisteredPointer();init_embind();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");init_emval();__ATINIT__.push({func:function(){___wasm_call_ctors()}});var asmLibraryArg={"t":__embind_finalize_value_object,"I":__embind_register_bool,"x":__embind_register_class,"w":__embind_register_class_constructor,"d":__embind_register_class_function,"k":__embind_register_constant,"H":__embind_register_emval,"n":__embind_register_enum,"a":__embind_register_enum_value,"A":__embind_register_float,"i":__embind_register_function,"j":__embind_register_integer,"h":__embind_register_memory_view,"B":__embind_register_std_string,"v":__embind_register_std_wstring,"u":__embind_register_value_object,"c":__embind_register_value_object_field,"J":__embind_register_void,"m":__emval_as,"s":__emval_call_void_method,"b":__emval_decref,"y":__emval_get_global,"p":__emval_get_method_caller,"r":__emval_get_module_property,"e":__emval_get_property,"g":__emval_incref,"q":__emval_new,"f":__emval_new_cstring,"l":__emval_run_destructors,"o":_abort,"E":_emscripten_memcpy_big,"F":_emscripten_resize_heap,"G":_fd_close,"C":_fd_seek,"z":_fd_write,"D":_setTempRet0};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["M"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["N"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["O"]).apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return(___getTypeName=Module["___getTypeName"]=Module["asm"]["P"]).apply(null,arguments)};var ___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=function(){return(___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=Module["asm"]["Q"]).apply(null,arguments)};var dynCall_jiji=Module["dynCall_jiji"]=function(){return(dynCall_jiji=Module["dynCall_jiji"]=Module["asm"]["R"]).apply(null,arguments)};var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run();
+var Module=typeof BASIS!=="undefined"?BASIS:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var tempRet0=0;var setTempRet0=function(value){tempRet0=value};var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){err("no native wasm support detected")}var wasmMemory;var wasmTable=new WebAssembly.Table({"initial":157,"maximum":157+0,"element":"anyfunc"});var ABORT=false;var EXITSTATUS=0;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx<endPtr){var u0=heap[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heap[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heap[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heap[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function UTF16ToString(ptr,maxBytesToRead){var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder){return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr))}else{var i=0;var str="";while(1){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0||i==maxBytesToRead/2)return str;++i;str+=String.fromCharCode(codeUnit)}}}function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite<str.length*2?maxBytesToWrite/2:str.length;for(var i=0;i<numCharsToWrite;++i){var codeUnit=str.charCodeAt(i);HEAP16[outPtr>>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}function lengthBytesUTF16(str){return str.length*2}function UTF32ToString(ptr,maxBytesToRead){var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str}function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr}function lengthBytesUTF32(str){var len=0;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343)++i;len+=4}return len}var WASM_PAGE_SIZE=65536;function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var DYNAMIC_BASE=5552064,DYNAMICTOP_PTR=309024;var INITIAL_INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;if(Module["wasmMemory"]){wasmMemory=Module["wasmMemory"]}else{wasmMemory=new WebAssembly.Memory({"initial":INITIAL_INITIAL_MEMORY/WASM_PAGE_SIZE,"maximum":2147483648/WASM_PAGE_SIZE})}if(wasmMemory){buffer=wasmMemory.buffer}INITIAL_INITIAL_MEMORY=buffer.byteLength;updateGlobalBufferAndViews(buffer);HEAP32[DYNAMICTOP_PTR>>2]=DYNAMIC_BASE;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Module["dynCall_v"](func)}else{Module["dynCall_vi"](func,callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var Math_ceil=Math.ceil;var Math_floor=Math.floor;var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";out(what);err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";throw new WebAssembly.RuntimeError(what)}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";function isFileURI(filename){return hasPrefix(filename,fileURIPrefix)}var wasmBinaryFile="basis_transcoder.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return new Promise(function(resolve,reject){resolve(getBinary())})}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync();return{}}__ATINIT__.push({func:function(){___wasm_call_ctors()}});var structRegistrations={};function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function makeLegalFunctionName(name){if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return"_"+name}else{return name}}function createNamedFunction(name,body){name=makeLegalFunctionName(name);return new Function("body","return function "+name+"() {\n"+'    "use strict";'+"    return body.apply(this, arguments);\n"+"};\n")(body)}function extendError(baseErrorType,errorName){var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return this.name+": "+this.message}};return errorClass}var InternalError=undefined;function throwInternalError(message){throw new InternalError(message)}function whenDependentTypesAreResolved(myTypes,dependentTypes,getTypeConverters){myTypes.forEach(function(type){typeDependencies[type]=dependentTypes});function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i<myTypes.length;++i){registerType(myTypes[i],myTypeConverters[i])}}var typeConverters=new Array(dependentTypes.length);var unregisteredTypes=[];var registered=0;dependentTypes.forEach(function(dt,i){if(registeredTypes.hasOwnProperty(dt)){typeConverters[i]=registeredTypes[dt]}else{unregisteredTypes.push(dt);if(!awaitingDependencies.hasOwnProperty(dt)){awaitingDependencies[dt]=[]}awaitingDependencies[dt].push(function(){typeConverters[i]=registeredTypes[dt];++registered;if(registered===unregisteredTypes.length){onComplete(typeConverters)}})}});if(0===unregisteredTypes.length){onComplete(typeConverters)}}function __embind_finalize_value_object(structType){var reg=structRegistrations[structType];delete structRegistrations[structType];var rawConstructor=reg.rawConstructor;var rawDestructor=reg.rawDestructor;var fieldRecords=reg.fields;var fieldTypes=fieldRecords.map(function(field){return field.getterReturnType}).concat(fieldRecords.map(function(field){return field.setterArgumentType}));whenDependentTypesAreResolved([structType],fieldTypes,function(fieldTypes){var fields={};fieldRecords.forEach(function(field,i){var fieldName=field.fieldName;var getterReturnType=fieldTypes[i];var getter=field.getter;var getterContext=field.getterContext;var setterArgumentType=fieldTypes[i+fieldRecords.length];var setter=field.setter;var setterContext=field.setterContext;fields[fieldName]={read:function(ptr){return getterReturnType["fromWireType"](getter(getterContext,ptr))},write:function(ptr,o){var destructors=[];setter(setterContext,ptr,setterArgumentType["toWireType"](destructors,o));runDestructors(destructors)}}});return[{name:reg.name,"fromWireType":function(ptr){var rv={};for(var i in fields){rv[i]=fields[i].read(ptr)}rawDestructor(ptr);return rv},"toWireType":function(destructors,o){for(var fieldName in fields){if(!(fieldName in o)){throw new TypeError("Missing field")}}var ptr=rawConstructor();for(fieldName in fields){fields[fieldName].write(ptr,o[fieldName])}if(destructors!==null){destructors.push(rawDestructor,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:rawDestructor}]})}function getShiftFromSize(size){switch(size){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+size)}}function embind_init_charCodes(){var codes=new Array(256);for(var i=0;i<256;++i){codes[i]=String.fromCharCode(i)}embind_charCodes=codes}var embind_charCodes=undefined;function readLatin1String(ptr){var ret="";var c=ptr;while(HEAPU8[c]){ret+=embind_charCodes[HEAPU8[c++]]}return ret}var BindingError=undefined;function throwBindingError(message){throw new BindingError(message)}function registerType(rawType,registeredInstance,options){options=options||{};if(!("argPackAdvance"in registeredInstance)){throw new TypeError("registerType registeredInstance requires argPackAdvance")}var name=registeredInstance.name;if(!rawType){throwBindingError('type "'+name+'" must have a positive integer typeid pointer')}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError("Cannot register type '"+name+"' twice")}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(function(cb){cb()})}}function __embind_register_bool(rawType,name,size,trueValue,falseValue){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(wt){return!!wt},"toWireType":function(destructors,o){return o?trueValue:falseValue},"argPackAdvance":8,"readValueFromPointer":function(pointer){var heap;if(size===1){heap=HEAP8}else if(size===2){heap=HEAP16}else if(size===4){heap=HEAP32}else{throw new TypeError("Unknown boolean type size: "+name)}return this["fromWireType"](heap[pointer>>shift])},destructorFunction:null})}function ClassHandle_isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right}function shallowCopyInternalPointer(o){return{count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType}}function throwInstanceAlreadyDeleted(obj){function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")}var finalizationGroup=false;function detachFinalizer(handle){}function runDestructor($$){if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}}function releaseClassHandle($$){$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}}function attachFinalizer(handle){if("undefined"===typeof FinalizationGroup){attachFinalizer=function(handle){return handle};return handle}finalizationGroup=new FinalizationGroup(function(iter){for(var result=iter.next();!result.done;result=iter.next()){var $$=result.value;if(!$$.ptr){console.warn("object already deleted: "+$$.ptr)}else{releaseClassHandle($$)}}});attachFinalizer=function(handle){finalizationGroup.register(handle,handle.$$,handle.$$);return handle};detachFinalizer=function(handle){finalizationGroup.unregister(handle.$$)};return attachFinalizer(handle)}function ClassHandle_clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}}function ClassHandle_delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}}function ClassHandle_isDeleted(){return!this.$$.ptr}var delayFunction=undefined;var deletionQueue=[];function flushPendingDeletes(){while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}}function ClassHandle_deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}function init_ClassHandle(){ClassHandle.prototype["isAliasOf"]=ClassHandle_isAliasOf;ClassHandle.prototype["clone"]=ClassHandle_clone;ClassHandle.prototype["delete"]=ClassHandle_delete;ClassHandle.prototype["isDeleted"]=ClassHandle_isDeleted;ClassHandle.prototype["deleteLater"]=ClassHandle_deleteLater}function ClassHandle(){}var registeredPointers={};function ensureOverloadTable(proto,methodName,humanName){if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(){if(!proto[methodName].overloadTable.hasOwnProperty(arguments.length)){throwBindingError("Function '"+humanName+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+proto[methodName].overloadTable+")!")}return proto[methodName].overloadTable[arguments.length].apply(this,arguments)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}}function exposePublicSymbol(name,value,numArguments){if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError("Cannot register public name '"+name+"' twice")}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError("Cannot register multiple overloads of a function with the same number of arguments ("+numArguments+")!")}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}}function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}function upcastPointer(ptr,ptrClass,desiredClass){while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError("Expected null or instance of "+desiredClass.name+", got an instance of "+ptrClass.name)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr}function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,__emval_register(function(){clonedHandle["delete"]()}));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupporting sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+handle.$$.ptrType.name+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function RegisteredPointer_getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr}function RegisteredPointer_destructor(ptr){if(this.rawDestructor){this.rawDestructor(ptr)}}function RegisteredPointer_deleteObject(handle){if(handle!==null){handle["delete"]()}}function downcastPointer(ptr,ptrClass,desiredClass){if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)}function getInheritedInstanceCount(){return Object.keys(registeredInstances).length}function getLiveInheritedInstances(){var rv=[];for(var k in registeredInstances){if(registeredInstances.hasOwnProperty(k)){rv.push(registeredInstances[k])}}return rv}function setDelayFunction(fn){delayFunction=fn;if(deletionQueue.length&&delayFunction){delayFunction(flushPendingDeletes)}}function init_embind(){Module["getInheritedInstanceCount"]=getInheritedInstanceCount;Module["getLiveInheritedInstances"]=getLiveInheritedInstances;Module["flushPendingDeletes"]=flushPendingDeletes;Module["setDelayFunction"]=setDelayFunction}var registeredInstances={};function getBasestPointer(class_,ptr){if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr}function getInheritedInstance(class_,ptr){ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]}function makeClassHandle(prototype,record){if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record}}))}function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr:ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}function init_RegisteredPointer(){RegisteredPointer.prototype.getPointee=RegisteredPointer_getPointee;RegisteredPointer.prototype.destructor=RegisteredPointer_destructor;RegisteredPointer.prototype["argPackAdvance"]=8;RegisteredPointer.prototype["readValueFromPointer"]=simpleReadValueFromPointer;RegisteredPointer.prototype["deleteObject"]=RegisteredPointer_deleteObject;RegisteredPointer.prototype["fromWireType"]=RegisteredPointer_fromWireType}function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&&registeredClass.baseClass===undefined){if(isConst){this["toWireType"]=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this["toWireType"]=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this["toWireType"]=genericPointerToWireType}}function replacePublicSymbol(name,value,numArguments){if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistant public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}}function embind__requireFunction(signature,rawFunction){signature=readLatin1String(signature);function makeDynCaller(dynCall){var args=[];for(var i=1;i<signature.length;++i){args.push("a"+i)}var name="dynCall_"+signature+"_"+rawFunction;var body="return function "+name+"("+args.join(", ")+") {\n";body+="    return dynCall(rawFunction"+(args.length?", ":"")+args.join(", ")+");\n";body+="};\n";return new Function("dynCall","rawFunction",body)(dynCall,rawFunction)}var dc=Module["dynCall_"+signature];var fp=makeDynCaller(dc);if(typeof fp!=="function"){throwBindingError("unknown function pointer with signature "+signature+": "+rawFunction)}return fp}var UnboundTypeError=undefined;function getTypeName(type){var ptr=___getTypeName(type);var rv=readLatin1String(ptr);_free(ptr);return rv}function throwUnboundTypeError(message,types){var unboundTypes=[];var seen={};function visit(type){if(seen[type]){return}if(registeredTypes[type]){return}if(typeDependencies[type]){typeDependencies[type].forEach(visit);return}unboundTypes.push(type);seen[type]=true}types.forEach(visit);throw new UnboundTypeError(message+": "+unboundTypes.map(getTypeName).join([", "]))}function __embind_register_class(rawType,rawPointerType,rawConstPointerType,baseClassRawType,getActualTypeSignature,getActualType,upcastSignature,upcast,downcastSignature,downcast,name,destructorSignature,rawDestructor){name=readLatin1String(name);getActualType=embind__requireFunction(getActualTypeSignature,getActualType);if(upcast){upcast=embind__requireFunction(upcastSignature,upcast)}if(downcast){downcast=embind__requireFunction(downcastSignature,downcast)}rawDestructor=embind__requireFunction(destructorSignature,rawDestructor);var legalFunctionName=makeLegalFunctionName(name);exposePublicSymbol(legalFunctionName,function(){throwUnboundTypeError("Cannot construct "+name+" due to unbound types",[baseClassRawType])});whenDependentTypesAreResolved([rawType,rawPointerType,rawConstPointerType],baseClassRawType?[baseClassRawType]:[],function(base){base=base[0];var baseClass;var basePrototype;if(baseClassRawType){baseClass=base.registeredClass;basePrototype=baseClass.instancePrototype}else{basePrototype=ClassHandle.prototype}var constructor=createNamedFunction(legalFunctionName,function(){if(Object.getPrototypeOf(this)!==instancePrototype){throw new BindingError("Use 'new' to construct "+name)}if(undefined===registeredClass.constructor_body){throw new BindingError(name+" has no accessible constructor")}var body=registeredClass.constructor_body[arguments.length];if(undefined===body){throw new BindingError("Tried to invoke ctor of "+name+" with invalid number of parameters ("+arguments.length+") - expected ("+Object.keys(registeredClass.constructor_body).toString()+") parameters instead!")}return body.apply(this,arguments)});var instancePrototype=Object.create(basePrototype,{constructor:{value:constructor}});constructor.prototype=instancePrototype;var registeredClass=new RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast);var referenceConverter=new RegisteredPointer(name,registeredClass,true,false,false);var pointerConverter=new RegisteredPointer(name+"*",registeredClass,false,false,false);var constPointerConverter=new RegisteredPointer(name+" const*",registeredClass,false,true,false);registeredPointers[rawType]={pointerType:pointerConverter,constPointerType:constPointerConverter};replacePublicSymbol(legalFunctionName,constructor);return[referenceConverter,pointerConverter,constPointerConverter]})}function heap32VectorToArray(count,firstElement){var array=[];for(var i=0;i<count;i++){array.push(HEAP32[(firstElement>>2)+i])}return array}function __embind_register_class_constructor(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor){assert(argCount>0);var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);var args=[rawConstructor];var destructors=[];whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName="constructor "+classType.name;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError("Cannot register multiple constructors with identical number of parameters ("+(argCount-1)+") for class '"+classType.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!")}classType.registeredClass.constructor_body[argCount-1]=function unboundTypeHandler(){throwUnboundTypeError("Cannot construct "+classType.name+" due to unbound types",rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){classType.registeredClass.constructor_body[argCount-1]=function constructor_body(){if(arguments.length!==argCount-1){throwBindingError(humanName+" called with "+arguments.length+" arguments, expected "+(argCount-1))}destructors.length=0;args.length=argCount;for(var i=1;i<argCount;++i){args[i]=argTypes[i]["toWireType"](destructors,arguments[i-1])}var ptr=invoker.apply(null,args);runDestructors(destructors);return argTypes[0]["fromWireType"](ptr)};return[]});return[]})}function new_(constructor,argumentList){if(!(constructor instanceof Function)){throw new TypeError("new_ called with constructor type "+typeof constructor+" which is not a function")}var dummy=createNamedFunction(constructor.name||"unknownFunctionName",function(){});dummy.prototype=constructor.prototype;var obj=new dummy;var r=constructor.apply(obj,argumentList);return r instanceof Object?r:obj}function craftInvokerFunction(humanName,argTypes,classType,cppInvokerFunc,cppTargetFunc){var argCount=argTypes.length;if(argCount<2){throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!")}var isClassMethodFunc=argTypes[1]!==null&&classType!==null;var needsDestructorStack=false;for(var i=1;i<argTypes.length;++i){if(argTypes[i]!==null&&argTypes[i].destructorFunction===undefined){needsDestructorStack=true;break}}var returns=argTypes[0].name!=="void";var argsList="";var argsListWired="";for(var i=0;i<argCount-2;++i){argsList+=(i!==0?", ":"")+"arg"+i;argsListWired+=(i!==0?", ":"")+"arg"+i+"Wired"}var invokerFnBody="return function "+makeLegalFunctionName(humanName)+"("+argsList+") {\n"+"if (arguments.length !== "+(argCount-2)+") {\n"+"throwBindingError('function "+humanName+" called with ' + arguments.length + ' arguments, expected "+(argCount-2)+" args!');\n"+"}\n";if(needsDestructorStack){invokerFnBody+="var destructors = [];\n"}var dtorStack=needsDestructorStack?"destructors":"null";var args1=["throwBindingError","invoker","fn","runDestructors","retType","classParam"];var args2=[throwBindingError,cppInvokerFunc,cppTargetFunc,runDestructors,argTypes[0],argTypes[1]];if(isClassMethodFunc){invokerFnBody+="var thisWired = classParam.toWireType("+dtorStack+", this);\n"}for(var i=0;i<argCount-2;++i){invokerFnBody+="var arg"+i+"Wired = argType"+i+".toWireType("+dtorStack+", arg"+i+"); // "+argTypes[i+2].name+"\n";args1.push("argType"+i);args2.push(argTypes[i+2])}if(isClassMethodFunc){argsListWired="thisWired"+(argsListWired.length>0?", ":"")+argsListWired}invokerFnBody+=(returns?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\n"}else{for(var i=isClassMethodFunc?1:2;i<argTypes.length;++i){var paramName=i===1?"thisWired":"arg"+(i-2)+"Wired";if(argTypes[i].destructorFunction!==null){invokerFnBody+=paramName+"_dtor("+paramName+"); // "+argTypes[i].name+"\n";args1.push(paramName+"_dtor");args2.push(argTypes[i].destructorFunction)}}}if(returns){invokerFnBody+="var ret = retType.fromWireType(rv);\n"+"return ret;\n"}else{}invokerFnBody+="}\n";args1.push(invokerFnBody);var invokerFunction=new_(Function,args1).apply(null,args2);return invokerFunction}function __embind_register_class_function(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,context,isPureVirtual){var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=readLatin1String(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker);whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName=classType.name+"."+methodName;if(isPureVirtual){classType.registeredClass.pureVirtualFunctions.push(methodName)}function unboundTypesHandler(){throwUnboundTypeError("Cannot call "+humanName+" due to unbound types",rawArgTypes)}var proto=classType.registeredClass.instancePrototype;var method=proto[methodName];if(undefined===method||undefined===method.overloadTable&&method.className!==classType.name&&method.argCount===argCount-2){unboundTypesHandler.argCount=argCount-2;unboundTypesHandler.className=classType.name;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-2]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){var memberFunction=craftInvokerFunction(humanName,argTypes,classType,rawInvoker,context);if(undefined===proto[methodName].overloadTable){memberFunction.argCount=argCount-2;proto[methodName]=memberFunction}else{proto[methodName].overloadTable[argCount-2]=memberFunction}return[]});return[]})}function __embind_register_constant(name,type,value){name=readLatin1String(name);whenDependentTypesAreResolved([],[type],function(type){type=type[0];Module[name]=type["fromWireType"](value);return[]})}var emval_free_list=[];var emval_handle_array=[{},{value:undefined},{value:null},{value:true},{value:false}];function __emval_decref(handle){if(handle>4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i<emval_handle_array.length;++i){if(emval_handle_array[i]!==undefined){++count}}return count}function get_first_emval(){for(var i=5;i<emval_handle_array.length;++i){if(emval_handle_array[i]!==undefined){return emval_handle_array[i]}}return null}function init_emval(){Module["count_emval_handles"]=count_emval_handles;Module["get_first_emval"]=get_first_emval}function __emval_register(value){switch(value){case undefined:{return 1}case null:{return 2}case true:{return 3}case false:{return 4}default:{var handle=emval_free_list.length?emval_free_list.pop():emval_handle_array.length;emval_handle_array[handle]={refcount:1,value:value};return handle}}}function __embind_register_emval(rawType,name){name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(handle){var rv=emval_handle_array[handle].value;__emval_decref(handle);return rv},"toWireType":function(destructors,value){return __emval_register(value)},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:null})}function enumReadValueFromPointer(name,shift,signed){switch(shift){case 0:return function(pointer){var heap=signed?HEAP8:HEAPU8;return this["fromWireType"](heap[pointer])};case 1:return function(pointer){var heap=signed?HEAP16:HEAPU16;return this["fromWireType"](heap[pointer>>1])};case 2:return function(pointer){var heap=signed?HEAP32:HEAPU32;return this["fromWireType"](heap[pointer>>2])};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_enum(rawType,name,size,isSigned){var shift=getShiftFromSize(size);name=readLatin1String(name);function ctor(){}ctor.values={};registerType(rawType,{name:name,constructor:ctor,"fromWireType":function(c){return this.constructor.values[c]},"toWireType":function(destructors,c){return c.value},"argPackAdvance":8,"readValueFromPointer":enumReadValueFromPointer(name,shift,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor)}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __embind_register_enum_value(rawEnumType,name,enumValue){var enumType=requireRegisteredType(rawEnumType,"enum");name=readLatin1String(name);var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(enumType.name+"_"+name,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value}function _embind_repr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function __embind_register_function(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn){var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError("Cannot call "+name+" due to unbound types",argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn),argCount-1);return[]})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=function(value){return value};if(minRange===0){var bitshift=32-8*size;fromWireType=function(value){return value<<bitshift>>>bitshift}}var isUnsignedType=name.indexOf("unsigned")!=-1;registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}if(value<minRange||value>maxRange){throw new TypeError('Passing a number "'+_embind_repr(value)+'" from JS side to C/C++ side to an argument of type "'+name+'", which is outside the valid range ['+minRange+", "+maxRange+"]!")}return isUnsignedType?value>>>0:value|0},"argPackAdvance":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var str;if(stdStringIsUTF8){var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i;if(HEAPU8[currentBytePtr]==0||i==length){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i<length;++i){a[i]=String.fromCharCode(HEAPU8[value+4+i])}str=a.join("")}_free(value);return str},"toWireType":function(destructors,value){if(value instanceof ArrayBuffer){value=new Uint8Array(value)}var getLength;var valueIsOfTypeString=typeof value==="string";if(!(valueIsOfTypeString||value instanceof Uint8Array||value instanceof Uint8ClampedArray||value instanceof Int8Array)){throwBindingError("Cannot pass non-string to std::string")}if(stdStringIsUTF8&&valueIsOfTypeString){getLength=function(){return lengthBytesUTF8(value)}}else{getLength=function(){return value.length}}var length=getLength();var ptr=_malloc(4+length+1);HEAPU32[ptr>>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr+4,length+1)}else{if(valueIsOfTypeString){for(var i=0;i<length;++i){var charCode=value.charCodeAt(i);if(charCode>255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+4+i]=charCode}}else{for(var i=0;i<length;++i){HEAPU8[ptr+4+i]=value[i]}}}if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_std_wstring(rawType,charSize,name){name=readLatin1String(name);var decodeString,encodeString,getHeap,lengthBytesUTF,shift;if(charSize===2){decodeString=UTF16ToString;encodeString=stringToUTF16;lengthBytesUTF=lengthBytesUTF16;getHeap=function(){return HEAPU16};shift=1}else if(charSize===4){decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32;getHeap=function(){return HEAPU32};shift=2}registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var HEAP=getHeap();var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(HEAP[currentBytePtr>>shift]==0||i==length){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_free(value);return str},"toWireType":function(destructors,value){if(!(typeof value==="string")){throwBindingError("Cannot pass non-string to C++ string type "+name)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length>>shift;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_value_object(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor){structRegistrations[rawType]={name:readLatin1String(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),fields:[]}}function __embind_register_value_object_field(structType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){structRegistrations[structType].fields.push({fieldName:readLatin1String(fieldName),getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}function requireHandle(handle){if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value}function __emval_as(handle,returnType,destructorsRef){handle=requireHandle(handle);returnType=requireRegisteredType(returnType,"emval::as");var destructors=[];var rd=__emval_register(destructors);HEAP32[destructorsRef>>2]=rd;return returnType["toWireType"](destructors,handle)}var emval_symbols={};function getStringOrSymbol(address){var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}else{return symbol}}var emval_methodCallers=[];function __emval_call_void_method(caller,handle,methodName,args){caller=emval_methodCallers[caller];handle=requireHandle(handle);methodName=getStringOrSymbol(methodName);caller(handle,methodName,null,args)}function emval_get_global(){if(typeof globalThis==="object"){return globalThis}return function(){return Function}()("return this")()}function __emval_get_global(name){if(name===0){return __emval_register(emval_get_global())}else{name=getStringOrSymbol(name);return __emval_register(emval_get_global()[name])}}function __emval_addMethodCaller(caller){var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id}function __emval_lookupTypes(argCount,argTypes){var a=new Array(argCount);for(var i=0;i<argCount;++i){a[i]=requireRegisteredType(HEAP32[(argTypes>>2)+i],"parameter "+i)}return a}function __emval_get_method_caller(argCount,argTypes){var types=__emval_lookupTypes(argCount,argTypes);var retType=types[0];var signatureName=retType.name+"_$"+types.slice(1).map(function(t){return t.name}).join("_")+"$";var params=["retType"];var args=[retType];var argsList="";for(var i=0;i<argCount-1;++i){argsList+=(i!==0?", ":"")+"arg"+i;params.push("argType"+i);args.push(types[1+i])}var functionName=makeLegalFunctionName("methodCaller_"+signatureName);var functionBody="return function "+functionName+"(handle, name, destructors, args) {\n";var offset=0;for(var i=0;i<argCount-1;++i){functionBody+="    var arg"+i+" = argType"+i+".readValueFromPointer(args"+(offset?"+"+offset:"")+");\n";offset+=types[i+1]["argPackAdvance"]}functionBody+="    var rv = handle[name]("+argsList+");\n";for(var i=0;i<argCount-1;++i){if(types[i+1]["deleteObject"]){functionBody+="    argType"+i+".deleteObject(arg"+i+");\n"}}if(!retType.isVoid){functionBody+="    return retType.toWireType(destructors, rv);\n"}functionBody+="};\n";params.push(functionBody);var invokerFunction=new_(Function,params).apply(null,args);return __emval_addMethodCaller(invokerFunction)}function __emval_get_module_property(name){name=getStringOrSymbol(name);return __emval_register(Module[name])}function __emval_get_property(handle,key){handle=requireHandle(handle);key=requireHandle(key);return __emval_register(handle[key])}function __emval_incref(handle){if(handle>4){emval_handle_array[handle].refcount+=1}}function craftEmvalAllocator(argCount){var argsList="";for(var i=0;i<argCount;++i){argsList+=(i!==0?", ":"")+"arg"+i}var functionBody="return function emval_allocator_"+argCount+"(constructor, argTypes, args) {\n";for(var i=0;i<argCount;++i){functionBody+="var argType"+i+" = requireRegisteredType(Module['HEAP32'][(argTypes >>> 2) + "+i+'], "parameter '+i+'");\n'+"var arg"+i+" = argType"+i+".readValueFromPointer(args);\n"+"args += argType"+i+"['argPackAdvance'];\n"}functionBody+="var obj = new constructor("+argsList+");\n"+"return __emval_register(obj);\n"+"}\n";return new Function("requireRegisteredType","Module","__emval_register",functionBody)(requireRegisteredType,Module,__emval_register)}var emval_newers={};function __emval_new(handle,argCount,argTypes,args){handle=requireHandle(handle);var newer=emval_newers[argCount];if(!newer){newer=craftEmvalAllocator(argCount);emval_newers[argCount]=newer}return newer(handle,argTypes,args)}function __emval_new_cstring(v){return __emval_register(getStringOrSymbol(v))}function __emval_run_destructors(handle){var destructors=emval_handle_array[handle].value;runDestructors(destructors);__emval_decref(handle)}function _abort(){abort()}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_size(){return HEAPU8.length}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;var oldSize=_emscripten_get_heap_size();var PAGE_MULTIPLE=65536;var maxHeapSize=2147483648;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),PAGE_MULTIPLE));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var PATH={splitPath:function(filename){var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:function(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:function(path){var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:function(path){if(path==="/")return"/";var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},extname:function(path){return PATH.splitPath(path)[3]},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:function(l,r){return PATH.normalize(l+"/"+r)}};var SYSCALLS={mappings:{},buffers:[null,[],[]],printChar:function(stream,curr){var buffer=SYSCALLS.buffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},get64:function(low,high){return low}};function _fd_close(fd){return 0}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){}function _fd_write(fd,iov,iovcnt,pnum){var num=0;for(var i=0;i<iovcnt;i++){var ptr=HEAP32[iov+i*8>>2];var len=HEAP32[iov+(i*8+4)>>2];for(var j=0;j<len;j++){SYSCALLS.printChar(fd,HEAPU8[ptr+j])}num+=len}HEAP32[pnum>>2]=num;return 0}function _roundf(d){d=+d;return d>=+0?+Math_floor(d+ +.5):+Math_ceil(d-+.5)}function _setTempRet0($i){setTempRet0($i|0)}InternalError=Module["InternalError"]=extendError(Error,"InternalError");embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");init_ClassHandle();init_RegisteredPointer();init_embind();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");init_emval();var asmLibraryArg={"u":__embind_finalize_value_object,"J":__embind_register_bool,"y":__embind_register_class,"x":__embind_register_class_constructor,"d":__embind_register_class_function,"k":__embind_register_constant,"I":__embind_register_emval,"n":__embind_register_enum,"a":__embind_register_enum_value,"B":__embind_register_float,"i":__embind_register_function,"j":__embind_register_integer,"h":__embind_register_memory_view,"C":__embind_register_std_string,"w":__embind_register_std_wstring,"v":__embind_register_value_object,"c":__embind_register_value_object_field,"K":__embind_register_void,"m":__emval_as,"s":__emval_call_void_method,"b":__emval_decref,"z":__emval_get_global,"t":__emval_get_method_caller,"r":__emval_get_module_property,"e":__emval_get_property,"g":__emval_incref,"q":__emval_new,"f":__emval_new_cstring,"l":__emval_run_destructors,"p":_abort,"F":_emscripten_memcpy_big,"G":_emscripten_resize_heap,"H":_fd_close,"D":_fd_seek,"A":_fd_write,"memory":wasmMemory,"o":_roundf,"E":_setTempRet0,"table":wasmTable};var asm=createWasm();Module["asm"]=asm;var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["L"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["M"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["N"]).apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return(___getTypeName=Module["___getTypeName"]=Module["asm"]["O"]).apply(null,arguments)};var ___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=function(){return(___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=Module["asm"]["P"]).apply(null,arguments)};var dynCall_viii=Module["dynCall_viii"]=function(){return(dynCall_viii=Module["dynCall_viii"]=Module["asm"]["Q"]).apply(null,arguments)};var dynCall_vi=Module["dynCall_vi"]=function(){return(dynCall_vi=Module["dynCall_vi"]=Module["asm"]["R"]).apply(null,arguments)};var dynCall_v=Module["dynCall_v"]=function(){return(dynCall_v=Module["dynCall_v"]=Module["asm"]["S"]).apply(null,arguments)};var dynCall_i=Module["dynCall_i"]=function(){return(dynCall_i=Module["dynCall_i"]=Module["asm"]["T"]).apply(null,arguments)};var dynCall_iii=Module["dynCall_iii"]=function(){return(dynCall_iii=Module["dynCall_iii"]=Module["asm"]["U"]).apply(null,arguments)};var dynCall_ii=Module["dynCall_ii"]=function(){return(dynCall_ii=Module["dynCall_ii"]=Module["asm"]["V"]).apply(null,arguments)};var dynCall_vii=Module["dynCall_vii"]=function(){return(dynCall_vii=Module["dynCall_vii"]=Module["asm"]["W"]).apply(null,arguments)};var dynCall_iiii=Module["dynCall_iiii"]=function(){return(dynCall_iiii=Module["dynCall_iiii"]=Module["asm"]["X"]).apply(null,arguments)};var dynCall_iiiii=Module["dynCall_iiiii"]=function(){return(dynCall_iiiii=Module["dynCall_iiiii"]=Module["asm"]["Y"]).apply(null,arguments)};var dynCall_iiiiii=Module["dynCall_iiiiii"]=function(){return(dynCall_iiiiii=Module["dynCall_iiiiii"]=Module["asm"]["Z"]).apply(null,arguments)};var dynCall_iiiiiiii=Module["dynCall_iiiiiiii"]=function(){return(dynCall_iiiiiiii=Module["dynCall_iiiiiiii"]=Module["asm"]["_"]).apply(null,arguments)};var dynCall_iiiiiiiii=Module["dynCall_iiiiiiiii"]=function(){return(dynCall_iiiiiiiii=Module["dynCall_iiiiiiiii"]=Module["asm"]["$"]).apply(null,arguments)};var dynCall_viiii=Module["dynCall_viiii"]=function(){return(dynCall_viiii=Module["dynCall_viiii"]=Module["asm"]["aa"]).apply(null,arguments)};var dynCall_iiiiiii=Module["dynCall_iiiiiii"]=function(){return(dynCall_iiiiiii=Module["dynCall_iiiiiii"]=Module["asm"]["ba"]).apply(null,arguments)};var dynCall_iiiiiiiiiiiiiiiiiiii=Module["dynCall_iiiiiiiiiiiiiiiiiiii"]=function(){return(dynCall_iiiiiiiiiiiiiiiiiiii=Module["dynCall_iiiiiiiiiiiiiiiiiiii"]=Module["asm"]["ca"]).apply(null,arguments)};var dynCall_iiiiiiiiiiiiiiiiiiiii=Module["dynCall_iiiiiiiiiiiiiiiiiiiii"]=function(){return(dynCall_iiiiiiiiiiiiiiiiiiiii=Module["dynCall_iiiiiiiiiiiiiiiiiiiii"]=Module["asm"]["da"]).apply(null,arguments)};var dynCall_iiiiiiiiiiiiiiiiiii=Module["dynCall_iiiiiiiiiiiiiiiiiii"]=function(){return(dynCall_iiiiiiiiiiiiiiiiiii=Module["dynCall_iiiiiiiiiiiiiiiiiii"]=Module["asm"]["ea"]).apply(null,arguments)};var dynCall_viiiii=Module["dynCall_viiiii"]=function(){return(dynCall_viiiii=Module["dynCall_viiiii"]=Module["asm"]["fa"]).apply(null,arguments)};var dynCall_iiiiiiiiii=Module["dynCall_iiiiiiiiii"]=function(){return(dynCall_iiiiiiiiii=Module["dynCall_iiiiiiiiii"]=Module["asm"]["ga"]).apply(null,arguments)};var dynCall_iiiiiiiiiii=Module["dynCall_iiiiiiiiiii"]=function(){return(dynCall_iiiiiiiiiii=Module["dynCall_iiiiiiiiiii"]=Module["asm"]["ha"]).apply(null,arguments)};var dynCall_jiji=Module["dynCall_jiji"]=function(){return(dynCall_jiji=Module["dynCall_jiji"]=Module["asm"]["ia"]).apply(null,arguments)};var dynCall_viiiiii=Module["dynCall_viiiiii"]=function(){return(dynCall_viiiiii=Module["dynCall_viiiiii"]=Module["asm"]["ja"]).apply(null,arguments)};Module["asm"]=asm;var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run();
 
 
   return BASIS.ready
@@ -14,8 +14,9 @@
 );
 })();
 if (typeof exports === 'object' && typeof module === 'object')
-  module.exports = BASIS;
-else if (typeof define === 'function' && define['amd'])
-  define([], function() { return BASIS; });
-else if (typeof exports === 'object')
-  exports["BASIS"] = BASIS;
+      module.exports = BASIS;
+    else if (typeof define === 'function' && define['amd'])
+      define([], function() { return BASIS; });
+    else if (typeof exports === 'object')
+      exports["BASIS"] = BASIS;
+    
\ No newline at end of file
diff --git a/webgl/transcoder/build/basis_transcoder.wasm b/webgl/transcoder/build/basis_transcoder.wasm
index 4b9c343..bc74770 100644
--- a/webgl/transcoder/build/basis_transcoder.wasm
+++ b/webgl/transcoder/build/basis_transcoder.wasm
Binary files differ
diff --git a/webgl_videotest/6.1.01.basis b/webgl_videotest/6.1.01.basis
new file mode 100644
index 0000000..d3a06dc
--- /dev/null
+++ b/webgl_videotest/6.1.01.basis
Binary files differ
diff --git a/webgl_videotest/basis.js b/webgl_videotest/basis.js
index 73aaf58..f89ea8e 100644
--- a/webgl_videotest/basis.js
+++ b/webgl_videotest/basis.js
@@ -1 +1 @@
-var Module=typeof Module!=="undefined"?Module:{};var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);if(typeof module!=="undefined"){module["exports"]=Module}process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var STACK_ALIGN=16;function warnOnce(text){if(!warnOnce.shown)warnOnce.shown={};if(!warnOnce.shown[text]){warnOnce.shown[text]=1;err(text)}}function convertJsFunctionToWasm(func,sig){if(typeof WebAssembly.Function==="function"){var typeNames={"i":"i32","j":"i64","f":"f32","d":"f64"};var type={parameters:[],results:sig[0]=="v"?[]:[typeNames[sig[0]]]};for(var i=1;i<sig.length;++i){type.parameters.push(typeNames[sig[i]])}return new WebAssembly.Function(type,func)}var typeSection=[1,0,1,96];var sigRet=sig.slice(0,1);var sigParam=sig.slice(1);var typeCodes={"i":127,"j":126,"f":125,"d":124};typeSection.push(sigParam.length);for(var i=0;i<sigParam.length;++i){typeSection.push(typeCodes[sigParam[i]])}if(sigRet=="v"){typeSection.push(0)}else{typeSection=typeSection.concat([1,typeCodes[sigRet]])}typeSection[1]=typeSection.length-2;var bytes=new Uint8Array([0,97,115,109,1,0,0,0].concat(typeSection,[2,7,1,1,101,1,102,0,0,7,5,1,1,102,0,0]));var module=new WebAssembly.Module(bytes);var instance=new WebAssembly.Instance(module,{"e":{"f":func}});var wrappedFunc=instance.exports["f"];return wrappedFunc}var freeTableIndexes=[];var functionsInTableMap;function getEmptyTableSlot(){if(freeTableIndexes.length){return freeTableIndexes.pop()}try{wasmTable.grow(1)}catch(err){if(!(err instanceof RangeError)){throw err}throw"Unable to grow wasm table. Set ALLOW_TABLE_GROWTH."}return wasmTable.length-1}function addFunctionWasm(func,sig){if(!functionsInTableMap){functionsInTableMap=new WeakMap;for(var i=0;i<wasmTable.length;i++){var item=wasmTable.get(i);if(item){functionsInTableMap.set(item,i)}}}if(functionsInTableMap.has(func)){return functionsInTableMap.get(func)}var ret=getEmptyTableSlot();try{wasmTable.set(ret,func)}catch(err){if(!(err instanceof TypeError)){throw err}var wrapped=convertJsFunctionToWasm(func,sig);wasmTable.set(ret,wrapped)}functionsInTableMap.set(func,ret);return ret}var tempRet0=0;var setTempRet0=function(value){tempRet0=value};var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}function getCFunc(ident){var func=Module["_"+ident];assert(func,"Cannot call unknown function "+ident+", make sure it is exported");return func}function ccall(ident,returnType,argTypes,args,opts){var toC={"string":function(str){var ret=0;if(str!==null&&str!==undefined&&str!==0){var len=(str.length<<2)+1;ret=stackAlloc(len);stringToUTF8(str,ret,len)}return ret},"array":function(arr){var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string")return UTF8ToString(ret);if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func.apply(null,cArgs);ret=convertReturnValue(ret);if(stack!==0)stackRestore(stack);return ret}var ALLOC_STACK=1;var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx<endPtr){var u0=heap[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heap[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heap[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heap[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i<str.length;++i){HEAP8[buffer++>>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||749993984;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);throw e}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";function isFileURI(filename){return hasPrefix(filename,fileURIPrefix)}var wasmBinaryFile="basis.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return Promise.resolve().then(getBinary)}function createWasm(){var info={"env":asmLibraryArg,"wasi_snapshot_preview1":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["memory"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["__indirect_function_table"];removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync();return{}}var tempDouble;var tempI64;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}function demangle(func){return func}function demangleAll(text){var regex=/\b_Z[\w\d_]+/g;return text.replace(regex,function(x){var y=demangle(x);return x===y?x:y+" ["+x+"]"})}function jsStackTrace(){var error=new Error;if(!error.stack){try{throw new Error}catch(e){error=e}if(!error.stack){return"(no stack trace available)"}}return error.stack.toString()}function ___assert_fail(condition,filename,line,func){abort("Assertion failed: "+UTF8ToString(condition)+", at: "+[filename?UTF8ToString(filename):"unknown filename",line,func?UTF8ToString(func):"unknown function"])}function _abort(){abort()}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function abortOnCannotGrowMemory(requestedSize){abort("OOM")}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;abortOnCannotGrowMemory(requestedSize)}var SYSCALLS={mappings:{},buffers:[null,[],[]],printChar:function(stream,curr){var buffer=SYSCALLS.buffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},get64:function(low,high){return low}};function _fd_close(fd){return 0}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){}function _fd_write(fd,iov,iovcnt,pnum){var num=0;for(var i=0;i<iovcnt;i++){var ptr=HEAP32[iov+i*8>>2];var len=HEAP32[iov+(i*8+4)>>2];for(var j=0;j<len;j++){SYSCALLS.printChar(fd,HEAPU8[ptr+j])}num+=len}HEAP32[pnum>>2]=num;return 0}function _setTempRet0($i){setTempRet0($i|0)}var ASSERTIONS=false;__ATINIT__.push({func:function(){___wasm_call_ctors()}});var asmLibraryArg={"__assert_fail":___assert_fail,"abort":_abort,"emscripten_memcpy_big":_emscripten_memcpy_big,"emscripten_resize_heap":_emscripten_resize_heap,"fd_close":_fd_close,"fd_seek":_fd_seek,"fd_write":_fd_write,"setTempRet0":_setTempRet0};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["__wasm_call_ctors"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["malloc"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["free"]).apply(null,arguments)};var _basis_init=Module["_basis_init"]=function(){return(_basis_init=Module["_basis_init"]=Module["asm"]["basis_init"]).apply(null,arguments)};var _basis_open=Module["_basis_open"]=function(){return(_basis_open=Module["_basis_open"]=Module["asm"]["basis_open"]).apply(null,arguments)};var _basis_close=Module["_basis_close"]=function(){return(_basis_close=Module["_basis_close"]=Module["asm"]["basis_close"]).apply(null,arguments)};var _basis_get_has_alpha=Module["_basis_get_has_alpha"]=function(){return(_basis_get_has_alpha=Module["_basis_get_has_alpha"]=Module["asm"]["basis_get_has_alpha"]).apply(null,arguments)};var _basis_get_num_images=Module["_basis_get_num_images"]=function(){return(_basis_get_num_images=Module["_basis_get_num_images"]=Module["asm"]["basis_get_num_images"]).apply(null,arguments)};var _basis_get_num_levels=Module["_basis_get_num_levels"]=function(){return(_basis_get_num_levels=Module["_basis_get_num_levels"]=Module["asm"]["basis_get_num_levels"]).apply(null,arguments)};var _basis_get_image_width=Module["_basis_get_image_width"]=function(){return(_basis_get_image_width=Module["_basis_get_image_width"]=Module["asm"]["basis_get_image_width"]).apply(null,arguments)};var _basis_get_image_height=Module["_basis_get_image_height"]=function(){return(_basis_get_image_height=Module["_basis_get_image_height"]=Module["asm"]["basis_get_image_height"]).apply(null,arguments)};var _basis_get_image_transcoded_size_in_bytes=Module["_basis_get_image_transcoded_size_in_bytes"]=function(){return(_basis_get_image_transcoded_size_in_bytes=Module["_basis_get_image_transcoded_size_in_bytes"]=Module["asm"]["basis_get_image_transcoded_size_in_bytes"]).apply(null,arguments)};var _basis_start_transcoding=Module["_basis_start_transcoding"]=function(){return(_basis_start_transcoding=Module["_basis_start_transcoding"]=Module["asm"]["basis_start_transcoding"]).apply(null,arguments)};var _basis_transcode_image=Module["_basis_transcode_image"]=function(){return(_basis_transcode_image=Module["_basis_transcode_image"]=Module["asm"]["basis_transcode_image"]).apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return(___errno_location=Module["___errno_location"]=Module["asm"]["__errno_location"]).apply(null,arguments)};var stackSave=Module["stackSave"]=function(){return(stackSave=Module["stackSave"]=Module["asm"]["stackSave"]).apply(null,arguments)};var stackRestore=Module["stackRestore"]=function(){return(stackRestore=Module["stackRestore"]=Module["asm"]["stackRestore"]).apply(null,arguments)};var stackAlloc=Module["stackAlloc"]=function(){return(stackAlloc=Module["stackAlloc"]=Module["asm"]["stackAlloc"]).apply(null,arguments)};var dynCall_jiji=Module["dynCall_jiji"]=function(){return(dynCall_jiji=Module["dynCall_jiji"]=Module["asm"]["dynCall_jiji"]).apply(null,arguments)};var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run();
+var Module=typeof Module!=="undefined"?Module:{};var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);if(typeof module!=="undefined"){module["exports"]=Module}process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(document.currentScript){scriptDirectory=document.currentScript.src}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var STACK_ALIGN=16;function dynamicAlloc(size){var ret=HEAP32[DYNAMICTOP_PTR>>2];var end=ret+size+15&-16;HEAP32[DYNAMICTOP_PTR>>2]=end;return ret}function getNativeTypeSize(type){switch(type){case"i1":case"i8":return 1;case"i16":return 2;case"i32":return 4;case"i64":return 8;case"float":return 4;case"double":return 8;default:{if(type[type.length-1]==="*"){return 4}else if(type[0]==="i"){var bits=Number(type.substr(1));assert(bits%8===0,"getNativeTypeSize invalid bits "+bits+", type "+type);return bits/8}else{return 0}}}}function warnOnce(text){if(!warnOnce.shown)warnOnce.shown={};if(!warnOnce.shown[text]){warnOnce.shown[text]=1;err(text)}}function convertJsFunctionToWasm(func,sig){if(typeof WebAssembly.Function==="function"){var typeNames={"i":"i32","j":"i64","f":"f32","d":"f64"};var type={parameters:[],results:sig[0]=="v"?[]:[typeNames[sig[0]]]};for(var i=1;i<sig.length;++i){type.parameters.push(typeNames[sig[i]])}return new WebAssembly.Function(type,func)}var typeSection=[1,0,1,96];var sigRet=sig.slice(0,1);var sigParam=sig.slice(1);var typeCodes={"i":127,"j":126,"f":125,"d":124};typeSection.push(sigParam.length);for(var i=0;i<sigParam.length;++i){typeSection.push(typeCodes[sigParam[i]])}if(sigRet=="v"){typeSection.push(0)}else{typeSection=typeSection.concat([1,typeCodes[sigRet]])}typeSection[1]=typeSection.length-2;var bytes=new Uint8Array([0,97,115,109,1,0,0,0].concat(typeSection,[2,7,1,1,101,1,102,0,0,7,5,1,1,102,0,0]));var module=new WebAssembly.Module(bytes);var instance=new WebAssembly.Instance(module,{"e":{"f":func}});var wrappedFunc=instance.exports["f"];return wrappedFunc}var freeTableIndexes=[];var functionsInTableMap;function addFunctionWasm(func,sig){var table=wasmTable;if(!functionsInTableMap){functionsInTableMap=new WeakMap;for(var i=0;i<table.length;i++){var item=table.get(i);if(item){functionsInTableMap.set(item,i)}}}if(functionsInTableMap.has(func)){return functionsInTableMap.get(func)}var ret;if(freeTableIndexes.length){ret=freeTableIndexes.pop()}else{ret=table.length;try{table.grow(1)}catch(err){if(!(err instanceof RangeError)){throw err}throw"Unable to grow wasm table. Set ALLOW_TABLE_GROWTH."}}try{table.set(ret,func)}catch(err){if(!(err instanceof TypeError)){throw err}var wrapped=convertJsFunctionToWasm(func,sig);table.set(ret,wrapped)}functionsInTableMap.set(func,ret);return ret}function removeFunctionWasm(index){functionsInTableMap.delete(wasmTable.get(index));freeTableIndexes.push(index)}var funcWrappers={};function dynCall(sig,ptr,args){if(args&&args.length){return Module["dynCall_"+sig].apply(null,[ptr].concat(args))}else{return Module["dynCall_"+sig].call(null,ptr)}}var tempRet0=0;var setTempRet0=function(value){tempRet0=value};var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){err("no native wasm support detected")}function setValue(ptr,value,type,noSafe){type=type||"i8";if(type.charAt(type.length-1)==="*")type="i32";switch(type){case"i1":HEAP8[ptr>>0]=value;break;case"i8":HEAP8[ptr>>0]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":tempI64=[value>>>0,(tempDouble=value,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[ptr>>2]=tempI64[0],HEAP32[ptr+4>>2]=tempI64[1];break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;default:abort("invalid type for setValue: "+type)}}var wasmMemory;var wasmTable=new WebAssembly.Table({"initial":7,"maximum":7+0,"element":"anyfunc"});var ABORT=false;var EXITSTATUS=0;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}function getCFunc(ident){var func=Module["_"+ident];assert(func,"Cannot call unknown function "+ident+", make sure it is exported");return func}function ccall(ident,returnType,argTypes,args,opts){var toC={"string":function(str){var ret=0;if(str!==null&&str!==undefined&&str!==0){var len=(str.length<<2)+1;ret=stackAlloc(len);stringToUTF8(str,ret,len)}return ret},"array":function(arr){var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string")return UTF8ToString(ret);if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func.apply(null,cArgs);ret=convertReturnValue(ret);if(stack!==0)stackRestore(stack);return ret}var ALLOC_NONE=3;var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx<endPtr){var u0=heap[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heap[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heap[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heap[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i<str.length;++i){HEAP8[buffer++>>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}var WASM_PAGE_SIZE=65536;var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var STACK_BASE=5738768,DYNAMIC_BASE=5738768,DYNAMICTOP_PTR=495728;var INITIAL_INITIAL_MEMORY=Module["INITIAL_MEMORY"]||749993984;if(Module["wasmMemory"]){wasmMemory=Module["wasmMemory"]}else{wasmMemory=new WebAssembly.Memory({"initial":INITIAL_INITIAL_MEMORY/WASM_PAGE_SIZE,"maximum":INITIAL_INITIAL_MEMORY/WASM_PAGE_SIZE})}if(wasmMemory){buffer=wasmMemory.buffer}INITIAL_INITIAL_MEMORY=buffer.byteLength;updateGlobalBufferAndViews(buffer);HEAP32[DYNAMICTOP_PTR>>2]=DYNAMIC_BASE;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Module["dynCall_v"](func)}else{Module["dynCall_vi"](func,callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var Math_abs=Math.abs;var Math_ceil=Math.ceil;var Math_floor=Math.floor;var Math_min=Math.min;var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";out(what);err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";throw new WebAssembly.RuntimeError(what)}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";function isFileURI(filename){return hasPrefix(filename,fileURIPrefix)}var wasmBinaryFile="basis.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return new Promise(function(resolve,reject){resolve(getBinary())})}function createWasm(){var info={"env":asmLibraryArg,"wasi_snapshot_preview1":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync();return{}}var tempDouble;var tempI64;__ATINIT__.push({func:function(){___wasm_call_ctors()}});function demangle(func){return func}function demangleAll(text){var regex=/\b_Z[\w\d_]+/g;return text.replace(regex,function(x){var y=demangle(x);return x===y?x:y+" ["+x+"]"})}function jsStackTrace(){var err=new Error;if(!err.stack){try{throw new Error}catch(e){err=e}if(!err.stack){return"(no stack trace available)"}}return err.stack.toString()}function ___assert_fail(condition,filename,line,func){abort("Assertion failed: "+UTF8ToString(condition)+", at: "+[filename?UTF8ToString(filename):"unknown filename",line,func?UTF8ToString(func):"unknown function"])}function _abort(){abort()}function _emscripten_get_sbrk_ptr(){return 495728}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function abortOnCannotGrowMemory(requestedSize){abort("OOM")}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;abortOnCannotGrowMemory(requestedSize)}var PATH={splitPath:function(filename){var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:function(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:function(path){var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:function(path){if(path==="/")return"/";var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},extname:function(path){return PATH.splitPath(path)[3]},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:function(l,r){return PATH.normalize(l+"/"+r)}};var SYSCALLS={mappings:{},buffers:[null,[],[]],printChar:function(stream,curr){var buffer=SYSCALLS.buffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},get64:function(low,high){return low}};function _fd_close(fd){return 0}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){}function _fd_write(fd,iov,iovcnt,pnum){var num=0;for(var i=0;i<iovcnt;i++){var ptr=HEAP32[iov+i*8>>2];var len=HEAP32[iov+(i*8+4)>>2];for(var j=0;j<len;j++){SYSCALLS.printChar(fd,HEAPU8[ptr+j])}num+=len}HEAP32[pnum>>2]=num;return 0}function _roundf(d){d=+d;return d>=+0?+Math_floor(d+ +.5):+Math_ceil(d-+.5)}function _setTempRet0($i){setTempRet0($i|0)}var ASSERTIONS=false;var asmLibraryArg={"__assert_fail":___assert_fail,"abort":_abort,"emscripten_get_sbrk_ptr":_emscripten_get_sbrk_ptr,"emscripten_memcpy_big":_emscripten_memcpy_big,"emscripten_resize_heap":_emscripten_resize_heap,"fd_close":_fd_close,"fd_seek":_fd_seek,"fd_write":_fd_write,"memory":wasmMemory,"roundf":_roundf,"setTempRet0":_setTempRet0,"table":wasmTable};var asm=createWasm();Module["asm"]=asm;var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["__wasm_call_ctors"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["malloc"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["free"]).apply(null,arguments)};var _basis_set_debug_flags=Module["_basis_set_debug_flags"]=function(){return(_basis_set_debug_flags=Module["_basis_set_debug_flags"]=Module["asm"]["basis_set_debug_flags"]).apply(null,arguments)};var _basis_get_debug_flags=Module["_basis_get_debug_flags"]=function(){return(_basis_get_debug_flags=Module["_basis_get_debug_flags"]=Module["asm"]["basis_get_debug_flags"]).apply(null,arguments)};var _basis_init=Module["_basis_init"]=function(){return(_basis_init=Module["_basis_init"]=Module["asm"]["basis_init"]).apply(null,arguments)};var _basis_open=Module["_basis_open"]=function(){return(_basis_open=Module["_basis_open"]=Module["asm"]["basis_open"]).apply(null,arguments)};var _basis_close=Module["_basis_close"]=function(){return(_basis_close=Module["_basis_close"]=Module["asm"]["basis_close"]).apply(null,arguments)};var _basis_get_has_alpha=Module["_basis_get_has_alpha"]=function(){return(_basis_get_has_alpha=Module["_basis_get_has_alpha"]=Module["asm"]["basis_get_has_alpha"]).apply(null,arguments)};var _basis_get_num_images=Module["_basis_get_num_images"]=function(){return(_basis_get_num_images=Module["_basis_get_num_images"]=Module["asm"]["basis_get_num_images"]).apply(null,arguments)};var _basis_get_num_levels=Module["_basis_get_num_levels"]=function(){return(_basis_get_num_levels=Module["_basis_get_num_levels"]=Module["asm"]["basis_get_num_levels"]).apply(null,arguments)};var _basis_get_image_width=Module["_basis_get_image_width"]=function(){return(_basis_get_image_width=Module["_basis_get_image_width"]=Module["asm"]["basis_get_image_width"]).apply(null,arguments)};var _basis_get_image_height=Module["_basis_get_image_height"]=function(){return(_basis_get_image_height=Module["_basis_get_image_height"]=Module["asm"]["basis_get_image_height"]).apply(null,arguments)};var _basis_get_image_transcoded_size_in_bytes=Module["_basis_get_image_transcoded_size_in_bytes"]=function(){return(_basis_get_image_transcoded_size_in_bytes=Module["_basis_get_image_transcoded_size_in_bytes"]=Module["asm"]["basis_get_image_transcoded_size_in_bytes"]).apply(null,arguments)};var _basis_start_transcoding=Module["_basis_start_transcoding"]=function(){return(_basis_start_transcoding=Module["_basis_start_transcoding"]=Module["asm"]["basis_start_transcoding"]).apply(null,arguments)};var _basis_transcode_image=Module["_basis_transcode_image"]=function(){return(_basis_transcode_image=Module["_basis_transcode_image"]=Module["asm"]["basis_transcode_image"]).apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return(___errno_location=Module["___errno_location"]=Module["asm"]["__errno_location"]).apply(null,arguments)};var stackSave=Module["stackSave"]=function(){return(stackSave=Module["stackSave"]=Module["asm"]["stackSave"]).apply(null,arguments)};var stackAlloc=Module["stackAlloc"]=function(){return(stackAlloc=Module["stackAlloc"]=Module["asm"]["stackAlloc"]).apply(null,arguments)};var stackRestore=Module["stackRestore"]=function(){return(stackRestore=Module["stackRestore"]=Module["asm"]["stackRestore"]).apply(null,arguments)};var __growWasmMemory=Module["__growWasmMemory"]=function(){return(__growWasmMemory=Module["__growWasmMemory"]=Module["asm"]["__growWasmMemory"]).apply(null,arguments)};var dynCall_viii=Module["dynCall_viii"]=function(){return(dynCall_viii=Module["dynCall_viii"]=Module["asm"]["dynCall_viii"]).apply(null,arguments)};var dynCall_ii=Module["dynCall_ii"]=function(){return(dynCall_ii=Module["dynCall_ii"]=Module["asm"]["dynCall_ii"]).apply(null,arguments)};var dynCall_iiii=Module["dynCall_iiii"]=function(){return(dynCall_iiii=Module["dynCall_iiii"]=Module["asm"]["dynCall_iiii"]).apply(null,arguments)};var dynCall_jiji=Module["dynCall_jiji"]=function(){return(dynCall_jiji=Module["dynCall_jiji"]=Module["asm"]["dynCall_jiji"]).apply(null,arguments)};Module["asm"]=asm;var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run();
diff --git a/webgl_videotest/basis.wasm b/webgl_videotest/basis.wasm
index 91a3f21..616761b 100644
--- a/webgl_videotest/basis.wasm
+++ b/webgl_videotest/basis.wasm
Binary files differ
diff --git a/webgl_videotest/basis_wrappers.cpp b/webgl_videotest/basis_wrappers.cpp
index fd6dcca..a758fe3 100644
--- a/webgl_videotest/basis_wrappers.cpp
+++ b/webgl_videotest/basis_wrappers.cpp
@@ -3,8 +3,6 @@
 
 using namespace basist;
 
-static basist::etc1_global_selector_codebook *g_pGlobal_codebook;
-
 typedef unsigned int uint;
 
 extern "C" 
@@ -38,9 +36,6 @@
 void basis_init()
 {
 	basisu_transcoder_init();
-	
-	if (!g_pGlobal_codebook)
-		g_pGlobal_codebook = new basist::etc1_global_selector_codebook(g_global_selector_cb_size, g_global_selector_cb);
 }
 
 #define MAGIC 0xDEADBEE1
@@ -53,7 +48,7 @@
 	uint m_file_size;
 
 	basis_file() : 
-		m_transcoder(g_pGlobal_codebook)
+		m_transcoder()
 	{
 	}
 };
diff --git a/webgl_videotest/build.sh b/webgl_videotest/build.sh
index c614d6c..91e76d6 100644
--- a/webgl_videotest/build.sh
+++ b/webgl_videotest/build.sh
@@ -1,3 +1,3 @@
 # rg - I haven't tested this shell script yet (I use build.bat on Windows)
-emcc -s EXPORTED_FUNCTIONS="['allocate', '_malloc', '_free', '_basis_init','_basis_open','_basis_close','_basis_get_has_alpha','_basis_get_num_images','_basis_get_num_levels','_basis_get_image_width','_basis_get_image_height','_basis_get_image_transcoded_size_in_bytes','_basis_transcode_image','_basis_start_transcoding']" -s TOTAL_MEMORY=749993984 -O2 -s ASSERTIONS=0 -I ../transcoder -o basis.js ../transcoder/basisu_transcoder.cpp basis_wrappers.cpp -D BASISD_SUPPORT_KTX2=0
+emcc -s EXPORTED_FUNCTIONS="['allocate', '_malloc', '_free', '_basis_init','_basis_open','_basis_close','_basis_get_has_alpha','_basis_get_num_images','_basis_get_num_levels','_basis_get_image_width','_basis_get_image_height','_basis_get_image_transcoded_size_in_bytes','_basis_transcode_image','_basis_start_transcoding','_basis_get_debug_flags','_basis_set_debug_flags']" -s TOTAL_MEMORY=749993984 -O2 -s ASSERTIONS=0 -I ../transcoder -o basis.js ../transcoder/basisu_transcoder.cpp basis_wrappers.cpp -D BASISD_SUPPORT_KTX2=0 -D BASISD_ENABLE_DEBUG_FLAGS=1
 # chmod -R a+rX .
diff --git a/webgl_videotest/index.html b/webgl_videotest/index.html
index a302a4a..4962018 100644
--- a/webgl_videotest/index.html
+++ b/webgl_videotest/index.html
@@ -508,7 +508,7 @@
   <br>
   <br>
       .basis file:
-      <input id="file" type="text" size=30 value="kodim18.basis"></input>
+      <input id="file" type="text" size=30 value="6.1.01.basis"></input>
       <input type="button" value="Run!" onclick="run()"></input>
   <br>
   <br>
diff --git a/webgl_videotest/kodim18.basis b/webgl_videotest/kodim18.basis
deleted file mode 100644
index fc660da..0000000
--- a/webgl_videotest/kodim18.basis
+++ /dev/null
Binary files differ