view maputils.c @ 945:81184d58133c aprilli2011

Sync.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 14 May 2010 11:18:20 +0000
parents 066fed52348d
children 82469eecbae7
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-2008 Tecnic Software productions (TNSP)
 */
#include <string.h>
#include "maputils.h"
#include "th_util.h"


const color_t mapColors[] = {
{ 0x00, 0x00, 0x00, 30,    ANSI_OFF },
{ 0x00, 0x00, 0xbb, 34,    ANSI_OFF },
{ 0xbb, 0x00, 0x00, 31,    ANSI_OFF },
{ 0xbb, 0xbb, 0x00, 33,    ANSI_OFF },
{ 0x00, 0xbb, 0x00, 32,    ANSI_OFF },
{ 0xbb, 0xbb, 0xbb, 37,    ANSI_OFF },
{ 0x00, 0xff, 0xff, 36,    ANSI_OFF },
{ 0x77, 0x00, 0x77, 35,    ANSI_OFF },

{ 0x77, 0x77, 0x77, 30,    ANSI_BOLD },
{ 0x00, 0x00, 0xff, 34,    ANSI_BOLD },
{ 0xee, 0x00, 0x00, 31,    ANSI_BOLD },
{ 0xff, 0xff, 0x00, 33,    ANSI_BOLD },
{ 0x00, 0xff, 0x00, 32,    ANSI_BOLD },
{ 0xff, 0xff, 0xff, 37,    ANSI_BOLD },
{ 0x00, 0xff, 0xff, 36,    ANSI_BOLD },
{ 0xff, 0x00, 0xff, 35,    ANSI_BOLD },

/* extra colors */
{ 0xff, 0x80, 0x00, 31,    ANSI_BOLD },
};

const int nmapColors = (sizeof(mapColors) / sizeof(mapColors[0]));


const mappiece_t mapPieces[] = {
{ '!', "Mountain Peak",     0xcc,0xff,0xff, col_white,          -1, -1,             NULL },
{ '#', "Ruins",             0x88,0x88,0x88, col_light_black,    -1, -1,             NULL },
{ '%', "Special Location",  0xff,0xff,0xff, col_light_white,    -1, -1,             NULL },
{ '+', "Crossing",          0x33,0x33,0x33, col_light_black,    -1, -1,             NULL },
{ '-', "Road",              0x33,0x33,0x33, col_light_black,    -1, -1,             NULL },
{ '|', "Road",              0x33,0x33,0x33, col_light_black,    -1, -1,             NULL },
{ '/', "Road",              0x33,0x33,0x33, col_light_black,    -1, -1,             NULL },
{ '\\',"Road",              0x33,0x33,0x33, col_light_black,    -1, -1,             NULL },
{ '.', "Plains",            0x55,0x92,0x00, col_green,          -1, col_light_black,     "Road" },
{ -1,  NULL,                0x33,0x33,0x33, -1,                 'p', col_light_green,    "Plains" },
{ '=', "Bridge",            0x33,0x33,0x33, col_light_black,    -1, -1,             NULL },
{ '?', "Scenic Location",   0xff,0xff,0xff, col_light_white,    -1, -1,             NULL },
{ '@', "Flowing Lava",      0xff,0x99,0x3f, col_very_light_red, -1, -1,             NULL },
{ 'C', "Player City",       0x88,0x88,0x88, col_light_black,    -1, -1,             NULL },
{ 'F', "Deep Forest",       0x00,0x88,0x00, col_green,          -1, -1,             NULL },
{ 'H', "Highlands",         0x66,0x3f,0x00, col_magenta,        -1, -1,             NULL },
{ 'L', "Lava Lake",         0xff,0x50,0x00, col_very_light_red, -1, -1,             NULL },
{ 'R', "Deep River",        0x33,0x66,0xff, col_blue,           -1, -1,             NULL },
{ 'V', "Volcano",           0xff,0x33,0x00, col_red,            -1, -1,             NULL },
{ '^', "Mountain",          0x71,0x82,0x92, col_light_magenta,  -1, -1,             NULL },
{ 'b', "Beach",             0xcf,0xc4,0xa5, col_yellow,         -1, -1,             NULL },
{ 'c', "City",              0x88,0x88,0x88, col_light_black,    -1, -1,             NULL },
{ 'd', "Desert",            0xee,0xbb,0x22, col_yellow,         -1, -1,             NULL },
{ 'f', "Forest",            0x00,0xb6,0x00, col_light_green,    -1, -1,             NULL },
{ 'h', "Hills",             0x99,0x66,0x00, col_magenta,        -1, -1,             NULL },
{ 'i', "Ice",               0xee,0xee,0xff, col_light_blue,     -1, col_magenta,    "???" },
{ 'j', "Jungle",            0x13,0x96,0x36, col_green,          -1, -1,             NULL },
{ 'l', "Lake",              0x21,0x33,0xcc, col_light_blue,     -1, -1,             NULL },
{ 'r', "River",             0x66,0x99,0xff, col_light_blue,     -1, -1,             NULL },
{ 's', "Swamp",             0x9d,0xa8,0x0a, col_light_red,      -1, -1,             NULL },
{ 't', "Tundra",            0x61,0xc3,0xa2, col_white,          -1, -1,             NULL },
{ 'v', "Valley",            0x22,0xdd,0x22, col_light_green,    -1, -1,             NULL },
{ 'w', "Waterfall",         0x77,0xaa,0xff, col_light_cyan,     -1, -1,             NULL },
{ 'x', "Badlands",          0x8a,0x83,0x60, col_light_red,      -1, -1,             NULL },
{ 'y', "Fields",            0xa7,0xcc,0x14, col_yellow,         -1, -1,             NULL },
{ 'z', "Shore",             0xa7,0xcc,0x14, col_light_yellow,   -1, -1,             NULL },
{ ',', "Muddy Trail",       0x8c,0x57,0x38, col_light_yellow,   -1, -1,             NULL },
{ '&', "Monster",           0xff,0x00,0x00, col_light_red,      -1, -1,             NULL },
#ifndef SECRET_MAP_DATA_FORMAT
{ 'S', "Shallows",          0x44,0xcc,0xcc, col_light_cyan,     -1, -1,             NULL },
{ '~', "Sea",               0x11,0x88,0xdd, col_blue,           -1, -1,             NULL },
#else
{ '~', "Sea 1",             0x00,0x11,0x88, col_blue,           -1, -1,             NULL },
{ '"', "Sea 2",             0x11,0x22,0x99, col_blue,           -1, -1,             NULL },
{ '\'',"Sea 3",             0x11,0x33,0xaa, col_blue,           -1, -1,             NULL },
{ '`', "Shallows?",         0x11,0x66,0xdd, col_blue,           -1, -1,             NULL },
{ 'p', "Plains?",           0x55,0x92,0x00, col_green,          -1, -1,             NULL },
{ 'S', "Swamp?",            0x77,0x77,0x33, col_yellow,         -1, -1,             NULL },
#endif
};

