/**************************************************************************************************
KPLIB.C: Ken's Picture LIBrary written by Ken Silverman
Copyright (c) 1998-2008 Ken Silverman
Ken Silverman's official web site: http://advsys.net/ken

Features of KPLIB.C:
	* Routines for decoding JPG/PNG/GIF/PCX/TGA/BMP/DDS/CEL.
		See kpgetdim(), kprender(), and optional helper function: kpzload().
	* Routines for reading files out of ZIP/GRP files. All ZIP/GRP functions start with "kz".
	* Multi-platform support: Dos/Windows/Linux/Mac/etc..
	* Compact code, all in a single source file. Yeah, bad design on my part... but makes life
		  easier for everyone else - you simply add a single C file to your project, throw a few
		  externs in there, add the function calls, and you're done!

Brief history:
1998?: Wrote KPEG, a JPEG viewer for DOS
2000: Wrote KPNG, a PNG viewer for DOS
2001: Combined KPEG & KPNG, ported to Visual C, and made it into a library called KPLIB.C
2002: Added support for TGA,GIF,CEL,ZIP
2003: Added support for BMP
05/18/2004: Added support for 8&24 bit PCX
12/09/2005: Added support for progressive JPEG
01/05/2006: Added support for DDS
07/28/2007: Added support for GRP (Build Engine archive)

I offer this code to the community for free use - all I ask is that my name be included in the
credits.

-Ken S.
**************************************************************************************************/

#include "compat.h"

#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>

#if defined(__POWERPC__) || defined(GEKKO)
#define BIGENDIAN 1
#endif

#ifdef BIGENDIAN
static uint32_t LSWAPIB(uint32_t a) { return(((a>>8)&0xff00)+((a&0xff00)<<8)+(a<<24)+(a>>24)); }
static uint16_t SSWAPIB(uint16_t a) { return((a>>8)+(a<<8)); }
#define LSWAPIL(a) (a)
#define SSWAPIL(a) (a)
#else
#define LSWAPIB(a) (a)
#define SSWAPIB(a) (a)
static uint32_t LSWAPIL(uint32_t a) { return(((a>>8)&0xff00)+((a&0xff00)<<8)+(a<<24)+(a>>24)); }
static uint16_t SSWAPIL(uint16_t a) { return((a>>8)+(a<<8)); }
#endif

#if !defined(_WIN32)
#include <unistd.h>
#include <dirent.h>
typedef long long  __int64;
static __inline int32_t _lrotl(int32_t i, int sh)
{ return((i>>(-sh))|(i<<sh)); }
/*__inline*/ int32_t filelength(int h)
{
    struct stat st;
    if (fstat(h,&st) < 0) return(-1);
    return(st.st_size);
}
#define _fileno fileno
#else
#include <io.h>
#endif

#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif

#ifndef O_BINARY
#define O_BINARY 0
#endif
#if !defined(max)
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif
#if !defined(min)
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif

#if defined __clang__ && __clang_major__==3
// clang 3.1 SVN r149129, assertion failure with inline asm
# define NOASM 1
#endif

#if defined(__GNUC__)
#define _inline inline
#endif

//use GCC-specific extension to force symbol name to be something in particular to override underscoring.
#if defined(__GNUC__) && defined(__i386__) && !defined(NOASM)
#define ASMNAME(x) asm(x)
#else
#define ASMNAME(x)
#endif

static intptr_t frameplace;
static int32_t bytesperline, xres, yres, globxoffs, globyoffs;

static const int32_t pow2mask[32] =
{
    0x00000000,0x00000001,0x00000003,0x00000007,
    0x0000000f,0x0000001f,0x0000003f,0x0000007f,
    0x000000ff,0x000001ff,0x000003ff,0x000007ff,
    0x00000fff,0x00001fff,0x00003fff,0x00007fff,
    0x0000ffff,0x0001ffff,0x0003ffff,0x0007ffff,
    0x000fffff,0x001fffff,0x003fffff,0x007fffff,
    0x00ffffff,0x01ffffff,0x03ffffff,0x07ffffff,
    0x0fffffff,0x1fffffff,0x3fffffff,0x7fffffff,
};
static const int32_t pow2long[32] =
{
    0x00000001,0x00000002,0x00000004,0x00000008,
    0x00000010,0x00000020,0x00000040,0x00000080,
    0x00000100,0x00000200,0x00000400,0x00000800,
    0x00001000,0x00002000,0x00004000,0x00008000,
    0x00010000,0x00020000,0x00040000,0x00080000,
    0x00100000,0x00200000,0x00400000,0x00800000,
    0x01000000,0x02000000,0x04000000,0x08000000,
    0x10000000,0x20000000,0x40000000,(int32_t)0x80000000,
};

//Hack for peekbits,getbits,suckbits (to prevent lots of duplicate code)
//   0: PNG: do 12-byte chunk_header removal hack
// !=0: ZIP: use 64K buffer (olinbuf)
static int32_t zipfilmode;
typedef struct
{
    FILE *fil;    //0:no file open, !=0:open file (either stand-alone or zip)
    int32_t comptyp; //0:raw data (can be ZIP or stand-alone), 8:PKZIP LZ77 *flate
    int32_t seek0;   //0:stand-alone file, !=0: start of zip compressed stream data
    int32_t compleng;//Global variable for compression FIFO
    int32_t comptell;//Global variable for compression FIFO
    int32_t leng;    //Uncompressed file size (bytes)
    int32_t pos;     //Current uncompressed relative file position (0<=pos<=leng)
    int32_t endpos;  //Temp global variable for kzread
    int32_t jmpplc;  //Store place where decompression paused
    int32_t i;       //For stand-alone/ZIP comptyp#0, this is like "uncomptell"
    //For ZIP comptyp#8&btype==0 "<64K store", this saves i state
    int32_t bfinal;  //LZ77 decompression state (for later calls)
} kzfilestate;
static kzfilestate kzfs;

//Initialized tables (can't be in union)
//jpg:                png:
//   crmul      16384    abstab10    4096
//   cbmul      16384    hxbit        472
//   dct         4608    pow2mask     128*
//   colclip     4096
//   colclipup8  4096
//   colclipup16 4096
//   unzig        256
//   pow2mask     128*
//   dcflagor      64

int32_t palcol[256] ASMNAME("palcol"), paleng, bakcol, numhufblocks, zlibcompflags;
int8_t coltype, filtype, bitdepth;

//============================ KPNGILIB begins ===============================

//07/31/2000: KPNG.C first ported to C from READPNG.BAS
//10/11/2000: KPNG.C split into 2 files: KPNG.C and PNGINLIB.C
//11/24/2000: Finished adding support for coltypes 4&6
//03/31/2001: Added support for Adam7-type interlaced images
//Currently, there is no support for:
//   * 16-bit color depth
//   * Some useless ancillary chunks, like: gAMA(gamma) & pHYs(aspect ratio)

// GCC 4.6 LTO build fix
#ifdef USING_LTO
# define B_KPLIB_STATIC
#else
# define B_KPLIB_STATIC static
#endif

//.PNG specific variables:
static int32_t bakr = 0x80, bakg = 0x80, bakb = 0x80; //this used to be public...
static int32_t gslidew = 0, gslider = 0, xm, xmn[4], xr0, xr1, xplc, yplc;
static intptr_t nfplace;
static int32_t clen[320], cclen[19], bitpos, filt, xsiz, ysiz;
int32_t xsizbpl, ixsiz, ixoff, iyoff, ixstp, iystp, intlac, nbpl;
B_KPLIB_STATIC int32_t ATTRIBUTE((used)) trnsrgb ASMNAME("trnsrgb");
static int32_t ccind[19] = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
static int32_t hxbit[59][2], ibuf0[288], nbuf0[32], ibuf1[32], nbuf1[32];
static const uint8_t *filptr;
static uint8_t slidebuf[32768], opixbuf0[4], opixbuf1[4];
static uint8_t pnginited = 0;
B_KPLIB_STATIC uint8_t olinbuf[131072] ASMNAME("olinbuf"); //WARNING:max xres is: 131072/bpp-1
static int32_t gotcmov = -2;
B_KPLIB_STATIC int32_t ATTRIBUTE((used)) abstab10[1024] ASMNAME("abstab10");

//Variables to speed up dynamic Huffman decoding:
#define LOGQHUFSIZ0 9
#define LOGQHUFSIZ1 6
static int32_t qhufval0[1<<LOGQHUFSIZ0], qhufval1[1<<LOGQHUFSIZ1];
static uint8_t qhufbit0[1<<LOGQHUFSIZ0], qhufbit1[1<<LOGQHUFSIZ1];

#if defined(_MSC_VER) && !defined(NOASM)

static _inline uint32_t bswap(uint32_t a)
{
    _asm
    {
        mov eax, a
        bswap eax
    }
}

static _inline int32_t bitrev(int32_t b, int32_t c)
{
    _asm
    {
        mov edx, b
        mov ecx, c
        xor eax, eax
        beg: shr edx, 1
        adc eax, eax
        sub ecx, 1
        jnz short beg
    }
}

static _inline int32_t testflag(int32_t c)
{
    _asm
    {
        mov ecx, c
        pushfd
        pop eax
        mov edx, eax
        xor eax, ecx
        push eax
        popfd
        pushfd
        pop eax
        xor eax, edx
        mov eax, 1
        jne menostinx
        xor eax, eax
        menostinx:
    }
}

static _inline void cpuid(int32_t a, int32_t *s)
{
    _asm
    {
        push ebx
        push esi
        mov eax, a
        cpuid
        mov esi, s
        mov dword ptr [esi+0], eax
        mov dword ptr [esi+4], ebx
        mov dword ptr [esi+8], ecx
        mov dword ptr [esi+12], edx
        pop esi
        pop ebx
    }
}

#elif defined(__GNUC__) && defined(__i386__) && !defined(NOASM)

static inline uint32_t bswap(uint32_t a)
{
    __asm__ __volatile__("bswap %0" : "+r"(a) : : "cc");
    return a;
}

static inline int32_t bitrev(int32_t b, int32_t c)
{
    int32_t a = 0;
    __asm__ __volatile__(
        "xorl %%eax, %%eax\n\t0:\n\tshrl $1, %%ebx\n\tadcl %%eax, %%eax\n\tsubl $1, %%ecx\n\tjnz 0b"
        : "+a"(a), "+b"(b), "+c"(c) : : "cc");
    return a;
}

static inline int32_t testflag(int32_t c)
{
    int32_t a;
    __asm__ __volatile__(
        "pushf\n\tpopl %%eax\n\tmovl %%eax, %%ebx\n\txorl %%ecx, %%eax\n\tpushl %%eax\n\t"
        "popf\n\tpushf\n\tpopl %%eax\n\txorl %%ebx, %%eax\n\tmovl $1, %%eax\n\tjne 0f\n\t"
        "xorl %%eax, %%eax\n\t0:"
        : "=a"(a) : "c"(c) : "ebx","cc");
    return a;
}

static inline void cpuid(int32_t a, int32_t *s)
{
    __asm__ __volatile__(
        "cpuid\n\tmovl %%eax, (%%esi)\n\tmovl %%ebx, 4(%%esi)\n\t"
        "movl %%ecx, 8(%%esi)\n\tmovl %%edx, 12(%%esi)"
        : "+a"(a) : "S"(s) : "ebx","ecx","edx","memory","cc");
}

#else

static inline uint32_t bswap(uint32_t a)
{
    return(((a&0xff0000)>>8) + ((a&0xff00)<<8) + (a<<24) + (a>>24));
}

static inline int32_t bitrev(int32_t b, int32_t c)
{
    int32_t i, j;
    for (i=1,j=0,c=(1<<c); i<c; i+=i) { j += j; if (b&i) j++; }
    return(j);
}

static inline int32_t testflag(int32_t c) { UNREFERENCED_PARAMETER(c); return(0); }

static inline void cpuid(int32_t a, int32_t *s) { UNREFERENCED_PARAMETER(a); s[0]=0; }

#endif

//Bit numbers of return value:
//0:FPU, 4:RDTSC, 15:CMOV, 22:MMX+, 23:MMX, 25:SSE, 26:SSE2, 30:3DNow!+, 31:3DNow!
static int32_t getcputype()
{
    int32_t i, cpb[4], cpid[4];
    if (!testflag(0x200000)) return(0);
    cpuid(0,cpid); if (!cpid[0]) return(0);
    cpuid(1,cpb); i = (cpb[3]&~((1<<22)|(1<<30)|(1<<31)));
    cpuid(0x80000000,cpb);
    if (((uint32_t)cpb[0]) > 0x80000000)
    {
        cpuid(0x80000001,cpb);
        i |= (cpb[3]&(1<<31));
        if (!((cpid[1]^0x68747541)|(cpid[3]^0x69746e65)|(cpid[2]^0x444d4163))) //AuthenticAMD
            i |= (cpb[3]&((1<<22)|(1<<30)));
    }
    if (i&(1<<25)) i |= (1<<22); //SSE implies MMX+ support
    return(i);
}

static uint8_t fakebuf[8], *nfilptr;
static int32_t nbitpos;
static void suckbitsnextblock()
{
    int32_t n;

    if (!zipfilmode)
    {
        if (!nfilptr)
        {
            //|===|===|crc|lng|typ|===|===|
            //        \  fakebuf: /
            //          |===|===|
            //----x     O---x     O--------
            nbitpos = LSWAPIL(*(int32_t *)&filptr[8]);
            nfilptr = (uint8_t *)&filptr[nbitpos+12];
            *(int32_t *)&fakebuf[0] = *(int32_t *)&filptr[0]; //Copy last dword of IDAT chunk
            if (*(int32_t *)&filptr[12] == LSWAPIB(0x54414449)) //Copy 1st dword of next IDAT chunk
                *(int32_t *)&fakebuf[4] = *(int32_t *)&filptr[16];
            filptr = &fakebuf[4]; bitpos -= 32;
        }
        else
        {
            filptr = nfilptr; nfilptr = 0;
            bitpos -= ((nbitpos-4)<<3);
        }
        //if (n_from_suckbits < 4) will it crash?
    }
    else
    {
        //NOTE: should only read bytes inside compsize, not 64K!!! :/
        *(int32_t *)&olinbuf[0] = *(int32_t *)&olinbuf[sizeof(olinbuf)-4];
        n = min((unsigned)(kzfs.compleng-kzfs.comptell),sizeof(olinbuf)-4);
        fread(&olinbuf[4],n,1,kzfs.fil);
        kzfs.comptell += n;
        bitpos -= ((sizeof(olinbuf)-4)<<3);
    }
}

static _inline int32_t peekbits(int32_t n) { return((LSWAPIB(*(int32_t *)&filptr[bitpos>>3])>>(bitpos&7))&pow2mask[n]); }
static _inline void suckbits(int32_t n) { bitpos += n; if (bitpos >= 0) suckbitsnextblock(); }
static _inline int32_t getbits(int32_t n) { int32_t i = peekbits(n); suckbits(n); return(i); }

static int32_t hufgetsym(int32_t *hitab, int32_t *hbmax)
{
    int32_t v, n;

    v = n = 0;
    do { v = (v<<1)+getbits(1)+hbmax[n]-hbmax[n+1]; n++; }
    while (v >= 0);
    return(hitab[hbmax[n]+v]);
}

//This did not result in a speed-up on P4-3.6Ghz (02/22/2005)
//static int32_t hufgetsym_skipb (int32_t *hitab, int32_t *hbmax, int32_t n, int32_t addit)
//{
//   int32_t v;
//
//   v = bitrev(getbits(n),n)+addit;
//   do { v = (v<<1)+getbits(1)+hbmax[n]-hbmax[n+1]; n++; } while (v >= 0);
//   return(hitab[hbmax[n]+v]);
//}

static void qhufgencode(int32_t *hitab, int32_t *hbmax, int32_t *qhval, uint8_t *qhbit, int32_t numbits)
{
    int32_t i, j, k, n, r;

    //r is the bit reverse of i. Ex: if: i = 1011100111, r = 1110011101
    i = r = 0;
    for (n=1; n<=numbits; n++)
        for (k=hbmax[n-1]; k<hbmax[n]; k++)
            for (j=i+pow2mask[numbits-n]; i<=j; i++)
            {
                r = bitrev(i,numbits);
                qhval[r] = hitab[k];
                qhbit[r] = (uint8_t)n;
            }
    for (j=pow2mask[numbits]; i<=j; i++)
    {
        r = bitrev(i,numbits);

        //k = 0;
        //for(n=0;n<numbits;n++)
        //   k = (k<<1) + ((r>>n)&1) + hbmax[n]-hbmax[n+1];
        //
        //n = numbits;
        //k = hbmax[n]-r;
        //
        //j = peekbits(LOGQHUFSIZ); i = qhufval[j]; j = qhufbit[j];
        //
        //i = j = 0;
        //do
        //{
        //   i = (i<<1)+getbits(1)+nbuf0[j]-nbuf0[j+1]; j++;
        //} while (i >= 0);
        //i = ibuf0[nbuf0[j]+i];
        //qhval[r] = k;

        qhbit[r] = 0; //n-32;
    }

    //   //hufgetsym_skipb related code:
    //for(k=n=0;n<numbits;n++) k = (k<<1)+hbmax[n]-hbmax[n+1];
    //return(k);
}

//inbuf[inum] : Bit length of each symbol
//inum        : Number of indices
//hitab[inum] : Indices from size-ordered list to original symbol
//hbmax[0-31] : Highest index (+1) of n-bit symbol
static void hufgencode(int32_t *inbuf, int32_t inum, int32_t *hitab, int32_t *hbmax)
{
    int32_t i, tbuf[31];

    for (i=30; i; i--) tbuf[i] = 0;
    for (i=inum-1; i>=0; i--) tbuf[inbuf[i]]++;
    tbuf[0] = hbmax[0] = 0; //Hack to remove symbols of length 0?
    for (i=0; i<31; i++) hbmax[i+1] = hbmax[i]+tbuf[i];
    for (i=0; i<inum; i++) if (inbuf[i]) hitab[hbmax[inbuf[i]]++] = i;
}

