/*
    Copyright 2009-2012 Luigi Auriemma

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

    http://www.gnu.org/licenses/gpl-2.0.txt
*/

// QuickBMS general functions



void show_dump(int left, u8 *data, int len, FILE *stream) {
    int                 rem;
    static const u8     hex[16] = "0123456789abcdef";
    u8                  leftbuff[80],
                        buff[67],
                        chr,
                        *bytes,
                        *p,
                        *limit,
                        *glimit = data + len;

    if(len < 0) return;
    memset(buff + 2, ' ', 48);
    memset(leftbuff, ' ', sizeof(leftbuff));

    while(data < glimit) {
        limit = data + 16;
        if(limit > glimit) {
            limit = glimit;
            memset(buff, ' ', 48);
        }

        p     = buff;
        bytes = p + 50;
        while(data < limit) {
            chr = *data;
            *p++ = hex[chr >> 4];
            *p++ = hex[chr & 15];
            p++;
            *bytes++ = ((chr < ' ') || (chr >= 0x7f)) ? '.' : chr;
            data++;
        }
        *bytes++ = '\n';

        for(rem = left; rem >= sizeof(leftbuff); rem -= sizeof(leftbuff)) {
            fwrite(leftbuff, sizeof(leftbuff), 1, stream);
        }
        if(rem > 0) fwrite(leftbuff, rem, 1, stream);
        fwrite(buff, bytes - buff, 1, stream);
    }
}



int check_extension(u8 *fname, u8 *ext) {
    u8      *p;

    if(!fname || !ext) return(0);
    p = strrchr(fname, '.');
    if(!p) return(0);
    p++;
    if(!stricmp(p, ext)) return(1);
    return(0);
}



u8 *mystrcpy(u8 *dst, u8 *src, int max) {
    u8      *p,
            *l;

    if(dst && (max > 0)) {
        if(!src) src = "";
        p = dst;
        l = dst + max - 1;
        while(p < l) {
            if(!*src) break;
            *p++ = *src++;
        }
        *p = 0;
    }
    return(dst);
}



u8 *mystrdup_simple(u8 *str) { // multiplatform compatible
    int     len;
    u8      *o  = NULL;

    if(str) {
        len = strlen(str);
        o = malloc(len + 1);
        if(o) memcpy(o, str, len + 1);
    }
    return(o);
}



u8 *mystrdup(u8 **old_buff, u8 *str) { // multiplatform compatible
    int     len;
    u8      *o  = NULL;

    if(old_buff) o = *old_buff;
    if(str) {
        len = strlen(str);
        o = realloc(o, len + 1);
        if(o) memcpy(o, str, len + 1);
    }
    if(old_buff) {
        if(!o) {
            FREEZ(*old_buff)
        } else {
            *old_buff = o;
        }
    }
    return(o);
}



u8 *mystrchrs(u8 *str, u8 *chrs) {
    //int     i;
    u8      *p,
            *ret = NULL;

    if(str && chrs) {
        for(p = str; *p; p++) {
            if(strchr(chrs, *p)) return(p);
        }
        /*
        for(i = 0; chrs[i]; i++) {
            p = strchr(str, chrs[i]);
            if(p && (!ret || (p < ret))) {
                ret = p;
            }
        }
        */
    }
    return(ret);
}



u8 *mystrrchrs(u8 *str, u8 *chrs) {
    //int     i;
    u8      *p,
            *ret = NULL;

    if(str && chrs) {
        for(p = str + strlen(str) - 1; p >= str; p--) {
            if(strchr(chrs, *p)) return(p);
        }
        /*
        for(i = 0; chrs[i]; i++) {
            p = strrchr(str, chrs[i]);
            if(p) {
                str = p;
                ret = p;
            }
        }
        */
    }
    return(ret);
}



#define mystrstr    stristr
#define mystrrstr   strristr

/*
u8 *mystrstr(u8 *str, u8 *s) {
    u8      *p;

    if(str && s) {
        for(p = str; *p; p++) {
            if(!stricmp(p, s)) return(p);
        }
    }
    return(NULL);
}

u8 *mystrrstr(u8 *str, u8 *s) {
    int     slen;
    u8      *p;

    if(str && s) {
        slen = strlen(s);
        for(p = str + strlen(str) - slen; p >= str; p--) {
            if(!stricmp(p, s)) return(p);
        }
    }
    return(NULL);
}
*/



int check_is_dir(u8 *fname) {
    struct stat xstat;

    if(!fname) return(1);
    if(stat(fname, &xstat) < 0) return(0);
    if(!S_ISDIR(xstat.st_mode)) return(0);
    return(1);
}



u8 *get_main_path(u8 *fname, u8 *argv0, u8 *output) {
    static u8   fullname[PATHSZ + 1];
    u8      *p;

    if(!output) output = fullname;
#ifdef WIN32
    if(GetModuleFileName(NULL, output, PATHSZ) >= PATHSZ)
#endif
    sprintf(output, "%.*s", PATHSZ, argv0);

    if(check_is_dir(output)) return(output);
    p = mystrrchrs(output, "\\/");
    if(fname) {
        if(!p) p = output - 1;
        sprintf(p + 1, "%.*s", PATHSZ - (p - output), fname);
    } else {
        if(p) *p = 0;
    }
    return(output);
}



int copycut_folder(u8 *input, u8 *output) {
    u8      *p;

    if(!output) return(-1);
    if(input) mystrcpy(output, input, PATHSZ);
    if(check_is_dir(output)) return(0);
    p = mystrrchrs(output, "\\/");
    if(!p) {
        if(input) output[0] = 0;
    } else {
        *p = 0;
    }
    if(check_is_dir(output)) return(0);
    return(-1);
}



