Mercurial > hg > batmud > maputils
view src/mapstats.c @ 2833:d0e186348cb2 default tip
Add mention of soft level limitation to 'Eightleg woods'.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sun, 26 May 2024 20:33:53 +0300 |
parents | 441ad33367a7 |
children |
line wrap: on
line source
/* * Calculate terrain type (and other) statistics from ASCII map * Programmed by Matti 'ccr' Hämäläinen <ccr@tnsp.org> * (C) Copyright 2007-2022 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], *dstFilename = NULL; int nsrcFilenames = 0, optSortBy = SORT_NONE; bool optUseOldFormat = false, optInputIsDiff = false, optCityFormat = false; typedef struct { unsigned int 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, 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: th_verbosity = -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: dstFilename = optArg; THMSG(2, "Output file set to '%s'.\n", dstFilename); 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); return false; } 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[]) { int res = 0; FILE *outFile = NULL; MapBlock *map = NULL; unsigned int statTotal, statUnknown; MapPieceStat statPieces[nmapPieces]; // Initialize th_init("mapstats", "ASCII map statistics generator", "0.2", NULL, NULL); th_verbosity = 1; // Parse arguments if (!th_args_process(argc, argv, optList, optListN, argHandleOpt, argHandleFile, OPTH_BAILOUT)) { res = 1; goto out; } if (nsrcFilenames == 0) { argShowHelp(); THERR("Nothing to do.\n"); res = 1; goto out; } // 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]); res = -9; goto out; } THMSG(1, "Analyzing %dx%d area ...\n", map->width, map->height); for (int y = 0; y < map->height; y++) { unsigned char *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); res = -7; goto out; } qsort(statPieces, nmapPieces, sizeof(MapPieceStat), tmpFunc); } // Open output file if (dstFilename == NULL) outFile = stdout; else if ((outFile = fopen(dstFilename, "wb")) == NULL) { THERR("Error opening output file '%s'!\n", dstFilename); res = -8; goto out; } 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]: %6d (%1.3f%%)\n", mapPieces[p].desc, symbol, statPieces[i].n, (statPieces[i].n * 100.0f / statTotal)); } } fprintf(outFile, " %d total, %d unknown.\n", statTotal, statUnknown); out: if (outFile != NULL) fclose(outFile); return res; }