Mercurial > hg > batmud > maputils
view colormap.c @ 1822:892d5277f1ff
Remove note about the search pattern parser being not very tolerant, it's somewhat better now.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Mon, 30 Oct 2017 12:55:21 +0200 |
parents | cf0369e8aeff |
children | 79dd960610cb |
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-2015 Tecnic Software productions (TNSP) */ #include "libmaputils.h" #include "th_args.h" #include "th_string.h" #define SET_MAX_COLORS (256) #define SET_MAX_STRLEN (1024) typedef struct { char *fmtName; char *fmtDescription; BOOL supBackColor; void (*putTagMarker) (FILE *, const char *, const int color, const int marker); void (*putTagLocation) (FILE *, const char *, const int color); void (*putTagLocationEnd) (FILE *, const char *); void (*putTagStart) (FILE *, const int color); void (*putTagEnd) (FILE *); BOOL (*putTagEOL) (FILE *); void (*putFileStart) (FILE *); void (*putFileEnd) (FILE *); int (*putString) (const char *str, FILE *); } CMapOutFormat; char *inFilename = NULL, *outFilename = 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 */ static const th_optarg 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 }, }; static const int optListN = sizeof(optList) / sizeof(optList[0]); /* * ANSI output format functions */ void putTagStartANSI(FILE *outFile, const 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, const 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" #define XHTML_LOCMARKER "marker" void putColorClassXHTML(FILE *outFile, const int color) { int q = 'a' + color; if (optCheatMode) fprintf(outFile, "class=%c>", q); else fprintf(outFile, "class=\"%c\">", q); } void putTagMarkerXHTML(FILE *outFile, const char *locID, const int color, const int marker) { fprintf(outFile, "<i class=\"" XHTML_LOCMARKER " %c\" id=\"%s\">%c", 'a' + color, locID, marker); } void putTagLocationXHTML(FILE *outFile, const char *locID, const int color) { fprintf(outFile, "<i class=\"" XHTML_LOCLABEL "\"><a id=\"%s\" ", locID); putColorClassXHTML(outFile, color); } void putTagLocationEndXHTML(FILE *outFile, const char *locID) { (void) locID; fprintf(outFile, "</a>"); } void putTagStartXHTML(FILE *outFile, const 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 putFileGenericHTML(FILE *outFile) { char buf[32]; fprintf(outFile, " <script type=\"text/javascript\" src=\"util.js\"></script>\n" " <style type=\"text/css\">\n" " <!--\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; " : "", muColorToCSSColor(buf, sizeof(buf), optBackColor), optXHTMLTagName ); muPrintHTMLcolors(outFile, optXHTMLTagName, NULL, NULL); muPrintHTMLcolors(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" " border: 2px solid black;\n" " border-radius: 0.35em;\n" " padding: 0.15em;\n" " box-shadow: 2px 2px 4px black;\n" " }\n" " div.sbtitle {\n" " font-size: 1.5em;\n" " font-weight: bold;\n" " }\n" " i.label a.nactive {\n" " border: 2px solid rgba(100,0,0, 0.5);\n" " background: rgba(255,0,0, 0.7);\n" " color: rgba(255,255,255, 0.8);\n" " text-shadow: 1px 1px 1px #000;\n" " font-weight: bold;\n" " transform: scale(1.1);\n" " }\n" " i.marker.nactive {\n" " animation: pulse 1s ease infinite;\n" " }\n" " @keyframes pulse {\n" " 0%% { background: #000; }\n" " 50%% { background: #f00; }\n" " 100%% { background: #000; }\n" " }\n" ); } fprintf(outFile, " -->\n" " </style>\n" ); if (optUrchinFile) { if (muCopyFileToStream(outFile, optUrchinFile) < 0) { THERR("Error copying urchin file '%s' to output!\n", optUrchinFile); exit(17); } } fprintf(outFile, "</head>\n" "<body onLoad=\"jsGotoOnLoadPos();\">\n"); if (!optPosGlue) { if (optMapTitle) { fprintf(outFile, "<h2>"); fputse(optMapTitle, outFile); fprintf(outFile, "</h2>\n"); } } else { fprintf(outFile, "<div id=\"sbox\" class=\"sgbox\">\n" " <div class=\"sbtitle\">"); fputse(optMapTitle, outFile); fprintf(outFile, "</div>\n" " <form>\n" " <select id=\"slocation\" onChange=\"jsGotoPos();\" autofocus=\"autofocus\">\n" "@LOCATIONS@\n" " </select>\n" " <br />\n" " <input id=\"shide\" onClick=\"jsToggleLabels();\" type=\"checkbox\"%s><label for=\"shide\">Labels</label>\n" " <input id=\"sscroll\" type=\"checkbox\"%s><label for=\"sscroll\">Smooth scroll</label>\n" " </form>\n" "</div>\n", (1 ? " checked=\"checked\"" : ""), (0 ? " checked=\"checked\"" : "") ); } fprintf(outFile, "<pre>\n" ); } void putFileStartXHTML(FILE *outFile) { muPrintHTMLhead(outFile, optMapTitle, FALSE); putFileGenericHTML(outFile); } void putFileEndXHTML(FILE *outFile) { // XHTML document end tags fprintf(outFile, "</pre>\n" "</body>\n" "</html>\n" ); } /* * XHTML+CSS output format functions */ void putFileStartHTML5(FILE *outFile) { muPrintHTMLhead(outFile, optMapTitle, TRUE); putFileGenericHTML(outFile); } /* Process a normal format input */ void checkEndTag(FILE *outFile, const CMapOutFormat *fmt, const int prevColor) { if (prevColor != -1 && (!fmt->supBackColor || prevColor != optBackColor)) fmt->putTagEnd(outFile); } BOOL getTagStr(FILE *inFile, char *tmpStr, const size_t len, const int endch) { size_t i; int mch = EOF; for (i = 0; i < len && (mch = fgetc(inFile)) != EOF;) { if (mch == endch) break; else tmpStr[i++] = mch; } tmpStr[i] = 0; if (mch == EOF) { THERR("Unexpected end of file.\n"); return FALSE; } else if (mch != endch) { THERR("No end tag 0x%02x found.\n", endch); return FALSE; } return TRUE; } BOOL processData(FILE *inFile, FILE *outFile, const CMapOutFormat *fmt) { int k, currColor, prevColor; currColor = prevColor = -1; while ((k = fgetc(inFile)) != EOF) { if (k == '\n') { if (fmt->putTagEOL(outFile)) currColor = -1; } else if (k == 0xfb) { char tmpStr[SET_MAX_STRLEN]; int mch; // Location marker mode checkEndTag(outFile, fmt, prevColor); // Get location marker tag if (!getTagStr(inFile, tmpStr, SET_MAX_STRLEN, 0xfc)) return FALSE; // Get marker character mch = fgetc(inFile); k = fgetc(inFile); if (k != 0xfe) { THERR("Expected location tag '%s' end, but did not find one.\n", tmpStr); return FALSE; } currColor = muGetMapPieceColor(mch, optUseOldFormat, optCityFormat); if (fmt->putTagMarker != NULL) fmt->putTagMarker(outFile, tmpStr, currColor, mch); else fprintf(outFile, "%c", mch); currColor = prevColor = -2; } else if (k == 0xff) { char tmpStr[SET_MAX_STRLEN], tmpStr2[SET_MAX_STRLEN]; int mcol; // Location title mode checkEndTag(outFile, fmt, prevColor); // Get location marker tag if (!getTagStr(inFile, tmpStr, SET_MAX_STRLEN, 0xfc)) return FALSE; // Get color mcol = fgetc(inFile); if (mcol == EOF) { THERR("Unexpected end of file (location tag colour).\n"); return FALSE; } // Get location name if (!getTagStr(inFile, tmpStr2, SET_MAX_STRLEN, 0xfe)) { THERR("Expected location tag '%s' end, but did not find one.\n", tmpStr); return FALSE; } if (!fmt->putTagLocation && fmt->putTagStart) fmt->putTagStart(outFile, -mcol - 1); if (fmt->putTagLocation) fmt->putTagLocation(outFile, tmpStr, mcol); if (fmt->putString) fmt->putString(tmpStr2, outFile); else fputs(tmpStr2, outFile); if (fmt->putTagLocationEnd) fmt->putTagLocationEnd(outFile, tmpStr); currColor = prevColor = -2; } else { currColor = muGetMapPieceColor(k, optUseOldFormat, optCityFormat); if (currColor != prevColor) { checkEndTag(outFile, fmt, prevColor); if ((!fmt->supBackColor || currColor != optBackColor) && fmt->putTagStart) fmt->putTagStart(outFile, currColor); fprintf(outFile, "%c", k); } else { fprintf(outFile, "%c", k); } } prevColor = currColor; } checkEndTag(outFile, fmt, prevColor); 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 */ CMapOutFormat outputFormats[] = { { "xhtml", "XHTML+CSS", TRUE, putTagMarkerXHTML, putTagLocationXHTML, putTagLocationEndXHTML, putTagStartXHTML, putTagEndXHTML, putEOLXHTML, putFileStartXHTML, putFileEndXHTML, fputse }, { "html5", "HTML5+CSS", TRUE, putTagMarkerXHTML, putTagLocationXHTML, putTagLocationEndXHTML, putTagStartXHTML, putTagEndXHTML, putEOLXHTML, putFileStartHTML5, putFileEndXHTML, fputse }, { "ansi", "ANSI text", FALSE, NULL, NULL, NULL, putTagStartANSI, putTagEndANSI, putEOLANSI, putFileStartANSI, putFileEndANSI, NULL }, { "text", "Plain ASCII text", FALSE, NULL, NULL, NULL, putTagStartText, putTagEndText, putEOLText, putFileStartText, NULL, NULL }, }; const int noutputFormats = sizeof(outputFormats) / sizeof(outputFormats[0]); void argShowHelp(void) { th_print_banner(stdout, th_prog_name, "[options] <input mapfile>"); th_args_help(stdout, optList, optListN, 0); fprintf(stdout, "\nAvailable OUTPUT formats:\n"); for (int i = 0; i < noutputFormats; i++) { fprintf(stdout, " %-8s - %s %s\n", outputFormats[i].fmtName, outputFormats[i].fmtDescription, (i == optOutputFormat) ? "(default)" : "" ); } fprintf(stdout, "\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: outFilename = optArg; THMSG(2, "Output file set to '%s'.\n", outFilename); 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 (!inFilename) inFilename = currArg; else { THERR("Too many input map files specified!\n"); return FALSE; } return TRUE; } /* Main program */ int main(int argc, char *argv[]) { FILE *inFile = NULL, *outFile = NULL; CMapOutFormat *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, OPTH_BAILOUT)) exit(1); if (inFilename == 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[SET_MAX_COLORS]; int colChangesFrom[SET_MAX_COLORS]; THMSG(1, "Performing statistical frequency analysis ...\n"); THMSG(2, "Reading '%s' for analysis data ...\n", inFilename); if ((inFile = fopen(inFilename, "rb")) == NULL) { THERR("Error opening input file '%s'!\n", inFilename); exit(1); } // Initialize counters memset(colChangesTo, 0, sizeof(colChangesTo)); 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 < 0xfb) { c = muGetMapPieceColor(k, optUseOldFormat, optCityFormat); if (c != p && c >= 0 && c < SET_MAX_COLORS) { colChangesTo[c]++; if (p >= 0 && p < SET_MAX_COLORS) colChangesFrom[p]++; } } p = c; } fclose(inFile); // Find highest frequency THMSG(2, "Computing results.\n"); tMax = fMax = tn = fn = -1; for (i = 0; i < SET_MAX_COLORS; 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(inFilename, "rb")) == NULL) { THERR("Error opening input file '%s'!\n", inFilename); goto out; } // Open output file if (outFilename == NULL) outFile = stdout; else if ((outFile = fopen(outFilename, "wb")) == NULL) { THERR("Error opening output file '%s'!\n", outFilename); goto out; } // Okay, let's process the shit fmt = &outputFormats[optOutputFormat]; THMSG(1, "Converting to '%s' ...\n", fmt->fmtName); if (!optNoHeaders && fmt->putFileStart) fmt->putFileStart(outFile); processData(inFile, outFile, fmt); if (!optNoHeaders && fmt->putFileEnd) fmt->putFileEnd(outFile); out: if (outFile != NULL) fclose(outFile); if (inFile != NULL) fclose(inFile); THMSG(1, "Done.\n"); exit(0); return 0; }