view colormap.c @ 945:81184d58133c aprilli2011

Sync.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 14 May 2010 11:18:20 +0000
parents 87af6f129228
children 5c2bd0ac3118
line wrap: on
line source

/*
 * Convert BatMUD ASCII map to different formats
 * Programmed by Matti 'ccr' Hämäläinen (Ggr Pupunen)
 * (C) Copyright 2006-2008 Tecnic Software productions (TNSP)
 */
#include "maputils.h"
#include "th_args.h"
#include "th_string.h"

#define MAXCOL    (256)
#define MAXSTR    (1024)

typedef struct {
    char *fmtName;
    char *fmtDescription;
    BOOL supBackColor;
    void (*putTagLocation) (FILE *, char *, int);
    void (*putTagLocationEnd) (FILE *, char *);
    void (*putTagStart) (FILE *, int);
    void (*putTagEnd) (FILE *);
    BOOL (*putTagEOL) (FILE *);
    void (*putFileStart) (FILE *);
    void (*putFileEnd) (FILE *);
    void (*putString) (FILE *, char *);
} outfmt_t;


char    *srcFilename = NULL,
        *destFilename = NULL;

char    *optXHTMLTagName = "i",
        *optMapTitle = NULL,
        *optUrchinFile = NULL;
    
BOOL    optPerformAnalysis = FALSE,
        optUseOldFormat = FALSE,
        optCheatMode = FALSE,
        optNoHeaders = FALSE,
        optPosGlue = FALSE,
        optCityFormat = FALSE;

int     optOutputFormat = 0,
        optBackColor = 0;

    
/* Arguments
 */
optarg_t optList[] = {
    { 0, '?', "help",           "Show this help", OPT_NONE },
    { 1, 'v', "verbose",        "Be more verbose", OPT_NONE },
    { 2, 'q', "quiet",          "Be quiet", OPT_NONE },
    { 3, 'T', "html-tag",       "XHTML tag used for map", OPT_ARGREQ },
    { 4, 'a', "analysis",       "Perform statistical analysis", OPT_NONE },
    { 6, 'O', "old-format",     "Input using old symbols/colors", OPT_NONE },
    { 7, 'o', "output",         "Output filename", OPT_ARGREQ },
    { 8, 'f', "format",         "Specify output format", OPT_ARGREQ },
    { 9, 't', "title",          "Map title", OPT_ARGREQ },
    { 10,'C', "cheat-mode",     "Use cheating in HTML", OPT_NONE },
    { 11,'n', "no-headers",     "Do not output headers/footers", OPT_NONE },
    { 12,'P', "pos-glue",       "Generate JavaScript, etc. for posglue", OPT_NONE },
    { 13,'c', "city-format",    "Input is a city map", OPT_NONE },
    { 14,'u', "urchin-file",    "Specify urchin file", OPT_ARGREQ },
};

const int optListN = (sizeof(optList) / sizeof(optarg_t));


/*
 * ANSI output format functions
 */
void putTagStartANSI(FILE *outFile, int c)
{
    fputc(0x1b, outFile);

    if (c < 0) {
        fprintf(outFile, "[0;%d;30m",
            10 + mapColors[-c-1].ansi);
    } else {
        fprintf(outFile, "[0;%d;%dm",
            mapColors[c].ansiAttr,
            mapColors[c].ansi);
    }
}

void putTagEndANSI(FILE *outFile)
{
    (void) outFile;
    /*
    fputc(0x1b, outFile);
    fprintf(outFile, "[0m");
    */
}

BOOL putEOLANSI(FILE *outFile)
{
    fprintf(outFile, "\n");
    return TRUE;
}

void putFileStartANSI(FILE *outFile)
{
    fputc(0x1b, outFile);
    fprintf(outFile, "[0m");

    if (optMapTitle) {
        fprintf(outFile, "%s\n", optMapTitle);
    }
}


void putFileEndANSI(FILE *outFile)
{
    fputc(0x1b, outFile);
    fprintf(outFile, "[0m");
}


/*
 * ASCII output format functions
 */
void putTagStartText(FILE *outFile, int c)
{
    (void) outFile; (void) c;
//    fprintf(outFile, "§");
}