#ifdef WIN32
char *get_file(char *title, i32 bms, i32 multi) {
    OPENFILENAME    ofn;
    int     maxlen;
    char    *filename;

    if(multi) {
        maxlen = 32768; // 32k limit ansi, no limit unicode
    } else {
        maxlen = PATHSZ;
    }
    filename = malloc(maxlen + 1);
    if(!filename) STD_ERR;
    filename[0] = 0;
    memset(&ofn, 0, sizeof(ofn));
    ofn.lStructSize     = sizeof(ofn);
    if(bms) {
        ofn.lpstrFilter =
            "script/plugin (bms/txt/wcx)\0"  "*.bms;*.txt;*.wcx\0"
            //"WCX plugin\0"  "*.wcx\0"
            "(*.*)\0"       "*.*\0"
            "\0"            "\0";
    } else {
        ofn.lpstrFilter =
            "(*.*)\0"       "*.*\0"
            "\0"            "\0";
    }
    ofn.nFilterIndex    = 1;
    ofn.lpstrFile       = filename;
    ofn.nMaxFile        = maxlen;
    ofn.lpstrTitle      = title;
    ofn.Flags           = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST |
                          OFN_LONGNAMES     | OFN_EXPLORER |
                          OFN_HIDEREADONLY  | OFN_ENABLESIZING;
    if(multi) ofn.Flags |= OFN_ALLOWMULTISELECT;

    printf("- %s\n", ofn.lpstrTitle);
    if(!GetOpenFileName(&ofn)) exit(1); // terminate immediately
    return(filename);
}

char *get_folder(char *title) {
    OPENFILENAME    ofn;
    char    *p;
    char    *filename;

    filename = malloc(PATHSZ + 1);
    if(!filename) STD_ERR;

    strcpy(filename, "enter in the output folder and press Save");
    memset(&ofn, 0, sizeof(ofn));
    ofn.lStructSize     = sizeof(ofn);
    ofn.lpstrFilter     = "(*.*)\0" "*.*\0" "\0" "\0";
    ofn.nFilterIndex    = 1;
    ofn.lpstrFile       = filename;
    ofn.nMaxFile        = PATHSZ;
    ofn.lpstrTitle      = title;
    ofn.Flags           = OFN_PATHMUSTEXIST | /*OFN_FILEMUSTEXIST |*/
                          OFN_LONGNAMES     | OFN_EXPLORER |
                          OFN_HIDEREADONLY  | OFN_ENABLESIZING;

    printf("- %s\n", ofn.lpstrTitle);
    if(!GetSaveFileName(&ofn)) exit(1); // terminate immediately
    p = mystrrchrs(filename, "\\/");
    if(p) *p = 0;
    return(filename);
}
#endif



int fgetz(u8 *data, int datalen, FILE *fd, u8 *fmt, ...) {
    va_list ap;
    u8      *p;

    if(!data) return(-1);
    if(datalen <= 0) return(-1);
    if(fmt) {
        va_start(ap, fmt);
        vprintf(fmt, ap);
        va_end(ap);
    }
    data[0] = 0;
    if(!fgets(data, datalen, fd)) myexit(1);    //return(-1);
    for(p = data; *p && (*p != '\r') && (*p != '\n'); p++);
    *p = 0;
    return(p - data);
}



QUICKBMS_int readbase(u8 *data, QUICKBMS_int size, QUICKBMS_int *readn) {
    static const u8 table[256] =    // fast performances
            "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
            "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
            "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
            "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\xff\xff\xff\xff\xff\xff"
            "\xff\x0a\x0b\x0c\x0d\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff"
            "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
            "\xff\x0a\x0b\x0c\x0d\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff"
            "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
            "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
            "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
            "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
            "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
            "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
            "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
            "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
            "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
    int     num     = 0;
    int     sign;
    u8      c,
            *s,
            *hex_fix;

    s = data;
    if(!data || !size || !data[0]) {
        // do nothing (for readn)
    } else {
        // useful in some occasions, for example if the input is external!
        for(; *s; s++) {
            if(!strchr(" \t\r\n", *s)) break;
        }
        if(*s == '-') {
            sign = -1;
            s++;
        } else {
            sign = 0;
        }
        hex_fix = s;
        for(; *s; s++) {
            c = *s;
            //if((c == 'x') || (c == 'X') || (c == '$')) {  // auto base switching
            if(
                (((c == 'h') || (c == 'x') || (c == 'X')) && (s > hex_fix)) // 0x and 100h, NOT x123 or h123
             || (c == '$')                                                  // $1234 or 1234$
            ) {
                size = 16;
                continue;
            }
            c = table[c];
            if(c >= size) break;    // necessary to recognize the invalid chars based on the size
            num = (num * size) + c;
        }
        if(sign) num = -num;
    }
    if(readn) *readn = s - data;
    return(num);
}



int myisalnum(int chr) {
    if((chr >= '0') && (chr <= '9')) return(1);
    if((chr >= 'a') && (chr <= 'z')) return(1);
    if((chr >= 'A') && (chr <= 'Z')) return(1);
    if(chr == '-') return(1);   // negative number
    //if(chr == '+') return(1);   // positive number
    return(0);
}



int myishexdigit(int chr) {
    if((chr >= 'A') && (chr <= 'F')) return(1);
    if((chr >= 'a') && (chr <= 'f')) return(1);
    if((chr >= '0') && (chr <= '9')) return(1); // this is enough because hex start ever with 0x
    if(chr == '-') return(1);   // negative number
    //if(chr == '+') return(1);   // positive number
    //if(chr == '$') return(1);   // delphi/vb hex
    return(0);
}



int myisdigit(int chr) {
    if((chr >= '0') && (chr <= '9')) return(1); // this is enough because hex start ever with 0x
    if(chr == '-') return(1);   // negative number
    //if(chr == '+') return(1);   // positive number
    //if(chr == '$') return(1);   // delphi/vb hex
    return(0);
}



int myisdigitstr(u8 *str) { // only a quick version
    int     i;

    if(!str) return(0);
    if(!myisdigit(str[0])) return(0);
    for(i = 1; str[i]; i++) {
        if(i >= NUMBERSZ) return(0);    // avoid to waste time with long strings
        if(!strchr("0123456789abcdefABCDEFx$", str[i])) return(0);
    }
    return(1);
}