static int32_t initpass()  //Interlaced images have 7 "passes", non-interlaced have 1
{
    int32_t i, j, k;

    do
    {
        i = (intlac<<2);
        ixoff = ((0x04020100>>i)&15);
        iyoff = ((0x00402010>>i)&15);
        if (((ixoff >= xsiz) || (iyoff >= ysiz)) && (intlac >= 2)) { i = -1; intlac--; }
    }
    while (i < 0);
    j = ((0x33221100>>i)&15); ixstp = (1<<j);
    k = ((0x33322110>>i)&15); iystp = (1<<k);

    //xsiz=12      0123456789ab
    //j=3,ixoff=0  0       1       ((12+(1<<3)-1 - 0)>>3) = 2
    //j=3,ixoff=4      2           ((12+(1<<3)-1 - 4)>>3) = 1
    //j=2,ixoff=2    3   4   5     ((12+(1<<2)-1 - 2)>>2) = 3
    //j=1,ixoff=1   6 7 8 9 a b    ((12+(1<<1)-1 - 1)>>1) = 6
    ixsiz = ((xsiz+ixstp-1-ixoff)>>j); //It's confusing! See the above example.
    nbpl = (bytesperline<<k);

    //Initialize this to make filters fast:
    xsizbpl = ((0x04021301>>(coltype<<2))&15)*ixsiz;
    switch (bitdepth)
    {
    case 1: xsizbpl = ((xsizbpl+7)>>3); break;
    case 2: xsizbpl = ((xsizbpl+3)>>2); break;
    case 4: xsizbpl = ((xsizbpl+1)>>1); break;
    }

    Bmemset(olinbuf,0,(xsizbpl+1)*sizeof(olinbuf[0]));
    *(int32_t *)&opixbuf0[0] = *(int32_t *)&opixbuf1[0] = 0;
    xplc = xsizbpl; yplc = globyoffs+iyoff; xm = 0; filt = -1;

    i = globxoffs+ixoff; i = (((-(i>=0))|(ixstp-1))&i);
    k = (((-(yplc>=0))|(iystp-1))&yplc);
    nfplace = k*bytesperline + (i<<2) + frameplace;

    //Precalculate x-clipping to screen borders (speeds up putbuf)
    //Equation: (0 <= xr <= ixsiz) && (0 <= xr*ixstp+globxoffs+ixoff <= xres)
    xr0 = max((-globxoffs-ixoff+(1<<j)-1)>>j,0);
    xr1 = min((xres-globxoffs-ixoff+(1<<j)-1)>>j,ixsiz);
    xr0 = ixsiz-xr0;
    xr1 = ixsiz-xr1;

    if (coltype == 4) { xr0 = xr0*2;   xr1 = xr1*2;   }
    else if (coltype == 2) { xr0 = xr0*3-2; xr1 = xr1*3-2; }
    else if (coltype == 6) { xr0 = xr0*4-2; xr1 = xr1*4-2; }
    else
    {
        switch (bitdepth)
        {
        case 1: xr0 += ((-ixsiz)&7)+7;
            xr1 += ((-ixsiz)&7)+7; break;
        case 2: xr0 = ((xr0+((-ixsiz)&3)+3)<<1);
            xr1 = ((xr1+((-ixsiz)&3)+3)<<1); break;
        case 4: xr0 = ((xr0+((-ixsiz)&1)+1)<<2);
            xr1 = ((xr1+((-ixsiz)&1)+1)<<2); break;
        }
    }
    ixstp <<= 2;
    return(0);
}

static int32_t Paeth(int32_t a, int32_t b, int32_t c)
{
    int32_t pa, pb, pc;

    pa = b-c; pb = a-c; pc = labs(pa+pb); pa = labs(pa); pb = labs(pb);
    if ((pa <= pb) && (pa <= pc)) return(a);
    if (pb <= pc) return(b); else return(c);
}

#if defined(_MSC_VER) && !defined(NOASM)

static _inline int32_t Paeth686(int32_t a, int32_t b, int32_t c)
{
    _asm
    {
        push ebx
        push esi
        push edi
        mov eax, a
        mov ebx, b
        mov ecx, c
        mov edx, ecx
        sub edx, eax
        sub edx, ebx
        lea edx, abstab10[edx*4+2048]
        mov esi, [ebx*4+edx]
        mov edi, [ecx*4+edx]
        cmp edi, esi
        cmovge edi, esi
        cmovge ecx, ebx
        cmp edi, [eax*4+edx]
        cmovl eax, ecx
        pop edi
        pop esi
        pop ebx
    }
}

static _inline void rgbhlineasm(int32_t c, int32_t d, int32_t t, int32_t b)
{
    _asm
    {
        push ebx
        push edi

        mov ecx, c
        mov edx, d
        mov edi, t
        mov ebx, b
        sub ecx, edx
        jle short endit
        add edx, offset olinbuf
        cmp dword ptr trnsrgb, 0
        jz short begit2
        begit:
        mov eax, dword ptr [ecx+edx]
        or eax, 0xff000000
        cmp eax, dword ptr trnsrgb
        jne short skipit
        and eax, 0xffffff
        skipit:
        sub ecx, 3
        mov [edi], eax
        lea edi, [edi+ebx]
        jnz short begit
        jmp short endit
        begit2:
        mov eax, dword ptr [ecx+edx]
        or eax, 0xff000000
        sub ecx, 3
        mov [edi], eax
        lea edi, [edi+ebx]
        jnz short begit2
        endit:
        pop edi
        pop ebx
    }
}

static _inline void pal8hlineasm(int32_t c, int32_t d, int32_t t, int32_t b)
{
    _asm
    {
        mov ecx, c
        mov edx, d
        sub ecx, edx
        jle short endit

        push ebx
        push edi
        mov edi, t
        mov ebx, b
        add edx, offset olinbuf
        begit:movzx eax, byte ptr [ecx+edx]
        mov eax, dword ptr palcol[eax*4]
        sub ecx, 1
        mov [edi], eax
        lea edi, [edi+ebx]
        jnz short begit
        pop edi
        pop ebx
        endit:
    }
}

#elif defined(__GNUC__) && defined(__i386__) && !defined(NOASM)

static inline int32_t Paeth686(int32_t a, int32_t b, int32_t c)
{
    __asm__ __volatile__(
        "movl %%ecx, %%edx \n"
        "subl %%eax, %%edx \n"
        "subl %%ebx, %%edx \n"
        "leal (abstab10+2048)(,%%edx,4), %%edx \n"
        "movl (%%edx,%%ebx,4), %%esi \n"
        "movl (%%edx,%%ecx,4), %%edi \n"
        "cmpl %%esi, %%edi \n"
        "cmovgel %%esi, %%edi \n"
        "cmovgel %%ebx, %%ecx \n"
        "cmpl (%%edx,%%eax,4), %%edi \n"
        "cmovgel %%eax, %%ecx \n"
        : "+c"(c) : "a"(a), "b"(b) : "edx","esi","edi","memory","cc"
    );
    return c;
}

//Note: "cmove eax,?" may be faster than "jne ?:and eax,?" but who cares
static inline void rgbhlineasm(int32_t c, int32_t d, int32_t t, int32_t b)
{
    __asm__ __volatile__(
        "subl %%edx, %%ecx \n"
        "jle 3f \n"
        "addl $olinbuf, %%edx \n"
        "cmpl $0, trnsrgb(,1) \n"
        "jz 2f \n"
        "0: movl (%%ecx,%%edx,1), %%eax \n"
        "orl $0xff000000, %%eax \n"
        "cmpl trnsrgb(,1), %%eax \n"
        "jne 1f \n"
        "andl $0xffffff, %%eax \n"
        "1: subl $3, %%ecx \n"
        "movl %%eax, (%%edi) \n"
        "leal (%%edi,%%ebx,1), %%edi \n"
        "jnz 0b \n"
        "jmp 3f \n"
        "2: movl (%%ecx,%%edx,1), %%eax \n"
        "orl $0xff000000, %%eax \n"
        "subl $3, %%ecx \n"
        "movl %%eax, (%%edi) \n"
        "leal (%%edi,%%ebx,1), %%edi \n"
        "jnz 2b \n"
        "3: \n"
        : "+c"(c), "+d"(d), "+D"(t) : "b"(b) : "eax","memory","cc"
    );
}

static inline void pal8hlineasm(int32_t c, int32_t d, int32_t t, int32_t b)
{
    __asm__ __volatile__(
        "subl %%edx, %%ecx \n"
        "jle 1f \n"
        "addl $olinbuf, %%edx \n"
        "0: movzbl (%%ecx,%%edx,1), %%eax \n"
        "movl palcol(,%%eax,4), %%eax \n"
        "subl $1, %%ecx \n"
        "movl %%eax, (%%edi) \n"
        "leal (%%edi,%%ebx,1), %%edi \n"
        "jnz 0b \n"
        "1: \n"
        : "+c"(c), "+d"(d), "+D"(t) : "b"(b) : "eax","memory","cc"
    );
}

#else

static inline int32_t Paeth686(int32_t a, int32_t b, int32_t c)
{
    return(Paeth(a,b,c));
}

static inline void rgbhlineasm(int32_t x, int32_t xr1, intptr_t p, int32_t ixstp)
{
    int32_t i;
    if (!trnsrgb)
    {
        for (; x>xr1; p+=ixstp,x-=3) *(int32_t *)p = (*(int32_t *)&olinbuf[x])|LSWAPIB(0xff000000);
        return;
    }
    for (; x>xr1; p+=ixstp,x-=3)
    {
        i = (*(int32_t *)&olinbuf[x])|LSWAPIB(0xff000000);
        if (i == trnsrgb) i &= LSWAPIB(0xffffff);
        *(int32_t *)p = i;
    }
}

static inline void pal8hlineasm(int32_t x, int32_t xr1, intptr_t p, int32_t ixstp)
{
    for (; x>xr1; p+=ixstp,x--) *(int32_t *)p = palcol[olinbuf[x]];
}

#endif

//Autodetect filter
//    /f0: 0000000...
//    /f1: 1111111...
//    /f2: 2222222...
//    /f3: 1333333...
//    /f3: 3333333...
//    /f4: 4444444...
//    /f5: 0142321...
static int32_t filter1st, filterest;
static void putbuf(const uint8_t *buf, int32_t leng)
{
    int32_t i, x;
    intptr_t p;

    if (filt < 0)
    {
        if (leng <= 0) return;
        filt = buf[0];
        if (filter1st < 0) filter1st = filt; else filterest |= (1<<filt);
        if (filt == gotcmov) filt = 5;
        i = 1;
    }
    else i = 0;

    while (i < leng)
    {
        x = i+xplc; if (x > leng) x = leng;
        switch (filt)
        {
        case 0:
                while (i < x) { olinbuf[xplc] = buf[i]; xplc--; i++; }
            break;
        case 1:
                while (i < x)
                {
                    olinbuf[xplc] = (uint8_t)(opixbuf1[xm] += buf[i]);
                    xm = xmn[xm]; xplc--; i++;
                }
            break;
        case 2:
                while (i < x) { olinbuf[xplc] += (uint8_t)buf[i]; xplc--; i++; }
            break;
        case 3:
                while (i < x)
                {
                    opixbuf1[xm] = olinbuf[xplc] = (uint8_t)(((opixbuf1[xm]+olinbuf[xplc])>>1)+buf[i]);
                    xm = xmn[xm]; xplc--; i++;
                }
            break;
        case 4:
                while (i < x)
                {
                    opixbuf1[xm] = (uint8_t)(Paeth(opixbuf1[xm],olinbuf[xplc],opixbuf0[xm])+buf[i]);
                    opixbuf0[xm] = olinbuf[xplc];
                    olinbuf[xplc] = opixbuf1[xm];
                    xm = xmn[xm]; xplc--; i++;
                }
            break;
        case 5: //Special hack for Paeth686 (Doesn't have to be case 5)
                while (i < x)
                {
                    opixbuf1[xm] = (uint8_t)(Paeth686(opixbuf1[xm],olinbuf[xplc],opixbuf0[xm])+buf[i]);
                    opixbuf0[xm] = olinbuf[xplc];
                    olinbuf[xplc] = opixbuf1[xm];
                    xm = xmn[xm]; xplc--; i++;
                }
            break;
        }

        if (xplc > 0) return;

        //Draw line!
        if ((uint32_t)yplc < (uint32_t)yres)
        {
            x = xr0; p = nfplace;
            switch (coltype)
            {
            case 2: rgbhlineasm(x,xr1,p,ixstp); break;
            case 4:
                    for (; x>xr1; p+=ixstp,x-=2)
                        *(int32_t *)p = (palcol[olinbuf[x]]&LSWAPIB(0xffffff))|LSWAPIL((int32_t)olinbuf[x-1]);
                break;
            case 6:
                    for (; x>xr1; p+=ixstp,x-=4)
                    {
                        *(char *)(p) = olinbuf[x  ];   //B
                        *(char *)(p+1) = olinbuf[x+1]; //G
                        *(char *)(p+2) = olinbuf[x+2]; //R
                        *(char *)(p+3) = olinbuf[x-1]; //A
                    }
                break;
            default:
                    switch (bitdepth)
                    {
                    case 1: for (; x>xr1; p+=ixstp,x--) *(int32_t *)p = palcol[olinbuf[x>>3]>>(x&7)]; break;
                    case 2: for (; x>xr1; p+=ixstp,x-=2) *(int32_t *)p = palcol[olinbuf[x>>3]>>(x&6)]; break;
                    case 4: for (; x>xr1; p+=ixstp,x-=4) *(int32_t *)p = palcol[olinbuf[x>>3]>>(x&4)]; break;
                    case 8: pal8hlineasm(x,xr1,p,ixstp); break; //for(;x>xr1;p+=ixstp,x-- ) *(int32_t *)p = palcol[olinbuf[x]]; break;
                    }
                break;
            }
            nfplace += nbpl;
        }

        *(int32_t *)&opixbuf0[0] = *(int32_t *)&opixbuf1[0] = 0;
        xplc = xsizbpl; yplc += iystp;
        if ((intlac) && (yplc >= globyoffs+ysiz)) { intlac--; initpass(); }
        if (i < leng)
        {
            filt = buf[i++];
            if (filter1st < 0) filter1st = filt; else filterest |= (1<<filt);
            if (filt == gotcmov) filt = 5;
        }
        else filt = -1;
    }
}

static void initpngtables()
{
    int32_t i, j, k;

    //hxbit[0-58][0-1] is a combination of 4 different tables:
    //   1st parameter: [0-29] are distances, [30-58] are lengths
    //   2nd parameter: [0]: extra bits, [1]: base number

    j = 1; k = 0;
    for (i=0; i<30; i++)
    {
        hxbit[i][1] = j; j += (1<<k);
        hxbit[i][0] = k; k += ((i&1) && (i >= 2));
    }
    j = 3; k = 0;
    for (i=257; i<285; i++)
    {
        hxbit[i+30-257][1] = j; j += (1<<k);
        hxbit[i+30-257][0] = k; k += ((!(i&3)) && (i >= 264));
    }
    hxbit[285+30-257][1] = 258; hxbit[285+30-257][0] = 0;

    k = getcputype();
    if (k&(1<<15))
    {
        gotcmov = 4;
        for (i=0; i<512; i++) abstab10[512+i] = abstab10[512-i] = i;
    }
}

