view mkcitymap.c @ 1771:72adabd8e746

More cleanups.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 27 Oct 2017 05:10:08 +0300
parents cc59f80b0e78
children db7fdbc8f81b
line wrap: on
line source

/*
 * mkcitymap - Create interactive XHTML+CSS+JS map from input
 * Programmed by Matti 'ccr' Hämäläinen (Ggr Pupunen)
 * (C) Copyright 2006-2015 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,
        *optUrchinFile = NULL,
        *optMapTitle = NULL;

BOOL    optUseTable = 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 },
    { 4, 'T', "table",          "Use HTML table", OPT_NONE },
    { 5, 't', "title",          "Map title", OPT_ARGREQ },
    { 6, 'u', "urchin-file",    "Specify urchin file", OPT_ARGREQ },
};

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);
}


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

    case 1:
        th_verbosityLevel++;
        break;

    case 2:
        optDestFilename = optArg;
        THMSG(2, "Output file set to '%s'.\n", optDestFilename);
        break;

    case 4:
        optUseTable = TRUE;
        THMSG(2, "Using HTML table formatting.\n");
        break;

    case 5:
        optMapTitle = optArg;
        THMSG(2, "Map title string set to '%s'.\n", optMapTitle);
        break;

    case 6:
        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 (!optMapFilename)
        optMapFilename = currArg;
    else
    if (!optLocFilename)
        optLocFilename = currArg;
    else
    {
        THERR("Too many filenames specified!\n");
        return FALSE;
    }

    return TRUE;
}


void outputLocationBlockHTML(FILE *outFile, MapLocations *l, int nStart, int nEnd)
{
    if (nStart >= l->n || nStart < 0 || nStart > nEnd) return;
    if (nEnd >= l->n) nEnd = l->n-1;

    fprintf(outFile, "<td>\n");

    for (int n = nStart; n <= nEnd; n++)
    if (l->locations[n] != NULL)
    {
        LocMarker *tmp = l->locations[n];
        if (tmp->flags & LOCF_INVIS) continue;

        fprintf(outFile,
        "<a class=\"loc\" id=\"listloc%d_%d\" href=\"?%d_%d\" onmouseover=\"qh('%d_%d')",
        tmp->x, tmp->y,
        tmp->x, tmp->y,
        tmp->x, tmp->y);

        if (tmp->freeform)
        {
            fprintf(outFile, ";stt('%d_%d');", tmp->x, tmp->y);
        }

        fprintf(outFile, "\" "
        "onmouseout=\"qn('%d_%d');\">", tmp->x, tmp->y);

        if (tmp->names[0].name != NULL)
        {
            fprintf(outFile,
            " <span class=\"desc\">%s</span>", tmp->names[0].name);
        }

        fprintf(outFile, "</a><br />\n");
    }

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


void outputHTMLHeader(FILE *outFile, MapLocations *l)
{
    (void) l;

    muPrintHTMLhead(outFile, optMapTitle, TRUE);

    fprintf(outFile,
    " <script type=\"text/javascript\" src=\"tooltip.js\"></script>\n"
    " <style type=\"text/css\">\n"
    "  body { background: black; color: white; font-family: Arial, Verdana, sans-serif; %s }\n"
    "  a.loc { background: black; color: red; text-decoration: none; }\n"
    "  a.loc:hover { background: white; color: red; }\n"
    "  pre.map { font-size: 9pt; %s }\n",
    optUseTable ? "" : "height: 100%; padding: 0; margin: 0;",
    optUseTable ? "" : "overflow: auto;"
    );

    fprintf(outFile,
    "  .tooltip {\n"
    "    position: absolute;\n"
    "    display: none;\n"
    "    min-width: 200px; max-width: 500px;\n"
    "    background: white; color: black;\n"
    "    padding: 5px;\n"
    "    border: 2px solid gray;\n"
    "    -moz-border-radius: 5px;\n"
    "    border-radius: 5px;\n"
    "    font-size: 8pt;\n"
    "    z-index: 15;\n"
    "  }\n"
    "  .holder { display: block; color: #000; }\n"
    "  * html .tooltip { border-right: 400px solid #fff; }\n"
    "  * html .holder { display: inline-block; position: relative; margin-right: -400px; }\n"
    "  div.loctab table tr { vertical-align: top; }\n"
    );

    if (!optUseTable)
    {
    fprintf(outFile,
    "  div.map {\n"
    "    font-weight: bold;\n"
    "    position: absolute;\n"
    "    left: 0;\n"
    "    right: 0;\n"
    "    top: 0;\n"
    "    margin: 0;\n"
    "    padding: 0;\n"
    "    height: 75%%;\n"
    "    overflow: auto;\n"
    "    text-align: center;\n"
    "  }\n"
    "  div.loctab {\n"
    "    background: black;\n"
    "    color: white;\n"
    "    position: absolute;\n"
    "    right: 0;\n"
    "    left: 0;\n"
    "    height: 20%%;\n"
    "    bottom: 0;\n"
    "    padding: 4px;\n"
    "    z-index: 10;\n"
    "    border-top: 4px solid gray;\n"
    "    font-size: 10pt;\n"
    "    overflow: auto;\n"
    "  }\n"
    "  div.loctab table { width: 95%%; }\n"
    );
    }

    muPrintHTMLcolors(outFile, "span", NULL, NULL);

    fprintf(outFile,
    " </style>\n"
    );

    if (optUrchinFile && muCopyFileToStream(outFile, optUrchinFile) < 0)
    {
        THERR("Error copying urchin file '%s' to output.\n", optUrchinFile);
    }

    fprintf(outFile,
    "</head>\n"
    "<body onload=\"httOnLoad();\">\n"
    );

    if (optUseTable && optMapTitle)
    {
      fprintf(outFile, "<h3>");
      fputse(optMapTitle, outFile);
      fprintf(outFile, "</h3>\n");
    }

    if (optUseTable)
    {
    fprintf(outFile,
    "<table>\n"
    " <tr>\n"
    "  <td>\n"
    );
    }

    fprintf(outFile,
    "<div class=\"map\">"
    "<pre class=\"map\">"
    );
}


void outputHTMLFooter(FILE *outFile, MapLocations *l)
{
    int n, k, num;

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

    if (optUseTable)
    {
    fprintf(outFile,
    "  </td>\n"
    "  <td>\n"
    );
    }

    fprintf(outFile,
    "<div class=\"loctab\">\n"
    "<table><tr>\n"
    );

    for (num = n = 0; n < l->n; n++)
    if (l->locations[n] != NULL)
    {
        LocMarker *tmp = l->locations[n];
        if ((tmp->flags & LOCF_INVIS) == 0)
            num++;
    }

    k = (num / (optUseTable ? 2 : 4)) + 1;
    for (n = 0; n < l->n; n += k)
      outputLocationBlockHTML(outFile, l, n, n + k - 1);

    fprintf(outFile,
    "</tr></table>\n"
    "</div>\n"
    );

    if (optUseTable)
    {
    fprintf(outFile,
    "  </td>\n"
    " </tr>\n"
    "</table>\n"
    );
    }

    for (n = 0; n < l->n; n++)
    if (l->locations[n] != NULL)
    {
        LocMarker *tmp = l->locations[n];
        if (tmp->flags & LOCF_INVIS) continue;

        fprintf(outFile,
        "<div class=\"tooltip\" name=\"tooltip\" id=\"tt%d_%d\">"
        "<div class=\"holder\">",
        tmp->x, tmp->y);

        if (tmp->names[0].name != NULL)
        {
            fprintf(outFile, "<b>%s</b>", tmp->names[0].name);
        }

        if (tmp->freeform)
        {
            fprintf(outFile, "<br />%s", tmp->freeform);
        }

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

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


void outputMapHTML(FILE *outFile, MapBlock *map, MapLocations *l)
{
    int x, y, n, p = -1, c;
    BOOL span = FALSE;

    for (y = 0; y < map->height; y++)
    {
        unsigned char *d = map->data + (map->scansize * y);
        for (x = 0; x < map->width; x++)
        {
            if ((n = locFindByCoords(l, x, y, TRUE)) >= 0)
            {
                LocMarker *tmp = l->locations[n];
                if (span) fprintf(outFile, "</span>");

                if (tmp->uri)
                {
                    c = atoi(tmp->uri);
                    if (c < 0 || c >= nmapColors) c = 0;

                    fprintf(outFile,
                    "<span class=\"%c\">%c</span>",
                    'a' + c, *d);
                }
                else
                {
                    fprintf(outFile,
                    "<span class=\"%c\">"
                    "<a name=\"locpt\" onmouseover=\"stt('%d_%d');\" "
                    "onmouseout=\"htt();\" "
                    "class=\"loc\" id=\"maploc%d_%d\" "
                    "href=\"?%d_%d\">%c</span></a></span>",
                    'a' + col_light_red,
                    tmp->x, tmp->y,
                    tmp->x, tmp->y,
                    tmp->x, tmp->y, *d);
                }
                span = FALSE;
                p = -1;
            }
            else
            {
                if (p != *d)
                {
                    if (span) fprintf(outFile, "</span>");
                    c = muGetMapPieceColor(*d, FALSE, TRUE);
                    fprintf(outFile, "<span class=\"%c\">", 'a' + c);
                    span = TRUE;
                }
                fputc(*d, outFile);
                p = *d;
            }
            d++;
        }
        if (span) fprintf(outFile, "</span>\n");
        p = -1;
        span = FALSE;
    }
}


int main(int argc, char *argv[])
{
    MapBlock *map = NULL;
    FILE *outFile, *inFile;
    LocFileInfo locFile;
    MapLocations locations;

    memset(&locations, 0, sizeof(locations));

    th_init("mkcitymap", "ASCII citymap converter", "0.5", NULL, NULL);
    th_verbosityLevel = 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");
        exit(0);
    }

    // Parse location file
    setLocFileInfo(&locFile, optLocFilename, NULL);
    if ((inFile = fopen(locFile.filename, "rb")) == NULL)
    {
        THERR("Could not open location file '%s' for reading.\n",
            locFile.filename);
        exit(3);
    }

    if (!locParseLocStream(inFile, &locFile, &locations, locFile.x, locFile.y))
        exit(1);

    fclose(inFile);

    // Open mapfile
    map = mapBlockParseFile(optMapFilename, FALSE);
    if (map == NULL)
    {
        THERR("Error parsing/opening mapfile '%s'.\n",
            optMapFilename);
        exit(3);
    }

    if (optDestFilename == NULL)
        outFile = stdout;
    else
    if ((outFile = fopen(optDestFilename, "wb")) == NULL)
    {
        THERR("Error opening output file '%s'!\n",
            optDestFilename);
        exit(1);
    }

    // Output data
    outputHTMLHeader(outFile, &locations);
    outputMapHTML(outFile, map, &locations);
    outputHTMLFooter(outFile, &locations);

    // Close input and output files
    fclose(outFile);

    mapBlockFree(map);
    locFreeMapLocations(&locations);

    exit(0);
    return 0;
}