const int nmapPieces = (sizeof(mapPieces) / sizeof(mapPieces[0]));

const mappiece_t mapCityPieces[] = {
{ '.', "Street",            0x00,0x00,0x00, col_yellow,         -1, col_white,      NULL },
{ '-', "Door/Gate",         0x00,0x00,0x00, col_light_yellow,   -1, col_yellow,     NULL },
{ '|', "Door/Gate",         0x00,0x00,0x00, col_light_yellow,   -1, col_yellow,     NULL },
{ '=', "Bridge/Gate",       0x00,0x00,0x00, col_yellow,         -1, -1,             NULL },
{ '*', "Fountain",          0x00,0x00,0x00, col_light_blue,     -1, col_light_white,"???" },
{ '@', "???",               0x00,0x00,0x00, col_light_yellow,   -1, -1,             NULL },
{ '$', "Tree",              0x00,0x00,0x00, col_green,          -1, -1,             NULL },
{ '#', "Wall",              0x00,0x00,0x00, col_light_black,    -1, -1,             NULL },
{ '"', "Grass",             0x00,0x00,0x00, col_green,          -1, -1,             NULL },
{ ':', "???",               0x00,0x00,0x00, col_white,          -1, -1,             NULL },
{ 'z', "Shore",             0xa7,0xcc,0x14, col_yellow,         -1, -1,             NULL },
/*
{ '.', "",            0x00,0x00,0x00, col_,        -1, -1,            NULL },
*/
};

const int nmapCityPieces = (sizeof(mapCityPieces) / sizeof(mapCityPieces[0]));


typedef struct {
    char c;
    char *ent;
} html_entity_t;


static const html_entity_t HTMLEntities[] = {
    { '&', "amp" },
    { '<', "lt" },
    { '>', "gt" },
    { '"', "quot" },

    { 'ä', "#228" },
    { 'ö', "#246" },
    { 'Ä', "#196" },
    { 'Ö', "#214" },
};

static const int numHTMLEntities = (sizeof(HTMLEntities) / sizeof(HTMLEntities[0]));


void fprinte(FILE *outFile, char *fmt)
{
    char *s = fmt;
    if (fmt == NULL) return;
    
    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++;
    }
}