u8 *myitoa(int num) {
    static const u8 table[] = "0123456789abcdef";
    static u8       dstx[MULTISTATIC][3 + NUMBERSZ + 1] = {{""}};
    static int      dsty = 0;
    u_int   unum;
    u8      tmp[NUMBERSZ + 1],  // needed because hex numbers are inverted, I have already done various tests and this is the fastest!
            *p,                 // even faster than using directly dst as output
            *t,
            *dst;

    dst = (u8 *)dstx[dsty++ % MULTISTATIC];

    if(!num) {  // quick way, 0 is used enough often... ok it's probably useless
        dst[0] = '0';
        dst[1] = 0;
        return(dst);
    }

    p = dst;
    if(num < 0) {
        num = -num;
        *p++ = '-';
    }
    unum = num; // needed for the sign... many troubles

    //if((unum >= 0) && (unum <= 9)) {  // quick solution for numbers under 10, so uses only one char, (unum >= 0) avoids problems with 0x80000000
        //*p++ = table[unum];
        //*p   = 0;
        //return(dst);
    //}
    t = tmp + (NUMBERSZ - 1);   // the -1 is needed (old tests)
    *t = 0;
    t--;
    if(decimal_notation) {
        do {   // "unum" MUST be handled at the end of the cycle! example: 0
            *t = table[unum % (u_int)10];
            unum = unum / (u_int)10;
            if(!unum) break;
            t--;
        } while(t >= tmp);
    } else {
        *p++ = '0'; // hex notation is better for debugging
        *p++ = 'x';
        do {   // "unum" MUST be handled at the end of the cycle! example: 0
            *t = table[unum & 15];
            unum = unum >> (u_int)4;
            if(!unum) break;
            t--;
        } while(t >= tmp);
    }
    strcpy(p, t);

    //sprintf(dst, "%"PRId"", unum);  // old "one-instruction-only" solution, mine is better
    return(dst);
}



u8 *strdupcpy(u8 *dst, int *dstlen, u8 *src, int srclen) {
    int     tmp;

    if(srclen < 0) {
        if(src) srclen = strlen(src);
        else    srclen = 0;
    }

    /*
    // normal solution
    //if(srclen < STRINGSZ) srclen = STRINGSZ;  // disabled for testing
    if(dstlen) *dstlen = srclen;
    dst = realloc(dst, srclen + 2); // unicode
    if(!dst) STD_ERR;
    // normal solution
    */

    // optimized solution
    if(!dstlen) {
        dstlen = &tmp;
        *dstlen = -1;
    }
    if(!dst || (*dstlen < srclen) || (*dstlen < 2)) {   // NULL + unicode to avoid srclen 0
        *dstlen = srclen;
        //if(*dstlen == -1) ALLOC_ERR;        // note that dstlen can't be < 0 due to the "srclen < 0" check
        //if(*dstlen == -2) ALLOC_ERR;        // big endian undelimited unicode
        if(*dstlen < STRINGSZ) *dstlen = STRINGSZ;    // better for numbers and common filenames
        dst = realloc(dst, (*dstlen) + 2);  // big endian undelimited unicode (now it's rare but in future it may be more used)
        if(!dst) STD_ERR;
    }
    // optimized solution

    if(dst) {
        if(src) memcpy(dst, src, srclen);
        else    memset(dst, 0,   srclen);
        dst[srclen]     = 0;
        dst[srclen + 1] = 0;    // big endian undelimited unicode
    }
    return(dst);
}



u8 *re_strdup(u8 *dst, u8 *src, int *retlen) {  // only for NULL delimited strings, NOT bytes!
    dst = strdupcpy(dst, retlen, src, -1);

    /*
    int     dstlen  = -1;
    // dst && src checked by strdupcpy
    if(retlen) dstlen = *retlen;
    dst = strdupcpy(dst, &dstlen, src, -1);
    if(retlen) *retlen = dstlen;
    */
    return(dst);
}



int strdup_replace(u8 **dstp, u8 *src, int src_len, int *dstp_len) {  // should improve a bit the performances
    if(!dstp) return(-1);
    *dstp = strdupcpy(*dstp, dstp_len, src, src_len);

    /*
    int     dst_len = -1;
    u8      *dst;

    if(!dstp) return(-1);
    dst = *dstp;

    if(!dstp_len && dst) {
        dst_len = strlen(dst);  // or is it better to use "dst_len = 0"?
    } else if(dstp_len) {
        dst_len = *dstp_len;
    }

    dst = strdupcpy(dst, &dst_len, src, src_len);

    *dstp = dst;
    if(dstp_len) *dstp_len = dst_len;
    */
    return(0);
}



int myisdechex_string(u8 *str) {
    QUICKBMS_int    len;

    // I have already verified that using a quick test only on the first char doesn't improve the performances if compared to the current full check
    if(!str) return(0);
    readbase(str, 10, &len);    // no need to know the number
    if(len <= 0) return(0);     // FALSE
    return(1);                  // TRUE
}



u16 swap16(u16 n) {
    n = (((n & 0xff00) >> 8) |
         ((n & 0x00ff) << 8));
    return(n);
}



u32 swap32(u32 n) {
    n = (((n & 0xff000000) >> 24) |
         ((n & 0x00ff0000) >>  8) |
         ((n & 0x0000ff00) <<  8) |
         ((n & 0x000000ff) << 24));
    return(n);
}



