/*
    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 cool hexdump HTML output



int             hexhtml_idx     = 0,    // CMD.var[0], don't worry it's only a temporary global variable
                hexhtml_idx_max = 0,
                hexhtml_skip    = 1,
                hexhtml_noascii_details = 0; // save space for the browser
u8              *hexhtml_name   = NULL; // temporary variable

/* types
-1 = cool, small and safe way
0  = tables
1  = a title
2  = a span
*/



#define hexhtml_span        "span"  // IE wants span or nothing
#define hexthml_skip_char   "."     // "\xa0"

#define HEXHTML_ASSIGN \
        hexhtml_t   **hexhtml = NULL; \
        int     *hexhtml_size = NULL; \
        if(fdnum < 0) { \
            hexhtml      = &memory_file[-fdnum].hexhtml; \
            hexhtml_size = &memory_file[-fdnum].hexhtml_size; \
        } else { \
            hexhtml      = &filenumber[fdnum].hexhtml; \
            hexhtml_size = &filenumber[fdnum].hexhtml_size; \
        } \



u_int myftell(int fdnum);



int hexhtml_init(int fdnum, int filesize) {
    int     i;

    HEXHTML_ASSIGN

    if(filesize > ((1024 * 1024) / 2)) {
        fprintf(stderr, "\n"
            "Alert: the input file is too big for the HTML output.\n"
            "       I will skip the visualization of unhandled bytes\n");
        /*
        fprintf(stderr, "\n"
            "Error: the input file is too big for the HTML output.\n"
            "       it takes tons of resources of the browser so you can use this feature\n"
            "       only with small files.\n");
        myexit(-1);
        */
        hexhtml_skip = 1;
    } else {
        hexhtml_skip = 0;
    }

    if(hexhtml && *hexhtml) {
        for(i = 0; i < *hexhtml_size; i++) {
            if((*hexhtml)[i].name) FREEZ((*hexhtml)[i].name);
        }
        FREEZ(*hexhtml);
    }
    *hexhtml_size = filesize;
    *hexhtml = calloc(*hexhtml_size, sizeof(hexhtml_t));
    if(!*hexhtml) STD_ERR;
    hexhtml_name = NULL;
    return(0);
}



int hexhtml_add(int fdnum, u8 *data, int size) {
    int     i,
            len,
            offset;
    u8      *p;

    HEXHTML_ASSIGN
    if(!hexhtml || !*hexhtml) return(-1);

    if(size < 0) return(-1);
    if(!size) return(0);

    offset = myftell(fdnum);
    if(offset > *hexhtml_size) return(-1);
    offset -= size;
    if(offset < 0) return(-1);

    for(i = 0; i < size; i++) {
        (*hexhtml)[offset + i].byte = data[i];
        (*hexhtml)[offset + i].idx  = 1 + hexhtml_idx;
        if(hexhtml_name) {
            // bits are not handled separately because this is a file reading operation
            p = (*hexhtml)[offset + i].name;
            len = 0;
            if(p) len = strlen(p) + 1;    // included '|'
            p = realloc(p, len + strlen(hexhtml_name) + 1);
            if(len) p[len - 1] = '|';
            strcpy(p + len, hexhtml_name);
            (*hexhtml)[offset + i].name = p;
        } else {
            FREEZ((*hexhtml)[offset + i].name)
        }
        (*hexhtml)[offset + i].flags = i ? 0 : 1;
    }
    if(hexhtml_idx > hexhtml_idx_max) hexhtml_idx_max = hexhtml_idx;
    return(0);
}



u8 *hexhtml_color(int idx) {
    static const int minchr = 0x90;
    static const int maxchr = 0xff;
    static u8   ret[32];
    i32     i;
    u8      rgb[3];

    if(idx <= 0) return("ffffff");

    idx *= -0x335313;

    rgb[0] = idx >> 16;
    rgb[1] = idx >> 8;
    rgb[2] = idx;
    for(i = 0; i < 3; i++) {
        if(rgb[i] < minchr) {
            rgb[i] = minchr;
            rgb[(i - 1) & 2] += 0x10;
        }
        if(rgb[i] > maxchr) {
            rgb[i] = maxchr;
            rgb[(i + 1) & 2] -= 0x10;
        }
    }
    sprintf(ret, "%02x%02x%02x", rgb[0], rgb[1], rgb[2]);
    return(ret);
}



