Mercurial > hg > batmud > maputils
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; }