static int32_t kpngrend(const char *kfilebuf, int32_t kfilength,
                        intptr_t daframeplace, int32_t dabytesperline, int32_t daxres, int32_t dayres,
                        int32_t daglobxoffs, int32_t daglobyoffs)
{
    int32_t i, j, k, bfinal, btype, hlit, hdist, leng;
    int32_t slidew, slider;
    //int32_t qhuf0v, qhuf1v;

    UNREFERENCED_PARAMETER(kfilength);

    if (!pnginited) { pnginited = 1; initpngtables(); }

    if ((*(int32_t *)&kfilebuf[0] != LSWAPIB(0x474e5089)) || (*(int32_t *)&kfilebuf[4] != LSWAPIB(0x0a1a0a0d)))
        return(-1); //"Invalid PNG file signature"
    filptr = (uint8_t *)&kfilebuf[8];

    trnsrgb = 0; filter1st = -1; filterest = 0;

    while (1)
    {
        leng = LSWAPIL(*(int32_t *)&filptr[0]); i = *(int32_t *)&filptr[4];
        filptr = &filptr[8];

        if (i == LSWAPIB(0x52444849)) //IHDR (must be first)
        {
            xsiz = LSWAPIL(*(int32_t *)&filptr[0]); if (xsiz <= 0) return(-1);
            ysiz = LSWAPIL(*(int32_t *)&filptr[4]); if (ysiz <= 0) return(-1);
            bitdepth = filptr[8]; if (!((1<<bitdepth)&0x116)) return(-1); //"Bit depth not supported"
            coltype = filptr[9]; if (!((1<<coltype)&0x5d)) return(-1); //"Color type not supported"
            if (filptr[10]) return(-1); //"Only *flate is supported"
            if (filptr[11]) return(-1); //"Filter not supported"
            if (filptr[12] >= 2) return(-1); //"Unsupported interlace type"
            intlac = filptr[12]*7; //0=no interlace/1=Adam7 interlace

            //Save code by making grayscale look like a palette color scheme
            if ((!coltype) || (coltype == 4))
            {
                j = 0xff000000; k = (255 / ((1<<bitdepth)-1))*0x10101;
                paleng = (1<<bitdepth);
                for (i=0; i<paleng; i++,j+=k) palcol[i] = LSWAPIB(j);
            }
        }
        else if (i == LSWAPIB(0x45544c50)) //PLTE (must be before IDAT)
        {
            paleng = leng/3;
            for (i=paleng-1; i>=0; i--) palcol[i] = LSWAPIB((LSWAPIL(*(int32_t *)&filptr[i*3])>>8)|0xff000000);
        }
        else if (i == LSWAPIB(0x44474b62)) //bKGD (must be after PLTE and before IDAT)
        {
            switch (coltype)
            {
            case 0: case 4:
                        bakcol = (((int32_t)filptr[0]<<8)+(int32_t)filptr[1])*255/((1<<bitdepth)-1);
                bakcol = bakcol*0x10101+0xff000000; break;
            case 2: case 6:
                        if (bitdepth == 8)
                            { bakcol = (((int32_t)filptr[1])<<16)+(((int32_t)filptr[3])<<8)+((int32_t)filptr[5])+0xff000000; }
                        else
                        {
                            for (i=0,bakcol=0xff000000; i<3; i++)
                                bakcol += ((((((int32_t)filptr[i<<1])<<8)+((int32_t)filptr[(i<<1)+1]))/257)<<(16-(i<<3)));
                        }
                break;
            case 3:
                    bakcol = palcol[filptr[0]]; break;
            }
            bakr = ((bakcol>>16)&255);
            bakg = ((bakcol>>8)&255);
            bakb = (bakcol&255);
            bakcol = LSWAPIB(bakcol);
        }
        else if (i == LSWAPIB(0x534e5274)) //tRNS (must be after PLTE and before IDAT)
        {
            switch (coltype)
            {
            case 0:
                    if (bitdepth <= 8)
                        palcol[(int32_t)filptr[1]] &= LSWAPIB(0xffffff);
                //else {} // /c0 /d16 not yet supported
                break;
            case 2:
                    if (bitdepth == 8)
                        { trnsrgb = LSWAPIB((((int32_t)filptr[1])<<16)+(((int32_t)filptr[3])<<8)+((int32_t)filptr[5])+0xff000000); }
                //else {} //WARNING: PNG docs say: MUST compare all 48 bits :(
                break;
            case 3:
                    for (i=min(leng,paleng)-1; i>=0; i--)
                        palcol[i] &= LSWAPIB((((int32_t)filptr[i])<<24)|0xffffff);
                break;
            default:;
            }
        }
        else if (i == LSWAPIB(0x54414449)) { break; }  //IDAT

        filptr = &filptr[leng+4]; //crc = LSWAPIL(*(int32_t *)&filptr[-4]);
    }

    //Initialize this for the getbits() function
    zipfilmode = 0;
    filptr = &filptr[leng-4]; bitpos = -((leng-4)<<3); nfilptr = 0;
    //if (leng < 4) will it crash?

    frameplace = daframeplace;
    bytesperline = dabytesperline;
    xres = daxres;
    yres = dayres;
    globxoffs = daglobxoffs;
    globyoffs = daglobyoffs;

    switch (coltype)
    {
    case 4: xmn[0] = 1; xmn[1] = 0; break;
    case 2: xmn[0] = 1; xmn[1] = 2; xmn[2] = 0; break;
    case 6: xmn[0] = 1; xmn[1] = 2; xmn[2] = 3; xmn[3] = 0; break;
    default: xmn[0] = 0; break;
    }
    switch (bitdepth)
    {
    case 1: for (i=2; i<256; i++) palcol[i] = palcol[i&1]; break;
    case 2: for (i=4; i<256; i++) palcol[i] = palcol[i&3]; break;
    case 4: for (i=16; i<256; i++) palcol[i] = palcol[i&15]; break;
    }

    //coltype: bitdepth:  format:
    //  0     1,2,4,8,16  I
    //  2           8,16  RGB
    //  3     1,2,4,8     P
    //  4           8,16  IA
    //  6           8,16  RGBA
    xsizbpl = ((0x04021301>>(coltype<<2))&15)*xsiz;
    switch (bitdepth)
    {
    case 1: xsizbpl = ((xsizbpl+7)>>3); break;
    case 2: xsizbpl = ((xsizbpl+3)>>2); break;
    case 4: xsizbpl = ((xsizbpl+1)>>1); break;
    }
    //Tests to see if xsiz > allocated space in olinbuf
    //Note: xsizbpl gets re-written inside initpass()
    if ((xsizbpl+1)*sizeof(olinbuf[0]) > sizeof(olinbuf)) return(-1);

    initpass();

    slidew = 0; slider = 16384;
    zlibcompflags = getbits(16); //Actually 2 fields: 8:compmethflags, 8:addflagscheck
    do
    {
        bfinal = getbits(1); btype = getbits(2);
        if (btype == 0)
        {
            //Raw (uncompressed)
            suckbits((-bitpos)&7);  //Synchronize to start of next byte
            i = getbits(16); if ((getbits(16)^i) != 0xffff) return(-1);
            for (; i; i--)
            {
                if (slidew >= slider)
                {
                    putbuf(&slidebuf[(slider-16384)&32767],16384); slider += 16384;
                    if ((yplc >= yres) && (intlac < 2)) goto kpngrend_goodret;
                }
                slidebuf[(slidew++)&32767] = (uint8_t)getbits(8);
            }
            continue;
        }
        if (btype == 3) continue;

        if (btype == 1) //Fixed Huffman
        {
            hlit = 288; hdist = 32; i = 0;
            for (; i<144; i++) clen[i] = 8; //Fixed bit sizes (literals)
            for (; i<256; i++) clen[i] = 9; //Fixed bit sizes (literals)
            for (; i<280; i++) clen[i] = 7; //Fixed bit sizes (EOI,lengths)
            for (; i<288; i++) clen[i] = 8; //Fixed bit sizes (lengths)
            for (; i<320; i++) clen[i] = 5; //Fixed bit sizes (distances)
        }
        else  //Dynamic Huffman
        {
            numhufblocks++;
            hlit = getbits(5)+257; hdist = getbits(5)+1; j = getbits(4)+4;
            for (i=0; i<j; i++) cclen[ccind[i]] = getbits(3);
            for (; i<19; i++) cclen[ccind[i]] = 0;
            hufgencode(cclen,19,ibuf0,nbuf0);

            j = 0; k = hlit+hdist;
            while (j < k)
            {
                i = hufgetsym(ibuf0,nbuf0);
                if (i < 16) { clen[j++] = i; continue; }
                if (i == 16)
                    { for (i=getbits(2)+3; i; i--) { clen[j] = clen[j-1]; j++; } }
                else
                {
                    if (i == 17) i = getbits(3)+3; else i = getbits(7)+11;
                    for (; i; i--) clen[j++] = 0;
                }
            }
        }

        hufgencode(clen,hlit,ibuf0,nbuf0);
        //qhuf0v = //hufgetsym_skipb related code
        qhufgencode(ibuf0,nbuf0,qhufval0,qhufbit0,LOGQHUFSIZ0);

        hufgencode(&clen[hlit],hdist,ibuf1,nbuf1);
        //qhuf1v = //hufgetsym_skipb related code
        qhufgencode(ibuf1,nbuf1,qhufval1,qhufbit1,LOGQHUFSIZ1);

        while (1)
        {
            if (slidew >= slider)
            {
                putbuf(&slidebuf[(slider-16384)&32767],16384); slider += 16384;
                if ((yplc >= yres) && (intlac < 2)) goto kpngrend_goodret;
            }

            k = peekbits(LOGQHUFSIZ0);
            if (qhufbit0[k]) { i = qhufval0[k]; suckbits((int32_t)qhufbit0[k]); }
            else i = hufgetsym(ibuf0,nbuf0);
            //else i = hufgetsym_skipb(ibuf0,nbuf0,LOGQHUFSIZ0,qhuf0v); //hufgetsym_skipb related code

            if (i < 256) { slidebuf[(slidew++)&32767] = (uint8_t)i; continue; }
            if (i == 256) break;
            i = getbits(hxbit[i+30-257][0]) + hxbit[i+30-257][1];

            k = peekbits(LOGQHUFSIZ1);
            if (qhufbit1[k]) { j = qhufval1[k]; suckbits((int32_t)qhufbit1[k]); }
            else j = hufgetsym(ibuf1,nbuf1);
            //else j = hufgetsym_skipb(ibuf1,nbuf1,LOGQHUFSIZ1,qhuf1v); //hufgetsym_skipb related code

            j = getbits(hxbit[j][0]) + hxbit[j][1];
            i += slidew; do { slidebuf[slidew&32767] = slidebuf[(slidew-j)&32767]; slidew++; }
            while (slidew < i);
        }
    }
    while (!bfinal);

    slider -= 16384;
    if (!((slider^slidew)&32768))
        putbuf(&slidebuf[slider&32767],slidew-slider);
    else
    {
        putbuf(&slidebuf[slider&32767],(-slider)&32767);
        putbuf(slidebuf,slidew&32767);
    }

    kpngrend_goodret:;
    if (!(filterest&~(1<<filter1st))) filtype = (int8_t)filter1st;
    else if ((filter1st == 1) && (!(filterest&~(1<<3)))) filtype = 3;
    else filtype = 5;
    if (coltype == 4) paleng = 0; //For /c4, palcol/paleng used as LUT for "*0x10101": alpha is invalid!
    return(0);
}

//============================= KPNGILIB ends ================================
//============================ KPEGILIB begins ===============================

//11/01/2000: This code was originally from KPEG.C
//   All non 32-bit color drawing was removed
//   "Motion" JPG code was removed
//   A lot of parameters were added to kpeg() for library usage
static int32_t kpeginited = 0;
static int32_t clipxdim, clipydim;

static int32_t hufmaxatbit[8][20], hufvalatbit[8][20], hufcnt[8];
static uint8_t hufnumatbit[8][20], huftable[8][256];
static int32_t hufquickval[8][1024], hufquickbits[8][1024], hufquickcnt[8];
static int32_t quantab[4][64], dct[12][64], lastdc[4], unzig[64], zigit[64]; //dct:10=MAX (says spec);+2 for hacks
static uint8_t gnumcomponents, dcflagor[64];
static int32_t gcompid[4], gcomphsamp[4], gcompvsamp[4], gcompquantab[4], gcomphsampshift[4], gcompvsampshift[4];
static int32_t lnumcomponents, lcompid[4], lcompdc[4], lcompac[4], lcomphsamp[4], lcompvsamp[4], lcompquantab[4];
static int32_t lcomphvsamp0, lcomphsampshift0, lcompvsampshift0;
static int32_t colclip[1024], colclipup8[1024], colclipup16[1024];
/*static uint8_t pow2char[8] = {1,2,4,8,16,32,64,128};*/

#if defined(_MSC_VER) && !defined(NOASM)

static _inline int32_t mulshr24(int32_t a, int32_t d)
{
    _asm
    {
        mov eax, a
        imul d
        shrd eax, edx, 24
    }
}

static _inline int32_t mulshr32(int32_t a, int32_t d)
{
    _asm
    {
        mov eax, a
        imul d
        mov eax, edx
    }
}

#elif defined(__GNUC__) && defined(__i386__) && !defined(NOASM)

#define mulshr24(a,d) \
	({ int32_t __a=(a), __d=(d); \
		__asm__ __volatile__ ("imull %%edx; shrdl $24, %%edx, %%eax" \
		: "+a" (__a), "+d" (__d) : : "cc"); \
	 __a; })

#define mulshr32(a,d) \
	({ int32_t __a=(a), __d=(d); \
		__asm__ __volatile__ ("imull %%edx" \
		: "+a" (__a), "+d" (__d) : : "cc"); \
	 __d; })

#else

static inline int32_t mulshr24(int32_t a, int32_t b)
{
    return((int32_t)((((__int64)a)*((__int64)b))>>24));
}

static inline int32_t mulshr32(int32_t a, int32_t b)
{
    return((int32_t)((((__int64)a)*((__int64)b))>>32));
}

#endif

static int32_t cosqr16[8] =    //cosqr16[i] = ((cos(PI*i/16)*sqrt(2))<<24);
{23726566,23270667,21920489,19727919,16777216,13181774,9079764,4628823};
static int32_t crmul[4096], cbmul[4096];

static void initkpeg()
{
    int32_t i, j, x, y;

    x = 0;  //Back & forth diagonal pattern (aligning bytes for best compression)
    for (i=0; i<16; i+=2)
    {
        for (y=8-1; y>=0; y--)
            if ((unsigned)(i-y) < (unsigned)8) unzig[x++] = (y<<3)+i-y;
        for (y=0; y<8; y++)
            if ((unsigned)(i+1-y) < (unsigned)8) unzig[x++] = (y<<3)+i+1-y;
    }
    for (i=64-1; i>=0; i--) zigit[unzig[i]] = i;
    for (i=64-1; i>=0; i--) dcflagor[i] = (uint8_t)(1<<(unzig[i]>>3));

    for (i=0; i<128; i++) colclip[i] = i+128;
    for (i=128; i<512; i++) colclip[i] = 255;
    for (i=512; i<896; i++) colclip[i] = 0;
    for (i=896; i<1024; i++) colclip[i] = i-896;
    for (i=0; i<1024; i++)
    {
        colclipup8[i] = (colclip[i]<<8);
        colclipup16[i] = (colclip[i]<<16)+0xff000000; //Hack: set alphas to 255
    }
#if defined(BIGENDIAN)
    for (i=0; i<1024; i++)
    {
        colclip[i] = bswap(colclip[i]);
        colclipup8[i] = bswap(colclipup8[i]);
        colclipup16[i] = bswap(colclipup16[i]);
    }
#endif

    for (i=0; i<2048; i++)
    {
        j = i-1024;
        crmul[(i<<1)+0] = j*1470104; //1.402*1048576
        crmul[(i<<1)+1] = j*-748830; //-0.71414*1048576
        cbmul[(i<<1)+0] = j*-360857; //-0.34414*1048576
        cbmul[(i<<1)+1] = j*1858077; //1.772*1048576
    }

    Bmemset((void *)&dct[10][0],0,64*2*sizeof(dct[0][0]));
}

static void huffgetval(int32_t index, int32_t curbits, int32_t num, int32_t *daval, int32_t *dabits)
{
    int32_t b, v, pow2, *hmax;

    hmax = &hufmaxatbit[index][0];
    pow2 = pow2long[curbits-1];
    if (num&pow2) v = 1; else v = 0;
    for (b=1; b<=16; b++)
    {
        if (v < hmax[b])
        {
            *dabits = b;
            *daval = huftable[index][hufvalatbit[index][b]+v];
            return;
        }
        pow2 >>= 1; v <<= 1;
        if (num&pow2) v++;
    }
    *dabits = 16; *daval = 0;
}

static void invdct8x8(int32_t *dc, uint8_t dcflag)
{
#define SQRT2 23726566   //(sqrt(2))<<24
#define C182 31000253    //(cos(PI/8)*2)<<24
#define C18S22 43840978  //(cos(PI/8)*sqrt(2)*2)<<24
#define C38S22 18159528  //(cos(PI*3/8)*sqrt(2)*2)<<24
    int32_t *edc, t0, t1, t2, t3, t4, t5, t6, t7;

    edc = dc+64;
    do
    {
        if (dcflag&1) //pow2char[z])
        {
            t3 = dc[2] + dc[6];
            t2 = (mulshr32(dc[2]-dc[6],SQRT2<<6)<<2) - t3;
            t4 = dc[0] + dc[4]; t5 = dc[0] - dc[4];
            t0 = t4+t3; t3 = t4-t3; t1 = t5+t2; t2 = t5-t2;
            t4 = (mulshr32(dc[5]-dc[3]+dc[1]-dc[7],C182<<6)<<2);
            t7 = dc[1] + dc[7] + dc[5] + dc[3];
            t6 = (mulshr32(dc[3]-dc[5],C18S22<<5)<<3) + t4 - t7;
            t5 = (mulshr32(dc[1]+dc[7]-dc[5]-dc[3],SQRT2<<6)<<2) - t6;
            t4 = (mulshr32(dc[1]-dc[7],C38S22<<6)<<2) - t4 + t5;
            dc[0] = t0+t7; dc[7] = t0-t7; dc[1] = t1+t6; dc[6] = t1-t6;
            dc[2] = t2+t5; dc[5] = t2-t5; dc[4] = t3+t4; dc[3] = t3-t4;
        }
        dc += 8; dcflag >>= 1;
    }
    while (dc < edc);
    dc -= 32; edc -= 24;
    do
    {
        t3 = dc[2*8-32] + dc[6*8-32];
        t2 = (mulshr32(dc[2*8-32]-dc[6*8-32],SQRT2<<6)<<2) - t3;
        t4 = dc[0*8-32] + dc[4*8-32]; t5 = dc[0*8-32] - dc[4*8-32];
        t0 = t4+t3; t3 = t4-t3; t1 = t5+t2; t2 = t5-t2;
        t4 = (mulshr32(dc[5*8-32]-dc[3*8-32]+dc[1*8-32]-dc[7*8-32],C182<<6)<<2);
        t7 = dc[1*8-32] + dc[7*8-32] + dc[5*8-32] + dc[3*8-32];
        t6 = (mulshr32(dc[3*8-32]-dc[5*8-32],C18S22<<5)<<3) + t4 - t7;
        t5 = (mulshr32(dc[1*8-32]+dc[7*8-32]-dc[5*8-32]-dc[3*8-32],SQRT2<<6)<<2) - t6;
        t4 = (mulshr32(dc[1*8-32]-dc[7*8-32],C38S22<<6)<<2) - t4 + t5;
        dc[0*8-32] = t0+t7; dc[7*8-32] = t0-t7; dc[1*8-32] = t1+t6; dc[6*8-32] = t1-t6;
        dc[2*8-32] = t2+t5; dc[5*8-32] = t2-t5; dc[4*8-32] = t3+t4; dc[3*8-32] = t3-t4;
        dc++;
    }
    while (dc < edc);
}