void fprintve(FILE *outFile, char *fmt, va_list ap)
{
    int n, bufsize = strlen(fmt) * 2;
    char *buf, *tmp;
    
    if ((buf = th_malloc(bufsize)) == NULL)
        return;
    
    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 */
            fprinte(outFile, buf);
            th_free(buf);
            return;
        }
        
        /* 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;
        } else
            buf = tmp;
    }
}

void fprintfe(FILE *outFile, char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    fprintve(outFile, fmt, ap);
    va_end(ap);
}


char *mcGetCSS(int c)
{
    static char tmpStr[32];
    assert(c >= 0);
    assert(c < nmapColors);
    
    snprintf(tmpStr, sizeof(tmpStr), "#%02x%02x%02x",
        mapColors[c].r, mapColors[c].g, mapColors[c].b);
    
    return tmpStr;
}


int mcGetPieceFromList(const mappiece_t pieces[], const int npieces, int symbol, BOOL getOld)
{
    int i;
    
    if (getOld) {
        for (i = 0; i < npieces; i++)
        if (pieces[i].oldSymbol == symbol)
            return i;
    }

    for (i = 0; i < npieces; i++)
    if (pieces[i].symbol == symbol)
        return i;

    return -1;
}


int mcGet(int symbol, BOOL getOld, BOOL getCity)
{
    int n;
    
    if (getCity && (n = mcGetPieceFromList(mapCityPieces, nmapCityPieces, symbol, getOld)) >= 0)
        return n;
        
    return mcGetPieceFromList(mapPieces, nmapPieces, symbol, getOld);
}


int mcGetColorFromList(const mappiece_t pieces[], const int npieces, int symbol, BOOL getOld)
{
    int i;
    
    if (getOld) {
        for (i = 0; i < npieces; i++)
        if (pieces[i].oldSymbol == symbol) {
            if (pieces[i].oldColor >= 0)
                return pieces[i].oldColor;
            else
                return pieces[i].color;
        }
    }

    for (i = 0; i < npieces; i++)
    if (pieces[i].symbol == symbol) {
        if (getOld && pieces[i].oldColor >= 0)
            return pieces[i].oldColor;
        else
            return pieces[i].color;
    }

    return -1;
}


int mcGetColor(int symbol, BOOL getOld, BOOL getCity)
{
    int n;
    
    if (getCity && (n = mcGetColorFromList(mapCityPieces, nmapCityPieces, symbol, getOld)) >= 0)
        return n;
        
    if ((n = mcGetColorFromList(mapPieces, nmapPieces, symbol, getOld)) >= 0)
        return n;
    else
        return 0;
}


void mcXHTMLhead(FILE *outFile, char *title)
{
    static const char *strCharSet = "utf-8";
    assert(outFile != NULL);
    
    /* Output XHTML header */
    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>");
        fprinte(outFile, title);
        fprintf(outFile, "</title>\n");
    }
}


void mcXHTMLcolors(FILE *outFile, char *tagName, char *propName, char *extra)
{
    int n;
    assert(outFile != NULL);
    
    for (n = 0; n < nmapColors; n++) {
        fprintf(outFile,
        "  %s.%c { %s: #%02x%02x%02x;%s }\n",
        tagName, 'a'+n, propName ? propName : "color",
        mapColors[n].r,  mapColors[n].g,  mapColors[n].b,
        extra ? extra : "");
    }
}


/* Map block handling
 */
mapblock_t * allocBlock(int blockW, int blockH)
{
    mapblock_t *blockRes;
    
    /* Check arguments */
    if (blockW <= 0 || blockH <= 0)
        return NULL;
    
    /* Allocate struct and data */
    blockRes = (mapblock_t *) th_calloc(1, sizeof(mapblock_t));
    if (!blockRes) return NULL;
    
    blockRes->d = (unsigned char *) th_calloc(blockH * blockW, sizeof(char));
    if (!blockRes->d) {
        th_free(blockRes);
        return NULL;
    }
    
    blockRes->w = blockW;
    blockRes->h = blockH;

    return blockRes;
}


void freeBlock(mapblock_t *b)
{
    if (b) {
        if (b->d)
            th_free(b->d);
        b->d = NULL;
        th_free(b);
    }
}

/* Parse single arbitrary sized block from given mapfile
 */