u64 swap64(u64 n) {
//#ifdef QUICKBMS64
    n = (((n & (u64)0xFF00000000000000ULL) >> (u64)56) |
         ((n & (u64)0x00FF000000000000ULL) >> (u64)40) |
         ((n & (u64)0x0000FF0000000000ULL) >> (u64)24) |
         ((n & (u64)0x000000FF00000000ULL) >> (u64) 8) |
         ((n & (u64)0x00000000FF000000ULL) << (u64) 8) |
         ((n & (u64)0x0000000000FF0000ULL) << (u64)24) |
         ((n & (u64)0x000000000000FF00ULL) << (u64)40) |
         ((n & (u64)0x00000000000000FFULL) << (u64)56));
//#else
//    n = swap32(n);
//#endif
    return(n);
}



u16 myhtons(u16 n) {
    int endian = 1;
    if(!*(char *)&endian) return(n);
    return(swap16(n));
}
u16 myntohs(u16 n) {
    int endian = 1;
    if(!*(char *)&endian) return(n);
    return(swap16(n));
}
u32 myhtonl(u32 n) {
    int endian = 1;
    if(!*(char *)&endian) return(n);
    return(swap32(n));
}
u32 myntohl(u32 n) {
    int endian = 1;
    if(!*(char *)&endian) return(n);
    return(swap32(n));
}



u8 *strristr(u8 *s1, u8 *s2) {
    int     s1n,
            s2n;
    u8      *p;

    if(!s1 || !s2) return(NULL);
    s1n = strlen(s1);
    s2n = strlen(s2);
    if(s2n > s1n) return(NULL);
    for(p = s1 + (s1n - s2n); p >= s1; p--) {
        if(!strnicmp(p, s2, s2n)) return(p);
    }
    return(NULL);
}



int vspr(u8 **buff, u8 *fmt, va_list ap) {
    int     len,
            mlen;
    u8      *ret    = NULL;

    if(!fmt) return(0);
    mlen = strlen(fmt) + 128;
    for(;;) {
        ret = realloc(ret, mlen + 1);
        if(!ret) return(0);     // return(-1);
        len = vsnprintf(ret, mlen, fmt, ap);
        if((len >= 0) && (len < mlen)) break;
        mlen += 128;
    }
    *buff = ret;
    return(len);
}



int spr(u8 **buff, u8 *fmt, ...) {
    va_list ap;
    int     len;

    va_start(ap, fmt);
    len = vspr(buff, fmt, ap);
    va_end(ap);
    return(len);
}



u8 *find_replace_string(u8 *buf, int *buflen, u8 *old, int oldlen, u8 *news, int newlen) {
    int     i,
            len,
            //len_bck,
            tlen,
            found;
    u8      *nbuf,
            *p;

    if(!buf) return(buf);
    found  = 0;
    len = -1;
    if(buflen) len = *buflen;
    if(len < 0) len = strlen(buf);
    if(oldlen < 0) {
        oldlen = 0;
        if(old) oldlen = strlen(old);
    }
    tlen    = len - oldlen;
    //len_bck = len;

    for(i = 0; i <= tlen; i++) {
        if(!strnicmp(buf + i, old, oldlen)) found++;
    }
    if(!found) return(buf); // nothing to change: return buf or a positive value

    //if(!news) return(NULL);  // if we want to know only if the searched string has been found, we will get NULL if YES and buf if NOT!!!
    if(newlen < 0) {
        newlen = 0;
        if(news) newlen = strlen(news);
    }

    if(newlen <= oldlen) {  // if the length of new string is equal/minor than the old one don't waste space for another buffer
        nbuf = buf;
    } else {                // allocate the new size
        nbuf = malloc(len + ((newlen - oldlen) * found) + 1);
    }

    p = nbuf;
    for(i = 0; i <= tlen;) {
        if(!strnicmp(buf + i, old, oldlen)) {
            memcpy(p, news, newlen);
            p += newlen;
            i += oldlen;
        } else {
            *p++ = buf[i];
            i++;
        }
    }
    while(i < len) {
        *p++ = buf[i];
        i++;
    }
    len = p - nbuf;
    if(buflen) *buflen = len;
    nbuf[len] = 0;  // hope the original input string has the +1 space
    return(nbuf);
}



u8 *numbers_to_bytes(u8 *str, int *ret_size, int hex) {
    static int  buffsz  = 0;
    static u8   *buff   = NULL;
    u_int   num;
    QUICKBMS_int len;
    int     i,
            t,
            size;

    if(ret_size) *ret_size = 0;
    if(!str) return(NULL);
    for(i = 0; *str;) {
        if(*str == '\\') *str = '0';  // yeah so it can handle also \x11\x22\x33
        //while(*str && !(myisdigit(*str) || (*str == '$'))) str++;  // this one handles also dots, commas and other bad chars

        // this one handles also dots, commas and other bad chars
        while(*str) {
            if(hex) {
                if(myishexdigit(*str)) break;
            } else {
                if(myisdigit(*str) || (*str == '$')) break;
            }
            str++;
        }

        num = readbase(str, hex ? 16 : 10, &len);
        if(len <= 0) break;

        t = 1;
        if(num > 0xff) t = 4;

        if((i + t) > buffsz) {
            buffsz += t + STRINGSZ;
            buff = realloc(buff, buffsz + 1);
            if(!buff) STD_ERR;
        }
        i += putxx(buff + i, num, t);

        str += len;
    }
    if(!buff) {
        buff = realloc(buff, buffsz + 1);
        if(!buff) STD_ERR;
    }
    buff[i] = 0; // useless, only for possible new usages in future //, return ret as NULL
    size = i;
    if(ret_size) *ret_size = size;
    if(verbose > 0) {
        printf("- numbers_to_bytes of %d bytes\n ", (i32)size);
        for(i = 0; i < size; i++) printf(" 0x%02x", buff[i]);
        printf("\n");
    }
    return(buff);
}



