view mapstats.c @ 1775:db7fdbc8f81b

Modernification cleanups.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 28 Oct 2017 04:07:28 +0300
parents cc59f80b0e78
children 79dd960610cb
line wrap: on
line source

/*
 * Calculate terrain type (and other) statistics from ASCII map
 * Programmed by Matti 'ccr' Hämäläinen (Ggr Pupunen)
 * (C) Copyright 2007 Tecnic Software productions (TNSP)
 */
#include "libmaputils.h"
#include "th_args.h"
#include "th_string.h"


typedef int (*compareFunc)(const void *, const void *);


enum
{
    SORT_NONE,
    SORT_NAME,
    SORT_SYMBOL,
    SORT_AMOUNT
};

#define SET_MAX_FILES (512)

char    *srcFilenames[SET_MAX_FILES],
        *destFilename = NULL;
int     nsrcFilenames = 0;

BOOL    optUseOldFormat = FALSE,
        optInputIsDiff = FALSE,
        optSortBy = SORT_NONE,
        optCityFormat = FALSE;


typedef struct
{
    ulint_t n;
    int piece;
} MapPieceStat;


/* 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, 'd', "input-diff", "Input is a diff", OPT_NONE },
    { 4, 'O', "old-format", "Input using old symbols/colors", OPT_NONE },
    { 5, 'o', "output",     "Output filename", OPT_ARGREQ },
    { 6, 's', "sort",       "Sort (by name,symbol,amount)", OPT_ARGREQ },
    { 7, 'c', "city-format","Input is a city map", OPT_NONE },
};

static const int optListN = sizeof(optList) / sizeof(optList[0]);


void argShowHelp(void)
{
    th_print_banner(stdout, th_prog_name,
        "[options] <input mapfile>");

    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:
        th_verbosityLevel = -1;
        break;

    case 3:
        optInputIsDiff = TRUE;
        THMSG(2, "Input is a 'diff', handling it as such.\n");
        break;

    case 4:
        optUseOldFormat = TRUE;
        THMSG(2, "Input is using old map symbols/colors.\n");
        break;

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

    case 6:
        if (!strncmp(optArg, "n", 1))
            optSortBy = SORT_NAME;
        else
        if (!strncmp(optArg, "s", 1))
            optSortBy = SORT_SYMBOL;
        else
        if (!strncmp(optArg, "a", 1))
            optSortBy = SORT_AMOUNT;
        else
        {
            THERR("Unknown sorting method name '%s'!\n", optArg);
            exit(1);
        }

        THMSG(2, "Sorting via method %d\n", optSortBy);
        break;

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

    default:
        THERR("Unknown option '%s'.\n", currArg);
        return FALSE;
    }

    return TRUE;
}


BOOL argHandleFile(char *currArg)
{
    if (nsrcFilenames < SET_MAX_FILES)
    {
        srcFilenames[nsrcFilenames++] = currArg;
    }
    else
    {
        THERR("Too many input map files specified!\n");
        return FALSE;
    }

    return TRUE;
}


int compareByName(const void *p1, const void *p2)
{
    MapPieceStat *vp1 = (MapPieceStat *) p1, *vp2 = (MapPieceStat *) p2;
    const MapPiece *mp1 = &mapPieces[vp1->piece], *mp2 = &mapPieces[vp2->piece];

    if (optUseOldFormat)
    {
        if (mp1->oldSymbol >= 0 && mp2->oldSymbol >= 0)
            return strcmp(mp1->desc, mp2->desc);
        else
            return -1;
    }
    else
    {
        if (mp1->symbol >= 0 && mp2->symbol >= 0)
            return strcmp(mp1->desc, mp2->desc);
        else
            return -1;
    }
}


int compareBySymbol(const void *p1, const void *p2)
{
    MapPieceStat *vp1 = (MapPieceStat *) p1, *vp2 = (MapPieceStat *) p2;
    const MapPiece *mp1 = &mapPieces[vp1->piece], *mp2 = &mapPieces[vp2->piece];

    if (optUseOldFormat)
    {
        if (mp1->oldSymbol >= 0 && mp2->oldSymbol >= 0)
            return mp1->oldSymbol - mp2->oldSymbol;
        else
            return -1;
    }
    else
    {
        if (mp1->symbol >= 0 && mp2->symbol >= 0)
            return mp1->symbol - mp2->symbol;
        else
            return -1;
    }
}


int compareByAmount(const void *p1, const void *p2)
{
    MapPieceStat *vp1 = (MapPieceStat *) p1, *vp2 = (MapPieceStat *) p2;

    if (vp1->n < vp2->n)
        return -1;
    else
    if (vp1->n > vp2->n)
        return 1;
    else
        return 0;
}


/* Main program
 */