u8 *html_isprint(u8 byte) {
    static u8   ret[32];

         if(byte == '\"') return("&quot;");
    else if(byte == '&')  return("&amp;");
    else if(byte == '<')  return("&lt;");
    else if(byte == '>')  return("&gt;");
    else if(byte == 0x00) byte = 0xa0;
    else if(byte == ' ')  byte = 0xa0;
    else if(byte == '\t') byte = 0xa0;
    else if(byte == '\v') byte = 0xa0;
    else if(byte <= ' ')  byte = '.';
    else if(byte >= 0x7f) byte = '.';
    ret[0] = byte;
    ret[1] = 0;
    return(ret);
}



int hexhtml_build(int fdnum) {
    static u8   tmp[PATHSZ + 1] = "";
    FILE    *fd;
    i32     i,
            j,
            len,
            table,
            old_idx,
            skip;
    u8      *name,
            *p;

    HEXHTML_ASSIGN
    if(!hexhtml || !*hexhtml) return(-1);

    if(fdnum < 0) {
        name = "MEMORY_FILE";
    } else {
        name = filenumber[fdnum].filename;
        if(!name) {
            name = filenumber[fdnum].basename;
            if(!name) {
                name = filenumber[fdnum].fullname;
                if(!name) name = "FILE";
            }
        }
    }

    sprintf(tmp, "%.*s_%08x.htm", PATHSZ - 16, name, (i32)(time(NULL) + fdnum));
    p = clean_filename(tmp);

    fprintf(stderr, "\n"
        "- hex HTML output:\n"
        "  file: %s\n"
        "  dump: %s\n"
        "\n"
        "- wait patiently...\n",
        name,
        p);

    if(check_overwrite(p, 0) < 0) return(-1);
    fd = fopen(p, "wb");
    if(!fd) STD_ERR;

    fprintf(fd,
        "<html>\n"
        "<title>QuickBMS: %s</title>\n"
        "<style type='text/css'>\n"
        "<!--\n"
        "*{margin:0;padding:0;color:#000000;font-size:14;font-family:courier,monospace;text-decoration:none}\n"
        "table{border-style:solid}\n"
        "table{table-layout:fixed}\n"
        "td{background-color:#f8f8f8;border-style:solid;border-width:0px 0px 1px 1px;border-color:#c0c0c0;padding:1px 0px 1px 0px}\n"
        "td.offset{background-color:#d4d0c8}\n"
        "td.hex   {background-color:#f8f8f8}\n"
        "td.ascii {background-color:#f4f0e8}\n",
        name);

    for(i = 0; i <= (hexhtml_idx_max + 1); i++) {
        for(j = 0;; j++) {
            switch(j) {
                case 0:  p = "%s.x%d:hover";    break;
                case 1:  p = "%s.x%d";          break;
                //case 2:  p = "%s.x%d:link";     break;
                default: p = NULL;              break;
            }
            if(!p) break;

            sprintf(tmp, p, enable_hexhtml ? "a" : "td", i);
            if(!i) {
                tmp[0] = ' ';
                tmp[1] = ' ';
                tmp[2] = ' ';
                sprintf(tmp + 3, "%s", enable_hexhtml ? "a" : "td");
            }

            fprintf(fd, tmp);
            fprintf(fd,
                "{position:relative;z-index:%d;color:#%s;background-color:#%s;border-style:solid;border-width:0px 0px 0px 0px;border-color:#c0c0c0;padding:2px 0px 1px 0px}\n",
                (!j) ? 200 : 0,
                (!j) ? "ffffff" : "000000",
                (!j) ? "0a246a" : (char *)hexhtml_color(i));

            fprintf(fd, tmp);
            if(j) {
                fprintf(fd, " "hexhtml_span"{display:none}\n");
            } else {
                fprintf(fd, " "hexhtml_span"{display:block;position:absolute;bottom:20;border:1px solid #80ff80;background-color:#008000;color:#ffffff;text-align:center}\n");
            }
        }
    }

    fprintf(fd,
        "-->\n"
        "</style>\n"
        "<body bgcolor=#808080>\n"
        "\n"
        "<table align=left border=3 cellspacing=0 cellpadding=0 rules=none>\n");

    if(enable_hexhtml >= 0) goto hexhtml_other_types;

    fprintf(fd, "<tr>\n");

    for(table = 0; table < 3; table++) {
        switch(table) {
            case 0:  name = "offset width=70";  break;
            case 1:  name = "hex";              break;
            case 2:  name = "ascii";            break;
            default: name = "";                 break;
        }
        fprintf(fd,
            "</td>\n"
            "<td class=%s>",
            name);

        old_idx = -1;
        skip = 0;
        for(i = 0; i < *hexhtml_size; i++) {
            name = (*hexhtml)[i].name;
            if(!name) name = get_varname((*hexhtml)[i].idx - 1);

            if(hexhtml_skip) {
                if(!(*hexhtml)[i].idx) {
                    if(skip < 0) continue;
                    skip = 1;
                } else {
                    if(skip < 0) {
                        if(!table) {
                            len = sprintf(tmp, "...");
                            for(j = 8; j > len; j--) fprintf(fd, "\xa0");   // like a space/dot
                            fprintf(fd, "%s", tmp);
                            fprintf(fd, "<br>");
                            if(i & 0xf) {
                                len = sprintf(tmp, "%X", i - (i & 0xf));
                                for(j = 8; j > len; j--) fprintf(fd, "\xa0");   // like a space/dot
                                fprintf(fd, "%s", tmp);
                            }
                        } else {
                            for(j = 0; j < 16; j++) {
                                switch(table) {
                                    case 1:  fprintf(fd, hexthml_skip_char hexthml_skip_char " "); break;
                                    case 2:  fprintf(fd, hexthml_skip_char " ");  break;
                                    default: break;
                                }
                            }
                            fprintf(fd, "<br>");
                            for(j = 0; j < (i & 0xf); j++) {
                                /*
                                switch(table) {
                                    case 1:  fprintf(fd, hexthml_skip_char hexthml_skip_char " "); break;
                                    case 2:  fprintf(fd, hexthml_skip_char " ");  break;
                                    default: break;
                                }
                                */
                                switch(table) {
                                    case 1:  fprintf(fd, "%02X ", (*hexhtml)[i - (i & 0xf) + j].byte); break;
                                    case 2:  fprintf(fd, "%s ", html_isprint((*hexhtml)[i - (i & 0xf) + j].byte)); break;
                                    default: break;
                                }
                            }
                        }
                    }
                    skip = 0;
                }
            }

            if(!table) {
                if(!(i & 0xf)) {
                    len = sprintf(tmp, "%X", i);
                    for(j = 8; j > len; j--) fprintf(fd, "\xa0");   // like a space/dot
                    fprintf(fd, "%s", tmp);
                }
            } else {
                if(skip >= 0) {
                    if(i & 0xf) fprintf(fd, " ");
                }

                if((table == 2) && hexhtml_noascii_details) {
                    // do nothing
                } else {
                    if(((*hexhtml)[i].flags & 1) || ((*hexhtml)[i].idx != old_idx)) {
                        if(old_idx > 0) fprintf(fd, "</a>");
                        if((*hexhtml)[i].flags & 1) {
                            fprintf(fd,
                                //"<a class=x%d href=:%X><"hexhtml_span">%s</"hexhtml_span">",    // popup mode (IE suffers!)
                                "<a class=x%d href=:%X title=\'%s\'>",    // title mode
                                (*hexhtml)[i].idx,
                                i,
                                name);
                        }
                        old_idx = (*hexhtml)[i].idx;
                    }
                }
            }

            //if(!skip) {
                switch(table) {
                    case 1:  fprintf(fd, "%02X", (*hexhtml)[i].byte); break;
                    case 2:  fprintf(fd, "%s", html_isprint((*hexhtml)[i].byte)); break;
                    default: break;
                }
            /*
            } else {
                switch(table) {
                    case 1:  fprintf(fd, hexthml_skip_char hexthml_skip_char); break;
                    case 2:  fprintf(fd, hexthml_skip_char);  break;
                    default: break;
                }
            }
            */

            if((i & 0xf) == 0xf) {
                fprintf(fd, "<br>");
                if(skip) skip = -1;
            }
        }
    }

    fprintf(fd, "</td>\n");
    goto hexhtml_quit;

hexhtml_other_types:

    /*
        the original cool methods have the big problem of being horribly slow
        and consume an incredible amount of resources... but they were really
        cool and nice to watch, oh well.
        WORK-IN-PROGRESS because I have modified the default values for type -1.
    */

    for(i = 0; i < *hexhtml_size; i += 0x10) {
        len = sprintf(tmp, "%X", i);
        fprintf(fd, "<tr><td class='offset'>");
        for(j = 8; j > len; j--) fprintf(fd, "&nbsp;");
        fprintf(fd, "%s</td></tr>\n", tmp);
    }

    fprintf(fd,
        "</table>\n"
        "<table align=left border=3 cellspacing=0 cellpadding=0 rules=none>\n");

    for(i = 0; i < *hexhtml_size; i++) {
        if(!(i & 0xf)) {
            fprintf(fd, "<tr>");
            if(enable_hexhtml) fprintf(fd, "<td>");
        }

        name = (*hexhtml)[i].name;
        if(!name) name = get_varname((*hexhtml)[i].idx - 1);

        if(!(*hexhtml)[i].idx) {
            if(enable_hexhtml) {
                fprintf(fd,
                    "<a href=:%X>%02X</a>",
                    i,
                    (*hexhtml)[i].byte);
            } else {
                fprintf(fd,
                    "<td>%02X</td>",
                    (*hexhtml)[i].byte);
            }
        } else {
            switch(enable_hexhtml) {
                case 2: {
                    fprintf(fd,
                        "<a class=x%d href=:%X>%02X<"hexhtml_span">%s</"hexhtml_span"></a>",
                        (*hexhtml)[i].idx,
                        i,
                        (*hexhtml)[i].byte,
                        name);
                    break;
                }
                case 1: {
                    fprintf(fd,
                        "<a class=x%d title=\'%s\' href=:%X>%02X</a>",
                        (*hexhtml)[i].idx,
                        name,
                        i,
                        (*hexhtml)[i].byte);
                    break;
                }
                default: {
                    fprintf(fd,
                        "<td class=x%d>%02X<span>%s</span></td>",
                        (*hexhtml)[i].idx,
                        (*hexhtml)[i].byte,
                        name);
                    break;
                }
            }
        }
        if((i & 0xf) == 0xf) {
            if(enable_hexhtml) fprintf(fd, "</td>");
            fprintf(fd, "</tr>\n");
        }
    }

    fprintf(fd,
        "</table>\n"
        "<table align=left border=3 cellspacing=0 cellpadding=0 rules=none>\n");

    for(i = 0; i < *hexhtml_size; i++) {
        if(!(i & 0xf)) {
            fprintf(fd, "<tr>");
            if(enable_hexhtml) fprintf(fd, "<td>");
        }

        name = (*hexhtml)[i].name;
        if(!name) name = get_varname((*hexhtml)[i].idx - 1);

#ifndef hexhtml_noascii_details
        if(!(*hexhtml)[i].idx) {
#endif
            if(enable_hexhtml) {
                fprintf(fd,
                    "<a href=:%X>%s</a>",
                    i,
                    html_isprint((*hexhtml)[i].byte));
            } else {
                fprintf(fd,
                    "<td>%s</td>",
                    html_isprint((*hexhtml)[i].byte));
            }
#ifndef hexhtml_noascii_details
        } else {
            switch(enable_hexhtml) {
                case 2: {
                    fprintf(fd,
                        "<a class=x%d href=:%X>%s<"hexhtml_span">%s</"hexhtml_span"></a>",
                        (*hexhtml)[i].idx,
                        i,
                        html_isprint((*hexhtml)[i].byte),
                        name);
                    break;
                }
                case 1: {
                    fprintf(fd,
                        "<a class=x%d title=\'%s\' href=:%X>%s</a>",
                        (*hexhtml)[i].idx,
                        name,
                        i,
                        html_isprint((*hexhtml)[i].byte));
                    break;
                }
                default: {
                    fprintf(fd,
                        "<td class=x%d>%s<span>%s</span></td>",
                        (*hexhtml)[i].idx,
                        html_isprint((*hexhtml)[i].byte),
                        name);
                }
            }
        }
#endif
        if((i & 0xf) == 0xf) {
            if(enable_hexhtml) fprintf(fd, "</td>");
            fprintf(fd, "</tr>\n");
        }
    }

hexhtml_quit:
    fprintf(fd,
        "</table>\n"
        "</body>\n"
        "</html>\n");

    fclose(fd);
    fprintf(stderr, "- done\n");
    return(0);
}