files_t *add_files(u8 *fname, int fsize, int *ret_files) {
    static int      filesi  = 0,
                    filesn  = 0;
    static files_t  *files  = NULL;
    files_t         *ret;

    if(ret_files) {
        *ret_files = filesi;
        files = realloc(files, sizeof(files_t) * (filesi + 1)); // not needed, but it's ok
        if(!files) STD_ERR;
        files[filesi].name   = NULL;
        //files[filesi].offset = 0;
        files[filesi].size   = 0;
        ret    = files;
        filesi = 0;
        filesn = 0;
        files  = NULL;
        return(ret);
    }

    if(!fname) return(NULL);
    if(filter_in_files && (check_wildcard(fname, filter_in_files) < 0)) return(NULL);

    if(filesi >= filesn) {
        filesn += 1024;
        files = realloc(files, sizeof(files_t) * filesn);
        if(!files) STD_ERR;
    }
    files[filesi].name   = mystrdup_simple(fname);
    //files[filesi].offset = 0;
    files[filesi].size   = fsize;
    filesi++;
    return(NULL);
}



int quick_simple_tmpname_scanner(u8 *filedir, int filedirsz) {
    int     plen,
            namelen,
            ret     = -1;
#ifdef WIN32
    u8      *p;
    static int      winnt = -1;
    OSVERSIONINFO   osver;
    WIN32_FIND_DATA wfd;
    HANDLE          hFind = INVALID_HANDLE_VALUE;

    if(winnt < 0) {
        osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        GetVersionEx(&osver);
        if(osver.dwPlatformId >= VER_PLATFORM_WIN32_NT) {
            winnt = 1;
        } else {
            winnt = 0;
        }
    }

    p = strrchr(filedir, '.');
    if(p) {
        sprintf(p, ".*");
    } else {
        sprintf(p, "%"PRIx".*", extracted_files);
    }
    plen = 0;

    if(winnt) { // required to avoid problems with Vista and Windows7!
        hFind = FindFirstFileEx(filedir, FindExInfoStandard, &wfd, FindExSearchNameMatch, NULL, 0);
    } else {
        hFind = FindFirstFile(filedir, &wfd);
    }
    if(hFind == INVALID_HANDLE_VALUE) goto quit;
    do {
        if(!strcmp(wfd.cFileName, ".") || !strcmp(wfd.cFileName, "..")) continue;

        namelen = strlen(wfd.cFileName);
        if((plen + namelen) >= filedirsz) goto quit;
        strcpy(filedir + plen, wfd.cFileName);
        memcpy(filedir + plen, wfd.cFileName, namelen);
        filedir[plen + namelen] = 0;

        if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            // no recursion
        } else {
            // file found!
            break;
        }
    } while(FindNextFile(hFind, &wfd));
    ret = 0;

quit:
    if(hFind != INVALID_HANDLE_VALUE) FindClose(hFind);
#else
    // do nothing, not supported
    // you must rename the file as .dat
#endif
    return(ret);
}



#define recursive_dir_skip_path 0
//#define recursive_dir_skip_path 2
int recursive_dir(u8 *filedir, int filedirsz) {
    int     plen,
            namelen,
            ret     = -1;

    if(!filedir) return(ret);
#ifdef WIN32
    static int      winnt = -1;
    OSVERSIONINFO   osver;
    WIN32_FIND_DATA wfd;
    HANDLE          hFind = INVALID_HANDLE_VALUE;

    if(winnt < 0) {
        osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        GetVersionEx(&osver);
        if(osver.dwPlatformId >= VER_PLATFORM_WIN32_NT) {
            winnt = 1;
        } else {
            winnt = 0;
        }
    }

    plen = strlen(filedir);
    if((plen + 4) >= filedirsz) goto quit;
    strcpy(filedir + plen, "\\*.*");
    plen++;

    if(winnt) { // required to avoid problems with Vista and Windows7!
        hFind = FindFirstFileEx(filedir, FindExInfoStandard, &wfd, FindExSearchNameMatch, NULL, 0);
    } else {
        hFind = FindFirstFile(filedir, &wfd);
    }
    if(hFind == INVALID_HANDLE_VALUE) goto quit;
    do {
        if(!strcmp(wfd.cFileName, ".") || !strcmp(wfd.cFileName, "..")) continue;

        namelen = strlen(wfd.cFileName);
        if((plen + namelen) >= filedirsz) goto quit;
        //strcpy(filedir + plen, wfd.cFileName);
        memcpy(filedir + plen, wfd.cFileName, namelen);
        filedir[plen + namelen] = 0;

        if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            if(recursive_dir(filedir, filedirsz) < 0) goto quit;
        } else {
            add_files(filedir + recursive_dir_skip_path, wfd.nFileSizeLow, NULL);
        }
    } while(FindNextFile(hFind, &wfd));
    ret = 0;

quit:
    if(hFind != INVALID_HANDLE_VALUE) FindClose(hFind);
#else
    struct  stat    xstat;
    struct  dirent  **namelist;
    int     n,
            i;

    n = scandir(filedir, &namelist, NULL, NULL);
    if(n < 0) {
        if(stat(filedir, &xstat) < 0) {
            fprintf(stderr, "**** %s", filedir);
            STD_ERR;
        }
        add_files(filedir + recursive_dir_skip_path, xstat.st_size, NULL);
        return(0);
    }

    plen = strlen(filedir);
    if((plen + 1) >= filedirsz) goto quit;
    strcpy(filedir + plen, "/");
    plen++;

    for(i = 0; i < n; i++) {
        if(!strcmp(namelist[i]->d_name, ".") || !strcmp(namelist[i]->d_name, "..")) continue;

        namelen = strlen(namelist[i]->d_name);
        if((plen + namelen) >= filedirsz) goto quit;
        //strcpy(filedir + plen, namelist[i]->d_name);
        memcpy(filedir + plen, namelist[i]->d_name, namelen);
        filedir[plen + namelen] = 0;

        if(stat(filedir, &xstat) < 0) {
            fprintf(stderr, "**** %s", filedir);
            STD_ERR;
        }
        if(S_ISDIR(xstat.st_mode)) {
            if(recursive_dir(filedir, filedirsz) < 0) goto quit;
        } else {
            add_files(filedir + recursive_dir_skip_path, xstat.st_size, NULL);
        }
        FREEZ(namelist[i]);
    }
    ret = 0;