void putTagEndText(FILE *outFile)
{
    (void) outFile;
//    fprintf(outFile, "$");
}

BOOL putEOLText(FILE *outFile)
{
    fprintf(outFile, "\n");
    return FALSE;
}

void putFileStartText(FILE *outFile)
{
    if (optMapTitle) {
        fprintf(outFile, "%s\n", optMapTitle);
    }
}


/*
 * XHTML+CSS output format functions
 */
#define XHTML_LOCLABEL "label"

void putColorClassXHTML(FILE *outFile, int c)
{
    int q = 'a'+c;

    if (optCheatMode)
        fprintf(outFile, "class=%c>", q);
    else
        fprintf(outFile, "class=\"%c\">", q);
}

void putTagLocationXHTML(FILE *outFile, char * locID, int c)
{
    fprintf(outFile, "<i class=\"" XHTML_LOCLABEL "\"><a id=\"%s\" ", locID);
    putColorClassXHTML(outFile, c);
}

void putTagLocationEndXHTML(FILE *outFile, char *locID)
{
    (void) locID;
    fprintf(outFile, "</a>");
}

void putTagStartXHTML(FILE *outFile, int c)
{
    fprintf(outFile, "<i ");
    putColorClassXHTML(outFile, c);
}

void putTagEndXHTML(FILE *outFile)
{
    fprintf(outFile, "</i>");
}

BOOL putEOLXHTML(FILE *outFile)
{
    fprintf(outFile, "\n");
    return FALSE;
}

void putFileStartXHTML(FILE *outFile)
{
    mcXHTMLhead(outFile, optMapTitle);
    
    fprintf(outFile,
    " <script type=\"text/javascript\" src=\"util.js\"></script>\n"
    " <style type=\"text/css\">\n"
    "  %s." XHTML_LOCLABEL " { background: white; color: black; %s}\n"
    "  body { background: black; color: %s; font-family: Arial, Verdana, sans-serif; }\n"
    "  h2 { color: white; font-size: 14pt; }\n"
    "  pre { font-size: 10pt; }\n"
    "  %s { text-decoration: none; font-style: normal; }\n",
    optXHTMLTagName, (optPosGlue) ? "position: relative; " : "",
    mcGetCSS(optBackColor),
    optXHTMLTagName
    );
    
    mcXHTMLcolors(outFile, optXHTMLTagName, NULL, NULL);
    mcXHTMLcolors(outFile, "a", "background", " color: black;");

    if (optPosGlue) {
    fprintf(outFile,
    "  div.sgbox {\n"
    "    background: #bbb;\n"
    "    color: black;\n"
    "    position: fixed;\n"
    "    top: 0.5em;\n"
    "    right: 0.5em;\n"
    "    width: 25em;\n"
    "    height: 6em;\n"
    "    margin: 4px;\n"
    "    padding: 4px;\n"
    "    z-index: 10;\n"
    "    border: 2px solid gray;\n"
    "    font-size: 10pt;\n"
    "  }\n"
    "  i." XHTML_LOCLABEL " a {\n"
    "    position: absolute;\n"
    "    top: 0px;\n"
    "    left: 0px;\n"
    "    z-index: 2;\n"
    "  }\n"
    );
    }

    fprintf(outFile,
    " </style>\n"
    "</head>\n"
    "<body onLoad=\"gotoOnLoadPos();\">\n");

    if (!optPosGlue) {
        if (optMapTitle) {
            fprintf(outFile, "<h2>");
            fprinte(outFile, optMapTitle);
            fprintf(outFile, "</h2>\n");
        }
    } else {
        fprintf(outFile,
        "<div id=\"sbox\" class=\"sgbox\">\n"
        " <b>");
        fprinte(outFile, optMapTitle);
        fprintf(outFile,
        "</b>\n"
        " <form>\n"
        "  <select name=\"slocation\" onChange=\"gotoPos();\">\n"
        "@LOCATIONS@\n"
        "  </select>\n"
        "  <br /><input name=\"shide\" onClick=\"toggleLabels();\" type=\"checkbox\" checked=\"checked\">Labels</input>\n"
        " </form>\n"
        "</div>\n"
        );
    }

    fprintf(outFile,
    "<pre>\n"
    );
}

