view src/diffmap.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 1fef8c9797f7
children
line wrap: on
line source

/*
 * Compute 'diff' between two maps
 * Programmed by Matti 'ccr' Hämäläinen <ccr@tnsp.org>
 * (C) Copyright 2006-2022 Tecnic Software productions (TNSP)
 */
#include "libmaputils.h"
#include "th_util.h"
#include "th_args.h"
#include "th_string.h"


#define SET_DEF_FILTER "C?%"


/* Variables
 */
char    *srcFilename1 = NULL,
        *srcFilename2 = NULL,
        *destFilename = NULL;
bool    optUseOldFormat = false,
        optAlwaysDiff = false;
char    *optFilter1 = "",
        *optFilter2 = SET_DEF_FILTER;


/* 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, 'O', "old-format", "Input using old symbols/colors", OPT_NONE },
    { 4, 'o', "output",     "Output filename", OPT_ARGREQ },
    { 5, 'F', NULL,         "Filter chars (none) for mapfile #1", OPT_ARGREQ },
    { 6, 'f', NULL,         "Filter chars (" SET_DEF_FILTER ") for mapfile #2", OPT_ARGREQ },
    { 7, 'a', "always",     "Always output diff file even when no changes", OPT_NONE },
};

static const int optListN = sizeof(optList) / sizeof(optList[0]);


void argShowHelp(void)
{
    th_print_banner(stdout, th_prog_name,
        "[options] <mapfile #1> <mapfile #2>");

    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:
        optUseOldFormat = true;
        THMSG(2, "Input is using old map symbols/colors.\n");
        break;

    case 4:
        destFilename = optArg;
        THMSG(2, "Output file set to '%s'.\n", optArg);
        break;

    case 5:
        optFilter1 = optArg;
        THMSG(2, "Filter characters for mapfile #1 set to'%s'.\n", optArg);
        break;

    case 6:
        optFilter2 = optArg;
        THMSG(2, "Filter characters for mapfile #2 set to'%s'.\n", optArg);
        break;

    case 7:
        optAlwaysDiff = true;
        THMSG(2, "Outputting diff file even with no changes.\n");
        break;

    default:
        THERR("Unknown option '%s'.\n", currArg);
        return false;
    }

    return true;
}


bool argHandleFile(char *currArg)
{
    if (srcFilename1 == NULL)
        srcFilename1 = currArg;
    else
    if (srcFilename2 == NULL)
        srcFilename2 = currArg;
    else
    {
        THERR("Too many input map files specified!\n");
        return false;
    }

    return true;
}


MapBlock * diffBlocks(const MapBlock *map1, const MapBlock *map2,
    const bool useOld, const char *filter1, const char *filter2,
    size_t *count)
{
    MapBlock *res;
    size_t
        nfilter1 = strlen(filter1),
        nfilter2 = strlen(filter2);

    if (map1->width != map2->width || map1->height != map2->height)
    {
        THERR("Mapblock size mismatch (%d, %d) vs (%d, %d)\n",
            map1->width, map1->height,
            map2->width, map2->height);
        return NULL;
    }

    if ((res = mapBlockAlloc(map1->width, map1->height)) == NULL)
    {
        THERR("Could not allocate mapblock (%d, %d)\n",
            map1->width, map1->height);
        return NULL;
    }

    *count = 0;
    for (int y = 0; y < map1->height; y++)
    {
        unsigned char
            *p1 = map1->data + (map1->scansize * y),
            *p2 = map2->data + (map2->scansize * y),
            *pd = res->data + (res->scansize * y);

        for (int x = 0; x < map1->width; x++)
        {
            int c;

            if (*p1 != *p2 &&
                !muStrChr((unsigned char *) filter1, nfilter1, *p1) &&
                !muStrChr((unsigned char *) filter2, nfilter2, *p2))
            {
                c = muGetMapPieceIndex(*p2, useOld, false);
                *pd = (c < 0) ? 0xfd : (c | 64);
                THMSG(2, "[%d,%d]: %d '%c' -> %d '%c'\n", x+1, y+1, *p1, *p1, *p2, *p2);
                (*count)++;
            }
            else
            {
                c = muGetMapPieceIndex(*p1, useOld, false);
                *pd = (c < 0) ? 0xfd : c;
            }

            p1++;
            p2++;
            pd++;
        }
    }

    return res;
}


int main(int argc, char *argv[])
{
    int res = 0;
    MapBlock
        *srcMap1 = NULL,
        *srcMap2 = NULL,
        *resMap = NULL;
    FILE *outFile = NULL;
    size_t count;

    // Initialize
    th_init("diffmap", "Create a diff between two ASCII mapfiles", "0.5", NULL, NULL);
    th_verbosity = 1;

    // Parse arguments
    if (!th_args_process(argc, argv, optList, optListN,
        argHandleOpt, argHandleFile, true))
    {
        res = 1;
        goto out;
    }

    if (srcFilename1 == NULL || srcFilename2 == NULL)
    {
        argShowHelp();
        THERR("Nothing to do.\n");
        res = 1;
        goto out;
    }

    // Read mapfiles
    if ((srcMap1 = mapBlockParseFile(srcFilename1, false)) == NULL ||
        (srcMap2 = mapBlockParseFile(srcFilename2, false)) == NULL)
    {
        res = -2;
        goto out;
    }

    // Compute and output data
    count = 0;
    if ((resMap = diffBlocks(srcMap1, srcMap2,
        optUseOldFormat, optFilter1, optFilter2, &count)) == NULL)
    {
        THERR("Could not create diff between inputs!\n");
        res = -3;
        goto out;
    }
    THMSG(1, "%" PRIu_SIZE_T " differences.\n", count);

    // Open output file
    res = count > 0 ? 0 : -4;
    if (optAlwaysDiff || count > 0)
    {
        THMSG(2, "Outputting map diff %d x %d...\n",
            resMap->width, resMap->height);

        if (destFilename == NULL)
            outFile = stdout;
        else
        if ((outFile = fopen(destFilename, "wb")) == NULL)
        {
            THERR("Error opening output file '%s'!\n",
                destFilename);
            res = -6;
            goto out;
        }

        fprintf(outFile, DIFF_MAGIC "%d\n", DIFF_VERSION);
        mapBlockPrintRaw(outFile, resMap);
    }
    else
    {
        THMSG(2, "Not outputting diff as no changes were found.\n");
        goto out;
    }


out:
    if (outFile != NULL)
        fclose(outFile);

    mapBlockFree(srcMap1);
    mapBlockFree(srcMap2);
    mapBlockFree(resMap);

    return res;
}