mapblock_t * parseFile(char *mapFilename, BOOL isDiff)
{
    FILE *inFile;
    mapblock_t *res;
    unsigned char *o;
    long pos = 0L;
    BOOL flag;
    int x, y, resW, resH, c;
    assert(mapFilename != NULL);
    
    /* Open file */
    if ((inFile = fopen(mapFilename, "rb")) == NULL) {
        THERR("Could not open mapfile '%s' for reading.\n",
            mapFilename);
        return NULL;
    }
    
    if (isDiff) {
        char buf[128];
        int ver;
        fgets(buf, sizeof(buf), inFile);
        if (sscanf(buf, DIFF_MAGIC "%d\n", &ver) != 1) {
            fclose(inFile);
            THERR("Not a DIFF format file '%s'.\n", mapFilename);
            return NULL;
        }
        if (ver != DIFF_VERSION) {
            fclose(inFile);
            THERR("Not a correct DIFF format version (%d != %d) '%s'.\n",
                ver, DIFF_VERSION, mapFilename);
            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) {
        fclose(inFile);
        THERR("Could not rewind file '%s'.\n",
            mapFilename);
        return NULL;
    }
    
    /* Allocate block */
    if ((res = allocBlock(resW, resH)) == NULL) {
        fclose(inFile);
        THERR("Could not allocate mapblock (%d, %d) for '%s'.\n",
            resW, resH, mapFilename);
        return NULL;
    }
    
    /* Read data */
    o = res->d;
    y = 0; x = 0; flag = FALSE;
    while ((c = fgetc(inFile)) != EOF && (y < res->h)) {
        if ((!isDiff && c == '\n') || (isDiff && c == 0xff)) {
            if (x != res->w) {
                THERR("Broken block in '%s', line #%d width %d < %d!\n",
                    mapFilename, y, x, res->w);
                fclose(inFile);
                freeBlock(res);
                return NULL;
            }
            flag = TRUE;
        } else {
            if (flag) {
                x = 0;
                y++;
                flag = FALSE;
            }
            *(o++) = c;
            x++;
        }
    }

    /* Close file */
    fclose(inFile);
    
    if (y >= res->h) {
        THERR("Broken block in '%s', height %d >= %d\n",
            mapFilename, y, res->h);
        freeBlock(res);
        return NULL;
    }
    
    return res;
}


/* Print block to given output stream
 */
void printBlock(FILE *f, mapblock_t *map)
{
    unsigned char *c;
    int x, y;
    assert(f != NULL);
    assert(map != NULL);

    c = map->d;
        
    for (y = 0; y < map->h; y++) {
        for (x = 0; x < map->w; 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 putBlockDo(mapblock_t *map, mapblock_t *b, int ox, int oy)
{
    int x, y;
    assert(map != NULL);
    assert(b != NULL);
    
    for (y = 0; y < b->h; y++)
    for (x = 0; x < b->w; x++) {
        int dx = (ox + x);
        int dy = (oy + y);
        
        if (dx >= 0 && dx < map->w &&
            dy >= 0 && dy < map->h) {
            char c = b->d[(y * b->w) + x];

            if (c != 0)
                map->d[(dy * map->w) + dx] = c;
        } else
            return -1;
    }
    
    b->mark = TRUE;
    
    return 0;
}


int putBlock(mapblock_t **map, mapblock_t *b, int ox, int oy)
{
    mapblock_t *tmp;
    int x0, y0, x1, y1, mx, my;

    assert(map != NULL);
    assert(*map != NULL);
    assert(b != NULL);
    
    /* Determine new block size */
    x0 = mx = y0 = my = 0;
    x1 = (*map)->w - 1;
    y1 = (*map)->h - 1;
    
    if (ox < 0) { x0 = ox; mx = -ox; ox = 0; }
    if (oy < 0) { y0 = oy; my = -oy; oy = 0; }

    if ((x0 + ox + b->w - 1) > x1) x1 = (x0 + ox + b->w - 1);
    if ((y0 + oy + b->h - 1) > y1) y1 = (y0 + oy + b->h - 1);

    /* Allocate new block */
    if ((tmp = allocBlock((x1 - x0 + 1), (y1 - y0 + 1))) == NULL)
        return -1;
    
    /* Copy data */
    if (putBlockDo(tmp, *map, mx, my) < 0) {
        th_free(tmp);
        return -2;
    }

    if (putBlockDo(tmp, b, ox, oy) < 0) {
        th_free(tmp);
        return -3;
    }
    
    tmp->x = -mx;
    tmp->y = -my;
    
    /* Out with the old, in with the new */
    freeBlock(*map);
    *map = tmp;

    return 0;
}


/* Clean given block from position markers and whitespaces
 */
void cleanBlock(mapblock_t *map, char *symbols)
{
    int x, y;
    unsigned char *c;
    assert(map != NULL);
    assert(symbols != NULL);
    
    c = map->d;
    for (y = 0; y < map->h; y++)
    for (x = 0; x < map->w; x++) {
        if (strchr(symbols, *c) != NULL)
            *c = 0;
        c++;
    }
}


void printBlockRaw(FILE *f, mapblock_t *map)
{
    unsigned char *c = map->d;
    int x, y;
    assert(f != NULL);
    assert(map != NULL);
    
    for (y = 0; y < map->h; y++) {
        for (x = 0; x < map->w; x++) {
            fputc(*c, f);
            c++;
        }
        
        fputc(0xff, f);
    }
}