void putFileEndXHTML(FILE *outFile)
{
    FILE *f;
    
    /* XHTML document end tags */
    fprintf(outFile,
    "</pre>\n"
    );
    
    if (optUrchinFile) {
        if ((f = fopen(optUrchinFile, "rb")) != NULL) {
            int c;
            while ((c = fgetc(f)) != EOF)
                fputc(c, outFile);
            fclose(f);
        } else {
            THERR("Urchin file '%s' not found!\n", optUrchinFile);
            exit(17);
        }
    }

    fprintf(outFile,
    "</body>\n"
    "</html>\n"
    );
}


/*
 * Old HTML output format functions
 */
void putTagStartHTML(FILE *outFile, int c)
{
    if (c < 0) {
        fprintf(outFile,
        "<font bgcolor='%s' color='black'>",
        mcGetCSS(-c-1));
    } else {
        fprintf(outFile, "<font color='%s'>",
        mcGetCSS(c));
    }
}

void putTagEndHTML(FILE *outFile)
{
    fprintf(outFile, "</font>");
}

BOOL putEOLHTML(FILE *outFile)
{
    fprintf(outFile, "\n");
    return FALSE;
}

void putFileStartHTML(FILE *outFile)
{
    fprintf(outFile,
    "<html>\n"
    "<head>\n"
    );
    
    if (optMapTitle) {
        fprintf(outFile, " <title>");
        fprinte(outFile, optMapTitle);
        fprintf(outFile, "</title>\n");
    }
    
    fprintf(outFile,
    "</head>\n"
    "<body bgcolor=\"black\" text=\"%s\">\n",
    mcGetCSS(optBackColor)
    );

    if (optMapTitle) {
        fprintf(outFile, "<h2>");
        fprinte(outFile, optMapTitle);
        fprintf(outFile, "</h2>\n");
    }

    fprintf(outFile,
    "<pre>\n"
    );
}

void putFileEndHTML(FILE *outFile)
{
    fprintf(outFile,
    "</pre>\n"
    "</body>\n"
    "</html>\n"
    );
}


/* Process a normal format input
 */
BOOL processNormal(FILE *inFile, FILE *outFile, outfmt_t *fmt)
{
    int k, c, p;

    c = p = -1;
    while ((k = fgetc(inFile)) != EOF) {
        if (k == '\n') {
            if (fmt->putTagEOL(outFile))
                c = -1;
        } else if (k == 0xff) {
            char tmpStr[MAXSTR+1], tmpStr2[MAXSTR+1];
            int i;
            
            /* Location title mode */
            if (p != -1 && (!fmt->supBackColor || p != optBackColor))
                    fmt->putTagEnd(outFile);

            c = p = -2;
            
            /* Location marker tag */
            for (i = 0; i < MAXSTR && (k = fgetc(inFile)) != EOF;) {
                if (k == 0xfc)
                    break;
                else
                    tmpStr[i++] = k;
            }
            
            tmpStr[i] = 0;
            
            if (k != 0xfc) {
                THERR("Unexpected end of file (location tag name).\n");
                return FALSE;
            }
            
            
            /* Get colour */
            k = fgetc(inFile);
            if (k == EOF) {
                THERR("Unexpected end of file (location tag colour).\n");
                return FALSE;
            }
            
            if (!fmt->putTagLocation && fmt->putTagStart)
                fmt->putTagStart(outFile, -k - 1);
            
            if (fmt->putTagLocation)
                fmt->putTagLocation(outFile, tmpStr, k);

            for (i = 0; i < MAXSTR && (k = fgetc(inFile)) != EOF;) {
                if (k == 0xfe)
                    break;
                else
                    tmpStr2[i++] = k;
            }
            tmpStr2[i] = 0;
            
            if (k != 0xfe) {
                THERR("Expected location tag '%s' end, but did not find one.\n", tmpStr);
                return FALSE;
            }
            
            if (fmt->putString)
                fmt->putString(outFile, tmpStr2);
            else
                fputs(tmpStr2, outFile);
            
            
            if (fmt->putTagLocationEnd)
                fmt->putTagLocationEnd(outFile, tmpStr);
        } else {
            c = mcGetColor(k, optUseOldFormat, optCityFormat);
            if (c != p) {
                if (p != -1 && (!fmt->supBackColor || p != optBackColor))
                    fmt->putTagEnd(outFile);
                
                if (!fmt->supBackColor || (c != optBackColor))
                    fmt->putTagStart(outFile, c);
                
                fprintf(outFile, "%c", k);
            } else {
                fprintf(outFile, "%c", k);
            }
        }
        
        p = c;
    }
    
    if (p != -1 && (!fmt->supBackColor || p != optBackColor))
        fmt->putTagEnd(outFile);

    return TRUE;
}