static void yrbrend(int32_t x, int32_t y, int32_t *ldct)
{
    int32_t i, j, ox, oy, xx, yy, xxx, yyy, xxxend, yyyend, yv, cr = 0, cb = 0, *odc, *dc, *dc2;
    intptr_t p, pp;

    odc = ldct; dc2 = &ldct[10<<6];
    for (yy=0; yy<(lcompvsamp[0]<<3); yy+=8)
    {
        oy = y+yy+globyoffs; if ((unsigned)oy >= (unsigned)clipydim) { odc += (lcomphsamp[0]<<6); continue; }
        pp = oy*bytesperline + ((x+globxoffs)<<2) + frameplace;
        for (xx=0; xx<(lcomphsamp[0]<<3); xx+=8,odc+=64)
        {
            ox = x+xx+globxoffs; if ((unsigned)ox >= (unsigned)clipxdim) continue;
            p = pp+(xx<<2);
            dc = odc;
            if (lnumcomponents > 1) dc2 = &ldct[(lcomphvsamp0<<6)+((yy>>lcompvsampshift0)<<3)+(xx>>lcomphsampshift0)];
            xxxend = min(clipxdim-ox,8);
            yyyend = min(clipydim-oy,8);
            if ((lcomphsamp[0] == 1) && (xxxend == 8))
            {
                for (yyy=0; yyy<yyyend; yyy++)
                {
                    for (xxx=0; xxx<8; xxx++)
                    {
                        yv = dc[xxx];
                        cr = (dc2[xxx+64]>>(20-1))&~1;
                        cb = (dc2[xxx   ]>>(20-1))&~1;
                        ((int32_t *)p)[xxx] = colclipup16[(unsigned)(yv+crmul[cr+2048])>>22]+
                                              colclipup8[(unsigned)(yv+crmul[cr+2049]+cbmul[cb+2048])>>22]+
                                              colclip[(unsigned)(yv+cbmul[cb+2049])>>22];
                    }
                    p += bytesperline;
                    dc += 8;
                    if (!((yyy+1)&(lcompvsamp[0]-1))) dc2 += 8;
                }
            }
            else if ((lcomphsamp[0] == 2) && (xxxend == 8))
            {
                for (yyy=0; yyy<yyyend; yyy++)
                {
                    for (xxx=0; xxx<8; xxx+=2)
                    {
                        yv = dc[xxx];
                        cr = (dc2[(xxx>>1)+64]>>(20-1))&~1;
                        cb = (dc2[(xxx>>1)]>>(20-1))&~1;
                        i = crmul[cr+2049]+cbmul[cb+2048];
                        cr = crmul[cr+2048];
                        cb = cbmul[cb+2049];
                        ((int32_t *)p)[xxx] = colclipup16[(unsigned)(yv+cr)>>22]+
                                              colclipup8[(unsigned)(yv+ i)>>22]+
                                              colclip[(unsigned)(yv+cb)>>22];
                        yv = dc[xxx+1];
                        ((int32_t *)p)[xxx+1] = colclipup16[(unsigned)(yv+cr)>>22]+
                                                colclipup8[(unsigned)(yv+ i)>>22]+
                                                colclip[(unsigned)(yv+cb)>>22];
                    }
                    p += bytesperline;
                    dc += 8;
                    if (!((yyy+1)&(lcompvsamp[0]-1))) dc2 += 8;
                }
            }
            else
            {
                for (yyy=0; yyy<yyyend; yyy++)
                {
                    i = 0; j = 1;
                    for (xxx=0; xxx<xxxend; xxx++)
                    {
                        yv = dc[xxx];
                        j--;
                        if (!j)
                        {
                            j = lcomphsamp[0];
                            cr = (dc2[i+64]>>(20-1))&~1;
                            cb = (dc2[i   ]>>(20-1))&~1;
                            i++;
                        }
                        ((int32_t *)p)[xxx] = colclipup16[(unsigned)(yv+crmul[cr+2048])>>22]+
                                              colclipup8[(unsigned)(yv+crmul[cr+2049]+cbmul[cb+2048])>>22]+
                                              colclip[(unsigned)(yv+cbmul[cb+2049])>>22];
                    }
                    p += bytesperline;
                    dc += 8;
                    if (!((yyy+1)&(lcompvsamp[0]-1))) dc2 += 8;
                }
            }
        }
    }
}
void (*kplib_yrbrend_func)(int32_t,int32_t,int32_t *) = yrbrend;

static int32_t kpegrend(const char *kfilebuf, int32_t kfilength,
                        intptr_t daframeplace, int32_t dabytesperline, int32_t daxres, int32_t dayres,
                        int32_t daglobxoffs, int32_t daglobyoffs)
{
    int32_t i, j, v, leng = 0, xdim = 0, ydim = 0, index, prec, restartcnt, restartinterval;
    int32_t x, y, z, xx, yy, zz, *dc = NULL, num, curbits, c, daval, dabits, *hqval, *hqbits, hqcnt, *quanptr = NULL;
    int32_t passcnt = 0, ghsampmax = 0, gvsampmax = 0, glhsampmax = 0, glvsampmax = 0, glhstep, glvstep;
    int32_t eobrun, Ss, Se, Ah, Al, Alut[2], dctx[12], dcty[12], ldctx[12], /* ldcty[12], */ lshx[4], lshy[4];
    int16_t *dctbuf = 0, *dctptr[12], *ldctptr[12], *dcs = NULL;
    uint8_t ch, marker, dcflag;
    const uint8_t *kfileptr;

    if (!kpeginited) { kpeginited = 1; initkpeg(); }

    kfileptr = (uint8_t *)kfilebuf;

    if (*(uint16_t *)kfileptr == SSWAPIB(0xd8ff)) kfileptr += 2;
    else return(-1); //"%s is not a JPEG file\n",filename

    restartinterval = 0;
    for (i=0; i<4; i++) lastdc[i] = 0;
    for (i=0; i<8; i++) hufcnt[i] = 0;

    coltype = 0; bitdepth = 8; //For PNGOUT
    do
    {
        ch = *kfileptr++; if (ch != 255) continue;
        do { marker = *kfileptr++; }
        while (marker == 255);
        if (marker != 0xd9) //Don't read past end of buffer
        {
            leng = ((intptr_t)kfileptr[0]<<8)+(intptr_t)kfileptr[1]-2;
            kfileptr += 2;
        }
        //printf("fileoffs=%08x, marker=%02x,leng=%d",((int32_t)kfileptr)-((int32_t)kfilebuf)-2,marker,leng);
        switch (marker)
        {
        case 0xc0: case 0xc1: case 0xc2:
                        //processit!
                        kfileptr++; //numbits = *kfileptr++;

            ydim = SSWAPIL(*(uint16_t *)&kfileptr[0]);
            xdim = SSWAPIL(*(uint16_t *)&kfileptr[2]);
            //printf("%s: %ld / %ld = %ld\n",filename,xdim*ydim*3,kfilength,(xdim*ydim*3)/kfilength);

            frameplace = daframeplace;
            bytesperline = dabytesperline;
            xres = daxres;
            yres = dayres;
            globxoffs = daglobxoffs;
            globyoffs = daglobyoffs;

            gnumcomponents = kfileptr[4];
            kfileptr += 5;
            ghsampmax = gvsampmax = glhsampmax = glvsampmax = 0;
            for (z=0; z<gnumcomponents; z++)
            {
                gcompid[z] = kfileptr[0];
                gcomphsamp[z] = (kfileptr[1]>>4);
                gcompvsamp[z] = (kfileptr[1]&15);
                gcompquantab[z] = kfileptr[2];
                for (i=0; i<8; i++) if (gcomphsamp[z] == pow2long[i]) { gcomphsampshift[z] = i; break; }
                for (i=0; i<8; i++) if (gcompvsamp[z] == pow2long[i]) { gcompvsampshift[z] = i; break; }
                if (gcomphsamp[z] > ghsampmax) { ghsampmax = gcomphsamp[z]; glhsampmax = gcomphsampshift[z]; }
                if (gcompvsamp[z] > gvsampmax) { gvsampmax = gcompvsamp[z]; glvsampmax = gcompvsampshift[z]; }
                kfileptr += 3;
            }

            break;
        case 0xc4:  //Huffman table
                do
                {
                    ch = *kfileptr++; leng--;
                    if (ch >= 16) { index = ch-12; }
                    else { index = ch; }
                    Bmemcpy((void *)&hufnumatbit[index][1],(void *)kfileptr,16); kfileptr += 16;
                    leng -= 16;

                    v = 0; hufcnt[index] = 0;
                    hufquickcnt[index] = 0;
                    for (i=1; i<=16; i++)
                    {
                        hufmaxatbit[index][i] = v+hufnumatbit[index][i];
                        hufvalatbit[index][i] = hufcnt[index]-v;
                        Bmemcpy((void *)&huftable[index][hufcnt[index]],(void *)kfileptr,(int32_t)hufnumatbit[index][i]);
                        if (i <= 10)
                            for (c=0; c<hufnumatbit[index][i]; c++)
                                for (j=(1<<(10-i)); j>0; j--)
                                {
                                    hufquickval[index][hufquickcnt[index]] = huftable[index][hufcnt[index]+c];
                                    hufquickbits[index][hufquickcnt[index]] = i;
                                    hufquickcnt[index]++;
                                }
                        kfileptr += hufnumatbit[index][i];
                        leng -= hufnumatbit[index][i];
                        hufcnt[index] += hufnumatbit[index][i];
                        v = ((v+hufnumatbit[index][i])<<1);
                    }

                }
                while (leng > 0);
            break;
        case 0xdb:
                do
                {
                    ch = *kfileptr++; leng--;
                    index = (ch&15);
                    prec = (ch>>4);
                    for (z=0; z<64; z++)
                    {
                        v = (int32_t)(*kfileptr++);
                        if (prec) v = (v<<8)+((int32_t)(*kfileptr++));
                        v <<= 19;
                        if (unzig[z]&7) v = mulshr24(v,cosqr16[unzig[z]&7 ]);
                        if (unzig[z]>>3) v = mulshr24(v,cosqr16[unzig[z]>>3]);
                        quantab[index][unzig[z]] = v;
                    }
                    leng -= 64;
                    if (prec) leng -= 64;
                }
                while (leng > 0);
            break;
        case 0xdd:
                restartinterval = SSWAPIL(*(uint16_t *)&kfileptr[0]);
            kfileptr += leng;
            break;
        case 0xda:
                if ((xdim <= 0) || (ydim <= 0)) { if (dctbuf) Bfree(dctbuf); return(-1); }

            lnumcomponents = (int32_t)(*kfileptr++); if (!lnumcomponents) { if (dctbuf) Bfree(dctbuf); return(-1); }
            if (lnumcomponents > 1) coltype = 2;
            for (z=0; z<lnumcomponents; z++)
            {
                lcompid[z] = kfileptr[0];
                lcompdc[z] = (kfileptr[1]>>4);
                lcompac[z] = (kfileptr[1]&15);
                kfileptr += 2;
            }

            Ss = kfileptr[0];
            Se = kfileptr[1];
            Ah = (kfileptr[2]>>4);
            Al = (kfileptr[2]&15);
            kfileptr += 3;
            //printf("passcnt=%d, Ss=%d, Se=%d, Ah=%d, Al=%d\n",passcnt,Ss,Se,Ah,Al);

            if ((!passcnt) && ((Ss) || (Se != 63) || (Ah) || (Al)))
            {
                for (z=zz=0; z<gnumcomponents; z++)
                {
                    dctx[z] = ((xdim+(ghsampmax<<3)-1)>>(glhsampmax+3)) << gcomphsampshift[z];
                    dcty[z] = ((ydim+(gvsampmax<<3)-1)>>(glvsampmax+3)) << gcompvsampshift[z];
                    zz += dctx[z]*dcty[z];
                }
                z = zz*64*sizeof(int16_t);
                dctbuf = (int16_t *)Bmalloc(z); if (!dctbuf) return(-1);
                Bmemset(dctbuf,0,z);
                for (z=zz=0; z<gnumcomponents; z++) { dctptr[z] = &dctbuf[zz*64]; zz += dctx[z]*dcty[z]; }
            }

            glhstep = glvstep = 0x7fffffff;
            for (z=0; z<lnumcomponents; z++)
                for (zz=0; zz<gnumcomponents; zz++)
                    if (lcompid[z] == gcompid[zz])
                    {
                        ldctptr[z] = dctptr[zz];
                        ldctx[z] = dctx[zz];
                       // ldcty[z] = dcty[zz];
                        lcomphsamp[z] = gcomphsamp[zz];
                        lcompvsamp[z] = gcompvsamp[zz];
                        lcompquantab[z] = gcompquantab[zz];
                        if (!z)
                        {
                            lcomphsampshift0 = gcomphsampshift[zz];
                            lcompvsampshift0 = gcompvsampshift[zz];
                        }
                        lshx[z] = glhsampmax-gcomphsampshift[zz]+3;
                        lshy[z] = glvsampmax-gcompvsampshift[zz]+3;
                        if (gcomphsampshift[zz] < glhstep) glhstep = gcomphsampshift[zz];
                        if (gcompvsampshift[zz] < glvstep) glvstep = gcompvsampshift[zz];
                    }
            glhstep = (ghsampmax>>glhstep); lcomphsamp[0] = min(lcomphsamp[0],glhstep); glhstep <<= 3;
            glvstep = (gvsampmax>>glvstep); lcompvsamp[0] = min(lcompvsamp[0],glvstep); glvstep <<= 3;
            lcomphvsamp0 = lcomphsamp[0]*lcompvsamp[0];

            clipxdim = min(xdim+globxoffs,xres);
            clipydim = min(ydim+globyoffs,yres);

            if ((max(globxoffs,0) >= xres) || (min(globxoffs+xdim,xres) <= 0) ||
                    (max(globyoffs,0) >= yres) || (min(globyoffs+ydim,yres) <= 0))
                { if (dctbuf) Bfree(dctbuf); return(0); }

            Alut[0] = (1<<Al); Alut[1] = -Alut[0];

            restartcnt = restartinterval; eobrun = 0; marker = 0xd0;
            num = 0; curbits = 0;
            for (y=0; y<ydim; y+=glvstep)
                for (x=0; x<xdim; x+=glhstep)
                {
                    if (kfileptr-4-(uint8_t *)kfilebuf >= kfilength) goto kpegrend_break2; //rest of file is missing!

                    if (!dctbuf) dc = dct[0];
                    for (c=0; c<lnumcomponents; c++)
                    {
                        hqval = &hufquickval[lcompac[c]+4][0];
                        hqbits = &hufquickbits[lcompac[c]+4][0];
                        hqcnt = hufquickcnt[lcompac[c]+4];
                        if (!dctbuf) quanptr = &quantab[lcompquantab[c]][0];
                        for (yy=0; yy<(lcompvsamp[c]<<3); yy+=8)
                            for (xx=0; xx<(lcomphsamp[c]<<3); xx+=8)
                            {
                                //NOTE: Might help to split this code into firstime vs. refinement (!Ah vs. Ah!=0)

                                if (dctbuf) dcs = &ldctptr[c][(((y+yy)>>lshy[c])*ldctx[c] + ((x+xx)>>lshx[c]))<<6];

                                //Get DC
                                if (!Ss)
                                {
                                    while (curbits < 16) //Getbits
                                    {
                                        ch = *kfileptr++; if (ch == 255) kfileptr++;
                                        num = (num<<8)+((int32_t)ch); curbits += 8;
                                    }

                                    if (!Ah)
                                    {
                                        i = ((num>>(curbits-10))&1023);
                                        if (i < hufquickcnt[lcompdc[c]])
                                            { daval = hufquickval[lcompdc[c]][i]; curbits -= hufquickbits[lcompdc[c]][i]; }
                                        else { huffgetval(lcompdc[c],curbits,num,&daval,&dabits); curbits -= dabits; }

                                        if (daval)
                                        {
                                            while (curbits < daval) //Getbits
                                            {
                                                ch = *kfileptr++; if (ch == 255) kfileptr++;
                                                num = (num<<8)+((int32_t)ch); curbits += 8;
                                            }

                                            curbits -= daval; v = ((unsigned)num >> curbits) & pow2mask[daval];
                                            if (v <= pow2mask[daval-1]) v -= pow2mask[daval];
                                            lastdc[c] += v;
                                        }
                                        if (!dctbuf) dc[0] = lastdc[c]; else dcs[0] = (int16_t)(lastdc[c]<<Al);
                                    }
                                    else if (num&(pow2long[--curbits])) dcs[0] |= ((int16_t)Alut[0]);
                                }

                                //Get AC
                                if (!dctbuf) Bmemset((void *)&dc[1],0,63*4);
                                z = max(Ss,1); dcflag = 1;
                                if (eobrun <= 0)
                                {
                                    for (; z<=Se; z++)
                                    {
                                        while (curbits < 16) //Getbits
                                        {
                                            ch = *kfileptr++;  // BUF_LENG_READ
                                            if (ch == 255) kfileptr++;
                                            num = (num<<8)+((int32_t)ch); curbits += 8;
                                        }
                                        i = ((num>>(curbits-10))&1023);
                                        if (i < hqcnt)
                                            { daval = hqval[i]; curbits -= hqbits[i]; }
                                        else { huffgetval(lcompac[c]+4,curbits,num,&daval,&dabits); curbits -= dabits; }

                                        zz = (daval>>4); daval &= 15;
                                        if (daval)
                                        {
                                            if (Ah)
                                            {
                                                if (curbits < 8) //Getbits
                                                {
                                                    ch = *kfileptr++; if (ch == 255) kfileptr++;
                                                    num = (num<<8)+((long)ch); curbits += 8;
                                                }
                                                if (num&(pow2long[--curbits])) daval = Alut[0]; else daval = Alut[1];
                                            }
                                        }
                                        else if (zz < 15)
                                        {
                                            eobrun = pow2long[zz];
                                            if (zz)
                                            {
                                                while (curbits < zz) //Getbits
                                                {
                                                    ch = *kfileptr++; if (ch == 255) kfileptr++;
                                                    num = (num<<8)+((int32_t)ch); curbits += 8;
                                                }
                                                curbits -= zz; eobrun += ((unsigned)num >> curbits) & pow2mask[zz];
                                            }
                                            if (!Ah) eobrun--;
                                            break;
                                        }
                                        if (Ah)
                                        {
                                            do
                                            {
                                                if (dcs[z])
                                                {
                                                    if (curbits < 8) //Getbits
                                                    {
                                                        ch = *kfileptr++; if (ch == 255) kfileptr++;
                                                        num = (num<<8)+((int32_t)ch); curbits += 8;
                                                    }
                                                    if (num&(pow2long[--curbits])) dcs[z] += (int16_t)Alut[dcs[z] < 0];
                                                }
                                                else if (--zz < 0) break;
                                                z++;
                                            }
                                            while (z <= Se);
                                            if (daval) dcs[z] = (int16_t)daval;
                                        }
                                        else
                                        {
                                            z += zz; if (z > Se) break;

                                            while (curbits < daval) //Getbits
                                            {
                                                ch = *kfileptr++; if (ch == 255) kfileptr++;
                                                num = (num<<8)+((int32_t)ch); curbits += 8;
                                            }
                                            curbits -= daval; v = ((unsigned)num >> curbits) & pow2mask[daval];
                                            if (daval>=1 /* FIXME ? */ && v <= pow2mask[daval-1]) v -= pow2mask[daval];
                                            dcflag |= dcflagor[z];
                                            if (!dctbuf) dc[unzig[z]] = v; else dcs[z] = (int16_t)(v<<Al);
                                        }
                                    }
                                }
                                else if (!Ah) eobrun--;
                                if ((Ah) && (eobrun > 0))
                                {
                                    eobrun--;
                                    for (; z<=Se; z++)
                                    {
                                        if (!dcs[z]) continue;
                                        if (curbits < 8) //Getbits
                                        {
                                            ch = *kfileptr++; if (ch == 255) kfileptr++;
                                            num = (num<<8)+((int32_t)ch); curbits += 8;
                                        }
                                        if (num&(pow2long[--curbits])) dcs[z] += ((int16_t)Alut[dcs[z] < 0]);
                                    }
                                }

                                if (!dctbuf)
                                {
                                    for (z=64-1; z>=0; z--) dc[z] *= quanptr[z];
                                    invdct8x8(dc,dcflag); dc += 64;
                                }
                            }
                    }

                    if (!dctbuf) kplib_yrbrend_func(x,y,&dct[0][0]);

                    restartcnt--;
                    if (!restartcnt)
                    {
                        kfileptr += 1-(curbits>>3); curbits = 0;
                        if ((kfileptr[-2] != 255) || (kfileptr[-1] != marker)) kfileptr--;
                        marker++; if (marker >= 0xd8) marker = 0xd0;
                        restartcnt = restartinterval;
                        for (i=0; i<4; i++) lastdc[i] = 0;
                        eobrun = 0;
                    }
                }
            kpegrend_break2:;
            if (!dctbuf) return(0);
            passcnt++; kfileptr -= ((curbits>>3)+1); break;
        case 0xd9: break;
        default: kfileptr += leng; break;
        }
    }
    while (kfileptr-(uint8_t *)kfilebuf < kfilength);

    if (!dctbuf) return(0);

    lnumcomponents = gnumcomponents;
    for (i=0; i<gnumcomponents; i++)
    {
        lcomphsamp[i] = gcomphsamp[i]; gcomphsamp[i] <<= 3;
        lcompvsamp[i] = gcompvsamp[i]; gcompvsamp[i] <<= 3;
        lshx[i] = glhsampmax-gcomphsampshift[i]+3;
        lshy[i] = glvsampmax-gcompvsampshift[i]+3;
    }
    lcomphsampshift0 = gcomphsampshift[0];
    lcompvsampshift0 = gcompvsampshift[0];
    lcomphvsamp0 = (lcomphsamp[0]<<lcompvsampshift0);
    for (y=0; y<ydim; y+=gcompvsamp[0])
        for (x=0; x<xdim; x+=gcomphsamp[0])
        {
            dc = dct[0];
            for (c=0; c<gnumcomponents; c++)
                for (yy=0; yy<gcompvsamp[c]; yy+=8)
                    for (xx=0; xx<gcomphsamp[c]; xx+=8,dc+=64)
                    {
                        dcs = &dctptr[c][(((y+yy)>>lshy[c])*dctx[c] + ((x+xx)>>lshx[c]))<<6];
                        quanptr = &quantab[gcompquantab[c]][0];
                        for (z=0; z<64; z++) dc[z] = ((int32_t)dcs[zigit[z]])*quanptr[z];
                        invdct8x8(dc,0xff);
                    }
            kplib_yrbrend_func(x,y,&dct[0][0]);
        }

    Bfree(dctbuf); return(0);
}

