Mercurial > hg > batmud > maputils
view src/combine.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
/* * Combine several maps into one bigger * Programmed by Matti 'ccr' Hämäläinen <ccr@tnsp.org> * (C) Copyright 2006-2022 Tecnic Software productions (TNSP) */ #include "libmaputils.h" #include "th_args.h" #include "th_string.h" #define SET_MAX_FILES (256) typedef struct { char *filename; int xc, yc; MapBlock *blk; } MapFile; /* Variables */ int nsrcFiles = 0; MapFile srcFiles[SET_MAX_FILES]; char *dstFile = NULL; int optFillChar = -1; bool optInputIsDiff = false; int optWorldMinW = 0, optWorldMinH = 0; /* Arguments */ static const th_optarg optList[] = { { 0, '?', "help", "Show this help", OPT_NONE }, { 1, 'v', "verbose", "Be more verbose", OPT_NONE }, { 2, 'o', "output", "Specify output file", OPT_ARGREQ }, { 3, 'q', "quiet", "Be quiet", OPT_NONE }, { 5, 'd', "diff", "Map files are in 'diff' format", OPT_NONE }, { 4, 'f', "fill", "Fill character", OPT_ARGREQ }, { 6, 'w', "width", "Minimum width", OPT_ARGREQ }, { 7, 'h', "height", "Minimum height", OPT_ARGREQ }, }; const int optListN = sizeof(optList) / sizeof(optList[0]); void argShowHelp() { th_print_banner(stdout, th_prog_name, "[options] <mapfile1:x-offset:y-offset> [<mapfile2:x:y> ...]"); 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: dstFile = optArg; THMSG(1, "Output file '%s'\n", dstFile); break; case 3: th_verbosity = -1; break; case 4: if (optArg[1] != 0) { THERR("Fill character is not a string, dumbass!\n"); return false; } optFillChar = optArg[0]; THMSG(1, "Using fill character '%c'\n", optFillChar); break; case 5: THMSG(1, "Assuming all input files are diffs.\n"); optInputIsDiff = true; break; case 6: case 7: { char *s; int v = atoi(optArg); if (optN == 6) { s = "width"; optWorldMinW = v; } else { s = "height"; optWorldMinH = v; } if (v < 0 || v > 128 * 1024) { THERR("Invalid %s setting %d!\n", s, v); return false; } THMSG(1, "Using world minimum %s %d\n", s, v); } break; default: THERR("Unknown option '%s'.\n", currArg); return false; break; } return true; } bool argHandleFile(char *currArg) { char *tmpArg = NULL; if (nsrcFiles < SET_MAX_FILES) { char *q, *c; MapFile *file = &srcFiles[nsrcFiles++]; if ((tmpArg = th_strdup(currArg)) == NULL) { THERR("Could not allocate temp buffer!\n"); goto err; } // Get filename component for (q = c = tmpArg; *c && (*c != ':'); c++); if (*c != ':') { THERR("Expected ':' after filename in '%s'.\n", currArg); goto err; } *(c++) = 0; file->filename = th_strdup(q); // Get X offset if (!th_isdigit(*c) && *c != '-') { THERR("Expected decimal X offset value in '%s'.\n", currArg); goto err; } q = c; if (*c == '-') c++; while (*c && th_isdigit(*c)) c++; if (*c != ':') { THERR("Expected ':' after X offset value in '%s'.\n", currArg); goto err; } *(c++) = 0; file->xc = atoi(q); // Get Y offset if (!th_isdigit(*c) && *c != '-') { THERR("Expected decimal Y offset value in '%s'.\n", currArg); goto err; } q = c; if (*c == '-') c++; while (*c && th_isdigit(*c)) c++; if (*c != 0) { THERR("Invalid Y offset value in '%s'.\n", currArg); goto err; } file->yc = atoi(q); } else { THERR("Too many input files specified (%d max)!\n", SET_MAX_FILES); goto err; } th_free(tmpArg); return true; err: th_free(tmpArg); return false; } void findWorldSize(MapBlock *tmp, int *worldX0, int *worldY0, int *worldX1, int *worldY1) { if (tmp->xc < *worldX0) *worldX0 = tmp->xc; if (tmp->xc + tmp->width > *worldX1) *worldX1 = tmp->xc + tmp->width; if (tmp->yc < *worldY0) *worldY0 = tmp->yc; if (tmp->yc + tmp->height > *worldY1) *worldY1 = tmp->yc + tmp->height; } int main(int argc, char *argv[]) { int i, worldX0, worldY0, worldX1, worldY1, worldW, worldH; MapBlock *worldMap = NULL; FILE *tmpFile = NULL; th_init("combine", "Combine several maps into one", "0.1", NULL, NULL); th_verbosity = 0; // Parse arguments if (!th_args_process(argc, argv, optList, optListN, argHandleOpt, argHandleFile, OPTH_BAILOUT)) goto exit; if (nsrcFiles < 1) { THERR("Nothing to do. (try --help)\n"); goto exit; } /* Read map files */ for (i = 0; i < nsrcFiles; i++) { MapFile *file = &srcFiles[i]; if ((file->blk = mapBlockParseFile(file->filename, optInputIsDiff)) == NULL) { THERR("Error reading input file '%s'!\n", file->filename); goto exit; } mapBlockClean(file->blk, (unsigned char *) " ", 1); file->blk->xc = file->xc; file->blk->yc = file->yc; } THMSG(2, "Total of %d maps read.\n", nsrcFiles); if (nsrcFiles <= 0) { THERR("No maps, nothing to do.\n"); goto exit; } // Get world dimensions worldX0 = worldY0 = worldX1 = worldY1 = 0; for (i = 0; i < nsrcFiles; i++) findWorldSize(srcFiles[i].blk, &worldX0, &worldY0, &worldX1, &worldY1); worldW = worldX1 - worldX0 + 1; worldH = worldY1 - worldY0 + 1; THMSG(2, "Initial dimensions <%d, %d> - <%d, %d> (%d x %d)\n", worldX0, worldY0, worldX1, worldY1, worldW, worldH); // Adjust to origo <0, 0> worldX0 = -worldX0; worldY0 = -worldY0; worldX1 = worldX0 + worldW - 1; worldY1 = worldY0 + worldH - 1; THMSG(2, "Adjusted dimensions <%d, %d> - <%d, %d> (%d x %d)\n", worldX0, worldY0, worldX1, worldY1, worldW, worldH); // Adjust for requested minimum dimensions if (worldW < optWorldMinW) { int tmp = (optWorldMinW - worldW) / 2; worldX0 += tmp; worldX1 += tmp; worldW = optWorldMinW; } if (worldH < optWorldMinH) { int tmp = (optWorldMinH - worldH) / 2; worldY0 += tmp; worldY1 += tmp; worldH = optWorldMinH; } // Allocate world map THMSG(1, "Initializing world map of <%d, %d> - <%d, %d> (%d x %d)\n", worldX0, worldY0, worldX1, worldY1, worldW, worldH); if ((worldMap = mapBlockAlloc(worldW, worldH)) == NULL) { THERR("Error allocating world map!\n"); goto exit; } /* Clear with some character, if requested */ if (optFillChar > 0) { memset(worldMap->data, optFillChar, worldMap->size); } /* Blit map blocks */ for (i = 0; i < nsrcFiles; i++) { MapFile *file = &srcFiles[i]; if (mapBlockPutDo(worldMap, file->blk, worldX0 + file->blk->xc, worldY0 + file->blk->yc) != 0) { THERR("Mapblock #%d ['%s' @ %d,%d] placement failed!\n", i, file->filename, file->blk->xc, file->blk->yc); goto exit; } } /* Output generated map */ if (worldMap != NULL) { THMSG(1, "Outputting generated map of (%d x %d) ...\n", worldMap->width, worldMap->height); if (dstFile == NULL) tmpFile = stdout; else if ((tmpFile = fopen(dstFile, "wb")) == NULL) { THERR("Error opening output file '%s'!\n", dstFile); goto exit; } if (optInputIsDiff) mapBlockPrintRaw(tmpFile, worldMap); else mapBlockPrint(tmpFile, worldMap); } else { THERR("No map generated?\n"); } exit: mapBlockFree(worldMap); if (tmpFile != NULL) fclose(tmpFile); return 0; }