/* Get a symbol
 */
char getSymbol(int i, BOOL useOld)
{
    if (useOld)
        return mapPieces[i].oldSymbol;
    else
        return mapPieces[i].symbol;
}


/* List of output formats
 */
outfmt_t outputFormats[] = {
    { "xhtml", "XHTML+CSS", TRUE,
    putTagLocationXHTML, putTagLocationEndXHTML,
    putTagStartXHTML, putTagEndXHTML, putEOLXHTML,
    putFileStartXHTML, putFileEndXHTML, fprinte
    },

    { "html", "Old HTML without CSS", TRUE,
    putTagLocationXHTML, putTagLocationEndXHTML,
    putTagStartHTML, putTagEndHTML, putEOLHTML,
    putFileStartHTML, putFileEndHTML, fprinte
    },

    { "ansi", "ANSI text", FALSE,
    NULL, NULL,
    putTagStartANSI, putTagEndANSI, putEOLANSI,
    putFileStartANSI, putFileEndANSI, NULL
    },

    { "text", "Plain ASCII text", FALSE,
    NULL, NULL,
    putTagStartText, putTagEndText, putEOLText,
    putFileStartText, NULL, NULL
    },
};

const int noutputFormats = (sizeof(outputFormats) / sizeof(outfmt_t));


void argShowHelp(void)
{
    int i;
    
    th_args_help(stderr, optList, optListN, th_prog_name,
    "[options] <input mapfile>");
    
    fprintf(stderr, "\nAvailable OUTPUT formats:\n");
    for (i = 0; i < noutputFormats; i++) {
        fprintf(stderr, "  %-8s - %s %s\n",
        outputFormats[i].fmtName,
        outputFormats[i].fmtDescription,
        (i == optOutputFormat) ? "(default)" : ""
        );
    }

    fprintf(stderr, "\n");
}


BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
{
    int i, n;
    
    switch (optN) {
    case 0:
        argShowHelp();
        exit(0);
        break;

    case 1:
        th_verbosityLevel++;
        break;
    
    case 2:
        th_verbosityLevel = -1;
        break;
    
    case 3:
        optXHTMLTagName = optArg;
        THMSG(2, "HTML tag set to '%s'\n", optXHTMLTagName);
        break;
    
    case 4:
        optPerformAnalysis = TRUE;
        THMSG(2, "Analysis enabled.\n");
        break;
    
    case 6:
        optUseOldFormat = TRUE;
        THMSG(2, "Input is using old map symbols/colors.\n");
        break;
    
    case 7:
        destFilename = optArg;
        THMSG(2, "Output file set to '%s'.\n", destFilename);
        break;
    
    case 8:
        /* Get format */
        for (i = 0, n = -1; (i < noutputFormats) && (n < 0); i++)
            if (strcmp(optArg, outputFormats[i].fmtName) == 0)
                n = i;
        
        if (n < 0) {
            THERR("Invalid output format '%s'\n", optArg);
            return FALSE;
        }
        
        optOutputFormat = n;
        THMSG(2, "Output format set to '%s'\n", optArg);
        break;
    
    case 9:
        optMapTitle = optArg;
        THMSG(2, "Map title string set to '%s'.\n", optMapTitle);
        break;
    
    case 10:
        optCheatMode = TRUE;
        THMSG(2, "HTML cheats mode enabled!\n");
        break;

    case 11:
        optNoHeaders = TRUE;
        THMSG(2, "Not outputting headers/footers\n");
        break;

    case 12:
        optPosGlue = TRUE;
        THMSG(2, "Generating JavaScript junk for positioning glue\n");
        break;

    case 13:
        optCityFormat = TRUE;
        THMSG(2, "Input is handled as a city map\n");
        break;

    case 14:
        optUrchinFile = optArg;
        THMSG(2, "Urchin filename set to '%s'.\n", optUrchinFile);
        break;
    
    default:
        THERR("Unknown option '%s'.\n", currArg);
        return FALSE;
    }
    
    return TRUE;
}


