Mercurial > hg > batmud > maputils
view combine.c @ 1822:892d5277f1ff
Remove note about the search pattern parser being not very tolerant, it's somewhat better now.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Mon, 30 Oct 2017 12:55:21 +0200 |
parents | cc59f80b0e78 |
children | 79dd960610cb |
line wrap: on
line source
/* * combine - Combine several maps into one bigger * * Programmed by Matti 'ccr' Hämäläinen (Ggr Pupunen) * (C) Copyright 2006-2015 Tecnic Software productions (TNSP) */ #include "libmaputils.h" #include "th_args.h" #include "th_string.h" #define MAX_FILES (256) typedef struct { char *fileName; int x, y; MapBlock *b; } mapfile_t; /* Variables */ int nsrcFiles = 0; mapfile_t srcFiles[MAX_FILES]; char *destFile = 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, 'o', "output", "Specify output file", OPT_ARGREQ }, { 2, 'v', "verbose", "Be more verbose", OPT_NONE }, { 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); } BOOL argHandleOpt(const int optN, char *optArg, char *currArg) { switch (optN) { case 0: argShowHelp(); exit(0); break; case 1: destFile = optArg; THMSG(1, "Output file '%s'\n", destFile); break; case 2: th_verbosityLevel++; break; case 3: th_verbosityLevel = -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) { if (nsrcFiles < MAX_FILES) { char *q, *c, *tmpArg = th_strdup(currArg); if (!tmpArg) { THERR("Could not allocate temp buffer!\n"); return FALSE; } // Get filename component for (q = c = tmpArg; *c && (*c != ':'); c++); if (*c != ':') { th_free(tmpArg); THERR("Expected ':' after filename in '%s'.\n", currArg); return FALSE; } *(c++) = 0; srcFiles[nsrcFiles].fileName = th_strdup(q); // Get X offset if (!th_isdigit(*c) && *c != '-') { th_free(tmpArg); THERR("Expected decimal X offset value in '%s'.\n", currArg); return FALSE; } q = c; if (*c == '-') c++; while (*c && th_isdigit(*c)) c++; if (*c != ':') { th_free(tmpArg); THERR("Expected ':' after X offset value in '%s'.\n", currArg); return FALSE; } *(c++) = 0; srcFiles[nsrcFiles].x = atoi(q); // Get Y offset if (!th_isdigit(*c) && *c != '-') { th_free(tmpArg); THERR("Expected decimal Y offset value in '%s'.\n", currArg); return FALSE; } q = c; if (*c == '-') c++; while (*c && th_isdigit(*c)) c++; if (*c != 0) { th_free(tmpArg); THERR("Invalid Y offset value in '%s'.\n", currArg); return FALSE; } srcFiles[nsrcFiles].y = atoi(q); th_free(tmpArg); nsrcFiles++; } else { THERR("Too many input files specified (%d max)!\n", MAX_FILES); return FALSE; } return TRUE; } void findWorldSize(MapBlock *tmp, int *worldX0, int *worldY0, int *worldX1, int *worldY1) { if (tmp->x < *worldX0) *worldX0 = tmp->x; if (tmp->x + tmp->width > *worldX1) *worldX1 = tmp->x + tmp->width; if (tmp->y < *worldY0) *worldY0 = tmp->y; if (tmp->y + tmp->height > *worldY1) *worldY1 = tmp->y + tmp->height; } int main(int argc, char *argv[]) { int i, worldX0, worldY0, worldX1, worldY1, worldW, worldH; MapBlock *worldMap = NULL; th_init("combine", "Combine several maps into one", "0.1", NULL, NULL); th_verbosityLevel = 0; // Parse arguments if (!th_args_process(argc, argv, optList, optListN, argHandleOpt, argHandleFile, OPTH_BAILOUT)) exit(1); if (nsrcFiles < 1) { THERR("Nothing to do. (try --help)\n"); exit(0); } /* Read map files */ for (i = 0; i < nsrcFiles; i++) { if ((srcFiles[i].b = mapBlockParseFile(srcFiles[i].fileName, optInputIsDiff)) == NULL) { THERR("Error reading input file '%s'!\n", srcFiles[i]); exit(1); } mapBlockClean(srcFiles[i].b, " "); srcFiles[i].b->x = srcFiles[i].x; srcFiles[i].b->y = srcFiles[i].y; } THMSG(2, "Total of %d maps read.\n", nsrcFiles); if (nsrcFiles <= 0) { THERR("No maps, nothing to do.\n"); exit(11); } // Get world dimensions worldX0 = worldY0 = worldX1 = worldY1 = 0; for (i = 0; i < nsrcFiles; i++) findWorldSize(srcFiles[i].b, &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"); exit(4); } /* Clear with some character, if requested */ if (optFillChar > 0) { memset(worldMap->data, optFillChar, worldMap->size); } /* Blit map blocks */ for (i = 0; i < nsrcFiles; i++) if (mapBlockPutDo(worldMap, srcFiles[i].b, worldX0 + srcFiles[i].b->x, worldY0 + srcFiles[i].b->y) != 0) { THERR("Mapblock #%d ['%s' @ %d,%d] placement failed!\n", i, srcFiles[i].fileName, srcFiles[i].b->x, srcFiles[i].b->y); exit(8); } /* Output generated map */ if (worldMap) { FILE *tmpFile; THMSG(1, "Outputting generated map of (%d x %d) ...\n", worldMap->width, worldMap->height); if (destFile == NULL) tmpFile = stdout; else if ((tmpFile = fopen(destFile, "wb")) == NULL) { THERR("Error opening output file '%s'!\n", destFile); exit(1); } if (optInputIsDiff) mapBlockPrintRaw(tmpFile, worldMap); else mapBlockPrint(tmpFile, worldMap); fclose(tmpFile); } else { THERR("No map generated?\n"); } exit(0); return 0; }