//==============================  KPEGILIB ends ==============================
//================================ GIF begins ================================

static uint8_t suffix[4100], filbuffer[768], tempstack[4096];
static int32_t prefix[4100];

static int32_t kgifrend(const char *kfilebuf, int32_t kfilelength,
                        intptr_t daframeplace, int32_t dabytesperline, int32_t daxres, int32_t dayres,
                        int32_t daglobxoffs, int32_t daglobyoffs)
{
    int32_t i, x, y, xsiz, ysiz, yinc, xend, xspan, yspan, currstr, numbitgoal;
    int32_t lzcols, dat, blocklen, bitcnt, xoff, transcol, backcol, *lptr;
    intptr_t yoff;
    char numbits, startnumbits, chunkind, ilacefirst;
    const uint8_t *ptr, *cptr = NULL;

    UNREFERENCED_PARAMETER(kfilelength);

    coltype = 3; bitdepth = 8; //For PNGOUT

    if ((kfilebuf[0] != 'G') || (kfilebuf[1] != 'I') || (kfilebuf[2] != 'F')) return(-1);
    paleng = (1<<((kfilebuf[10]&7)+1));
    ptr = (uint8_t *)&kfilebuf[13];
    if (kfilebuf[10]&128) { cptr = ptr; ptr += paleng*3; }
    transcol = -1;
    while ((chunkind = *ptr++) == '!')
    {
        //! 0xf9 leng flags ? ? transcol
        if (ptr[0] == 0xf9) { if (ptr[2]&1) transcol = (int32_t)(((uint8_t)ptr[5])); }
        ptr++;
        do { i = *ptr++; ptr += i; }
        while (i);
    }
    if (chunkind != ',') return(-1);

    xoff = SSWAPIB(*(uint16_t *)&ptr[0]);
    yoff = SSWAPIB(*(uint16_t *)&ptr[2]);
    xspan = SSWAPIB(*(uint16_t *)&ptr[4]);
    yspan = SSWAPIB(*(uint16_t *)&ptr[6]); ptr += 9;
    if (ptr[-1]&64) { yinc = 8; ilacefirst = 1; }
    else { yinc = 1; ilacefirst = 0; }
    if (ptr[-1]&128)
    {
        paleng = (1<<((ptr[-1]&7)+1));
        cptr = ptr; ptr += paleng*3;
    }

    for (i=0; i<paleng; i++)
        palcol[i] = LSWAPIB((((int32_t)cptr[i*3])<<16) + (((int32_t)cptr[i*3+1])<<8) + ((int32_t)cptr[i*3+2]) + 0xff000000);
    for (; i<256; i++) palcol[i] = LSWAPIB(0xff000000);
    if (transcol >= 0) palcol[transcol] &= LSWAPIB(~0xff000000);

    //Handle GIF files with different logical&image sizes or non-0 offsets (added 05/15/2004)
    xsiz = SSWAPIB(*(uint16_t *)&kfilebuf[6]);
    ysiz = SSWAPIB(*(uint16_t *)&kfilebuf[8]);
    if ((xoff != 0) || (yoff != 0) || (xsiz != xspan) || (ysiz != yspan))
    {
        int32_t xx[4], yy[4];
        if (kfilebuf[10]&128) backcol = palcol[(uint8_t)kfilebuf[11]]; else backcol = 0;

        //Fill border to backcol
        xx[0] = max(daglobxoffs           ,     0); yy[0] = max(daglobyoffs           ,     0);
        xx[1] = min(daglobxoffs+xoff      ,daxres); yy[1] = min(daglobyoffs+yoff      ,dayres);
        xx[2] = max(daglobxoffs+xoff+xspan,     0); yy[2] = min(daglobyoffs+yoff+yspan,dayres);
        xx[3] = min(daglobxoffs+xsiz      ,daxres); yy[3] = min(daglobyoffs+ysiz      ,dayres);

        lptr = (int32_t *)(yy[0]*dabytesperline+daframeplace);
        for (y=yy[0]; y<yy[1]; y++,lptr=(int32_t *)(((intptr_t)lptr)+dabytesperline))
            for (x=xx[0]; x<xx[3]; x++) lptr[x] = backcol;
        for (; y<yy[2]; y++,lptr=(int32_t *)(((intptr_t)lptr)+dabytesperline))
        {
            for (x=xx[0]; x<xx[1]; x++) lptr[x] = backcol;
            for (x=xx[2]; x<xx[3]; x++) lptr[x] = backcol;
        }
        for (; y<yy[3]; y++,lptr=(int32_t *)(((intptr_t)lptr)+dabytesperline))
            for (x=xx[0]; x<xx[3]; x++) lptr[x] = backcol;

        daglobxoffs += xoff; //Offset bitmap image by extra amount
        daglobyoffs += yoff;
    }

    xspan += daglobxoffs;
    yspan += daglobyoffs;  //UGLY HACK
    y = daglobyoffs;
    if ((uint32_t)y < (uint32_t)dayres)
        { yoff = y*dabytesperline+daframeplace; x = daglobxoffs; xend = xspan; }
    else
        { x = daglobxoffs+0x80000000; xend = xspan+0x80000000; }

    lzcols = (1<<(*ptr)); startnumbits = (uint8_t)((*ptr)+1); ptr++;
    for (i=lzcols-1; i>=0; i--) { suffix[i] = (uint8_t)(prefix[i] = i); }
    currstr = lzcols+2; numbits = startnumbits; numbitgoal = (lzcols<<1);
    blocklen = *ptr++;
    Bmemcpy(filbuffer,ptr,blocklen); ptr += blocklen;
    bitcnt = 0;
    while (1)
    {
        dat = (LSWAPIB(*(int32_t *)&filbuffer[bitcnt>>3])>>(bitcnt&7)) & (numbitgoal-1);
        bitcnt += numbits;
        if ((bitcnt>>3) > blocklen-3)
        {
            *(int16_t *)filbuffer = *(int16_t *)&filbuffer[bitcnt>>3];
            i = blocklen-(bitcnt>>3);
            blocklen = (int32_t)*ptr++;
            Bmemcpy(&filbuffer[i],ptr,blocklen); ptr += blocklen;
            bitcnt &= 7; blocklen += i;
        }
        if (dat == lzcols)
        {
            currstr = lzcols+2; numbits = startnumbits; numbitgoal = (lzcols<<1);
            continue;
        }
        if ((currstr == numbitgoal) && (numbits < 12))
            { numbits++; numbitgoal <<= 1; }

        prefix[currstr] = dat;
        for (i=0; dat>=lzcols; dat=prefix[dat]) tempstack[i++] = suffix[dat];
        tempstack[i] = (uint8_t)prefix[dat];
        suffix[currstr-1] = suffix[currstr] = (uint8_t)dat;

        for (; i>=0; i--)
        {
            if ((uint32_t)x < (uint32_t)daxres)
                *(int32_t *)(yoff+(x<<2)) = palcol[(int32_t)tempstack[i]];
            x++;
            if (x == xend)
            {
                y += yinc;
                if (y >= yspan)
                    switch (yinc)
                    {
                    case 8: if (!ilacefirst) { y = daglobyoffs+2; yinc = 4; break; }
                        ilacefirst = 0; y = daglobyoffs+4; yinc = 8; break;
                    case 4: y = daglobyoffs+1; yinc = 2; break;
                    case 2: case 1: return(0);
                    }
                if ((uint32_t)y < (uint32_t)dayres)
                    { yoff = y*dabytesperline+daframeplace; x = daglobxoffs; xend = xspan; }
                else
                    { x = daglobxoffs+0x80000000; xend = xspan+0x80000000; }
            }
        }
        currstr++;
    }
}

//===============================  GIF ends ==================================
//==============================  CEL begins =================================

//   //old .CEL format:
//int16_t id = 0x9119, xdim, ydim, xoff, yoff, id = 0x0008;
//int32_t imagebytes, filler[4];
//char pal6bit[256][3], image[ydim][xdim];
static int32_t kcelrend(const char *buf, int32_t fleng,
                        intptr_t daframeplace, int32_t dabytesperline, int32_t daxres, int32_t dayres,
                        int32_t daglobxoffs, int32_t daglobyoffs)
{
    int32_t i, x, y, x0, x1, y0, y1, xsiz, ysiz;
    const char *cptr;

    UNREFERENCED_PARAMETER(fleng);
    UNREFERENCED_PARAMETER(daglobxoffs);

    coltype = 3; bitdepth = 8; paleng = 256; //For PNGOUT

    xsiz = (int32_t)SSWAPIB(*(uint16_t *)&buf[2]); if (xsiz <= 0) return(-1);
    ysiz = (int32_t)SSWAPIB(*(uint16_t *)&buf[4]); if (ysiz <= 0) return(-1);

    cptr = &buf[32];
    for (i=0; i<256; i++)
    {
        palcol[i] = (((int32_t)cptr[0])<<18) +
                    (((int32_t)cptr[1])<<10) +
                    (((int32_t)cptr[2])<< 2) + LSWAPIB(0xff000000);
        cptr += 3;
    }

    x0 = daglobyoffs; x1 = xsiz+daglobyoffs;
    y0 = daglobyoffs; y1 = ysiz+daglobyoffs;
    for (y=y0; y<y1; y++)
        for (x=x0; x<x1; x++)
        {
            if (((uint32_t)x < (uint32_t)daxres) && ((uint32_t)y < (uint32_t)dayres))
                *(int32_t *)(y*dabytesperline+x*4+daframeplace) = palcol[cptr[0]];
            cptr++;
        }
    return(0);
}

//===============================  CEL ends ==================================
//=============================  TARGA begins ================================

static int32_t ktgarend(const char *header, int32_t fleng,
                        intptr_t daframeplace, int32_t dabytesperline, int32_t daxres, int32_t dayres,
                        int32_t daglobxoffs, int32_t daglobyoffs)
{
    int32_t i = 0, x, y, pi, xi, yi, x0, x1, y0, y1, xsiz, ysiz, rlestat, colbyte, pixbyte;
    intptr_t p;

    const uint8_t *fptr, *cptr = NULL, *nptr;

    //Ugly and unreliable identification for .TGA!
    if ((fleng < 19) || (header[1]&0xfe)) return(-1);
    if ((header[2] >= 12) || (!((1<<header[2])&0xe0e))) return(-1);
    if ((header[16]&7) || (header[16] == 0) || (header[16] > 32)) return(-1);
    if (header[17]&0xc0) return(-1);

    fptr = (uint8_t *)&header[header[0]+18];
    xsiz = (int32_t)SSWAPIB(*(uint16_t *)&header[12]); if (xsiz <= 0) return(-1);
    ysiz = (int32_t)SSWAPIB(*(uint16_t *)&header[14]); if (ysiz <= 0) return(-1);
    colbyte = ((((int32_t)header[16])+7)>>3);

    if (header[1] == 1)
    {
        pixbyte = ((((int32_t)header[7])+7)>>3);
        cptr = &fptr[-SSWAPIB(*(uint16_t *)&header[3])*pixbyte];
        fptr += SSWAPIB(*(uint16_t *)&header[5])*pixbyte;
    }
    else pixbyte = colbyte;

    switch (pixbyte) //For PNGOUT
    {
    case 1: coltype = 0; bitdepth = 8; palcol[0] = LSWAPIB(0xff000000);
        for (i=1; i<256; i++) palcol[i] = palcol[i-1]+LSWAPIB(0x10101); break;
    case 2: case 3: coltype = 2; break;
    case 4: coltype = 6; break;
    }

    if (!(header[17]&16)) { x0 = 0;      x1 = xsiz; xi = 1; }
    else { x0 = xsiz-1; x1 = -1;   xi =-1; }
    if (header[17]&32) { y0 = 0;      y1 = ysiz; yi = 1; pi = dabytesperline; }
    else { y0 = ysiz-1; y1 = -1;   yi =-1; pi =-dabytesperline; }
    x0 += daglobxoffs; y0 += daglobyoffs;
    x1 += daglobxoffs; y1 += daglobyoffs;
    if (header[2] < 8) rlestat = -2; else rlestat = -1;

    p = y0*dabytesperline+daframeplace;
    for (y=y0; y!=y1; y+=yi,p+=pi)
        for (x=x0; x!=x1; x+=xi)
        {
            if (rlestat < 128)
            {
                if ((rlestat&127) == 127) { rlestat = (int32_t)fptr[0]; fptr++; }
                if (header[1] == 1)
                {
                    if (colbyte == 1) i = fptr[0];
                    else i = (int32_t)SSWAPIB(*(uint16_t *)&fptr[0]);
                    nptr = &cptr[i*pixbyte];
                }
                else nptr = fptr;

                switch (pixbyte)
                {
                case 1: i = palcol[(int32_t)nptr[0]]; break;
                case 2: i = (int32_t)SSWAPIB(*(uint16_t *)&nptr[0]);
                    i = LSWAPIB(((i&0x7c00)<<9) + ((i&0x03e0)<<6) + ((i&0x001f)<<3) + 0xff000000);
                    break;
                case 3: i = (*(int32_t *)&nptr[0]) | LSWAPIB(0xff000000); break;
                case 4: i = (*(int32_t *)&nptr[0]); break;
                }
                fptr += colbyte;
            }
            if (rlestat >= 0) rlestat--;

            if (((uint32_t)x < (uint32_t)daxres) && ((uint32_t)y < (uint32_t)dayres))
                *(int32_t *)(x*4+p) = i;
        }
    return(0);
}