BOOL argHandleFile(char *currArg)
{
    if (!srcFilename)
        srcFilename = currArg;
    else {
        THERR("Too many input map files specified!\n");
        return FALSE;
    }

    return TRUE;
}


/* Main program
 */
int main(int argc, char *argv[])
{
    FILE *inFile, *outFile;
    outfmt_t *fmt = NULL;

    /* Initialize */
    th_init("colormap", "ASCII map colorizer", "0.5", NULL, NULL);
    th_verbosityLevel = 0;
    
    /* Parse arguments */
    if (!th_args_process(argc, argv, optList, optListN,
        argHandleOpt, argHandleFile, TRUE))
        exit(1);
    
    if (srcFilename == NULL) {
        THERR("Nothing to do. (try --help)\n");
        exit(0);
    }
    
    /* Do statistical analysis, if needed */
    if (optPerformAnalysis) {
        int k, c, p, i, tMax, fMax, tn, fn;
        int colChangesTo[MAXCOL];
        int colChangesFrom[MAXCOL];
        
        THMSG(1, "Performing statistical frequency analysis ...\n");
        
        THMSG(2, "Reading '%s' for analysis data ...\n",
            srcFilename);

        if ((inFile = fopen(srcFilename, "rb")) == NULL) {
            THERR("Error opening input file '%s'!\n",
                srcFilename);
            exit(1);
        }
        
        /* Initialize counters */
        th_memset(colChangesTo, 0, sizeof(colChangesTo));
        th_memset(colChangesFrom, 0, sizeof(colChangesFrom));
        
        /* Read data, keeping statistics of colour change frequencies
         */
        c = p = -1;
        while ((k = fgetc(inFile)) != EOF) {
            if (k != '\n' && k != ' ' && k < 0xfe) {
                c = mcGetColor(k, optUseOldFormat, optCityFormat);
                if ((c != p) && (c >= 0) && (c < MAXCOL)) {
                    colChangesTo[c]++;
                    
                    if ((p >= 0) && (p < MAXCOL))
                        colChangesFrom[p]++;
                }
            }
            p = c;
        }
        
        fclose(inFile);
        
        /* Find highest frequency
         */
        THMSG(2, "Computing results.\n");
        
        tMax = fMax = tn = fn = -1;
        for (i = 0; i < MAXCOL; i++) {
            if (colChangesTo[i] > tMax) {
                tMax = colChangesTo[i];
                tn = i;
            }
            if (colChangesFrom[i] > fMax) {
                fMax = colChangesFrom[i];
                fn = i;
            }
        }
        
        THMSG(2, "tMax=%d, tn=%d -- fMax=%d, fn=%d\n",
            tMax, tn, fMax, fn);

        if (tMax > fMax)
            optBackColor = tn;
        else
            optBackColor = fn;
        
        THMSG(1, "Using colour %d as 'background' color.\n",
            optBackColor);
    }
    
    /* Open input file */
    if ((inFile = fopen(srcFilename, "rb")) == NULL) {
        THERR("Error opening input file '%s'!\n",
            srcFilename);
        exit(1);
    }
    
    /* Open output file */
    if (destFilename == NULL)
        outFile = stdout;
    else if ((outFile = fopen(destFilename, "wb")) == NULL) {
        THERR("Error opening output file '%s'!\n",
            destFilename);
        exit(1);
    }

    /* Okay, let's process the shit */
    fmt = &outputFormats[optOutputFormat];
    THMSG(1, "Converting to '%s' ...\n", fmt->fmtName);

    if (!optNoHeaders && fmt->putFileStart)
        fmt->putFileStart(outFile);

    processNormal(inFile, outFile, fmt);

    if (!optNoHeaders && fmt->putFileEnd)
        fmt->putFileEnd(outFile);

    fclose(outFile);
    fclose(inFile);
    
    THMSG(1, "Done.\n");
    
    exit(0);
    return 0;
}