/*
 * apck.h - Asset Pack (unpacker)
 * https://gitlab.com/bztsrc/assetpack
 *
 * Copyright (C) 2024 bzt, MIT license
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL ANY
 * DEVELOPER OR DISTRIBUTOR 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.
 *
 * @brief Asset Pack single header library
 */

#ifndef APCK_H
#define APCK_H
#ifdef  __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#define APCK_MAGIC "APCK"

#ifdef _MSC_VER
#pragma pack(push)
#pragma pack(1)
#define _PACK
#else
#define _PACK __attribute__((packed))
#endif

typedef struct {
  uint8_t   magic[4];
  uint32_t  hdr_size;
  uint8_t   engine[12];
  uint32_t  version;
  uint32_t  size;
  uint32_t  chksum;
  uint8_t   enckey[32];
} _PACK apck_hdr_t;

#ifdef _MSC_VER
#pragma pack(pop)
#endif
#undef _PACK

#ifndef DBG
#define DBG(a)
#endif
#ifndef __THROW
#define __THROW
#endif

int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen);
uint8_t *stbi_load_from_memory(uint8_t const *buffer, int len, int *x, int *y, int *comp, int req_comp);
int stb_vorbis_decode_memory(const uint8_t *mem, int len, int *channels, int *sample_rate, short **output);

enum { APCK_OK, APCK_ERR_BADINP, APCK_ERR_BADPCK, APCK_ERR_NOMEM };
enum { APCK_LOCALE, APCK_FONT = 32, APCK_BGM, APCK_SFX, APCK_VIDEO, APCK_MODEL, APCK_IMAGE, APCK_ATLAS, APCK_SPRITE, APCK_MAP,
    APCK_OBJECT = 46, APCK_SCRIPT, APCK_DATA01, APCK_DATA02, APCK_DATA03, APCK_DATA04, APCK_DATA05, APCK_DATA06, APCK_DATA07,
    APCK_DATA08, APCK_DATA09, APCK_DATA10, APCK_DATA11, APCK_DATA12, APCK_DATA13, APCK_DATA14, APCK_DATA15, APCK_DATA16,
    APCK_NUMTYPES };
enum { APCK_REGULAR = 0, APCK_BOLD = 1, APCK_ITALIC = 2, APCK_UNDERLINE = 16, APCK_STRIKETHROUGH = 32 };

#define APCK_COMPRESSED(c,i) (((c) < APCK_FONT && !(i)) || (c) == APCK_FONT || ((c) >= APCK_SPRITE && (c) <= APCK_SCRIPT))

typedef void* (*apck_initcb_t)(uint8_t *enckey);
typedef int (*apck_readcb_t)(void *enc, uint64_t offs, uint8_t *buf, uint64_t size);

typedef struct {
    void *f;
    uint64_t size;
    uint8_t *str;
    uint32_t aidx;
    void *enc;
} apck_archive_t;

typedef struct {
    int archive, w, h;
    char *name;
    uint32_t str;
    uint64_t offs;
    uint64_t size;
    uint64_t comp;
    uint8_t *buf;
} apck_file_t;

typedef struct {
    char engine[16];
    uint32_t version;
    uint32_t numarchive;
    uint32_t numfiles[APCK_NUMTYPES];
    apck_initcb_t initcb;
    apck_readcb_t readcb;
    apck_archive_t *archives;
    apck_file_t *files[APCK_NUMTYPES];
    uint32_t locale, *msgstr[APCK_FONT];
    void *font;
} apck_t;

uint8_t *apck_uleb128(uint8_t *ptr, uint64_t *value);
uint32_t apck_crc32(uint8_t *buf, uint32_t size);
int apck_init(apck_t *ctx, char *engine, uint32_t version, apck_initcb_t initcb, apck_readcb_t readcb);
uint8_t *apck_readbuf(apck_t *ctx, uint32_t archive, uint64_t offs, uint64_t size);
uint64_t apck_read(apck_t *ctx, uint32_t archive, uint64_t offs, uint8_t *buf, uint64_t size);
int apck_load(apck_t *ctx, char *fn);
int apck_free(apck_t *ctx);
int apck_release(apck_t *ctx, int type, char *name);
apck_file_t *apck_lookup(apck_t *ctx, int type, char *name);
uint8_t *apck_asset(apck_t *ctx, int type, char *name, uint64_t *size);
uint32_t *apck_image(apck_t *ctx, int type, char *name, int *w, int *h);
int16_t *apck_audio(apck_t *ctx, int type, char *name, int *numsamples, int *channels);
int apck_font(apck_t *ctx, char *name);
uint32_t *apck_text(apck_t *ctx, char *msgid, uint32_t color, int fontstyle, int fontsize, int *w, int *h);

/**************** embeded apck_file.h ****************/

#ifndef APCK_FILE_H
#define APCK_FILE_H
#ifdef __WIN32__
#include <windows.h>
#include <winnls.h>
#include <fileapi.h>
#include <wchar.h>
void apck_utf8_ucs2(char *fn, wchar_t *szFn);
#else
#include <unistd.h>
#endif
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
#ifndef FILENAME_MAX
#define FILENAME_MAX 255
#endif
void *apck_fileopen(char *fn, uint64_t *size);
void *apck_fileopenw(char *fn);
uint64_t apck_filewrite(void *f, void *buf, uint64_t size);
uint64_t apck_fileread(void *f, void *buf, uint64_t size);
void apck_fileseek(void *f, uint64_t offs);
void apck_fileclose(void *f);
#ifdef APCK_IMPLEMENTATION
#ifdef __WIN32__
void apck_utf8_ucs2(char *fn, wchar_t *szFn)
{
    int i;
    memset(szFn, 0, 2*(PATH_MAX + FILENAME_MAX + 1));
    MultiByteToWideChar(CP_UTF8, 0, fn, -1, szFn, PATH_MAX + FILENAME_MAX);
    for(i = 0; szFn[i]; i++) if(szFn[i] == L'/') szFn[i] = L'\\';
}
#endif
void *apck_fileopen(char *fn, uint64_t *size)
{
#ifdef __WIN32__
    struct _stat64 st;
    static wchar_t szFn[PATH_MAX + FILENAME_MAX + 1];
    HANDLE f;
    if(size) *size = 0;
    if(!fn || !*fn) return NULL;
    apck_utf8_ucs2(fn, (wchar_t*)szFn);
    if(_wstat64(szFn, &st) || !S_ISREG(st.st_mode)) return NULL;
    f = CreateFileW(szFn, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    if(f == INVALID_HANDLE_VALUE) f = NULL;
#else
    struct stat st;
    FILE *f;
    if(size) *size = 0;
    if(!fn || !*fn || (size && (stat(fn, &st) || !S_ISREG(st.st_mode)))) return NULL;
    f = fopen(fn, "rb");
#endif
    if(size) *size = (uint64_t)st.st_size;
    return (void*)f;
}
void *apck_fileopenw(char *fn)
{
#ifdef __WIN32__
    wchar_t szFn[PATH_MAX + FILENAME_MAX + 1];
    HANDLE f;
    if(!fn || !*fn) return NULL;
    apck_utf8_ucs2(fn, szFn);
    f = CreateFileW(szFn, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if(f == INVALID_HANDLE_VALUE) f = NULL;
#else
    FILE *f;
    if(!fn || !*fn) return NULL;
    f = fopen(fn, "wb");
#endif
    return (void*)f;
}
uint64_t apck_filewrite(void *f, void *buf, uint64_t size)
{
#ifdef __WIN32__
    DWORD t;
    if(!f || !buf || !size) return 0;
    if(!WriteFile((HANDLE)f, buf, (DWORD)size, &t, NULL)) t = 0;
    return (uint64_t)t;
#else
    if(!f || !buf || !size) return 0;
    return (uint64_t)fwrite(buf, 1, size, (FILE*)f);
#endif
}
uint64_t apck_fileread(void *f, void *buf, uint64_t size)
{
#ifdef __WIN32__
    DWORD t;
    if(!f || !buf || !size) return 0;
    if(!ReadFile((HANDLE)f, buf, (DWORD)size, &t, NULL)) t = 0;
    return (uint64_t)t;
#else
    if(!f || !buf || !size) return 0;
    return (uint64_t)fread(buf, 1, (size_t)size, (FILE*)f);
#endif
}
void apck_fileseek(void *f, uint64_t offs)
{
#ifdef __WIN32__
    LARGE_INTEGER pos;
    pos.QuadPart = offs;
    SetFilePointerEx((HANDLE)f, pos, NULL, FILE_BEGIN);
#else
    fseek((FILE*)f, offs, SEEK_SET);
#endif
}
void apck_fileclose(void *f)
{
    if(f) {
#ifdef __WIN32__
        CloseHandle((HANDLE)f);
#else
        fclose((FILE*)f);
#endif
    }
}
#endif
#endif

/**************** embeded apck_map.h ****************/

#ifndef APCK_MAP_H
#define APCK_MAP_H
enum { APCK_ORTHOGONAL = 'o', APCK_ISOMETRIC = 'i', APCK_VERTHEX = 'v', APCK_HORIZHEX = 'h' };
typedef struct {
    uint8_t *v;
    uint32_t x, y;
} apck_map_list_t;
typedef struct {
    int type, disptype;
    int offX, offY;
    int parX, parY;
    int movX, movY;
    uint32_t size;
    uint8_t *data;
} apck_map_layer_t;
typedef struct {
    int type, tilew, tileh, w, h, l, proplen;
    uint8_t *properties;
    apck_map_layer_t *layers;
} apck_map_t;
apck_map_t *apck_map_new(int type, int tilew, int tileh, int w, int h, int proplen, uint8_t *properties);
apck_map_layer_t *apck_map_layer(apck_map_t *map, int type, uint32_t datasize, void *data);
int apck_map_free(apck_map_t *map);
apck_map_t *apck_map_decode(uint8_t *str, uint8_t *buf, uint64_t size);
#ifdef APCK_IMPLEMENTATION
apck_map_t *apck_map_new(int type, int tilew, int tileh, int w, int h, int proplen, uint8_t *properties)
{
    apck_map_t *ret;
    if(tilew < 1 || tileh < 1 || w < 1 || h < 1 || !(ret = (apck_map_t*)malloc(sizeof(apck_map_t))))
        return NULL;
    memset(ret, 0, sizeof(apck_map_t));
    ret->type = type;
    ret->tilew = tilew;
    ret->tileh = tileh;
    ret->w = w;
    ret->h = h;
    if(proplen && properties && (ret->properties = (uint8_t*)malloc(proplen))) {
        ret->proplen = proplen;
        memcpy(ret->properties, properties, proplen);
    }
    return ret;
}
apck_map_layer_t *apck_map_layer(apck_map_t *map, int type, uint32_t datasize, void *data)
{
    int i;
    uint32_t l = 0;
    if(!map) return NULL;
    if(type == APCK_SPRITE) l = datasize * sizeof(apck_map_list_t); else
    if(type == APCK_MAP) l = map->w * map->h * sizeof(uint32_t); else {
        if(!data) return NULL;
        if(type >= APCK_OBJECT) l = datasize;
        else l = strlen((char*)data) + 1;
    }
    i = map->l++;
    if(!(map->layers = (apck_map_layer_t*)realloc(map->layers, map->l * sizeof(apck_map_layer_t)))) {
        map->l = 0;
        return NULL;
    }
    memset(&map->layers[i], 0, sizeof(apck_map_layer_t));
    map->layers[i].type = type;
    map->layers[i].parX = map->layers[i].parY = map->layers[i].movX = map->layers[i].movY = 100;
    if(l) {
        if(!(map->layers[i].data = (uint8_t*)malloc(l))) {
            map->l--;
            return NULL;
        }
        memset(map->layers[i].data, 0, l);
        map->layers[i].size = l;
        if(type != APCK_MAP && type != APCK_SPRITE && data)
            memcpy(map->layers[i].data, data, l);
    }
    return &map->layers[i];
}
int apck_map_free(apck_map_t *map)
{
    int i;
    if(!map) return APCK_ERR_BADINP;
    if(map->layers) {
        for(i = 0; i < map->l; i++)
            if(map->layers[i].data)
                free(map->layers[i].data);
        free(map->layers);
    }
    if(map->properties)
        free(map->properties);
    free(map);
    return APCK_OK;
}
apck_map_t *apck_map_decode(uint8_t *str, uint8_t *buf, uint64_t size)
{
    apck_map_t *map = NULL;
    apck_map_layer_t *layer;
    apck_map_list_t *list;
    uint64_t i, j, k, n, s, tw, th, w, h, l, parX, parY, offX, offY, movX, movY, type, disptype;
    uint32_t *data;
    uint8_t *ptr = buf + 4;
    if(!str || !buf || !size || memcmp(buf, "MAP", 3)) return NULL;
    ptr = apck_uleb128(ptr, &tw);
    ptr = apck_uleb128(ptr, &th);
    ptr = apck_uleb128(ptr, &w);
    ptr = apck_uleb128(ptr, &h);
    ptr = apck_uleb128(ptr, &l);
    ptr = apck_uleb128(ptr, &n);
    if(!(map = apck_map_new(buf[3], tw, th, w, h, n, ptr)))
        return NULL;
    ptr += n;
    for(j = 0; j < l; j++) {
        ptr = apck_uleb128(ptr, &type);
        ptr = apck_uleb128(ptr, &disptype);
        ptr = apck_uleb128(ptr, &offX);
        ptr = apck_uleb128(ptr, &offY);
        ptr = apck_uleb128(ptr, &parX);
        ptr = apck_uleb128(ptr, &parY);
        ptr = apck_uleb128(ptr, &movX);
        ptr = apck_uleb128(ptr, &movY);
        if(type != APCK_MAP)
            ptr = apck_uleb128(ptr, &s);
        else
            s = 0;
        if((layer = apck_map_layer(map, type, s,
          type >= APCK_OBJECT ? ptr : (type != APCK_MAP && type != APCK_SPRITE && str && s ? str + s : NULL)))) {
            layer->disptype = disptype;
            layer->offX = offX; layer->offY = offY;
            layer->parX = parX; layer->parY = parY;
            layer->movX = movX - 100; layer->movY = movY - 100;
            if(type == APCK_MAP) {
                data = (uint32_t*)layer->data;
                for(i = 0, n = w * h; i < n;) {
                    s = ((*ptr++) & 0x7F) + 1;
                    if(ptr[-1] & 0x80) {
                        ptr = apck_uleb128(ptr, &k);
                        while(s-- && i < n) { data[i++] = k; }
                    } else
                        while(s-- && i < n) { ptr = apck_uleb128(ptr, &k); data[i++] = k; }
                }
            } else
            if(type == APCK_SPRITE) {
                list = (apck_map_list_t*)layer->data;
                for(i = 0; i < s; i++) {
                    ptr = apck_uleb128(ptr, &k); list[i].v = str + k;
                    ptr = apck_uleb128(ptr, &k); list[i].x = k;
                    ptr = apck_uleb128(ptr, &k); list[i].y = k;
                }
            } else
            if(type >= APCK_OBJECT)
                ptr += s;
        } else
            break;
    }
    return map;
}
#endif
#endif

/**************** embeded apck_locale.h ****************/

#ifndef APCK_LOCALE_H
#define APCK_LOCALE_H
int apck_setlocale(apck_t *ctx, char *locale);
char *apck_msgstr(apck_t *ctx, char *msgid);
#ifdef APCK_IMPLEMENTATION
static char *_apck_sort_msgid = NULL;
int _apck_msgidcmp(const void *a, const void *b)
{
    if(!_apck_sort_msgid) return 0;
    return strcmp(_apck_sort_msgid + *((uint32_t*)a), _apck_sort_msgid + *((uint32_t*)b));
}
static int _apck_merge_locale(apck_t *ctx, uint32_t locale, char *str, uint32_t size)
{
    char *msgid, *msgstr, *ptr, *end;
    int i;
    if(!(ctx->files[locale][0].buf = (uint8_t*)realloc(ctx->files[locale][0].buf, ctx->files[locale][0].size + size))) {
        DBG(("unable to allocate memory for locale %u strings\n", locale));
        return 0;
    }
    _apck_sort_msgid = (char*)ctx->files[locale][0].buf;
    ptr = (char*)ctx->files[locale][0].buf + ctx->files[locale][0].size;
    end = ptr + size;
    memcpy(ptr, str, size);
    ctx->files[locale][0].size += size;
    while(ptr < end) {
        msgid = ptr; ptr += strlen(ptr) + 1;
        msgstr = ptr; ptr += strlen(ptr) + 1;
        for(i = 0; i < ctx->files[locale][0].w && strcmp(_apck_sort_msgid + ctx->msgstr[locale][i * 2], msgid); i++);
        if(i >= ctx->files[locale][0].w) {
            i = ctx->files[locale][0].w++;
            if(!(ctx->msgstr[locale] = (uint32_t*)realloc(ctx->msgstr[locale], ctx->files[locale][0].w * 2 * sizeof(uint32_t)))) {
                DBG(("unable to allocate memory for locale %u index\n", locale));
                ctx->files[locale][0].w = 0;
                return 0;
            }
            ctx->msgstr[locale][i * 2] = msgid - _apck_sort_msgid;
        }
        ctx->msgstr[locale][i * 2 + 1] = msgstr - _apck_sort_msgid;
    }
    if(ctx->files[locale][0].w && ctx->msgstr[locale])
        qsort(ctx->msgstr[locale], ctx->files[locale][0].w, 2 * sizeof(uint32_t), _apck_msgidcmp);
    return 1;
}
int apck_setlocale(apck_t *ctx, char *locale)
{
    uint32_t i;
    if(!ctx || !locale || !*locale) return APCK_ERR_BADINP;
    for(i = 0; i < APCK_FONT && ctx->files[i] && strcmp(ctx->files[i][0].name, locale); i++);
    if(i >= APCK_FONT) {
        for(i = 0; i < APCK_FONT && ctx->files[i] && memcmp(ctx->files[i][0].name, locale, 2); i++);
        if(i >= APCK_FONT) return APCK_ERR_BADINP;
    }
    if(!ctx->msgstr[i]) return APCK_ERR_BADINP;
    ctx->locale = i;
    return APCK_OK;
}
char *apck_msgstr(apck_t *ctx, char *msgid)
{
    uint32_t *idx;
    char *str;
    int s = 0, e, h, m;
    if(!ctx || !msgid || !*msgid || ctx->locale >= APCK_FONT || !ctx->msgstr[ctx->locale] ||
      !ctx->files[ctx->locale] || !ctx->files[ctx->locale][0].w || !ctx->files[ctx->locale][0].buf)
        return msgid;
    str = (char*)ctx->files[ctx->locale][0].buf;
    idx = ctx->msgstr[ctx->locale];
    e = ctx->files[ctx->locale][0].w - 1;
    while(s <= e) {
        h = ((e + s) >> 1);
        m = strcmp(str + idx[h * 2], msgid);
        if(!m) return str + idx[h * 2 + 1];
        if(m > 0) e = h - 1; else s = h + 1;
    }
    return msgid;
}
#endif
#endif

/**************** embeded apck_sprite.h ****************/

#ifndef APCK_SPRITE_H
#define APCK_SPRITE_H
enum { APCK_ANIM_LOOP, APCK_ANIM_ONCE, APCK_ANIM_BF, APCK_TILE_WANG, APCK_TILE_WALL };
enum { APCK_DIR_S, APCK_DIR_SW, APCK_DIR_W, APCK_DIR_NW, APCK_DIR_N, APCK_DIR_NE, APCK_DIR_E, APCK_DIR_SE, APCK_NUMDIR };
enum { APCK_M_COPY, APCK_M_FLIPV, APCK_M_FLIPH, APCK_M_ROT90, APCK_M_ROT180, APCK_M_ROT270 };
typedef struct {
    int atlas, m, w, h, sx, sy, dx, dy, dur;
    void *sfx;
} apck_frame_t;
typedef struct {
    int type, w, h, nf[APCK_NUMDIR];
    apck_frame_t *frames[APCK_NUMDIR];
} apck_sprite_t;
apck_sprite_t *apck_sprite_new(int type, int w, int h);
int apck_sprite_frame(apck_sprite_t *sprite, int dir, int atlas, int m, int w, int h, int sx, int sy, int dx, int dy, int dur, char *sfx);
int apck_sprite_free(apck_sprite_t *sprite);
apck_sprite_t *apck_sprite_decode(uint32_t archive, uint8_t *str, uint8_t *buf, uint64_t size);
void apck_sprite_pose(apck_t *ctx, apck_sprite_t *sprite, int dir, int frame, uint32_t *buf, int x, int y, int w, int h, int p);
#ifdef APCK_IMPLEMENTATION
apck_sprite_t *apck_sprite_new(int type, int w, int h)
{
    apck_sprite_t *ret;
    if(w < 1 || w > 65535 || h < 1 || h > 65535 || !(ret = (apck_sprite_t*)malloc(sizeof(apck_sprite_t))))
        return NULL;
    memset(ret, 0, sizeof(apck_sprite_t));
    ret->type = type < APCK_ANIM_LOOP || type > APCK_ANIM_BF ? APCK_ANIM_LOOP : type;
    ret->w = w;
    ret->h = h;
    return ret;
}
int apck_sprite_frame(apck_sprite_t *sprite, int dir, int atlas, int m, int w, int h, int sx, int sy, int dx, int dy, int dur, char *sfx)
{
    int i;
    if(!sprite || dir < APCK_DIR_S || dir >= APCK_NUMDIR || m < APCK_M_COPY || m > APCK_M_ROT270 ||
      w < 1 || h < 1 || sx < 0 || sy < 0 || dx < 0 || dx >= sprite->w || dy < 0 || dy >= sprite->h)
        return APCK_ERR_BADINP;
    if(dur < 1) dur = 1;
    if(dur > 65535) dur = 65535;
    if(dx + w > sprite->w) w = sprite->w - dx;
    if(dy + h > sprite->h) h = sprite->h - dy;
    i = sprite->nf[dir]++;
    if(!(sprite->frames[dir] = (apck_frame_t*)realloc(sprite->frames[dir], sprite->nf[dir] * sizeof(apck_frame_t)))) {
        sprite->nf[dir] = 0;
        return APCK_ERR_NOMEM;
    }
    sprite->frames[dir][i].m = m;
    sprite->frames[dir][i].w = w;
    sprite->frames[dir][i].h = h;
    sprite->frames[dir][i].sx = sx;
    sprite->frames[dir][i].sy = sy;
    sprite->frames[dir][i].dx = dx;
    sprite->frames[dir][i].dy = dy;
    sprite->frames[dir][i].dur = dur;
    sprite->frames[dir][i].atlas = atlas;
    sprite->frames[dir][i].sfx = sfx;
    return APCK_OK;
}
int apck_sprite_free(apck_sprite_t *sprite)
{
    int i;
    if(!sprite) return APCK_ERR_BADINP;
    for(i = 0; i < APCK_NUMDIR; i++)
        if(sprite->frames[i])
            free(sprite->frames[i]);
    free(sprite);
    return APCK_OK;
}
apck_sprite_t *apck_sprite_decode(uint32_t aidx, uint8_t *str, uint8_t *buf, uint64_t size)
{
    apck_sprite_t *sprite = NULL;
    uint64_t i, j, k, w, h, t, l;
    uint8_t *ptr = buf + 4;
    if(!str || !buf || !size || memcmp(buf, "SPFR", 4)) return NULL;
    ptr = apck_uleb128(ptr, &t);
    ptr = apck_uleb128(ptr, &w);
    ptr = apck_uleb128(ptr, &h);
    if(!(sprite = apck_sprite_new(t, w, h)))
        return NULL;
    for(j = 0; j < APCK_NUMDIR; j++) {
        ptr = apck_uleb128(ptr, &l);
        sprite->nf[j] = l;
        if((sprite->frames[j] = (apck_frame_t*)realloc(sprite->frames[j], sprite->nf[j] * sizeof(apck_frame_t)))) {
            for(i = 0; i < l; i++) {
                ptr = apck_uleb128(ptr, &k); sprite->frames[j][i].atlas = aidx + k;
                ptr = apck_uleb128(ptr, &k); sprite->frames[j][i].m = k;
                ptr = apck_uleb128(ptr, &k); sprite->frames[j][i].w = k;
                ptr = apck_uleb128(ptr, &k); sprite->frames[j][i].h = k;
                ptr = apck_uleb128(ptr, &k); sprite->frames[j][i].sx = k;
                ptr = apck_uleb128(ptr, &k); sprite->frames[j][i].sy = k;
                ptr = apck_uleb128(ptr, &k); sprite->frames[j][i].dx = k;
                ptr = apck_uleb128(ptr, &k); sprite->frames[j][i].dy = k;
                ptr = apck_uleb128(ptr, &k); sprite->frames[j][i].dur = k;
                ptr = apck_uleb128(ptr, &k); sprite->frames[j][i].sfx = str && k ? str + k : NULL;
            }
        } else
            sprite->nf[j] = 0;
    }
    return sprite;
}
void apck_sprite_pose(apck_t *ctx, apck_sprite_t *sprite, int dir, int frame, uint32_t *buf, int x, int y, int w, int h, int p)
{
    apck_file_t *atlas;
    apck_frame_t *f;
    uint8_t *fbuf;
    uint32_t *src, *dst;
    int X, Y, W, H;
    if(!sprite || dir < APCK_DIR_S || dir >= APCK_NUMDIR || frame < 0 || !sprite->frames[dir] || !buf || w < 1 || h < 1 ||
      p < 4 || x >= w || y >= h)
        return;
    p >>= 2;
    f = &sprite->frames[dir][frame % sprite->nf[dir]];
    if(f->atlas >= (int)ctx->numfiles[APCK_ATLAS]) return;
    atlas = &ctx->files[APCK_ATLAS][f->atlas];
    if(!atlas->buf || !atlas->w || !atlas->h) {
        if(atlas->size && (fbuf = apck_readbuf(ctx, atlas->archive, atlas->offs, atlas->size))) {
            if(!(atlas->buf = (uint8_t*)stbi_load_from_memory(fbuf, atlas->size, &atlas->w, &atlas->h, &W, 4)))
                atlas->size = atlas->comp = atlas->w = atlas->h = 0;
            free(fbuf);
        }
        if(!atlas->buf || !atlas->w || !atlas->h) return;
    }
    W = f->w;
    if(x + W > w) W = w - x;
    if(f->sx + W > atlas->w) W = atlas->w - f->sx;
    H = f->h;
    if(y + H > h) H = h - y;
    if(f->sy + H > atlas->h) H = atlas->h - f->sy;
    if(W < 1 || H < 1) return;
    src = (uint32_t*)atlas->buf + f->sy * atlas->w + f->sx;
    dst = buf + (y + f->dy) * p + x + f->dx;
    switch(f->m) {
        case APCK_M_ROT270:
            for(Y = 0; Y < H; Y++, dst += p)
                for(X = 0; X < W; X++)
                    dst[X] = src[(H - 1 - X) * atlas->w + Y];
        break;
        case APCK_M_ROT180:
            src += (f->h - 1) * atlas->w;
            for(Y = 0; Y < H; Y++, src -= atlas->w, dst += p)
                for(X = 0; X < W; X++)
                    dst[X] = src[f->w - 1 - X];
        break;
        case APCK_M_ROT90:
            for(Y = 0; Y < H; Y++, dst += p)
                for(X = 0; X < W; X++)
                    dst[X] = src[X * atlas->w + f->w - 1 - Y];
        break;
        case APCK_M_FLIPH:
            src += (f->h - 1) * atlas->w;
            for(Y = 0; Y < H; Y++, src -= atlas->w, dst += p)
                memcpy(dst, src, W * 4);
        break;
        case APCK_M_FLIPV:
            for(Y = 0; Y < H; Y++, src += atlas->w, dst += p)
                for(X = 0; X < W; X++)
                    dst[X] = src[f->w - 1 - X];
        break;
        default:
            for(Y = 0; Y < H; Y++, src += atlas->w, dst += p)
                memcpy(dst, src, W * 4);
        break;
    }
}
#endif
#endif


apck_map_t *apck_map(apck_t *ctx, char *name);

#ifdef APCK_IMPLEMENTATION

#define inline __inline__

#ifndef STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#define STBI_NO_FAILURE_STRINGS
#define STBI_NO_STDIO
#define STBI_NO_LINEAR
#define STBI_ONLY_PNG
/* stb_image - v2.30 - public domain image loader - http://nothings.org/stb
                                  no warranty implied; use at your own risk
    https://github.com/nothings/stb */
/**************** embeded stb_image.h ****************/

#ifndef STBI_INCLUDE_STB_IMAGE_H
#define STBI_INCLUDE_STB_IMAGE_H
#define STBI_VERSION 1
enum
{
   STBI_default = 0,
   STBI_grey       = 1,
   STBI_grey_alpha = 2,
   STBI_rgb        = 3,
   STBI_rgb_alpha  = 4
};
#include <stdlib.h>
typedef unsigned char stbi_uc;
typedef unsigned short stbi_us;
#ifdef __cplusplus
extern "C" {
#endif
#ifndef STBIDEF
#ifdef STB_IMAGE_STATIC
#define STBIDEF static
#else
#define STBIDEF extern
#endif
#endif
typedef struct
{
   int      (*read)  (void *user,char *data,int size);
   void     (*skip)  (void *user,int n);
   int      (*eof)   (void *user);
} stbi_io_callbacks;
STBIDEF stbi_uc *stbi_load_from_memory   (stbi_uc           const *buffer, int len   , int *x, int *y, int *channels_in_file, int desired_channels);
STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk  , void *user, int *x, int *y, int *channels_in_file, int desired_channels);
#ifdef STBI_WINDOWS_UTF8
STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input);
#endif
STBIDEF stbi_us *stbi_load_16_from_memory   (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);
STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels);
STBIDEF int    stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user);
STBIDEF int    stbi_is_hdr_from_memory(stbi_uc const *buffer, int len);
STBIDEF const char *stbi_failure_reason  (void);
STBIDEF void     stbi_image_free      (void *retval_from_stbi_load);
STBIDEF int      stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
STBIDEF int      stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp);
STBIDEF int      stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len);
STBIDEF int      stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user);
STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply);
STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert);
STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);
STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply);
STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert);
STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip);
STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen);
STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header);
STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen);
STBIDEF int   stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen);
STBIDEF int   stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
#ifdef __cplusplus
}
#endif
#endif
#ifdef STB_IMAGE_IMPLEMENTATION
#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \
  || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \
  || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \
  || defined(STBI_ONLY_ZLIB)
   #ifndef STBI_ONLY_JPEG
   #define STBI_NO_JPEG
   #endif
   #ifndef STBI_ONLY_PNG
   #define STBI_NO_PNG
   #endif
   #ifndef STBI_ONLY_BMP
   #define STBI_NO_BMP
   #endif
   #ifndef STBI_ONLY_PSD
   #define STBI_NO_PSD
   #endif
   #ifndef STBI_ONLY_TGA
   #define STBI_NO_TGA
   #endif
   #ifndef STBI_ONLY_GIF
   #define STBI_NO_GIF
   #endif
   #ifndef STBI_ONLY_HDR
   #define STBI_NO_HDR
   #endif
   #ifndef STBI_ONLY_PIC
   #define STBI_NO_PIC
   #endif
   #ifndef STBI_ONLY_PNM
   #define STBI_NO_PNM
   #endif
#endif
#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB)
#define STBI_NO_ZLIB
#endif
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
#include <math.h>
#endif
#ifndef STBI_ASSERT
#include <assert.h>
#define STBI_ASSERT(x) assert(x)
#endif
#ifdef __cplusplus
#define STBI_EXTERN extern "C"
#else
#define STBI_EXTERN extern
#endif
#ifndef _MSC_VER
   #ifdef __cplusplus
   #define stbi_inline inline
   #else
   #define stbi_inline
   #endif
#else
   #define stbi_inline __forceinline
#endif
#ifndef STBI_NO_THREAD_LOCALS
   #if defined(__cplusplus) &&  __cplusplus >= 201103L
      #define STBI_THREAD_LOCAL       thread_local
   #elif defined(__GNUC__) && __GNUC__ < 5
      #define STBI_THREAD_LOCAL       __thread
   #elif defined(_MSC_VER)
      #define STBI_THREAD_LOCAL       __declspec(thread)
   #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)
      #define STBI_THREAD_LOCAL       _Thread_local
   #endif
   #ifndef STBI_THREAD_LOCAL
      #if defined(__GNUC__)
        #define STBI_THREAD_LOCAL       __thread
      #endif
   #endif
#endif
#if defined(_MSC_VER) || defined(__SYMBIAN32__)
typedef unsigned short stbi__uint16;
typedef   signed short stbi__int16;
typedef unsigned int   stbi__uint32;
typedef   signed int   stbi__int32;
#else
#include <stdint.h>
typedef uint16_t stbi__uint16;
typedef int16_t  stbi__int16;
typedef uint32_t stbi__uint32;
typedef int32_t  stbi__int32;
#endif
typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
#ifdef _MSC_VER
#define STBI_NOTUSED(v)  (void)(v)
#else
#define STBI_NOTUSED(v)  (void)sizeof(v)
#endif
#ifdef _MSC_VER
#define STBI_HAS_LROTL
#endif
#ifdef STBI_HAS_LROTL
   #define stbi_lrot(x,y)  _lrotl(x,y)
#else
   #define stbi_lrot(x,y)  (((x) << (y)) | ((x) >> (-(y) & 31)))
#endif
#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED))
#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED)
#else
#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)."
#endif
#ifndef STBI_MALLOC
#define STBI_MALLOC(sz)           malloc(sz)
#define STBI_REALLOC(p,newsz)     realloc(p,newsz)
#define STBI_FREE(p)              free(p)
#endif
#ifndef STBI_REALLOC_SIZED
#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz)
#endif
#if defined(__x86_64__) || defined(_M_X64)
#define STBI__X64_TARGET
#elif defined(__i386) || defined(_M_IX86)
#define STBI__X86_TARGET
#endif
#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD)
#define STBI_NO_SIMD
#endif
#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD)
#define STBI_NO_SIMD
#endif
#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET))
#define STBI_SSE2
#include <emmintrin.h>
#ifdef _MSC_VER
#if _MSC_VER >= 1400
#include <intrin.h>
static int stbi__cpuid3(void)
{
   int info[4];
   __cpuid(info,1);
   return info[3];
}
#else
static int stbi__cpuid3(void)
{
   int res;
   __asm {
      mov  eax,1
      cpuid
      mov  res,edx
   }
   return res;
}
#endif
#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name
#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2)
static int stbi__sse2_available(void)
{
   int info3 = stbi__cpuid3();
   return ((info3 >> 26) & 1) != 0;
}
#endif
#else
#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2)
static int stbi__sse2_available(void)
{
   return 1;
}
#endif
#endif
#endif
#if defined(STBI_NO_SIMD) && defined(STBI_NEON)
#undef STBI_NEON
#endif
#ifdef STBI_NEON
#include <arm_neon.h>
#ifdef _MSC_VER
#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name
#else
#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
#endif
#endif
#ifndef STBI_SIMD_ALIGN
#define STBI_SIMD_ALIGN(type, name) type name
#endif
#ifndef STBI_MAX_DIMENSIONS
#define STBI_MAX_DIMENSIONS (1 << 24)
#endif
typedef struct
{
   stbi__uint32 img_x, img_y;
   int img_n, img_out_n;
   stbi_io_callbacks io;
   void *io_user_data;
   int read_from_callbacks;
   int buflen;
   stbi_uc buffer_start[128];
   int callback_already_read;
   stbi_uc *img_buffer, *img_buffer_end;
   stbi_uc *img_buffer_original, *img_buffer_original_end;
} stbi__context;
static void stbi__refill_buffer(stbi__context *s);
static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len)
{
   s->io.read = NULL;
   s->read_from_callbacks = 0;
   s->callback_already_read = 0;
   s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer;
   s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len;
}
static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user)
{
   s->io = *c;
   s->io_user_data = user;
   s->buflen = sizeof(s->buffer_start);
   s->read_from_callbacks = 1;
   s->callback_already_read = 0;
   s->img_buffer = s->img_buffer_original = s->buffer_start;
   stbi__refill_buffer(s);
   s->img_buffer_original_end = s->img_buffer_end;
}
static void stbi__rewind(stbi__context *s)
{
   s->img_buffer = s->img_buffer_original;
   s->img_buffer_end = s->img_buffer_original_end;
}
enum
{
   STBI_ORDER_RGB,
   STBI_ORDER_BGR
};
typedef struct
{
   int bits_per_channel;
   int num_channels;
   int channel_order;
} stbi__result_info;
#ifndef STBI_NO_PNG
static int      stbi__png_test(stbi__context *s);
static void    *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
static int      stbi__png_info(stbi__context *s, int *x, int *y, int *comp);
static int      stbi__png_is16(stbi__context *s);
#endif
static
#ifdef STBI_THREAD_LOCAL
STBI_THREAD_LOCAL
#endif
const char *stbi__g_failure_reason;
STBIDEF const char *stbi_failure_reason(void)
{
   return stbi__g_failure_reason;
}
#ifndef STBI_NO_FAILURE_STRINGS
static int stbi__err(const char *str)
{
   stbi__g_failure_reason = str;
   return 0;
}
#endif
static void *stbi__malloc(size_t size)
{
    return STBI_MALLOC(size);
}
static int stbi__addsizes_valid(int a, int b)
{
   if (b < 0) return 0;
   return a <= INT_MAX - b;
}
static int stbi__mul2sizes_valid(int a, int b)
{
   if (a < 0 || b < 0) return 0;
   if (b == 0) return 1;
   return a <= INT_MAX/b;
}
#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR)
static int stbi__mad2sizes_valid(int a, int b, int add)
{
   return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add);
}
#endif
static int stbi__mad3sizes_valid(int a, int b, int c, int add)
{
   return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&
      stbi__addsizes_valid(a*b*c, add);
}
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)
static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add)
{
   return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&
      stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add);
}
#endif
#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR)
static void *stbi__malloc_mad2(int a, int b, int add)
{
   if (!stbi__mad2sizes_valid(a, b, add)) return NULL;
   return stbi__malloc(a*b + add);
}
#endif
static void *stbi__malloc_mad3(int a, int b, int c, int add)
{
   if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL;
   return stbi__malloc(a*b*c + add);
}
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)
static void *stbi__malloc_mad4(int a, int b, int c, int d, int add)
{
   if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL;
   return stbi__malloc(a*b*c*d + add);
}
#endif
#ifdef STBI_NO_FAILURE_STRINGS
   #define stbi__err(x,y)  0
#elif defined(STBI_FAILURE_USERMSG)
   #define stbi__err(x,y)  stbi__err(y)
#else
   #define stbi__err(x,y)  stbi__err(x)
#endif
#define stbi__errpf(x,y)   ((float *)(size_t) (stbi__err(x,y)?NULL:NULL))
#define stbi__errpuc(x,y)  ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL))
STBIDEF void stbi_image_free(void *retval_from_stbi_load)
{
   STBI_FREE(retval_from_stbi_load);
}
static int stbi__vertically_flip_on_load_global = 0;
STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip)
{
   stbi__vertically_flip_on_load_global = flag_true_if_should_flip;
}
#ifndef STBI_THREAD_LOCAL
#define stbi__vertically_flip_on_load  stbi__vertically_flip_on_load_global
#else
static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set;
STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip)
{
   stbi__vertically_flip_on_load_local = flag_true_if_should_flip;
   stbi__vertically_flip_on_load_set = 1;
}
#define stbi__vertically_flip_on_load  (stbi__vertically_flip_on_load_set       \
                                         ? stbi__vertically_flip_on_load_local  \
                                         : stbi__vertically_flip_on_load_global)
#endif
static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc)
{
   memset(ri, 0, sizeof(*ri));
   ri->bits_per_channel = 8;
   ri->channel_order = STBI_ORDER_RGB;
   ri->num_channels = 0;
   #ifndef STBI_NO_PNG
   if (stbi__png_test(s))  return stbi__png_load(s,x,y,comp,req_comp, ri);
   #endif
   (void)bpc;
   return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt");
}
static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels)
{
   int i;
   int img_len = w * h * channels;
   stbi_uc *reduced;
   reduced = (stbi_uc *) stbi__malloc(img_len);
   if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory");
   for (i = 0; i < img_len; ++i)
      reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF);
   STBI_FREE(orig);
   return reduced;
}
static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels)
{
   int i;
   int img_len = w * h * channels;
   stbi__uint16 *enlarged;
   enlarged = (stbi__uint16 *) stbi__malloc(img_len*2);
   if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory");
   for (i = 0; i < img_len; ++i)
      enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]);
   STBI_FREE(orig);
   return enlarged;
}
static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel)
{
   int row;
   size_t bytes_per_row = (size_t)w * bytes_per_pixel;
   stbi_uc temp[2048];
   stbi_uc *bytes = (stbi_uc *)image;
   for (row = 0; row < (h>>1); row++) {
      stbi_uc *row0 = bytes + row*bytes_per_row;
      stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row;
      size_t bytes_left = bytes_per_row;
      while (bytes_left) {
         size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp);
         memcpy(temp, row0, bytes_copy);
         memcpy(row0, row1, bytes_copy);
         memcpy(row1, temp, bytes_copy);
         row0 += bytes_copy;
         row1 += bytes_copy;
         bytes_left -= bytes_copy;
      }
   }
}
static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp)
{
   stbi__result_info ri;
   void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8);
   if (result == NULL)
      return NULL;
   STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16);
   if (ri.bits_per_channel != 8) {
      result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp);
      ri.bits_per_channel = 8;
   }
   if (stbi__vertically_flip_on_load) {
      int channels = req_comp ? req_comp : *comp;
      stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc));
   }
   return (unsigned char *) result;
}
static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp)
{
   stbi__result_info ri;
   void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16);
   if (result == NULL)
      return NULL;
   STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16);
   if (ri.bits_per_channel != 16) {
      result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp);
      ri.bits_per_channel = 16;
   }
   if (stbi__vertically_flip_on_load) {
      int channels = req_comp ? req_comp : *comp;
      stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16));
   }
   return (stbi__uint16 *) result;
}
#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR)
static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp)
{
   if (stbi__vertically_flip_on_load && result != NULL) {
      int channels = req_comp ? req_comp : *comp;
      stbi__vertical_flip(result, *x, *y, channels * sizeof(float));
   }
}
#endif
STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels)
{
   stbi__context s;
   stbi__start_mem(&s,buffer,len);
   return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels);
}
STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels)
{
   stbi__context s;
   stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user);
   return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels);
}
STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
{
   stbi__context s;
   stbi__start_mem(&s,buffer,len);
   return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
}
STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
{
   stbi__context s;
   stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
   return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
}
static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f;
STBIDEF void   stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; }
STBIDEF void   stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; }
enum
{
   STBI__SCAN_load=0,
   STBI__SCAN_type,
   STBI__SCAN_header
};
static void stbi__refill_buffer(stbi__context *s)
{
   int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen);
   s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original);
   if (n == 0) {
      s->read_from_callbacks = 0;
      s->img_buffer = s->buffer_start;
      s->img_buffer_end = s->buffer_start+1;
      *s->img_buffer = 0;
   } else {
      s->img_buffer = s->buffer_start;
      s->img_buffer_end = s->buffer_start + n;
   }
}
stbi_inline static stbi_uc stbi__get8(stbi__context *s)
{
   if (s->img_buffer < s->img_buffer_end)
      return *s->img_buffer++;
   if (s->read_from_callbacks) {
      stbi__refill_buffer(s);
      return *s->img_buffer++;
   }
   return 0;
}
#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM)
#else
stbi_inline static int stbi__at_eof(stbi__context *s)
{
   if (s->io.read) {
      if (!(s->io.eof)(s->io_user_data)) return 0;
      if (s->read_from_callbacks == 0) return 1;
   }
   return s->img_buffer >= s->img_buffer_end;
}
#endif
#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC)
#else
static void stbi__skip(stbi__context *s, int n)
{
   if (n == 0) return;
   if (n < 0) {
      s->img_buffer = s->img_buffer_end;
      return;
   }
   if (s->io.read) {
      int blen = (int) (s->img_buffer_end - s->img_buffer);
      if (blen < n) {
         s->img_buffer = s->img_buffer_end;
         (s->io.skip)(s->io_user_data, n - blen);
         return;
      }
   }
   s->img_buffer += n;
}
#endif
#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM)
#else
static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n)
{
   if (s->io.read) {
      int blen = (int) (s->img_buffer_end - s->img_buffer);
      if (blen < n) {
         int res, count;
         memcpy(buffer, s->img_buffer, blen);
         count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen);
         res = (count == (n-blen));
         s->img_buffer = s->img_buffer_end;
         return res;
      }
   }
   if (s->img_buffer+n <= s->img_buffer_end) {
      memcpy(buffer, s->img_buffer, n);
      s->img_buffer += n;
      return 1;
   } else
      return 0;
}
#endif
#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC)
#else
static int stbi__get16be(stbi__context *s)
{
   int z = stbi__get8(s);
   return (z << 8) + stbi__get8(s);
}
#endif
#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC)
#else
static stbi__uint32 stbi__get32be(stbi__context *s)
{
   stbi__uint32 z = stbi__get16be(s);
   return (z << 16) + stbi__get16be(s);
}
#endif
#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF)
#else
static int stbi__get16le(stbi__context *s)
{
   int z = stbi__get8(s);
   return z + (stbi__get8(s) << 8);
}
#endif
#define STBI__BYTECAST(x)  ((stbi_uc) ((x) & 255))
#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM)
#else
static stbi_uc stbi__compute_y(int r, int g, int b)
{
   return (stbi_uc) (((r*77) + (g*150) +  (29*b)) >> 8);
}
#endif
#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM)
#else
static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y)
{
   int i,j;
   unsigned char *good;
   if (req_comp == img_n) return data;
   STBI_ASSERT(req_comp >= 1 && req_comp <= 4);
   good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0);
   if (good == NULL) {
      STBI_FREE(data);
      return stbi__errpuc("outofmem", "Out of memory");
   }
   for (j=0; j < (int) y; ++j) {
      unsigned char *src  = data + j * x * img_n   ;
      unsigned char *dest = good + j * x * req_comp;
      #define STBI__COMBO(a,b)  ((a)*8+(b))
      #define STBI__CASE(a,b)   case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)
      switch (STBI__COMBO(img_n, req_comp)) {
         STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255;                                     } break;
         STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0];                                  } break;
         STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255;                     } break;
         STBI__CASE(2,1) { dest[0]=src[0];                                                  } break;
         STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0];                                  } break;
         STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1];                  } break;
         STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255;        } break;
         STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]);                   } break;
         STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255;    } break;
         STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]);                   } break;
         STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break;
         STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];                    } break;
         default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion");
      }
      #undef STBI__CASE
   }
   STBI_FREE(data);
   return good;
}
#endif
#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD)
#else
static stbi__uint16 stbi__compute_y_16(int r, int g, int b)
{
   return (stbi__uint16) (((r*77) + (g*150) +  (29*b)) >> 8);
}
#endif
#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD)
#else
static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y)
{
   int i,j;
   stbi__uint16 *good;
   if (req_comp == img_n) return data;
   STBI_ASSERT(req_comp >= 1 && req_comp <= 4);
   good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2);
   if (good == NULL) {
      STBI_FREE(data);
      return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory");
   }
   for (j=0; j < (int) y; ++j) {
      stbi__uint16 *src  = data + j * x * img_n   ;
      stbi__uint16 *dest = good + j * x * req_comp;
      #define STBI__COMBO(a,b)  ((a)*8+(b))
      #define STBI__CASE(a,b)   case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)
      switch (STBI__COMBO(img_n, req_comp)) {
         STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff;                                     } break;
         STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0];                                     } break;
         STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff;                     } break;
         STBI__CASE(2,1) { dest[0]=src[0];                                                     } break;
         STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0];                                     } break;
         STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1];                     } break;
         STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff;        } break;
         STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]);                   } break;
         STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break;
         STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]);                   } break;
         STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break;
         STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];                       } break;
         default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion");
      }
      #undef STBI__CASE
   }
   STBI_FREE(data);
   return good;
}
#endif
#ifndef STBI_NO_ZLIB
#define STBI__ZFAST_BITS  9
#define STBI__ZFAST_MASK  ((1 << STBI__ZFAST_BITS) - 1)
#define STBI__ZNSYMS 288
typedef struct
{
   stbi__uint16 fast[1 << STBI__ZFAST_BITS];
   stbi__uint16 firstcode[16];
   int maxcode[17];
   stbi__uint16 firstsymbol[16];
   stbi_uc  size[STBI__ZNSYMS];
   stbi__uint16 value[STBI__ZNSYMS];
} stbi__zhuffman;
stbi_inline static int stbi__bitreverse16(int n)
{
  n = ((n & 0xAAAA) >>  1) | ((n & 0x5555) << 1);
  n = ((n & 0xCCCC) >>  2) | ((n & 0x3333) << 2);
  n = ((n & 0xF0F0) >>  4) | ((n & 0x0F0F) << 4);
  n = ((n & 0xFF00) >>  8) | ((n & 0x00FF) << 8);
  return n;
}
stbi_inline static int stbi__bit_reverse(int v, int bits)
{
   STBI_ASSERT(bits <= 16);
   return stbi__bitreverse16(v) >> (16-bits);
}
static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num)
{
   int i,k=0;
   int code, next_code[16], sizes[17];
   memset(sizes, 0, sizeof(sizes));
   memset(z->fast, 0, sizeof(z->fast));
   for (i=0; i < num; ++i)
      ++sizes[sizelist[i]];
   sizes[0] = 0;
   for (i=1; i < 16; ++i)
      if (sizes[i] > (1 << i))
         return stbi__err("bad sizes", "Corrupt PNG");
   code = 0;
   for (i=1; i < 16; ++i) {
      next_code[i] = code;
      z->firstcode[i] = (stbi__uint16) code;
      z->firstsymbol[i] = (stbi__uint16) k;
      code = (code + sizes[i]);
      if (sizes[i])
         if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG");
      z->maxcode[i] = code << (16-i);
      code <<= 1;
      k += sizes[i];
   }
   z->maxcode[16] = 0x10000;
   for (i=0; i < num; ++i) {
      int s = sizelist[i];
      if (s) {
         int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s];
         stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i);
         z->size [c] = (stbi_uc     ) s;
         z->value[c] = (stbi__uint16) i;
         if (s <= STBI__ZFAST_BITS) {
            int j = stbi__bit_reverse(next_code[s],s);
            while (j < (1 << STBI__ZFAST_BITS)) {
               z->fast[j] = fastv;
               j += (1 << s);
            }
         }
         ++next_code[s];
      }
   }
   return 1;
}
typedef struct
{
   stbi_uc *zbuffer, *zbuffer_end;
   int num_bits;
   int hit_zeof_once;
   stbi__uint32 code_buffer;
   char *zout;
   char *zout_start;
   char *zout_end;
   int   z_expandable;
   stbi__zhuffman z_length, z_distance;
} stbi__zbuf;
stbi_inline static int stbi__zeof(stbi__zbuf *z)
{
   return (z->zbuffer >= z->zbuffer_end);
}
stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z)
{
   return stbi__zeof(z) ? 0 : *z->zbuffer++;
}
static void stbi__fill_bits(stbi__zbuf *z)
{
   do {
      if (z->code_buffer >= (1U << z->num_bits)) {
        z->zbuffer = z->zbuffer_end;
        return;
      }
      z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits;
      z->num_bits += 8;
   } while (z->num_bits <= 24);
}
stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n)
{
   unsigned int k;
   if (z->num_bits < n) stbi__fill_bits(z);
   k = z->code_buffer & ((1 << n) - 1);
   z->code_buffer >>= n;
   z->num_bits -= n;
   return k;
}
static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z)
{
   int b,s,k;
   k = stbi__bit_reverse(a->code_buffer, 16);
   for (s=STBI__ZFAST_BITS+1; ; ++s)
      if (k < z->maxcode[s])
         break;
   if (s >= 16) return -1;
   b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s];
   if (b >= STBI__ZNSYMS) return -1;
   if (z->size[b] != s) return -1;
   a->code_buffer >>= s;
   a->num_bits -= s;
   return z->value[b];
}
stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)
{
   int b,s;
   if (a->num_bits < 16) {
      if (stbi__zeof(a)) {
         if (!a->hit_zeof_once) {
            a->hit_zeof_once = 1;
            a->num_bits += 16;
         } else {
            return -1;
         }
      } else {
         stbi__fill_bits(a);
      }
   }
   b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
   if (b) {
      s = b >> 9;
      a->code_buffer >>= s;
      a->num_bits -= s;
      return b & 511;
   }
   return stbi__zhuffman_decode_slowpath(a, z);
}
static int stbi__zexpand(stbi__zbuf *z, char *zout, int n)
{
   char *q;
   unsigned int cur, limit, old_limit;
   z->zout = zout;
   if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG");
   cur   = (unsigned int) (z->zout - z->zout_start);
   limit = old_limit = (unsigned) (z->zout_end - z->zout_start);
   if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory");
   while (cur + n > limit) {
      if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory");
      limit *= 2;
   }
   q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit);
   STBI_NOTUSED(old_limit);
   if (q == NULL) return stbi__err("outofmem", "Out of memory");
   z->zout_start = q;
   z->zout       = q + cur;
   z->zout_end   = q + limit;
   return 1;
}
static const int stbi__zlength_base[31] = {
   3,4,5,6,7,8,9,10,11,13,
   15,17,19,23,27,31,35,43,51,59,
   67,83,99,115,131,163,195,227,258,0,0 };
static const int stbi__zlength_extra[31]=
{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,
257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
static const int stbi__zdist_extra[32] =
{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
static int stbi__parse_huffman_block(stbi__zbuf *a)
{
   char *zout = a->zout;
   for(;;) {
      int z = stbi__zhuffman_decode(a, &a->z_length);
      if (z < 256) {
         if (z < 0) return stbi__err("bad huffman code","Corrupt PNG");
         if (zout >= a->zout_end) {
            if (!stbi__zexpand(a, zout, 1)) return 0;
            zout = a->zout;
         }
         *zout++ = (char) z;
      } else {
         stbi_uc *p;
         int len,dist;
         if (z == 256) {
            a->zout = zout;
            if (a->hit_zeof_once && a->num_bits < 16) {
               return stbi__err("unexpected end","Corrupt PNG");
            }
            return 1;
         }
         if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG");
         z -= 257;
         len = stbi__zlength_base[z];
         if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);
         z = stbi__zhuffman_decode(a, &a->z_distance);
         if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG");
         dist = stbi__zdist_base[z];
         if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
         if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
         if (len > a->zout_end - zout) {
            if (!stbi__zexpand(a, zout, len)) return 0;
            zout = a->zout;
         }
         p = (stbi_uc *) (zout - dist);
         if (dist == 1) {
            stbi_uc v = *p;
            if (len) { do *zout++ = v; while (--len); }
         } else {
            if (len) { do *zout++ = *p++; while (--len); }
         }
      }
   }
}
static int stbi__compute_huffman_codes(stbi__zbuf *a)
{
   static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
   stbi__zhuffman z_codelength;
   stbi_uc lencodes[286+32+137];
   stbi_uc codelength_sizes[19];
   int i,n;
   int hlit  = stbi__zreceive(a,5) + 257;
   int hdist = stbi__zreceive(a,5) + 1;
   int hclen = stbi__zreceive(a,4) + 4;
   int ntot  = hlit + hdist;
   memset(codelength_sizes, 0, sizeof(codelength_sizes));
   for (i=0; i < hclen; ++i) {
      int s = stbi__zreceive(a,3);
      codelength_sizes[length_dezigzag[i]] = (stbi_uc) s;
   }
   if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0;
   n = 0;
   while (n < ntot) {
      int c = stbi__zhuffman_decode(a, &z_codelength);
      if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG");
      if (c < 16)
         lencodes[n++] = (stbi_uc) c;
      else {
         stbi_uc fill = 0;
         if (c == 16) {
            c = stbi__zreceive(a,2)+3;
            if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG");
            fill = lencodes[n-1];
         } else if (c == 17) {
            c = stbi__zreceive(a,3)+3;
         } else if (c == 18) {
            c = stbi__zreceive(a,7)+11;
         } else {
            return stbi__err("bad codelengths", "Corrupt PNG");
         }
         if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG");
         memset(lencodes+n, fill, c);
         n += c;
      }
   }
   if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG");
   if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0;
   if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0;
   return 1;
}
static int stbi__parse_uncompressed_block(stbi__zbuf *a)
{
   stbi_uc header[4];
   int len,nlen,k;
   if (a->num_bits & 7)
      stbi__zreceive(a, a->num_bits & 7);
   k = 0;
   while (a->num_bits > 0) {
      header[k++] = (stbi_uc) (a->code_buffer & 255);
      a->code_buffer >>= 8;
      a->num_bits -= 8;
   }
   if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG");
   while (k < 4)
      header[k++] = stbi__zget8(a);
   len  = header[1] * 256 + header[0];
   nlen = header[3] * 256 + header[2];
   if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG");
   if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG");
   if (a->zout + len > a->zout_end)
      if (!stbi__zexpand(a, a->zout, len)) return 0;
   memcpy(a->zout, a->zbuffer, len);
   a->zbuffer += len;
   a->zout += len;
   return 1;
}
static int stbi__parse_zlib_header(stbi__zbuf *a)
{
   int cmf   = stbi__zget8(a);
   int cm    = cmf & 15;
   int flg   = stbi__zget8(a);
   if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG");
   if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG");
   if (flg & 32) return stbi__err("no preset dict","Corrupt PNG");
   if (cm != 8) return stbi__err("bad compression","Corrupt PNG");
   return 1;
}
static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] =
{
   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
   9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
   9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
   9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8
};
static const stbi_uc stbi__zdefault_distance[32] =
{
   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5
};
static int stbi__parse_zlib(stbi__zbuf *a, int parse_header)
{
   int final, type;
   if (parse_header)
      if (!stbi__parse_zlib_header(a)) return 0;
   a->num_bits = 0;
   a->code_buffer = 0;
   a->hit_zeof_once = 0;
   do {
      final = stbi__zreceive(a,1);
      type = stbi__zreceive(a,2);
      if (type == 0) {
         if (!stbi__parse_uncompressed_block(a)) return 0;
      } else if (type == 3) {
         return 0;
      } else {
         if (type == 1) {
            if (!stbi__zbuild_huffman(&a->z_length  , stbi__zdefault_length  , STBI__ZNSYMS)) return 0;
            if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance,  32)) return 0;
         } else {
            if (!stbi__compute_huffman_codes(a)) return 0;
         }
         if (!stbi__parse_huffman_block(a)) return 0;
      }
   } while (!final);
   return 1;
}
static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header)
{
   a->zout_start = obuf;
   a->zout       = obuf;
   a->zout_end   = obuf + olen;
   a->z_expandable = exp;
   return stbi__parse_zlib(a, parse_header);
}
STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen)
{
   stbi__zbuf a;
   char *p = (char *) stbi__malloc(initial_size);
   if (p == NULL) return NULL;
   a.zbuffer = (stbi_uc *) buffer;
   a.zbuffer_end = (stbi_uc *) buffer + len;
   if (stbi__do_zlib(&a, p, initial_size, 1, 1)) {
      if (outlen) *outlen = (int) (a.zout - a.zout_start);
      return a.zout_start;
   } else {
      STBI_FREE(a.zout_start);
      return NULL;
   }
}
STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen)
{
   return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen);
}
STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header)
{
   stbi__zbuf a;
   char *p = (char *) stbi__malloc(initial_size);
   if (p == NULL) return NULL;
   a.zbuffer = (stbi_uc *) buffer;
   a.zbuffer_end = (stbi_uc *) buffer + len;
   if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) {
      if (outlen) *outlen = (int) (a.zout - a.zout_start);
      return a.zout_start;
   } else {
      STBI_FREE(a.zout_start);
      return NULL;
   }
}
STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen)
{
   stbi__zbuf a;
   a.zbuffer = (stbi_uc *) ibuffer;
   a.zbuffer_end = (stbi_uc *) ibuffer + ilen;
   if (stbi__do_zlib(&a, obuffer, olen, 0, 1))
      return (int) (a.zout - a.zout_start);
   else
      return -1;
}
STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen)
{
   stbi__zbuf a;
   char *p = (char *) stbi__malloc(16384);
   if (p == NULL) return NULL;
   a.zbuffer = (stbi_uc *) buffer;
   a.zbuffer_end = (stbi_uc *) buffer+len;
   if (stbi__do_zlib(&a, p, 16384, 1, 0)) {
      if (outlen) *outlen = (int) (a.zout - a.zout_start);
      return a.zout_start;
   } else {
      STBI_FREE(a.zout_start);
      return NULL;
   }
}
STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen)
{
   stbi__zbuf a;
   a.zbuffer = (stbi_uc *) ibuffer;
   a.zbuffer_end = (stbi_uc *) ibuffer + ilen;
   if (stbi__do_zlib(&a, obuffer, olen, 0, 0))
      return (int) (a.zout - a.zout_start);
   else
      return -1;
}
#endif
#ifndef STBI_NO_PNG
typedef struct
{
   stbi__uint32 length;
   stbi__uint32 type;
} stbi__pngchunk;
static stbi__pngchunk stbi__get_chunk_header(stbi__context *s)
{
   stbi__pngchunk c;
   c.length = stbi__get32be(s);
   c.type   = stbi__get32be(s);
   return c;
}
static int stbi__check_png_header(stbi__context *s)
{
   static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 };
   int i;
   for (i=0; i < 8; ++i)
      if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG");
   return 1;
}
typedef struct
{
   stbi__context *s;
   stbi_uc *idata, *expanded, *out;
   int depth;
} stbi__png;
enum {
   STBI__F_none=0,
   STBI__F_sub=1,
   STBI__F_up=2,
   STBI__F_avg=3,
   STBI__F_paeth=4,
   STBI__F_avg_first
};
static stbi_uc first_row_filter[5] =
{
   STBI__F_none,
   STBI__F_sub,
   STBI__F_none,
   STBI__F_avg_first,
   STBI__F_sub
};
static int stbi__paeth(int a, int b, int c)
{
   int thresh = c*3 - (a + b);
   int lo = a < b ? a : b;
   int hi = a < b ? b : a;
   int t0 = (hi <= thresh) ? lo : c;
   int t1 = (thresh <= lo) ? hi : t0;
   return t1;
}
static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };
static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n)
{
   int i;
   if (img_n == 1) {
      for (i=x-1; i >= 0; --i) {
         dest[i*2+1] = 255;
         dest[i*2+0] = src[i];
      }
   } else {
      STBI_ASSERT(img_n == 3);
      for (i=x-1; i >= 0; --i) {
         dest[i*4+3] = 255;
         dest[i*4+2] = src[i*3+2];
         dest[i*4+1] = src[i*3+1];
         dest[i*4+0] = src[i*3+0];
      }
   }
}
static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
{
   int bytes = (depth == 16 ? 2 : 1);
   stbi__context *s = a->s;
   stbi__uint32 i,j,stride = x*out_n*bytes;
   stbi__uint32 img_len, img_width_bytes;
   stbi_uc *filter_buf;
   int all_ok = 1;
   int k;
   int img_n = s->img_n;
   int output_bytes = out_n*bytes;
   int filter_bytes = img_n*bytes;
   int width = x;
   STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1);
   a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0);
   if (!a->out) return stbi__err("outofmem", "Out of memory");
   if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG");
   img_width_bytes = (((img_n * x * depth) + 7) >> 3);
   if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG");
   img_len = (img_width_bytes + 1) * y;
   if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG");
   filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0);
   if (!filter_buf) return stbi__err("outofmem", "Out of memory");
   if (depth < 8) {
      filter_bytes = 1;
      width = img_width_bytes;
   }
   for (j=0; j < y; ++j) {
      stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes;
      stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes;
      stbi_uc *dest = a->out + stride*j;
      int nk = width * filter_bytes;
      int filter = *raw++;
      if (filter > 4) {
         all_ok = stbi__err("invalid filter","Corrupt PNG");
         break;
      }
      if (j == 0) filter = first_row_filter[filter];
      switch (filter) {
      case STBI__F_none:
         memcpy(cur, raw, nk);
         break;
      case STBI__F_sub:
         memcpy(cur, raw, filter_bytes);
         for (k = filter_bytes; k < nk; ++k)
            cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]);
         break;
      case STBI__F_up:
         for (k = 0; k < nk; ++k)
            cur[k] = STBI__BYTECAST(raw[k] + prior[k]);
         break;
      case STBI__F_avg:
         for (k = 0; k < filter_bytes; ++k)
            cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1));
         for (k = filter_bytes; k < nk; ++k)
            cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1));
         break;
      case STBI__F_paeth:
         for (k = 0; k < filter_bytes; ++k)
            cur[k] = STBI__BYTECAST(raw[k] + prior[k]);
         for (k = filter_bytes; k < nk; ++k)
            cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes]));
         break;
      case STBI__F_avg_first:
         memcpy(cur, raw, filter_bytes);
         for (k = filter_bytes; k < nk; ++k)
            cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1));
         break;
      }
      raw += nk;
      if (depth < 8) {
         stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1;
         stbi_uc *in = cur;
         stbi_uc *out = dest;
         stbi_uc inb = 0;
         stbi__uint32 nsmp = x*img_n;
         if (depth == 4) {
            for (i=0; i < nsmp; ++i) {
               if ((i & 1) == 0) inb = *in++;
               *out++ = scale * (inb >> 4);
               inb <<= 4;
            }
         } else if (depth == 2) {
            for (i=0; i < nsmp; ++i) {
               if ((i & 3) == 0) inb = *in++;
               *out++ = scale * (inb >> 6);
               inb <<= 2;
            }
         } else {
            STBI_ASSERT(depth == 1);
            for (i=0; i < nsmp; ++i) {
               if ((i & 7) == 0) inb = *in++;
               *out++ = scale * (inb >> 7);
               inb <<= 1;
            }
         }
         if (img_n != out_n)
            stbi__create_png_alpha_expand8(dest, dest, x, img_n);
      } else if (depth == 8) {
         if (img_n == out_n)
            memcpy(dest, cur, x*img_n);
         else
            stbi__create_png_alpha_expand8(dest, cur, x, img_n);
      } else if (depth == 16) {
         stbi__uint16 *dest16 = (stbi__uint16*)dest;
         stbi__uint32 nsmp = x*img_n;
         if (img_n == out_n) {
            for (i = 0; i < nsmp; ++i, ++dest16, cur += 2)
               *dest16 = (cur[0] << 8) | cur[1];
         } else {
            STBI_ASSERT(img_n+1 == out_n);
            if (img_n == 1) {
               for (i = 0; i < x; ++i, dest16 += 2, cur += 2) {
                  dest16[0] = (cur[0] << 8) | cur[1];
                  dest16[1] = 0xffff;
               }
            } else {
               STBI_ASSERT(img_n == 3);
               for (i = 0; i < x; ++i, dest16 += 4, cur += 6) {
                  dest16[0] = (cur[0] << 8) | cur[1];
                  dest16[1] = (cur[2] << 8) | cur[3];
                  dest16[2] = (cur[4] << 8) | cur[5];
                  dest16[3] = 0xffff;
               }
            }
         }
      }
   }
   STBI_FREE(filter_buf);
   if (!all_ok) return 0;
   return 1;
}
static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced)
{
   int bytes = (depth == 16 ? 2 : 1);
   int out_bytes = out_n * bytes;
   stbi_uc *final;
   int p;
   if (!interlaced)
      return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color);
   final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0);
   if (!final) return stbi__err("outofmem", "Out of memory");
   for (p=0; p < 7; ++p) {
      int xorig[] = { 0,4,0,2,0,1,0 };
      int yorig[] = { 0,0,4,0,2,0,1 };
      int xspc[]  = { 8,8,4,4,2,2,1 };
      int yspc[]  = { 8,8,8,4,4,2,2 };
      int i,j,x,y;
      x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p];
      y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p];
      if (x && y) {
         stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y;
         if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) {
            STBI_FREE(final);
            return 0;
         }
         for (j=0; j < y; ++j) {
            for (i=0; i < x; ++i) {
               int out_y = j*yspc[p]+yorig[p];
               int out_x = i*xspc[p]+xorig[p];
               memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes,
                      a->out + (j*x+i)*out_bytes, out_bytes);
            }
         }
         STBI_FREE(a->out);
         image_data += img_len;
         image_data_len -= img_len;
      }
   }
   a->out = final;
   return 1;
}
static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n)
{
   stbi__context *s = z->s;
   stbi__uint32 i, pixel_count = s->img_x * s->img_y;
   stbi_uc *p = z->out;
   STBI_ASSERT(out_n == 2 || out_n == 4);
   if (out_n == 2) {
      for (i=0; i < pixel_count; ++i) {
         p[1] = (p[0] == tc[0] ? 0 : 255);
         p += 2;
      }
   } else {
      for (i=0; i < pixel_count; ++i) {
         if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
            p[3] = 0;
         p += 4;
      }
   }
   return 1;
}
static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n)
{
   stbi__context *s = z->s;
   stbi__uint32 i, pixel_count = s->img_x * s->img_y;
   stbi__uint16 *p = (stbi__uint16*) z->out;
   STBI_ASSERT(out_n == 2 || out_n == 4);
   if (out_n == 2) {
      for (i = 0; i < pixel_count; ++i) {
         p[1] = (p[0] == tc[0] ? 0 : 65535);
         p += 2;
      }
   } else {
      for (i = 0; i < pixel_count; ++i) {
         if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
            p[3] = 0;
         p += 4;
      }
   }
   return 1;
}
static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n)
{
   stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y;
   stbi_uc *p, *temp_out, *orig = a->out;
   p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0);
   if (p == NULL) return stbi__err("outofmem", "Out of memory");
   temp_out = p;
   if (pal_img_n == 3) {
      for (i=0; i < pixel_count; ++i) {
         int n = orig[i]*4;
         p[0] = palette[n  ];
         p[1] = palette[n+1];
         p[2] = palette[n+2];
         p += 3;
      }
   } else {
      for (i=0; i < pixel_count; ++i) {
         int n = orig[i]*4;
         p[0] = palette[n  ];
         p[1] = palette[n+1];
         p[2] = palette[n+2];
         p[3] = palette[n+3];
         p += 4;
      }
   }
   STBI_FREE(a->out);
   a->out = temp_out;
   STBI_NOTUSED(len);
   return 1;
}
static int stbi__unpremultiply_on_load_global = 0;
static int stbi__de_iphone_flag_global = 0;
STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply)
{
   stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply;
}
STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)
{
   stbi__de_iphone_flag_global = flag_true_if_should_convert;
}
#ifndef STBI_THREAD_LOCAL
#define stbi__unpremultiply_on_load  stbi__unpremultiply_on_load_global
#define stbi__de_iphone_flag  stbi__de_iphone_flag_global
#else
static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set;
static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set;
STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)
{
   stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply;
   stbi__unpremultiply_on_load_set = 1;
}
STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert)
{
   stbi__de_iphone_flag_local = flag_true_if_should_convert;
   stbi__de_iphone_flag_set = 1;
}
#define stbi__unpremultiply_on_load  (stbi__unpremultiply_on_load_set           \
                                       ? stbi__unpremultiply_on_load_local      \
                                       : stbi__unpremultiply_on_load_global)
#define stbi__de_iphone_flag  (stbi__de_iphone_flag_set                         \
                                ? stbi__de_iphone_flag_local                    \
                                : stbi__de_iphone_flag_global)
#endif
static void stbi__de_iphone(stbi__png *z)
{
   stbi__context *s = z->s;
   stbi__uint32 i, pixel_count = s->img_x * s->img_y;
   stbi_uc *p = z->out;
   if (s->img_out_n == 3) {
      for (i=0; i < pixel_count; ++i) {
         stbi_uc t = p[0];
         p[0] = p[2];
         p[2] = t;
         p += 3;
      }
   } else {
      STBI_ASSERT(s->img_out_n == 4);
      if (stbi__unpremultiply_on_load) {
         for (i=0; i < pixel_count; ++i) {
            stbi_uc a = p[3];
            stbi_uc t = p[0];
            if (a) {
               stbi_uc half = a / 2;
               p[0] = (p[2] * 255 + half) / a;
               p[1] = (p[1] * 255 + half) / a;
               p[2] = ( t   * 255 + half) / a;
            } else {
               p[0] = p[2];
               p[2] = t;
            }
            p += 4;
         }
      } else {
         for (i=0; i < pixel_count; ++i) {
            stbi_uc t = p[0];
            p[0] = p[2];
            p[2] = t;
            p += 4;
         }
      }
   }
}
#define STBI__PNG_TYPE(a,b,c,d)  (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d))
static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
{
   stbi_uc palette[1024], pal_img_n=0;
   stbi_uc has_trans=0, tc[3]={0};
   stbi__uint16 tc16[3];
   stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0;
   int first=1,k,interlace=0, color=0, is_iphone=0;
   stbi__context *s = z->s;
   z->expanded = NULL;
   z->idata = NULL;
   z->out = NULL;
   if (!stbi__check_png_header(s)) return 0;
   if (scan == STBI__SCAN_type) return 1;
   for (;;) {
      stbi__pngchunk c = stbi__get_chunk_header(s);
      switch (c.type) {
         case STBI__PNG_TYPE('C','g','B','I'):
            is_iphone = 1;
            stbi__skip(s, c.length);
            break;
         case STBI__PNG_TYPE('I','H','D','R'): {
            int comp,filter;
            if (!first) return stbi__err("multiple IHDR","Corrupt PNG");
            first = 0;
            if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG");
            s->img_x = stbi__get32be(s);
            s->img_y = stbi__get32be(s);
            if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)");
            if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)");
            z->depth = stbi__get8(s);  if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16)  return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only");
            color = stbi__get8(s);  if (color > 6)         return stbi__err("bad ctype","Corrupt PNG");
            if (color == 3 && z->depth == 16)                  return stbi__err("bad ctype","Corrupt PNG");
            if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG");
            comp  = stbi__get8(s);  if (comp) return stbi__err("bad comp method","Corrupt PNG");
            filter= stbi__get8(s);  if (filter) return stbi__err("bad filter method","Corrupt PNG");
            interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG");
            if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG");
            if (!pal_img_n) {
               s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
               if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");
            } else {
               s->img_n = 1;
               if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG");
            }
            break;
         }
         case STBI__PNG_TYPE('P','L','T','E'):  {
            if (first) return stbi__err("first not IHDR", "Corrupt PNG");
            if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG");
            pal_len = c.length / 3;
            if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG");
            for (i=0; i < pal_len; ++i) {
               palette[i*4+0] = stbi__get8(s);
               palette[i*4+1] = stbi__get8(s);
               palette[i*4+2] = stbi__get8(s);
               palette[i*4+3] = 255;
            }
            break;
         }
         case STBI__PNG_TYPE('t','R','N','S'): {
            if (first) return stbi__err("first not IHDR", "Corrupt PNG");
            if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG");
            if (pal_img_n) {
               if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; }
               if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG");
               if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG");
               pal_img_n = 4;
               for (i=0; i < c.length; ++i)
                  palette[i*4+3] = stbi__get8(s);
            } else {
               if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG");
               if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG");
               has_trans = 1;
               if (scan == STBI__SCAN_header) { ++s->img_n; return 1; }
               if (z->depth == 16) {
                  for (k = 0; k < s->img_n && k < 3; ++k)
                     tc16[k] = (stbi__uint16)stbi__get16be(s);
               } else {
                  for (k = 0; k < s->img_n && k < 3; ++k)
                     tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth];
               }
            }
            break;
         }
         case STBI__PNG_TYPE('I','D','A','T'): {
            if (first) return stbi__err("first not IHDR", "Corrupt PNG");
            if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG");
            if (scan == STBI__SCAN_header) {
               if (pal_img_n)
                  s->img_n = pal_img_n;
               return 1;
            }
            if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes");
            if ((int)(ioff + c.length) < (int)ioff) return 0;
            if (ioff + c.length > idata_limit) {
               stbi__uint32 idata_limit_old = idata_limit;
               stbi_uc *p;
               if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096;
               while (ioff + c.length > idata_limit)
                  idata_limit *= 2;
               STBI_NOTUSED(idata_limit_old);
               p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory");
               z->idata = p;
            }
            if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG");
            ioff += c.length;
            break;
         }
         case STBI__PNG_TYPE('I','E','N','D'): {
            stbi__uint32 raw_len, bpl;
            if (first) return stbi__err("first not IHDR", "Corrupt PNG");
            if (scan != STBI__SCAN_load) return 1;
            if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG");
            bpl = (s->img_x * z->depth + 7) / 8;
            raw_len = bpl * s->img_y * s->img_n + s->img_y;
            z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone);
            if (z->expanded == NULL) return 0;
            STBI_FREE(z->idata); z->idata = NULL;
            if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans)
               s->img_out_n = s->img_n+1;
            else
               s->img_out_n = s->img_n;
            if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0;
            if (has_trans) {
               if (z->depth == 16) {
                  if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0;
               } else {
                  if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0;
               }
            }
            if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2)
               stbi__de_iphone(z);
            if (pal_img_n) {
               s->img_n = pal_img_n;
               s->img_out_n = pal_img_n;
               if (req_comp >= 3) s->img_out_n = req_comp;
               if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n))
                  return 0;
            } else if (has_trans) {
               ++s->img_n;
            }
            STBI_FREE(z->expanded); z->expanded = NULL;
            stbi__get32be(s);
            return 1;
         }
         default:
            if (first) return stbi__err("first not IHDR", "Corrupt PNG");
            if ((c.type & (1 << 29)) == 0) {
               #ifndef STBI_NO_FAILURE_STRINGS
               static char invalid_chunk[] = "XXXX PNG chunk not known";
               invalid_chunk[0] = STBI__BYTECAST(c.type >> 24);
               invalid_chunk[1] = STBI__BYTECAST(c.type >> 16);
               invalid_chunk[2] = STBI__BYTECAST(c.type >>  8);
               invalid_chunk[3] = STBI__BYTECAST(c.type >>  0);
               #endif
               return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type");
            }
            stbi__skip(s, c.length);
            break;
      }
      stbi__get32be(s);
   }
}
static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri)
{
   void *result=NULL;
   if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error");
   if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) {
      if (p->depth <= 8)
         ri->bits_per_channel = 8;
      else if (p->depth == 16)
         ri->bits_per_channel = 16;
      else
         return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth");
      result = p->out;
      p->out = NULL;
      if (req_comp && req_comp != p->s->img_out_n) {
         if (ri->bits_per_channel == 8)
            result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
         else
            result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
         p->s->img_out_n = req_comp;
         if (result == NULL) return result;
      }
      *x = p->s->img_x;
      *y = p->s->img_y;
      if (n) *n = p->s->img_n;
   }
   STBI_FREE(p->out);      p->out      = NULL;
   STBI_FREE(p->expanded); p->expanded = NULL;
   STBI_FREE(p->idata);    p->idata    = NULL;
   return result;
}
static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
{
   stbi__png p;
   p.s = s;
   return stbi__do_png(&p, x,y,comp,req_comp, ri);
}
static int stbi__png_test(stbi__context *s)
{
   int r;
   r = stbi__check_png_header(s);
   stbi__rewind(s);
   return r;
}
static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp)
{
   if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) {
      stbi__rewind( p->s );
      return 0;
   }
   if (x) *x = p->s->img_x;
   if (y) *y = p->s->img_y;
   if (comp) *comp = p->s->img_n;
   return 1;
}
static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp)
{
   stbi__png p;
   p.s = s;
   return stbi__png_info_raw(&p, x, y, comp);
}
static int stbi__png_is16(stbi__context *s)
{
   stbi__png p;
   p.s = s;
   if (!stbi__png_info_raw(&p, NULL, NULL, NULL))
	   return 0;
   if (p.depth != 16) {
      stbi__rewind(p.s);
      return 0;
   }
   return 1;
}
#endif
static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp)
{
   #ifndef STBI_NO_PNG
   if (stbi__png_info(s, x, y, comp))  return 1;
   #endif
   return stbi__err("unknown image type", "Image not of any known type, or corrupt");
}
static int stbi__is_16_main(stbi__context *s)
{
   #ifndef STBI_NO_PNG
   if (stbi__png_is16(s))  return 1;
   #endif
   return 0;
}
STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp)
{
   stbi__context s;
   stbi__start_mem(&s,buffer,len);
   return stbi__info_main(&s,x,y,comp);
}
STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp)
{
   stbi__context s;
   stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user);
   return stbi__info_main(&s,x,y,comp);
}
STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len)
{
   stbi__context s;
   stbi__start_mem(&s,buffer,len);
   return stbi__is_16_main(&s);
}
STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user)
{
   stbi__context s;
   stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user);
   return stbi__is_16_main(&s);
}
#endif

#endif

#ifndef APCK_OGG
#define APCK_OGG stb_vorbis_decode_memory
#define STB_VORBIS_NO_STDIO
#define STB_VORBIS_NO_PUSHDATA_API
#define STB_VORBIS_MAX_CHANNELS    8
/* Ogg Vorbis audio decoder - v1.22 - public domain
    https://github.com/nothings/stb */
/**************** embeded stb_vorbis.h ****************/

#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H
#define STB_VORBIS_INCLUDE_STB_VORBIS_H
#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO)
#define STB_VORBIS_NO_STDIO 1
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
   char *alloc_buffer;
   int   alloc_buffer_length_in_bytes;
} stb_vorbis_alloc;
typedef struct stb_vorbis stb_vorbis;
typedef struct
{
   unsigned int sample_rate;
   int channels;
   unsigned int setup_memory_required;
   unsigned int setup_temp_memory_required;
   unsigned int temp_memory_required;
   int max_frame_size;
} stb_vorbis_info;
typedef struct
{
   char *vendor;
   int comment_list_length;
   char **comment_list;
} stb_vorbis_comment;
extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f);
extern stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f);
extern int stb_vorbis_get_error(stb_vorbis *f);
extern void stb_vorbis_close(stb_vorbis *f);
extern int stb_vorbis_get_sample_offset(stb_vorbis *f);
extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f);
#ifndef STB_VORBIS_NO_PULLDATA_API
#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION)
extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output);
#endif
#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION)
extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output);
#endif
extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len,
                                  int *error, const stb_vorbis_alloc *alloc_buffer);
extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number);
extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number);
extern int stb_vorbis_seek_start(stb_vorbis *f);
extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f);
extern float        stb_vorbis_stream_length_in_seconds(stb_vorbis *f);
extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output);
#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts);
extern int stb_vorbis_get_frame_short            (stb_vorbis *f, int num_c, short **buffer, int num_samples);
#endif
extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats);
extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples);
#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts);
extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples);
#endif
#endif
enum STBVorbisError
{
   VORBIS__no_error,
   VORBIS_need_more_data=1,
   VORBIS_invalid_api_mixing,
   VORBIS_outofmem,
   VORBIS_feature_not_supported,
   VORBIS_too_many_channels,
   VORBIS_file_open_failure,
   VORBIS_seek_without_length,
   VORBIS_unexpected_eof=10,
   VORBIS_seek_invalid,
   VORBIS_invalid_setup=20,
   VORBIS_invalid_stream,
   VORBIS_missing_capture_pattern=30,
   VORBIS_invalid_stream_structure_version,
   VORBIS_continued_packet_flag_invalid,
   VORBIS_incorrect_stream_serial_number,
   VORBIS_invalid_first_page,
   VORBIS_bad_packet_type,
   VORBIS_cant_find_last_page,
   VORBIS_seek_failed,
   VORBIS_ogg_skeleton_not_supported
};
#ifdef __cplusplus
}
#endif
#endif
#ifndef STB_VORBIS_HEADER_ONLY
#ifndef STB_VORBIS_MAX_CHANNELS
#define STB_VORBIS_MAX_CHANNELS    16
#endif
#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT
#define STB_VORBIS_PUSHDATA_CRC_COUNT  4
#endif
#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH
#define STB_VORBIS_FAST_HUFFMAN_LENGTH   10
#endif
#ifndef STB_VORBIS_FAST_HUFFMAN_INT
#define STB_VORBIS_FAST_HUFFMAN_SHORT
#endif
#ifdef STB_VORBIS_CODEBOOK_SHORTS
#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats"
#endif
#ifdef STB_VORBIS_NO_PULLDATA_API
   #define STB_VORBIS_NO_INTEGER_CONVERSION
   #define STB_VORBIS_NO_STDIO
#endif
#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO)
   #define STB_VORBIS_NO_STDIO 1
#endif
#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT
   #ifndef STB_VORBIS_BIG_ENDIAN
     #define STB_VORBIS_ENDIAN  0
   #else
     #define STB_VORBIS_ENDIAN  1
   #endif
#endif
#endif
#ifndef STB_VORBIS_NO_CRT
   #include <stdlib.h>
   #include <string.h>
   #include <assert.h>
   #include <math.h>
   #if defined(_MSC_VER) || defined(__MINGW32__)
      #include <malloc.h>
   #endif
   #if defined(__linux__) || defined(__linux) || defined(__sun__) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__)
      #include <alloca.h>
   #endif
#else
   #define NULL 0
   #define malloc(s)   0
   #define free(s)     ((void) 0)
   #define realloc(s)  0
#endif
#include <limits.h>
#ifdef __MINGW32__
   #ifdef __forceinline
   #undef __forceinline
   #endif
   #define __forceinline
   #ifndef alloca
   #define alloca __builtin_alloca
   #endif
#elif !defined(_MSC_VER)
   #if __GNUC__
      #define __forceinline inline
   #else
      #define __forceinline
   #endif
#endif
#if STB_VORBIS_MAX_CHANNELS > 256
#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range"
#endif
#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24
#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range"
#endif
#if 0
#include <crtdbg.h>
#define CHECK(f)   _CrtIsValidHeapPointer(f->channel_buffers[1])
#else
#define CHECK(f)   ((void) 0)
#endif
#define MAX_BLOCKSIZE_LOG  13
#define MAX_BLOCKSIZE      (1 << MAX_BLOCKSIZE_LOG)
typedef unsigned char  uint8;
typedef   signed char   int8;
typedef unsigned short uint16;
typedef   signed short  int16;
typedef unsigned int   uint32;
typedef   signed int    int32;
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
typedef float codetype;
#ifdef _MSC_VER
#define STBV_NOTUSED(v)  (void)(v)
#else
#define STBV_NOTUSED(v)  (void)sizeof(v)
#endif
#define FAST_HUFFMAN_TABLE_SIZE   (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH)
#define FAST_HUFFMAN_TABLE_MASK   (FAST_HUFFMAN_TABLE_SIZE - 1)
typedef struct
{
   int dimensions, entries;
   uint8 *codeword_lengths;
   float  minimum_value;
   float  delta_value;
   uint8  value_bits;
   uint8  lookup_type;
   uint8  sequence_p;
   uint8  sparse;
   uint32 lookup_values;
   codetype *multiplicands;
   uint32 *codewords;
   #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT
    int16  fast_huffman[FAST_HUFFMAN_TABLE_SIZE];
   #else
    int32  fast_huffman[FAST_HUFFMAN_TABLE_SIZE];
   #endif
   uint32 *sorted_codewords;
   int    *sorted_values;
   int     sorted_entries;
} Codebook;
typedef struct
{
   uint8 order;
   uint16 rate;
   uint16 bark_map_size;
   uint8 amplitude_bits;
   uint8 amplitude_offset;
   uint8 number_of_books;
   uint8 book_list[16];
} Floor0;
typedef struct
{
   uint8 partitions;
   uint8 partition_class_list[32];
   uint8 class_dimensions[16];
   uint8 class_subclasses[16];
   uint8 class_masterbooks[16];
   int16 subclass_books[16][8];
   uint16 Xlist[31*8+2];
   uint8 sorted_order[31*8+2];
   uint8 neighbors[31*8+2][2];
   uint8 floor1_multiplier;
   uint8 rangebits;
   int values;
} Floor1;
typedef union
{
   Floor0 floor0;
   Floor1 floor1;
} Floor;
typedef struct
{
   uint32 begin, end;
   uint32 part_size;
   uint8 classifications;
   uint8 classbook;
   uint8 **classdata;
   int16 (*residue_books)[8];
} Residue;
typedef struct
{
   uint8 magnitude;
   uint8 angle;
   uint8 mux;
} MappingChannel;
typedef struct
{
   uint16 coupling_steps;
   MappingChannel *chan;
   uint8  submaps;
   uint8  submap_floor[15];
   uint8  submap_residue[15];
} Mapping;
typedef struct
{
   uint8 blockflag;
   uint8 mapping;
   uint16 windowtype;
   uint16 transformtype;
} Mode;
typedef struct
{
   uint32  goal_crc;
   int     bytes_left;
   uint32  crc_so_far;
   int     bytes_done;
   uint32  sample_loc;
} CRCscan;
typedef struct
{
   uint32 page_start, page_end;
   uint32 last_decoded_sample;
} ProbedPage;
struct stb_vorbis
{
   unsigned int sample_rate;
   int channels;
   unsigned int setup_memory_required;
   unsigned int temp_memory_required;
   unsigned int setup_temp_memory_required;
   char *vendor;
   int comment_list_length;
   char **comment_list;
   uint8 *stream;
   uint8 *stream_start;
   uint8 *stream_end;
   uint32 stream_len;
   uint8  push_mode;
   uint32 first_audio_page_offset;
   ProbedPage p_first, p_last;
   stb_vorbis_alloc alloc;
   int setup_offset;
   int temp_offset;
   int eof;
   enum STBVorbisError error;
   int blocksize[2];
   int blocksize_0, blocksize_1;
   int codebook_count;
   Codebook *codebooks;
   int floor_count;
   uint16 floor_types[64];
   Floor *floor_config;
   int residue_count;
   uint16 residue_types[64];
   Residue *residue_config;
   int mapping_count;
   Mapping *mapping;
   int mode_count;
   Mode mode_config[64];
   uint32 total_samples;
   float *channel_buffers[STB_VORBIS_MAX_CHANNELS];
   float *outputs        [STB_VORBIS_MAX_CHANNELS];
   float *previous_window[STB_VORBIS_MAX_CHANNELS];
   int previous_length;
   #ifndef STB_VORBIS_NO_DEFER_FLOOR
   int16 *finalY[STB_VORBIS_MAX_CHANNELS];
   #else
   float *floor_buffers[STB_VORBIS_MAX_CHANNELS];
   #endif
   uint32 current_loc;
   int    current_loc_valid;
   float *A[2],*B[2],*C[2];
   float *window[2];
   uint16 *bit_reverse[2];
   uint32 serial;
   int last_page;
   int segment_count;
   uint8 segments[255];
   uint8 page_flag;
   uint8 bytes_in_seg;
   uint8 first_decode;
   int next_seg;
   int last_seg;
   int last_seg_which;
   uint32 acc;
   int valid_bits;
   int packet_bytes;
   int end_seg_with_known_loc;
   uint32 known_loc_for_packet;
   int discard_samples_deferred;
   uint32 samples_output;
   int page_crc_tests;
   int channel_buffer_start;
   int channel_buffer_end;
};
#if defined(STB_VORBIS_NO_PUSHDATA_API)
   #define IS_PUSH_MODE(f)   FALSE
#elif defined(STB_VORBIS_NO_PULLDATA_API)
   #define IS_PUSH_MODE(f)   TRUE
#else
   #define IS_PUSH_MODE(f)   ((f)->push_mode)
#endif
typedef struct stb_vorbis vorb;
static int error(vorb *f, enum STBVorbisError e)
{
   f->error = e;
   if (!f->eof && e != VORBIS_need_more_data) {
      f->error=e;
   }
   return 0;
}
#define array_size_required(count,size)  (count*(sizeof(void *)+(size)))
#define temp_alloc(f,size)              (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size))
#define temp_free(f,p)                  (void)0
#define temp_alloc_save(f)              ((f)->temp_offset)
#define temp_alloc_restore(f,p)         ((f)->temp_offset = (p))
#define temp_block_array(f,count,size)  make_block_array(temp_alloc(f,array_size_required(count,size)), count, size)
static void *make_block_array(void *mem, int count, int size)
{
   int i;
   void ** p = (void **) mem;
   char *q = (char *) (p + count);
   for (i=0; i < count; ++i) {
      p[i] = q;
      q += size;
   }
   return p;
}
static void *setup_malloc(vorb *f, int sz)
{
   sz = (sz+7) & ~7;
   f->setup_memory_required += sz;
   if (f->alloc.alloc_buffer) {
      void *p = (char *) f->alloc.alloc_buffer + f->setup_offset;
      if (f->setup_offset + sz > f->temp_offset) return NULL;
      f->setup_offset += sz;
      return p;
   }
   return sz ? malloc(sz) : NULL;
}
static void setup_free(vorb *f, void *p)
{
   if (f->alloc.alloc_buffer) return;
   free(p);
}
static void *setup_temp_malloc(vorb *f, int sz)
{
   sz = (sz+7) & ~7;
   if (f->alloc.alloc_buffer) {
      if (f->temp_offset - sz < f->setup_offset) return NULL;
      f->temp_offset -= sz;
      return (char *) f->alloc.alloc_buffer + f->temp_offset;
   }
   return malloc(sz);
}
static void setup_temp_free(vorb *f, void *p, int sz)
{
   if (f->alloc.alloc_buffer) {
      f->temp_offset += (sz+7)&~7;
      return;
   }
   free(p);
}
#define CRC32_POLY    0x04c11db7
static uint32 crc_table[256];
static void crc32_init(void)
{
   int i,j;
   uint32 s;
   for(i=0; i < 256; i++) {
      for (s=(uint32) i << 24, j=0; j < 8; ++j)
         s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0);
      crc_table[i] = s;
   }
}
static __forceinline uint32 crc32_update(uint32 crc, uint8 byte)
{
   return (crc << 8) ^ crc_table[byte ^ (crc >> 24)];
}
static unsigned int bit_reverse(unsigned int n)
{
  n = ((n & 0xAAAAAAAA) >>  1) | ((n & 0x55555555) << 1);
  n = ((n & 0xCCCCCCCC) >>  2) | ((n & 0x33333333) << 2);
  n = ((n & 0xF0F0F0F0) >>  4) | ((n & 0x0F0F0F0F) << 4);
  n = ((n & 0xFF00FF00) >>  8) | ((n & 0x00FF00FF) << 8);
  return (n >> 16) | (n << 16);
}
static float square(float x)
{
   return x*x;
}
static int ilog(int32 n)
{
   static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 };
   if (n < 0) return 0;
   if (n < (1 << 14))
        if (n < (1 <<  4))            return  0 + log2_4[n      ];
        else if (n < (1 <<  9))       return  5 + log2_4[n >>  5];
             else                     return 10 + log2_4[n >> 10];
   else if (n < (1 << 24))
             if (n < (1 << 19))       return 15 + log2_4[n >> 15];
             else                     return 20 + log2_4[n >> 20];
        else if (n < (1 << 29))       return 25 + log2_4[n >> 25];
             else                     return 30 + log2_4[n >> 30];
}
#ifndef M_PI
  #define M_PI  3.14159265358979323846264f
#endif
#define NO_CODE   255
static float float32_unpack(uint32 x)
{
   uint32 mantissa = x & 0x1fffff;
   uint32 sign = x & 0x80000000;
   uint32 exp = (x & 0x7fe00000) >> 21;
   double res = sign ? -(double)mantissa : (double)mantissa;
   return (float) ldexp((float)res, (int)exp-788);
}
static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values)
{
   if (!c->sparse) {
      c->codewords      [symbol] = huff_code;
   } else {
      c->codewords       [count] = huff_code;
      c->codeword_lengths[count] = len;
      values             [count] = symbol;
   }
}
static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values)
{
   int i,k,m=0;
   uint32 available[32];
   memset(available, 0, sizeof(available));
   for (k=0; k < n; ++k) if (len[k] < NO_CODE) break;
   if (k == n) { assert(c->sorted_entries == 0); return TRUE; }
   assert(len[k] < 32);
   add_entry(c, 0, k, m++, len[k], values);
   for (i=1; i <= len[k]; ++i)
      available[i] = 1U << (32-i);
   for (i=k+1; i < n; ++i) {
      uint32 res;
      int z = len[i], y;
      if (z == NO_CODE) continue;
      assert(z < 32);
      while (z > 0 && !available[z]) --z;
      if (z == 0) { return FALSE; }
      res = available[z];
      available[z] = 0;
      add_entry(c, bit_reverse(res), i, m++, len[i], values);
      if (z != len[i]) {
         for (y=len[i]; y > z; --y) {
            assert(available[y] == 0);
            available[y] = res + (1 << (32-y));
         }
      }
   }
   return TRUE;
}
static void compute_accelerated_huffman(Codebook *c)
{
   int i, len;
   for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i)
      c->fast_huffman[i] = -1;
   len = c->sparse ? c->sorted_entries : c->entries;
   #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT
   if (len > 32767) len = 32767;
   #endif
   for (i=0; i < len; ++i) {
      if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) {
         uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i];
         while (z < FAST_HUFFMAN_TABLE_SIZE) {
             c->fast_huffman[z] = i;
             z += 1 << c->codeword_lengths[i];
         }
      }
   }
}
#ifdef _MSC_VER
#define STBV_CDECL __cdecl
#else
#define STBV_CDECL
#endif
static int STBV_CDECL uint32_compare(const void *p, const void *q)
{
   uint32 x = * (uint32 *) p;
   uint32 y = * (uint32 *) q;
   return x < y ? -1 : x > y;
}
static int include_in_sort(Codebook *c, uint8 len)
{
   if (c->sparse) { assert(len != NO_CODE); return TRUE; }
   if (len == NO_CODE) return FALSE;
   if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE;
   return FALSE;
}
static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values)
{
   int i, len;
   if (!c->sparse) {
      int k = 0;
      for (i=0; i < c->entries; ++i)
         if (include_in_sort(c, lengths[i]))
            c->sorted_codewords[k++] = bit_reverse(c->codewords[i]);
      assert(k == c->sorted_entries);
   } else {
      for (i=0; i < c->sorted_entries; ++i)
         c->sorted_codewords[i] = bit_reverse(c->codewords[i]);
   }
   qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare);
   c->sorted_codewords[c->sorted_entries] = 0xffffffff;
   len = c->sparse ? c->sorted_entries : c->entries;
   for (i=0; i < len; ++i) {
      int huff_len = c->sparse ? lengths[values[i]] : lengths[i];
      if (include_in_sort(c,huff_len)) {
         uint32 code = bit_reverse(c->codewords[i]);
         int x=0, n=c->sorted_entries;
         while (n > 1) {
            int m = x + (n >> 1);
            if (c->sorted_codewords[m] <= code) {
               x = m;
               n -= (n>>1);
            } else {
               n >>= 1;
            }
         }
         assert(c->sorted_codewords[x] == code);
         if (c->sparse) {
            c->sorted_values[x] = values[i];
            c->codeword_lengths[x] = huff_len;
         } else {
            c->sorted_values[x] = i;
         }
      }
   }
}
static int vorbis_validate(uint8 *data)
{
   static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' };
   return memcmp(data, vorbis, 6) == 0;
}
static int lookup1_values(int entries, int dim)
{
   int r = (int) floor(exp((float) log((float) entries) / dim));
   if ((int) floor(pow((float) r+1, dim)) <= entries)
      ++r;
   if (pow((float) r+1, dim) <= entries)
      return -1;
   if ((int) floor(pow((float) r, dim)) > entries)
      return -1;
   return r;
}
static void compute_twiddle_factors(int n, float *A, float *B, float *C)
{
   int n4 = n >> 2, n8 = n >> 3;
   int k,k2;
   for (k=k2=0; k < n4; ++k,k2+=2) {
      A[k2  ] = (float)  cos(4*k*M_PI/n);
      A[k2+1] = (float) -sin(4*k*M_PI/n);
      B[k2  ] = (float)  cos((k2+1)*M_PI/n/2) * 0.5f;
      B[k2+1] = (float)  sin((k2+1)*M_PI/n/2) * 0.5f;
   }
   for (k=k2=0; k < n8; ++k,k2+=2) {
      C[k2  ] = (float)  cos(2*(k2+1)*M_PI/n);
      C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n);
   }
}
static void compute_window(int n, float *window)
{
   int n2 = n >> 1, i;
   for (i=0; i < n2; ++i)
      window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI)));
}
static void compute_bitreverse(int n, uint16 *rev)
{
   int ld = ilog(n) - 1;
   int i, n8 = n >> 3;
   for (i=0; i < n8; ++i)
      rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2;
}
static int init_blocksize(vorb *f, int b, int n)
{
   int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3;
   f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2);
   f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2);
   f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4);
   if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem);
   compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]);
   f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2);
   if (!f->window[b]) return error(f, VORBIS_outofmem);
   compute_window(n, f->window[b]);
   f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8);
   if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem);
   compute_bitreverse(n, f->bit_reverse[b]);
   return TRUE;
}
static void neighbors(uint16 *x, int n, int *plow, int *phigh)
{
   int low = -1;
   int high = 65536;
   int i;
   for (i=0; i < n; ++i) {
      if (x[i] > low  && x[i] < x[n]) { *plow  = i; low = x[i]; }
      if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; }
   }
}
typedef struct
{
   uint16 x,id;
} stbv__floor_ordering;
static int STBV_CDECL point_compare(const void *p, const void *q)
{
   stbv__floor_ordering *a = (stbv__floor_ordering *) p;
   stbv__floor_ordering *b = (stbv__floor_ordering *) q;
   return a->x < b->x ? -1 : a->x > b->x;
}
#if defined(STB_VORBIS_NO_STDIO)
   #define USE_MEMORY(z)    TRUE
#else
   #define USE_MEMORY(z)    ((z)->stream)
#endif
static uint8 get8(vorb *z)
{
   if (USE_MEMORY(z)) {
      if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; }
      return *z->stream++;
   }
}
static uint32 get32(vorb *f)
{
   uint32 x;
   x = get8(f);
   x += get8(f) << 8;
   x += get8(f) << 16;
   x += (uint32) get8(f) << 24;
   return x;
}
static int getn(vorb *z, uint8 *data, int n)
{
   if (USE_MEMORY(z)) {
      if (z->stream+n > z->stream_end) { z->eof = 1; return 0; }
      memcpy(data, z->stream, n);
      z->stream += n;
      return 1;
   }
}
static void skip(vorb *z, int n)
{
   if (USE_MEMORY(z)) {
      z->stream += n;
      if (z->stream >= z->stream_end) z->eof = 1;
      return;
   }
}
static int set_file_offset(stb_vorbis *f, unsigned int loc)
{
   f->eof = 0;
   if (USE_MEMORY(f)) {
      if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) {
         f->stream = f->stream_end;
         f->eof = 1;
         return 0;
      } else {
         f->stream = f->stream_start + loc;
         return 1;
      }
   }
}
static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 };
static int capture_pattern(vorb *f)
{
   if (0x4f != get8(f)) return FALSE;
   if (0x67 != get8(f)) return FALSE;
   if (0x67 != get8(f)) return FALSE;
   if (0x53 != get8(f)) return FALSE;
   return TRUE;
}
#define PAGEFLAG_continued_packet   1
#define PAGEFLAG_first_page         2
#define PAGEFLAG_last_page          4
static int start_page_no_capturepattern(vorb *f)
{
   uint32 loc0,loc1,n;
   if (f->first_decode && !IS_PUSH_MODE(f)) {
      f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4;
   }
   if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version);
   f->page_flag = get8(f);
   loc0 = get32(f);
   loc1 = get32(f);
   get32(f);
   n = get32(f);
   f->last_page = n;
   get32(f);
   f->segment_count = get8(f);
   if (!getn(f, f->segments, f->segment_count))
      return error(f, VORBIS_unexpected_eof);
   f->end_seg_with_known_loc = -2;
   if (loc0 != ~0U || loc1 != ~0U) {
      int i;
      for (i=f->segment_count-1; i >= 0; --i)
         if (f->segments[i] < 255)
            break;
      if (i >= 0) {
         f->end_seg_with_known_loc = i;
         f->known_loc_for_packet   = loc0;
      }
   }
   if (f->first_decode) {
      int i,len;
      len = 0;
      for (i=0; i < f->segment_count; ++i)
         len += f->segments[i];
      len += 27 + f->segment_count;
      f->p_first.page_end = f->p_first.page_start + len;
      f->p_first.last_decoded_sample = loc0;
   }
   f->next_seg = 0;
   return TRUE;
}
static int start_page(vorb *f)
{
   if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern);
   return start_page_no_capturepattern(f);
}
static int start_packet(vorb *f)
{
   while (f->next_seg == -1) {
      if (!start_page(f)) return FALSE;
      if (f->page_flag & PAGEFLAG_continued_packet)
         return error(f, VORBIS_continued_packet_flag_invalid);
   }
   f->last_seg = FALSE;
   f->valid_bits = 0;
   f->packet_bytes = 0;
   f->bytes_in_seg = 0;
   return TRUE;
}
static int maybe_start_packet(vorb *f)
{
   if (f->next_seg == -1) {
      int x = get8(f);
      if (f->eof) return FALSE;
      if (0x4f != x      ) return error(f, VORBIS_missing_capture_pattern);
      if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern);
      if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern);
      if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern);
      if (!start_page_no_capturepattern(f)) return FALSE;
      if (f->page_flag & PAGEFLAG_continued_packet) {
         f->last_seg = FALSE;
         f->bytes_in_seg = 0;
         return error(f, VORBIS_continued_packet_flag_invalid);
      }
   }
   return start_packet(f);
}
static int next_segment(vorb *f)
{
   int len;
   if (f->last_seg) return 0;
   if (f->next_seg == -1) {
      f->last_seg_which = f->segment_count-1;
      if (!start_page(f)) { f->last_seg = 1; return 0; }
      if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid);
   }
   len = f->segments[f->next_seg++];
   if (len < 255) {
      f->last_seg = TRUE;
      f->last_seg_which = f->next_seg-1;
   }
   if (f->next_seg >= f->segment_count)
      f->next_seg = -1;
   assert(f->bytes_in_seg == 0);
   f->bytes_in_seg = len;
   return len;
}
#define EOP    (-1)
#define INVALID_BITS  (-1)
static int get8_packet_raw(vorb *f)
{
   if (!f->bytes_in_seg) {
      if (f->last_seg) return EOP;
      else if (!next_segment(f)) return EOP;
   }
   assert(f->bytes_in_seg > 0);
   --f->bytes_in_seg;
   ++f->packet_bytes;
   return get8(f);
}
static int get8_packet(vorb *f)
{
   int x = get8_packet_raw(f);
   f->valid_bits = 0;
   return x;
}
static int get32_packet(vorb *f)
{
   uint32 x;
   x = get8_packet(f);
   x += get8_packet(f) << 8;
   x += get8_packet(f) << 16;
   x += (uint32) get8_packet(f) << 24;
   return x;
}
static void flush_packet(vorb *f)
{
   while (get8_packet_raw(f) != EOP);
}
static uint32 get_bits(vorb *f, int n)
{
   uint32 z;
   if (f->valid_bits < 0) return 0;
   if (f->valid_bits < n) {
      if (n > 24) {
         z = get_bits(f, 24);
         z += get_bits(f, n-24) << 24;
         return z;
      }
      if (f->valid_bits == 0) f->acc = 0;
      while (f->valid_bits < n) {
         int z = get8_packet_raw(f);
         if (z == EOP) {
            f->valid_bits = INVALID_BITS;
            return 0;
         }
         f->acc += z << f->valid_bits;
         f->valid_bits += 8;
      }
   }
   assert(f->valid_bits >= n);
   z = f->acc & ((1 << n)-1);
   f->acc >>= n;
   f->valid_bits -= n;
   return z;
}
static __forceinline void prep_huffman(vorb *f)
{
   if (f->valid_bits <= 24) {
      if (f->valid_bits == 0) f->acc = 0;
      do {
         int z;
         if (f->last_seg && !f->bytes_in_seg) return;
         z = get8_packet_raw(f);
         if (z == EOP) return;
         f->acc += (unsigned) z << f->valid_bits;
         f->valid_bits += 8;
      } while (f->valid_bits <= 24);
   }
}
enum
{
   VORBIS_packet_id = 1,
   VORBIS_packet_comment = 3,
   VORBIS_packet_setup = 5
};
static int codebook_decode_scalar_raw(vorb *f, Codebook *c)
{
   int i;
   prep_huffman(f);
   if (c->codewords == NULL && c->sorted_codewords == NULL)
      return -1;
   if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) {
      uint32 code = bit_reverse(f->acc);
      int x=0, n=c->sorted_entries, len;
      while (n > 1) {
         int m = x + (n >> 1);
         if (c->sorted_codewords[m] <= code) {
            x = m;
            n -= (n>>1);
         } else {
            n >>= 1;
         }
      }
      if (!c->sparse) x = c->sorted_values[x];
      len = c->codeword_lengths[x];
      if (f->valid_bits >= len) {
         f->acc >>= len;
         f->valid_bits -= len;
         return x;
      }
      f->valid_bits = 0;
      return -1;
   }
   assert(!c->sparse);
   for (i=0; i < c->entries; ++i) {
      if (c->codeword_lengths[i] == NO_CODE) continue;
      if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) {
         if (f->valid_bits >= c->codeword_lengths[i]) {
            f->acc >>= c->codeword_lengths[i];
            f->valid_bits -= c->codeword_lengths[i];
            return i;
         }
         f->valid_bits = 0;
         return -1;
      }
   }
   error(f, VORBIS_invalid_stream);
   f->valid_bits = 0;
   return -1;
}
#ifndef STB_VORBIS_NO_INLINE_DECODE
#define DECODE_RAW(var, f,c)                                  \
   if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH)        \
      prep_huffman(f);                                        \
   var = f->acc & FAST_HUFFMAN_TABLE_MASK;                    \
   var = c->fast_huffman[var];                                \
   if (var >= 0) {                                            \
      int n = c->codeword_lengths[var];                       \
      f->acc >>= n;                                           \
      f->valid_bits -= n;                                     \
      if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \
   } else {                                                   \
      var = codebook_decode_scalar_raw(f,c);                  \
   }
#else
static int codebook_decode_scalar(vorb *f, Codebook *c)
{
   int i;
   if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH)
      prep_huffman(f);
   i = f->acc & FAST_HUFFMAN_TABLE_MASK;
   i = c->fast_huffman[i];
   if (i >= 0) {
      f->acc >>= c->codeword_lengths[i];
      f->valid_bits -= c->codeword_lengths[i];
      if (f->valid_bits < 0) { f->valid_bits = 0; return -1; }
      return i;
   }
   return codebook_decode_scalar_raw(f,c);
}
#define DECODE_RAW(var,f,c)    var = codebook_decode_scalar(f,c);
#endif
#define DECODE(var,f,c)                                       \
   DECODE_RAW(var,f,c)                                        \
   if (c->sparse) var = c->sorted_values[var];
#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
  #define DECODE_VQ(var,f,c)   DECODE_RAW(var,f,c)
#else
  #define DECODE_VQ(var,f,c)   DECODE(var,f,c)
#endif
#define CODEBOOK_ELEMENT(c,off)          (c->multiplicands[off])
#define CODEBOOK_ELEMENT_FAST(c,off)     (c->multiplicands[off])
#define CODEBOOK_ELEMENT_BASE(c)         (0)
static int codebook_decode_start(vorb *f, Codebook *c)
{
   int z = -1;
   if (c->lookup_type == 0)
      error(f, VORBIS_invalid_stream);
   else {
      DECODE_VQ(z,f,c);
      if (c->sparse) assert(z < c->sorted_entries);
      if (z < 0) {
         if (!f->bytes_in_seg)
            if (f->last_seg)
               return z;
         error(f, VORBIS_invalid_stream);
      }
   }
   return z;
}
static int codebook_decode(vorb *f, Codebook *c, float *output, int len)
{
   int i,z = codebook_decode_start(f,c);
   if (z < 0) return FALSE;
   if (len > c->dimensions) len = c->dimensions;
#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
   if (c->lookup_type == 1) {
      float last = CODEBOOK_ELEMENT_BASE(c);
      int div = 1;
      for (i=0; i < len; ++i) {
         int off = (z / div) % c->lookup_values;
         float val = CODEBOOK_ELEMENT_FAST(c,off) + last;
         output[i] += val;
         if (c->sequence_p) last = val + c->minimum_value;
         div *= c->lookup_values;
      }
      return TRUE;
   }
#endif
   z *= c->dimensions;
   if (c->sequence_p) {
      float last = CODEBOOK_ELEMENT_BASE(c);
      for (i=0; i < len; ++i) {
         float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
         output[i] += val;
         last = val + c->minimum_value;
      }
   } else {
      float last = CODEBOOK_ELEMENT_BASE(c);
      for (i=0; i < len; ++i) {
         output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last;
      }
   }
   return TRUE;
}
static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step)
{
   int i,z = codebook_decode_start(f,c);
   float last = CODEBOOK_ELEMENT_BASE(c);
   if (z < 0) return FALSE;
   if (len > c->dimensions) len = c->dimensions;
#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
   if (c->lookup_type == 1) {
      int div = 1;
      for (i=0; i < len; ++i) {
         int off = (z / div) % c->lookup_values;
         float val = CODEBOOK_ELEMENT_FAST(c,off) + last;
         output[i*step] += val;
         if (c->sequence_p) last = val;
         div *= c->lookup_values;
      }
      return TRUE;
   }
#endif
   z *= c->dimensions;
   for (i=0; i < len; ++i) {
      float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
      output[i*step] += val;
      if (c->sequence_p) last = val;
   }
   return TRUE;
}
static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode)
{
   int c_inter = *c_inter_p;
   int p_inter = *p_inter_p;
   int i,z, effective = c->dimensions;
   if (c->lookup_type == 0)   return error(f, VORBIS_invalid_stream);
   while (total_decode > 0) {
      float last = CODEBOOK_ELEMENT_BASE(c);
      DECODE_VQ(z,f,c);
      #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
      assert(!c->sparse || z < c->sorted_entries);
      #endif
      if (z < 0) {
         if (!f->bytes_in_seg)
            if (f->last_seg) return FALSE;
         return error(f, VORBIS_invalid_stream);
      }
      if (c_inter + p_inter*ch + effective > len * ch) {
         effective = len*ch - (p_inter*ch - c_inter);
      }
   #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
      if (c->lookup_type == 1) {
         int div = 1;
         for (i=0; i < effective; ++i) {
            int off = (z / div) % c->lookup_values;
            float val = CODEBOOK_ELEMENT_FAST(c,off) + last;
            if (outputs[c_inter])
               outputs[c_inter][p_inter] += val;
            if (++c_inter == ch) { c_inter = 0; ++p_inter; }
            if (c->sequence_p) last = val;
            div *= c->lookup_values;
         }
      } else
   #endif
      {
         z *= c->dimensions;
         if (c->sequence_p) {
            for (i=0; i < effective; ++i) {
               float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
               if (outputs[c_inter])
                  outputs[c_inter][p_inter] += val;
               if (++c_inter == ch) { c_inter = 0; ++p_inter; }
               last = val;
            }
         } else {
            for (i=0; i < effective; ++i) {
               float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
               if (outputs[c_inter])
                  outputs[c_inter][p_inter] += val;
               if (++c_inter == ch) { c_inter = 0; ++p_inter; }
            }
         }
      }
      total_decode -= effective;
   }
   *c_inter_p = c_inter;
   *p_inter_p = p_inter;
   return TRUE;
}
static int predict_point(int x, int x0, int x1, int y0, int y1)
{
   int dy = y1 - y0;
   int adx = x1 - x0;
   int err = abs(dy) * (x - x0);
   int off = err / adx;
   return dy < 0 ? y0 - off : y0 + off;
}
static float inverse_db_table[256] =
{
  1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f,
  1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f,
  1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f,
  2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f,
  2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f,
  3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f,
  4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f,
  6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f,
  7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f,
  1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f,
  1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f,
  1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f,
  2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f,
  2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f,
  3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f,
  4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f,
  5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f,
  7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f,
  9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f,
  1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f,
  1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f,
  2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f,
  2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f,
  3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f,
  4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f,
  5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f,
  7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f,
  9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f,
  0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f,
  0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f,
  0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f,
  0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f,
  0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f,
  0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f,
  0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f,
  0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f,
  0.00092223983f, 0.00098217216f, 0.0010459992f,  0.0011139742f,
  0.0011863665f,  0.0012634633f,  0.0013455702f,  0.0014330129f,
  0.0015261382f,  0.0016253153f,  0.0017309374f,  0.0018434235f,
  0.0019632195f,  0.0020908006f,  0.0022266726f,  0.0023713743f,
  0.0025254795f,  0.0026895994f,  0.0028643847f,  0.0030505286f,
  0.0032487691f,  0.0034598925f,  0.0036847358f,  0.0039241906f,
  0.0041792066f,  0.0044507950f,  0.0047400328f,  0.0050480668f,
  0.0053761186f,  0.0057254891f,  0.0060975636f,  0.0064938176f,
  0.0069158225f,  0.0073652516f,  0.0078438871f,  0.0083536271f,
  0.0088964928f,  0.009474637f,   0.010090352f,   0.010746080f,
  0.011444421f,   0.012188144f,   0.012980198f,   0.013823725f,
  0.014722068f,   0.015678791f,   0.016697687f,   0.017782797f,
  0.018938423f,   0.020169149f,   0.021479854f,   0.022875735f,
  0.024362330f,   0.025945531f,   0.027631618f,   0.029427276f,
  0.031339626f,   0.033376252f,   0.035545228f,   0.037855157f,
  0.040315199f,   0.042935108f,   0.045725273f,   0.048696758f,
  0.051861348f,   0.055231591f,   0.058820850f,   0.062643361f,
  0.066714279f,   0.071049749f,   0.075666962f,   0.080584227f,
  0.085821044f,   0.091398179f,   0.097337747f,   0.10366330f,
  0.11039993f,    0.11757434f,    0.12521498f,    0.13335215f,
  0.14201813f,    0.15124727f,    0.16107617f,    0.17154380f,
  0.18269168f,    0.19456402f,    0.20720788f,    0.22067342f,
  0.23501402f,    0.25028656f,    0.26655159f,    0.28387361f,
  0.30232132f,    0.32196786f,    0.34289114f,    0.36517414f,
  0.38890521f,    0.41417847f,    0.44109412f,    0.46975890f,
  0.50028648f,    0.53279791f,    0.56742212f,    0.60429640f,
  0.64356699f,    0.68538959f,    0.72993007f,    0.77736504f,
  0.82788260f,    0.88168307f,    0.9389798f,     1.0f
};
#ifndef STB_VORBIS_NO_DEFER_FLOOR
#define LINE_OP(a,b)   a *= b
#else
#define LINE_OP(a,b)   a = b
#endif
#ifdef STB_VORBIS_DIVIDE_TABLE
#define DIVTAB_NUMER   32
#define DIVTAB_DENOM   64
int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM];
#endif
static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n)
{
   int dy = y1 - y0;
   int adx = x1 - x0;
   int ady = abs(dy);
   int base;
   int x=x0,y=y0;
   int err = 0;
   int sy;
#ifdef STB_VORBIS_DIVIDE_TABLE
   if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) {
      if (dy < 0) {
         base = -integer_divide_table[ady][adx];
         sy = base-1;
      } else {
         base =  integer_divide_table[ady][adx];
         sy = base+1;
      }
   } else {
      base = dy / adx;
      if (dy < 0)
         sy = base - 1;
      else
         sy = base+1;
   }
#else
   base = dy / adx;
   if (dy < 0)
      sy = base - 1;
   else
      sy = base+1;
#endif
   ady -= abs(base) * adx;
   if (x1 > n) x1 = n;
   if (x < x1) {
      LINE_OP(output[x], inverse_db_table[y&255]);
      for (++x; x < x1; ++x) {
         err += ady;
         if (err >= adx) {
            err -= adx;
            y += sy;
         } else
            y += base;
         LINE_OP(output[x], inverse_db_table[y&255]);
      }
   }
}
static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype)
{
   int k;
   if (rtype == 0) {
      int step = n / book->dimensions;
      for (k=0; k < step; ++k)
         if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step))
            return FALSE;
   } else {
      for (k=0; k < n; ) {
         if (!codebook_decode(f, book, target+offset, n-k))
            return FALSE;
         k += book->dimensions;
         offset += book->dimensions;
      }
   }
   return TRUE;
}
static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode)
{
   int i,j,pass;
   Residue *r = f->residue_config + rn;
   int rtype = f->residue_types[rn];
   int c = r->classbook;
   int classwords = f->codebooks[c].dimensions;
   unsigned int actual_size = rtype == 2 ? n*2 : n;
   unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size);
   unsigned int limit_r_end   = (r->end   < actual_size ? r->end   : actual_size);
   int n_read = limit_r_end - limit_r_begin;
   int part_read = n_read / r->part_size;
   int temp_alloc_point = temp_alloc_save(f);
   #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
   uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata));
   #else
   int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications));
   #endif
   CHECK(f);
   for (i=0; i < ch; ++i)
      if (!do_not_decode[i])
         memset(residue_buffers[i], 0, sizeof(float) * n);
   if (rtype == 2 && ch != 1) {
      for (j=0; j < ch; ++j)
         if (!do_not_decode[j])
            break;
      if (j == ch)
         goto done;
      for (pass=0; pass < 8; ++pass) {
         int pcount = 0, class_set = 0;
         if (ch == 2) {
            while (pcount < part_read) {
               int z = r->begin + pcount*r->part_size;
               int c_inter = (z & 1), p_inter = z>>1;
               if (pass == 0) {
                  Codebook *c = f->codebooks+r->classbook;
                  int q;
                  DECODE(q,f,c);
                  if (q == EOP) goto done;
                  #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
                  part_classdata[0][class_set] = r->classdata[q];
                  #else
                  for (i=classwords-1; i >= 0; --i) {
                     classifications[0][i+pcount] = q % r->classifications;
                     q /= r->classifications;
                  }
                  #endif
               }
               for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) {
                  int z = r->begin + pcount*r->part_size;
                  #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
                  int c = part_classdata[0][class_set][i];
                  #else
                  int c = classifications[0][pcount];
                  #endif
                  int b = r->residue_books[c][pass];
                  if (b >= 0) {
                     Codebook *book = f->codebooks + b;
                     #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
                     if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size))
                        goto done;
                     #else
                     if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size))
                        goto done;
                     #endif
                  } else {
                     z += r->part_size;
                     c_inter = z & 1;
                     p_inter = z >> 1;
                  }
               }
               #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
               ++class_set;
               #endif
            }
         } else if (ch > 2) {
            while (pcount < part_read) {
               int z = r->begin + pcount*r->part_size;
               int c_inter = z % ch, p_inter = z/ch;
               if (pass == 0) {
                  Codebook *c = f->codebooks+r->classbook;
                  int q;
                  DECODE(q,f,c);
                  if (q == EOP) goto done;
                  #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
                  part_classdata[0][class_set] = r->classdata[q];
                  #else
                  for (i=classwords-1; i >= 0; --i) {
                     classifications[0][i+pcount] = q % r->classifications;
                     q /= r->classifications;
                  }
                  #endif
               }
               for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) {
                  int z = r->begin + pcount*r->part_size;
                  #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
                  int c = part_classdata[0][class_set][i];
                  #else
                  int c = classifications[0][pcount];
                  #endif
                  int b = r->residue_books[c][pass];
                  if (b >= 0) {
                     Codebook *book = f->codebooks + b;
                     if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size))
                        goto done;
                  } else {
                     z += r->part_size;
                     c_inter = z % ch;
                     p_inter = z / ch;
                  }
               }
               #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
               ++class_set;
               #endif
            }
         }
      }
      goto done;
   }
   CHECK(f);
   for (pass=0; pass < 8; ++pass) {
      int pcount = 0, class_set=0;
      while (pcount < part_read) {
         if (pass == 0) {
            for (j=0; j < ch; ++j) {
               if (!do_not_decode[j]) {
                  Codebook *c = f->codebooks+r->classbook;
                  int temp;
                  DECODE(temp,f,c);
                  if (temp == EOP) goto done;
                  #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
                  part_classdata[j][class_set] = r->classdata[temp];
                  #else
                  for (i=classwords-1; i >= 0; --i) {
                     classifications[j][i+pcount] = temp % r->classifications;
                     temp /= r->classifications;
                  }
                  #endif
               }
            }
         }
         for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) {
            for (j=0; j < ch; ++j) {
               if (!do_not_decode[j]) {
                  #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
                  int c = part_classdata[j][class_set][i];
                  #else
                  int c = classifications[j][pcount];
                  #endif
                  int b = r->residue_books[c][pass];
                  if (b >= 0) {
                     float *target = residue_buffers[j];
                     int offset = r->begin + pcount * r->part_size;
                     int n = r->part_size;
                     Codebook *book = f->codebooks + b;
                     if (!residue_decode(f, book, target, offset, n, rtype))
                        goto done;
                  }
               }
            }
         }
         #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
         ++class_set;
         #endif
      }
   }
  done:
   CHECK(f);
   #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
   temp_free(f,part_classdata);
   #else
   temp_free(f,classifications);
   #endif
   temp_alloc_restore(f,temp_alloc_point);
}
#if 0
void inverse_mdct_slow(float *buffer, int n)
{
   int i,j;
   int n2 = n >> 1;
   float *x = (float *) malloc(sizeof(*x) * n2);
   memcpy(x, buffer, sizeof(*x) * n2);
   for (i=0; i < n; ++i) {
      float acc = 0;
      for (j=0; j < n2; ++j)
         acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1));
      buffer[i] = acc;
   }
   free(x);
}
#elif 0
void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype)
{
   float mcos[16384];
   int i,j;
   int n2 = n >> 1, nmask = (n << 2) -1;
   float *x = (float *) malloc(sizeof(*x) * n2);
   memcpy(x, buffer, sizeof(*x) * n2);
   for (i=0; i < 4*n; ++i)
      mcos[i] = (float) cos(M_PI / 2 * i / n);
   for (i=0; i < n; ++i) {
      float acc = 0;
      for (j=0; j < n2; ++j)
         acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask];
      buffer[i] = acc;
   }
   free(x);
}
#elif 0
void dct_iv_slow(float *buffer, int n)
{
   float mcos[16384];
   float x[2048];
   int i,j;
   int n2 = n >> 1, nmask = (n << 3) - 1;
   memcpy(x, buffer, sizeof(*x) * n);
   for (i=0; i < 8*n; ++i)
      mcos[i] = (float) cos(M_PI / 4 * i / n);
   for (i=0; i < n; ++i) {
      float acc = 0;
      for (j=0; j < n; ++j)
         acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask];
      buffer[i] = acc;
   }
}
void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype)
{
   int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4;
   float temp[4096];
   memcpy(temp, buffer, n2 * sizeof(float));
   dct_iv_slow(temp, n2);
   for (i=0; i < n4  ; ++i) buffer[i] = temp[i+n4];
   for (   ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1];
   for (   ; i < n   ; ++i) buffer[i] = -temp[i - n3_4];
}
#endif
#ifndef LIBVORBIS_MDCT
#define LIBVORBIS_MDCT 0
#endif
#if LIBVORBIS_MDCT
typedef struct
{
  int n;
  int log2n;
  float *trig;
  int   *bitrev;
  float scale;
} mdct_lookup;
extern void mdct_init(mdct_lookup *lookup, int n);
extern void mdct_clear(mdct_lookup *l);
extern void mdct_backward(mdct_lookup *init, float *in, float *out);
mdct_lookup M1,M2;
void inverse_mdct(float *buffer, int n, vorb *f, int blocktype)
{
   mdct_lookup *M;
   if (M1.n == n) M = &M1;
   else if (M2.n == n) M = &M2;
   else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; }
   else {
      if (M2.n) __asm int 3;
      mdct_init(&M2, n);
      M = &M2;
   }
   mdct_backward(M, buffer, buffer);
}
#endif
static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A)
{
   float *ee0 = e + i_off;
   float *ee2 = ee0 + k_off;
   int i;
   assert((n & 3) == 0);
   for (i=(n>>2); i > 0; --i) {
      float k00_20, k01_21;
      k00_20  = ee0[ 0] - ee2[ 0];
      k01_21  = ee0[-1] - ee2[-1];
      ee0[ 0] += ee2[ 0];
      ee0[-1] += ee2[-1];
      ee2[ 0] = k00_20 * A[0] - k01_21 * A[1];
      ee2[-1] = k01_21 * A[0] + k00_20 * A[1];
      A += 8;
      k00_20  = ee0[-2] - ee2[-2];
      k01_21  = ee0[-3] - ee2[-3];
      ee0[-2] += ee2[-2];
      ee0[-3] += ee2[-3];
      ee2[-2] = k00_20 * A[0] - k01_21 * A[1];
      ee2[-3] = k01_21 * A[0] + k00_20 * A[1];
      A += 8;
      k00_20  = ee0[-4] - ee2[-4];
      k01_21  = ee0[-5] - ee2[-5];
      ee0[-4] += ee2[-4];
      ee0[-5] += ee2[-5];
      ee2[-4] = k00_20 * A[0] - k01_21 * A[1];
      ee2[-5] = k01_21 * A[0] + k00_20 * A[1];
      A += 8;
      k00_20  = ee0[-6] - ee2[-6];
      k01_21  = ee0[-7] - ee2[-7];
      ee0[-6] += ee2[-6];
      ee0[-7] += ee2[-7];
      ee2[-6] = k00_20 * A[0] - k01_21 * A[1];
      ee2[-7] = k01_21 * A[0] + k00_20 * A[1];
      A += 8;
      ee0 -= 8;
      ee2 -= 8;
   }
}
static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1)
{
   int i;
   float k00_20, k01_21;
   float *e0 = e + d0;
   float *e2 = e0 + k_off;
   for (i=lim >> 2; i > 0; --i) {
      k00_20 = e0[-0] - e2[-0];
      k01_21 = e0[-1] - e2[-1];
      e0[-0] += e2[-0];
      e0[-1] += e2[-1];
      e2[-0] = (k00_20)*A[0] - (k01_21) * A[1];
      e2[-1] = (k01_21)*A[0] + (k00_20) * A[1];
      A += k1;
      k00_20 = e0[-2] - e2[-2];
      k01_21 = e0[-3] - e2[-3];
      e0[-2] += e2[-2];
      e0[-3] += e2[-3];
      e2[-2] = (k00_20)*A[0] - (k01_21) * A[1];
      e2[-3] = (k01_21)*A[0] + (k00_20) * A[1];
      A += k1;
      k00_20 = e0[-4] - e2[-4];
      k01_21 = e0[-5] - e2[-5];
      e0[-4] += e2[-4];
      e0[-5] += e2[-5];
      e2[-4] = (k00_20)*A[0] - (k01_21) * A[1];
      e2[-5] = (k01_21)*A[0] + (k00_20) * A[1];
      A += k1;
      k00_20 = e0[-6] - e2[-6];
      k01_21 = e0[-7] - e2[-7];
      e0[-6] += e2[-6];
      e0[-7] += e2[-7];
      e2[-6] = (k00_20)*A[0] - (k01_21) * A[1];
      e2[-7] = (k01_21)*A[0] + (k00_20) * A[1];
      e0 -= 8;
      e2 -= 8;
      A += k1;
   }
}
static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0)
{
   int i;
   float A0 = A[0];
   float A1 = A[0+1];
   float A2 = A[0+a_off];
   float A3 = A[0+a_off+1];
   float A4 = A[0+a_off*2+0];
   float A5 = A[0+a_off*2+1];
   float A6 = A[0+a_off*3+0];
   float A7 = A[0+a_off*3+1];
   float k00,k11;
   float *ee0 = e  +i_off;
   float *ee2 = ee0+k_off;
   for (i=n; i > 0; --i) {
      k00     = ee0[ 0] - ee2[ 0];
      k11     = ee0[-1] - ee2[-1];
      ee0[ 0] =  ee0[ 0] + ee2[ 0];
      ee0[-1] =  ee0[-1] + ee2[-1];
      ee2[ 0] = (k00) * A0 - (k11) * A1;
      ee2[-1] = (k11) * A0 + (k00) * A1;
      k00     = ee0[-2] - ee2[-2];
      k11     = ee0[-3] - ee2[-3];
      ee0[-2] =  ee0[-2] + ee2[-2];
      ee0[-3] =  ee0[-3] + ee2[-3];
      ee2[-2] = (k00) * A2 - (k11) * A3;
      ee2[-3] = (k11) * A2 + (k00) * A3;
      k00     = ee0[-4] - ee2[-4];
      k11     = ee0[-5] - ee2[-5];
      ee0[-4] =  ee0[-4] + ee2[-4];
      ee0[-5] =  ee0[-5] + ee2[-5];
      ee2[-4] = (k00) * A4 - (k11) * A5;
      ee2[-5] = (k11) * A4 + (k00) * A5;
      k00     = ee0[-6] - ee2[-6];
      k11     = ee0[-7] - ee2[-7];
      ee0[-6] =  ee0[-6] + ee2[-6];
      ee0[-7] =  ee0[-7] + ee2[-7];
      ee2[-6] = (k00) * A6 - (k11) * A7;
      ee2[-7] = (k11) * A6 + (k00) * A7;
      ee0 -= k0;
      ee2 -= k0;
   }
}
static __forceinline void iter_54(float *z)
{
   float k00,k11,k22,k33;
   float y0,y1,y2,y3;
   k00  = z[ 0] - z[-4];
   y0   = z[ 0] + z[-4];
   y2   = z[-2] + z[-6];
   k22  = z[-2] - z[-6];
   z[-0] = y0 + y2;
   z[-2] = y0 - y2;
   k33  = z[-3] - z[-7];
   z[-4] = k00 + k33;
   z[-6] = k00 - k33;
   k11  = z[-1] - z[-5];
   y1   = z[-1] + z[-5];
   y3   = z[-3] + z[-7];
   z[-1] = y1 + y3;
   z[-3] = y1 - y3;
   z[-5] = k11 - k22;
   z[-7] = k11 + k22;
}
static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n)
{
   int a_off = base_n >> 3;
   float A2 = A[0+a_off];
   float *z = e + i_off;
   float *base = z - 16 * n;
   while (z > base) {
      float k00,k11;
      float l00,l11;
      k00    = z[-0] - z[ -8];
      k11    = z[-1] - z[ -9];
      l00    = z[-2] - z[-10];
      l11    = z[-3] - z[-11];
      z[ -0] = z[-0] + z[ -8];
      z[ -1] = z[-1] + z[ -9];
      z[ -2] = z[-2] + z[-10];
      z[ -3] = z[-3] + z[-11];
      z[ -8] = k00;
      z[ -9] = k11;
      z[-10] = (l00+l11) * A2;
      z[-11] = (l11-l00) * A2;
      k00    = z[ -4] - z[-12];
      k11    = z[ -5] - z[-13];
      l00    = z[ -6] - z[-14];
      l11    = z[ -7] - z[-15];
      z[ -4] = z[ -4] + z[-12];
      z[ -5] = z[ -5] + z[-13];
      z[ -6] = z[ -6] + z[-14];
      z[ -7] = z[ -7] + z[-15];
      z[-12] = k11;
      z[-13] = -k00;
      z[-14] = (l11-l00) * A2;
      z[-15] = (l00+l11) * -A2;
      iter_54(z);
      iter_54(z-8);
      z -= 16;
   }
}
static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype)
{
   int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l;
   int ld;
   int save_point = temp_alloc_save(f);
   float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2));
   float *u=NULL,*v=NULL;
   float *A = f->A[blocktype];
   {
      float *d,*e, *AA, *e_stop;
      d = &buf2[n2-2];
      AA = A;
      e = &buffer[0];
      e_stop = &buffer[n2];
      while (e != e_stop) {
         d[1] = (e[0] * AA[0] - e[2]*AA[1]);
         d[0] = (e[0] * AA[1] + e[2]*AA[0]);
         d -= 2;
         AA += 2;
         e += 4;
      }
      e = &buffer[n2-3];
      while (d >= buf2) {
         d[1] = (-e[2] * AA[0] - -e[0]*AA[1]);
         d[0] = (-e[2] * AA[1] + -e[0]*AA[0]);
         d -= 2;
         AA += 2;
         e -= 4;
      }
   }
   u = buffer;
   v = buf2;
   {
      float *AA = &A[n2-8];
      float *d0,*d1, *e0, *e1;
      e0 = &v[n4];
      e1 = &v[0];
      d0 = &u[n4];
      d1 = &u[0];
      while (AA >= A) {
         float v40_20, v41_21;
         v41_21 = e0[1] - e1[1];
         v40_20 = e0[0] - e1[0];
         d0[1]  = e0[1] + e1[1];
         d0[0]  = e0[0] + e1[0];
         d1[1]  = v41_21*AA[4] - v40_20*AA[5];
         d1[0]  = v40_20*AA[4] + v41_21*AA[5];
         v41_21 = e0[3] - e1[3];
         v40_20 = e0[2] - e1[2];
         d0[3]  = e0[3] + e1[3];
         d0[2]  = e0[2] + e1[2];
         d1[3]  = v41_21*AA[0] - v40_20*AA[1];
         d1[2]  = v40_20*AA[0] + v41_21*AA[1];
         AA -= 8;
         d0 += 4;
         d1 += 4;
         e0 += 4;
         e1 += 4;
      }
   }
   ld = ilog(n) - 1;
   imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A);
   imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A);
   imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16);
   imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16);
   imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16);
   imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16);
   l=2;
   for (; l < (ld-3)>>1; ++l) {
      int k0 = n >> (l+2), k0_2 = k0>>1;
      int lim = 1 << (l+1);
      int i;
      for (i=0; i < lim; ++i)
         imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3));
   }
   for (; l < ld-6; ++l) {
      int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1;
      int rlim = n >> (l+6), r;
      int lim = 1 << (l+1);
      int i_off;
      float *A0 = A;
      i_off = n2-1;
      for (r=rlim; r > 0; --r) {
         imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0);
         A0 += k1*4;
         i_off -= 8;
      }
   }
   imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n);
   {
      uint16 *bitrev = f->bit_reverse[blocktype];
      float *d0 = &v[n4-4];
      float *d1 = &v[n2-4];
      while (d0 >= v) {
         int k4;
         k4 = bitrev[0];
         d1[3] = u[k4+0];
         d1[2] = u[k4+1];
         d0[3] = u[k4+2];
         d0[2] = u[k4+3];
         k4 = bitrev[1];
         d1[1] = u[k4+0];
         d1[0] = u[k4+1];
         d0[1] = u[k4+2];
         d0[0] = u[k4+3];
         d0 -= 4;
         d1 -= 4;
         bitrev += 2;
      }
   }
   assert(v == buf2);
   {
      float *C = f->C[blocktype];
      float *d, *e;
      d = v;
      e = v + n2 - 4;
      while (d < e) {
         float a02,a11,b0,b1,b2,b3;
         a02 = d[0] - e[2];
         a11 = d[1] + e[3];
         b0 = C[1]*a02 + C[0]*a11;
         b1 = C[1]*a11 - C[0]*a02;
         b2 = d[0] + e[ 2];
         b3 = d[1] - e[ 3];
         d[0] = b2 + b0;
         d[1] = b3 + b1;
         e[2] = b2 - b0;
         e[3] = b1 - b3;
         a02 = d[2] - e[0];
         a11 = d[3] + e[1];
         b0 = C[3]*a02 + C[2]*a11;
         b1 = C[3]*a11 - C[2]*a02;
         b2 = d[2] + e[ 0];
         b3 = d[3] - e[ 1];
         d[2] = b2 + b0;
         d[3] = b3 + b1;
         e[0] = b2 - b0;
         e[1] = b1 - b3;
         C += 4;
         d += 4;
         e -= 4;
      }
   }
   {
      float *d0,*d1,*d2,*d3;
      float *B = f->B[blocktype] + n2 - 8;
      float *e = buf2 + n2 - 8;
      d0 = &buffer[0];
      d1 = &buffer[n2-4];
      d2 = &buffer[n2];
      d3 = &buffer[n-4];
      while (e >= v) {
         float p0,p1,p2,p3;
         p3 =  e[6]*B[7] - e[7]*B[6];
         p2 = -e[6]*B[6] - e[7]*B[7];
         d0[0] =   p3;
         d1[3] = - p3;
         d2[0] =   p2;
         d3[3] =   p2;
         p1 =  e[4]*B[5] - e[5]*B[4];
         p0 = -e[4]*B[4] - e[5]*B[5];
         d0[1] =   p1;
         d1[2] = - p1;
         d2[1] =   p0;
         d3[2] =   p0;
         p3 =  e[2]*B[3] - e[3]*B[2];
         p2 = -e[2]*B[2] - e[3]*B[3];
         d0[2] =   p3;
         d1[1] = - p3;
         d2[2] =   p2;
         d3[1] =   p2;
         p1 =  e[0]*B[1] - e[1]*B[0];
         p0 = -e[0]*B[0] - e[1]*B[1];
         d0[3] =   p1;
         d1[0] = - p1;
         d2[3] =   p0;
         d3[0] =   p0;
         B -= 8;
         e -= 8;
         d0 += 4;
         d2 += 4;
         d1 -= 4;
         d3 -= 4;
      }
   }
   temp_free(f,buf2);
   temp_alloc_restore(f,save_point);
}
#if 0
void inverse_mdct_naive(float *buffer, int n)
{
   float s;
   float A[1 << 12], B[1 << 12], C[1 << 11];
   int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l;
   int n3_4 = n - n4, ld;
   float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13];
   for (k=k2=0; k < n4; ++k,k2+=2) {
      A[k2  ] = (float)  cos(4*k*M_PI/n);
      A[k2+1] = (float) -sin(4*k*M_PI/n);
      B[k2  ] = (float)  cos((k2+1)*M_PI/n/2);
      B[k2+1] = (float)  sin((k2+1)*M_PI/n/2);
   }
   for (k=k2=0; k < n8; ++k,k2+=2) {
      C[k2  ] = (float)  cos(2*(k2+1)*M_PI/n);
      C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n);
   }
   for (k=0; k < n2; ++k) u[k] = buffer[k];
   for (   ; k < n ; ++k) u[k] = -buffer[n - k - 1];
   for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) {
      v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2]   - (u[k4+2] - u[n-k4-3])*A[k2+1];
      v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2];
   }
   for (k=k4=0; k < n8; k+=1, k4+=4) {
      w[n2+3+k4] = v[n2+3+k4] + v[k4+3];
      w[n2+1+k4] = v[n2+1+k4] + v[k4+1];
      w[k4+3]    = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4];
      w[k4+1]    = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4];
   }
   ld = ilog(n) - 1;
   for (l=0; l < ld-3; ++l) {
      int k0 = n >> (l+2), k1 = 1 << (l+3);
      int rlim = n >> (l+4), r4, r;
      int s2lim = 1 << (l+2), s2;
      for (r=r4=0; r < rlim; r4+=4,++r) {
         for (s2=0; s2 < s2lim; s2+=2) {
            u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4];
            u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4];
            u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1]
                                - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1];
            u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1]
                                + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1];
         }
      }
      if (l+1 < ld-3) {
         memcpy(w, u, sizeof(u));
      }
   }
   for (i=0; i < n8; ++i) {
      int j = bit_reverse(i) >> (32-ld+3);
      assert(j < n8);
      if (i == j) {
         int i8 = i << 3;
         v[i8+1] = u[i8+1];
         v[i8+3] = u[i8+3];
         v[i8+5] = u[i8+5];
         v[i8+7] = u[i8+7];
      } else if (i < j) {
         int i8 = i << 3, j8 = j << 3;
         v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1];
         v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3];
         v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5];
         v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7];
      }
   }
   for (k=0; k < n2; ++k) {
      w[k] = v[k*2+1];
   }
   for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) {
      u[n-1-k2] = w[k4];
      u[n-2-k2] = w[k4+1];
      u[n3_4 - 1 - k2] = w[k4+2];
      u[n3_4 - 2 - k2] = w[k4+3];
   }
   for (k=k2=0; k < n8; ++k, k2 += 2) {
      v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2;
      v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2;
      v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2;
      v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2;
   }
   for (k=k2=0; k < n4; ++k,k2 += 2) {
      X[k]      = v[k2+n2]*B[k2  ] + v[k2+1+n2]*B[k2+1];
      X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2  ];
   }
   s = 0.5;
   for (i=0; i < n4  ; ++i) buffer[i] = s * X[i+n4];
   for (   ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1];
   for (   ; i < n   ; ++i) buffer[i] = -s * X[i - n3_4];
}
#endif
static float *get_window(vorb *f, int len)
{
   len <<= 1;
   if (len == f->blocksize_0) return f->window[0];
   if (len == f->blocksize_1) return f->window[1];
   return NULL;
}
#ifndef STB_VORBIS_NO_DEFER_FLOOR
typedef int16 YTYPE;
#else
typedef int YTYPE;
#endif
static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag)
{
   int n2 = n >> 1;
   int s = map->chan[i].mux, floor;
   floor = map->submap_floor[s];
   if (f->floor_types[floor] == 0) {
      return error(f, VORBIS_invalid_stream);
   } else {
      Floor1 *g = &f->floor_config[floor].floor1;
      int j,q;
      int lx = 0, ly = finalY[0] * g->floor1_multiplier;
      for (q=1; q < g->values; ++q) {
         j = g->sorted_order[q];
         #ifndef STB_VORBIS_NO_DEFER_FLOOR
         STBV_NOTUSED(step2_flag);
         if (finalY[j] >= 0)
         #else
         if (step2_flag[j])
         #endif
         {
            int hy = finalY[j] * g->floor1_multiplier;
            int hx = g->Xlist[j];
            if (lx != hx)
               draw_line(target, lx,ly, hx,hy, n2);
            CHECK(f);
            lx = hx, ly = hy;
         }
      }
      if (lx < n2) {
         for (j=lx; j < n2; ++j)
            LINE_OP(target[j], inverse_db_table[ly]);
         CHECK(f);
      }
   }
   return TRUE;
}
static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode)
{
   Mode *m;
   int i, n, prev, next, window_center;
   f->channel_buffer_start = f->channel_buffer_end = 0;
  retry:
   if (f->eof) return FALSE;
   if (!maybe_start_packet(f))
      return FALSE;
   if (get_bits(f,1) != 0) {
      if (IS_PUSH_MODE(f))
         return error(f,VORBIS_bad_packet_type);
      while (EOP != get8_packet(f));
      goto retry;
   }
   if (f->alloc.alloc_buffer)
      assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset);
   i = get_bits(f, ilog(f->mode_count-1));
   if (i == EOP) return FALSE;
   if (i >= f->mode_count) return FALSE;
   *mode = i;
   m = f->mode_config + i;
   if (m->blockflag) {
      n = f->blocksize_1;
      prev = get_bits(f,1);
      next = get_bits(f,1);
   } else {
      prev = next = 0;
      n = f->blocksize_0;
   }
   window_center = n >> 1;
   if (m->blockflag && !prev) {
      *p_left_start = (n - f->blocksize_0) >> 2;
      *p_left_end   = (n + f->blocksize_0) >> 2;
   } else {
      *p_left_start = 0;
      *p_left_end   = window_center;
   }
   if (m->blockflag && !next) {
      *p_right_start = (n*3 - f->blocksize_0) >> 2;
      *p_right_end   = (n*3 + f->blocksize_0) >> 2;
   } else {
      *p_right_start = window_center;
      *p_right_end   = n;
   }
   return TRUE;
}
static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left)
{
   Mapping *map;
   int i,j,k,n,n2;
   int zero_channel[256];
   int really_zero_channel[256];
   STBV_NOTUSED(left_end);
   n = f->blocksize[m->blockflag];
   map = &f->mapping[m->mapping];
   n2 = n >> 1;
   CHECK(f);
   for (i=0; i < f->channels; ++i) {
      int s = map->chan[i].mux, floor;
      zero_channel[i] = FALSE;
      floor = map->submap_floor[s];
      if (f->floor_types[floor] == 0) {
         return error(f, VORBIS_invalid_stream);
      } else {
         Floor1 *g = &f->floor_config[floor].floor1;
         if (get_bits(f, 1)) {
            short *finalY;
            uint8 step2_flag[256];
            static int range_list[4] = { 256, 128, 86, 64 };
            int range = range_list[g->floor1_multiplier-1];
            int offset = 2;
            finalY = f->finalY[i];
            finalY[0] = get_bits(f, ilog(range)-1);
            finalY[1] = get_bits(f, ilog(range)-1);
            for (j=0; j < g->partitions; ++j) {
               int pclass = g->partition_class_list[j];
               int cdim = g->class_dimensions[pclass];
               int cbits = g->class_subclasses[pclass];
               int csub = (1 << cbits)-1;
               int cval = 0;
               if (cbits) {
                  Codebook *c = f->codebooks + g->class_masterbooks[pclass];
                  DECODE(cval,f,c);
               }
               for (k=0; k < cdim; ++k) {
                  int book = g->subclass_books[pclass][cval & csub];
                  cval = cval >> cbits;
                  if (book >= 0) {
                     int temp;
                     Codebook *c = f->codebooks + book;
                     DECODE(temp,f,c);
                     finalY[offset++] = temp;
                  } else
                     finalY[offset++] = 0;
               }
            }
            if (f->valid_bits == INVALID_BITS) goto error;
            step2_flag[0] = step2_flag[1] = 1;
            for (j=2; j < g->values; ++j) {
               int low, high, pred, highroom, lowroom, room, val;
               low = g->neighbors[j][0];
               high = g->neighbors[j][1];
               pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]);
               val = finalY[j];
               highroom = range - pred;
               lowroom = pred;
               if (highroom < lowroom)
                  room = highroom * 2;
               else
                  room = lowroom * 2;
               if (val) {
                  step2_flag[low] = step2_flag[high] = 1;
                  step2_flag[j] = 1;
                  if (val >= room)
                     if (highroom > lowroom)
                        finalY[j] = val - lowroom + pred;
                     else
                        finalY[j] = pred - val + highroom - 1;
                  else
                     if (val & 1)
                        finalY[j] = pred - ((val+1)>>1);
                     else
                        finalY[j] = pred + (val>>1);
               } else {
                  step2_flag[j] = 0;
                  finalY[j] = pred;
               }
            }
#ifdef STB_VORBIS_NO_DEFER_FLOOR
            do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag);
#else
            for (j=0; j < g->values; ++j) {
               if (!step2_flag[j])
                  finalY[j] = -1;
            }
#endif
         } else {
           error:
            zero_channel[i] = TRUE;
         }
      }
   }
   CHECK(f);
   if (f->alloc.alloc_buffer)
      assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset);
   memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels);
   for (i=0; i < map->coupling_steps; ++i)
      if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) {
         zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE;
      }
   CHECK(f);
   for (i=0; i < map->submaps; ++i) {
      float *residue_buffers[STB_VORBIS_MAX_CHANNELS];
      int r;
      uint8 do_not_decode[256];
      int ch = 0;
      for (j=0; j < f->channels; ++j) {
         if (map->chan[j].mux == i) {
            if (zero_channel[j]) {
               do_not_decode[ch] = TRUE;
               residue_buffers[ch] = NULL;
            } else {
               do_not_decode[ch] = FALSE;
               residue_buffers[ch] = f->channel_buffers[j];
            }
            ++ch;
         }
      }
      r = map->submap_residue[i];
      decode_residue(f, residue_buffers, ch, n2, r, do_not_decode);
   }
   if (f->alloc.alloc_buffer)
      assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset);
   CHECK(f);
   for (i = map->coupling_steps-1; i >= 0; --i) {
      int n2 = n >> 1;
      float *m = f->channel_buffers[map->chan[i].magnitude];
      float *a = f->channel_buffers[map->chan[i].angle    ];
      for (j=0; j < n2; ++j) {
         float a2,m2;
         if (m[j] > 0)
            if (a[j] > 0)
               m2 = m[j], a2 = m[j] - a[j];
            else
               a2 = m[j], m2 = m[j] + a[j];
         else
            if (a[j] > 0)
               m2 = m[j], a2 = m[j] + a[j];
            else
               a2 = m[j], m2 = m[j] - a[j];
         m[j] = m2;
         a[j] = a2;
      }
   }
   CHECK(f);
#ifndef STB_VORBIS_NO_DEFER_FLOOR
   for (i=0; i < f->channels; ++i) {
      if (really_zero_channel[i]) {
         memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2);
      } else {
         do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL);
      }
   }
#else
   for (i=0; i < f->channels; ++i) {
      if (really_zero_channel[i]) {
         memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2);
      } else {
         for (j=0; j < n2; ++j)
            f->channel_buffers[i][j] *= f->floor_buffers[i][j];
      }
   }
#endif
   CHECK(f);
   for (i=0; i < f->channels; ++i)
      inverse_mdct(f->channel_buffers[i], n, f, m->blockflag);
   CHECK(f);
   flush_packet(f);
   if (f->first_decode) {
      f->current_loc = 0u - n2;
      f->discard_samples_deferred = n - right_end;
      f->current_loc_valid = TRUE;
      f->first_decode = FALSE;
   } else if (f->discard_samples_deferred) {
      if (f->discard_samples_deferred >= right_start - left_start) {
         f->discard_samples_deferred -= (right_start - left_start);
         left_start = right_start;
         *p_left = left_start;
      } else {
         left_start += f->discard_samples_deferred;
         *p_left = left_start;
         f->discard_samples_deferred = 0;
      }
   } else if (f->previous_length == 0 && f->current_loc_valid) {
   }
   if (f->last_seg_which == f->end_seg_with_known_loc) {
      if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) {
         uint32 current_end = f->known_loc_for_packet;
         if (current_end < f->current_loc + (right_end-left_start)) {
            if (current_end < f->current_loc) {
               *len = 0;
            } else {
               *len = current_end - f->current_loc;
            }
            *len += left_start;
            if (*len > right_end) *len = right_end;
            f->current_loc += *len;
            return TRUE;
         }
      }
      f->current_loc = f->known_loc_for_packet - (n2-left_start);
      f->current_loc_valid = TRUE;
   }
   if (f->current_loc_valid)
      f->current_loc += (right_start - left_start);
   if (f->alloc.alloc_buffer)
      assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset);
   *len = right_end;
   CHECK(f);
   return TRUE;
}
static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right)
{
   int mode, left_end, right_end;
   if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0;
   return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left);
}
static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right)
{
   int prev,i,j;
   if (f->previous_length) {
      int i,j, n = f->previous_length;
      float *w = get_window(f, n);
      if (w == NULL) return 0;
      for (i=0; i < f->channels; ++i) {
         for (j=0; j < n; ++j)
            f->channel_buffers[i][left+j] =
               f->channel_buffers[i][left+j]*w[    j] +
               f->previous_window[i][     j]*w[n-1-j];
      }
   }
   prev = f->previous_length;
   f->previous_length = len - right;
   for (i=0; i < f->channels; ++i)
      for (j=0; right+j < len; ++j)
         f->previous_window[i][j] = f->channel_buffers[i][right+j];
   if (!prev)
      return 0;
   if (len < right) right = len;
   f->samples_output += right-left;
   return right - left;
}
static int vorbis_pump_first_frame(stb_vorbis *f)
{
   int len, right, left, res;
   res = vorbis_decode_packet(f, &len, &left, &right);
   if (res)
      vorbis_finish_frame(f, len, left, right);
   return res;
}
static int start_decoder(vorb *f)
{
   uint8 header[6], x,y;
   int len,i,j,k, max_submaps = 0;
   int longest_floorlist=0;
   f->first_decode = TRUE;
   if (!start_page(f))                              return FALSE;
   if (!(f->page_flag & PAGEFLAG_first_page))       return error(f, VORBIS_invalid_first_page);
   if (f->page_flag & PAGEFLAG_last_page)           return error(f, VORBIS_invalid_first_page);
   if (f->page_flag & PAGEFLAG_continued_packet)    return error(f, VORBIS_invalid_first_page);
   if (f->segment_count != 1)                       return error(f, VORBIS_invalid_first_page);
   if (f->segments[0] != 30) {
      if (f->segments[0] == 64 &&
          getn(f, header, 6) &&
          header[0] == 'f' &&
          header[1] == 'i' &&
          header[2] == 's' &&
          header[3] == 'h' &&
          header[4] == 'e' &&
          header[5] == 'a' &&
          get8(f)   == 'd' &&
          get8(f)   == '\0')                        return error(f, VORBIS_ogg_skeleton_not_supported);
      else
                                                    return error(f, VORBIS_invalid_first_page);
   }
   if (get8(f) != VORBIS_packet_id)                 return error(f, VORBIS_invalid_first_page);
   if (!getn(f, header, 6))                         return error(f, VORBIS_unexpected_eof);
   if (!vorbis_validate(header))                    return error(f, VORBIS_invalid_first_page);
   if (get32(f) != 0)                               return error(f, VORBIS_invalid_first_page);
   f->channels = get8(f); if (!f->channels)         return error(f, VORBIS_invalid_first_page);
   if (f->channels > STB_VORBIS_MAX_CHANNELS)       return error(f, VORBIS_too_many_channels);
   f->sample_rate = get32(f); if (!f->sample_rate)  return error(f, VORBIS_invalid_first_page);
   get32(f);
   get32(f);
   get32(f);
   x = get8(f);
   {
      int log0,log1;
      log0 = x & 15;
      log1 = x >> 4;
      f->blocksize_0 = 1 << log0;
      f->blocksize_1 = 1 << log1;
      if (log0 < 6 || log0 > 13)                       return error(f, VORBIS_invalid_setup);
      if (log1 < 6 || log1 > 13)                       return error(f, VORBIS_invalid_setup);
      if (log0 > log1)                                 return error(f, VORBIS_invalid_setup);
   }
   x = get8(f);
   if (!(x & 1))                                    return error(f, VORBIS_invalid_first_page);
   if (!start_page(f))                              return FALSE;
   if (!start_packet(f))                            return FALSE;
   if (!next_segment(f))                            return FALSE;
   if (get8_packet(f) != VORBIS_packet_comment)            return error(f, VORBIS_invalid_setup);
   for (i=0; i < 6; ++i) header[i] = get8_packet(f);
   if (!vorbis_validate(header))                    return error(f, VORBIS_invalid_setup);
   len = get32_packet(f);
   f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1));
   if (f->vendor == NULL)                           return error(f, VORBIS_outofmem);
   for(i=0; i < len; ++i) {
      f->vendor[i] = get8_packet(f);
   }
   f->vendor[len] = (char)'\0';
   f->comment_list_length = get32_packet(f);
   f->comment_list = NULL;
   if (f->comment_list_length > 0)
   {
      f->comment_list = (char**) setup_malloc(f, sizeof(char*) * (f->comment_list_length));
      if (f->comment_list == NULL)                  return error(f, VORBIS_outofmem);
   }
   for(i=0; i < f->comment_list_length; ++i) {
      len = get32_packet(f);
      f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1));
      if (f->comment_list[i] == NULL)               return error(f, VORBIS_outofmem);
      for(j=0; j < len; ++j) {
         f->comment_list[i][j] = get8_packet(f);
      }
      f->comment_list[i][len] = (char)'\0';
   }
   x = get8_packet(f);
   if (!(x & 1))                                    return error(f, VORBIS_invalid_setup);
   skip(f, f->bytes_in_seg);
   f->bytes_in_seg = 0;
   do {
      len = next_segment(f);
      skip(f, len);
      f->bytes_in_seg = 0;
   } while (len);
   if (!start_packet(f))                            return FALSE;
   crc32_init();
   if (get8_packet(f) != VORBIS_packet_setup)       return error(f, VORBIS_invalid_setup);
   for (i=0; i < 6; ++i) header[i] = get8_packet(f);
   if (!vorbis_validate(header))                    return error(f, VORBIS_invalid_setup);
   f->codebook_count = get_bits(f,8) + 1;
   f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count);
   if (f->codebooks == NULL)                        return error(f, VORBIS_outofmem);
   memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count);
   for (i=0; i < f->codebook_count; ++i) {
      uint32 *values;
      int ordered, sorted_count;
      int total=0;
      uint8 *lengths;
      Codebook *c = f->codebooks+i;
      CHECK(f);
      x = get_bits(f, 8); if (x != 0x42)            return error(f, VORBIS_invalid_setup);
      x = get_bits(f, 8); if (x != 0x43)            return error(f, VORBIS_invalid_setup);
      x = get_bits(f, 8); if (x != 0x56)            return error(f, VORBIS_invalid_setup);
      x = get_bits(f, 8);
      c->dimensions = (get_bits(f, 8)<<8) + x;
      x = get_bits(f, 8);
      y = get_bits(f, 8);
      c->entries = (get_bits(f, 8)<<16) + (y<<8) + x;
      ordered = get_bits(f,1);
      c->sparse = ordered ? 0 : get_bits(f,1);
      if (c->dimensions == 0 && c->entries != 0)    return error(f, VORBIS_invalid_setup);
      if (c->sparse)
         lengths = (uint8 *) setup_temp_malloc(f, c->entries);
      else
         lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries);
      if (!lengths) return error(f, VORBIS_outofmem);
      if (ordered) {
         int current_entry = 0;
         int current_length = get_bits(f,5) + 1;
         while (current_entry < c->entries) {
            int limit = c->entries - current_entry;
            int n = get_bits(f, ilog(limit));
            if (current_length >= 32) return error(f, VORBIS_invalid_setup);
            if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); }
            memset(lengths + current_entry, current_length, n);
            current_entry += n;
            ++current_length;
         }
      } else {
         for (j=0; j < c->entries; ++j) {
            int present = c->sparse ? get_bits(f,1) : 1;
            if (present) {
               lengths[j] = get_bits(f, 5) + 1;
               ++total;
               if (lengths[j] == 32)
                  return error(f, VORBIS_invalid_setup);
            } else {
               lengths[j] = NO_CODE;
            }
         }
      }
      if (c->sparse && total >= c->entries >> 2) {
         if (c->entries > (int) f->setup_temp_memory_required)
            f->setup_temp_memory_required = c->entries;
         c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries);
         if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem);
         memcpy(c->codeword_lengths, lengths, c->entries);
         setup_temp_free(f, lengths, c->entries);
         lengths = c->codeword_lengths;
         c->sparse = 0;
      }
      if (c->sparse) {
         sorted_count = total;
      } else {
         sorted_count = 0;
         #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH
         for (j=0; j < c->entries; ++j)
            if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE)
               ++sorted_count;
         #endif
      }
      c->sorted_entries = sorted_count;
      values = NULL;
      CHECK(f);
      if (!c->sparse) {
         c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries);
         if (!c->codewords)                  return error(f, VORBIS_outofmem);
      } else {
         unsigned int size;
         if (c->sorted_entries) {
            c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries);
            if (!c->codeword_lengths)           return error(f, VORBIS_outofmem);
            c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries);
            if (!c->codewords)                  return error(f, VORBIS_outofmem);
            values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries);
            if (!values)                        return error(f, VORBIS_outofmem);
         }
         size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries;
         if (size > f->setup_temp_memory_required)
            f->setup_temp_memory_required = size;
      }
      if (!compute_codewords(c, lengths, c->entries, values)) {
         if (c->sparse) setup_temp_free(f, values, 0);
         return error(f, VORBIS_invalid_setup);
      }
      if (c->sorted_entries) {
         c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1));
         if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem);
         c->sorted_values    = ( int   *) setup_malloc(f, sizeof(*c->sorted_values   ) * (c->sorted_entries+1));
         if (c->sorted_values == NULL) return error(f, VORBIS_outofmem);
         ++c->sorted_values;
         c->sorted_values[-1] = -1;
         compute_sorted_huffman(c, lengths, values);
      }
      if (c->sparse) {
         setup_temp_free(f, values, sizeof(*values)*c->sorted_entries);
         setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries);
         setup_temp_free(f, lengths, c->entries);
         c->codewords = NULL;
      }
      compute_accelerated_huffman(c);
      CHECK(f);
      c->lookup_type = get_bits(f, 4);
      if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup);
      if (c->lookup_type > 0) {
         uint16 *mults;
         c->minimum_value = float32_unpack(get_bits(f, 32));
         c->delta_value = float32_unpack(get_bits(f, 32));
         c->value_bits = get_bits(f, 4)+1;
         c->sequence_p = get_bits(f,1);
         if (c->lookup_type == 1) {
            int values = lookup1_values(c->entries, c->dimensions);
            if (values < 0) return error(f, VORBIS_invalid_setup);
            c->lookup_values = (uint32) values;
         } else {
            c->lookup_values = c->entries * c->dimensions;
         }
         if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup);
         mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values);
         if (mults == NULL) return error(f, VORBIS_outofmem);
         for (j=0; j < (int) c->lookup_values; ++j) {
            int q = get_bits(f, c->value_bits);
            if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); }
            mults[j] = q;
         }
#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
         if (c->lookup_type == 1) {
            int len, sparse = c->sparse;
            float last=0;
            if (sparse) {
               if (c->sorted_entries == 0) goto skip;
               c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions);
            } else
               c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries        * c->dimensions);
            if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); }
            len = sparse ? c->sorted_entries : c->entries;
            for (j=0; j < len; ++j) {
               unsigned int z = sparse ? c->sorted_values[j] : j;
               unsigned int div=1;
               for (k=0; k < c->dimensions; ++k) {
                  int off = (z / div) % c->lookup_values;
                  float val = mults[off]*c->delta_value + c->minimum_value + last;
                  c->multiplicands[j*c->dimensions + k] = val;
                  if (c->sequence_p)
                     last = val;
                  if (k+1 < c->dimensions) {
                     if (div > UINT_MAX / (unsigned int) c->lookup_values) {
                        setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values);
                        return error(f, VORBIS_invalid_setup);
                     }
                     div *= c->lookup_values;
                  }
               }
            }
            c->lookup_type = 2;
         }
         else
#endif
         {
            float last=0;
            CHECK(f);
            c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values);
            if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); }
            for (j=0; j < (int) c->lookup_values; ++j) {
               float val = mults[j] * c->delta_value + c->minimum_value + last;
               c->multiplicands[j] = val;
               if (c->sequence_p)
                  last = val;
            }
         }
#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
        skip:;
#endif
         setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values);
         CHECK(f);
      }
      CHECK(f);
   }
   x = get_bits(f, 6) + 1;
   for (i=0; i < x; ++i) {
      uint32 z = get_bits(f, 16);
      if (z != 0) return error(f, VORBIS_invalid_setup);
   }
   f->floor_count = get_bits(f, 6)+1;
   f->floor_config = (Floor *)  setup_malloc(f, f->floor_count * sizeof(*f->floor_config));
   if (f->floor_config == NULL) return error(f, VORBIS_outofmem);
   for (i=0; i < f->floor_count; ++i) {
      f->floor_types[i] = get_bits(f, 16);
      if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup);
      if (f->floor_types[i] == 0) {
         Floor0 *g = &f->floor_config[i].floor0;
         g->order = get_bits(f,8);
         g->rate = get_bits(f,16);
         g->bark_map_size = get_bits(f,16);
         g->amplitude_bits = get_bits(f,6);
         g->amplitude_offset = get_bits(f,8);
         g->number_of_books = get_bits(f,4) + 1;
         for (j=0; j < g->number_of_books; ++j)
            g->book_list[j] = get_bits(f,8);
         return error(f, VORBIS_feature_not_supported);
      } else {
         stbv__floor_ordering p[31*8+2];
         Floor1 *g = &f->floor_config[i].floor1;
         int max_class = -1;
         g->partitions = get_bits(f, 5);
         for (j=0; j < g->partitions; ++j) {
            g->partition_class_list[j] = get_bits(f, 4);
            if (g->partition_class_list[j] > max_class)
               max_class = g->partition_class_list[j];
         }
         for (j=0; j <= max_class; ++j) {
            g->class_dimensions[j] = get_bits(f, 3)+1;
            g->class_subclasses[j] = get_bits(f, 2);
            if (g->class_subclasses[j]) {
               g->class_masterbooks[j] = get_bits(f, 8);
               if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
            }
            for (k=0; k < 1 << g->class_subclasses[j]; ++k) {
               g->subclass_books[j][k] = (int16)get_bits(f,8)-1;
               if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
            }
         }
         g->floor1_multiplier = get_bits(f,2)+1;
         g->rangebits = get_bits(f,4);
         g->Xlist[0] = 0;
         g->Xlist[1] = 1 << g->rangebits;
         g->values = 2;
         for (j=0; j < g->partitions; ++j) {
            int c = g->partition_class_list[j];
            for (k=0; k < g->class_dimensions[c]; ++k) {
               g->Xlist[g->values] = get_bits(f, g->rangebits);
               ++g->values;
            }
         }
         for (j=0; j < g->values; ++j) {
            p[j].x = g->Xlist[j];
            p[j].id = j;
         }
         qsort(p, g->values, sizeof(p[0]), point_compare);
         for (j=0; j < g->values-1; ++j)
            if (p[j].x == p[j+1].x)
               return error(f, VORBIS_invalid_setup);
         for (j=0; j < g->values; ++j)
            g->sorted_order[j] = (uint8) p[j].id;
         for (j=2; j < g->values; ++j) {
            int low = 0,hi = 0;
            neighbors(g->Xlist, j, &low,&hi);
            g->neighbors[j][0] = low;
            g->neighbors[j][1] = hi;
         }
         if (g->values > longest_floorlist)
            longest_floorlist = g->values;
      }
   }
   f->residue_count = get_bits(f, 6)+1;
   f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0]));
   if (f->residue_config == NULL) return error(f, VORBIS_outofmem);
   memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0]));
   for (i=0; i < f->residue_count; ++i) {
      uint8 residue_cascade[64];
      Residue *r = f->residue_config+i;
      f->residue_types[i] = get_bits(f, 16);
      if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup);
      r->begin = get_bits(f, 24);
      r->end = get_bits(f, 24);
      if (r->end < r->begin) return error(f, VORBIS_invalid_setup);
      r->part_size = get_bits(f,24)+1;
      r->classifications = get_bits(f,6)+1;
      r->classbook = get_bits(f,8);
      if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup);
      for (j=0; j < r->classifications; ++j) {
         uint8 high_bits=0;
         uint8 low_bits=get_bits(f,3);
         if (get_bits(f,1))
            high_bits = get_bits(f,5);
         residue_cascade[j] = high_bits*8 + low_bits;
      }
      r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications);
      if (r->residue_books == NULL) return error(f, VORBIS_outofmem);
      for (j=0; j < r->classifications; ++j) {
         for (k=0; k < 8; ++k) {
            if (residue_cascade[j] & (1 << k)) {
               r->residue_books[j][k] = get_bits(f, 8);
               if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
            } else {
               r->residue_books[j][k] = -1;
            }
         }
      }
      r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries);
      if (!r->classdata) return error(f, VORBIS_outofmem);
      memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries);
      for (j=0; j < f->codebooks[r->classbook].entries; ++j) {
         int classwords = f->codebooks[r->classbook].dimensions;
         int temp = j;
         r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords);
         if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem);
         for (k=classwords-1; k >= 0; --k) {
            r->classdata[j][k] = temp % r->classifications;
            temp /= r->classifications;
         }
      }
   }
   f->mapping_count = get_bits(f,6)+1;
   f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping));
   if (f->mapping == NULL) return error(f, VORBIS_outofmem);
   memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping));
   for (i=0; i < f->mapping_count; ++i) {
      Mapping *m = f->mapping + i;
      int mapping_type = get_bits(f,16);
      if (mapping_type != 0) return error(f, VORBIS_invalid_setup);
      m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan));
      if (m->chan == NULL) return error(f, VORBIS_outofmem);
      if (get_bits(f,1))
         m->submaps = get_bits(f,4)+1;
      else
         m->submaps = 1;
      if (m->submaps > max_submaps)
         max_submaps = m->submaps;
      if (get_bits(f,1)) {
         m->coupling_steps = get_bits(f,8)+1;
         if (m->coupling_steps > f->channels) return error(f, VORBIS_invalid_setup);
         for (k=0; k < m->coupling_steps; ++k) {
            m->chan[k].magnitude = get_bits(f, ilog(f->channels-1));
            m->chan[k].angle = get_bits(f, ilog(f->channels-1));
            if (m->chan[k].magnitude >= f->channels)        return error(f, VORBIS_invalid_setup);
            if (m->chan[k].angle     >= f->channels)        return error(f, VORBIS_invalid_setup);
            if (m->chan[k].magnitude == m->chan[k].angle)   return error(f, VORBIS_invalid_setup);
         }
      } else
         m->coupling_steps = 0;
      if (get_bits(f,2)) return error(f, VORBIS_invalid_setup);
      if (m->submaps > 1) {
         for (j=0; j < f->channels; ++j) {
            m->chan[j].mux = get_bits(f, 4);
            if (m->chan[j].mux >= m->submaps)                return error(f, VORBIS_invalid_setup);
         }
      } else
         for (j=0; j < f->channels; ++j)
            m->chan[j].mux = 0;
      for (j=0; j < m->submaps; ++j) {
         get_bits(f,8);
         m->submap_floor[j] = get_bits(f,8);
         m->submap_residue[j] = get_bits(f,8);
         if (m->submap_floor[j] >= f->floor_count)      return error(f, VORBIS_invalid_setup);
         if (m->submap_residue[j] >= f->residue_count)  return error(f, VORBIS_invalid_setup);
      }
   }
   f->mode_count = get_bits(f, 6)+1;
   for (i=0; i < f->mode_count; ++i) {
      Mode *m = f->mode_config+i;
      m->blockflag = get_bits(f,1);
      m->windowtype = get_bits(f,16);
      m->transformtype = get_bits(f,16);
      m->mapping = get_bits(f,8);
      if (m->windowtype != 0)                 return error(f, VORBIS_invalid_setup);
      if (m->transformtype != 0)              return error(f, VORBIS_invalid_setup);
      if (m->mapping >= f->mapping_count)     return error(f, VORBIS_invalid_setup);
   }
   flush_packet(f);
   f->previous_length = 0;
   for (i=0; i < f->channels; ++i) {
      f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1);
      f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2);
      f->finalY[i]          = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist);
      if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem);
      memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1);
      #ifdef STB_VORBIS_NO_DEFER_FLOOR
      f->floor_buffers[i]   = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2);
      if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem);
      #endif
   }
   if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE;
   if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE;
   f->blocksize[0] = f->blocksize_0;
   f->blocksize[1] = f->blocksize_1;
#ifdef STB_VORBIS_DIVIDE_TABLE
   if (integer_divide_table[1][1]==0)
      for (i=0; i < DIVTAB_NUMER; ++i)
         for (j=1; j < DIVTAB_DENOM; ++j)
            integer_divide_table[i][j] = i / j;
#endif
   {
      uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1);
      uint32 classify_mem;
      int i,max_part_read=0;
      for (i=0; i < f->residue_count; ++i) {
         Residue *r = f->residue_config + i;
         unsigned int actual_size = f->blocksize_1 / 2;
         unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size;
         unsigned int limit_r_end   = r->end   < actual_size ? r->end   : actual_size;
         int n_read = limit_r_end - limit_r_begin;
         int part_read = n_read / r->part_size;
         if (part_read > max_part_read)
            max_part_read = part_read;
      }
      #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
      classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *));
      #else
      classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *));
      #endif
      f->temp_memory_required = classify_mem;
      if (imdct_mem > f->temp_memory_required)
         f->temp_memory_required = imdct_mem;
   }
   if (f->alloc.alloc_buffer) {
      assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes);
      if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset)
         return error(f, VORBIS_outofmem);
   }
   if (f->next_seg == -1) {
      f->first_audio_page_offset = stb_vorbis_get_file_offset(f);
   } else {
      f->first_audio_page_offset = 0;
   }
   return TRUE;
}
static void vorbis_deinit(stb_vorbis *p)
{
   int i,j;
   setup_free(p, p->vendor);
   for (i=0; i < p->comment_list_length; ++i) {
      setup_free(p, p->comment_list[i]);
   }
   setup_free(p, p->comment_list);
   if (p->residue_config) {
      for (i=0; i < p->residue_count; ++i) {
         Residue *r = p->residue_config+i;
         if (r->classdata) {
            for (j=0; j < p->codebooks[r->classbook].entries; ++j)
               setup_free(p, r->classdata[j]);
            setup_free(p, r->classdata);
         }
         setup_free(p, r->residue_books);
      }
   }
   if (p->codebooks) {
      CHECK(p);
      for (i=0; i < p->codebook_count; ++i) {
         Codebook *c = p->codebooks + i;
         setup_free(p, c->codeword_lengths);
         setup_free(p, c->multiplicands);
         setup_free(p, c->codewords);
         setup_free(p, c->sorted_codewords);
         setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL);
      }
      setup_free(p, p->codebooks);
   }
   setup_free(p, p->floor_config);
   setup_free(p, p->residue_config);
   if (p->mapping) {
      for (i=0; i < p->mapping_count; ++i)
         setup_free(p, p->mapping[i].chan);
      setup_free(p, p->mapping);
   }
   CHECK(p);
   for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) {
      setup_free(p, p->channel_buffers[i]);
      setup_free(p, p->previous_window[i]);
      #ifdef STB_VORBIS_NO_DEFER_FLOOR
      setup_free(p, p->floor_buffers[i]);
      #endif
      setup_free(p, p->finalY[i]);
   }
   for (i=0; i < 2; ++i) {
      setup_free(p, p->A[i]);
      setup_free(p, p->B[i]);
      setup_free(p, p->C[i]);
      setup_free(p, p->window[i]);
      setup_free(p, p->bit_reverse[i]);
   }
}
void stb_vorbis_close(stb_vorbis *p)
{
   if (p == NULL) return;
   vorbis_deinit(p);
   setup_free(p,p);
}
static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z)
{
   memset(p, 0, sizeof(*p));
   if (z) {
      p->alloc = *z;
      p->alloc.alloc_buffer_length_in_bytes &= ~7;
      p->temp_offset = p->alloc.alloc_buffer_length_in_bytes;
   }
   p->eof = 0;
   p->error = VORBIS__no_error;
   p->stream = NULL;
   p->codebooks = NULL;
   p->page_crc_tests = -1;
}
int stb_vorbis_get_sample_offset(stb_vorbis *f)
{
   if (f->current_loc_valid)
      return f->current_loc;
   else
      return -1;
}
stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f)
{
   stb_vorbis_info d;
   d.channels = f->channels;
   d.sample_rate = f->sample_rate;
   d.setup_memory_required = f->setup_memory_required;
   d.setup_temp_memory_required = f->setup_temp_memory_required;
   d.temp_memory_required = f->temp_memory_required;
   d.max_frame_size = f->blocksize_1 >> 1;
   return d;
}
stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f)
{
   stb_vorbis_comment d;
   d.vendor = f->vendor;
   d.comment_list_length = f->comment_list_length;
   d.comment_list = f->comment_list;
   return d;
}
int stb_vorbis_get_error(stb_vorbis *f)
{
   int e = f->error;
   f->error = VORBIS__no_error;
   return e;
}
static stb_vorbis * vorbis_alloc(stb_vorbis *f)
{
   stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p));
   return p;
}
unsigned int stb_vorbis_get_file_offset(stb_vorbis *f)
{
   if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start);
}
#ifndef STB_VORBIS_NO_PULLDATA_API
static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last)
{
   for(;;) {
      int n;
      if (f->eof) return 0;
      n = get8(f);
      if (n == 0x4f) {
         unsigned int retry_loc = stb_vorbis_get_file_offset(f);
         int i;
         if (retry_loc - 25 > f->stream_len)
            return 0;
         for (i=1; i < 4; ++i)
            if (get8(f) != ogg_page_header[i])
               break;
         if (f->eof) return 0;
         if (i == 4) {
            uint8 header[27];
            uint32 i, crc, goal, len;
            for (i=0; i < 4; ++i)
               header[i] = ogg_page_header[i];
            for (; i < 27; ++i)
               header[i] = get8(f);
            if (f->eof) return 0;
            if (header[4] != 0) goto invalid;
            goal = header[22] + (header[23] << 8) + (header[24]<<16) + ((uint32)header[25]<<24);
            for (i=22; i < 26; ++i)
               header[i] = 0;
            crc = 0;
            for (i=0; i < 27; ++i)
               crc = crc32_update(crc, header[i]);
            len = 0;
            for (i=0; i < header[26]; ++i) {
               int s = get8(f);
               crc = crc32_update(crc, s);
               len += s;
            }
            if (len && f->eof) return 0;
            for (i=0; i < len; ++i)
               crc = crc32_update(crc, get8(f));
            if (crc == goal) {
               if (end)
                  *end = stb_vorbis_get_file_offset(f);
               if (last) {
                  if (header[5] & 0x04)
                     *last = 1;
                  else
                     *last = 0;
               }
               set_file_offset(f, retry_loc-1);
               return 1;
            }
         }
        invalid:
         set_file_offset(f, retry_loc);
      }
   }
}
#define SAMPLE_unknown  0xffffffff
static int get_seek_page_info(stb_vorbis *f, ProbedPage *z)
{
   uint8 header[27] = { 0 }, lacing[255];
   int i,len;
   z->page_start = stb_vorbis_get_file_offset(f);
   getn(f, header, 27);
   if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S')
      return 0;
   getn(f, lacing, header[26]);
   len = 0;
   for (i=0; i < header[26]; ++i)
      len += lacing[i];
   z->page_end = z->page_start + 27 + header[26] + len;
   z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24);
   set_file_offset(f, z->page_start);
   return 1;
}
static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset)
{
   unsigned int previous_safe, end;
   if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset)
      previous_safe = limit_offset - 65536;
   else
      previous_safe = f->first_audio_page_offset;
   set_file_offset(f, previous_safe);
   while (vorbis_find_page(f, &end, NULL)) {
      if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset)
         return 1;
      set_file_offset(f, end);
   }
   return 0;
}
static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number)
{
   ProbedPage left, right, mid;
   int i, start_seg_with_known_loc, end_pos, page_start;
   uint32 delta, stream_length, padding, last_sample_limit;
   double offset = 0.0, bytes_per_sample = 0.0;
   int probe = 0;
   stream_length = stb_vorbis_stream_length_in_samples(f);
   if (stream_length == 0)            return error(f, VORBIS_seek_without_length);
   if (sample_number > stream_length) return error(f, VORBIS_seek_invalid);
   padding = ((f->blocksize_1 - f->blocksize_0) >> 2);
   if (sample_number < padding)
      last_sample_limit = 0;
   else
      last_sample_limit = sample_number - padding;
   left = f->p_first;
   while (left.last_decoded_sample == ~0U) {
      set_file_offset(f, left.page_end);
      if (!get_seek_page_info(f, &left)) goto error;
   }
   right = f->p_last;
   assert(right.last_decoded_sample != ~0U);
   if (last_sample_limit <= left.last_decoded_sample) {
      if (stb_vorbis_seek_start(f)) {
         if (f->current_loc > sample_number)
            return error(f, VORBIS_seek_failed);
         return 1;
      }
      return 0;
   }
   while (left.page_end != right.page_start) {
      assert(left.page_end < right.page_start);
      delta = right.page_start - left.page_end;
      if (delta <= 65536) {
         set_file_offset(f, left.page_end);
      } else {
         if (probe < 2) {
            if (probe == 0) {
               double data_bytes = right.page_end - left.page_start;
               bytes_per_sample = data_bytes / right.last_decoded_sample;
               offset = left.page_start + bytes_per_sample * (last_sample_limit - left.last_decoded_sample);
            } else {
               double error = ((double) last_sample_limit - mid.last_decoded_sample) * bytes_per_sample;
               if (error >= 0 && error <  8000) error =  8000;
               if (error <  0 && error > -8000) error = -8000;
               offset += error * 2;
            }
            if (offset < left.page_end)
               offset = left.page_end;
            if (offset > right.page_start - 65536)
               offset = right.page_start - 65536;
            set_file_offset(f, (unsigned int) offset);
         } else {
            set_file_offset(f, left.page_end + (delta / 2) - 32768);
         }
         if (!vorbis_find_page(f, NULL, NULL)) goto error;
      }
      for (;;) {
         if (!get_seek_page_info(f, &mid)) goto error;
         if (mid.last_decoded_sample != ~0U) break;
         set_file_offset(f, mid.page_end);
         assert(mid.page_start < right.page_start);
      }
      if (mid.page_start == right.page_start) {
         if (probe >= 2 || delta <= 65536)
            break;
      } else {
         if (last_sample_limit < mid.last_decoded_sample)
            right = mid;
         else
            left = mid;
      }
      ++probe;
   }
   page_start = left.page_start;
   set_file_offset(f, page_start);
   if (!start_page(f)) return error(f, VORBIS_seek_failed);
   end_pos = f->end_seg_with_known_loc;
   assert(end_pos >= 0);
   for (;;) {
      for (i = end_pos; i > 0; --i)
         if (f->segments[i-1] != 255)
            break;
      start_seg_with_known_loc = i;
      if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet))
         break;
      if (!go_to_page_before(f, page_start))
         goto error;
      page_start = stb_vorbis_get_file_offset(f);
      if (!start_page(f)) goto error;
      end_pos = f->segment_count - 1;
   }
   f->current_loc_valid = FALSE;
   f->last_seg = FALSE;
   f->valid_bits = 0;
   f->packet_bytes = 0;
   f->bytes_in_seg = 0;
   f->previous_length = 0;
   f->next_seg = start_seg_with_known_loc;
   for (i = 0; i < start_seg_with_known_loc; i++)
      skip(f, f->segments[i]);
   if (!vorbis_pump_first_frame(f))
      return 0;
   if (f->current_loc > sample_number)
      return error(f, VORBIS_seek_failed);
   return 1;
error:
   stb_vorbis_seek_start(f);
   return error(f, VORBIS_seek_failed);
}
static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode)
{
   int bits_read, bytes_read;
   if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode))
      return 0;
   bits_read = 1 + ilog(f->mode_count-1);
   if (f->mode_config[*mode].blockflag)
      bits_read += 2;
   bytes_read = (bits_read + 7) / 8;
   f->bytes_in_seg += bytes_read;
   f->packet_bytes -= bytes_read;
   skip(f, -bytes_read);
   if (f->next_seg == -1)
      f->next_seg = f->segment_count - 1;
   else
      f->next_seg--;
   f->valid_bits = 0;
   return 1;
}
int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number)
{
   uint32 max_frame_samples;
   if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing);
   if (!seek_to_sample_coarse(f, sample_number))
      return 0;
   assert(f->current_loc_valid);
   assert(f->current_loc <= sample_number);
   max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2;
   while (f->current_loc < sample_number) {
      int left_start, left_end, right_start, right_end, mode, frame_samples;
      if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode))
         return error(f, VORBIS_seek_failed);
      frame_samples = right_start - left_start;
      if (f->current_loc + frame_samples > sample_number) {
         return 1;
      } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) {
         vorbis_pump_first_frame(f);
      } else {
         f->current_loc += frame_samples;
         f->previous_length = 0;
         maybe_start_packet(f);
         flush_packet(f);
      }
   }
   if (f->current_loc != sample_number) return error(f, VORBIS_seek_failed);
   return 1;
}
int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number)
{
   if (!stb_vorbis_seek_frame(f, sample_number))
      return 0;
   if (sample_number != f->current_loc) {
      int n;
      uint32 frame_start = f->current_loc;
      stb_vorbis_get_frame_float(f, &n, NULL);
      assert(sample_number > frame_start);
      assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end);
      f->channel_buffer_start += (sample_number - frame_start);
   }
   return 1;
}
int stb_vorbis_seek_start(stb_vorbis *f)
{
   if (IS_PUSH_MODE(f)) { return error(f, VORBIS_invalid_api_mixing); }
   set_file_offset(f, f->first_audio_page_offset);
   f->previous_length = 0;
   f->first_decode = TRUE;
   f->next_seg = -1;
   return vorbis_pump_first_frame(f);
}
unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f)
{
   unsigned int restore_offset, previous_safe;
   unsigned int end, last_page_loc;
   if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing);
   if (!f->total_samples) {
      unsigned int last;
      uint32 lo,hi;
      char header[6];
      restore_offset = stb_vorbis_get_file_offset(f);
      if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset)
         previous_safe = f->stream_len - 65536;
      else
         previous_safe = f->first_audio_page_offset;
      set_file_offset(f, previous_safe);
      if (!vorbis_find_page(f, &end, &last)) {
         f->error = VORBIS_cant_find_last_page;
         f->total_samples = 0xffffffff;
         goto done;
      }
      last_page_loc = stb_vorbis_get_file_offset(f);
      while (!last) {
         set_file_offset(f, end);
         if (!vorbis_find_page(f, &end, &last)) {
            break;
         }
         last_page_loc = stb_vorbis_get_file_offset(f);
      }
      set_file_offset(f, last_page_loc);
      getn(f, (unsigned char *)header, 6);
      lo = get32(f);
      hi = get32(f);
      if (lo == 0xffffffff && hi == 0xffffffff) {
         f->error = VORBIS_cant_find_last_page;
         f->total_samples = SAMPLE_unknown;
         goto done;
      }
      if (hi)
         lo = 0xfffffffe;
      f->total_samples = lo;
      f->p_last.page_start = last_page_loc;
      f->p_last.page_end   = end;
      f->p_last.last_decoded_sample = lo;
     done:
      set_file_offset(f, restore_offset);
   }
   return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples;
}
float stb_vorbis_stream_length_in_seconds(stb_vorbis *f)
{
   return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate;
}
int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output)
{
   int len, right,left,i;
   if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing);
   if (!vorbis_decode_packet(f, &len, &left, &right)) {
      f->channel_buffer_start = f->channel_buffer_end = 0;
      return 0;
   }
   len = vorbis_finish_frame(f, len, left, right);
   for (i=0; i < f->channels; ++i)
      f->outputs[i] = f->channel_buffers[i] + left;
   f->channel_buffer_start = left;
   f->channel_buffer_end   = left+len;
   if (channels) *channels = f->channels;
   if (output)   *output = f->outputs;
   return len;
}
stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc)
{
   stb_vorbis *f, p;
   if (!data) {
      if (error) *error = VORBIS_unexpected_eof;
      return NULL;
   }
   vorbis_init(&p, alloc);
   p.stream = (uint8 *) data;
   p.stream_end = (uint8 *) data + len;
   p.stream_start = (uint8 *) p.stream;
   p.stream_len = len;
   p.push_mode = FALSE;
   if (start_decoder(&p)) {
      f = vorbis_alloc(&p);
      if (f) {
         *f = p;
         vorbis_pump_first_frame(f);
         if (error) *error = VORBIS__no_error;
         return f;
      }
   }
   if (error) *error = p.error;
   vorbis_deinit(&p);
   return NULL;
}
#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
#define PLAYBACK_MONO     1
#define PLAYBACK_LEFT     2
#define PLAYBACK_RIGHT    4
#define L  (PLAYBACK_LEFT  | PLAYBACK_MONO)
#define C  (PLAYBACK_LEFT  | PLAYBACK_RIGHT | PLAYBACK_MONO)
#define R  (PLAYBACK_RIGHT | PLAYBACK_MONO)
static int8 channel_position[7][6] =
{
   { 0 },
   { C },
   { L, R },
   { L, C, R },
   { L, R, L, R },
   { L, C, R, L, R },
   { L, C, R, L, R, C },
};
#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT
   typedef union {
      float f;
      int i;
   } float_conv;
   typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4];
   #define FASTDEF(x) float_conv x
   #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT))
   #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22))
   #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s))
   #define check_endianness()
#else
   #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s))))
   #define check_endianness()
   #define FASTDEF(x)
#endif
static void copy_samples(short *dest, float *src, int len)
{
   int i;
   check_endianness();
   for (i=0; i < len; ++i) {
      FASTDEF(temp);
      int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15);
      if ((unsigned int) (v + 32768) > 65535)
         v = v < 0 ? -32768 : 32767;
      dest[i] = v;
   }
}
static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len)
{
   #define STB_BUFFER_SIZE  32
   float buffer[STB_BUFFER_SIZE];
   int i,j,o,n = STB_BUFFER_SIZE;
   check_endianness();
   for (o = 0; o < len; o += STB_BUFFER_SIZE) {
      memset(buffer, 0, sizeof(buffer));
      if (o + n > len) n = len - o;
      for (j=0; j < num_c; ++j) {
         if (channel_position[num_c][j] & mask) {
            for (i=0; i < n; ++i)
               buffer[i] += data[j][d_offset+o+i];
         }
      }
      for (i=0; i < n; ++i) {
         FASTDEF(temp);
         int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15);
         if ((unsigned int) (v + 32768) > 65535)
            v = v < 0 ? -32768 : 32767;
         output[o+i] = v;
      }
   }
   #undef STB_BUFFER_SIZE
}
static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len)
{
   #define STB_BUFFER_SIZE  32
   float buffer[STB_BUFFER_SIZE];
   int i,j,o,n = STB_BUFFER_SIZE >> 1;
   check_endianness();
   for (o = 0; o < len; o += STB_BUFFER_SIZE >> 1) {
      int o2 = o << 1;
      memset(buffer, 0, sizeof(buffer));
      if (o + n > len) n = len - o;
      for (j=0; j < num_c; ++j) {
         int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT);
         if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) {
            for (i=0; i < n; ++i) {
               buffer[i*2+0] += data[j][d_offset+o+i];
               buffer[i*2+1] += data[j][d_offset+o+i];
            }
         } else if (m == PLAYBACK_LEFT) {
            for (i=0; i < n; ++i) {
               buffer[i*2+0] += data[j][d_offset+o+i];
            }
         } else if (m == PLAYBACK_RIGHT) {
            for (i=0; i < n; ++i) {
               buffer[i*2+1] += data[j][d_offset+o+i];
            }
         }
      }
      for (i=0; i < (n<<1); ++i) {
         FASTDEF(temp);
         int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15);
         if ((unsigned int) (v + 32768) > 65535)
            v = v < 0 ? -32768 : 32767;
         output[o2+i] = v;
      }
   }
   #undef STB_BUFFER_SIZE
}
static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples)
{
   int i;
   if (buf_c != data_c && buf_c <= 2 && data_c <= 6) {
      static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} };
      for (i=0; i < buf_c; ++i)
         compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples);
   } else {
      int limit = buf_c < data_c ? buf_c : data_c;
      for (i=0; i < limit; ++i)
         copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples);
      for (   ; i < buf_c; ++i)
         memset(buffer[i]+b_offset, 0, sizeof(short) * samples);
   }
}
int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples)
{
   float **output = NULL;
   int len = stb_vorbis_get_frame_float(f, NULL, &output);
   if (len > num_samples) len = num_samples;
   if (len)
      convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len);
   return len;
}
static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len)
{
   int i;
   check_endianness();
   if (buf_c != data_c && buf_c <= 2 && data_c <= 6) {
      assert(buf_c == 2);
      for (i=0; i < buf_c; ++i)
         compute_stereo_samples(buffer, data_c, data, d_offset, len);
   } else {
      int limit = buf_c < data_c ? buf_c : data_c;
      int j;
      for (j=0; j < len; ++j) {
         for (i=0; i < limit; ++i) {
            FASTDEF(temp);
            float f = data[i][d_offset+j];
            int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);
            if ((unsigned int) (v + 32768) > 65535)
               v = v < 0 ? -32768 : 32767;
            *buffer++ = v;
         }
         for (   ; i < buf_c; ++i)
            *buffer++ = 0;
      }
   }
}
int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts)
{
   float **output;
   int len;
   if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts);
   len = stb_vorbis_get_frame_float(f, NULL, &output);
   if (len) {
      if (len*num_c > num_shorts) len = num_shorts / num_c;
      convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len);
   }
   return len;
}
int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts)
{
   float **outputs;
   int len = num_shorts / channels;
   int n=0;
   while (n < len) {
      int k = f->channel_buffer_end - f->channel_buffer_start;
      if (n+k >= len) k = len - n;
      if (k)
         convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k);
      buffer += k*channels;
      n += k;
      f->channel_buffer_start += k;
      if (n == len) break;
      if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break;
   }
   return n;
}
int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len)
{
   float **outputs;
   int n=0;
   while (n < len) {
      int k = f->channel_buffer_end - f->channel_buffer_start;
      if (n+k >= len) k = len - n;
      if (k)
         convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k);
      n += k;
      f->channel_buffer_start += k;
      if (n == len) break;
      if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break;
   }
   return n;
}
int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output)
{
   int data_len, offset, total, limit, error;
   short *data;
   stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL);
   if (v == NULL) return -1;
   limit = v->channels * 4096;
   *channels = v->channels;
   if (sample_rate)
      *sample_rate = v->sample_rate;
   offset = data_len = 0;
   total = limit;
   data = (short *) malloc(total * sizeof(*data));
   if (data == NULL) {
      stb_vorbis_close(v);
      return -2;
   }
   for (;;) {
      int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset);
      if (n == 0) break;
      data_len += n;
      offset += n * v->channels;
      if (offset + limit > total) {
         short *data2;
         total *= 2;
         data2 = (short *) realloc(data, total * sizeof(*data));
         if (data2 == NULL) {
            free(data);
            stb_vorbis_close(v);
            return -2;
         }
         data = data2;
      }
   }
   *output = data;
   stb_vorbis_close(v);
   return data_len;
}
#endif
int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats)
{
   float **outputs;
   int len = num_floats / channels;
   int n=0;
   int z = f->channels;
   if (z > channels) z = channels;
   while (n < len) {
      int i,j;
      int k = f->channel_buffer_end - f->channel_buffer_start;
      if (n+k >= len) k = len - n;
      for (j=0; j < k; ++j) {
         for (i=0; i < z; ++i)
            *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j];
         for (   ; i < channels; ++i)
            *buffer++ = 0;
      }
      n += k;
      f->channel_buffer_start += k;
      if (n == len)
         break;
      if (!stb_vorbis_get_frame_float(f, NULL, &outputs))
         break;
   }
   return n;
}
int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples)
{
   float **outputs;
   int n=0;
   int z = f->channels;
   if (z > channels) z = channels;
   while (n < num_samples) {
      int i;
      int k = f->channel_buffer_end - f->channel_buffer_start;
      if (n+k >= num_samples) k = num_samples - n;
      if (k) {
         for (i=0; i < z; ++i)
            memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k);
         for (   ; i < channels; ++i)
            memset(buffer[i]+n, 0, sizeof(float) * k);
      }
      n += k;
      f->channel_buffer_start += k;
      if (n == num_samples)
         break;
      if (!stb_vorbis_get_frame_float(f, NULL, &outputs))
         break;
   }
   return n;
}
#endif
#endif

#endif

#if !defined(SSFN_HEADERONLY) && !defined(SSFN_IMPLEMENTATION)
#define SSFN_IMPLEMENTATION
#define SSFN_MAXLINES 1024
#endif
/* ssfn.h - Scalable Screen Font - MIT license
    https://gitlab.com/bztsrc/scalable-font2 */
/**************** embeded ssfn.h ****************/

#ifndef _SSFN_H_
#define _SSFN_H_
#define SSFN_VERSION 0x0200
#ifndef _STDINT_H
typedef unsigned char       uint8_t;
typedef unsigned short int  uint16_t;
typedef short int           int16_t;
typedef unsigned int        uint32_t;
#ifndef _UINT64_T
typedef unsigned long int   uint64_t;
#endif
#endif
#define SSFN_MAGIC "SFN2"
#define SSFN_COLLECTION "SFNC"
#define SSFN_ENDMAGIC "2NFS"
#define SSFN_LIG_FIRST          0xF000
#define SSFN_LIG_LAST           0xF8FF
#define SSFN_TYPE_FAMILY(x)     ((x)&15)
#define SSFN_FAMILY_SERIF       0
#define SSFN_FAMILY_SANS        1
#define SSFN_FAMILY_DECOR       2
#define SSFN_FAMILY_MONOSPACE   3
#define SSFN_FAMILY_HAND        4
#define SSFN_TYPE_STYLE(x)      (((x)>>4)&15)
#define SSFN_STYLE_REGULAR      0
#define SSFN_STYLE_BOLD         1
#define SSFN_STYLE_ITALIC       2
#define SSFN_STYLE_USRDEF1      4
#define SSFN_STYLE_USRDEF2      8
#define SSFN_CONTOUR_MOVE       0
#define SSFN_CONTOUR_LINE       1
#define SSFN_CONTOUR_QUAD       2
#define SSFN_CONTOUR_CUBIC      3
#define SSFN_FRAG_CONTOUR       0
#define SSFN_FRAG_BITMAP        1
#define SSFN_FRAG_PIXMAP        2
#define SSFN_FRAG_KERNING       3
#define SSFN_FRAG_HINTING       4
#ifndef _MSC_VER
#define _pack __attribute__((packed))
#else
#define _pack
#pragma pack(push)
#pragma pack(1)
#endif
typedef struct {
    uint8_t     magic[4];
    uint32_t    size;
    uint8_t     type;
    uint8_t     features;
    uint8_t     width;
    uint8_t     height;
    uint8_t     baseline;
    uint8_t     underline;
    uint16_t    fragments_offs;
    uint32_t    characters_offs;
    uint32_t    ligature_offs;
    uint32_t    kerning_offs;
    uint32_t    cmap_offs;
} _pack ssfn_font_t;
#ifdef _MSC_VER
#pragma pack(pop)
#endif
#define SSFN_FAMILY_ANY      0xff
#define SSFN_FAMILY_BYNAME   0xfe
#define SSFN_STYLE_UNDERLINE   16
#define SSFN_STYLE_STHROUGH    32
#define SSFN_STYLE_NOAA        64
#define SSFN_STYLE_NOKERN     128
#define SSFN_STYLE_NODEFGLYPH 256
#define SSFN_STYLE_NOCACHE    512
#define SSFN_STYLE_NOHINTING 1024
#define SSFN_STYLE_RTL       2048
#define SSFN_STYLE_ABS_SIZE  4096
#define SSFN_STYLE_NOSMOOTH  8192
#define SSFN_OK                 0
#define SSFN_ERR_ALLOC         -1
#define SSFN_ERR_BADFILE       -2
#define SSFN_ERR_NOFACE        -3
#define SSFN_ERR_INVINP        -4
#define SSFN_ERR_BADSTYLE      -5
#define SSFN_ERR_BADSIZE       -6
#define SSFN_ERR_NOGLYPH       -7
#define SSFN_SIZE_MAX         192
#define SSFN_ITALIC_DIV         4
#define SSFN_PREC               4
typedef struct {
    uint8_t *ptr;
    int w;
    int h;
    uint16_t p;
    int x;
    int y;
    uint32_t fg;
    uint32_t bg;
} ssfn_buf_t;
#define SSFN_DATA_MAX       65536
typedef struct {
    uint16_t p;
    uint8_t h;
    uint8_t o;
    uint8_t x;
    uint8_t y;
    uint8_t a;
    uint8_t d;
    uint8_t data[SSFN_DATA_MAX];
} ssfn_glyph_t;
typedef struct {
    uint8_t t;
    uint8_t n;
    uint8_t w;
    uint8_t h;
    uint8_t x;
    uint8_t y;
} ssfn_chr_t;
#ifdef SSFN_PROFILING
#include <string.h>
#include <sys/time.h>
#endif
typedef struct {
#ifdef SSFN_MAXLINES
    const ssfn_font_t *fnt[5][16];
#else
    const ssfn_font_t **fnt[5];
#endif
    const ssfn_font_t *s;
    const ssfn_font_t *f;
    ssfn_glyph_t ga;
    ssfn_glyph_t *g;
#ifdef SSFN_MAXLINES
    uint16_t p[SSFN_MAXLINES*2];
#else
    ssfn_glyph_t ***c[17];
    uint16_t *p;
    char **bufs;
#endif
    ssfn_chr_t *rc;
    int numbuf, lenbuf, np, ap, ox, oy, ax;
    int mx, my, lx, ly;
    int len[5];
    int family;
    int style;
    int size;
    int line;
#ifdef SSFN_PROFILING
    uint64_t lookup, raster, blit, kern;
#endif
} ssfn_t;
uint32_t ssfn_utf8(char **str);
int ssfn_load(ssfn_t *ctx, const void *data);
int ssfn_select(ssfn_t *ctx, int family, const char *name, int style, int size);
int ssfn_render(ssfn_t *ctx, ssfn_buf_t *dst, const char *str);
int ssfn_bbox(ssfn_t *ctx, const char *str, int *w, int *h, int *left, int *top);
ssfn_buf_t *ssfn_text(ssfn_t *ctx, const char *str, unsigned int fg);
int ssfn_mem(ssfn_t *ctx);
void ssfn_free(ssfn_t *ctx);
#define ssfn_error(err) (err<0&&err>=-7?ssfn_errstr[-err]:"Unknown error")
extern const char *ssfn_errstr[];
extern ssfn_font_t *ssfn_src;
extern ssfn_buf_t ssfn_dst;
int ssfn_putc(uint32_t unicode);
#if (defined(SSFN_IMPLEMENTATION) || defined(SSFN_CONSOLEBITMAP_PALETTE) || \
    defined(SSFN_CONSOLEBITMAP_HICOLOR) || defined(SSFN_CONSOLEBITMAP_TRUECOLOR)) && !defined(SSFN_COMMON)
#define SSFN_COMMON
const char *ssfn_errstr[] = { "",
    "Memory allocation error",
    "Bad file format",
    "No font face found",
    "Invalid input value",
    "Invalid style",
    "Invalid size",
    "Glyph not found"
};
uint32_t ssfn_utf8(char **s)
{
    uint32_t c = **s;
    if((**s & 128) != 0) {
        if(!(**s & 32)) { c = ((**s & 0x1F)<<6)|(*(*s+1) & 0x3F); *s += 1; } else
        if(!(**s & 16)) { c = ((**s & 0xF)<<12)|((*(*s+1) & 0x3F)<<6)|(*(*s+2) & 0x3F); *s += 2; } else
        if(!(**s & 8)) { c = ((**s & 0x7)<<18)|((*(*s+1) & 0x3F)<<12)|((*(*s+2) & 0x3F)<<6)|(*(*s+3) & 0x3F); *s += 3; }
        else c = 0;
    }
    (*s)++;
    return c;
}
#endif
#ifdef SSFN_IMPLEMENTATION
# ifndef NULL
#  define NULL (void*)0
# endif
# ifndef size_t
   typedef __SIZE_TYPE__ size_t;
# endif
# ifndef inline
#  define inline __inline__
# endif
#ifndef _STRING_H_
extern int memcmp (const void *__s1, const void *__s2, size_t __n) __THROW;
extern void *memset (void *__s, int __c, size_t __n) __THROW;
#endif
# ifndef SSFN_memcmp
#  ifdef __builtin_memcmp
#   define SSFN_memcmp __builtin_memcmp
#  else
#   ifndef SSFN_MAXLINES
#    define SSFN_memcmp memcmp
#   else
static int SSFN_memcmp(const void *__s1, const void *__s2, size_t __n)
{   unsigned char *a = (unsigned char *)__s1, *b = (unsigned char *)__s2;
    if(__n > 0) { while(__n-- > 0) { if(*a != *b) { return *a - *b; } a++; b++; } } return 0; }
#   endif
#  endif
# endif
# ifndef SSFN_memset
#  ifdef __builtin_memset
#   define SSFN_memset __builtin_memset
#  else
#   ifndef SSFN_MAXLINES
#    define SSFN_memset memset
#   else
static void *SSFN_memset(void *__s, int __c, size_t __n)
{ unsigned char *a = __s; if(__n > 0) { while(__n-- > 0) *a++ = __c; } return __s; }
#   endif
#  endif
# endif
# ifndef SSFN_MAXLINES
#  ifndef SSFN_realloc
#   ifdef __builtin_realloc
#    define SSFN_realloc __builtin_realloc
#   else
#    define SSFN_realloc realloc
     extern void *realloc (void *__ptr, size_t __size) __THROW;
#   endif
#  endif
#  ifndef SSFN_free
#   ifdef __builtin_free
#    define SSFN_free __builtin_free
#   else
#    define SSFN_free free
     extern void free (void *p) __THROW;
#   endif
#  endif
# endif
static uint8_t *_ssfn_c(const ssfn_font_t *font, const char *str, int *len, uint32_t *unicode)
{
    uint32_t i, j, u = -1U;
    uint16_t *l;
    uint8_t *ptr, *s;
    *len = 0; *unicode = 0;
    if(!font || !font->characters_offs || !str || !*str) return NULL;
    if(font->ligature_offs) {
        for(l = (uint16_t*)((uint8_t*)font + font->ligature_offs), i = 0; l[i] && u == -1U; i++) {
            for(ptr = (uint8_t*)font + l[i], s = (uint8_t*)str; *ptr && *ptr == *s; ptr++, s++);
            if(!*ptr) { u = SSFN_LIG_FIRST + i; break; }
        }
    }
    if(u == -1U) {
        s = (uint8_t*)str; u = *s;
        if((*s & 128) != 0) {
            if(!(*s & 32)) { u = ((*s & 0x1F)<<6)|(*(s+1) & 0x3F); s++; } else
            if(!(*s & 16)) { u = ((*s & 0xF)<<12)|((*(s+1) & 0x3F)<<6)|(*(s+2) & 0x3F); s += 2; } else
            if(!(*s & 8)) { u = ((*s & 0x7)<<18)|((*(s+1) & 0x3F)<<12)|((*(s+2) & 0x3F)<<6)|(*(s+3) & 0x3F); s += 3; }
            else u = 0;
        }
        s++;
    }
    *len = (int)(s - (uint8_t*)str);
    *unicode = u;
    for(ptr = (uint8_t*)font + font->characters_offs, i = 0; i < 0x110000; i++) {
        if(ptr[0] == 0xFF) { i += 65535; ptr++; }
        else if((ptr[0] & 0xC0) == 0xC0) { j = (((ptr[0] & 0x3F) << 8) | ptr[1]); i += j; ptr += 2; }
        else if((ptr[0] & 0xC0) == 0x80) { j = (ptr[0] & 0x3F); i += j; ptr++; }
        else {
            if(i == u) return ptr;
            ptr += 6 + ptr[1] * (ptr[0] & 0x40 ? 6 : 5);
        }
    }
    return NULL;
}
static void _ssfn_l(ssfn_t *ctx, int p, int h, int x, int y)
{
    if(x < 0 || y < 0 || x >= p || y >= h || (
        ((ctx->lx + (1 << (SSFN_PREC-1))) >> SSFN_PREC) == ((x + (1 << (SSFN_PREC-1))) >> SSFN_PREC) &&
        ((ctx->ly + (1 << (SSFN_PREC-1))) >> SSFN_PREC) == ((y + (1 << (SSFN_PREC-1))) >> SSFN_PREC))) return;
#ifdef SSFN_MAXLINES
    if(ctx->np >= SSFN_MAXLINES*2-2) return;
#else
    if(ctx->ap <= ctx->np) {
        ctx->ap = ctx->np + 512;
        ctx->p = (uint16_t*)SSFN_realloc(ctx->p, ctx->ap * sizeof(uint16_t));
        if(!ctx->p) { ctx->ap = ctx->np = 0; return; }
    }
#endif
    if(!ctx->np) {
        ctx->p[0] = ctx->mx;
        ctx->p[1] = ctx->my;
        ctx->np += 2;
    }
    ctx->p[ctx->np+0] = x;
    ctx->p[ctx->np+1] = y;
    ctx->np += 2;
    ctx->lx = x; ctx->ly = y;
}
static void _ssfn_b(ssfn_t *ctx, int p,int h, int x0,int y0, int x1,int y1, int x2,int y2, int x3,int y3, int l)
{
    int m0x, m0y, m1x, m1y, m2x, m2y, m3x, m3y, m4x, m4y,m5x, m5y;
    if(l<4 && (x0!=x3 || y0!=y3)) {
        m0x = ((x1-x0)/2) + x0;     m0y = ((y1-y0)/2) + y0;
        m1x = ((x2-x1)/2) + x1;     m1y = ((y2-y1)/2) + y1;
        m2x = ((x3-x2)/2) + x2;     m2y = ((y3-y2)/2) + y2;
        m3x = ((m1x-m0x)/2) + m0x;  m3y = ((m1y-m0y)/2) + m0y;
        m4x = ((m2x-m1x)/2) + m1x;  m4y = ((m2y-m1y)/2) + m1y;
        m5x = ((m4x-m3x)/2) + m3x;  m5y = ((m4y-m3y)/2) + m3y;
        _ssfn_b(ctx, p,h, x0,y0, m0x,m0y, m3x,m3y, m5x,m5y, l+1);
        _ssfn_b(ctx, p,h, m5x,m5y, m4x,m4y, m2x,m2y, x3,y3, l+1);
    }
    if(l) _ssfn_l(ctx, p,h, x3, y3);
}
int ssfn_load(ssfn_t *ctx, const void *data)
{
    const ssfn_font_t *font = (const ssfn_font_t *)data;
    ssfn_font_t *fnt, *end;
    int family;
    if(!ctx || !font)
        return SSFN_ERR_INVINP;
    if(((uint8_t *)font)[0] == 0x1f && ((uint8_t *)font)[1] == 0x8b) {
#ifdef SSFN_MAXLINES
        return SSFN_ERR_BADFILE;
#else
        ptr += 2;
        if(*ptr++ != 8) return SSFN_ERR_BADFILE;
        c = *ptr++; ptr += 6;
        if(c & 4) { r = *ptr++; r += (*ptr++ << 8); ptr += r; }
        if(c & 8) { while(*ptr++ != 0); }
        if(c & 16) { while(*ptr++ != 0); }
        font = (ssfn_font_t*)_ssfn_zlib_decode((const char*)ptr);
        if(!font) return SSFN_ERR_BADFILE;
        ctx->bufs = (char**)SSFN_realloc(ctx->bufs, (ctx->numbuf + 1) * sizeof(char*));
        if(!ctx->bufs) { ctx->numbuf = 0; return SSFN_ERR_ALLOC; }
        ctx->bufs[ctx->numbuf++] = (char*)font;
        ctx->lenbuf += font->size;
#endif
    }
    if(!SSFN_memcmp(font->magic, SSFN_COLLECTION, 4)) {
        end = (ssfn_font_t*)((uint8_t*)font + font->size);
        for(fnt = (ssfn_font_t*)((uint8_t*)font + 8); fnt < end && !ssfn_load(ctx, (const void *)fnt);
            fnt = (ssfn_font_t*)((uint8_t*)fnt + fnt->size));
    } else {
        family = SSFN_TYPE_FAMILY(font->type);
        if(SSFN_memcmp(font->magic, SSFN_MAGIC, 4) || SSFN_memcmp((uint8_t*)font + font->size - 4, SSFN_ENDMAGIC, 4) ||
            family > SSFN_FAMILY_HAND || font->fragments_offs >= font->size || font->characters_offs >= font->size ||
            font->ligature_offs >= font->size || font->kerning_offs >= font->size || font->cmap_offs >= font->size ||
            font->fragments_offs >= font->characters_offs) {
                return SSFN_ERR_BADFILE;
        } else {
            ctx->len[family]++;
#ifdef SSFN_MAXLINES
            if(ctx->len[family] > 15) return SSFN_ERR_ALLOC;
#else
            ctx->fnt[family] = (const ssfn_font_t**)SSFN_realloc(ctx->fnt[family], ctx->len[family]*sizeof(void*));
            if(!ctx->fnt[family]) {
                ctx->len[family] = 0;
                return SSFN_ERR_ALLOC;
            } else
#endif
                ctx->fnt[family][ctx->len[family]-1] = font;
        }
    }
    return SSFN_OK;
}
void ssfn_free(ssfn_t *ctx)
{
    if(!ctx) return;
    SSFN_memset(ctx, 0, sizeof(ssfn_t));
}
int ssfn_mem(ssfn_t *ctx)
{
#ifdef SSFN_MAXLINES
    return ctx ? sizeof(ssfn_t) : 0;
#else
    int i, j, k, ret = sizeof(ssfn_t);
    if(!ctx) return 0;
    for(i = 0; i < 5; i++) ret += ctx->len[i] * sizeof(ssfn_font_t*);
    ret += ctx->lenbuf;
    for(k = 0; k <= 16; k++) {
        if(ctx->c[k]) {
            for(j = 0; j < 256; j++)
                if(ctx->c[k][j]) {
                    for(i = 0; i < 256; i++)
                        if(ctx->c[k][j][i]) ret += 8 + ctx->c[k][j][i]->p * ctx->c[k][j][i]->h;
                    ret += 256 * sizeof(void*);
                }
            ret += 256 * sizeof(void*);
        }
    }
    if(ctx->p) ret += ctx->ap * sizeof(uint16_t);
    return ret;
#endif
}
int ssfn_select(ssfn_t *ctx, int family, const char *name, int style, int size)
{
    int i, j, l;
    if(!ctx) return SSFN_ERR_INVINP;
    if((style & ~0x5FFF)) return SSFN_ERR_BADSTYLE;
    if(size < 8 || size > SSFN_SIZE_MAX) return SSFN_ERR_BADSIZE;
    if(family == SSFN_FAMILY_BYNAME) {
        if(!name || !name[0]) return SSFN_ERR_INVINP;
        for(l=0; name[l]; l++);
        for(i=0; i < 5; i++) {
            for(j=0; j < ctx->len[i]; j++) {
                if(!SSFN_memcmp(name, (uint8_t*)&ctx->fnt[i][j]->magic + sizeof(ssfn_font_t), l)) {
                    ctx->s = ctx->fnt[i][j];
                    goto familyfound;
                }
            }
        }
        return SSFN_ERR_NOFACE;
    } else {
        if(family != SSFN_FAMILY_ANY && (family > SSFN_FAMILY_HAND || !ctx->len[family])) return SSFN_ERR_NOFACE;
        ctx->s = NULL;
    }
familyfound:
    ctx->f = NULL;
    ctx->family = family;
    ctx->style = style;
    ctx->size = size;
    ctx->line = 0;
    return SSFN_OK;
}
int ssfn_render(ssfn_t *ctx, ssfn_buf_t *dst, const char *str)
{
    ssfn_font_t **fl;
    uint8_t *ptr = NULL, *frg, *end, *tmp, color, ci = 0, cb = 0, cs, dec[65536];
    uint16_t r[640];
    uint32_t unicode, P, O, *Op, *Ol, sR, sG, sB, sA, bA;
    int ret = 0, i, j, k, l, p, m, n, o, s, x, y, w, h, H, a, A, b, B, nr, uix, uax;
    int ox, oy, y0, y1, Y0, Y1, x0, x1, X0, X1, X2, xs, ys, yp, pc, fB, fG, fR, fA, bB, bG, bR, dB, dG, dR, dA;
#ifdef SSFN_PROFILING
    struct timeval tv0, tv1, tvd;
    gettimeofday(&tv0, NULL);
#endif
#define PUTPIXEL  O = *Ol;bR = (O >> (16 - cs)) & 0xFF; bG = (O >> 8) & 0xFF; bB = (O >> cs) & 0xFF; bA = (O >> 24) & 0xFF;\
    bB += ((fB - bB) * fA) >> 8; bG += ((fG - bG) * fA) >> 8; bR += ((fR - bR) * fA) >> 8; bA += ((fA - bA) * fA) >> 8;\
    *Ol = (bA << 24) | (bR << (16 - cs)) | (bG << 8) | (bB << cs);
    if(!ctx || !str) return SSFN_ERR_INVINP;
    if(!*str) return 0;
    if(*str == '\r') { dst->x = 0; return 1; }
    if(*str == '\n') { dst->x = 0; dst->y += ctx->line ? ctx->line : ctx->size; return 1; }
    if(ctx->s) {
        ctx->f = ctx->s;
        ptr = _ssfn_c(ctx->f, str, &ret, &unicode);
    } else {
        p = ctx->family;
        ctx->f = NULL;
again:  if(p >= SSFN_FAMILY_BYNAME) { n = 0; m = 4; } else n = m = p;
        for(; n <= m; n++) {
            fl = (ssfn_font_t **)ctx->fnt[n];
            if(ctx->style & 3) {
                for(i=0;i<ctx->len[n];i++)
                    if(((fl[i]->type>>4) & 3) == (ctx->style & 3) && fl[i]->height == ctx->size &&
                        (ptr = _ssfn_c(fl[i], str, &ret, &unicode))) { ctx->f = fl[i]; break; }
                if(!ptr)
                    for(i=0;i<ctx->len[n];i++)
                        if(fl[i]->height == ctx->size && (ptr = _ssfn_c(fl[i], str, &ret, &unicode))) { ctx->f = fl[i]; break; }
                if(!ptr)
                    for(i=0;i<ctx->len[n];i++)
                        if(((fl[i]->type>>4) & 3) == (ctx->style & 3) && (ptr = _ssfn_c(fl[i], str, &ret, &unicode)))
                            { ctx->f = fl[i]; break; }
                if(!ptr && (ctx->style & 3) == 3)
                    for(i=0;i<ctx->len[n];i++)
                        if(((fl[i]->type>>4) & 3) && (ptr = _ssfn_c(fl[i], str, &ret, &unicode))) { ctx->f = fl[i]; break; }
            }
            if(!ptr) {
                for(i=0;i<ctx->len[n];i++)
                    if((ptr = _ssfn_c(fl[i], str, &ret, &unicode))) { ctx->f = fl[i]; break; }
            }
        }
        if(!ptr && p != SSFN_FAMILY_ANY) { p = SSFN_FAMILY_ANY; goto again; }
    }
    if(!ptr) {
        if(ctx->style & SSFN_STYLE_NODEFGLYPH) return SSFN_ERR_NOGLYPH;
        else {
            unicode = 0;
            if(ctx->family >= SSFN_FAMILY_BYNAME) { n = 0; m = 4; } else n = m = ctx->family;
            for(; n <= m && !ptr; n++)
                if(ctx->len[n] && ctx->fnt[n][0] && !(*((uint8_t*)ctx->fnt[n][0] + ctx->fnt[n][0]->characters_offs) & 0x80))
                    { ctx->f = ctx->fnt[n][0]; ptr = (uint8_t*)ctx->f + ctx->f->characters_offs; }
        }
        if(!ptr) return SSFN_ERR_NOGLYPH;
    }
    if(!ctx->f || !ctx->f->height || !ctx->size) return SSFN_ERR_NOFACE;
    if((unicode >> 16) > 0x10) return SSFN_ERR_INVINP;
    ctx->rc = (ssfn_chr_t*)ptr; ptr += sizeof(ssfn_chr_t);
    H = (ctx->style & SSFN_STYLE_ABS_SIZE) || SSFN_TYPE_FAMILY(ctx->f->type) == SSFN_FAMILY_MONOSPACE || !ctx->f->baseline ?
        ctx->size : ctx->size * ctx->f->height / ctx->f->baseline;
#ifdef SSFN_PROFILING
    gettimeofday(&tv1, NULL); tvd.tv_sec = tv1.tv_sec - tv0.tv_sec; tvd.tv_usec = tv1.tv_usec - tv0.tv_usec;
    if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; }
    ctx->lookup += tvd.tv_sec * 1000000L + tvd.tv_usec;
    memcpy(&tv0, &tv1, sizeof(struct timeval));
#endif
    {
        h = ctx->style & SSFN_STYLE_NOAA ? H : (ctx->size > ctx->f->height ? (ctx->size + 4) & ~3 : ctx->f->height);
        ci = (ctx->style & SSFN_STYLE_ITALIC) && !(SSFN_TYPE_STYLE(ctx->f->type) & SSFN_STYLE_ITALIC);
        cb = (ctx->style & SSFN_STYLE_BOLD) && !(SSFN_TYPE_STYLE(ctx->f->type) & SSFN_STYLE_BOLD) ? (ctx->f->height+64)>>6 : 0;
        w = (ctx->rc->w * h + ctx->f->height - 1) / ctx->f->height;
        if(w > SSFN_SIZE_MAX) { h = h * SSFN_SIZE_MAX / w; w = SSFN_SIZE_MAX; }
        p = w + (ci ? h / SSFN_ITALIC_DIV : 0) + cb;
        if(p * h >= SSFN_DATA_MAX) return SSFN_ERR_BADSIZE;
            ctx->g = &ctx->ga;
        x = (ctx->rc->x > 0 && ci ? (ctx->f->height - ctx->f->baseline) * h / SSFN_ITALIC_DIV / ctx->f->height : 0);
        ctx->g->p = p;
        ctx->g->h = h;
        ctx->g->x = (ctx->rc->x + x > 255 ? 255 : ctx->rc->x + x);
        ctx->g->y = ctx->rc->y;
        ctx->g->o = (ctx->rc->t & 0x3F) + x;
        SSFN_memset(&ctx->g->data, 0xFF, p * h);
        color = 0xFE; ctx->g->a = ctx->g->d = 0;
        for(n = 0; n < ctx->rc->n; n++) {
            if(ptr[0] == 255 && ptr[1] == 255) { color = ptr[2]; ptr += ctx->rc->t & 0x40 ? 6 : 5; continue; }
            x = ((ptr[0] + cb) << SSFN_PREC) * h / ctx->f->height; y = (ptr[1] << SSFN_PREC) * h / ctx->f->height;
            if(ctx->rc->t & 0x40) { m = (ptr[5] << 24) | (ptr[4] << 16) | (ptr[3] << 8) | ptr[2]; ptr += 6; }
            else { m = (ptr[4] << 16) | (ptr[3] << 8) | ptr[2]; ptr += 5; }
            frg = (uint8_t*)ctx->f +  m;
            if(!(frg[0] & 0x80)) {
                j = (frg[0] & 0x3F);
                if(frg[0] & 0x40) { j <<= 8; j |= frg[1]; frg++; }
                j++; frg++; tmp = frg; frg += (j+3)/4; ctx->np = 0;
                for(i = 0; i < j; i++) {
                    k = (frg[0] << SSFN_PREC) * h / ctx->f->height + x; m = (frg[1] << SSFN_PREC) * h / ctx->f->height + y;
                    switch((tmp[i >> 2] >> ((i & 3) << 1)) & 3) {
                        case SSFN_CONTOUR_MOVE: ctx->mx = ctx->lx = k; ctx->my = ctx->ly = m; frg += 2; break;
                        case SSFN_CONTOUR_LINE: _ssfn_l(ctx, p << SSFN_PREC, h << SSFN_PREC, k, m); frg += 2; break;
                        case SSFN_CONTOUR_QUAD:
                            a = (frg[2] << SSFN_PREC) * h / ctx->f->height + x; A = (frg[3] << SSFN_PREC) * h / ctx->f->height + y;
                            _ssfn_b(ctx, p << SSFN_PREC,h << SSFN_PREC, ctx->lx,ctx->ly, ((a-ctx->lx)/2)+ctx->lx,
                                ((A-ctx->ly)/2)+ctx->ly, ((k-a)/2)+a,((A-m)/2)+m, k,m, 0);
                            frg += 4;
                        break;
                        case SSFN_CONTOUR_CUBIC:
                            a = (frg[2] << SSFN_PREC) * h / ctx->f->height + x; A = (frg[3] << SSFN_PREC) * h / ctx->f->height + y;
                            b = (frg[4] << SSFN_PREC) * h / ctx->f->height + x; B = (frg[5] << SSFN_PREC) * h / ctx->f->height + y;
                            _ssfn_b(ctx, p << SSFN_PREC,h << SSFN_PREC, ctx->lx,ctx->ly, a,A, b,B, k,m, 0);
                            frg += 6;
                        break;
                    }
                }
                if(ctx->mx != ctx->lx || ctx->my != ctx->ly) { ctx->p[ctx->np+0] = ctx->mx; ctx->p[ctx->np+1] = ctx->my; ctx->np += 2; }
                if(ctx->np > 4) {
                    for(b = A = B = o = 0; b < h; b++, B += p) {
                        a = b << SSFN_PREC;
                        for(nr = 0, i = 0; i < ctx->np - 3; i += 2) {
                            if( (ctx->p[i+1] < a && ctx->p[i+3] >= a) ||
                                (ctx->p[i+3] < a && ctx->p[i+1] >= a)) {
                                    if((ctx->p[i+1] >> SSFN_PREC) == (ctx->p[i+3] >> SSFN_PREC))
                                        x = (((int)ctx->p[i]+(int)ctx->p[i+2])>>1);
                                    else
                                        x = ((int)ctx->p[i]) + ((a - (int)ctx->p[i+1])*
                                            ((int)ctx->p[i+2] - (int)ctx->p[i])/
                                            ((int)ctx->p[i+3] - (int)ctx->p[i+1]));
                                    x >>= SSFN_PREC;
                                    if(ci) x += (h - b) / SSFN_ITALIC_DIV;
                                    if(cb && !o) {
                                        if(ctx->g->data[B + x] != color) { o = -cb; A = cb; }
                                        else { o = cb; A = -cb; }
                                    }
                                    for(k = 0; k < nr && x > r[k]; k++);
                                    for(l = nr; l > k; l--) r[l] = r[l-1];
                                    r[k] = x;
                                    nr++;
                            }
                        }
                        if(nr > 1 && nr & 1) { r[nr - 2] = r[nr - 1]; nr--; }
                        if(nr) {
                            if(ctx->g->d < y + b) ctx->g->d = y + b;
                            for(i = 0; i < nr - 1; i += 2) {
                                l = r[i] + o; m = r[i + 1] + A;
                                if(l < 0) l = 0;
                                if(m > p) m = p;
                                if(i > 0 && l < r[i - 1] + A) l = r[i - 1] + A;
                                for(; l < m; l++)
                                    ctx->g->data[B + l] = ctx->g->data[B + l] == color ? 0xFF : color;
                            }
                        }
                    }
                }
            } else if((frg[0] & 0x60) == 0x00) {
                B = ((frg[0] & 0x1F) + 1) << 3; A = frg[1] + 1; x >>= SSFN_PREC; y >>= SSFN_PREC;
                b = B * h / ctx->f->height; a = A * h / ctx->f->height;
                if(ctx->g->d < y + a) ctx->g->d = y + a;
                frg += 2;
                for(j = 0; j < a; j++) {
                    k = j * A / a;
                    l = (y + j) * p + x + (ci ? (h - y - j) / SSFN_ITALIC_DIV : 0);
                    for(i = 0; i < b; i++) {
                        m = i * B / b;
                        if(frg[(k * B + m) >> 3] & (1 << (m & 7))) {
                            for(o = 0; o <= cb; o++)
                                ctx->g->data[l + i + o] = color;
                        }
                    }
                }
                if(!(ctx->style & (SSFN_STYLE_NOAA|SSFN_STYLE_NOSMOOTH))) {
                    m = color == 0xFD ? 0xFC : 0xFD; o = y * p + p + x;
                    for(k = h; k > ctx->f->height + 4; k -= 2*ctx->f->height) {
                        for(j = 1, l = o; j < a - 1; j++, l += p)
                            for(i = 1; i < b - 1; i++) {
                                if(ctx->g->data[l + i] == 0xFF && (ctx->g->data[l + i - p] == color ||
                                    ctx->g->data[l + i + p] == color) && (ctx->g->data[l + i - 1] == color ||
                                    ctx->g->data[l + i + 1] == color)) ctx->g->data[l + i] = m;
                            }
                        for(j = 1, l = o; j < a - 1; j++, l += p)
                            for(i = 1; i < b - 1; i++) {
                                if(ctx->g->data[l + i] == m) ctx->g->data[l + i] = color;
                            }
                    }
                }
            } else if((frg[0] & 0x60) == 0x20) {
                k = (((frg[0] & 0x1F) << 8) | frg[1]) + 1; B = frg[2] + 1; A = frg[3] + 1; x >>= SSFN_PREC; y >>= SSFN_PREC;
                b = B * h / ctx->f->height; a = A * h / ctx->f->height;
                if(ctx->g->d < y + a) ctx->g->d = y + a;
                frg += 4; end = frg + k; i = 0;
                while(frg < end) {
                    l = ((*frg++) & 0x7F) + 1;
                    if(frg[-1] & 0x80) {
                        while(l--) dec[i++] = *frg;
                        frg++;
                    } else while(l--) dec[i++] = *frg++;
                }
                for(j = 0; j < a; j++) {
                    k = j * A / a * B;
                    l = (y + j) * p + x + (ci ? (h - y - j) / SSFN_ITALIC_DIV : 0);
                    for(i = 0; i < b; i++) {
                        m = dec[k + i * B / b];
                        if(m != 0xFF) ctx->g->data[l + i] = m;
                    }
                }
            }
            color = 0xFE;
        }
        ctx->g->a = ctx->f->baseline;
        if(ctx->g->d > ctx->g->a + 1) ctx->g->d -= ctx->g->a + 1; else ctx->g->d = 0;
#ifdef SSFN_DEBUGGLYPH
        printf("\nU+%06X size %d p %d h %d base %d under %d overlap %d ascender %d descender %d advance x %d advance y %d cb %d\n",
            unicode, ctx->size,p,h,ctx->f->baseline,ctx->f->underline,ctx->g->o,ctx->g->a,ctx->g->d,ctx->g->x,ctx->g->y,cb);
        for(j = 0; j < h; j++) { printf("%3d: ", j); for(i = 0; i < p; i++) { if(ctx->g->data[j*p+i] == 0xFF) printf(j == ctx->g->a ? "_" : "."); else printf("%x", ctx->g->data[j*p+i] & 0xF); } printf("\n"); }
#endif
#ifdef SSFN_PROFILING
        gettimeofday(&tv1, NULL); tvd.tv_sec = tv1.tv_sec - tv0.tv_sec; tvd.tv_usec = tv1.tv_usec - tv0.tv_usec;
        if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; }
        ctx->raster += tvd.tv_sec * 1000000L + tvd.tv_usec;
        memcpy(&tv0, &tv1, sizeof(struct timeval));
#endif
    }
    if(dst) {
        h = H;
        if(h > ctx->line) ctx->line = h;
        w = ctx->g->p * h / ctx->g->h;
        s = ((ctx->g->x - ctx->g->o) * h + ctx->f->height - 1) / ctx->f->height;
        n = ctx->size > 16 ? 2 : 1;
        if(w < n) w = n;
        if(s < n) s = n;
        if(ctx->g->x) {
            ctx->ox = ox = ((ctx->g->o * h + ctx->f->height - 1) / ctx->f->height) + (ctx->style & SSFN_STYLE_RTL ? w : 0);
            ctx->oy = oy = (ctx->g->a * h + ctx->f->height - 1) / ctx->f->height;
        } else { ctx->ox = ox = w / 2; ctx->oy = oy = 0; }
        if(dst->ptr) {
            j = dst->w < 0 ? -dst->w : dst->w;
            cs = dst->w < 0 ? 16 : 0;
            cb = (h + 64) >> 6; uix = w > s ? w : s; uax = 0;
            n = (ctx->f->underline * h + ctx->f->height - 1) / ctx->f->height;
#ifdef SSFN_DEBUGGLYPH
            printf("Scaling to w %d h %d (glyph %d %d, cache %d %d, font %d)\n",
                w,h,ctx->rc->w,ctx->rc->h,ctx->g->p,ctx->g->h,ctx->f->height);
#endif
            fR = (dst->fg >> 16) & 0xFF; fG = (dst->fg >> 8) & 0xFF; fB = (dst->fg >> 0) & 0xFF; fA = (dst->fg >> 24) & 0xFF;
            bR = (dst->bg >> 16) & 0xFF; bG = (dst->bg >> 8) & 0xFF; bB = (dst->bg >> 0) & 0xFF; O = 0xFF000000;
            Op = (uint32_t*)(dst->ptr + dst->p * (dst->y - oy) + ((dst->x - ox) << 2));
            for (y = 0; y < h && dst->y + y - oy < dst->h; y++, Op += dst->p >> 2) {
                if(dst->y + y - oy < 0) continue;
                y0 = (y << 8) * ctx->g->h / h; Y0 = y0 >> 8; y1 = ((y + 1) << 8) * ctx->g->h / h; Y1 = y1 >> 8; Ol = Op;
                for (x = 0; x < w && dst->x + x - ox < j; x++, Ol++) {
                    if(dst->x + x - ox < 0) continue;
                    m = 0; sR = sG = sB = sA = bA = 0;
                    if(!dst->bg) {
                        O = *Ol;
                        bA = (O >> 24) & 0xFF;
                        bR = (O >> (16 - cs)) & 0xFF;
                        bG = (O >> 8) & 0xFF;
                        bB = (O >> cs) & 0xFF;
                    }
                    x0 = (x << 8) * ctx->g->p / w; X0 = x0 >> 8; x1 = ((x + 1) << 8) * ctx->g->p / w; X1 = x1 >> 8;
                    for(ys = y0; ys < y1; ys += 256) {
                        if(ys >> 8 == Y0) { yp = 256 - (ys & 0xFF); ys &= ~0xFF; if(yp > y1 - y0) yp = y1 - y0; }
                        else if(ys >> 8 == Y1) yp = y1 & 0xFF; else yp = 256;
                        X2 = (ys >> 8) * ctx->g->p;
                        for(xs = x0; xs < x1; xs += 256) {
                            if (xs >> 8 == X0) {
                                k = 256 - (xs & 0xFF); xs &= ~0xFF; if(k > x1 - x0) k = x1 - x0; pc = k == 256 ? yp : (k * yp)>>8;
                            } else
                            if (xs >> 8 == X1) { k = x1 & 0xFF; pc = k == 256 ? yp : (k * yp) >> 8; }
                            else pc = yp;
                            m += pc;
                            k = ctx->g->data[X2 + (xs >> 8)];
                            if(k == 0xFF) {
                                sB += bB * pc; sG += bG * pc; sR += bR * pc; sA += 255;
                            } else {
                                if(k == 0xFE || !ctx->f->cmap_offs) {
                                    dB = fB; dG = fG; dR = fR; dA = fA;
                                } else {
                                    P = *((uint32_t*)((uint8_t*)ctx->f + ctx->f->cmap_offs + (k << 2)));
                                    dR = (P >> 16) & 0xFF; dG = (P >> 8) & 0xFF; dB = (P >> 0) & 0xFF; dA = (P >> 24) & 0xFF;
                                }
                                if(dA == 255) {
                                    sB += dB * pc; sG += dG * pc; sR += dR * pc; sA += dA * pc;
                                } else {
                                    sB += (dB * dA + bB * (255 - dA)) * pc / 255; sG += (dG * dA + bG * (255 - dA)) * pc / 255;
                                    sR += (dR * dA + bR * (255 - dA)) * pc / 255; sA += dA * pc;
                                }
                            }
                        }
                    }
                    if(m) { sR /= m; sG /= m; sB /= m; sA /= m; } else { sR >>= 8; sG >>= 8; sB >>= 8; sA >>= 8; }
                    if(ctx->style & SSFN_STYLE_NOAA) sA = sA > 127 ? 255 : 0;
                    if(sA > 15) {
                        *Ol = ((sA > 255 ? 255 : (sA > bA ? sA : bA)) << 24) |
                            ((sR > 255 ? 255 : sR) << (16 - cs)) | ((sG > 255 ? 255 : sG) << 8) | ((sB > 255 ? 255 : sB) << cs);
                        if(y == n) { if(uix > x) { uix = x; } if(uax < x) { uax = x; } }
                    }
                }
            }
            if(ctx->style & SSFN_STYLE_UNDERLINE) {
                uix -= cb + 1; uax += cb + 2;
                if(uax < uix) uax = uix + 1;
                k = (w > s ? w : s);
                Op = (uint32_t*)(dst->ptr + dst->p * (dst->y - oy + n) + ((dst->x - ox - 1) << 2));
                for (y = n; y < n + cb && dst->y + y - oy < dst->h; y++, Op += dst->p >> 2) {
                    if(dst->y + y - oy < 0) continue;
                    for (Ol = Op, x = 0; x <= k && dst->x + x - ox < j; x++, Ol++) {
                        if(dst->x + x - ox < 0 || (x > uix && x < uax)) continue;
                        PUTPIXEL;
                    }
                }
            }
            if(ctx->style & SSFN_STYLE_STHROUGH) {
                n = (h >> 1); k = (w > s ? w : s) + 1;
                Op = (uint32_t*)(dst->ptr + dst->p * (dst->y - oy + n) + ((dst->x - ox - 1) << 2));
                for (y = n; y < n + cb && dst->y + y - oy < dst->h; y++, Op += dst->p >> 2) {
                    if(dst->y + y - oy < 0) continue;
                    for (Ol = Op, x = 0; x <= k && dst->x + x - ox < j; x++, Ol++) {
                        if(dst->x + x - ox < 0) continue;
                        PUTPIXEL;
                    }
                }
            }
#ifdef SSFN_PROFILING
            gettimeofday(&tv1, NULL); tvd.tv_sec = tv1.tv_sec - tv0.tv_sec;tvd.tv_usec = tv1.tv_usec - tv0.tv_usec;
            if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; }
            ctx->blit += tvd.tv_sec * 1000000L + tvd.tv_usec;
            memcpy(&tv0, &tv1, sizeof(struct timeval));
#endif
        }
        ctx->ax = (ctx->style & SSFN_STYLE_RTL ? -s : s);
        dst->x += ctx->ax;
        dst->y += (ctx->g->y * h + ctx->f->height - 1) / ctx->f->height;
        ptr = (uint8_t*)str + ret;
        if(!(ctx->style & SSFN_STYLE_NOKERN) && ctx->f->kerning_offs && _ssfn_c(ctx->f, (const char*)ptr, &i, &P) && P > 32) {
            ptr = (uint8_t*)ctx->rc + sizeof(ssfn_chr_t);
            for(n = 0; n < ctx->rc->n; n++) {
                if(ptr[0] == 255 && ptr[1] == 255) { ptr += ctx->rc->t & 0x40 ? 6 : 5; continue; }
                x = ptr[0];
                if(ctx->rc->t & 0x40) { m = (ptr[5] << 24) | (ptr[4] << 16) | (ptr[3] << 8) | ptr[2]; ptr += 6; }
                else { m = (ptr[4] << 16) | (ptr[3] << 8) | ptr[2]; ptr += 5; }
                frg = (uint8_t*)ctx->f +  m;
                if((frg[0] & 0xE0) == 0xC0) {
                    k = (((frg[0] & 0x1F) << 8) | frg[1]) + 1; frg += 2;
                    while(k--) {
                        m = ((frg[2] & 0xF) << 16) | (frg[1] << 8) | frg[0];
                        if(P >= (uint32_t)m && P <= (uint32_t)(((frg[5] & 0xF) << 16) | (frg[4] << 8) | frg[3])) {
                            P -= m;
                            m = ctx->f->kerning_offs + ((((frg[2] >> 4) & 0xF) << 24) | (((frg[5] >> 4) & 0xF) << 16) |
                                (frg[7] << 8) | frg[6]);
                            tmp = (uint8_t*)ctx->f + m;
                            while(tmp < (uint8_t*)ctx->f + ctx->f->size - 4) {
                                if((tmp[0] & 0x7F) < P) {
                                    P -= (tmp[0] & 0x7F) + 1;
                                    tmp += 2 + (tmp[0] & 0x80 ? 0 : tmp[0] & 0x7F);
                                } else {
                                    y = (int)((signed char)tmp[1 + ((tmp[0] & 0x80) ? 0 : P)]) * h / ctx->f->height;
                                    if(x) dst->x += y; else dst->y += y;
                                    break;
                                }
                            }
                            break;
                        }
                        frg += 8;
                    }
                }
            }
#ifdef SSFN_PROFILING
            gettimeofday(&tv1, NULL); tvd.tv_sec = tv1.tv_sec - tv0.tv_sec; tvd.tv_usec = tv1.tv_usec - tv0.tv_usec;
            if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; }
            ctx->kern += tvd.tv_sec * 1000000L + tvd.tv_usec;
#endif
        }
    }
    return ret;
}
int ssfn_bbox(ssfn_t *ctx, const char *str, int *w, int *h, int *left, int *top)
{
    ssfn_buf_t buf;
    int ret, f = 1, l = 0, t = 0;
    if(!ctx || !str) return SSFN_ERR_INVINP;
    if(w) {*w = 0;} if(h) {*h = 0;} if(top) {*top = 0;} if(left) {*left = 0;}
    if(!*str) return SSFN_OK;
    SSFN_memset(&buf, 0, sizeof(ssfn_buf_t)); ctx->line = 0;
    while((ret = ssfn_render(ctx, &buf, str))) {
        if(ret < 0 || !ctx->g) return ret;
        if(f) { f = 0; l = ctx->ox; buf.x += l; }
        if(ctx->g->x) {
            if(ctx->oy > t) t = ctx->oy;
        } else {
            if(buf.w < ctx->g->p) buf.w = ctx->g->p;
            buf.h += ctx->g->y ? ctx->g->y : ctx->g->h;
        }
        str += ret;
    }
    if((ctx->style & SSFN_STYLE_ITALIC) && !(SSFN_TYPE_STYLE(ctx->f->type) & SSFN_STYLE_ITALIC))
        buf.x +=  ctx->size / SSFN_ITALIC_DIV - l;
    if(ctx->g->x) { if(w) {*w = buf.x;} if(h) {*h = ctx->line;} if(left) {*left = l;} if(top) {*top = t;} }
    else { if(w) {*w = buf.w;} if(h) {*h = buf.y;} if(top) {*top = 0;} if(left) {*left = 0;} }
    return SSFN_OK;
}
ssfn_buf_t *ssfn_text(ssfn_t *ctx, const char *str, unsigned int fg)
{
    (void)ctx;
    (void)str;
    (void)fg;
    return NULL;
}
#endif
#if defined(SSFN_CONSOLEBITMAP_PALETTE) || defined(SSFN_CONSOLEBITMAP_HICOLOR) || defined(SSFN_CONSOLEBITMAP_TRUECOLOR)
ssfn_font_t *ssfn_src;
ssfn_buf_t ssfn_dst;
int ssfn_putc(uint32_t unicode)
{
# ifdef SSFN_CONSOLEBITMAP_PALETTE
#  define SSFN_PIXEL  uint8_t
# else
#  ifdef SSFN_CONSOLEBITMAP_HICOLOR
#   define SSFN_PIXEL uint16_t
#  else
#   define SSFN_PIXEL uint32_t
#  endif
# endif
    register SSFN_PIXEL *o, *p;
    register uint8_t *ptr, *chr = NULL, *frg;
    register int i, j, k, l, m, y = 0, w, s = ssfn_dst.p / sizeof(SSFN_PIXEL);
    if(!ssfn_src || ssfn_src->magic[0] != 'S' || ssfn_src->magic[1] != 'F' || ssfn_src->magic[2] != 'N' ||
        ssfn_src->magic[3] != '2' || !ssfn_dst.ptr || !ssfn_dst.p) return SSFN_ERR_INVINP;
    w = ssfn_dst.w < 0 ? -ssfn_dst.w : ssfn_dst.w;
    for(ptr = (uint8_t*)ssfn_src + ssfn_src->characters_offs, i = 0; i < 0x110000; i++) {
        if(ptr[0] == 0xFF) { i += 65535; ptr++; }
        else if((ptr[0] & 0xC0) == 0xC0) { j = (((ptr[0] & 0x3F) << 8) | ptr[1]); i += j; ptr += 2; }
        else if((ptr[0] & 0xC0) == 0x80) { j = (ptr[0] & 0x3F); i += j; ptr++; }
        else { if((uint32_t)i == unicode) { chr = ptr; break; } ptr += 6 + ptr[1] * (ptr[0] & 0x40 ? 6 : 5); }
    }
#ifdef SSFN_CONSOLEBITMAP_CONTROL
    i = ssfn_src->height; j = ssfn_dst.h - i - (ssfn_dst.h % i);
    if(chr && w) {
        if(unicode == '\t') ssfn_dst.x -= ssfn_dst.x % chr[4];
        if(ssfn_dst.x + chr[4] > w) { ssfn_dst.x = 0; ssfn_dst.y += i; }
    }
    if(unicode == '\n') ssfn_dst.y += i;
    if(j > 0 && ssfn_dst.y > j) {
        ssfn_dst.y = j;
        for(k = 0; k < j; k++)
            for(l = 0; l < ssfn_dst.p; l++) ssfn_dst.ptr[k * ssfn_dst.p + l] = ssfn_dst.ptr[(k + i) * ssfn_dst.p + l];
    }
    if(unicode == '\r' || unicode == '\n') { ssfn_dst.x = 0; return SSFN_OK; }
#endif
    if(!chr) return SSFN_ERR_NOGLYPH;
    ptr = chr + 6; o = (SSFN_PIXEL*)(ssfn_dst.ptr + ssfn_dst.y * ssfn_dst.p + ssfn_dst.x * sizeof(SSFN_PIXEL));
    for(i = 0; i < chr[1]; i++, ptr += chr[0] & 0x40 ? 6 : 5) {
        if(ptr[0] == 255 && ptr[1] == 255) continue;
        frg = (uint8_t*)ssfn_src + (chr[0] & 0x40 ? ((ptr[5] << 24) | (ptr[4] << 16) | (ptr[3] << 8) | ptr[2]) :
            ((ptr[4] << 16) | (ptr[3] << 8) | ptr[2]));
        if((frg[0] & 0xE0) != 0x80) continue;
        if(ssfn_dst.bg) {
            for(; y < ptr[1] && (!ssfn_dst.h || ssfn_dst.y + y < ssfn_dst.h); y++, o += s) {
                for(p = o, j = 0; j < chr[2] && (!w || ssfn_dst.x + j < w); j++, p++)
                    *p = ssfn_dst.bg;
            }
        } else { o += (int)(ptr[1] - y) * s; y = ptr[1]; }
        k = ((frg[0] & 0x1F) + 1) << 3; j = frg[1] + 1; frg += 2;
        for(m = 1; j && (!ssfn_dst.h || ssfn_dst.y + y < ssfn_dst.h); j--, y++, o += s)
            for(p = o, l = 0; l < k; l++, p++, m <<= 1) {
                if(m > 0x80) { frg++; m = 1; }
                if(ssfn_dst.x + l >= 0 && (!w || ssfn_dst.x + l < w)) {
                    if(*frg & m) *p = ssfn_dst.fg; else
                    if(ssfn_dst.bg) *p = ssfn_dst.bg;
                }
            }
    }
    if(ssfn_dst.bg)
        for(; y < chr[3] && (!ssfn_dst.h || ssfn_dst.y + y < ssfn_dst.h); y++, o += s) {
            for(p = o, j = 0; j < chr[2] && (!w || ssfn_dst.x + j < w); j++, p++)
                *p = ssfn_dst.bg;
        }
    ssfn_dst.x += chr[4]; ssfn_dst.y += chr[5];
    return SSFN_OK;
}
#endif
#endif


#if !defined(M3D_HEADERONLY) && !defined(M3D_IMPLEMENTATION)
#define M3D_IMPLEMENTATION
#define M3D_NOTEXTURE
#endif
#endif /* APCK_IMPLEMENTATION */
/* m3d.h - Model3D - MIT license
    https://bztsrc.gitlab.com/model3d */
/**************** embeded m3d.h ****************/

#ifndef _M3D_H_
#define _M3D_H_
#include <stdint.h>
#ifndef M3D_MALLOC
# define M3D_MALLOC(sz)     malloc(sz)
#endif
#ifndef M3D_REALLOC
# define M3D_REALLOC(p,nsz) realloc(p,nsz)
#endif
#ifndef M3D_FREE
# define M3D_FREE(p)        free(p)
#endif
#ifndef M3D_LOG
# define M3D_LOG(x)
#endif
#ifndef M3D_APIVERSION
#define M3D_APIVERSION      0x0100
#ifndef M3D_DOUBLE
typedef float M3D_FLOAT;
#ifndef M3D_EPSILON
#define M3D_EPSILON ((M3D_FLOAT)1e-7)
#endif
#else
typedef double M3D_FLOAT;
#ifndef M3D_EPSILON
#define M3D_EPSILON ((M3D_FLOAT)1e-14)
#endif
#endif
#if !defined(M3D_SMALLINDEX)
typedef uint32_t M3D_INDEX;
typedef uint16_t M3D_VOXEL;
#define M3D_UNDEF 0xffffffff
#define M3D_INDEXMAX 0xfffffffe
#define M3D_VOXUNDEF 0xffff
#define M3D_VOXCLEAR 0xfffe
#else
typedef uint16_t M3D_INDEX;
typedef uint8_t M3D_VOXEL;
#define M3D_UNDEF 0xffff
#define M3D_INDEXMAX 0xfffe
#define M3D_VOXUNDEF 0xff
#define M3D_VOXCLEAR 0xfe
#endif
#define M3D_NOTDEFINED 0xffffffff
#ifndef M3D_NUMBONE
#define M3D_NUMBONE 4
#endif
#ifndef M3D_BONEMAXLEVEL
#define M3D_BONEMAXLEVEL 64
#endif
#ifndef _MSC_VER
#ifndef _inline
#define _inline __inline__
#endif
#define _pack __attribute__((packed))
#define _unused __attribute__((unused))
#else
#define _inline
#define _pack
#define _unused __pragma(warning(suppress:4100))
#endif
#ifndef  __cplusplus
#define _register register
#else
#define _register
#endif
#ifdef _MSC_VER
#pragma pack(push)
#pragma pack(1)
#endif
typedef struct {
    char magic[4];
    uint32_t length;
    float scale;
    uint32_t types;
} _pack m3dhdr_t;
typedef struct {
    char magic[4];
    uint32_t length;
} _pack m3dchunk_t;
#ifdef _MSC_VER
#pragma pack(pop)
#endif
typedef struct {
    M3D_FLOAT u;
    M3D_FLOAT v;
} m3dti_t;
#define m3d_textureindex_t m3dti_t
typedef struct {
    char *name;
    uint8_t *d;
    uint16_t w;
    uint16_t h;
    uint8_t f;
} m3dtx_t;
#define m3d_texturedata_t m3dtx_t
typedef struct {
    M3D_INDEX vertexid;
    M3D_FLOAT weight;
} m3dw_t;
#define m3d_weight_t m3dw_t
typedef struct {
    M3D_INDEX parent;
    char *name;
    M3D_INDEX pos;
    M3D_INDEX ori;
    M3D_INDEX numweight;
    m3dw_t *weight;
    M3D_FLOAT mat4[16];
} m3db_t;
#define m3d_bone_t m3db_t
typedef struct {
    M3D_INDEX boneid[M3D_NUMBONE];
    M3D_FLOAT weight[M3D_NUMBONE];
} m3ds_t;
#define m3d_skin_t m3ds_t
typedef struct {
    M3D_FLOAT x;
    M3D_FLOAT y;
    M3D_FLOAT z;
    M3D_FLOAT w;
    uint32_t color;
    M3D_INDEX skinid;
#ifdef M3D_VERTEXTYPE
    uint8_t type;
#endif
} m3dv_t;
#define m3d_vertex_t m3dv_t
enum {
    m3dpf_color,
    m3dpf_uint8,
    m3dpf_uint16,
    m3dpf_uint32,
    m3dpf_float,
    m3dpf_map
};
typedef struct {
    uint8_t format;
    uint8_t id;
#ifndef M3D_ASCII
#define M3D_PROPERTYDEF(f,i,n) { (f), (i) }
#endif
} m3dpd_t;
enum {
    m3dp_Kd = 0,
    m3dp_Ka,
    m3dp_Ks,
    m3dp_Ns,
    m3dp_Ke,
    m3dp_Tf,
    m3dp_Km,
    m3dp_d,
    m3dp_il,
    m3dp_Pr = 64,
    m3dp_Pm,
    m3dp_Ps,
    m3dp_Ni,
    m3dp_Nt,
    m3dp_map_Kd = 128,
    m3dp_map_Ka,
    m3dp_map_Ks,
    m3dp_map_Ns,
    m3dp_map_Ke,
    m3dp_map_Tf,
    m3dp_map_Km,
    m3dp_map_D,
    m3dp_map_N,
    m3dp_map_Pr = 192,
    m3dp_map_Pm,
    m3dp_map_Ps,
    m3dp_map_Ni,
    m3dp_map_Nt
};
enum {
    m3dp_bump = m3dp_map_Km,
    m3dp_map_il = m3dp_map_N,
    m3dp_refl = m3dp_map_Pm
};
typedef struct {
    uint8_t type;
    union {
        uint32_t color;
        uint32_t num;
        float    fnum;
        M3D_INDEX textureid;
    } value;
} m3dp_t;
#define m3d_property_t m3dp_t
typedef struct {
    char *name;
    uint8_t numprop;
    m3dp_t *prop;
} m3dm_t;
#define m3d_material_t m3dm_t
typedef struct {
    M3D_INDEX materialid;
    M3D_INDEX vertex[3];
    M3D_INDEX normal[3];
    M3D_INDEX texcoord[3];
#ifdef M3D_VERTEXMAX
    M3D_INDEX paramid;
    M3D_INDEX vertmax[3];
#endif
} m3df_t;
#define m3d_face_t m3df_t
typedef struct {
    uint16_t count;
    char *name;
} m3dvi_t;
#define m3d_voxelitem_t m3dvi_t
#define m3d_parameter_t m3dvi_t
typedef struct {
    char *name;
    uint8_t rotation;
    uint16_t voxshape;
    M3D_INDEX materialid;
    uint32_t color;
    M3D_INDEX skinid;
    uint8_t numitem;
    m3dvi_t *item;
} m3dvt_t;
#define m3d_voxeltype_t m3dvt_t
typedef struct {
    char *name;
    int32_t x, y, z;
    uint32_t w, h, d;
    uint8_t uncertain;
    uint8_t groupid;
    M3D_VOXEL *data;
} m3dvx_t;
#define m3d_voxel_t m3dvx_t
enum {
    m3dc_use = 0,
    m3dc_inc,
    m3dc_mesh,
    m3dc_div,
    m3dc_sub,
    m3dc_len,
    m3dc_dist,
    m3dc_degu,
    m3dc_deg,
    m3dc_rangeu,
    m3dc_range,
    m3dc_paru,
    m3dc_parv,
    m3dc_trim,
    m3dc_hole,
    m3dc_scrv,
    m3dc_sp,
    m3dc_bez1,
    m3dc_bsp1,
    m3dc_bez2,
    m3dc_bsp2,
    m3dc_bezun,
    m3dc_bezu,
    m3dc_bezn,
    m3dc_bez,
    m3dc_nurbsun,
    m3dc_nurbsu,
    m3dc_nurbsn,
    m3dc_nurbs,
    m3dc_conn,
    m3dc_line,
    m3dc_polygon,
    m3dc_circle,
    m3dc_cylinder,
    m3dc_shpere,
    m3dc_torus,
    m3dc_cone,
    m3dc_cube
};
enum {
    m3dcp_mi_t = 1,
    m3dcp_hi_t,
    m3dcp_fi_t,
    m3dcp_ti_t,
    m3dcp_vi_t,
    m3dcp_qi_t,
    m3dcp_vc_t,
    m3dcp_i1_t,
    m3dcp_i2_t,
    m3dcp_i4_t,
    m3dcp_va_t
};
#define M3D_CMDMAXARG 8
typedef struct {
#ifndef M3D_ASCII
#define M3D_CMDDEF(t,n,p,a,b,c,d,e,f,g,h) { (p), { (a), (b), (c), (d), (e), (f), (g), (h) } }
#endif
    uint8_t p;
    uint8_t a[M3D_CMDMAXARG];
} m3dcd_t;
typedef struct {
    uint16_t type;
    uint32_t *arg;
} m3dc_t;
#define m3d_shapecommand_t m3dc_t
typedef struct {
    char *name;
    M3D_INDEX group;
    uint32_t numcmd;
    m3dc_t *cmd;
} m3dh_t;
#define m3d_shape_t m3dh_t
typedef struct {
    char *name;
    char *lang;
    char *text;
    uint32_t color;
    M3D_INDEX vertexid;
} m3dl_t;
#define m3d_label_t m3dl_t
typedef struct {
    M3D_INDEX boneid;
    M3D_INDEX pos;
    M3D_INDEX ori;
} m3dtr_t;
#define m3d_transform_t m3dtr_t
typedef struct {
    uint32_t msec;
    M3D_INDEX numtransform;
    m3dtr_t *transform;
} m3dfr_t;
#define m3d_frame_t m3dfr_t
typedef struct {
    char *name;
    uint32_t durationmsec;
    M3D_INDEX numframe;
    m3dfr_t *frame;
} m3da_t;
#define m3d_action_t m3da_t
typedef struct {
    char *name;
    uint8_t *data;
    uint32_t length;
} m3di_t;
#define m3d_inlinedasset_t m3di_t
#define M3D_FLG_FREERAW     (1<<0)
#define M3D_FLG_FREESTR     (1<<1)
#define M3D_FLG_MTLLIB      (1<<2)
#define M3D_FLG_GENNORM     (1<<3)
typedef struct {
    m3dhdr_t *raw;
    char flags;
    signed char errcode;
    char vc_s, vi_s, si_s, ci_s, ti_s, bi_s, nb_s, sk_s, fc_s, hi_s, fi_s, vd_s, vp_s;
    char *name;
    char *license;
    char *author;
    char *desc;
    M3D_FLOAT scale;
    M3D_INDEX numcmap;
    uint32_t *cmap;
    M3D_INDEX numtmap;
    m3dti_t *tmap;
    M3D_INDEX numtexture;
    m3dtx_t *texture;
    M3D_INDEX numbone;
    m3db_t *bone;
    M3D_INDEX numvertex;
    m3dv_t *vertex;
    M3D_INDEX numskin;
    m3ds_t *skin;
    M3D_INDEX nummaterial;
    m3dm_t *material;
#ifdef M3D_VERTEXMAX
    M3D_INDEX numparam;
    m3dvi_t *param;
#endif
    M3D_INDEX numface;
    m3df_t *face;
    M3D_INDEX numvoxtype;
    m3dvt_t *voxtype;
    M3D_INDEX numvoxel;
    m3dvx_t *voxel;
    M3D_INDEX numshape;
    m3dh_t *shape;
    M3D_INDEX numlabel;
    m3dl_t *label;
    M3D_INDEX numaction;
    m3da_t *action;
    M3D_INDEX numinlined;
    m3di_t *inlined;
    M3D_INDEX numextra;
    m3dchunk_t **extra;
    m3di_t preview;
} m3d_t;
#define M3D_EXP_INT8        0
#define M3D_EXP_INT16       1
#define M3D_EXP_FLOAT       2
#define M3D_EXP_DOUBLE      3
#define M3D_EXP_NOCMAP      (1<<0)
#define M3D_EXP_NOMATERIAL  (1<<1)
#define M3D_EXP_NOFACE      (1<<2)
#define M3D_EXP_NONORMAL    (1<<3)
#define M3D_EXP_NOTXTCRD    (1<<4)
#define M3D_EXP_FLIPTXTCRD  (1<<5)
#define M3D_EXP_NORECALC    (1<<6)
#define M3D_EXP_IDOSUCK     (1<<7)
#define M3D_EXP_NOBONE      (1<<8)
#define M3D_EXP_NOACTION    (1<<9)
#define M3D_EXP_INLINE      (1<<10)
#define M3D_EXP_EXTRA       (1<<11)
#define M3D_EXP_NOZLIB      (1<<14)
#define M3D_EXP_ASCII       (1<<15)
#define M3D_EXP_NOVRTMAX    (1<<16)
#define M3D_SUCCESS         0
#define M3D_ERR_ALLOC       -1
#define M3D_ERR_BADFILE     -2
#define M3D_ERR_UNIMPL      -65
#define M3D_ERR_UNKPROP     -66
#define M3D_ERR_UNKMESH     -67
#define M3D_ERR_UNKIMG      -68
#define M3D_ERR_UNKFRAME    -69
#define M3D_ERR_UNKCMD      -70
#define M3D_ERR_UNKVOX      -71
#define M3D_ERR_TRUNC       -72
#define M3D_ERR_CMAP        -73
#define M3D_ERR_TMAP        -74
#define M3D_ERR_VRTS        -75
#define M3D_ERR_BONE        -76
#define M3D_ERR_MTRL        -77
#define M3D_ERR_SHPE        -78
#define M3D_ERR_VOXT        -79
#define M3D_ERR_ISFATAL(x)  ((x) < 0 && (x) > -65)
typedef unsigned char *(*m3dread_t)(char *filename, unsigned int *size);
typedef void (*m3dfree_t)(void *buffer);
typedef int (*m3dtxsc_t)(const char *name, const void *script, uint32_t len, m3dtx_t *output);
typedef int (*m3dprsc_t)(const char *name, const void *script, uint32_t len, m3d_t *model);
#endif
m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d_t *mtllib);
unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size);
void m3d_free(m3d_t *model);
m3dtr_t *m3d_frame(m3d_t *model, M3D_INDEX actionid, M3D_INDEX frameid, m3dtr_t *skeleton);
m3db_t *m3d_pose(m3d_t *model, M3D_INDEX actionid, uint32_t msec);
char *_m3d_safestr(char *in, int morelines);
#ifdef M3D_IMPLEMENTATION
#if !defined(M3D_NOIMPORTER) || defined(M3D_EXPORTER)
static m3dpd_t m3d_propertytypes[] = {
    M3D_PROPERTYDEF(m3dpf_color, m3dp_Kd, "Kd"),
    M3D_PROPERTYDEF(m3dpf_color, m3dp_Ka, "Ka"),
    M3D_PROPERTYDEF(m3dpf_color, m3dp_Ks, "Ks"),
    M3D_PROPERTYDEF(m3dpf_float, m3dp_Ns, "Ns"),
    M3D_PROPERTYDEF(m3dpf_color, m3dp_Ke, "Ke"),
    M3D_PROPERTYDEF(m3dpf_color, m3dp_Tf, "Tf"),
    M3D_PROPERTYDEF(m3dpf_float, m3dp_Km, "Km"),
    M3D_PROPERTYDEF(m3dpf_float, m3dp_d,  "d"),
    M3D_PROPERTYDEF(m3dpf_uint8, m3dp_il, "il"),
    M3D_PROPERTYDEF(m3dpf_float, m3dp_Pr, "Pr"),
    M3D_PROPERTYDEF(m3dpf_float, m3dp_Pm, "Pm"),
    M3D_PROPERTYDEF(m3dpf_float, m3dp_Ps, "Ps"),
    M3D_PROPERTYDEF(m3dpf_float, m3dp_Ni, "Ni"),
    M3D_PROPERTYDEF(m3dpf_float, m3dp_Nt, "Nt"),
    M3D_PROPERTYDEF(m3dpf_map, m3dp_map_Km, "bump"),
    M3D_PROPERTYDEF(m3dpf_map, m3dp_map_N, "map_N"),
    M3D_PROPERTYDEF(m3dpf_map, m3dp_map_Pm, "refl")
};
static m3dcd_t m3d_commandtypes[] = {
    M3D_CMDDEF(m3dc_use,     "use",     1, m3dcp_mi_t, 0, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_inc,     "inc",     3, m3dcp_hi_t, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vi_t, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_mesh,    "mesh",    1, m3dcp_fi_t, m3dcp_fi_t, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vi_t, 0, 0, 0),
    M3D_CMDDEF(m3dc_div,     "div",     1, m3dcp_vc_t, 0, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_sub,     "sub",     2, m3dcp_vc_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_len,     "len",     1, m3dcp_vc_t, 0, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_dist,    "dist",    2, m3dcp_vc_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_degu,    "degu",    1, m3dcp_i1_t, 0, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_deg,     "deg",     2, m3dcp_i1_t, m3dcp_i1_t, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_rangeu,  "rangeu",  1, m3dcp_ti_t, 0, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_range,   "range",   2, m3dcp_ti_t, m3dcp_ti_t, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_paru,    "paru",    2, m3dcp_va_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_parv,    "parv",    2, m3dcp_va_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_trim,    "trim",    3, m3dcp_va_t, m3dcp_ti_t, m3dcp_i2_t, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_hole,    "hole",    3, m3dcp_va_t, m3dcp_ti_t, m3dcp_i2_t, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_scrv,    "scrv",    3, m3dcp_va_t, m3dcp_ti_t, m3dcp_i2_t, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_sp,      "sp",      2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_bez1,    "bez1",    2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_bsp1,    "bsp1",    2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_bez2,    "bez2",    2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_bsp2,    "bsp2",    2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_bezun,   "bezun",   4, m3dcp_va_t, m3dcp_vi_t, m3dcp_ti_t, m3dcp_vi_t, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_bezu,    "bezu",    3, m3dcp_va_t, m3dcp_vi_t, m3dcp_ti_t, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_bezn,    "bezn",    3, m3dcp_va_t, m3dcp_vi_t, m3dcp_vi_t, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_bez,     "bez",     2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_nurbsun, "nurbsun", 4, m3dcp_va_t, m3dcp_vi_t, m3dcp_ti_t, m3dcp_vi_t, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_nurbsu,  "nurbsu",  3, m3dcp_va_t, m3dcp_vi_t, m3dcp_ti_t, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_nurbsn,  "nurbsn",  3, m3dcp_va_t, m3dcp_vi_t, m3dcp_vi_t, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_nurbs,   "nurbs",   2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_conn,    "conn",    6, m3dcp_i2_t, m3dcp_ti_t, m3dcp_i2_t, m3dcp_i2_t, m3dcp_ti_t, m3dcp_i2_t, 0, 0),
    M3D_CMDDEF(m3dc_line,    "line",    2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_polygon, "polygon", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_circle,  "circle",  3, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vc_t, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_cylinder,"cylinder",6, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vc_t, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vc_t, 0, 0),
    M3D_CMDDEF(m3dc_shpere,  "shpere",  2, m3dcp_vi_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_torus,   "torus",   4, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vc_t, m3dcp_vc_t, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_cone,    "cone",    3, m3dcp_vi_t, m3dcp_vi_t, m3dcp_vi_t, 0, 0, 0, 0, 0),
    M3D_CMDDEF(m3dc_cube,    "cube",    3, m3dcp_vi_t, m3dcp_vi_t, m3dcp_vi_t, 0, 0, 0, 0, 0)
};
#endif
#include <stdlib.h>
#include <string.h>
char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header);
#define M3D_CHUNKMAGIC(m, a,b,c,d) ((m)[0]==(a) && (m)[1]==(b) && (m)[2]==(c) && (m)[3]==(d))
#ifndef M3D_NOIMPORTER
M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char *fn)
{
    unsigned int i, len = 0;
    unsigned char *buff = NULL;
    char *fn2;
    if(!fn || !*fn) return M3D_UNDEF;
    for(i = 0; i < model->numtexture; i++)
        if(!strcmp(fn, model->texture[i].name)) return i;
    if(model->inlined) {
        for(i = 0; i < model->numinlined; i++)
            if(!strcmp(fn, model->inlined[i].name)) {
                buff = model->inlined[i].data;
                len = model->inlined[i].length;
                freecb = NULL;
                break;
            }
    }
    if(!buff && readfilecb) {
        i = (unsigned int)strlen(fn);
        if(i < 5 || fn[i - 4] != '.') {
            fn2 = (char*)M3D_MALLOC(i + 5);
            if(!fn2) { model->errcode = M3D_ERR_ALLOC; return M3D_UNDEF; }
            memcpy(fn2, fn, i);
            memcpy(fn2+i, ".png", 5);
            buff = (*readfilecb)(fn2, &len);
            M3D_FREE(fn2);
        }
        if(!buff) {
            buff = (*readfilecb)(fn, &len);
            if(!buff) return M3D_UNDEF;
        }
    }
    i = model->numtexture++;
    model->texture = (m3dtx_t*)M3D_REALLOC(model->texture, model->numtexture * sizeof(m3dtx_t));
    if(!model->texture) {
        if(buff && freecb) (*freecb)(buff);
        model->errcode = M3D_ERR_ALLOC;
        model->numtexture = 0;
        return M3D_UNDEF;
    }
    memset(&model->texture[i], 0, sizeof(m3dtx_t));
    model->texture[i].name = fn;
    if(buff) {
        if(buff[0] == 0x89 && buff[1] == 'P' && buff[2] == 'N' && buff[3] == 'G') {
        } else {
#ifdef M3D_TX_INTERP
            if((model->errcode = M3D_TX_INTERP(fn, buff, len, &model->texture[i])) != M3D_SUCCESS) {
                M3D_LOG("Unable to generate texture");
                M3D_LOG(fn);
            }
#else
            M3D_LOG("Unimplemented interpreter");
            M3D_LOG(fn);
#endif
        }
        if(freecb) (*freecb)(buff);
    }
    if(!model->texture[i].d)
        model->errcode = M3D_ERR_UNKIMG;
    return i;
}
void _m3d_getpr(m3d_t *model, _unused m3dread_t readfilecb, _unused  m3dfree_t freecb, _unused char *fn)
{
#ifdef M3D_PR_INTERP
    unsigned int i, len = 0;
    unsigned char *buff = readfilecb && fn && *fn ? (*readfilecb)(fn, &len) : NULL;
    if(!buff && fn && *fn && model->inlined) {
        for(i = 0; i < model->numinlined; i++)
            if(!strcmp(fn, model->inlined[i].name)) {
                buff = model->inlined[i].data;
                len = model->inlined[i].length;
                freecb = NULL;
                break;
            }
    }
    if(!buff || !len || (model->errcode = M3D_PR_INTERP(fn, buff, len, model)) != M3D_SUCCESS) {
        M3D_LOG("Unable to generate procedural surface");
        M3D_LOG(fn);
        model->errcode = M3D_ERR_UNKIMG;
    }
    if(freecb && buff) (*freecb)(buff);
#else
    (void)readfilecb;
    (void)freecb;
    (void)fn;
    M3D_LOG("Unimplemented interpreter");
    M3D_LOG(fn);
    model->errcode = M3D_ERR_UNIMPL;
#endif
}
#define M3D_GETSTR(x) do{offs=0;data=_m3d_getidx(data,model->si_s,&offs);x=offs?((char*)model->raw+16+offs):NULL;}while(0)
_inline static unsigned char *_m3d_getidx(unsigned char *data, char type, M3D_INDEX *idx)
{
    switch(type) {
        case 1: *idx = data[0] > 253 ? (int8_t)data[0] : data[0]; data++; break;
        case 2: *idx = *((uint16_t*)data) > 65533 ? *((int16_t*)data) : *((uint16_t*)data); data += 2; break;
        case 4: *idx = *((int32_t*)data); data += 4; break;
    }
    return data;
}
#ifndef M3D_NOANIMATION
void _m3d_mul(M3D_FLOAT *r, M3D_FLOAT *a, M3D_FLOAT *b)
{
    r[ 0] = b[ 0] * a[ 0] + b[ 4] * a[ 1] + b[ 8] * a[ 2] + b[12] * a[ 3];
    r[ 1] = b[ 1] * a[ 0] + b[ 5] * a[ 1] + b[ 9] * a[ 2] + b[13] * a[ 3];
    r[ 2] = b[ 2] * a[ 0] + b[ 6] * a[ 1] + b[10] * a[ 2] + b[14] * a[ 3];
    r[ 3] = b[ 3] * a[ 0] + b[ 7] * a[ 1] + b[11] * a[ 2] + b[15] * a[ 3];
    r[ 4] = b[ 0] * a[ 4] + b[ 4] * a[ 5] + b[ 8] * a[ 6] + b[12] * a[ 7];
    r[ 5] = b[ 1] * a[ 4] + b[ 5] * a[ 5] + b[ 9] * a[ 6] + b[13] * a[ 7];
    r[ 6] = b[ 2] * a[ 4] + b[ 6] * a[ 5] + b[10] * a[ 6] + b[14] * a[ 7];
    r[ 7] = b[ 3] * a[ 4] + b[ 7] * a[ 5] + b[11] * a[ 6] + b[15] * a[ 7];
    r[ 8] = b[ 0] * a[ 8] + b[ 4] * a[ 9] + b[ 8] * a[10] + b[12] * a[11];
    r[ 9] = b[ 1] * a[ 8] + b[ 5] * a[ 9] + b[ 9] * a[10] + b[13] * a[11];
    r[10] = b[ 2] * a[ 8] + b[ 6] * a[ 9] + b[10] * a[10] + b[14] * a[11];
    r[11] = b[ 3] * a[ 8] + b[ 7] * a[ 9] + b[11] * a[10] + b[15] * a[11];
    r[12] = b[ 0] * a[12] + b[ 4] * a[13] + b[ 8] * a[14] + b[12] * a[15];
    r[13] = b[ 1] * a[12] + b[ 5] * a[13] + b[ 9] * a[14] + b[13] * a[15];
    r[14] = b[ 2] * a[12] + b[ 6] * a[13] + b[10] * a[14] + b[14] * a[15];
    r[15] = b[ 3] * a[12] + b[ 7] * a[13] + b[11] * a[14] + b[15] * a[15];
}
void _m3d_inv(M3D_FLOAT *m)
{
    M3D_FLOAT r[16];
    M3D_FLOAT det =
          m[ 0]*m[ 5]*m[10]*m[15] - m[ 0]*m[ 5]*m[11]*m[14] + m[ 0]*m[ 6]*m[11]*m[13] - m[ 0]*m[ 6]*m[ 9]*m[15]
        + m[ 0]*m[ 7]*m[ 9]*m[14] - m[ 0]*m[ 7]*m[10]*m[13] - m[ 1]*m[ 6]*m[11]*m[12] + m[ 1]*m[ 6]*m[ 8]*m[15]
        - m[ 1]*m[ 7]*m[ 8]*m[14] + m[ 1]*m[ 7]*m[10]*m[12] - m[ 1]*m[ 4]*m[10]*m[15] + m[ 1]*m[ 4]*m[11]*m[14]
        + m[ 2]*m[ 7]*m[ 8]*m[13] - m[ 2]*m[ 7]*m[ 9]*m[12] + m[ 2]*m[ 4]*m[ 9]*m[15] - m[ 2]*m[ 4]*m[11]*m[13]
        + m[ 2]*m[ 5]*m[11]*m[12] - m[ 2]*m[ 5]*m[ 8]*m[15] - m[ 3]*m[ 4]*m[ 9]*m[14] + m[ 3]*m[ 4]*m[10]*m[13]
        - m[ 3]*m[ 5]*m[10]*m[12] + m[ 3]*m[ 5]*m[ 8]*m[14] - m[ 3]*m[ 6]*m[ 8]*m[13] + m[ 3]*m[ 6]*m[ 9]*m[12];
    if(det == (M3D_FLOAT)0.0 || det == (M3D_FLOAT)-0.0) det = (M3D_FLOAT)1.0; else det = (M3D_FLOAT)1.0 / det;
    r[ 0] = det *(m[ 5]*(m[10]*m[15] - m[11]*m[14]) + m[ 6]*(m[11]*m[13] - m[ 9]*m[15]) + m[ 7]*(m[ 9]*m[14] - m[10]*m[13]));
    r[ 1] = -det*(m[ 1]*(m[10]*m[15] - m[11]*m[14]) + m[ 2]*(m[11]*m[13] - m[ 9]*m[15]) + m[ 3]*(m[ 9]*m[14] - m[10]*m[13]));
    r[ 2] = det *(m[ 1]*(m[ 6]*m[15] - m[ 7]*m[14]) + m[ 2]*(m[ 7]*m[13] - m[ 5]*m[15]) + m[ 3]*(m[ 5]*m[14] - m[ 6]*m[13]));
    r[ 3] = -det*(m[ 1]*(m[ 6]*m[11] - m[ 7]*m[10]) + m[ 2]*(m[ 7]*m[ 9] - m[ 5]*m[11]) + m[ 3]*(m[ 5]*m[10] - m[ 6]*m[ 9]));
    r[ 4] = -det*(m[ 4]*(m[10]*m[15] - m[11]*m[14]) + m[ 6]*(m[11]*m[12] - m[ 8]*m[15]) + m[ 7]*(m[ 8]*m[14] - m[10]*m[12]));
    r[ 5] = det *(m[ 0]*(m[10]*m[15] - m[11]*m[14]) + m[ 2]*(m[11]*m[12] - m[ 8]*m[15]) + m[ 3]*(m[ 8]*m[14] - m[10]*m[12]));
    r[ 6] = -det*(m[ 0]*(m[ 6]*m[15] - m[ 7]*m[14]) + m[ 2]*(m[ 7]*m[12] - m[ 4]*m[15]) + m[ 3]*(m[ 4]*m[14] - m[ 6]*m[12]));
    r[ 7] = det *(m[ 0]*(m[ 6]*m[11] - m[ 7]*m[10]) + m[ 2]*(m[ 7]*m[ 8] - m[ 4]*m[11]) + m[ 3]*(m[ 4]*m[10] - m[ 6]*m[ 8]));
    r[ 8] = det *(m[ 4]*(m[ 9]*m[15] - m[11]*m[13]) + m[ 5]*(m[11]*m[12] - m[ 8]*m[15]) + m[ 7]*(m[ 8]*m[13] - m[ 9]*m[12]));
    r[ 9] = -det*(m[ 0]*(m[ 9]*m[15] - m[11]*m[13]) + m[ 1]*(m[11]*m[12] - m[ 8]*m[15]) + m[ 3]*(m[ 8]*m[13] - m[ 9]*m[12]));
    r[10] = det *(m[ 0]*(m[ 5]*m[15] - m[ 7]*m[13]) + m[ 1]*(m[ 7]*m[12] - m[ 4]*m[15]) + m[ 3]*(m[ 4]*m[13] - m[ 5]*m[12]));
    r[11] = -det*(m[ 0]*(m[ 5]*m[11] - m[ 7]*m[ 9]) + m[ 1]*(m[ 7]*m[ 8] - m[ 4]*m[11]) + m[ 3]*(m[ 4]*m[ 9] - m[ 5]*m[ 8]));
    r[12] = -det*(m[ 4]*(m[ 9]*m[14] - m[10]*m[13]) + m[ 5]*(m[10]*m[12] - m[ 8]*m[14]) + m[ 6]*(m[ 8]*m[13] - m[ 9]*m[12]));
    r[13] = det *(m[ 0]*(m[ 9]*m[14] - m[10]*m[13]) + m[ 1]*(m[10]*m[12] - m[ 8]*m[14]) + m[ 2]*(m[ 8]*m[13] - m[ 9]*m[12]));
    r[14] = -det*(m[ 0]*(m[ 5]*m[14] - m[ 6]*m[13]) + m[ 1]*(m[ 6]*m[12] - m[ 4]*m[14]) + m[ 2]*(m[ 4]*m[13] - m[ 5]*m[12]));
    r[15] = det *(m[ 0]*(m[ 5]*m[10] - m[ 6]*m[ 9]) + m[ 1]*(m[ 6]*m[ 8] - m[ 4]*m[10]) + m[ 2]*(m[ 4]*m[ 9] - m[ 5]*m[ 8]));
    memcpy(m, &r, sizeof(r));
}
void _m3d_mat(M3D_FLOAT *r, m3dv_t *p, m3dv_t *q)
{
    if(q->x == (M3D_FLOAT)0.0 && q->y == (M3D_FLOAT)0.0 && q->z >=(M3D_FLOAT) 0.7071065 && q->z <= (M3D_FLOAT)0.7071075 &&
        q->w == (M3D_FLOAT)0.0) {
        r[ 1] = r[ 2] = r[ 4] = r[ 6] = r[ 8] = r[ 9] = (M3D_FLOAT)0.0;
        r[ 0] = r[ 5] = r[10] = (M3D_FLOAT)-1.0;
    } else {
        r[ 0] = 1 - 2 * (q->y * q->y + q->z * q->z); if(r[ 0]>-M3D_EPSILON && r[ 0]<M3D_EPSILON) r[ 0]=(M3D_FLOAT)0.0;
        r[ 1] = 2 * (q->x * q->y - q->z * q->w);     if(r[ 1]>-M3D_EPSILON && r[ 1]<M3D_EPSILON) r[ 1]=(M3D_FLOAT)0.0;
        r[ 2] = 2 * (q->x * q->z + q->y * q->w);     if(r[ 2]>-M3D_EPSILON && r[ 2]<M3D_EPSILON) r[ 2]=(M3D_FLOAT)0.0;
        r[ 4] = 2 * (q->x * q->y + q->z * q->w);     if(r[ 4]>-M3D_EPSILON && r[ 4]<M3D_EPSILON) r[ 4]=(M3D_FLOAT)0.0;
        r[ 5] = 1 - 2 * (q->x * q->x + q->z * q->z); if(r[ 5]>-M3D_EPSILON && r[ 5]<M3D_EPSILON) r[ 5]=(M3D_FLOAT)0.0;
        r[ 6] = 2 * (q->y * q->z - q->x * q->w);     if(r[ 6]>-M3D_EPSILON && r[ 6]<M3D_EPSILON) r[ 6]=(M3D_FLOAT)0.0;
        r[ 8] = 2 * (q->x * q->z - q->y * q->w);     if(r[ 8]>-M3D_EPSILON && r[ 8]<M3D_EPSILON) r[ 8]=(M3D_FLOAT)0.0;
        r[ 9] = 2 * (q->y * q->z + q->x * q->w);     if(r[ 9]>-M3D_EPSILON && r[ 9]<M3D_EPSILON) r[ 9]=(M3D_FLOAT)0.0;
        r[10] = 1 - 2 * (q->x * q->x + q->y * q->y); if(r[10]>-M3D_EPSILON && r[10]<M3D_EPSILON) r[10]=(M3D_FLOAT)0.0;
    }
    r[ 3] = p->x; r[ 7] = p->y; r[11] = p->z;
    r[12] = 0; r[13] = 0; r[14] = 0; r[15] = 1;
}
#endif
#if !defined(M3D_NOANIMATION) || !defined(M3D_NONORMALS)
static M3D_FLOAT _m3d_rsq(M3D_FLOAT x)
{
#ifdef M3D_DOUBLE
    return ((M3D_FLOAT)15.0/(M3D_FLOAT)8.0) + ((M3D_FLOAT)-5.0/(M3D_FLOAT)4.0)*x + ((M3D_FLOAT)3.0/(M3D_FLOAT)8.0)*x*x;
#else
    float x2 = x * 0.5f;
    uint32_t *i = (uint32_t*)&x;
    *i = (0x5f3759df - (*i >> 1));
    return x * (1.5f - (x2 * x * x));
#endif
}
#endif
m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d_t *mtllib)
{
    unsigned char *end, *chunk, *buff, weights[8];
    unsigned int i, j, k, l, n, am, len = 0, reclen, offs;
#ifndef M3D_NOVOXELS
    int32_t min_x, min_y, min_z, max_x, max_y, max_z, sx, sy, sz, x, y, z;
    M3D_INDEX edge[8], enorm;
#endif
    char *name, *lang;
    float f;
    m3d_t *model;
    M3D_INDEX mi;
#ifdef M3D_VERTEXMAX
    M3D_INDEX pi;
#endif
    M3D_FLOAT w;
    m3dcd_t *cd;
    m3dtx_t *tx;
    m3dh_t *h;
    m3dm_t *m;
    m3da_t *a;
    m3di_t *t;
#ifndef M3D_NONORMALS
    char neednorm = 0;
    m3dv_t *norm = NULL, *v0, *v1, *v2, va, vb;
#endif
#ifndef M3D_NOANIMATION
    M3D_FLOAT r[16];
#endif
#if !defined(M3D_NOWEIGHTS) || !defined(M3D_NOANIMATION)
    m3db_t *b;
#endif
#ifndef M3D_NOWEIGHTS
    m3ds_t *sk;
#endif
    if(!data || (!M3D_CHUNKMAGIC(data, '3','D','M','O')
        )) return NULL;
    model = (m3d_t*)M3D_MALLOC(sizeof(m3d_t));
    if(!model) {
        M3D_LOG("Out of memory");
        return NULL;
    }
    memset(model, 0, sizeof(m3d_t));
    if(mtllib) {
        model->nummaterial = mtllib->nummaterial;
        model->material = mtllib->material;
        model->numtexture = mtllib->numtexture;
        model->texture = mtllib->texture;
        model->flags |= M3D_FLG_MTLLIB;
    }
    len = ((m3dhdr_t*)data)->length - 8;
    data += 8;
    if(M3D_CHUNKMAGIC(data, 'P','R','V','W')) {
        model->preview.length = ((m3dchunk_t*)data)->length;
        model->preview.data = data + sizeof(m3dchunk_t);
        data += model->preview.length;
        len -= model->preview.length;
    }
    if(!M3D_CHUNKMAGIC(data, 'H','E','A','D')) {
        buff = (unsigned char *)stbi_zlib_decode_malloc_guesssize_headerflag((const char*)data, len, 4096, (int*)&len, 1);
        if(!buff || !len || !M3D_CHUNKMAGIC(buff, 'H','E','A','D')) {
            if(buff) M3D_FREE(buff);
            M3D_FREE(model);
            return NULL;
        }
        buff = (unsigned char*)M3D_REALLOC(buff, len);
        model->flags |= M3D_FLG_FREERAW;
        data = buff;
    }
    model->raw = (m3dhdr_t*)data;
    end = data + len;
    data += sizeof(m3dhdr_t);
    M3D_LOG((char*)data);
    model->name = (char*)data;
    for(; data < end && *data; data++) {}; data++;
    model->license = (char*)data;
    for(; data < end && *data; data++) {}; data++;
    model->author = (char*)data;
    for(; data < end && *data; data++) {}; data++;
    model->desc = (char*)data;
    chunk = (unsigned char*)model->raw + model->raw->length;
    model->scale = (M3D_FLOAT)model->raw->scale;
    if(model->scale <= (M3D_FLOAT)0.0) model->scale = (M3D_FLOAT)1.0;
    model->vc_s = 1 << ((model->raw->types >> 0) & 3);
    model->vi_s = 1 << ((model->raw->types >> 2) & 3);
    model->si_s = 1 << ((model->raw->types >> 4) & 3);
    model->ci_s = 1 << ((model->raw->types >> 6) & 3);
    model->ti_s = 1 << ((model->raw->types >> 8) & 3);
    model->bi_s = 1 << ((model->raw->types >>10) & 3);
    model->nb_s = 1 << ((model->raw->types >>12) & 3);
    model->sk_s = 1 << ((model->raw->types >>14) & 3);
    model->fc_s = 1 << ((model->raw->types >>16) & 3);
    model->hi_s = 1 << ((model->raw->types >>18) & 3);
    model->fi_s = 1 << ((model->raw->types >>20) & 3);
    model->vd_s = 1 << ((model->raw->types >>22) & 3);
    model->vp_s = 1 << ((model->raw->types >>24) & 3);
    if(model->ci_s == 8) model->ci_s = 0;
    if(model->ti_s == 8) model->ti_s = 0;
    if(model->bi_s == 8) model->bi_s = 0;
    if(model->sk_s == 8) model->sk_s = 0;
    if(model->fc_s == 8) model->fc_s = 0;
    if(model->hi_s == 8) model->hi_s = 0;
    if(model->fi_s == 8) model->fi_s = 0;
    if(sizeof(M3D_FLOAT) == 4 && model->vc_s > 4) {
        M3D_LOG("Double precision coordinates not supported, truncating to float...");
        model->errcode = M3D_ERR_TRUNC;
    }
    if((sizeof(M3D_INDEX) == 2 && (model->vi_s > 2 || model->si_s > 2 || model->ci_s > 2 || model->ti_s > 2 ||
        model->bi_s > 2 || model->sk_s > 2 || model->fc_s > 2 || model->hi_s > 2 || model->fi_s > 2)) ||
       (sizeof(M3D_VOXEL) < (size_t)model->vp_s && model->vp_s != 8)) {
        M3D_LOG("32 bit indices not supported, unable to load model");
        M3D_FREE(model);
        return NULL;
    }
    if(model->vi_s > 4 || model->si_s > 4 || model->vp_s == 4) {
        M3D_LOG("Invalid index size, unable to load model");
        M3D_FREE(model);
        return NULL;
    }
    if(!M3D_CHUNKMAGIC(end - 4, 'O','M','D','3')) {
        M3D_LOG("Missing end chunk");
        M3D_FREE(model);
        return NULL;
    }
    if(model->nb_s > M3D_NUMBONE) {
        M3D_LOG("Model has more bones per vertex than what importer was configured to support");
        model->errcode = M3D_ERR_TRUNC;
    }
    buff = chunk;
    while(buff < end && !M3D_CHUNKMAGIC(buff, 'O','M','D','3')) {
        data = buff;
        len = ((m3dchunk_t*)data)->length;
        buff += len;
        if(len < sizeof(m3dchunk_t) || buff >= end) {
            M3D_LOG("Invalid chunk size");
            break;
        }
        len -= sizeof(m3dchunk_t) + model->si_s;
        if(M3D_CHUNKMAGIC(data, 'A','S','E','T') && len > 0) {
            M3D_LOG("Inlined asset");
            i = model->numinlined++;
            model->inlined = (m3di_t*)M3D_REALLOC(model->inlined, model->numinlined * sizeof(m3di_t));
            if(!model->inlined) {
memerr:         M3D_LOG("Out of memory");
                model->errcode = M3D_ERR_ALLOC;
                return model;
            }
            data += sizeof(m3dchunk_t);
            t = &model->inlined[i];
            M3D_GETSTR(t->name);
            M3D_LOG(t->name);
            t->data = (uint8_t*)data;
            t->length = len;
        }
    }
    while(chunk < end && !M3D_CHUNKMAGIC(chunk, 'O','M','D','3')) {
        data = chunk;
        len = ((m3dchunk_t*)chunk)->length;
        chunk += len;
        if(len < sizeof(m3dchunk_t) || chunk >= end) {
            M3D_LOG("Invalid chunk size");
            break;
        }
        len -= sizeof(m3dchunk_t);
        if(M3D_CHUNKMAGIC(data, 'C','M','A','P')) {
            M3D_LOG("Color map");
            if(model->cmap) { M3D_LOG("More color map chunks, should be unique"); model->errcode = M3D_ERR_CMAP; continue; }
            if(!model->ci_s) { M3D_LOG("Color map chunk, shouldn't be any"); model->errcode = M3D_ERR_CMAP; continue; }
            model->numcmap = len / sizeof(uint32_t);
            model->cmap = (uint32_t*)(data + sizeof(m3dchunk_t));
        } else
        if(M3D_CHUNKMAGIC(data, 'T','M','A','P')) {
            M3D_LOG("Texture map");
            if(model->tmap) { M3D_LOG("More texture map chunks, should be unique"); model->errcode = M3D_ERR_TMAP; continue; }
            if(!model->ti_s) { M3D_LOG("Texture map chunk, shouldn't be any"); model->errcode = M3D_ERR_TMAP; continue; }
            reclen = model->vc_s + model->vc_s;
            model->numtmap = len / reclen;
            model->tmap = (m3dti_t*)M3D_MALLOC(model->numtmap * sizeof(m3dti_t));
            if(!model->tmap) goto memerr;
            for(i = 0, data += sizeof(m3dchunk_t); data < chunk; i++) {
                switch(model->vc_s) {
                    case 1:
                        model->tmap[i].u = (M3D_FLOAT)((uint8_t)data[0]) / (M3D_FLOAT)255.0;
                        model->tmap[i].v = (M3D_FLOAT)((uint8_t)data[1]) / (M3D_FLOAT)255.0;
                    break;
                    case 2:
                        model->tmap[i].u = (M3D_FLOAT)(*((uint16_t*)(data+0))) / (M3D_FLOAT)65535.0;
                        model->tmap[i].v = (M3D_FLOAT)(*((uint16_t*)(data+2))) / (M3D_FLOAT)65535.0;
                    break;
                    case 4:
                        model->tmap[i].u = (M3D_FLOAT)(*((float*)(data+0)));
                        model->tmap[i].v = (M3D_FLOAT)(*((float*)(data+4)));
                    break;
                    case 8:
                        model->tmap[i].u = (M3D_FLOAT)(*((double*)(data+0)));
                        model->tmap[i].v = (M3D_FLOAT)(*((double*)(data+8)));
                    break;
                }
                data += reclen;
            }
        } else
        if(M3D_CHUNKMAGIC(data, 'V','R','T','S')) {
            M3D_LOG("Vertex list");
            if(model->vertex) { M3D_LOG("More vertex chunks, should be unique"); model->errcode = M3D_ERR_VRTS; continue; }
            if(model->ci_s && model->ci_s < 4 && !model->cmap) model->errcode = M3D_ERR_CMAP;
            reclen = model->ci_s + model->sk_s + 4 * model->vc_s;
            model->numvertex = len / reclen;
            model->vertex = (m3dv_t*)M3D_MALLOC(model->numvertex * sizeof(m3dv_t));
            if(!model->vertex) goto memerr;
            memset(model->vertex, 0, model->numvertex * sizeof(m3dv_t));
            for(i = 0, data += sizeof(m3dchunk_t); data < chunk && i < model->numvertex; i++) {
                switch(model->vc_s) {
                    case 1:
                        model->vertex[i].x = (M3D_FLOAT)((int8_t)data[0]) / (M3D_FLOAT)127.0;
                        model->vertex[i].y = (M3D_FLOAT)((int8_t)data[1]) / (M3D_FLOAT)127.0;
                        model->vertex[i].z = (M3D_FLOAT)((int8_t)data[2]) / (M3D_FLOAT)127.0;
                        model->vertex[i].w = (M3D_FLOAT)((int8_t)data[3]) / (M3D_FLOAT)127.0;
                        data += 4;
                    break;
                    case 2:
                        model->vertex[i].x = (M3D_FLOAT)(*((int16_t*)(data+0))) / (M3D_FLOAT)32767.0;
                        model->vertex[i].y = (M3D_FLOAT)(*((int16_t*)(data+2))) / (M3D_FLOAT)32767.0;
                        model->vertex[i].z = (M3D_FLOAT)(*((int16_t*)(data+4))) / (M3D_FLOAT)32767.0;
                        model->vertex[i].w = (M3D_FLOAT)(*((int16_t*)(data+6))) / (M3D_FLOAT)32767.0;
                        data += 8;
                    break;
                    case 4:
                        model->vertex[i].x = (M3D_FLOAT)(*((float*)(data+0)));
                        model->vertex[i].y = (M3D_FLOAT)(*((float*)(data+4)));
                        model->vertex[i].z = (M3D_FLOAT)(*((float*)(data+8)));
                        model->vertex[i].w = (M3D_FLOAT)(*((float*)(data+12)));
                        data += 16;
                    break;
                    case 8:
                        model->vertex[i].x = (M3D_FLOAT)(*((double*)(data+0)));
                        model->vertex[i].y = (M3D_FLOAT)(*((double*)(data+8)));
                        model->vertex[i].z = (M3D_FLOAT)(*((double*)(data+16)));
                        model->vertex[i].w = (M3D_FLOAT)(*((double*)(data+24)));
                        data += 32;
                    break;
                }
                switch(model->ci_s) {
                    case 1: model->vertex[i].color = model->cmap ? model->cmap[data[0]] : 0; data++; break;
                    case 2: model->vertex[i].color = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break;
                    case 4: model->vertex[i].color = *((uint32_t*)data); data += 4; break;
                }
                model->vertex[i].skinid = M3D_UNDEF;
                data = _m3d_getidx(data, model->sk_s, &model->vertex[i].skinid);
            }
        } else
        if(M3D_CHUNKMAGIC(data, 'B','O','N','E')) {
            M3D_LOG("Skeleton");
            if(model->bone) { M3D_LOG("More bone chunks, should be unique"); model->errcode = M3D_ERR_BONE; continue; }
            if(!model->bi_s) { M3D_LOG("Bone chunk, shouldn't be any"); model->errcode=M3D_ERR_BONE; continue; }
            if(!model->vertex) { M3D_LOG("No vertex chunk before bones"); model->errcode = M3D_ERR_VRTS; break; }
            data += sizeof(m3dchunk_t);
            model->numbone = 0;
            data = _m3d_getidx(data, model->bi_s, &model->numbone);
            if(model->numbone) {
                model->bone = (m3db_t*)M3D_MALLOC(model->numbone * sizeof(m3db_t));
                if(!model->bone) goto memerr;
            }
            model->numskin = 0;
            data = _m3d_getidx(data, model->sk_s, &model->numskin);
            for(i = 0; data < chunk && i < model->numbone; i++) {
                data = _m3d_getidx(data, model->bi_s, &model->bone[i].parent);
                M3D_GETSTR(model->bone[i].name);
                data = _m3d_getidx(data, model->vi_s, &model->bone[i].pos);
                data = _m3d_getidx(data, model->vi_s, &model->bone[i].ori);
                model->bone[i].numweight = 0;
                model->bone[i].weight = NULL;
            }
            if(i != model->numbone) { M3D_LOG("Truncated bone chunk"); model->numbone = i; model->numskin = 0; model->errcode = M3D_ERR_BONE; }
            if(model->numskin) {
                model->skin = (m3ds_t*)M3D_MALLOC(model->numskin * sizeof(m3ds_t));
                if(!model->skin) goto memerr;
                for(i = 0; data < chunk && i < model->numskin; i++) {
                    for(j = 0; j < M3D_NUMBONE; j++) {
                        model->skin[i].boneid[j] = M3D_UNDEF;
                        model->skin[i].weight[j] = (M3D_FLOAT)0.0;
                    }
                    memset(&weights, 0, sizeof(weights));
                    if(model->nb_s == 1) weights[0] = 255;
                    else {
                        memcpy(&weights, data, model->nb_s);
                        data += model->nb_s;
                    }
                    for(j = 0, w = (M3D_FLOAT)0.0; j < (unsigned int)model->nb_s; j++) {
                        if(weights[j]) {
                            if(j >= M3D_NUMBONE)
                                data += model->bi_s;
                            else {
                                model->skin[i].weight[j] = (M3D_FLOAT)(weights[j]) / (M3D_FLOAT)255.0;
                                w += model->skin[i].weight[j];
                                data = _m3d_getidx(data, model->bi_s, &model->skin[i].boneid[j]);
                            }
                        }
                    }
                    if(w != (M3D_FLOAT)1.0 && w != (M3D_FLOAT)0.0) {
                        for(j = 0; j < M3D_NUMBONE; j++)
                            model->skin[i].weight[j] /= w;
                    }
                }
                if(i != model->numskin) { M3D_LOG("Truncated skin in bone chunk"); model->numskin = i; model->errcode = M3D_ERR_BONE; }
            }
        } else
        if(M3D_CHUNKMAGIC(data, 'M','T','R','L')) {
            data += sizeof(m3dchunk_t);
            M3D_GETSTR(name);
            M3D_LOG("Material");
            M3D_LOG(name);
            if(model->ci_s < 4 && !model->numcmap) model->errcode = M3D_ERR_CMAP;
            for(i = 0; i < model->nummaterial; i++)
                if(!strcmp(name, model->material[i].name)) {
                    model->errcode = M3D_ERR_MTRL;
                    M3D_LOG("Multiple definitions for material");
                    M3D_LOG(name);
                    name = NULL;
                    break;
                }
            if(name) {
                i = model->nummaterial++;
                if(model->flags & M3D_FLG_MTLLIB) {
                    m = model->material;
                    model->material = (m3dm_t*)M3D_MALLOC(model->nummaterial * sizeof(m3dm_t));
                    if(!model->material) goto memerr;
                    memcpy(model->material, m, (model->nummaterial - 1) * sizeof(m3dm_t));
                    if(model->texture) {
                        tx = model->texture;
                        model->texture = (m3dtx_t*)M3D_MALLOC(model->numtexture * sizeof(m3dtx_t));
                        if(!model->texture) goto memerr;
                        memcpy(model->texture, tx, model->numtexture * sizeof(m3dm_t));
                    }
                    model->flags &= ~M3D_FLG_MTLLIB;
                } else {
                    model->material = (m3dm_t*)M3D_REALLOC(model->material, model->nummaterial * sizeof(m3dm_t));
                    if(!model->material) goto memerr;
                }
                m = &model->material[i];
                m->numprop = 0;
                m->name = name;
                m->prop = (m3dp_t*)M3D_MALLOC((len / 2) * sizeof(m3dp_t));
                if(!m->prop) goto memerr;
                while(data < chunk) {
                    i = m->numprop++;
                    m->prop[i].type = *data++;
                    m->prop[i].value.num = 0;
                    if(m->prop[i].type >= 128)
                        k = m3dpf_map;
                    else {
                        for(k = 256, j = 0; j < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); j++)
                            if(m->prop[i].type == m3d_propertytypes[j].id) { k = m3d_propertytypes[j].format; break; }
                    }
                    switch(k) {
                        case m3dpf_color:
                            switch(model->ci_s) {
                                case 1: m->prop[i].value.color = model->cmap ? model->cmap[data[0]] : 0; data++; break;
                                case 2: m->prop[i].value.color = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break;
                                case 4: m->prop[i].value.color = *((uint32_t*)data); data += 4; break;
                            }
                        break;
                        case m3dpf_uint8: m->prop[i].value.num = *data++; break;
                        case m3dpf_uint16:m->prop[i].value.num = *((uint16_t*)data); data += 2; break;
                        case m3dpf_uint32:m->prop[i].value.num = *((uint32_t*)data); data += 4; break;
                        case m3dpf_float: m->prop[i].value.fnum = *((float*)data); data += 4; break;
                        case m3dpf_map:
                            M3D_GETSTR(name);
                            m->prop[i].value.textureid = _m3d_gettx(model, readfilecb, freecb, name);
                            if(model->errcode == M3D_ERR_ALLOC) goto memerr;
                            if(m->prop[i].value.textureid == M3D_UNDEF) {
                                M3D_LOG("Texture not found");
                                M3D_LOG(m->name);
                                m->numprop--;
                            }
                        break;
                        default:
                            M3D_LOG("Unknown material property in");
                            M3D_LOG(m->name);
                            model->errcode = M3D_ERR_UNKPROP;
                            data = chunk;
                        break;
                    }
                }
                m->prop = (m3dp_t*)M3D_REALLOC(m->prop, m->numprop * sizeof(m3dp_t));
                if(!m->prop) goto memerr;
            }
        } else
        if(M3D_CHUNKMAGIC(data, 'P','R','O','C')) {
            M3D_GETSTR(name);
            M3D_LOG("Procedural surface");
            M3D_LOG(name);
            _m3d_getpr(model, readfilecb, freecb, name);
        } else
        if(M3D_CHUNKMAGIC(data, 'M','E','S','H')) {
            M3D_LOG("Mesh data");
            if(!model->vertex) { M3D_LOG("No vertex chunk before mesh"); model->errcode = M3D_ERR_VRTS; }
            data += sizeof(m3dchunk_t);
            mi = M3D_UNDEF;
#ifdef M3D_VERTEXMAX
            pi = M3D_UNDEF;
#endif
            am = model->numface;
            while(data < chunk) {
                k = *data++;
                n = k >> 4;
                k &= 15;
                if(!n) {
                    if(!k) {
                        mi = M3D_UNDEF;
                        M3D_GETSTR(name);
                        if(name) {
                            for(j = 0; j < model->nummaterial; j++)
                                if(!strcmp(name, model->material[j].name)) {
                                    mi = (M3D_INDEX)j;
                                    break;
                                }
                            if(mi == M3D_UNDEF) model->errcode = M3D_ERR_MTRL;
                        }
                    } else {
                        M3D_GETSTR(name);
#ifdef M3D_VERTEXMAX
                        pi = M3D_UNDEF;
                        if(name) {
                            for(j = 0; j < model->numparam; j++)
                                if(!strcmp(name, model->param[j].name)) {
                                    pi = (M3D_INDEX)j;
                                    break;
                                }
                            if(pi == M3D_UNDEF) {
                                pi = model->numparam++;
                                model->param = (m3dvi_t*)M3D_REALLOC(model->param, model->numparam * sizeof(m3dvi_t));
                                if(!model->param) goto memerr;
                                model->param[pi].name = name;
                                model->param[pi].count = 0;
                            }
                        }
#endif
                    }
                    continue;
                }
                if(n != 3) { M3D_LOG("Only triangle mesh supported for now"); model->errcode = M3D_ERR_UNKMESH; return model; }
                i = model->numface++;
                if(model->numface > am) {
                    am = model->numface + 4095;
                    model->face = (m3df_t*)M3D_REALLOC(model->face, am * sizeof(m3df_t));
                    if(!model->face) goto memerr;
                }
                memset(&model->face[i], 255, sizeof(m3df_t));
                model->face[i].materialid = mi;
#ifdef M3D_VERTEXMAX
                model->face[i].paramid = pi;
#endif
                for(j = 0; data < chunk && j < n; j++) {
                    data = _m3d_getidx(data, model->vi_s, &model->face[i].vertex[j]);
                    if(k & 1)
                        data = _m3d_getidx(data, model->ti_s, &model->face[i].texcoord[j]);
                    if(k & 2)
                        data = _m3d_getidx(data, model->vi_s, &model->face[i].normal[j]);
#ifndef M3D_NONORMALS
                    if(model->face[i].normal[j] == M3D_UNDEF) neednorm = 1;
#endif
                    if(k & 4)
#ifdef M3D_VERTEXMAX
                        data = _m3d_getidx(data, model->vi_s, &model->face[i].vertmax[j]);
#else
                        data += model->vi_s;
#endif
                }
                if(j != n) { M3D_LOG("Invalid mesh"); model->numface = 0; model->errcode = M3D_ERR_UNKMESH; return model; }
            }
            model->face = (m3df_t*)M3D_REALLOC(model->face, model->numface * sizeof(m3df_t));
        } else
        if(M3D_CHUNKMAGIC(data, 'V','O','X','T')) {
            M3D_LOG("Voxel types list");
            if(model->voxtype) { M3D_LOG("More voxel type chunks, should be unique"); model->errcode = M3D_ERR_VOXT; continue; }
            if(model->ci_s && model->ci_s < 4 && !model->cmap) model->errcode = M3D_ERR_CMAP;
            reclen = model->ci_s + model->si_s + 3 + model->sk_s;
            k = len / reclen;
            model->voxtype = (m3dvt_t*)M3D_MALLOC(k * sizeof(m3dvt_t));
            if(!model->voxtype) goto memerr;
            memset(model->voxtype, 0, k * sizeof(m3dvt_t));
            model->numvoxtype = 0;
            for(i = 0, data += sizeof(m3dchunk_t); data < chunk && i < k; i++) {
                switch(model->ci_s) {
                    case 1: model->voxtype[i].color = model->cmap ? model->cmap[data[0]] : 0; data++; break;
                    case 2: model->voxtype[i].color = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break;
                    case 4: model->voxtype[i].color = *((uint32_t*)data); data += 4; break;
                }
                M3D_GETSTR(name);
                model->voxtype[i].materialid = M3D_UNDEF;
                if(name) {
                    model->voxtype[i].name = name;
                }
                j = *data++;
                model->voxtype[i].rotation = j & 0xBF;
                model->voxtype[i].voxshape = ((j & 0x40) << 2) | *data++;
                model->voxtype[i].numitem = *data++;
                model->voxtype[i].skinid = M3D_UNDEF;
                data = _m3d_getidx(data, model->sk_s, &model->voxtype[i].skinid);
                if(model->voxtype[i].numitem) {
                    model->voxtype[i].item = (m3dvi_t*)M3D_MALLOC(model->voxtype[i].numitem * sizeof(m3dvi_t));
                    if(!model->voxtype[i].item) goto memerr;
                    memset(model->voxtype[i].item, 0, model->voxtype[i].numitem * sizeof(m3dvi_t));
                    for(j = 0; j < model->voxtype[i].numitem; j++) {
                        model->voxtype[i].item[j].count = *data++;
                        model->voxtype[i].item[j].count |= (*data++) << 8;
                        M3D_GETSTR(model->voxtype[i].item[j].name);
                    }
                }
            }
            model->numvoxtype = i;
            if(k != model->numvoxtype) {
                model->voxtype = (m3dvt_t*)M3D_REALLOC(model->voxtype, model->numvoxtype * sizeof(m3dvt_t));
                if(!model->voxtype) goto memerr;
            }
        } else
        if(M3D_CHUNKMAGIC(data, 'V','O','X','D')) {
            data += sizeof(m3dchunk_t);
            M3D_GETSTR(name);
            M3D_LOG("Voxel Data Layer");
            M3D_LOG(name);
            if(model->vd_s > 4 || model->vp_s > 2) { M3D_LOG("No voxel index size"); model->errcode = M3D_ERR_UNKVOX; continue; }
            if(!model->voxtype) { M3D_LOG("No voxel type chunk before voxel data"); model->errcode = M3D_ERR_VOXT; }
            i = model->numvoxel++;
            model->voxel = (m3dvx_t*)M3D_REALLOC(model->voxel, model->numvoxel * sizeof(m3dvx_t));
            if(!model->voxel) goto memerr;
            memset(&model->voxel[i], 0, sizeof(m3dvx_t));
            model->voxel[i].name = name;
            switch(model->vd_s) {
                case 1:
                    model->voxel[i].x = (int32_t)((int8_t)data[0]);
                    model->voxel[i].y = (int32_t)((int8_t)data[1]);
                    model->voxel[i].z = (int32_t)((int8_t)data[2]);
                    model->voxel[i].w = (uint32_t)(data[3]);
                    model->voxel[i].h = (uint32_t)(data[4]);
                    model->voxel[i].d = (uint32_t)(data[5]);
                    data += 6;
                break;
                case 2:
                    model->voxel[i].x = (int32_t)(*((int16_t*)(data+0)));
                    model->voxel[i].y = (int32_t)(*((int16_t*)(data+2)));
                    model->voxel[i].z = (int32_t)(*((int16_t*)(data+4)));
                    model->voxel[i].w = (uint32_t)(*((uint16_t*)(data+6)));
                    model->voxel[i].h = (uint32_t)(*((uint16_t*)(data+8)));
                    model->voxel[i].d = (uint32_t)(*((uint16_t*)(data+10)));
                    data += 12;
                break;
                case 4:
                    model->voxel[i].x = *((int32_t*)(data+0));
                    model->voxel[i].y = *((int32_t*)(data+4));
                    model->voxel[i].z = *((int32_t*)(data+8));
                    model->voxel[i].w = *((uint32_t*)(data+12));
                    model->voxel[i].h = *((uint32_t*)(data+16));
                    model->voxel[i].d = *((uint32_t*)(data+20));
                    data += 24;
                break;
            }
            model->voxel[i].uncertain = *data++;
            model->voxel[i].groupid = *data++;
            k = model->voxel[i].w * model->voxel[i].h * model->voxel[i].d;
            model->voxel[i].data = (M3D_VOXEL*)M3D_MALLOC(k * sizeof(M3D_VOXEL));
            if(!model->voxel[i].data) goto memerr;
            memset(model->voxel[i].data, 0xff, k * sizeof(M3D_VOXEL));
            for(j = 0; data < chunk && j < k;) {
                l = ((*data++) & 0x7F) + 1;
                if(data[-1] & 0x80) {
                    data = _m3d_getidx(data, model->vp_s, &mi);
                    while(l-- && j < k) model->voxel[i].data[j++] = (M3D_VOXEL)mi;
                } else
                    while(l-- && j < k) {
                        data = _m3d_getidx(data, model->vp_s, &mi);
                        model->voxel[i].data[j++] = (M3D_VOXEL)mi;
                    }
            }
        } else
        if(M3D_CHUNKMAGIC(data, 'S','H','P','E')) {
            data += sizeof(m3dchunk_t);
            M3D_GETSTR(name);
            M3D_LOG("Mathematical Shape");
            M3D_LOG(name);
            i = model->numshape++;
            model->shape = (m3dh_t*)M3D_REALLOC(model->shape, model->numshape * sizeof(m3dh_t));
            if(!model->shape) goto memerr;
            h = &model->shape[i];
            h->numcmd = 0;
            h->cmd = NULL;
            h->name = name;
            h->group = M3D_UNDEF;
            data = _m3d_getidx(data, model->bi_s, &h->group);
            if(h->group != M3D_UNDEF && h->group >= model->numbone) {
                M3D_LOG("Unknown bone id as shape group in shape");
                M3D_LOG(name);
                h->group = M3D_UNDEF;
                model->errcode = M3D_ERR_SHPE;
            }
            while(data < chunk) {
                i = h->numcmd++;
                h->cmd = (m3dc_t*)M3D_REALLOC(h->cmd, h->numcmd * sizeof(m3dc_t));
                if(!h->cmd) goto memerr;
                h->cmd[i].type = *data++;
                if(h->cmd[i].type & 0x80) {
                    h->cmd[i].type &= 0x7F;
                    h->cmd[i].type |= (*data++ << 7);
                }
                if(h->cmd[i].type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0]))) {
                    M3D_LOG("Unknown shape command in");
                    M3D_LOG(h->name);
                    model->errcode = M3D_ERR_UNKCMD;
                    break;
                }
                cd = &m3d_commandtypes[h->cmd[i].type];
                h->cmd[i].arg = (uint32_t*)M3D_MALLOC(cd->p * sizeof(uint32_t));
                if(!h->cmd[i].arg) goto memerr;
                memset(h->cmd[i].arg, 0, cd->p * sizeof(uint32_t));
                for(k = n = 0, l = cd->p; k < l; k++)
                    switch(cd->a[((k - n) % (cd->p - n)) + n]) {
                        case m3dcp_mi_t:
                            h->cmd[i].arg[k] = M3D_NOTDEFINED;
                            M3D_GETSTR(name);
                            if(name) {
                                for(n = 0; n < model->nummaterial; n++)
                                    if(!strcmp(name, model->material[n].name)) {
                                        h->cmd[i].arg[k] = n;
                                        break;
                                    }
                                if(h->cmd[i].arg[k] == M3D_NOTDEFINED) model->errcode = M3D_ERR_MTRL;
                            }
                        break;
                        case m3dcp_vc_t:
                            f = 0.0f;
                            switch(model->vc_s) {
                                case 1: f = (float)((int8_t)data[0]) / 127; break;
                                case 2: f = (float)(*((int16_t*)(data+0))) / 32767; break;
                                case 4: f = (float)(*((float*)(data+0))); break;
                                case 8: f = (float)(*((double*)(data+0))); break;
                            }
                            memcpy(&h->cmd[i].arg[k], &f, 4);
                            data += model->vc_s;
                        break;
                        case m3dcp_hi_t: data = _m3d_getidx(data, model->hi_s, &h->cmd[i].arg[k]); break;
                        case m3dcp_fi_t: data = _m3d_getidx(data, model->fi_s, &h->cmd[i].arg[k]); break;
                        case m3dcp_ti_t: data = _m3d_getidx(data, model->ti_s, &h->cmd[i].arg[k]); break;
                        case m3dcp_qi_t:
                        case m3dcp_vi_t: data = _m3d_getidx(data, model->vi_s, &h->cmd[i].arg[k]); break;
                        case m3dcp_i1_t: data = _m3d_getidx(data, 1, &h->cmd[i].arg[k]); break;
                        case m3dcp_i2_t: data = _m3d_getidx(data, 2, &h->cmd[i].arg[k]); break;
                        case m3dcp_i4_t: data = _m3d_getidx(data, 4, &h->cmd[i].arg[k]); break;
                        case m3dcp_va_t: data = _m3d_getidx(data, 4, &h->cmd[i].arg[k]);
                            n = k + 1; l += (h->cmd[i].arg[k] - 1) * (cd->p - k - 1);
                            h->cmd[i].arg = (uint32_t*)M3D_REALLOC(h->cmd[i].arg, l * sizeof(uint32_t));
                            if(!h->cmd[i].arg) goto memerr;
                            memset(&h->cmd[i].arg[k + 1], 0, (l - k - 1) * sizeof(uint32_t));
                        break;
                    }
            }
        } else
        if(M3D_CHUNKMAGIC(data, 'L','B','L','S')) {
            data += sizeof(m3dchunk_t);
            M3D_GETSTR(name);
            M3D_GETSTR(lang);
            M3D_LOG("Label list");
            if(name) { M3D_LOG(name); }
            if(lang) { M3D_LOG(lang); }
            if(model->ci_s && model->ci_s < 4 && !model->cmap) model->errcode = M3D_ERR_CMAP;
            k = 0;
            switch(model->ci_s) {
                case 1: k = model->cmap ? model->cmap[data[0]] : 0; data++; break;
                case 2: k = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break;
                case 4: k = *((uint32_t*)data); data += 4; break;
            }
            reclen = model->vi_s + model->si_s;
            i = model->numlabel; model->numlabel += len / reclen;
            model->label = (m3dl_t*)M3D_REALLOC(model->label, model->numlabel * sizeof(m3dl_t));
            if(!model->label) goto memerr;
            memset(&model->label[i], 0, (model->numlabel - i) * sizeof(m3dl_t));
            for(; data < chunk && i < model->numlabel; i++) {
                model->label[i].name = name;
                model->label[i].lang = lang;
                model->label[i].color = k;
                data = _m3d_getidx(data, model->vi_s, &model->label[i].vertexid);
                M3D_GETSTR(model->label[i].text);
            }
        } else
        if(M3D_CHUNKMAGIC(data, 'A','C','T','N')) {
            M3D_LOG("Action");
            i = model->numaction++;
            model->action = (m3da_t*)M3D_REALLOC(model->action, model->numaction * sizeof(m3da_t));
            if(!model->action) goto memerr;
            a = &model->action[i];
            data += sizeof(m3dchunk_t);
            M3D_GETSTR(a->name);
            M3D_LOG(a->name);
            a->numframe = *((uint16_t*)data); data += 2;
            if(a->numframe < 1) {
                model->numaction--;
            } else {
                a->durationmsec = *((uint32_t*)data); data += 4;
                a->frame = (m3dfr_t*)M3D_MALLOC(a->numframe * sizeof(m3dfr_t));
                if(!a->frame) goto memerr;
                for(i = 0; data < chunk && i < a->numframe; i++) {
                    a->frame[i].msec = *((uint32_t*)data); data += 4;
                    a->frame[i].numtransform = 0; a->frame[i].transform = NULL;
                    data = _m3d_getidx(data, model->fc_s, &a->frame[i].numtransform);
                    if(a->frame[i].numtransform > 0) {
                        a->frame[i].transform = (m3dtr_t*)M3D_MALLOC(a->frame[i].numtransform * sizeof(m3dtr_t));
                        for(j = 0; j < a->frame[i].numtransform; j++) {
                            data = _m3d_getidx(data, model->bi_s, &a->frame[i].transform[j].boneid);
                            data = _m3d_getidx(data, model->vi_s, &a->frame[i].transform[j].pos);
                            data = _m3d_getidx(data, model->vi_s, &a->frame[i].transform[j].ori);
                        }
                    }
                }
            }
        } else {
            i = model->numextra++;
            model->extra = (m3dchunk_t**)M3D_REALLOC(model->extra, model->numextra * sizeof(m3dchunk_t*));
            if(!model->extra) goto memerr;
            model->extra[i] = (m3dchunk_t*)data;
        }
    }
    if(model) {
        M3D_LOG("Post-process");
#ifndef M3D_NOVOXELS
        if(model->numvoxel && model->voxel) {
            M3D_LOG("Converting voxels into vertices and mesh");
            enorm = model->numvertex; model->numvertex += 6;
            model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t));
            if(!model->vertex) goto memerr;
            memset(&model->vertex[enorm], 0, 6 * sizeof(m3dv_t));
            for(l = 0; l < 6; l++)
                model->vertex[enorm+l].skinid = M3D_UNDEF;
            model->vertex[enorm+0].y = (M3D_FLOAT)-1.0;
            model->vertex[enorm+1].z = (M3D_FLOAT)-1.0;
            model->vertex[enorm+2].x = (M3D_FLOAT)-1.0;
            model->vertex[enorm+3].y = (M3D_FLOAT)1.0;
            model->vertex[enorm+4].z = (M3D_FLOAT)1.0;
            model->vertex[enorm+5].x = (M3D_FLOAT)1.0;
            min_x = min_y = min_z = 2147483647L;
            max_x = max_y = max_z = -2147483647L;
            for(i = 0; i < model->numvoxel; i++) {
                if(model->voxel[i].x + (int32_t)model->voxel[i].w > max_x) max_x = model->voxel[i].x + (int32_t)model->voxel[i].w;
                if(model->voxel[i].x < min_x) min_x = model->voxel[i].x;
                if(model->voxel[i].y + (int32_t)model->voxel[i].h > max_y) max_y = model->voxel[i].y + (int32_t)model->voxel[i].h;
                if(model->voxel[i].y < min_y) min_y = model->voxel[i].y;
                if(model->voxel[i].z + (int32_t)model->voxel[i].d > max_z) max_z = model->voxel[i].z + (int32_t)model->voxel[i].d;
                if(model->voxel[i].z < min_z) min_z = model->voxel[i].z;
            }
            i = (-min_x > max_x ? -min_x : max_x);
            j = (-min_y > max_y ? -min_y : max_y);
            k = (-min_z > max_z ? -min_z : max_z);
            if(j > i) i = j;
            if(k > i) i = k;
            if(i <= 1) i = 1;
            w = (M3D_FLOAT)1.0 / (M3D_FLOAT)i;
            if(i >= 254) model->vc_s = 2;
            if(i >= 65534) model->vc_s = 4;
            for(i = 0; i < model->numvoxel; i++) {
                sx = model->voxel[i].w; sz = model->voxel[i].d; sy = model->voxel[i].h;
                for(y = 0, j = 0; y < sy; y++)
                    for(z = 0; z < sz; z++)
                        for(x = 0; x < sx; x++, j++)
                            if(model->voxel[i].data[j] < model->numvoxtype) {
                                k = 0;
                                k = n = am = 0;
                                if(!y || model->voxel[i].data[j - sx*sz] >= model->numvoxtype) { n++; am |= 1; k |= 1|2|4|8; }
                                if(!z || model->voxel[i].data[j - sx] >= model->numvoxtype) { n++; am |= 2; k |= 1|2|16|32; }
                                if(!x || model->voxel[i].data[j - 1] >= model->numvoxtype) { n++; am |= 4; k |= 1|4|16|64; }
                                if(y == sy-1 || model->voxel[i].data[j + sx*sz] >= model->numvoxtype) { n++; am |= 8; k |= 16|32|64|128; }
                                if(z == sz-1 || model->voxel[i].data[j + sx] >= model->numvoxtype) { n++; am |= 16; k |= 4|8|64|128; }
                                if(x == sx-1 || model->voxel[i].data[j + 1] >= model->numvoxtype) { n++; am |= 32; k |= 2|8|32|128; }
                                if(k) {
                                    memset(edge, 255, sizeof(edge));
                                    for(l = 0, len = 1, reclen = model->numvertex; l < 8; l++, len <<= 1)
                                        if(k & len) edge[l] = model->numvertex++;
                                    model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t));
                                    if(!model->vertex) goto memerr;
                                    memset(&model->vertex[reclen], 0, (model->numvertex-reclen) * sizeof(m3dv_t));
                                    for(l = reclen; l < model->numvertex; l++) {
                                        model->vertex[l].skinid = model->voxtype[model->voxel[i].data[j]].skinid;
                                        model->vertex[l].color = model->voxtype[model->voxel[i].data[j]].color;
                                    }
                                    l = reclen;
                                    if(k & 1) {
                                        model->vertex[l].x = (model->voxel[i].x + x) * w;
                                        model->vertex[l].y = (model->voxel[i].y + y) * w;
                                        model->vertex[l].z = (model->voxel[i].z + z) * w;
                                        l++;
                                    }
                                    if(k & 2) {
                                        model->vertex[l].x = (model->voxel[i].x + x + 1) * w;
                                        model->vertex[l].y = (model->voxel[i].y + y) * w;
                                        model->vertex[l].z = (model->voxel[i].z + z) * w;
                                        l++;
                                    }
                                    if(k & 4) {
                                        model->vertex[l].x = (model->voxel[i].x + x) * w;
                                        model->vertex[l].y = (model->voxel[i].y + y) * w;
                                        model->vertex[l].z = (model->voxel[i].z + z + 1) * w;
                                        l++;
                                    }
                                    if(k & 8) {
                                        model->vertex[l].x = (model->voxel[i].x + x + 1) * w;
                                        model->vertex[l].y = (model->voxel[i].y + y) * w;
                                        model->vertex[l].z = (model->voxel[i].z + z + 1) * w;
                                        l++;
                                    }
                                    if(k & 16) {
                                        model->vertex[l].x = (model->voxel[i].x + x) * w;
                                        model->vertex[l].y = (model->voxel[i].y + y + 1) * w;
                                        model->vertex[l].z = (model->voxel[i].z + z) * w;
                                        l++;
                                    }
                                    if(k & 32) {
                                        model->vertex[l].x = (model->voxel[i].x + x + 1) * w;
                                        model->vertex[l].y = (model->voxel[i].y + y + 1) * w;
                                        model->vertex[l].z = (model->voxel[i].z + z) * w;
                                        l++;
                                    }
                                    if(k & 64) {
                                        model->vertex[l].x = (model->voxel[i].x + x) * w;
                                        model->vertex[l].y = (model->voxel[i].y + y + 1) * w;
                                        model->vertex[l].z = (model->voxel[i].z + z + 1) * w;
                                        l++;
                                    }
                                    if(k & 128) {
                                        model->vertex[l].x = (model->voxel[i].x + x + 1) * w;
                                        model->vertex[l].y = (model->voxel[i].y + y + 1) * w;
                                        model->vertex[l].z = (model->voxel[i].z + z + 1) * w;
                                        l++;
                                    }
                                    n <<= 1;
                                    l = model->numface; model->numface += n;
                                    model->face = (m3df_t*)M3D_REALLOC(model->face, model->numface * sizeof(m3df_t));
                                    if(!model->face) goto memerr;
                                    memset(&model->face[l], 255, n * sizeof(m3df_t));
                                    for(reclen = l; reclen < model->numface; reclen++)
                                        model->face[reclen].materialid = model->voxtype[model->voxel[i].data[j]].materialid;
                                    if(am & 1) {
                                        model->face[l].vertex[0] = edge[0];   model->face[l].vertex[1] = edge[1];   model->face[l].vertex[2] = edge[2];
                                        model->face[l+1].vertex[0] = edge[2]; model->face[l+1].vertex[1] = edge[1]; model->face[l+1].vertex[2] = edge[3];
                                        model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] =
                                        model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm;
                                        l += 2;
                                    }
                                    if(am & 2) {
                                        model->face[l].vertex[0] = edge[0];   model->face[l].vertex[1] = edge[4];   model->face[l].vertex[2] = edge[1];
                                        model->face[l+1].vertex[0] = edge[1]; model->face[l+1].vertex[1] = edge[4]; model->face[l+1].vertex[2] = edge[5];
                                        model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] =
                                        model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+1;
                                        l += 2;
                                    }
                                    if(am & 4) {
                                        model->face[l].vertex[0] = edge[0];   model->face[l].vertex[1] = edge[2];   model->face[l].vertex[2] = edge[4];
                                        model->face[l+1].vertex[0] = edge[2]; model->face[l+1].vertex[1] = edge[6]; model->face[l+1].vertex[2] = edge[4];
                                        model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] =
                                        model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+2;
                                        l += 2;
                                    }
                                    if(am & 8) {
                                        model->face[l].vertex[0] = edge[4];   model->face[l].vertex[1] = edge[6];   model->face[l].vertex[2] = edge[5];
                                        model->face[l+1].vertex[0] = edge[5]; model->face[l+1].vertex[1] = edge[6]; model->face[l+1].vertex[2] = edge[7];
                                        model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] =
                                        model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+3;
                                        l += 2;
                                    }
                                    if(am & 16) {
                                        model->face[l].vertex[0] = edge[2];   model->face[l].vertex[1] = edge[7];   model->face[l].vertex[2] = edge[6];
                                        model->face[l+1].vertex[0] = edge[7]; model->face[l+1].vertex[1] = edge[2]; model->face[l+1].vertex[2] = edge[3];
                                        model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] =
                                        model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+4;
                                        l += 2;
                                    }
                                    if(am & 32) {
                                        model->face[l].vertex[0] = edge[1];   model->face[l].vertex[1] = edge[5];   model->face[l].vertex[2] = edge[7];
                                        model->face[l+1].vertex[0] = edge[1]; model->face[l+1].vertex[1] = edge[7]; model->face[l+1].vertex[2] = edge[3];
                                        model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] =
                                        model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+5;
                                        l += 2;
                                    }
                                }
                            }
            }
        }
#endif
#ifndef M3D_NONORMALS
        if(model->numface && model->face && neednorm) {
            norm = (m3dv_t*)M3D_MALLOC(model->numface * sizeof(m3dv_t));
            if(!norm) goto memerr;
            for(i = 0, n = model->numvertex; i < model->numface; i++)
                if(model->face[i].normal[0] == M3D_UNDEF) {
                    v0 = &model->vertex[model->face[i].vertex[0]];
                    v1 = &model->vertex[model->face[i].vertex[1]];
                    v2 = &model->vertex[model->face[i].vertex[2]];
                    va.x = v1->x - v0->x; va.y = v1->y - v0->y; va.z = v1->z - v0->z;
                    vb.x = v2->x - v0->x; vb.y = v2->y - v0->y; vb.z = v2->z - v0->z;
                    v0 = &norm[i];
                    v0->x = (va.y * vb.z) - (va.z * vb.y);
                    v0->y = (va.z * vb.x) - (va.x * vb.z);
                    v0->z = (va.x * vb.y) - (va.y * vb.x);
                    w = _m3d_rsq((v0->x * v0->x) + (v0->y * v0->y) + (v0->z * v0->z));
                    v0->x *= w; v0->y *= w; v0->z *= w;
                    model->face[i].normal[0] = model->face[i].vertex[0] + n;
                    model->face[i].normal[1] = model->face[i].vertex[1] + n;
                    model->face[i].normal[2] = model->face[i].vertex[2] + n;
                }
            M3D_LOG("Generating normals");
            model->flags |= M3D_FLG_GENNORM;
            model->numvertex <<= 1;
            model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t));
            if(!model->vertex) goto memerr;
            memset(&model->vertex[n], 0, n * sizeof(m3dv_t));
            for(i = 0; i < model->numface; i++)
                for(j = 0; j < 3; j++) {
                    v0 = &model->vertex[model->face[i].vertex[j] + n];
                    v0->x += norm[i].x;
                    v0->y += norm[i].y;
                    v0->z += norm[i].z;
                }
            for(i = 0, v0 = &model->vertex[n]; i < n; i++, v0++) {
                w = _m3d_rsq((v0->x * v0->x) + (v0->y * v0->y) + (v0->z * v0->z));
                v0->x *= w; v0->y *= w; v0->z *= w;
                v0->skinid = M3D_UNDEF;
            }
            M3D_FREE(norm);
        }
#endif
        if(model->numbone && model->bone && model->numskin && model->skin && model->numvertex && model->vertex) {
#ifndef M3D_NOWEIGHTS
            M3D_LOG("Generating weight cross-reference");
            for(i = 0; i < model->numvertex; i++) {
                if(model->vertex[i].skinid < model->numskin) {
                    sk = &model->skin[model->vertex[i].skinid];
                    w = (M3D_FLOAT)0.0;
                    for(j = 0; j < M3D_NUMBONE && sk->boneid[j] != M3D_UNDEF && sk->weight[j] > (M3D_FLOAT)0.0; j++)
                        w += sk->weight[j];
                    for(j = 0; j < M3D_NUMBONE && sk->boneid[j] != M3D_UNDEF && sk->weight[j] > (M3D_FLOAT)0.0; j++) {
                        sk->weight[j] /= w;
                        b = &model->bone[sk->boneid[j]];
                        k = b->numweight++;
                        b->weight = (m3dw_t*)M3D_REALLOC(b->weight, b->numweight * sizeof(m3da_t));
                        if(!b->weight) goto memerr;
                        b->weight[k].vertexid = i;
                        b->weight[k].weight = sk->weight[j];
                    }
                }
            }
#endif
#ifndef M3D_NOANIMATION
            M3D_LOG("Calculating bone transformation matrices");
            for(i = 0; i < model->numbone; i++) {
                b = &model->bone[i];
                if(model->bone[i].parent == M3D_UNDEF) {
                    _m3d_mat((M3D_FLOAT*)&b->mat4, &model->vertex[b->pos], &model->vertex[b->ori]);
                } else {
                    _m3d_mat((M3D_FLOAT*)&r, &model->vertex[b->pos], &model->vertex[b->ori]);
                    _m3d_mul((M3D_FLOAT*)&b->mat4, (M3D_FLOAT*)&model->bone[b->parent].mat4, (M3D_FLOAT*)&r);
                }
            }
            for(i = 0; i < model->numbone; i++)
                _m3d_inv((M3D_FLOAT*)&model->bone[i].mat4);
#endif
        }
    }
    return model;
}
m3dtr_t *m3d_frame(m3d_t *model, M3D_INDEX actionid, M3D_INDEX frameid, m3dtr_t *skeleton)
{
    unsigned int i;
    M3D_INDEX s = frameid;
    m3dfr_t *fr;
    if(!model || !model->numbone || !model->bone || (actionid != M3D_UNDEF && (!model->action ||
        actionid >= model->numaction || frameid >= model->action[actionid].numframe))) {
            model->errcode = M3D_ERR_UNKFRAME;
            return skeleton;
    }
    model->errcode = M3D_SUCCESS;
    if(!skeleton) {
        skeleton = (m3dtr_t*)M3D_MALLOC(model->numbone * sizeof(m3dtr_t));
        if(!skeleton) {
            model->errcode = M3D_ERR_ALLOC;
            return NULL;
        }
        goto gen;
    }
    if(actionid == M3D_UNDEF || !frameid) {
gen:    s = 0;
        for(i = 0; i < model->numbone; i++) {
            skeleton[i].boneid = i;
            skeleton[i].pos = model->bone[i].pos;
            skeleton[i].ori = model->bone[i].ori;
        }
    }
    if(actionid < model->numaction && (frameid || !model->action[actionid].frame[0].msec)) {
        for(; s <= frameid; s++) {
            fr = &model->action[actionid].frame[s];
            for(i = 0; i < fr->numtransform; i++) {
                skeleton[fr->transform[i].boneid].pos = fr->transform[i].pos;
                skeleton[fr->transform[i].boneid].ori = fr->transform[i].ori;
            }
        }
    }
    return skeleton;
}
#ifndef M3D_NOANIMATION
m3db_t *m3d_pose(m3d_t *model, M3D_INDEX actionid, uint32_t msec)
{
    unsigned int i, j, l;
    M3D_FLOAT r[16], t, c, d, s;
    m3db_t *ret;
    m3dv_t *v, *p, *f;
    m3dtr_t *tmp;
    m3dfr_t *fr;
    if(!model || !model->numbone || !model->bone) {
        model->errcode = M3D_ERR_UNKFRAME;
        return NULL;
    }
    ret = (m3db_t*)M3D_MALLOC(model->numbone * sizeof(m3db_t));
    if(!ret) {
        model->errcode = M3D_ERR_ALLOC;
        return NULL;
    }
    memcpy(ret, model->bone, model->numbone * sizeof(m3db_t));
    for(i = 0; i < model->numbone; i++)
        _m3d_inv((M3D_FLOAT*)&ret[i].mat4);
    if(!model->action || actionid >= model->numaction) {
        model->errcode = M3D_ERR_UNKFRAME;
        return ret;
    }
    msec %= model->action[actionid].durationmsec;
    model->errcode = M3D_SUCCESS;
    fr = &model->action[actionid].frame[0];
    for(j = l = 0; j < model->action[actionid].numframe && model->action[actionid].frame[j].msec <= msec; j++) {
        fr = &model->action[actionid].frame[j];
        l = fr->msec;
        for(i = 0; i < fr->numtransform; i++) {
            ret[fr->transform[i].boneid].pos = fr->transform[i].pos;
            ret[fr->transform[i].boneid].ori = fr->transform[i].ori;
        }
    }
    if(l != msec) {
        model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, (model->numvertex + 2 * model->numbone) * sizeof(m3dv_t));
        if(!model->vertex) {
            free(ret);
            model->errcode = M3D_ERR_ALLOC;
            return NULL;
        }
        tmp = (m3dtr_t*)M3D_MALLOC(model->numbone * sizeof(m3dtr_t));
        if(tmp) {
            for(i = 0; i < model->numbone; i++) {
                tmp[i].pos = ret[i].pos;
                tmp[i].ori = ret[i].ori;
            }
            fr = &model->action[actionid].frame[j % model->action[actionid].numframe];
            t = l >= fr->msec ? (M3D_FLOAT)1.0 : (M3D_FLOAT)(msec - l) / (M3D_FLOAT)(fr->msec - l);
            for(i = 0; i < fr->numtransform; i++) {
                tmp[fr->transform[i].boneid].pos = fr->transform[i].pos;
                tmp[fr->transform[i].boneid].ori = fr->transform[i].ori;
            }
            for(i = 0, j = model->numvertex; i < model->numbone; i++) {
                if(ret[i].pos != tmp[i].pos) {
                    p = &model->vertex[ret[i].pos];
                    f = &model->vertex[tmp[i].pos];
                    v = &model->vertex[j];
                    v->x = p->x + t * (f->x - p->x);
                    v->y = p->y + t * (f->y - p->y);
                    v->z = p->z + t * (f->z - p->z);
                    ret[i].pos = j++;
                }
                if(ret[i].ori != tmp[i].ori) {
                    p = &model->vertex[ret[i].ori];
                    f = &model->vertex[tmp[i].ori];
                    v = &model->vertex[j];
                    d = p->w * f->w + p->x * f->x + p->y * f->y + p->z * f->z;
                    if(d < 0) { d = -d; s = (M3D_FLOAT)-1.0; } else s = (M3D_FLOAT)1.0;
#if 0
                    a = (M3D_FLOAT)1.0 - t; b = t;
                    if(d < (M3D_FLOAT)0.999999) { c = acosf(d); b = 1 / sinf(c); a = sinf(a * c) * b; b *= sinf(t * c) * s; }
                    v->x = p->x * a + f->x * b;
                    v->y = p->y * a + f->y * b;
                    v->z = p->z * a + f->z * b;
                    v->w = p->w * a + f->w * b;
#else
                    c = t - (M3D_FLOAT)0.5; t += t * c * (t - (M3D_FLOAT)1.0) * (((M3D_FLOAT)1.0904 + d * ((M3D_FLOAT)-3.2452 +
                        d * ((M3D_FLOAT)3.55645 - d * (M3D_FLOAT)1.43519))) * c * c + ((M3D_FLOAT)0.848013 + d *
                        ((M3D_FLOAT)-1.06021 + d * (M3D_FLOAT)0.215638)));
                    v->x = p->x + t * (s * f->x - p->x);
                    v->y = p->y + t * (s * f->y - p->y);
                    v->z = p->z + t * (s * f->z - p->z);
                    v->w = p->w + t * (s * f->w - p->w);
                    d = _m3d_rsq(v->w * v->w + v->x * v->x + v->y * v->y + v->z * v->z);
                    v->x *= d; v->y *= d; v->z *= d; v->w *= d;
#endif
                    ret[i].ori = j++;
                }
            }
            M3D_FREE(tmp);
        }
    }
    for(i = 0; i < model->numbone; i++) {
        if(ret[i].parent == M3D_UNDEF) {
            _m3d_mat((M3D_FLOAT*)&ret[i].mat4, &model->vertex[ret[i].pos], &model->vertex[ret[i].ori]);
        } else {
            _m3d_mat((M3D_FLOAT*)&r, &model->vertex[ret[i].pos], &model->vertex[ret[i].ori]);
            _m3d_mul((M3D_FLOAT*)&ret[i].mat4, (M3D_FLOAT*)&ret[ret[i].parent].mat4, (M3D_FLOAT*)&r);
        }
    }
    return ret;
}
#endif
#endif
#if !defined(M3D_NODUP) && (!defined(M3D_NOIMPORTER) || defined(M3D_EXPORTER))
void m3d_free(m3d_t *model)
{
    unsigned int i, j;
    if(!model) return;
    if(model->flags & M3D_FLG_FREERAW) M3D_FREE(model->raw);
    if(model->tmap) M3D_FREE(model->tmap);
    if(model->bone) {
        for(i = 0; i < model->numbone; i++)
            if(model->bone[i].weight)
                M3D_FREE(model->bone[i].weight);
        M3D_FREE(model->bone);
    }
    if(model->skin) M3D_FREE(model->skin);
    if(model->vertex) M3D_FREE(model->vertex);
    if(model->face) M3D_FREE(model->face);
    if(model->voxtype) {
        for(i = 0; i < model->numvoxtype; i++)
            if(model->voxtype[i].item)
                M3D_FREE(model->voxtype[i].item);
        M3D_FREE(model->voxtype);
    }
    if(model->voxel) {
        for(i = 0; i < model->numvoxel; i++)
            if(model->voxel[i].data)
                M3D_FREE(model->voxel[i].data);
        M3D_FREE(model->voxel);
    }
    if(model->shape) {
        for(i = 0; i < model->numshape; i++) {
            if(model->shape[i].cmd) {
                for(j = 0; j < model->shape[i].numcmd; j++)
                    if(model->shape[i].cmd[j].arg) M3D_FREE(model->shape[i].cmd[j].arg);
                M3D_FREE(model->shape[i].cmd);
            }
        }
        M3D_FREE(model->shape);
    }
    if(model->material && !(model->flags & M3D_FLG_MTLLIB)) {
        for(i = 0; i < model->nummaterial; i++)
            if(model->material[i].prop) M3D_FREE(model->material[i].prop);
        M3D_FREE(model->material);
    }
    if(model->texture) {
        for(i = 0; i < model->numtexture; i++)
            if(model->texture[i].d) M3D_FREE(model->texture[i].d);
        M3D_FREE(model->texture);
    }
    if(model->action) {
        for(i = 0; i < model->numaction; i++) {
            if(model->action[i].frame) {
                for(j = 0; j < model->action[i].numframe; j++)
                    if(model->action[i].frame[j].transform) M3D_FREE(model->action[i].frame[j].transform);
                M3D_FREE(model->action[i].frame);
            }
        }
        M3D_FREE(model->action);
    }
    if(model->label) M3D_FREE(model->label);
    if(model->inlined) M3D_FREE(model->inlined);
    if(model->extra) M3D_FREE(model->extra);
    free(model);
}
#endif
#endif
#endif

m3d_t *apck_model(apck_t *ctx, char *name);

#ifdef APCK_IMPLEMENTATION

/**
 * Read in an ULEB128 number
 */
uint8_t *apck_uleb128(uint8_t *ptr, uint64_t *value)
{
    uint32_t shift = 0, b;
    *value = 0;
    do {
        b = *ptr++;
        *value |= ((b & 0x7f) << shift);
        shift += 7;
    } while(shift < 64 && (b & 0x80));
    return ptr;
}

/**
 * Calculate checksum
 */
uint32_t apck_crc32(uint8_t *buf, uint32_t size)
{
    static uint32_t crc32_lookup[256] = { 0 };
    uint32_t crc32_val = 0xffffffff, i, j, d;
    if(!crc32_lookup[1])
        for(i = 0; i < 256; i++)
            for(d = i, j = 0; j < 8; j++)
                crc32_lookup[i] = d = d & 1 ? (d >> 1) ^ 0xedb88320 : d >> 1;
    while(size--) crc32_val = (crc32_val >> 8) ^ crc32_lookup[(crc32_val & 0xff) ^ *buf++];
    return crc32_val ^ 0xffffffff;
}

/**
 * Initialize unpacker context
 */
int apck_init(apck_t *ctx, char *engine, uint32_t version, apck_initcb_t initcb, apck_readcb_t readcb)
{
    if(!ctx) return APCK_ERR_BADINP;

    memset(ctx, 0, sizeof(apck_t));
    if(engine) strncpy((char*)ctx->engine, engine, 12);
    ctx->version = version;
    ctx->initcb = initcb;
    ctx->readcb = readcb;
    return APCK_OK;
}

/**
 * Read asset from archive into a newly allocated buffer. Supported by all ciphers.
 */
uint8_t *apck_readbuf(apck_t *ctx, uint32_t archive, uint64_t offs, uint64_t size)
{
    uint8_t *buf;
    uint64_t len;

    if(!ctx || !size || archive >= ctx->numarchive || offs >= ctx->archives[archive].size) return NULL;
    len = (size + 255) & ~255;
    if(!(buf = (uint8_t*)malloc(len))) return NULL;
    apck_fileseek(ctx->archives[archive].f, offs);
    if((size = apck_fileread(ctx->archives[archive].f, buf, len)) && ctx->readcb)
        (*ctx->readcb)(ctx->archives[archive].enc, offs, buf, size);
    if(!size) { free(buf); buf = NULL; }
    return buf;
}

/**
 * Read part of archive into an existing buffer. Not all ciphers supports this.
 */
uint64_t apck_read(apck_t *ctx, uint32_t archive, uint64_t offs, uint8_t *buf, uint64_t size)
{
    if(!ctx || !buf || !size || archive >= ctx->numarchive || offs >= ctx->archives[archive].size) return APCK_ERR_BADINP;
    if(offs + size > ctx->archives[archive].size) size = ctx->archives[archive].size - offs;
    apck_fileseek(ctx->archives[archive].f, offs);
    if((size = apck_fileread(ctx->archives[archive].f, buf, size)) && ctx->readcb)
        (*ctx->readcb)(ctx->archives[archive].enc, offs, buf, size);
    return size;
}

/**
 * Merge an asset into the global Asset Directory
 */
static int _apck_merge_asset(apck_t *ctx, char *name, uint32_t a, uint32_t j, uint32_t n, uint64_t o, uint64_t s, uint64_t c)
{
    uint32_t k = 0;

    if(j != APCK_ATLAS)
        for(k = 0; k < ctx->numfiles[j] && strcmp(ctx->files[j][k].name, name); k++);
    if(j == APCK_ATLAS || k >= ctx->numfiles[j]) {
        k = ctx->numfiles[j]++;
        if(!(ctx->files[j] = (apck_file_t*)realloc(ctx->files[j], ctx->numfiles[j] * sizeof(apck_file_t)))) {
            DBG(("unable to allocate files[%u] for '%s' (%u entries)\n", j, name, ctx->numfiles[j]));
            ctx->numfiles[j] = 0;
            return 0;
        }
        memset(&ctx->files[j][k], 0, sizeof(apck_file_t));
    }
    if(ctx->files[j][k].buf) { free(ctx->files[j][k].buf); ctx->files[j][k].buf = NULL; }
    ctx->files[j][k].archive = a;
    ctx->files[j][k].w = ctx->files[j][k].h = 0;
    ctx->files[j][k].name = name;
    ctx->files[j][k].str = n;
    ctx->files[j][k].offs = o;
    ctx->files[j][k].size = s;
    ctx->files[j][k].comp = c;
    return 1;
}

/**
 * Helper to sort assets
 */
int _apck_filecmp(const void *a, const void *b)
{
    return strcmp(((apck_file_t*)a)->name, ((apck_file_t*)b)->name);
}

/**
 * Add an archive to the context
 */
int apck_load(apck_t *ctx, char *fn)
{
    uint64_t n, m, o, s, c;
    uint32_t a, i, j, k = 0;
    uint64_t size;
    uint8_t *comp, *orig, *ptr, *end;
    apck_hdr_t hdr;
    void *f, *enc;
    char *str, *name;

    if(!ctx || !fn || !*fn) return APCK_ERR_BADINP;

    /* get header */
    if(!(f = apck_fileopen(fn, &size))) return APCK_ERR_BADPCK;
    if(!apck_fileread(f, &hdr, sizeof(hdr)) || memcmp(hdr.magic, APCK_MAGIC, 4) ||
      hdr.size < 9 || hdr.hdr_size - sizeof(apck_hdr_t) > hdr.size ||
      (ctx->engine[0] && memcmp(ctx->engine, hdr.engine, 12)) || (ctx->version && ctx->version < hdr.version) ||
      (*((uint64_t*)hdr.enckey) && (!ctx->initcb || !ctx->readcb))) {
            apck_fileclose(f);
            return APCK_ERR_BADPCK;
        }
    if(!(comp = (uint8_t*)malloc(hdr.hdr_size - sizeof(apck_hdr_t)))) {
        apck_fileclose(f);
        return APCK_ERR_NOMEM;
    }
    if(!(orig = (uint8_t*)malloc(hdr.size))) {
        free(comp);
        apck_fileclose(f);
        return APCK_ERR_NOMEM;
    }

    /* we can't use apck_read yet, because the archive isn't registered yet */
    enc = *((uint64_t*)hdr.enckey) ? (*ctx->initcb)(hdr.enckey) : NULL;
    if(!apck_fileread(f, comp, hdr.hdr_size - sizeof(apck_hdr_t)) ||
      (enc && (*ctx->readcb)(enc, sizeof(apck_hdr_t), comp, hdr.hdr_size - sizeof(apck_hdr_t))) ||
      stbi_zlib_decode_buffer((char*)orig, (int)hdr.size, (char*)comp, (int)(hdr.hdr_size - sizeof(apck_hdr_t))) < 1 ||
      apck_crc32(orig, hdr.size) != hdr.chksum || *((uint32_t*)orig) < 4 || *((uint32_t*)orig) >= hdr.size) {
        if(enc) free(enc);
        free(comp);
        free(orig);
        apck_fileclose(f);
        return APCK_ERR_BADPCK;
    }
    free(comp);

    /* add the archive to the list */
    a = ctx->numarchive++;
    if(!(ctx->archives = (apck_archive_t*)realloc(ctx->archives, ctx->numarchive * sizeof(apck_archive_t)))) {
        ctx->numarchive = 0;
        return APCK_ERR_NOMEM;
    }
    ctx->archives[a].f = f;
    ctx->archives[a].size = size;
    ctx->archives[a].str = orig;
    ctx->archives[a].enc = enc;
    ctx->archives[a].aidx = ctx->numfiles[APCK_ATLAS];

    /* merge its asset directory with the existing one */
    ptr = orig + *((uint32_t*)orig);
    end = orig + hdr.size;
    for(j = 0; j < APCK_NUMTYPES && ptr < end; j++) {
        ptr = apck_uleb128(ptr, &m);
        for(i = 0; i < m && ptr < end; i++) {
            ptr = apck_uleb128(ptr, &n);
            ptr = apck_uleb128(ptr, &o);
            ptr = apck_uleb128(ptr, &s);
            ptr = apck_uleb128(ptr, &c);
            o += hdr.hdr_size;
            name = (char*)orig + n;
            if(j < APCK_FONT) {
                /* locales need special treatment */
                if(!i) {
                    for(k = 0; k < APCK_FONT && ctx->files[k] && strcmp(ctx->files[k][0].name, name); k++);
                    if(k < APCK_FONT) {
                        if(!ctx->files[k]) {
                            if(!_apck_merge_asset(ctx, name, a, k, n, 0, 0, 0) || !ctx->files[k]) k = APCK_FONT;
                        }
                        if(k < APCK_FONT && (comp = apck_readbuf(ctx, a, o, c))) {
                            if((str = (char*)malloc(s))) {
                                if(stbi_zlib_decode_buffer((char*)str, (int)s, (char*)comp, (int)c) < 1 ||
                                  !_apck_merge_locale(ctx, k, str, s)) {
                                    k = APCK_FONT; DBG(("unable to uncompress locale '%s' from '%s'\n", name, fn));
                                }
                                free(str);
                            } else { k = APCK_FONT; DBG(("unable to allocate locale '%s' from '%s' (%u bytes)\n", name, fn, (uint32_t)s)); }
                            free(comp);
                        } else { k = APCK_FONT; DBG(("unable to read locale '%s' from '%s'\n", name, fn)); }
                    }
                } else
                if(k < APCK_FONT) {
                    /* voices and images with text for this locale */
                    _apck_merge_asset(ctx, name, a, k, n, o, s, c);
                }
            } else {
                /* every other asset */
                _apck_merge_asset(ctx, name, a, j, n, o, s, c);
            }
        }
    }
    /* sort assets by name so that we can do an O(log2) search later */
    for(j = 0; j < APCK_FONT; j++)
        if(ctx->files[j] && ctx->numfiles[j] > 1)
            qsort(&ctx->files[j][1], ctx->numfiles[j] - 1, sizeof(apck_file_t), _apck_filecmp);
    for(; j < APCK_NUMTYPES; j++)
        if(ctx->files[j] && ctx->numfiles[j] && j != APCK_ATLAS)
            qsort(ctx->files[j], ctx->numfiles[j], sizeof(apck_file_t), _apck_filecmp);
    return APCK_OK;
}

/**
 * Free buffer for one particular asset
 */
void _apck_asset_free(apck_file_t *file, int type)
{
    if(file && file->buf) {
        switch(type) {
            case APCK_MODEL: m3d_free((m3d_t*)file->buf); break;
            case APCK_SPRITE: apck_sprite_free((apck_sprite_t*)file->buf); break;
            case APCK_MAP: apck_map_free((apck_map_t*)file->buf); break;
            default: free(file->buf); break;
        }
        file->buf = NULL;
    }
}

/**
 * Free all resources
 */
int apck_free(apck_t *ctx)
{
    uint32_t i, j;

    if(!ctx) return APCK_ERR_BADINP;

    if(ctx->archives) {
        for(i = 0; i < ctx->numarchive; i++) {
            if(ctx->archives[i].f) apck_fileclose(ctx->archives[i].f);
            if(ctx->archives[i].str) free(ctx->archives[i].str);
            if(ctx->archives[i].enc) free(ctx->archives[i].enc);
        }
        free(ctx->archives);
    }
    for(j = 0; j < APCK_NUMTYPES; j++)
        if(ctx->files[j]) {
            for(i = 0; i < ctx->numfiles[j]; i++)
                _apck_asset_free(&ctx->files[j][i], j);
            free(ctx->files[j]);
        }
    for(i = 0; i < APCK_FONT; i++)
        if(ctx->msgstr[i]) free(ctx->msgstr[i]);
    if(ctx->font) {
        ssfn_free(ctx->font);
        free(ctx->font);
    }
    memset(ctx, 0, sizeof(apck_t));
    return APCK_OK;
}

/**
 * Look up an asset in the Global Asset Directory
 */
apck_file_t *apck_lookup(apck_t *ctx, int type, char *name)
{
    apck_file_t *dir;
    int s, e, h, m;

    if(!ctx || type < 0 || type >= APCK_NUMTYPES || !name || !*name || !ctx->files[type] || !ctx->numfiles[type])
        return NULL;

    /* use binary search */
    dir = ctx->files[type];
    s = !(type >= APCK_FONT);
    e = ctx->numfiles[type] - 1;
    while(s <= e) {
        h = ((e + s) >> 1);
        m = strcmp(dir[h].name, name);
        if(!m) return &dir[h];
        if(m > 0) e = h - 1; else s = h + 1;
    }
    return NULL;
}

/**
 * Free internal cache of one particular asset
 */
int apck_release(apck_t *ctx, int type, char *name)
{
    if(!ctx || type < 0 || type >= APCK_NUMTYPES || !name || !*name)
        return APCK_ERR_BADINP;

    _apck_asset_free(apck_lookup(ctx, type, name), type);
    return APCK_OK;
}

/**
 * Look up and return any arbitrary asset
 */
uint8_t *apck_asset(apck_t *ctx, int type, char *name, uint64_t *size)
{
    apck_file_t *file = NULL;
    uint8_t *buf, *comp;

    if(size) *size = 0;
    if(!ctx || type > APCK_MAP || type >= APCK_NUMTYPES || !name || !*name)
        return NULL;

    if((file = apck_lookup(ctx, type, name))) {
        /* we have this asset, let's see if we have cached its contents already */
        if(!file->buf) {
            if(file->size && (comp = apck_readbuf(ctx, file->archive, file->offs, file->comp ? file->comp : file->size))) {
                if(file->comp) {
                    /* we got the data from the archive, uncompress it */
                    if((buf = (uint8_t*)malloc(file->size))) {
                        if(stbi_zlib_decode_buffer((char*)buf, (int)file->size, (char*)comp, (int)file->comp) < 1) {
                            DBG(("unable to uncompress asset '%s'\n", name));
                            free(buf);
                            file->size = file->comp = 0;
                        } else
                            file->buf = buf;
                    } else { DBG(("unable to allocate asset '%s' (%u bytes)\n", name, (uint32_t)file->size)); }
                    free(comp);
                } else file->buf = comp;
            } else { DBG(("unable to read asset '%s'\n", name)); }
        }
        if(size) *size = file->size;
        return (uint8_t*)file->buf;
    }
    return NULL;
}

/**
 * Look up and set the font to be used by apck_text()
 */
int apck_font(apck_t *ctx, char *name)
{
    apck_file_t *file = NULL;
    uint8_t *buf, *comp;

    if(!ctx || !name || !*name)
        return APCK_ERR_BADINP;

    if(!ctx->font) {
        if(!(ctx->font = (ssfn_t*)malloc(sizeof(ssfn_t))))
            return APCK_ERR_NOMEM;
        memset(ctx->font, 0, sizeof(ssfn_t));
    } else
        ssfn_free(ctx->font);

    if((file = apck_lookup(ctx, APCK_FONT, name))) {
        /* we have this asset, let's see if we have cached its contents already */
        if(!file->buf) {
            if(file->comp && (comp = apck_readbuf(ctx, file->archive, file->offs, file->comp))) {
                /* we got the data from the archive, uncompress it */
                if((buf = (uint8_t*)malloc(file->size))) {
                    if(stbi_zlib_decode_buffer((char*)buf, (int)file->size, (char*)comp, (int)file->comp) < 1 || memcmp(buf, "SFN2", 4)) {
                        DBG(("unable to uncompress font '%s'\n", name));
                        file->size = file->comp = 0;
                        free(buf);
                    } else
                        file->buf = buf;
                } else { DBG(("unable to allocate font '%s' (%u bytes)\n", name, (uint32_t)file->size)); }
                free(comp);
            } else { DBG(("unable to read font '%s'\n", name)); }
        }
        if(ctx->font && file->buf)
            ssfn_load(ctx->font, file->buf);
        return APCK_OK;
    }
    return APCK_ERR_BADINP;
}

/**
 * Render localized text and return a pixel buffer as if the were an asset stored in archive
 */
uint32_t *apck_text(apck_t *ctx, char *msgid, uint32_t color, int fontstyle, int fontsize, int *w, int *h)
{
    ssfn_buf_t buf = { 0 };
    char *msgstr;
    int ret;

    if(w) *w = 0;
    if(h) *h = 0;
    if(!ctx || !ctx->font || !(color & 0xfc000000) || !(msgstr = apck_msgstr(ctx, msgid)) ||
      ssfn_select(ctx->font, SSFN_FAMILY_ANY, NULL, fontstyle | SSFN_STYLE_NOCACHE, fontsize) != SSFN_OK ||
      ssfn_bbox(ctx->font, msgstr, (int*)&buf.w, (int*)&buf.h, (int*)&buf.x, (int*)&buf.y) != SSFN_OK)
        return NULL;

    buf.fg = color;
    buf.p = buf.w * sizeof(uint32_t);
    if((buf.ptr = (uint8_t*)malloc(buf.p * buf.h))) {
        memset(buf.ptr, 0, buf.p * buf.h);
        while((ret = ssfn_render(ctx->font, &buf, msgstr)) > 0)
            msgstr += ret;
        if(w) *w = buf.w;
        if(h) *h = buf.h;
    }
    return (uint32_t*)buf.ptr;
}

/**
 * Look up and return a decoded 3D model
 */
m3d_t *apck_model(apck_t *ctx, char *name)
{
    apck_file_t *file = NULL;
    uint8_t *buf, *comp;

    if(!ctx || !name || !*name)
        return NULL;

    if((file = apck_lookup(ctx, APCK_MODEL, name))) {
        /* we have this asset, let's see if we have cached its contents already */
        if(!file->buf) {
            if(file->comp && (comp = apck_readbuf(ctx, file->archive, file->offs, file->comp))) {
                /* we got the data from the archive, uncompress it */
                if((buf = (uint8_t*)malloc(file->size))) {
                    if(stbi_zlib_decode_buffer((char*)buf, (int)file->size, (char*)comp, (int)file->comp) < 1) {
                        DBG(("unable to uncompress model '%s'\n", name));
                        file->size = file->comp = 0;
                    } else
                    if(!(file->buf = (uint8_t*)m3d_load(buf, NULL, NULL, NULL))) {
                        DBG(("unable to decode model '%s'\n", name));
                        file->size = file->comp = 0;
                    }
                    free(buf);
                } else { DBG(("unable to allocate model '%s' (%u bytes)\n", name, (uint32_t)file->size)); }
                free(comp);
            } else { DBG(("unable to read model '%s'\n", name)); }
        }
        return (m3d_t*)file->buf;
    }
    return NULL;
}

/**
 * Look up and return a decoded image asset
 */
uint32_t *apck_image(apck_t *ctx, int type, char *name, int *w, int *h)
{
    apck_file_t *file = NULL;
    uint8_t *buf;
    int p, i;

    if(w) *w = 0;
    if(h) *h = 0;
    if(!ctx || (type != APCK_IMAGE && type != APCK_ATLAS) || !name || !*name)
        return NULL;

    if(type == APCK_ATLAS) {
        /* atlases are generated so they don't have a real name, they use index instead */
        i = atoi(name) - 1;
        if(ctx->files[APCK_ATLAS] && i >= 0 && (uint32_t)i < ctx->numfiles[APCK_ATLAS])
            file = &ctx->files[APCK_ATLAS][i];
    } else {
        /* for images, try localized images first */
        if(ctx->locale < APCK_FONT && ctx->files[ctx->locale])
            file = apck_lookup(ctx, ctx->locale, name);
        if(!file) file = apck_lookup(ctx, type, name);
    }

    if(file) {
        /* we have this asset, let's see if we have cached its contents already */
        if(!file->buf) {
            if(file->size && (buf = apck_readbuf(ctx, file->archive, file->offs, file->size))) {
                if(!(file->buf = (uint8_t*)stbi_load_from_memory(buf, file->size, &file->w, &file->h, &p, 4))) {
                    DBG(("unable to decode image asset '%s'\n", name));
                    file->size = file->comp = 0;
                }
                free(buf);
            } else { DBG(("unable to read image asset '%s'\n", name)); }
        }
        /* extra check for images */
        if(!file->buf || file->w < 1 || file->h < 1) return NULL;
        /* return the decoded asset */
        if(w) *w = file->w;
        if(h) *h = file->h;
        return (uint32_t*)file->buf;
    }
    return NULL;
}

/**
 * Look up and return a decoded audio asset
 */
int16_t *apck_audio(apck_t *ctx, int type, char *name, int *numsamples, int *channels)
{
    apck_file_t *file = NULL;
    uint8_t *buf;
    int ch, hz;

    if(numsamples) *numsamples = 0;
    if(channels) *channels = 0;
    if(!ctx || (type != APCK_BGM && type != APCK_SFX) || !name || !*name)
        return NULL;

    /* for sound effects, try localized voices first */
    if(type == APCK_SFX && ctx->locale < APCK_FONT && ctx->files[ctx->locale])
        file = apck_lookup(ctx, ctx->locale, name);
    if(!file) file = apck_lookup(ctx, type, name);

    if(file) {
        /* we have this asset, let's see if we have cached its contents already */
        if(!file->buf) {
            if(file->size && (buf = apck_readbuf(ctx, file->archive, file->offs, file->size))) {
                if(!(file->w = APCK_OGG(buf, file->size, &ch, &hz, (int16_t**)&file->buf))) {
                    DBG(("unable to decode audio asset '%s'\n", name));
                    file->size = file->comp = 0;
                } else {
                    file->w *= ch;
                    file->h = -ch;
                }
                free(buf);
            } else { DBG(("unable to read audio asset '%s'\n", name)); }
        }
        /* extra check for audio */
        if(!file->buf || file->w < 1 || file->h > -1) return NULL;
        /* return the decoded asset */
        if(numsamples) *numsamples = file->w;
        if(channels) *channels = -file->h;
        return (int16_t*)file->buf;
    }
    return NULL;
}

/**
 * Look up and return a decoded sprite asset
 */
apck_sprite_t *apck_sprite(apck_t *ctx, char *name)
{
    apck_file_t *file = NULL;
    uint8_t *buf, *comp;

    if(!ctx || !name || !*name)
        return NULL;

    if((file = apck_lookup(ctx, APCK_SPRITE, name))) {
        /* we have this asset, let's see if we have cached its contents already */
        if(!file->buf) {
            if(file->comp && (comp = apck_readbuf(ctx, file->archive, file->offs, file->comp))) {
                if((buf = (uint8_t*)malloc(file->size))) {
                    if(stbi_zlib_decode_buffer((char*)buf, (int)file->size, (char*)comp, (int)file->comp) < 1 ||
                      !(file->buf = (uint8_t*)apck_sprite_decode(ctx->archives[file->archive].aidx,
                        ctx->archives[file->archive].str, buf, file->size))) {
                            DBG(("unable to uncompress sprite '%s'\n", name));
                            file->size = file->comp = 0;
                    }
                    free(buf);
                } else { DBG(("unable to allocate sprite '%s' (%u bytes)\n", name, (uint32_t)file->size)); }
                free(comp);
            } else { DBG(("unable to read sprite asset '%s'\n", name)); }
        }
        return (apck_sprite_t*)file->buf;
    }
    return NULL;
}

/**
 * Look up and return a decoded map asset
 */
apck_map_t *apck_map(apck_t *ctx, char *name)
{
    apck_file_t *file = NULL;
    uint8_t *buf, *comp;

    if(!ctx || !name || !*name)
        return NULL;

    if((file = apck_lookup(ctx, APCK_MAP, name))) {
        /* we have this asset, let's see if we have cached its contents already */
        if(!file->buf) {
            if(file->comp && (comp = apck_readbuf(ctx, file->archive, file->offs, file->comp))) {
                if((buf = (uint8_t*)malloc(file->size))) {
                    if(stbi_zlib_decode_buffer((char*)buf, (int)file->size, (char*)comp, (int)file->comp) < 1 ||
                      !(file->buf = (uint8_t*)apck_map_decode(ctx->archives[file->archive].str, buf, file->size))) {
                        DBG(("unable to uncompress map '%s'\n", name));
                        file->size = file->comp = 0;
                    }
                    free(buf);
                } else { DBG(("unable to allocate map '%s' (%u bytes)\n", name, (uint32_t)file->size)); }
                free(comp);
            } else { DBG(("unable to read map asset '%s'\n", name)); }
        }
        return (apck_map_t*)file->buf;
    }
    return NULL;
}

#endif /* APCK_IMPLEMENTATION */

#ifdef  __cplusplus
}
#endif
#endif /* APIC_H */