//==============================  TARGA ends =================================
//==============================  BMP begins =================================
//TODO: handle BI_RLE8 and BI_RLE4 (compression types 1&2 respectively)
//                        ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
//                        ³  0(2): "BM"   ³
// ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³ 10(4): rastoff³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
// ³headsiz=12 (OS/2 1.x)³³ 14(4): headsiz³ ³ All new formats: ³
//ÚÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÁÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÁÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
//³ 18(2): xsiz                         ³ 18(4): xsiz                                  ³
//³ 20(2): ysiz                         ³ 22(4): ysiz                                  ³
//³ 22(2): planes (always 1)            ³ 26(2): planes (always 1)                     ³
//³ 24(2): cdim (1,4,8,24)              ³ 28(2): cdim (1,4,8,16,24,32)                 ³
//³ if (cdim < 16)                      ³ 30(4): compression (0,1,2,3!?,4)             ³
//³    26(rastoff-14-headsiz): pal(bgr) ³ 34(4): (bitmap data size+3)&3                ³
//³                                     ³ 46(4): N colors (0=2^cdim)                   ³
//³                                     ³ if (cdim < 16)                               ³
//³                                     ³    14+headsiz(rastoff-14-headsiz): pal(bgr0) ³
//ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
//                      ³ rastoff(?): bitmap data ³
//                      ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
static int32_t kbmprend(const char *buf, int32_t fleng,
                        intptr_t daframeplace, int32_t dabytesperline, int32_t daxres, int32_t dayres,
                        int32_t daglobxoffs, int32_t daglobyoffs)
{
    int32_t i, j, x, y, x0, x1, y0, y1, rastoff, headsiz, xsiz, ysiz, cdim, comp, cptrinc, *lptr;
    const char *cptr;

    UNREFERENCED_PARAMETER(fleng);

    headsiz = *(int32_t *)&buf[14];
    if (headsiz == LSWAPIB(12)) //OS/2 1.x (old format)
    {
        if (*(int16_t *)(&buf[22]) != SSWAPIB(1)) return(-1);
        xsiz = (int32_t)SSWAPIB(*(uint16_t *)&buf[18]);
        ysiz = (int32_t)SSWAPIB(*(uint16_t *)&buf[20]);
        cdim = (int32_t)SSWAPIB(*(uint16_t *)&buf[24]);
        comp = 0;
    }
    else //All newer formats...
    {
        if (*(int16_t *)(&buf[26]) != SSWAPIB(1)) return(-1);
        xsiz = LSWAPIB(*(int32_t *)&buf[18]);
        ysiz = LSWAPIB(*(int32_t *)&buf[22]);
        cdim = (int32_t)SSWAPIB(*(uint16_t *)&buf[28]);
        comp = LSWAPIB(*(int32_t *)&buf[30]);
    }
    if ((xsiz <= 0) || (!ysiz)) return(-1);
    //cdim must be: (1,4,8,16,24,32)
    if (((uint32_t)(cdim-1) >= (uint32_t)32) || (!((1<<cdim)&0x1010113))) return(-1);
    if ((comp != 0) && (comp != 3)) return(-1);

    rastoff = LSWAPIB(*(int32_t *)&buf[10]);

    if (cdim < 16)
    {
        if (cdim == 2) { palcol[0] = 0xffffffff; palcol[1] = LSWAPIB(0xff000000); }
        if (headsiz == LSWAPIB(12)) j = 3; else j = 4;
        for (i=0,cptr=&buf[headsiz+14]; cptr<&buf[rastoff]; i++,cptr+=j)
            palcol[i] = ((*(int32_t *)&cptr[0])|LSWAPIB(0xff000000));
        coltype = 3; bitdepth = (int8_t)cdim; paleng = i; //For PNGOUT
    }
    else if (!(cdim&15))
    {
        coltype = 2;
        switch (cdim)
        {
        case 16: palcol[0] = 10; palcol[1] = 5; palcol[2] = 0; palcol[3] = 5; palcol[4] = 5; palcol[5] = 5; break;
        case 32: palcol[0] = 16; palcol[1] = 8; palcol[2] = 0; palcol[3] = 8; palcol[4] = 8; palcol[5] = 8; break;
        }
        if (comp == 3) //BI_BITFIELD (RGB masks)
        {
            for (i=0; i<3; i++)
            {
                j = *(int32_t *)&buf[headsiz+(i<<2)+14];
                for (palcol[i]=0; palcol[i]<32; palcol[i]++)
                {
                    if (j&1) break;
                    j = (((uint32_t)j)>>1);
                }
                for (palcol[i+3]=0; palcol[i+3]<32; palcol[i+3]++)
                {
                    if (!(j&1)) break;
                    j = (((uint32_t)j)>>1);
                }
            }
        }
        palcol[0] = 24-(palcol[0]+palcol[3]);
        palcol[1] = 16-(palcol[1]+palcol[4]);
        palcol[2] =  8-(palcol[2]+palcol[5]);
        palcol[3] = ((-1<<(24-palcol[3]))&0x00ff0000);
        palcol[4] = ((-1<<(16-palcol[4]))&0x0000ff00);
        palcol[5] = ((-1<<(8-palcol[5]))&0x000000ff);
    }

    cptrinc = (((xsiz*cdim+31)>>3)&~3); cptr = &buf[rastoff];
    if (ysiz < 0) { ysiz = -ysiz; }
    else { cptr = &cptr[(ysiz-1)*cptrinc]; cptrinc = -cptrinc; }

    x0 = daglobxoffs; x1 = xsiz+daglobxoffs;
    y0 = daglobyoffs; y1 = ysiz+daglobyoffs;
    if ((x0 >= daxres) || (x1 <= 0) || (y0 >= dayres) || (y1 <= 0)) return(0);
    if (x0 < 0) x0 = 0;
    if (x1 > daxres) x1 = daxres;
    for (y=y0; y<y1; y++,cptr=&cptr[cptrinc])
    {
        if ((uint32_t)y >= (uint32_t)dayres) continue;
        lptr = (int32_t *)(y*dabytesperline-(daglobyoffs<<2)+daframeplace);
        switch (cdim)
        {
        case  1: for (x=x0; x<x1; x++) lptr[x] = palcol[(int32_t)((cptr[x>>3]>>((x&7)^7))&1)]; break;
        case  4: for (x=x0; x<x1; x++) lptr[x] = palcol[(int32_t)((cptr[x>>1]>>(((x&1)^1)<<2))&15)]; break;
        case  8: for (x=x0; x<x1; x++) lptr[x] = palcol[(int32_t)(cptr[x])]; break;
        case 16: for (x=x0; x<x1; x++)
                {
                    i = ((int32_t)(*(int16_t *)&cptr[x<<1]));
                    lptr[x] = (_lrotl(i,palcol[0])&palcol[3]) +
                              (_lrotl(i,palcol[1])&palcol[4]) +
                              (_lrotl(i,palcol[2])&palcol[5]) + LSWAPIB(0xff000000);
                } break;
        case 24: for (x=x0; x<x1; x++) lptr[x] = ((*(int32_t *)&cptr[x*3])|LSWAPIB(0xff000000)); break;
        case 32: for (x=x0; x<x1; x++)
                {
                    i = (*(int32_t *)&cptr[x<<2]);
                    lptr[x] = (_lrotl(i,palcol[0])&palcol[3]) +
                              (_lrotl(i,palcol[1])&palcol[4]) +
                              (_lrotl(i,palcol[2])&palcol[5]) + LSWAPIB(0xff000000);
                } break;
        }

    }
    return(0);
}
//===============================  BMP ends ==================================
//==============================  PCX begins =================================
//Note: currently only supports 8 and 24 bit PCX
static int32_t kpcxrend(const char *buf, int32_t fleng,
                        intptr_t daframeplace, int32_t dabytesperline, int32_t daxres, int32_t dayres,
                        int32_t daglobxoffs, int32_t daglobyoffs)
{
    int32_t  j, x, y, nplanes, x0, x1, y0, y1, bpl, xsiz, ysiz;
    intptr_t p,i;
    uint8_t c, *cptr;

    if (*(int32_t *)buf != LSWAPIB(0x0801050a)) return(-1);
    xsiz = SSWAPIB(*(int16_t *)&buf[ 8])-SSWAPIB(*(int16_t *)&buf[4])+1; if (xsiz <= 0) return(-1);
    ysiz = SSWAPIB(*(int16_t *)&buf[10])-SSWAPIB(*(int16_t *)&buf[6])+1; if (ysiz <= 0) return(-1);
    //buf[3]: bpp/plane:{1,2,4,8}
    nplanes = buf[65]; //nplanes*bpl bytes per scanline; always be decoding break at the end of scan line
    bpl = SSWAPIB(*(int16_t *)&buf[66]); //#bytes per scanline. Must be EVEN. May have unused data.
    if (nplanes == 1)
    {
        //if (buf[fleng-769] != 12) return(-1); //Some PCX are buggy!
        cptr = (uint8_t *)&buf[fleng-768];
        for (i=0; i<256; i++)
        {
            palcol[i] = (((int32_t)cptr[0])<<16) +
                        (((int32_t)cptr[1])<< 8) +
                        (((int32_t)cptr[2])) + LSWAPIB(0xff000000);
            cptr += 3;
        }
        coltype = 3; bitdepth = 8; paleng = 256; //For PNGOUT
    }
    else if (nplanes == 3)
    {
        coltype = 2;

        //Make sure background is opaque (since 24-bit PCX renderer doesn't do it)
        x0 = max(daglobxoffs,0); x1 = min(xsiz+daglobxoffs,daxres);
        y0 = max(daglobyoffs,0); y1 = min(ysiz+daglobyoffs,dayres);
        i = y0*dabytesperline + daframeplace+3;
        for (y=y0; y<y1; y++,i+=dabytesperline)
            for (x=x0; x<x1; x++) *(char *)((x<<2)+i) = 255;
    }

    x = x0 = daglobxoffs; x1 = xsiz+daglobxoffs;
    y = y0 = daglobyoffs; y1 = ysiz+daglobyoffs;
    cptr = (uint8_t *)&buf[128];
    p = y*dabytesperline+daframeplace;

    if (bpl > xsiz) { daxres = min(daxres,x1); x1 += bpl-xsiz; }

    j = nplanes-1; daxres <<= 2; x0 <<= 2; x1 <<= 2; x <<= 2; x += j;
    if (nplanes == 1) //8-bit PCX
    {
        do
        {
            c = *cptr++; if (c < 192) i = 1; else { i = (c&63); c = *cptr++; }
            j = palcol[(int32_t)c];
            for (; i; i--)
            {
                if ((uint32_t)y < (uint32_t)dayres)
                    if ((uint32_t)x < (uint32_t)daxres) *(int32_t *)(x+p) = j;
                x += 4; if (x >= x1) { x = x0; y++; p += dabytesperline; }
            }
        }
        while (y < y1);
    }
    else if (nplanes == 3) //24-bit PCX
    {
        do
        {
            c = *cptr++; if (c < 192) i = 1; else { i = (c&63); c = *cptr++; }
            for (; i; i--)
            {
                if ((uint32_t)y < (uint32_t)dayres)
                    if ((uint32_t)x < (uint32_t)daxres) *(char *)(x+p) = c;
                x += 4; if (x >= x1) { j--; if (j < 0) { j = 3-1; y++; p += dabytesperline; } x = x0+j; }
            }
        }
        while (y < y1);
    }

    return(0);
}

//===============================  PCX ends ==================================
//==============================  DDS begins =================================

//Note:currently supports: DXT1,DXT2,DXT3,DXT4,DXT5,A8R8G8B8
static int32_t kddsrend(const char *buf, int32_t leng,
                        intptr_t frameptr, int32_t bpl, int32_t xdim, int32_t ydim, int32_t xoff, int32_t yoff)
{
    int32_t x, y, z = 0, xx, yy, xsiz, ysiz, dxt, al[2], ai, k, v, c0, c1, stride;
    intptr_t j;
    uint32_t lut[256], r[4], g[4], b[4], a[8], rr, gg, bb;
    uint8_t *uptr, *wptr;

    UNREFERENCED_PARAMETER(leng);

    xsiz = LSWAPIB(*(int32_t *)&buf[16]);
    ysiz = LSWAPIB(*(int32_t *)&buf[12]);
    if ((*(int32_t *)&buf[80])&LSWAPIB(64)) //Uncompressed supports only A8R8G8B8 for now
    {
        if ((*(int32_t *)&buf[88]) != LSWAPIB(32)) return(-1);
        if ((*(int32_t *)&buf[92]) != LSWAPIB(0x00ff0000)) return(-1);
        if ((*(int32_t *)&buf[96]) != LSWAPIB(0x0000ff00)) return(-1);
        if ((*(int32_t *)&buf[100]) != LSWAPIB(0x000000ff)) return(-1);
        if ((*(uint32_t *)&buf[104]) != LSWAPIB(0xff000000)) return(-1);
        buf += 128;

        j = yoff*bpl + (xoff<<2) + frameptr; xx = (xsiz<<2);
        if (xoff < 0) { j -= (xoff<<2); buf -= (xoff<<2); xsiz += xoff; }
        xsiz = (min(xsiz,xdim-xoff)<<2); ysiz = min(ysiz,ydim);
        for (y=0; y<ysiz; y++,j+=bpl,buf+=xx)
        {
            if ((uint32_t)(y+yoff) >= (uint32_t)ydim) continue;
            Bmemcpy((void *)j,(void *)buf,xsiz);
        }
        return(0);
    }
    if (!((*(int32_t *)&buf[80])&LSWAPIB(4))) return(-1); //FOURCC invalid
    dxt = buf[87]-'0';
    if ((buf[84] != 'D') || (buf[85] != 'X') || (buf[86] != 'T') || (dxt < 1) || (dxt > 5)) return(-1);
    buf += 128;

    if (!(dxt&1))
    {
        for (z=256-1; z>0; z--) lut[z] = (255<<16)/z;
        lut[0] = (1<<16);
    }
    if (dxt == 1) stride = (xsiz<<1); else stride = (xsiz<<2);

    for (y=0; y<ysiz; y+=4,buf+=stride)
        for (x=0; x<xsiz; x+=4)
        {
            if (dxt == 1) uptr = (uint8_t *)(((intptr_t)buf)+(x<<1));
            else uptr = (uint8_t *)(((intptr_t)buf)+(x<<2)+8);
            c0 = SSWAPIB(*(uint16_t *)&uptr[0]);
            r[0] = ((c0>>8)&0xf8); g[0] = ((c0>>3)&0xfc); b[0] = ((c0<<3)&0xfc); a[0] = 255;
            c1 = SSWAPIB(*(uint16_t *)&uptr[2]);
            r[1] = ((c1>>8)&0xf8); g[1] = ((c1>>3)&0xfc); b[1] = ((c1<<3)&0xfc); a[1] = 255;
            if ((c0 > c1) || (dxt != 1))
            {
                r[2] = (((r[0]*2 + r[1] + 1)*(65536/3))>>16);
                g[2] = (((g[0]*2 + g[1] + 1)*(65536/3))>>16);
                b[2] = (((b[0]*2 + b[1] + 1)*(65536/3))>>16); a[2] = 255;
                r[3] = (((r[0] + r[1]*2 + 1)*(65536/3))>>16);
                g[3] = (((g[0] + g[1]*2 + 1)*(65536/3))>>16);
                b[3] = (((b[0] + b[1]*2 + 1)*(65536/3))>>16); a[3] = 255;
            }
            else
            {
                r[2] = (r[0] + r[1])>>1;
                g[2] = (g[0] + g[1])>>1;
                b[2] = (b[0] + b[1])>>1; a[2] = 255;
                r[3] = g[3] = b[3] = a[3] = 0; //Transparent
            }
            v = LSWAPIB(*(int32_t *)&uptr[4]);
            if (dxt >= 4)
            {
                a[0] = uptr[-8]; a[1] = uptr[-7]; k = a[1]-a[0];
                if (k < 0)
                {
                    z = a[0]*6 + a[1] + 3;
                    for (j=2; j<8; j++) { a[j] = ((z*(65536/7))>>16); z += k; }
                }
                else
                {
                    z = a[0]*4 + a[1] + 2;
                    for (j=2; j<6; j++) { a[j] = ((z*(65536/5))>>16); z += k; }
                    a[6] = 0; a[7] = 255;
                }
                al[0] = LSWAPIB(*(int32_t *)&uptr[-6]);
                al[1] = LSWAPIB(*(int32_t *)&uptr[-3]);
            }
            wptr = (uint8_t *)((y+yoff)*bpl + ((x+xoff)<<2) + frameptr);
            ai = 0;
            for (yy=0; yy<4; yy++,wptr+=bpl)
            {
                if ((uint32_t)(y+yy+yoff) >= (uint32_t)ydim) { ai += 4; continue; }
                for (xx=0; xx<4; xx++,ai++)
                {
                    if ((uint32_t)(x+xx+xoff) >= (uint32_t)xdim) continue;

                    j = ((v>>(ai<<1))&3);
                    switch (dxt)
                    {
                    case 1: z = a[j]; break;
                    case 2: case 3: z = ((uptr[(ai>>1)-8] >> ((xx&1)<<2))&15)*17; break;
                    case 4: case 5: z = a[(al[yy>>1] >> ((ai&7)*3))&7]; break;
                    }
                    rr = r[j]; gg = g[j]; bb = b[j];
                    if (!(dxt&1))
                    {
                        bb = min((bb*lut[z])>>16,255);
                        gg = min((gg*lut[z])>>16,255);
                        rr = min((rr*lut[z])>>16,255);
                    }
                    wptr[(xx<<2)+0] = (uint8_t)bb;
                    wptr[(xx<<2)+1] = (uint8_t)gg;
                    wptr[(xx<<2)+2] = (uint8_t)rr;
                    wptr[(xx<<2)+3] = (uint8_t)z;
                }
            }
        }
    return(0);
}

//===============================  DDS ends ==================================
//=================== External picture interface begins ======================

void kpgetdim(const char *buf, int32_t leng, int32_t *xsiz, int32_t *ysiz)
{
    int32_t *lptr;
    const uint8_t *cptr;
    uint8_t *ubuf = (uint8_t *)buf;

    (*xsiz) = (*ysiz) = 0; if (leng < 16) return;
    if ((ubuf[0] == 0x89) && (ubuf[1] == 0x50)) //.PNG
    {
        lptr = (int32_t *)buf;
        if ((lptr[0] != LSWAPIB(0x474e5089)) || (lptr[1] != LSWAPIB(0x0a1a0a0d))) return;
        lptr = &lptr[2];
        while (((uintptr_t)lptr-(uintptr_t)buf) < (uintptr_t)(leng-16))
        {
            if (lptr[1] == LSWAPIB(0x52444849)) //IHDR
                {(*xsiz) = LSWAPIL(lptr[2]); (*ysiz) = LSWAPIL(lptr[3]); break; }
            lptr = (int32_t *)((intptr_t)lptr + LSWAPIL(lptr[0]) + 12);
        }
    }
    else if ((ubuf[0] == 0xff) && (ubuf[1] == 0xd8)) //.JPG
    {
        cptr = (uint8_t *)&buf[2];
        while (((uintptr_t)cptr-(uintptr_t)buf) < (uintptr_t)(leng-8))
        {
            if ((cptr[0] != 0xff) || (cptr[1] == 0xff)) { cptr++; continue; }
            if ((uint32_t)(cptr[1]-0xc0) < 3)
            {
                (*ysiz) = SSWAPIL(*(uint16_t *)&cptr[5]);
                (*xsiz) = SSWAPIL(*(uint16_t *)&cptr[7]);
                break;
            }
            cptr = &cptr[SSWAPIL(*(uint16_t *)&cptr[2])+2];
        }
    }
    else if ((ubuf[0] == 'G') && (ubuf[1] == 'I') && (ubuf[2] == 'F')) //.GIF
    {
        (*xsiz) = (int32_t)SSWAPIB(*(uint16_t *)&buf[6]);
        (*ysiz) = (int32_t)SSWAPIB(*(uint16_t *)&buf[8]);
    }
    else if ((ubuf[0] == 0x19) && (ubuf[1] == 0x91) && (ubuf[10] == 8) && (ubuf[11] == 0)) //old .CEL/.PIC
    {
        (*xsiz) = (int32_t)SSWAPIB(*(uint16_t *)&buf[2]);
        (*ysiz) = (int32_t)SSWAPIB(*(uint16_t *)&buf[4]);
    }
    else if ((ubuf[0] == 'B') && (ubuf[1] == 'M')) //.BMP
    {
        if (*(int32_t *)(&buf[14]) == LSWAPIB(12)) //OS/2 1.x (old format)
        {
            if (*(int16_t *)(&buf[22]) != SSWAPIB(1)) return;
            (*xsiz) = (int32_t)SSWAPIB(*(uint16_t *)&buf[18]);
            (*ysiz) = (int32_t)SSWAPIB(*(uint16_t *)&buf[20]);
        }
        else //All newer formats...
        {
            if (*(int16_t *)(&buf[26]) != SSWAPIB(1)) return;
            (*xsiz) = LSWAPIB(*(int32_t *)&buf[18]);
            (*ysiz) = LSWAPIB(*(int32_t *)&buf[22]);
        }
    }
    else if (*(int32_t *)ubuf == LSWAPIB(0x0801050a)) //.PCX
    {
        (*xsiz) = SSWAPIB(*(int16_t *)&buf[ 8])-SSWAPIB(*(int16_t *)&buf[4])+1;
        (*ysiz) = SSWAPIB(*(int16_t *)&buf[10])-SSWAPIB(*(int16_t *)&buf[6])+1;
    }
    else if ((*(int32_t *)ubuf == LSWAPIB(0x20534444)) && (*(int32_t *)&ubuf[4] == LSWAPIB(124))) //.DDS
    {
        (*xsiz) = LSWAPIB(*(int32_t *)&buf[16]);
        (*ysiz) = LSWAPIB(*(int32_t *)&buf[12]);
    }
    else
    {
        //Unreliable .TGA identification - this MUST be final case!
        if ((leng >= 19) && (!(ubuf[1]&0xfe)))
            if ((ubuf[2] < 12) && ((1<<ubuf[2])&0xe0e))
                if ((!(ubuf[16]&7)) && (ubuf[16] != 0) && (ubuf[16] <= 32))
                    if (!(buf[17]&0xc0))
                    {
                        (*xsiz) = (int32_t)SSWAPIB(*(uint16_t *)&buf[12]);
                        (*ysiz) = (int32_t)SSWAPIB(*(uint16_t *)&buf[14]);
                    }
    }
}