quit:
    for(; i < n; i++) FREEZ(namelist[i]);
    FREEZ(namelist);
#endif
    filedir[plen - 1] = 0;
    return(ret);
}



u8 *fdload(u8 *fname, int *fsize) {
    struct stat xstat;
    FILE    *fd;
    int     size;
    u8      *buff;

    if(!fname) return(NULL);
    printf("  %s\n", fname);
    fd = fopen(fname, "rb");
    if(!fd) return(NULL);
    fstat(fileno(fd), &xstat);
    size = xstat.st_size;
    if(size == -1) size = -2;   // lame, impossible case
    buff = malloc(size + 1);
    if(buff) {
        fread(buff, 1, size, fd);
        buff[size] = 0;
    } else {
        size = 0;
    }
    fclose(fd);
    if(fsize) *fsize = size;
    return(buff);
}



int cstring(u8 *input, u8 *output, int maxchars, int *inlen) {
    i32     n,
            len;
    u8      *p,
            *o;

    if(!input || !output) {
        if(inlen) *inlen = 0;
        return(0);
    }

    p = input;
    o = output;
    while(*p) {
        if(maxchars >= 0) {
            if((o - output) >= maxchars) break;
        }
        if(*p == '\\') {
            p++;
            switch(*p) {
                case 0:  return(-1); break;
                //case '0':  n = '\0'; break;
                case 'a':  n = '\a'; break;
                case 'b':  n = '\b'; break;
                case 'e':  n = '\e'; break;
                case 'f':  n = '\f'; break;
                case 'n':  n = '\n'; break;
                case 'r':  n = '\r'; break;
                case 't':  n = '\t'; break;
                case 'v':  n = '\v'; break;
                case '\"': n = '\"'; break;
                case '\'': n = '\''; break;
                case '\\': n = '\\'; break;
                case '?':  n = '\?'; break;
                case '.':  n = '.';  break;
                case ' ':  n = ' ';  break;
                case 'x': {
                    //n = readbase(p + 1, 16, &len);
                    //if(len <= 0) return(-1);
                    if(sscanf(p + 1, "%02x%n", &n, &len) != 1) return(-1);
                    if(len > 2) len = 2;
                    p += len;
                    } break;
                default: {
                    //n = readbase(p, 8, &len);
                    //if(len <= 0) return(-1);
                    if(sscanf(p, "%3o%n", &n, &len) != 1) return(-1);
                    if(len > 3) len = 3;
                    p += (len - 1); // work-around for the subsequent p++;
                    } break;
            }
            *o++ = n;
        } else {
            *o++ = *p;
        }
        p++;
    }
    *o = 0;
    len = o - output;
    if(inlen) *inlen = p - input;
    return(len);
}



// alternative to sscanf so it's possible to use also commas and hex numbers
int get_parameter_numbers(u8 *s, ...) {
    va_list ap;
    int     i,
            *par;

    // do NOT reset the parameters because they could have default values different than 0!

    if(!s) return(0);
    va_start(ap, s);
    for(i = 0;; i++) {
        par = va_arg(ap, int *);
        if(!par) break;

        while(*s && !myisalnum(*s)) s++;
        if(!*s) break;
        *par = myatoi(s);
        while(*s && myisalnum(*s)) s++;
        if(!*s) break;
    }
    va_end(ap);
    return(i);
}



int check_wildcard(u8 *fname, u8 *wildcard) {
    u8      *f,
            *w,
            *a;

    if(!fname) return(-1);
    if(!wildcard) return(-1);
    f = fname;
    w = wildcard;
    a = NULL;
    while(*f || *w) {
        if(!*w && !a) return(-1);
        if(*w == '?') {
            if(!*f) break;
            w++;
            f++;
        } else if(*w == '*') {
            w++;
            a = w;
        } else {
            if(!*f) break;
            if(tolower(*f) != tolower(*w)) {
                if(!a) return(-1);
                f++;
                w = a;
            } else {
                f++;
                w++;
            }
        }
    }
    if(*f || *w) return(-1);
    return(0);
}



u8 *create_dir(u8 *fname, int mdir) {
    u8      *p,
            *l;

    if(!fname) return(NULL);
    p = strchr(fname, ':'); // unused
    if(p) {
        *p = '_';
        fname = p + 1;
    }
    for(p = fname; *p && strchr("\\/. \t:", *p); p++) *p = '_';
    fname = p;

    for(p = fname; ; p = l + 1) {
        for(l = p; *l && (*l != '\\') && (*l != '/'); l++);
        if(!*l) break;
        *l = 0;

        if(!strcmp(p, "..")) {
            p[0] = '_';
            p[1] = '_';
        }

        if(mdir) make_dir(fname);
        *l = PATHSLASH;
    }
    return(fname);
}



int get_yesno(void) {
    u8      ans[16];

    if(fgetz(ans, sizeof(ans), stdin, NULL) < 0) return(0);
    return(tolower(ans[0]));
}



int check_overwrite(u8 *fname, int check_if_present_only) {
    FILE    *fd;
    int     c;

    if(force_overwrite) return(0);
    if(!fname) return(0);
    fd = fopen(fname, "rb");
    if(!fd) return(0);
    fclose(fd);
    if(check_if_present_only) return(-1);
    if(force_rename) return(-2);
    printf(
        "\n"
        "- the file \"%s\" already exists\n  do you want to overwrite it?\n"
        "    y = overwrite (you can use also the 'o' key)\n"
        "    n = skip (default, just press RETURN)\n"
        "    a = overwrite all the files without asking\n"
        "    r = automatically rename the files with the same name\n"
        "  \n",
        fname);
    if(append_mode) printf("\n"
        "  (remember that you are in append mode so be sure that the output folder was\n"
        "  empty otherwise the new data will be appended to the existent files!) ");
    c = get_yesno();
    if(c == 'y') return(0);
    if(c == 'o') return(0); // Overwrite
    if(c == 'a') {
        force_overwrite = 1;
        return(0);
    }
    if(c == 'r') {
        force_rename = 1;
        return(-2);
    }
    return(-1);
}



