Mercurial > hg > batmud > maputils
view src/mkcitymap.c @ 2470:d0aad04c3e61
th-libs now uses stdbool.h if possible, so we need to rename all
BOOL/TRUE/FALSE to bool/true/false.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Wed, 07 Dec 2022 13:23:46 +0200 |
parents | eba783bef2ee |
children | 76b67c40fbf5 |
line wrap: on
line source
/* * Create interactive XHTML+CSS+JS map from input * Programmed by Matti 'ccr' Hämäläinen <ccr@tnsp.org> * (C) Copyright 2006-2022 Tecnic Software productions (TNSP) */ #include "libmaputils.h" #include "liblocfile.h" #include "th_args.h" #include "th_string.h" char *optDestFilename = NULL, *optMapFilename = NULL, *optLocFilename = NULL, *optHeadFilename = NULL, *optMapTitle = NULL; bool optUseOldFormat = false; /* Arguments */ static const th_optarg optList[] = { { 0, '?', "help", "Show this help", OPT_NONE }, { 1, 'v', "verbose", "Be more verbose", OPT_NONE }, { 2, 'o', "output", "Output filename", OPT_ARGREQ }, { 3, 't', "title", "Map title", OPT_ARGREQ }, { 4, 'h', "header-file", "Specify extra <head> data in file", OPT_ARGREQ }, { 5, 'O', "old-format", "Input using old symbols/colors", OPT_NONE }, }; const int optListN = sizeof(optList) / sizeof(optList[0]); void argShowHelp(void) { th_print_banner(stdout, th_prog_name, "[options] <mapfile> <locfile>"); th_args_help(stdout, optList, optListN, 0, 80 - 2); } bool argHandleOpt(const int optN, char *optArg, char *currArg) { switch (optN) { case 0: argShowHelp(); exit(0); break; case 1: th_verbosity++; break; case 2: optDestFilename = optArg; THMSG(2, "Output file set to '%s'.\n", optDestFilename); break; case 3: optMapTitle = optArg; THMSG(2, "Map title string set to '%s'.\n", optMapTitle); break; case 4: optHeadFilename = optArg; THMSG(2, "Head filename set to '%s'.\n", optHeadFilename); break; case 5: optUseOldFormat = true; THMSG(2, "Input is using old map symbols/colors.\n"); break; default: THERR("Unknown option '%s'.\n", currArg); return false; } return true; } bool argHandleFile(char *currArg) { if (!optMapFilename) optMapFilename = currArg; else if (!optLocFilename) optLocFilename = currArg; else { THERR("Too many filenames specified!\n"); return false; } return true; } void outputHTMLHeader(FILE *outFile, const MapLocations *locs) { (void) locs; muPrintHTMLhead(outFile, optMapTitle, true); if (optHeadFilename != NULL && muCopyFileToStream(outFile, optHeadFilename) < 0) { THERR("Error copying urchin file '%s' to output.\n", optHeadFilename); } fprintf(outFile, " <style type=\"text/css\">\n"); muPrintHTMLcolors(outFile, "span", NULL, NULL); fprintf(outFile, " </style>\n"); fprintf(outFile, "</head>\n" "<body onload=\"mapOnLoad();\">\n" ); if (optMapTitle) { fprintf(outFile, "<h1>"); fputse(optMapTitle, outFile); fprintf(outFile, "</h1>\n"); } fprintf(outFile, "<div class=\"map %s\">" "<pre class=\"map\">", optUseOldFormat ? "old" : "new" ); } const char *getCityLocationType(const LocMarker *marker) { switch (marker->flags & LOCF_M_MASK) { // case LOCF_M_CITY: return "special"; case LOCF_M_PCITY: return "gov"; default: switch (marker->flags & LOCF_T_MASK) { case LOCF_T_SHRINE: return "shop"; case LOCF_T_GUILD: return "guild"; /* case LOCF_T_SS: return "ss"; case LOCF_T_MONSTER: return "monster"; case LOCF_T_TRAINER: return "trainer"; case LOCF_T_FORT: return "fort"; */ } break; } return "default"; } static char *getExtraNameData(const LocMarker *marker) { return th_strdup_printf( "%s", (marker->flags & LOCF_CLOSED) ? " <b>[CLOSED]</b>" : "" ); } static int compareLocation(const void *pp1, const void *pp2) { const LocMarker *vp1 = *(const LocMarker **) pp1, *vp2 = *(const LocMarker **) pp2; if (vp1->flags == vp2->flags) return th_strcasecmp(vp1->names[0].name, vp2->names[0].name); else return (vp1->flags & LOCF_MASK) - (vp2->flags & LOCF_MASK); } void outputHTMLFooter(FILE *outFile, MapLocations *locs) { char *tmps; fprintf(outFile, "</pre>" "</div>\n" "<div class=\"loctab\">\n" ); qsort(locs->locations, locs->nlocations, sizeof(LocMarker *), compareLocation); for (int n = 0; n < locs->nlocations; n++) { const LocMarker *marker = locs->locations[n]; if (marker->flags & LOCF_INVIS) continue; if ((tmps = getExtraNameData(marker)) == NULL) return; for (int i = 0; i < marker->nnames; i++) { fprintf(outFile, "<a class=\"loc %s%s lt%s lt%d\" id=\"listloc%d_%d\" href=\"?%d_%d\" " "onmouseover=\"%s('%d_%d');\" onmouseout=\"qn();\">", i == 0 ? "ltprimary" : "ltsecondary", i == 0 && marker->nnames > 1 ? " ltsubitems" : "", getCityLocationType(marker), marker->align, marker->xc, marker->yc, marker->xc, marker->yc, (marker->freeform /* be less spammy in the list || marker->nnames > 1 */) ? "sttq" : "qh", marker->xc, marker->yc); fputse(marker->names[i].name, outFile); fprintf(outFile, "%s</a>\n", tmps); } th_free(tmps); } fprintf(outFile, "</div>\n" ); for (int n = 0; n < locs->nlocations; n++) { const LocMarker *marker = locs->locations[n]; if (marker->flags & LOCF_INVIS) continue; if ((tmps = getExtraNameData(marker)) == NULL) return; fprintf(outFile, "<div class=\"tooltip holder\" id=\"tt%d_%d\">" "<h1>%s%s</h1>", marker->xc, marker->yc, marker->names[0].name, tmps); if (marker->nnames > 1) { for (int i = 1; i < marker->nnames; i++) { fprintf(outFile, "%s%s", marker->names[i].name, i + 1 < marker->nnames ? " ; " : ""); } } if (marker->freeform) { fprintf(outFile, "<br />%s", marker->freeform); } fprintf(outFile, "</div>\n"); th_free(tmps); } fprintf(outFile, "</body>\n" "</html>\n" ); } void outputMapHTML(FILE *outFile, const MapBlock *map, const MapLocations *locs) { int n, p = -1; bool span = false; for (int yc = 0; yc < map->height; yc++) { const unsigned char *dp = map->data + (map->scansize * yc); for (int xc = 0; xc < map->width; xc++) { if ((n = locFindByCoords(locs, xc, yc, true)) >= 0) { LocMarker *marker = locs->locations[n]; unsigned char chr; int color; if (span) fprintf(outFile, "</span>"); if (marker->uri != NULL) { // If URI/URL is set, parse piece color from it char *endptr = NULL; color = strtol(marker->uri, &endptr, 10); if (color < 0 || color >= nmapColors) color = 0; // If there is extra character after color number, // use it as the map patch if (endptr != NULL && *endptr != 0) chr = *endptr; else chr = *dp; } else { // If old format, use map piece color if (optUseOldFormat) color = muGetMapPieceColor(*dp, optUseOldFormat, true); else // Otherwise use "link" color color = col_light_red; chr = *dp; } if (marker->flags & LOCF_INVIS) { fprintf(outFile, "<span class=\"%c\">%c</span>", 'a' + color, chr); } else { fprintf(outFile, "<span class=\"%c\">" "<a onmouseover=\"sttq('%d_%d');\" " "onmouseout=\"qn();\" " "class=\"loc\" id=\"maploc%d_%d\" " "href=\"?%d_%d\">%c</a></span>", 'a' + color, marker->xc, marker->yc, marker->xc, marker->yc, marker->xc, marker->yc, chr); } span = false; p = -1; } else { if (p != *dp) { int color = muGetMapPieceColor(*dp, optUseOldFormat, true); if (span) fprintf(outFile, "</span>"); fprintf(outFile, "<span class=\"%c\">", 'a' + color); span = true; } fputc(*dp, outFile); p = *dp; } dp++; } if (span) fprintf(outFile, "</span>\n"); p = -1; span = false; } } int main(int argc, char *argv[]) { MapBlock *map = NULL; FILE *outFile = NULL, *inFile = NULL; LocFileInfo info; MapLocations locations; memset(&info, 0, sizeof(info)); memset(&locations, 0, sizeof(locations)); th_init("mkcitymap", "ASCII citymap converter", "0.5", NULL, NULL); th_verbosity = 0; // Parse arguments if (!th_args_process(argc, argv, optList, optListN, argHandleOpt, argHandleFile, OPTH_BAILOUT)) exit(1); if (optMapFilename == NULL || optLocFilename == NULL) { THERR("You need to specify at least map and loc filenames. (try --help)\n"); goto exit; } // Parse location file locSetFileInfo(&info, optLocFilename, NULL); if ((inFile = fopen(info.filename, "rb")) == NULL) { THERR("Could not open location file '%s' for reading.\n", info.filename); goto exit; } if (!locParseLocStream(inFile, &info, &locations, info.xoffs, info.yoffs)) goto exit; // Open mapfile if ((map = mapBlockParseFile(optMapFilename, false)) == NULL) { THERR("Error parsing/opening mapfile '%s'.\n", optMapFilename); goto exit; } if (optDestFilename == NULL) outFile = stdout; else if ((outFile = fopen(optDestFilename, "wb")) == NULL) { THERR("Error opening output file '%s'!\n", optDestFilename); goto exit; } // Output data outputHTMLHeader(outFile, &locations); outputMapHTML(outFile, map, &locations); outputHTMLFooter(outFile, &locations); exit: // Close input and output files if (outFile != NULL && outFile != stdout) fclose(outFile); if (inFile != NULL) fclose(inFile); mapBlockFree(map); locFreeMapLocations(&locations); locFreeFileInfo(&info); return 0; }