int32_t kprender(const char *buf, int32_t leng, intptr_t frameptr, int32_t bpl,
                 int32_t xdim, int32_t ydim, int32_t xoff, int32_t yoff)
{
    uint8_t *ubuf = (uint8_t *)buf;

    paleng = 0; bakcol = 0; numhufblocks = zlibcompflags = 0; filtype = -1;

    if ((ubuf[0] == 0x89) && (ubuf[1] == 0x50)) //.PNG
        return(kpngrend(buf,leng,frameptr,bpl,xdim,ydim,xoff,yoff));

    if ((ubuf[0] == 0xff) && (ubuf[1] == 0xd8)) //.JPG
        return(kpegrend(buf,leng,frameptr,bpl,xdim,ydim,xoff,yoff));

    if ((ubuf[0] == 'G') && (ubuf[1] == 'I') && (ubuf[2] == 'F')) //.GIF
        return(kgifrend(buf,leng,frameptr,bpl,xdim,ydim,xoff,yoff));

    if ((ubuf[0] == 0x19) && (ubuf[1] == 0x91) && (ubuf[10] == 8) && (ubuf[11] == 0)) //old .CEL/.PIC
        return(kcelrend(buf,leng,frameptr,bpl,xdim,ydim,xoff,yoff));

    if ((ubuf[0] == 'B') && (ubuf[1] == 'M')) //.BMP
        return(kbmprend(buf,leng,frameptr,bpl,xdim,ydim,xoff,yoff));

    if (*(int32_t *)ubuf == LSWAPIB(0x0801050a)) //.PCX
        return(kpcxrend(buf,leng,frameptr,bpl,xdim,ydim,xoff,yoff));

    if ((*(int32_t *)ubuf == LSWAPIB(0x20534444)) && (*(int32_t *)&ubuf[4] == LSWAPIB(124))) //.DDS
        return(kddsrend(buf,leng,frameptr,bpl,xdim,ydim,xoff,yoff));

    //Unreliable .TGA identification - this MUST be final case!
    if ((leng >= 19) && (!(ubuf[1]&0xfe)))
        if ((ubuf[2] < 12) && ((1<<ubuf[2])&0xe0e))
            if ((!(ubuf[16]&7)) && (ubuf[16] != 0) && (ubuf[16] <= 32))
                if (!(ubuf[17]&0xc0))
                    return(ktgarend(buf,leng,frameptr,bpl,xdim,ydim,xoff,yoff));

    return(-1);
}

//==================== External picture interface ends =======================

//Brute-force case-insensitive, slash-insensitive, * and ? wildcard matcher
//Given: string i and string j. string j can have wildcards
//Returns: 1:matches, 0:doesn't match
static int32_t wildmatch(const char *i, const char *j)
{
    const char *k;
    char c0, c1;

    if (!*j) return(1);
    do
    {
        if (*j == '*')
        {
            for (k=i,j++; *k; k++) if (wildmatch(k,j)) return(1);
            continue;
        }
        if (!*i) return(0);
        if (*j == '?') { i++; j++; continue; }
        c0 = *i; if ((c0 >= 'a') && (c0 <= 'z')) c0 -= 32;
        c1 = *j; if ((c1 >= 'a') && (c1 <= 'z')) c1 -= 32;
        if (c0 == '/') c0 = '\\';
        if (c1 == '/') c1 = '\\';
        if (c0 != c1) return(0);
        i++; j++;
    }
    while (*j);
    return(!*i);
}

//Same as: stricmp(st0,st1) except: '/' == '\'
static int32_t filnamcmp(const char *st0, const char *st1)
{
    int32_t i;
    char ch0, ch1;

    for (i=0; st0[i]; i++)
    {
        ch0 = st0[i]; if ((ch0 >= 'a') && (ch0 <= 'z')) ch0 -= 32;
        ch1 = st1[i]; if ((ch1 >= 'a') && (ch1 <= 'z')) ch1 -= 32;
        if (ch0 == '/') ch0 = '\\';
        if (ch1 == '/') ch1 = '\\';
        if (ch0 != ch1) return(-1);
    }
    if (!st1[i]) return(0);
    return(-1);
}

//===================== ZIP decompression code begins ========================

//format: (used by kzaddstack/kzopen to cache file name&start info)
//[char zipnam[?]\0]
//[next hashindex/-1][next index/-1][zipnam index][fileoffs][fileleng][iscomp][char filnam[?]\0]
//[next hashindex/-1][next index/-1][zipnam index][fileoffs][fileleng][iscomp][char filnam[?]\0]
//...
//[char zipnam[?]\0]
//[next hashindex/-1][next index/-1][zipnam index][fileoffs][fileleng][iscomp][char filnam[?]\0]
//[next hashindex/-1][next index/-1][zipnam index][fileoffs][fileleng][iscomp][char filnam[?]\0]
//...
#define KZHASHINITSIZE 8192
static char *kzhashbuf = 0;
static int32_t kzhashead[256], kzhashpos, kzlastfnam, kzhashsiz, kzdirnamhead = -1;

static int32_t kzcheckhashsiz(int32_t siz)
{
    int32_t i;

    if (!kzhashbuf) //Initialize hash table on first call
    {
        Bmemset(kzhashead,-1,sizeof(kzhashead));
        kzhashbuf = (char *)Bmalloc(KZHASHINITSIZE); if (!kzhashbuf) return(0);
        kzhashpos = 0; kzlastfnam = -1; kzhashsiz = KZHASHINITSIZE; kzdirnamhead = -1;
    }
    if (kzhashpos+siz > kzhashsiz) //Make sure string fits in kzhashbuf
    {
        i = kzhashsiz; do { i <<= 1; }
        while (kzhashpos+siz > i);
        kzhashbuf = (char *)Brealloc(kzhashbuf,i); if (!kzhashbuf) return(0);
        kzhashsiz = i;
    }
    return(1);
}

static int32_t kzcalchash(const char *st)
{
    int32_t i, hashind;
    char ch;

    for (i=0,hashind=0; st[i]; i++)
    {
        ch = st[i];
        if ((ch >= 'a') && (ch <= 'z')) ch -= 32;
        if (ch == '/') ch = '\\';
        hashind = (ch - hashind*3);
    }
    return(hashind%(sizeof(kzhashead)/sizeof(kzhashead[0])));
}

static int32_t kzcheckhash(const char *filnam, char **zipnam, int32_t *fileoffs, int32_t *fileleng, char *iscomp)
{
    int32_t i;

    if (!kzhashbuf) return(0);
    if (filnam[0] == '|') filnam++;
    for (i=kzhashead[kzcalchash(filnam)]; i>=0; i=(*(int32_t *)&kzhashbuf[i]))
        if (!filnamcmp(filnam,&kzhashbuf[i+21]))
        {
            (*zipnam) = &kzhashbuf[*(int32_t *)&kzhashbuf[i+8]];
            (*fileoffs) = *(int32_t *)&kzhashbuf[i+12];
            (*fileleng) = *(int32_t *)&kzhashbuf[i+16];
            (*iscomp) = kzhashbuf[i+20];
            return(1);
        }
    return(0);
}

void kzuninit()
{
    if (kzhashbuf) { Bfree(kzhashbuf); kzhashbuf = 0; }
    kzhashpos = kzhashsiz = 0; kzdirnamhead = -1;
}

//If file found, loads internal directory from ZIP/GRP into Bmemory (hash) to allow faster access later
//If file not found, assumes it's a directory and adds it to an internal list
int32_t kzaddstack(const char *filnam)
{
    FILE *fil;
    int32_t i, j, k, leng, hashind, zipnamoffs, numfiles;
    char tempbuf[260+46];

    fil = fopen(filnam,"rb");
    if (!fil) //if file not found, assume it's a directory
    {
        //Add directory name to internal list (using kzhashbuf for convenience of dynamic allocation)
        i = strlen(filnam)+5; if (!kzcheckhashsiz(i)) return(-1);
        *(int32_t *)&kzhashbuf[kzhashpos] = kzdirnamhead; kzdirnamhead = kzhashpos;
        strcpy(&kzhashbuf[kzhashpos+4],filnam);
        kzhashpos += i;

        return(-1);
    }

    //Write ZIP/GRP filename to hash
    i = strlen(filnam)+1; if (!kzcheckhashsiz(i)) { fclose(fil); return(-1); }
    strcpy(&kzhashbuf[kzhashpos],filnam);
    zipnamoffs = kzhashpos; kzhashpos += i;

    fread(&i,4,1,fil);
    if (i == LSWAPIB(0x04034b50)) //'PK\3\4' is ZIP file id
    {
        fseek(fil,-22,SEEK_END);
        fread(tempbuf,22,1,fil);
        if (*(int32_t *)&tempbuf[0] == LSWAPIB(0x06054b50)) //Fast way of finding dir info
        {
            numfiles = SSWAPIB(*(int16_t *)&tempbuf[10]);
            fseek(fil,LSWAPIB(*(int32_t *)&tempbuf[16]),SEEK_SET);
        }
        else //Slow way of finding dir info (used when ZIP has junk at end)
        {
            fseek(fil,0,SEEK_SET); numfiles = 0;
            while (1)
            {
                if (!fread(&j,4,1,fil)) { numfiles = -1; break; }
                if (j == LSWAPIB(0x02014b50)) break; //Found central file header :)
                if (j != LSWAPIB(0x04034b50)) { numfiles = -1; break; }
                fread(tempbuf,26,1,fil);
                fseek(fil,LSWAPIB(*(int32_t *)&tempbuf[14]) + SSWAPIB(*(int16_t *)&tempbuf[24]) + SSWAPIB(*(int16_t *)&tempbuf[22]),SEEK_CUR);
                numfiles++;
            }
            if (numfiles < 0) { fclose(fil); return(-1); }
            fseek(fil,-4,SEEK_CUR);
        }
        for (i=0; i<numfiles; i++)
        {
            fread(tempbuf,46,1,fil);
            if (*(int32_t *)&tempbuf[0] != LSWAPIB(0x02014b50)) { fclose(fil); return(0); }

            j = SSWAPIB(*(int16_t *)&tempbuf[28]); //filename length
            fread(&tempbuf[46],j,1,fil);
            tempbuf[j+46] = 0;

            //Write information into hash
            j = strlen(&tempbuf[46])+22; if (!kzcheckhashsiz(j)) { fclose(fil); return(-1); }
            hashind = kzcalchash(&tempbuf[46]);
            *(int32_t *)&kzhashbuf[kzhashpos] = kzhashead[hashind];
            *(int32_t *)&kzhashbuf[kzhashpos+4] = kzlastfnam;
            *(int32_t *)&kzhashbuf[kzhashpos+8] = zipnamoffs;
            *(int32_t *)&kzhashbuf[kzhashpos+12] = LSWAPIB(*(int32_t *)&tempbuf[42]); //fileoffs
            *(int32_t *)&kzhashbuf[kzhashpos+16] = 0; //fileleng not used for ZIPs (reserve space for simplicity)
            kzhashbuf[kzhashpos+20] = 1; //iscomp
            strcpy(&kzhashbuf[kzhashpos+21],&tempbuf[46]);
            kzhashead[hashind] = kzhashpos; kzlastfnam = kzhashpos; kzhashpos += j;

            j  = SSWAPIB(*(int16_t *)&tempbuf[30]); //extra field length
            j += SSWAPIB(*(int16_t *)&tempbuf[32]); //file comment length
            fseek(fil,j,SEEK_CUR);
        }
    }
    else if (i == LSWAPIB(0x536e654b)) //'KenS' is GRP file id
    {
        fread(tempbuf,12,1,fil);
        if ((*(int32_t *)&tempbuf[0] != LSWAPIB(0x65766c69)) || //'ilve'
                (*(int32_t *)&tempbuf[4] != LSWAPIB(0x6e616d72)))   //'rman'
            { fclose(fil); return(0); }
        numfiles = LSWAPIB(*(int32_t *)&tempbuf[8]); k = ((numfiles+1)<<4);
        for (i=0; i<numfiles; i++,k+=leng)
        {
            fread(tempbuf,16,1,fil);
            leng = LSWAPIB(*(int32_t *)&tempbuf[12]); //File length
            tempbuf[12] = 0;

            //Write information into hash
            j = strlen(tempbuf)+22; if (!kzcheckhashsiz(j)) { fclose(fil); return(-1); }
            hashind = kzcalchash(tempbuf);
            *(int32_t *)&kzhashbuf[kzhashpos] = kzhashead[hashind];
            *(int32_t *)&kzhashbuf[kzhashpos+4] = kzlastfnam;
            *(int32_t *)&kzhashbuf[kzhashpos+8] = zipnamoffs;
            *(int32_t *)&kzhashbuf[kzhashpos+12] = k; //fileoffs
            *(int32_t *)&kzhashbuf[kzhashpos+16] = leng; //fileleng
            kzhashbuf[kzhashpos+20] = 0; //iscomp
            strcpy(&kzhashbuf[kzhashpos+21],tempbuf);
            kzhashead[hashind] = kzhashpos; kzlastfnam = kzhashpos; kzhashpos += j;
        }
    }
    fclose(fil);
    return(0);
}

//this allows the use of kplib.c with a file that is already open
void kzsetfil(FILE *fil)
{
    kzfs.fil = fil;
    kzfs.comptyp = 0;
    kzfs.seek0 = 0;
    kzfs.leng = filelength(_fileno(kzfs.fil));
    kzfs.pos = 0;
    kzfs.i = 0;
}

intptr_t kzopen(const char *filnam)
{
    FILE *fil;
    int32_t i, j, fileoffs, fileleng;
    char tempbuf[46+260], *zipnam, iscomp;

    //kzfs.fil = 0;
    if (filnam[0] != '|') //Search standalone file first
    {
        kzfs.fil = fopen(filnam,"rb");
        if (kzfs.fil)
        {
            kzfs.comptyp = 0;
            kzfs.seek0 = 0;
            kzfs.leng = filelength(_fileno(kzfs.fil));
            kzfs.pos = 0;
            kzfs.i = 0;
            return((intptr_t)kzfs.fil);
        }
    }
    if (kzcheckhash(filnam,&zipnam,&fileoffs,&fileleng,&iscomp)) //Then check mounted ZIP/GRP files
    {
        fil = fopen(zipnam,"rb"); if (!fil) return(0);
        fseek(fil,fileoffs,SEEK_SET);
        if (!iscomp) //Must be from GRP file
        {
            kzfs.fil = fil;
            kzfs.comptyp = 0;
            kzfs.seek0 = fileoffs;
            kzfs.leng = fileleng;
            kzfs.pos = 0;
            kzfs.i = 0;
            return((intptr_t)kzfs.fil);
        }
        else
        {
            fread(tempbuf,30,1,fil);
            if (*(int32_t *)&tempbuf[0] != LSWAPIB(0x04034b50)) { fclose(fil); return(0); }
            fseek(fil,SSWAPIB(*(int16_t *)&tempbuf[26])+SSWAPIB(*(int16_t *)&tempbuf[28]),SEEK_CUR);

            kzfs.fil = fil;
            kzfs.comptyp = SSWAPIB(*(int16_t *)&tempbuf[8]);
            kzfs.seek0 = ftell(fil);
            kzfs.leng = LSWAPIB(*(int32_t *)&tempbuf[22]);
            kzfs.pos = 0;
            switch (kzfs.comptyp) //Compression method
            {
            case 0: kzfs.i = 0; return((intptr_t)kzfs.fil);
            case 8:
                    if (!pnginited) { pnginited = 1; initpngtables(); }
                kzfs.comptell = 0;
                kzfs.compleng = LSWAPIB(*(int32_t *)&tempbuf[18]);

                //WARNING: No file in ZIP can be > 2GB-32K bytes
                gslidew = 0x7fffffff; //Force reload at beginning

                return((intptr_t)kzfs.fil);
            default: fclose(kzfs.fil); kzfs.fil = 0; return(0);
            }
        }
    }

    //Finally, check mounted dirs
    for (i=kzdirnamhead; i>=0; i=*(int32_t *)&kzhashbuf[i])
    {
        strcpy(tempbuf,&kzhashbuf[i+4]);
        j = strlen(tempbuf);
        if (strlen(filnam)+1+j >= sizeof(tempbuf)) continue; //don't allow int32_t filenames to buffer overrun
        if ((j) && (tempbuf[j-1] != '/') && (tempbuf[j-1] != '\\') && (filnam[0] != '/') && (filnam[0] != '\\'))
#if defined(_WIN32)
            strcat(tempbuf,"\\");
#else
            strcat(tempbuf,"/");
#endif
        strcat(tempbuf,filnam);
        kzfs.fil = fopen(tempbuf,"rb");
        if (kzfs.fil)
        {
            kzfs.comptyp = 0;
            kzfs.seek0 = 0;
            kzfs.leng = filelength(_fileno(kzfs.fil));
            kzfs.pos = 0;
            kzfs.i = 0;
            return((intptr_t)kzfs.fil);
        }
    }

    return(0);
}