u8 *myalloc(u8 **data, QUICKBMS_int wantsize, QUICKBMS_int *currsize) {
    QUICKBMS_int    ows;    // original wantsize
    u8      *old_data;      // allocate it at any cost

    if(wantsize < 0) {
        fprintf(stderr, "\nError: the requested amount of bytes to allocate is negative (%"PRId")\n", wantsize);
        myexit(-1);
    }
    if(!wantsize) return(NULL);
    if(!data) return(NULL);

    ows = wantsize;
    wantsize += MYALLOC_ZEROES; // needed by XMemDecompress



    // quick secure way that uses the advantages of xdbg_alloc
    //if((wantsize < 0) || (wantsize < ows)) {    // due to integer rounding
    if(wantsize < 0) {
        fprintf(stderr, "\nError: the requested amount of bytes to allocate is negative/too big (%"PRId")\n", wantsize);
        myexit(-1);
    }
    if(currsize && (ows <= *currsize)) {
        if(*currsize > 0) return(*data);
    }
    *data = realloc(*data, wantsize);
    if(currsize) *currsize = ows;
    memset((*data) + ows, 0, wantsize - ows);
    return(*data);
    // end of quick secure way



    wantsize = (wantsize + 4095) & (~4095);     // not bad as fault-safe and fast alloc solution: padding (4096 is usually the default size of a memory page)
    if((wantsize < 0) || (wantsize < ows)) {    // due to integer rounding
        fprintf(stderr, "\nError: the requested amount of bytes to allocate is negative/too big (%"PRId")\n", wantsize);
        myexit(-1);
        //wantsize = ows;   // remember memset MYALLOC_ZEROES
    }

    if(currsize && (wantsize <= *currsize)) {
        if(*currsize > 0) goto quit;
    }

    old_data = *data;
    *data = realloc(*data, wantsize);
    if(!*data) {
        FREEZ(old_data);
        *data = malloc(wantsize);
        if(!*data) {
            fprintf(stderr, "- try allocating %"PRIu" bytes\n", wantsize);
            STD_ERR;
        }
    }
    if(currsize) *currsize = wantsize - MYALLOC_ZEROES;      // obviously
quit:
    memset((*data) + ows, 0, MYALLOC_ZEROES);   // ows is the original wantsize, useful in some cases like XMemDecompress
    return(*data);
}



int delimit(u8 *str) {
    u8      *p;

    if(!str) return(-1);
    for(p = str; *p && (*p != '\n') && (*p != '\r'); p++);
    *p = 0;
    return(p - str);
}



void alloc_err(const char *fname, i32 line, const char *func) {
    fprintf(stderr, "\n- error in %s line %d: %s()\n", fname, (i32)line, func);
    fprintf(stderr, "Error: tentative of allocating -1 bytes\n");
    myexit(-2);
}



void std_err(const char *fname, i32 line, const char *func) {
    fprintf(stderr, "\n- error in %s line %d: %s()\n", fname, (i32)line, func);
    perror("Error");
    myexit(-2);
}



void winerr(void) {
#ifdef WIN32
    u8      *message = NULL;

    FormatMessage(
      FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
      NULL,
      GetLastError(),
      0,
      (char *)&message,
      0,
      NULL);

    if(message) {
        fprintf(stderr, "\nError: %s\n", message);
        LocalFree(message);
    } else {
        fprintf(stderr, "\nError: unknown Windows error\n");
    }
    myexit(-1);
#else
    STD_ERR;
#endif
}



void myexit(int ret) {
    if(!ret && quick_gui_exit) {
        // nothing to do
    } else {
#ifdef WIN32
        u8      ans[16];

        if(GetWindowLong(mywnd, GWL_WNDPROC)) {
            fgetz(ans, sizeof(ans), stdin,
                "\nPress RETURN to quit");
        }
#endif
        if(ret == -1) {
            fprintf(stderr, "\n"
                "Note that if both the scripts and your files are correct then it's possible\n"
                "that the script needs a newer version of QuickBMS, in which case download it:\n"
                "\n"
                "  http://aluigi.org/quickbms\n"
                "\n");
        }
    }
    exit(ret);
}



u_int rol(u_int n1, u_int n2) {
    return((n1 << n2) | (n1 >> ((u_int)INTSZ - n2)));
}



u_int ror(u_int n1, u_int n2) {
    return((n1 >> n2) | (n1 << ((u_int)INTSZ - n2)));
}



u_int bitswap(u_int n1, u_int n2) {
    u_int   out,
            rem = 0;

    if(n2 < INTSZ) {
        rem = n1 & (((int)-1) ^ (((int)1 << n2) - (int)1));
    }

    for(out = 0; n2; n2--) {
        out = (out << (int)1) | (n1 & (int)1);
        n1 >>= (u_int)1;
    }
    return(out | rem);
}



u_int byteswap(u_int n1, u_int n2) {
    u_int   out,
            rem = 0;

    if(n2 < (INTSZ >> 3)) {
        rem = n1 & (((int)-1) ^ (((int)1 << (n2 << (int)3)) - (int)1));
    }

    for(out = 0; n2; n2--) {
        out = (out << (int)8) | (n1 & (int)0xff);
        n1 >>= (u_int)8;
    }
    return(out | rem);
}



int power(int n1, int n2) {
    int     out = 1;

    for(;;) {
        if(n2 & 1) out *= n1;
        n2 >>= (int)1;
        if(!n2) break;
        n1 *= n1;
    }
    return(out);
}



int mysqrt(int num) {
    int    ret    = 0,
           ret_sq = 0,
           b;
    int    s;

    for(s = (INTSZ >> 1) - 1; s >= 0; s--) {
        b = ret_sq + ((int)1 << (s << (int)1)) + ((ret << s) << (int)1);
        if(b <= num) {
            ret_sq = b;
            ret += (int)1 << s;
        }
    }
    return(ret);
}