int main(int argc, char *argv[])
{
    FILE *outFile;
    MapBlock *map;
    unsigned char *d;
    ulint_t statTotal, statUnknown;
    MapPieceStat statPieces[nmapPieces];

    // Initialize
    th_init("mapstats", "ASCII map statistics generator", "0.2", NULL, NULL);
    th_verbosityLevel = 1;

    // Parse arguments
    if (!th_args_process(argc, argv, optList, optListN,
        argHandleOpt, argHandleFile, OPTH_BAILOUT))
        exit(1);

    if (nsrcFilenames == 0)
    {
        THERR("Nothing to do. (try --help)\n");
        exit(0);
    }

    // Read input file
    statTotal = statUnknown = 0;
    for (int i = 0; i < nmapPieces; i++)
    {
        statPieces[i].n = 0;
        statPieces[i].piece = i;
    }

    for (int n = 0; n < nsrcFilenames; n++)
    {
        THMSG(1, "Reading map file '%s'\n", srcFilenames[n]);

        if ((map = mapBlockParseFile(srcFilenames[n], optInputIsDiff)) == NULL)
        {
            THERR("Error reading map file '%s'!\n",
                srcFilenames[n]);
            exit(1);
        }

        THMSG(1, "Analyzing %dx%d area ...\n",
            map->width, map->height);

        for (int y = 0; y < map->height; y++)
        {
            d = map->data + (map->scansize * y);
            for (int x = 0; x < map->width; x++)
            {
                int c;
                if ((c = muGetMapPieceIndex(*d, optUseOldFormat, optCityFormat)) >= 0)
                    statPieces[c].n++;
                else
                    statUnknown++;

                statTotal++;
                d++;
            }
        }
        mapBlockFree(map);
    }

    // Sort results, if needed
    if (optSortBy != SORT_NONE)
    {
        compareFunc tmpFunc;

        THMSG(2, "Sorting results ...\n");
        switch (optSortBy)
        {
        case SORT_NAME: tmpFunc = compareByName; break;
        case SORT_SYMBOL: tmpFunc = compareBySymbol; break;
        case SORT_AMOUNT: tmpFunc = compareByAmount; break;
        default:
            THERR("Internal error, no sort function for sort type %d.\n", optSortBy);
            exit(2);
            break;
        }
        qsort(statPieces, nmapPieces, sizeof(MapPieceStat), tmpFunc);
    }

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

    for (int i = 0; i < nmapPieces; i++)
    {
        const int p = statPieces[i].piece;
        int symbol = optUseOldFormat ? mapPieces[p].oldSymbol : mapPieces[p].symbol;
        if (symbol >= 0)
        {
            fprintf(outFile, "%-20s [%c]: %6ld (%1.3f%%)\n",
            mapPieces[p].desc,
            symbol,
            statPieces[i].n,
            (statPieces[i].n * 100.0f / statTotal));
        }
    }

    fprintf(outFile, " %ld total, %ld unknown.\n",
        statTotal, statUnknown);

    fclose(outFile);

    THMSG(1, "Done.\n");

    exit(0);
    return 0;
}