// --------------------------------------------------------------------------

#if defined(_WIN32)
static HANDLE hfind = INVALID_HANDLE_VALUE;
static WIN32_FIND_DATA findata;
#else
#define MAX_PATH 260
static DIR *hfind = NULL;
static struct dirent *findata = NULL;
#endif

//File find state variables. Example sequence (read top->bot, left->right):
//   srchstat   srchzoff    srchdoff
//   0,1,2,3
//              500,200,-1
//           4              300
//   0,1,2,3,4              100
//   0,1,2,3,4              -1
static int32_t srchstat = -1, srchzoff = 0, srchdoff = -1, wildstpathleng;
static char wildst[MAX_PATH] = "", newildst[MAX_PATH] = "";

void kzfindfilestart(const char *st)
{
#if defined(_WIN32)
    if (hfind != INVALID_HANDLE_VALUE)
        { FindClose(hfind); hfind = INVALID_HANDLE_VALUE; }
#else
    if (hfind) { closedir(hfind); hfind = NULL; }
#endif
    strcpy(wildst,st); strcpy(newildst,st);
    srchstat = 0; srchzoff = kzlastfnam; srchdoff = kzdirnamhead;
}

int32_t kzfindfile(char *filnam)
{
    int32_t i;

    kzfindfile_beg:;
    filnam[0] = 0;
    if (srchstat == 0)
    {
        if (!newildst[0]) { srchstat = -1; return(0); }
        do
        {
            srchstat = 1;

            //Extract directory from wildcard string for pre-pending
            wildstpathleng = 0;
            for (i=0; newildst[i]; i++)
                if ((newildst[i] == '/') || (newildst[i] == '\\'))
                    wildstpathleng = i+1;

            Bmemcpy(filnam,newildst,wildstpathleng);

#if defined(_WIN32)
            hfind = FindFirstFile(newildst,&findata);
            if (hfind == INVALID_HANDLE_VALUE)
                { if (!kzhashbuf) return(0); srchstat = 2; continue; }
            if (findata.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN) continue;
            i = wildstpathleng;
            if (findata.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
                if ((findata.cFileName[0] == '.') && (!findata.cFileName[1])) continue;
            strcpy(&filnam[i],findata.cFileName);
            if (findata.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) strcat(&filnam[i],"\\");
#else
            if (!hfind)
            {
                char *s = ".";
                if (wildstpathleng > 0)
                {
                    filnam[wildstpathleng] = 0;
                    s = filnam;
                }
                hfind = opendir(s);
                if (!hfind) { if (!kzhashbuf) return 0; srchstat = 2; continue; }
            }
            break;   // process srchstat == 1
#endif
            return(1);
        }
        while (0);
    }
    if (srchstat == 1)
    {
        while (1)
        {
            Bmemcpy(filnam,newildst,wildstpathleng);
#if defined(_WIN32)
            if (!FindNextFile(hfind,&findata))
                { FindClose(hfind); hfind = INVALID_HANDLE_VALUE; if (!kzhashbuf) return(0); srchstat = 2; break; }
            if (findata.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN) continue;
            i = wildstpathleng;
            if (findata.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
                if ((findata.cFileName[0] == '.') && (!findata.cFileName[1])) continue;
            strcpy(&filnam[i],findata.cFileName);
            if (findata.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) strcat(&filnam[i],"\\");
#else
            if ((findata = readdir(hfind)) == NULL)
                { closedir(hfind); hfind = NULL; if (!kzhashbuf) return 0; srchstat = 2; break; }
            i = wildstpathleng;
            if (findata->d_type == DT_DIR)
                { if (findata->d_name[0] == '.' && !findata->d_name[1]) continue; } //skip .
            else if ((findata->d_type == DT_REG) || (findata->d_type == DT_LNK))
                { if (findata->d_name[0] == '.') continue; } //skip hidden (dot) files
            else continue; //skip devices and fifos and such
            if (!wildmatch(findata->d_name,&newildst[wildstpathleng])) continue;
            strcpy(&filnam[i],findata->d_name);
            if (findata->d_type == DT_DIR) strcat(&filnam[i],"/");
#endif
            return(1);
        }
    }
    while (srchstat == 2)
    {
        if (srchzoff < 0) { srchstat = 3; break; }
        if (wildmatch(&kzhashbuf[srchzoff+21],newildst))
        {
            //strcpy(filnam,&kzhashbuf[srchzoff+21]);
            filnam[0] = '|'; strcpy(&filnam[1],&kzhashbuf[srchzoff+21]);
            srchzoff = *(int32_t *)&kzhashbuf[srchzoff+4];
            return(1);
        }
        srchzoff = *(int32_t *)&kzhashbuf[srchzoff+4];
    }
    while (srchstat == 3)
    {
        if (srchdoff < 0) { srchstat = -1; break; }
        strcpy(newildst,&kzhashbuf[srchdoff+4]);
        i = strlen(newildst);
        if ((i) && (newildst[i-1] != '/') && (newildst[i-1] != '\\') && (filnam[0] != '/') && (filnam[0] != '\\'))
#if defined(_WIN32)
            strcat(newildst,"\\");
#else
            strcat(newildst,"/");
#endif
        strcat(newildst,wildst);
        srchdoff = *(int32_t *)&kzhashbuf[srchdoff];
        srchstat = 0; goto kzfindfile_beg;
    }

    return(0);
}

//File searching code (supports inside ZIP files!) How to use this code:
//   char filnam[MAX_PATH];
//   kzfindfilestart("vxl/*.vxl");
//   while (kzfindfile(filnam)) puts(filnam);
//NOTES:
// * Directory names end with '\' or '/' (depending on system)
// * Files inside zip begin with '|'

// --------------------------------------------------------------------------

static char *gzbufptr;
static void putbuf4zip(const uint8_t *buf, int32_t uncomp0, int32_t uncomp1)
{
    int32_t i0, i1;
    //              uncomp0 ... uncomp1
    //  &gzbufptr[kzfs.pos] ... &gzbufptr[kzfs.endpos];
    i0 = max(uncomp0,kzfs.pos);
    i1 = min(uncomp1,kzfs.endpos);
    if (i0 < i1) Bmemcpy(&gzbufptr[i0],&buf[i0-uncomp0],i1-i0);
}

//returns number of bytes copied
int32_t kzread(void *buffer, int32_t leng)
{
    int32_t i, j, k, bfinal, btype, hlit, hdist;

    if ((!kzfs.fil) || (leng <= 0)) return(0);

    if (kzfs.comptyp == 0)
    {
        if (kzfs.pos != kzfs.i) //Seek only when position changes
            fseek(kzfs.fil,kzfs.seek0+kzfs.pos,SEEK_SET);
        i = min(kzfs.leng-kzfs.pos,leng);
        fread(buffer,i,1,kzfs.fil);
        kzfs.i += i; //kzfs.i is a local copy of ftell(kzfs.fil);
    }
    else if (kzfs.comptyp == 8)
    {
        zipfilmode = 1;

        //Initialize for putbuf4zip
        gzbufptr = (char *)buffer; gzbufptr = &gzbufptr[-kzfs.pos];
        kzfs.endpos = min(kzfs.pos+leng,kzfs.leng);
        if (kzfs.endpos == kzfs.pos) return(0); //Guard against reading 0 length

        if (kzfs.pos < gslidew-32768) // Must go back to start :(
        {
            if (kzfs.comptell) fseek(kzfs.fil,kzfs.seek0,SEEK_SET);

            gslidew = 0; gslider = 16384;
            kzfs.jmpplc = 0;

            //Initialize for suckbits/peekbits/getbits
            kzfs.comptell = min((unsigned)kzfs.compleng,sizeof(olinbuf));
            fread(&olinbuf[0],kzfs.comptell,1,kzfs.fil);
            //Make it re-load when there are < 32 bits left in FIFO
            bitpos = -(((int32_t)sizeof(olinbuf)-4)<<3);
            //Identity: filptr + (bitpos>>3) = &olinbuf[0]
            filptr = &olinbuf[-(bitpos>>3)];
        }
        else
        {
            i = max(gslidew-32768,0); j = gslider-16384;

            //HACK: Don't unzip anything until you have to...
            //   (keeps file pointer as low as possible)
            if (kzfs.endpos <= gslidew) j = kzfs.endpos;

            //write uncompoffs on slidebuf from: i to j
            if (!((i^j)&32768))
                putbuf4zip(&slidebuf[i&32767],i,j);
            else
            {
                putbuf4zip(&slidebuf[i&32767],i,j&~32767);
                putbuf4zip(slidebuf,j&~32767,j);
            }

            //HACK: Don't unzip anything until you have to...
            //   (keeps file pointer as low as possible)
            if (kzfs.endpos <= gslidew) goto retkzread;
        }

        switch (kzfs.jmpplc)
        {
        case 0: goto kzreadplc0;
        case 1: goto kzreadplc1;
        case 2: goto kzreadplc2;
        case 3: goto kzreadplc3;
        }
        kzreadplc0:;
        do
        {
            bfinal = getbits(1); btype = getbits(2);

#if 0
            //Display Huffman block offsets&lengths of input file - for debugging only!
            {
                static int32_t ouncomppos = 0, ocomppos = 0;
                if (kzfs.comptell == sizeof(olinbuf)) i = 0;
                else if (kzfs.comptell < kzfs.compleng) i = kzfs.comptell-(sizeof(olinbuf)-4);
                else i = kzfs.comptell-(kzfs.comptell%(sizeof(olinbuf)-4));
                i += ((int32_t)&filptr[bitpos>>3])-((int32_t)(&olinbuf[0]));
                i = (i<<3)+(bitpos&7)-3;
                if (gslidew) printf(" ULng:0x%08x CLng:0x%08x.%x",gslidew-ouncomppos,(i-ocomppos)>>3,((i-ocomppos)&7)<<1);
                printf("\ntype:%d, Uoff:0x%08x Coff:0x%08x.%x",btype,gslidew,i>>3,(i&7)<<1);
                if (bfinal)
                {
                    printf(" ULng:0x%08x CLng:0x%08x.%x",kzfs.leng-gslidew,((kzfs.compleng<<3)-i)>>3,(((kzfs.compleng<<3)-i)&7)<<1);
                    printf("\n        Uoff:0x%08x Coff:0x%08x.0",kzfs.leng,kzfs.compleng);
                    ouncomppos = ocomppos = 0;
                }
                else { ouncomppos = gslidew; ocomppos = i; }
            }
#endif

            if (btype == 0)
            {
                //Raw (uncompressed)
                suckbits((-bitpos)&7);  //Synchronize to start of next byte
                i = getbits(16); if ((getbits(16)^i) != 0xffff) return(-1);
                for (; i; i--)
                {
                    if (gslidew >= gslider)
                    {
                        putbuf4zip(&slidebuf[(gslider-16384)&32767],gslider-16384,gslider); gslider += 16384;
                        if (gslider-16384 >= kzfs.endpos)
                        {
                            kzfs.jmpplc = 1; kzfs.i = i; kzfs.bfinal = bfinal;
                            goto retkzread;
                            kzreadplc1:;         i = kzfs.i; bfinal = kzfs.bfinal;
                        }
                    }
                    slidebuf[(gslidew++)&32767] = (uint8_t)getbits(8);
                }
                continue;
            }
            if (btype == 3) continue;

            if (btype == 1) //Fixed Huffman
            {
                hlit = 288; hdist = 32; i = 0;
                for (; i<144; i++) clen[i] = 8; //Fixed bit sizes (literals)
                for (; i<256; i++) clen[i] = 9; //Fixed bit sizes (literals)
                for (; i<280; i++) clen[i] = 7; //Fixed bit sizes (EOI,lengths)
                for (; i<288; i++) clen[i] = 8; //Fixed bit sizes (lengths)
                for (; i<320; i++) clen[i] = 5; //Fixed bit sizes (distances)
            }
            else  //Dynamic Huffman
            {
                hlit = getbits(5)+257; hdist = getbits(5)+1; j = getbits(4)+4;
                for (i=0; i<j; i++) cclen[ccind[i]] = getbits(3);
                for (; i<19; i++) cclen[ccind[i]] = 0;
                hufgencode(cclen,19,ibuf0,nbuf0);

                j = 0; k = hlit+hdist;
                while (j < k)
                {
                    i = hufgetsym(ibuf0,nbuf0);
                    if (i < 16) { clen[j++] = i; continue; }
                    if (i == 16)
                        { for (i=getbits(2)+3; i; i--) { clen[j] = clen[j-1]; j++; } }
                    else
                    {
                        if (i == 17) i = getbits(3)+3; else i = getbits(7)+11;
                        for (; i; i--) clen[j++] = 0;
                    }
                }
            }

            hufgencode(clen,hlit,ibuf0,nbuf0);
            qhufgencode(ibuf0,nbuf0,qhufval0,qhufbit0,LOGQHUFSIZ0);

            hufgencode(&clen[hlit],hdist,ibuf1,nbuf1);
            qhufgencode(ibuf1,nbuf1,qhufval1,qhufbit1,LOGQHUFSIZ1);

            while (1)
            {
                if (gslidew >= gslider)
                {
                    putbuf4zip(&slidebuf[(gslider-16384)&32767],gslider-16384,gslider); gslider += 16384;
                    if (gslider-16384 >= kzfs.endpos)
                    {
                        kzfs.jmpplc = 2; kzfs.bfinal = bfinal; goto retkzread;
                        kzreadplc2:;      bfinal = kzfs.bfinal;
                    }
                }

                k = peekbits(LOGQHUFSIZ0);
                if (qhufbit0[k]) { i = qhufval0[k]; suckbits((int32_t)qhufbit0[k]); }
                else i = hufgetsym(ibuf0,nbuf0);

                if (i < 256) { slidebuf[(gslidew++)&32767] = (uint8_t)i; continue; }
                if (i == 256) break;
                i = getbits(hxbit[i+30-257][0]) + hxbit[i+30-257][1];

                k = peekbits(LOGQHUFSIZ1);
                if (qhufbit1[k]) { j = qhufval1[k]; suckbits((int32_t)qhufbit1[k]); }
                else j = hufgetsym(ibuf1,nbuf1);

                j = getbits(hxbit[j][0]) + hxbit[j][1];
                for (; i; i--,gslidew++) slidebuf[gslidew&32767] = slidebuf[(gslidew-j)&32767];
            }
        }
        while (!bfinal);

        gslider -= 16384;
        if (!((gslider^gslidew)&32768))
            putbuf4zip(&slidebuf[gslider&32767],gslider,gslidew);
        else
        {
            putbuf4zip(&slidebuf[gslider&32767],gslider,gslidew&~32767);
            putbuf4zip(slidebuf,gslidew&~32767,gslidew);
        }
        kzreadplc3:; kzfs.jmpplc = 3;
    }

    retkzread:;
    i = kzfs.pos;
    kzfs.pos += leng; if (kzfs.pos > kzfs.leng) kzfs.pos = kzfs.leng;
    return(kzfs.pos-i);
}

int32_t kzfilelength()
{
    if (!kzfs.fil) return(0);
    return(kzfs.leng);
}

//WARNING: kzseek(<-32768,SEEK_CUR); or:
//         kzseek(0,SEEK_END);       can make next kzread very slow!!!
int32_t kzseek(int32_t offset, int32_t whence)
{
    if (!kzfs.fil) return(-1);
    switch (whence)
    {
    case SEEK_CUR: kzfs.pos += offset; break;
    case SEEK_END: kzfs.pos = kzfs.leng+offset; break;
case SEEK_SET: default: kzfs.pos = offset;
    }
    if (kzfs.pos < 0) kzfs.pos = 0;
    if (kzfs.pos > kzfs.leng) kzfs.pos = kzfs.leng;
    return(kzfs.pos);
}

int32_t kztell()
{
    if (!kzfs.fil) return(-1);
    return(kzfs.pos);
}

int32_t kzgetc()
{
    char ch;
    if (!kzread(&ch,1)) return(-1);
    return((int32_t)ch);
}

int32_t kzeof()
{
    if (!kzfs.fil) return(-1);
    return(kzfs.pos >= kzfs.leng);
}

void kzclose()
{
    if (kzfs.fil) { fclose(kzfs.fil); kzfs.fil = 0; }
}

//====================== ZIP decompression code ends =========================
//===================== HANDY PICTURE function begins ========================
#include "cache1d.h"

void kpzload(const char *filnam, intptr_t *pic, int32_t *bpl, int32_t *xsiz, int32_t *ysiz)
{
    char *buf;
    int32_t leng;
    int32_t handle = kopen4load((char *)filnam, 0);

    (*pic) = 0;
    if (handle < 0) return;
    leng = kfilelength(handle);
    buf = (char *)Bmalloc(leng+1); if (!buf) { kclose(handle); return; }
    buf[leng]=0;  // FIXME: buf[leng] read in kpegrend(), see BUF_LENG_READ
    kread(handle,buf,leng);
    kclose(handle);

    kpgetdim(buf,leng,xsiz,ysiz);
    (*bpl) = ((*xsiz)<<2);
    (*pic) = (intptr_t)Bmalloc((*ysiz)*(*bpl)); if (!(*pic)) { Bfree(buf); return; }
    if (kprender(buf,leng,*pic,*bpl,*xsiz,*ysiz,0,0) < 0) { Bfree(buf); Bfree((void *)*pic); (*pic) = 0; return; }
    Bfree(buf);
}
//====================== HANDY PICTURE function ends =========================