int radix(int n1, int n2) {
    int     i,
            olds,    // due to the
            news;    // lack of bits

    if(!n1 || !n2) return(0);

    if(n2 == 2) return(mysqrt(n1)); // fast way

    for(i = olds = 1; ; i <<= 1) {   // faster???
        news = power(i, n2);
        if((news > n1) || (news < olds)) break;
        olds = news;
    }

    for(i >>= 1; ; i++) {
        news = power(i, n2);
        if((news > n1) || (news < olds)) break;
        olds = news;
    }
    return(i - 1);
}



u32 str2ip(u8 *data) {
    unsigned    a, b, c, d;

    if(!data[0]) return(0);
    sscanf(data, "%u.%u.%u.%u", &a, &b, &c, &d);
    return((a & 0xff) | ((b & 0xff) << 8) | ((c & 0xff) << 16) | ((d & 0xff) << 24));
}



u8 *ip2str(u32 ip) {
    static u8  data[16];

    sprintf(data, "%u.%u.%u.%u",
        (ip & 0xff), ((ip >> 8) & 0xff), ((ip >> 16) & 0xff), ((ip >> 24) & 0xff));
    return(data);
}



u8 *clean_filename(u8 *fname) {
    u8      *p;

    if(fname[1] == ':') fname += 2;
    for(p = fname; *p && (*p != '\n') && (*p != '\r'); p++) {
        if(strchr("?%*:|\"<>", *p)) {    // invalid filename chars not supported by the most used file systems
            *p = '_';
        }
    }
    *p = 0;

    // remove final spaces and dots
    for(p--; p >= fname; p--) {
        if((*p != ' ') && (*p != '.')) break;
        *p = 0;
    }
    return(fname);
}



int debug_privileges(void)  {
#ifdef WIN32
    TOKEN_PRIVILEGES tp;
    HANDLE  hp;

    if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hp)) return(-1);
    memset(&tp, 0, sizeof(tp));
    tp.PrivilegeCount = 1;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid)) return FALSE;
    if(!AdjustTokenPrivileges(hp, FALSE, &tp, sizeof(tp), NULL, NULL)) return(-1);
    CloseHandle(hp);
#endif
    return(0);
}



// I don't trust memmove, it gave me problems in the past
int mymemmove(u8 *dst, u8 *src, int size) {
    int     i;

    if(!dst || !src) return(0);
    if(size < 0) size = strlen(src) + 1;
    if(dst < src) {
        for(i = 0; i < size; i++) {
            dst[i] = src[i];
        }
    } else {
        for(i = size - 1; i >= 0; i--) {
            dst[i] = src[i];
        }
    }
    return(i);
}



int myrand(void) {
    static  int rnd = 0;

    if(!rnd) rnd = time(NULL);
    rnd = ((rnd * 0x343FD) + 0x269EC3);
    return(rnd);
}



u8 *quickbms_tmpname(u8 **fname, u8 *prefix, u8 *ext) {
    FILE    *fd;

    if(!prefix) prefix = temp_folder;
    for(;;) {
        spr(fname, "%s%cquickbms_%08x%08x.%s", prefix, PATHSLASH, (i32)myrand(), (i32)myrand(), ext);
        fd = fopen(*fname, "rb");
        if(!fd) return(*fname);
        fclose(fd);
    }
    return(NULL);
}



u32 mydump(u8 *fname, u8 *data, u32 size) {
    FILE    *fd;

    fd = fopen(fname, "wb");
    if(!fd) STD_ERR;
    fwrite(data, 1, size, fd);
    fclose(fd);
    return(size);
}



// from NetBSD
void *mymemmem(const void *b1, const void *b2, size_t len1, size_t len2) {
    unsigned char *sp  = (unsigned char *) b1;
    unsigned char *pp  = (unsigned char *) b2;
    unsigned char *eos = sp + len1 - len2;

    if(!(b1 && b2 && len1 && len2))
        return NULL;

    while (sp <= eos) {
        if (*sp == *pp)
            if (memcmp(sp, pp, len2) == 0)
                return sp;
        sp++;
    }
    return NULL;
}



int invalid_chars_to_spaces(u8 *s) {
    if(!s) return(-1);
    for(; *s; s++) {
        if(*s < ' ') *s = ' ';
    }
    return(0);
}



u8 *quickbms_fopen(u8 *fname) {
    FILE    *fd;
    int     i;
    u8      *new_fname,
            *mypath,
            *p;

    p = mystrrchrs(fname, "\\/");
    if(p) fname = p + 1;

    new_fname = NULL;
    for(i = 0; ; i++) {
        switch(i) {
            case 0:  mypath = bms_folder;       break;
            case 1:  mypath = exe_folder;       break;
            case 2:  mypath = file_folder;      break;
            case 3:  mypath = current_folder;   break;
            case 4:  mypath = output_folder;    break;
            case 5:  mypath = ".";              break;
            default: mypath = NULL;             break;
        }
        if(!mypath) {
            FREEZ(new_fname)
            break;
        }
        spr(&new_fname, "%s%c%s", mypath, PATHSLASH, fname);
        fd = fopen(new_fname, "rb");
        if(fd) {
            fclose(fd);
            break;
        }
    }
    return(new_fname);
}



u8 *skip_begin_string(u8 *p) {
    if(p) {
        while(*p) {
            if(*p > ' ') break;
            p++;
        }
    }
    return(p);
}



u8 *skip_end_string(u8 *p) {
    u8      *l;

    if(p) {
        for(l = p + strlen(p) - 1; l >= p; l--) {
            if(*l > ' ') return(l);
            *l = 0;
        }
    }
    return(p);
}



u8 *skip_delimit(u8 *p) {
    p = skip_begin_string(p);
    skip_end_string(p);
    return(p);
}

