Mercurial > hg > batmud > maputils
view libmaputils.c @ 1770:cc59f80b0e78
Various cleanups.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Fri, 27 Oct 2017 04:35:51 +0300 |
parents | 5dda4803c59b |
children | f4c49fd6557e |
line wrap: on
line source
/* * maputils - Generic functions/tables for maputils package * Programmed by Matti 'ccr' Hämäläinen (Ggr Pupunen) * (C) Copyright 2006-2017 Tecnic Software productions (TNSP) */ #include "libmaputils.h" const MapColor mapColors[] = { { 0x00, 0x00, 0x00, 30, ANSI_OFF, FALSE }, // col_black { 0x00, 0x00, 0xaa, 34, ANSI_OFF, FALSE }, // col_blue { 0xaa, 0x00, 0x00, 31, ANSI_OFF, FALSE }, // col_red { 0xaa, 0xaa, 0x00, 33, ANSI_OFF, FALSE }, // col_yellow { 0x00, 0xaa, 0x00, 32, ANSI_OFF, FALSE }, // col_green { 0xaa, 0xaa, 0xaa, 37, ANSI_OFF, FALSE }, // col_white { 0x00, 0xff, 0xff, 36, ANSI_OFF, FALSE }, // col_cyan { 0x77, 0x00, 0x77, 35, ANSI_OFF, FALSE }, // col_magenta { 0x77, 0x77, 0x77, 30, ANSI_BOLD, FALSE }, // col_light_black { 0x00, 0x00, 0xff, 34, ANSI_BOLD, FALSE }, // col_light_blue { 0xee, 0x00, 0x00, 31, ANSI_BOLD, FALSE }, // col_light_red { 0xff, 0xff, 0x00, 33, ANSI_BOLD, FALSE }, // col_light_yellow { 0x00, 0xff, 0x00, 32, ANSI_BOLD, FALSE }, // col_light_green { 0xff, 0xff, 0xff, 37, ANSI_BOLD, FALSE }, // col_light_white { 0x00, 0xff, 0xff, 36, ANSI_BOLD, FALSE }, // col_light_cyan { 0xff, 0x00, 0xff, 35, ANSI_BOLD, FALSE }, // col_light_magenta // extra colors, not in ANSI { 0xff, 0x00, 0x00, 31, ANSI_BOLD, TRUE }, // col_very_light_red }; const int nmapColors = sizeof(mapColors) / sizeof(mapColors[0]); const MapPiece mapPieces[] = { { '!', "Mountain Peak", 0xcc,0xff,0xff, col_white, -1, }, { '#', "Ruins", 0x88,0x88,0x88, col_light_black, -1, }, { '%', "Special Location", 0xff,0xff,0xff, col_light_white, -1, }, { '+', "Crossing", 0x33,0x33,0x33, col_light_black, -1, }, { '-', "Road", 0x33,0x33,0x33, col_light_black, -1, }, { '|', "Road", 0x33,0x33,0x33, col_light_black, -1, }, { '/', "Road", 0x33,0x33,0x33, col_light_black, -1, }, { '\\',"Road", 0x33,0x33,0x33, col_light_black, -1, }, { '.', "Plains", 0x55,0x92,0x00, col_green, -1, }, { '=', "Bridge", 0x33,0x33,0x33, col_light_black, -1, }, { '?', "Scenic Location", 0xff,0xff,0xff, col_light_white, -1, }, { '@', "Flowing Lava", 0xff,0x99,0x3f, col_very_light_red, -1, }, { 'C', "Player City", 0x88,0x88,0x88, col_light_black, -1, }, { 'F', "Deep Forest", 0x00,0x88,0x00, col_green, -1, }, { 'H', "Highlands", 0x66,0x3f,0x00, col_magenta, -1, }, { 'L', "Lava Lake", 0xff,0x50,0x00, col_very_light_red, -1, }, { 'R', "Deep River", 0x33,0x66,0xff, col_blue, -1, }, { 'V', "Volcano", 0xff,0x33,0x00, col_red, -1, }, { '^', "Mountain", 0x71,0x82,0x92, col_light_magenta, -1, }, { 'b', "Beach", 0xcf,0xc4,0xa5, col_yellow, -1, }, { 'c', "City", 0x88,0x88,0x88, col_light_black, -1, }, { 'd', "Desert", 0xee,0xaa,0x22, col_yellow, -1, }, { 'f', "Forest", 0x00,0xb6,0x00, col_light_green, -1, }, { 'h', "Hills", 0x99,0x66,0x00, col_magenta, -1, }, { 'i', "Ice", 0xee,0xee,0xff, col_light_blue, -1, }, { 'j', "Jungle", 0x13,0x96,0x36, col_green, -1, }, { 'l', "Lake", 0x21,0x33,0xcc, col_light_blue, -1, }, { 'r', "River", 0x66,0x99,0xff, col_light_blue, -1, }, { 's', "Swamp", 0x9d,0xa8,0x0a, col_light_red, -1, }, { 't', "Tundra", 0x61,0xc3,0xa2, col_white, -1, }, { 'v', "Valley", 0x22,0xdd,0x22, col_light_green, -1, }, { 'w', "Waterfall", 0x77,0xaa,0xff, col_light_cyan, -1, }, { 'x', "Badlands", 0x8a,0x83,0x60, col_light_red, -1, }, { 'y', "Fields", 0xa7,0xcc,0x14, col_yellow, -1, }, { 'z', "Shore", 0xa7,0xcc,0x14, col_light_yellow, -1, }, { ',', "Muddy Trail", 0x8c,0x57,0x38, col_light_yellow, -1, }, { '&', "Monster", 0xff,0x00,0x00, col_light_red, -1, }, #ifndef SECRET_MAP_DATA_FORMAT { 'S', "Shallows", 0x44,0xcc,0xcc, col_light_cyan, -1, }, { '~', "Sea", 0x11,0x88,0xdd, col_blue, -1, }, #else { '~', "Sea 1", 0x00,0x11,0x88, col_blue, -1, }, { '"', "Sea 2", 0x11,0x22,0x99, col_blue, -1, }, { '\'',"Sea 3", 0x11,0x33,0xaa, col_blue, -1, }, { '`', "Shallows?", 0x11,0x66,0xdd, col_blue, -1, }, { 'p', "Plains?", 0x55,0x92,0x00, col_green, -1, }, { 'S', "Swamp?", 0x77,0x77,0x33, col_yellow, -1, }, #endif { -1 , "Road", 0x33,0x33,0x33, col_light_black, '.', }, { -1 , "Plains", 0x00,0xff,0x00, col_light_green, 'p', }, { -1 , "Highlands", 0x77,0x00,0x77, col_magenta, 'i', }, }; const int nmapPieces = sizeof(mapPieces) / sizeof(mapPieces[0]); const MapPiece mapCityPieces[] = { { '.', "Street", 0xaa,0xaa,0x00, col_yellow, -1, }, { '-', "Door/Gate", 0xff,0xff,0x00, col_light_yellow, -1, }, { '|', "Door/Gate", 0xff,0xff,0x00, col_light_yellow, -1, }, { '=', "Bridge/Gate", 0x33,0x33,0x33, col_yellow, -1, }, { '*', "Fountain", 0x00,0x00,0xff, col_light_blue, -1, }, { '@', "???", 0xff,0xff,0x00, col_light_yellow, -1, }, { '$', "Tree", 0x00,0xaa,0x00, col_green, -1, }, { '#', "Wall", 0x33,0x33,0x33, col_light_black, -1, }, { '"', "Grass", 0x00,0x88,0x00, col_green, -1, }, { ':', "???", 0x00,0x00,0x00, col_light_white, -1, }, { 'z', "Shore", 0xa7,0xcc,0x14, col_yellow, -1, }, { ',', "Lawn", 0x00,0xcc,0x00, col_green, -1, }, // Old versions { -1, "Street", 0x00,0x00,0x00, col_white, '.', }, { -1, "Door/Gate", 0x00,0x00,0x00, col_yellow, '-', }, { -1, "Door/Gate", 0x00,0x00,0x00, col_yellow, '|', }, { -1, "Fountain", 0x00,0x00,0x00, col_light_white, '*', }, }; const int nmapCityPieces = sizeof(mapCityPieces) / sizeof(mapCityPieces[0]); typedef struct { unsigned char c; char *ent; } HTMLEntity; static const HTMLEntity HTMLEntities[] = { { '&', "amp" }, { '<', "lt" }, { '>', "gt" }, { '"', "quot" }, { 228, "#228" }, { 246, "#246" }, { 196, "#196" }, { 214, "#214" }, }; static const int numHTMLEntities = sizeof(HTMLEntities) / sizeof(HTMLEntities[0]); int tmpl_fprintve(int (*mputs)(const char *, FILE *), FILE *outFile, const char *fmt, va_list ap) { int n, bufsize = strlen(fmt) * 2; char *buf, *tmp; if ((buf = th_malloc(bufsize)) == NULL) return -1; while (1) { va_list tap; va_copy(tap, ap); n = vsnprintf(buf, bufsize, fmt, tap); va_end(tap); if (n > -1 && n < bufsize) { // String fit the buffer, print it out and return int ret = mputs(buf, outFile); if (ret < 0) return ret; th_free(buf); return n; } // Didn't fit, try reallocating some more space if (n > -1) bufsize = n + 1; else bufsize *= 2; if ((tmp = th_realloc(buf, bufsize)) == NULL) { th_free(buf); return -2; } else buf = tmp; } return 0; } int fputse(const char *str, FILE *outFile) { const char *s = str; if (str == NULL) return EOF; while (*s) { int i; BOOL found = FALSE; for (i = 0; i < numHTMLEntities; i++) if (HTMLEntities[i].c == *s) { fprintf(outFile, "&%s;", HTMLEntities[i].ent); found = TRUE; break; } if (!found) fputc(*s, outFile); s++; } return 0; } int fprintve(FILE *outFile, const char *fmt, va_list ap) { return tmpl_fprintve(fputse, outFile, fmt, ap); } int fprintfe(FILE *outFile, const char *fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = fprintve(outFile, fmt, ap); va_end(ap); return ret; } int fputsesc1(const char *str, FILE *f) { const char *p = str; if (str == NULL) return -1; while (*p) { int ret; switch (*p) { case '\\': ret = fputs("\\", f); break; case '"': ret = fputs("\\\"", f); break; default: ret = fputc(*p, f); break; } p++; if (ret < 0) return ret; } return 0; } int fprintvesc1(FILE *outFile, const char *fmt, va_list ap) { return tmpl_fprintve(fputsesc1, outFile, fmt, ap); } int fprintfesc1(FILE *outFile, const char *fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = fprintvesc1(outFile, fmt, ap); va_end(ap); return ret; } int fputsesc3(const char *str, FILE *f) { const char *p = str; if (str == NULL) return -1; while (*p) { int ret; switch (*p) { case ';': ret = fputs("\\;", f); break; case '\\': ret = fputs("\\", f); break; case '\'': ret = fputs("\\'", f); break; case '"': ret = fputs("\\\"", f); break; default: ret = fputc(*p, f); break; } p++; if (ret < 0) return ret; } return 0; } int fputsesc2(const char *str, FILE *f) { const char *p = str; if (str == NULL) return -1; while (*p) { int ret; switch (*p) { case ';': ret = fputs("\\;", f); break; case '\\': ret = fputs("\\", f); break; default: ret = fputc(*p, f); break; } p++; if (ret < 0) return ret; } return 0; } char *muColorToCSSColor(char *buf, const size_t len, const int c) { assert(c >= 0); assert(c < nmapColors); snprintf(buf, len, "#%02x%02x%02x", mapColors[c].r, mapColors[c].g, mapColors[c].b); return buf; } static int muGetPieceFromList(const MapPiece pieces[], const int npieces, int symbol, BOOL getOld) { int i; for (i = 0; i < npieces; i++) { if ((getOld && pieces[i].oldSymbol == symbol) || (!getOld && pieces[i].symbol == symbol)) return i; } for (i = 0; i < npieces; i++) { if (getOld && pieces[i].symbol == symbol) return i; } return -1; } int muGetMapPieceIndex(int symbol, BOOL getOld, BOOL getCity) { int n; if (getCity && (n = muGetPieceFromList(mapCityPieces, nmapCityPieces, symbol, getOld)) >= 0) return n; return muGetPieceFromList(mapPieces, nmapPieces, symbol, getOld); } static int muGetColorFromList(const MapPiece pieces[], const int npieces, int symbol, BOOL getOld) { int i; if (getOld) { for (i = 0; i < npieces; i++) if (pieces[i].oldSymbol == symbol) return pieces[i].color; } for (i = 0; i < npieces; i++) if (pieces[i].symbol == symbol) return pieces[i].color; return -1; } int muGetMapPieceColor(int symbol, BOOL getOld, BOOL getCity) { int n; if (getCity && (n = muGetColorFromList(mapCityPieces, nmapCityPieces, symbol, getOld)) >= 0) return n; return ((n = muGetColorFromList(mapPieces, nmapPieces, symbol, getOld)) >= 0) ? n : 0; } void muPrintHTMLhead(FILE *outFile, const char *title, BOOL html5) { static const char *strCharSet = "utf-8"; assert(outFile != NULL); if (html5) { fprintf(outFile, "<!DOCTYPE html>\n" "<html lang=\"en\">\n" "<head>\n" " <meta charset=\"%s\">\n", strCharSet); } else { fprintf(outFile, "<?xml version=\"1.0\" encoding=\"%s\"?>\n" "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" " "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">\n" "<head>\n" " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\" />\n", strCharSet, strCharSet); } if (title) { fprintf(outFile, " <title>"); fputse(title, outFile); fprintf(outFile, "</title>\n"); } } void muPrintHTMLcolors(FILE *outFile, const char *tagName, const char *propName, const char *extra) { int n; assert(outFile != NULL); for (n = 0; n < nmapColors; n++) { fprintf(outFile, " %s.%c { %s: #%02x%02x%02x;%s%s }\n", tagName, 'a'+n, propName ? propName : "color", mapColors[n].r, mapColors[n].g, mapColors[n].b, mapColors[n].bold ? " font-weight: bold;" : "", extra ? extra : ""); } } int muCopyFileToStream(FILE *outFile, const char *filename) { FILE *inFile; if ((inFile = fopen(filename, "rb")) != NULL) { int c; while ((c = fgetc(inFile)) != EOF) { if (fputc(c, outFile) == EOF) { fclose(inFile); return -2; } } fclose(inFile); return 0; } else return -1; } /* Map block handling */ MapBlock * mapBlockAlloc(const int width, const int height) { MapBlock *res; // Check arguments if (width <= 0 || height <= 0) return NULL; // Allocate struct and data res = (MapBlock *) th_malloc0(sizeof(MapBlock)); if (!res) return NULL; res->width = width; res->height = height; res->scansize = ((width / BLOCK_SCAN_ALIGN) + 1) * BLOCK_SCAN_ALIGN; res->size = res->height * res->scansize * sizeof(char); res->data = (unsigned char *) th_malloc0(res->size); if (!res->data) { th_free(res); return NULL; } return res; } void mapBlockFree(MapBlock *block) { if (block) { th_free(block->data); block->data = NULL; th_free(block); } } MapBlock * mapBlockCopy(MapBlock *block) { MapBlock *res; if (block == NULL) return NULL; res = (MapBlock *) th_malloc0(sizeof(MapBlock)); if (!res) return NULL; res->width = block->width; res->height = block->height; res->scansize = block->scansize; res->size = block->size; res->data = (unsigned char *) th_malloc(res->size); if (!res->data) { th_free(res); return NULL; } memcpy(res->data, block->data, res->size); return res; } /* Parse single arbitrary sized block from given mapfile */ MapBlock * mapBlockParseFile(const char *filename, BOOL isDiff) { MapBlock *block; FILE *inFile; if ((inFile = fopen(filename, "rb")) == NULL) { THERR("Could not open mapfile '%s' for reading.\n", filename); return NULL; } block = mapBlockParseStream(filename, inFile, isDiff); fclose(inFile); return block; } MapBlock * mapBlockParseStream(const char *filename, FILE *inFile, BOOL isDiff) { MapBlock *res; unsigned char *o; long pos = 0L; BOOL flag; int x, y, resW, resH, c; assert(filename != NULL); if (isDiff) { char buf[128]; int ver; if (fgets(buf, sizeof(buf), inFile) == NULL) { THERR("Failed to read file header from '%s': %s\n", filename, th_error_str(th_get_error())); return NULL; } if (sscanf(buf, DIFF_MAGIC "%d\n", &ver) != 1) { THERR("Not a DIFF format file '%s'.\n", filename); return NULL; } if (ver != DIFF_VERSION) { THERR("Not a correct DIFF format version (%d != %d) '%s'.\n", ver, DIFF_VERSION, filename); return NULL; } pos = ftell(inFile); } // Probe map width resH = 1; resW = -1; x = 0; flag = FALSE; while ((c = fgetc(inFile)) != EOF) { if ((!isDiff && c == '\n') || (isDiff && c == 0xff)) { if (x > resW) resW = x; flag = TRUE; } else { if (flag) { x = 0; resH++; flag = FALSE; } x++; } } // Seek back if (fseek(inFile, pos, SEEK_SET) == -1) { THERR("Could not rewind file '%s'.\n", filename); return NULL; } // Allocate block if ((res = mapBlockAlloc(resW, resH)) == NULL) { THERR("Could not allocate mapblock (%d, %d) for '%s'.\n", resW, resH, filename); return NULL; } // Read data o = res->data; y = 0; x = 0; flag = FALSE; while ((c = fgetc(inFile)) != EOF && (y < res->height)) { if ((!isDiff && c == '\n') || (isDiff && c == 0xff)) { if (x != res->width) { THERR("Broken block in '%s', line #%d width %d < %d!\n", filename, y, x, res->width); mapBlockFree(res); return NULL; } flag = TRUE; } else { if (flag) { x = 0; y++; o = res->data + (y * res->scansize); flag = FALSE; } o[x++] = c; if (x > res->scansize) { THERR("Broken block in '%s', line #%d width %d > scansize %d!\n", filename, y, x, res->scansize); mapBlockFree(res); return NULL; } } } // Close file if (y >= res->height) { THERR("Broken block in '%s', height %d >= %d\n", filename, y, res->height); mapBlockFree(res); return NULL; } return res; } /* Print block to given output stream */ void mapBlockPrint(FILE *f, MapBlock *map) { unsigned char *c; int x, y; assert(f != NULL); assert(map != NULL); for (y = 0; y < map->height; y++) { c = map->data + (y * map->scansize); for (x = 0; x < map->width; x++) { if (*c > 0) fputc(*c, f); else fputc(' ', f); c++; } fprintf(f, "\n"); } } /* Blit a block into another, assume that memory has been allocated * in sufficient way and other preparations are done. */ int mapBlockPutDo(MapBlock *map, const MapBlock *src, const int ox, const int oy) { assert(map != NULL); assert(b != NULL); for (int y = 0; y < src->height; y++) for (int x = 0; x < src->width; x++) { const int dx = ox + x; const int dy = oy + y; if (dx >= 0 && dx < map->width && dy >= 0 && dy < map->height) { char c = src->data[(y * src->scansize) + x]; if (c != 0) map->data[(dy * map->scansize) + dx] = c; } else return -1; } return 0; } int mapBlockPut(MapBlock **pmap, const MapBlock *src, int ox, int oy) { MapBlock *tmp; int x0, y0, x1, y1, mx, my; assert(pmap != NULL); assert(*pmap != NULL); assert(src != NULL); // Determine new block size x0 = mx = y0 = my = 0; x1 = (*pmap)->width - 1; y1 = (*pmap)->height - 1; if (ox < 0) { x0 = ox; mx = -ox; ox = 0; } if (oy < 0) { y0 = oy; my = -oy; oy = 0; } if ((x0 + ox + src->width - 1) > x1) x1 = (x0 + ox + src->width - 1); if ((y0 + oy + src->height - 1) > y1) y1 = (y0 + oy + src->height - 1); // Allocate new block if ((tmp = mapBlockAlloc(x1 - x0 + 1, y1 - y0 + 1)) == NULL) return -1; // Copy data if (mapBlockPutDo(tmp, *pmap, mx, my) < 0) { th_free(tmp); return -2; } if (mapBlockPutDo(tmp, src, ox, oy) < 0) { th_free(tmp); return -3; } tmp->x = -mx; tmp->y = -my; // Out with the old, in with the new mapBlockFree(*pmap); *pmap = tmp; return 0; } /* Clean given block from position markers and whitespaces */ void mapBlockClean(MapBlock *map, char *symbols) { assert(map != NULL); assert(symbols != NULL); for (int y = 0; y < map->height; y++) { unsigned char *dp = map->data + (y * map->scansize); for (int x = 0; x < map->width; x++) { if (strchr(symbols, *dp) != NULL) *dp = 0; dp++; } } } void mapBlockPrintRaw(FILE *f, MapBlock *map) { assert(f != NULL); assert(map != NULL); for (int y = 0; y < map->height; y++) { unsigned char *dp = map->data + (y * map->scansize); for (int x = 0; x < map->width; x++) { fputc(*dp, f); dp++; } fputc(0xff, f); } } int mapBlockGetEntropy(MapBlock *map, const unsigned char *exclude, const int nexclude) { unsigned char *list; int x, y, i, num; // Allocate memory for entropy array if ((list = th_malloc0(256)) == NULL) return -1; // Collect sums into entropy array for (y = 0; y < map->height; y++) { unsigned char *c = map->data + (y * map->scansize); for (x = 0; x < map->width; x++) { list[*c]++; c++; } } // Handle exclusions if (exclude != NULL && nexclude > 0) { for (i = 0; i < nexclude; i++) list[(int) exclude[i]] = 0; } // Calculate simple entropy for (num = 0, i = 0; i < 256; i++) if (list[i]) num++; th_free(list); return num; }