view packed.c @ 495:30145d17aebd

Move certain editor related targets to the actual TARGETS variable, and remove them from being separately specified in the "make clean" target, thusly cleaning them only when editor is enabled in build configuration.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 16 Nov 2012 19:18:03 +0200
parents ec70ae5aaa1a
children
line wrap: on
line source

/*
 * PACKed - PACKfile EDitor
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2011 Tecnic Software productions (TNSP)
 */
#include "dmlib.h"
#include "dmargs.h"
#include "dmpack.h"
#include "dmpackutil.h"
#include "dmres.h"
#include "dmmutex.h"
#include <errno.h>

#define	SET_MAX_FILES	  (4096)
#define SET_DEFAULT_PACK  "data.pak"

enum
{
    CMD_NONE = 0,
    CMD_CREATE,
    CMD_ADD,
    CMD_LIST,
    CMD_EXTRACT
} DCOMMAND;

enum
{
    PACK_EXTRACTED = 0x0001,
};

int    nsrcFilenames = 0;
char * srcFilenames[SET_MAX_FILES];
char * optPackFilename = NULL;
BOOL   optCompress = TRUE;
int    optCommand = CMD_NONE;
int    optDefResFlags = 0;


static DMOptArg optList[] =
{
    { 0, '?', "help",        "Show this help", OPT_NONE },
    { 1, 'p', "pack",        "Set pack filename (default: " SET_DEFAULT_PACK ")", OPT_ARGREQ },
    { 2, 'c', "create",      "Create and add files to PACK", OPT_NONE },
    { 3, 'a', "add",         "Add files to PACK", OPT_NONE },
    { 4, 'l', "list",        "List files in PACK", OPT_NONE },
    { 5, 'e', "extract",     "Extract files from PACK", OPT_NONE },
    { 6, 'n', "nocompress",  "No compression", OPT_NONE },
    { 7, 'v', "verbose",     "Increase verbosity", OPT_NONE },
    { 8, 'f', "resflags",    "Set default resource flags (-f 0xff)", OPT_ARGREQ },
};

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


void argShowHelp()
{
    dmPrintBanner(stdout, dmProgName, "[options] [-p <packfilename>] [filename[s]]");
    dmArgsPrintHelp(stdout, optList, optListN);
    fprintf(stdout,
    "\n"
    "Examples:\n"
    "$ %s -p test.pak -l         -- list files in test.pak\n"
    "$ %s -a foobar.jpg          -- add foobar.jpg in " SET_DEFAULT_PACK "\n"
    "$ %s -x foobar.jpg          -- extract foobar.jpg from " SET_DEFAULT_PACK "\n",
    dmProgName, dmProgName, dmProgName);
}


BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
{
    (void) optArg;
    switch (optN)
    {
        case 0:
            argShowHelp();
            exit(0);
            break;

        case 1:
            optPackFilename = optArg;
            break;
        case 2:
            optCommand = CMD_CREATE;
            break;
        case 3:
            optCommand = CMD_ADD;
            break;
        case 4:
            optCommand = CMD_LIST;
            break;
        case 5:
            optCommand = CMD_EXTRACT;
            break;

        case 6:
            optCompress = FALSE;
            break;

        case 7:
            dmVerbosity++;
            break;

        case 8:
            {
                int i;
                if (!dmGetIntVal(optArg, &i))
                {
                    dmError("Invalid flags value '%s'.\n", optArg);
                    return FALSE;
                }
                optDefResFlags = i;
            }
            break;

        default:
            dmError("Unknown argument '%s'.\n", currArg);
            return FALSE;
    }

    return TRUE;
}


BOOL argHandleFile(char *currArg)
{
    if (nsrcFilenames < SET_MAX_FILES)
    {
        srcFilenames[nsrcFilenames] = currArg;
        nsrcFilenames++;
    }
    else
    {
        dmError("Maximum number of input files (%d) exceeded!\n",
              SET_MAX_FILES);
        return FALSE;
    }
    return TRUE;
}


/* Compare a string to a pattern. Case-SENSITIVE version.
 * The matching pattern can consist of any normal characters plus
 * wildcards ? and *. "?" matches any character and "*" matches
 * any number of characters.
 */
BOOL dm_strmatch(const char *str, const char *pattern)
{
    BOOL didMatch = TRUE, isAnyMode = FALSE, isEnd = FALSE;
    const char *tmpPattern = NULL;

    // Check given pattern and string
    if (str == NULL || pattern == NULL)
        return FALSE;

    // Start comparision
    do {
        didMatch = FALSE;
        switch (*pattern)
        {
        case '?':
            // Any single character matches
            if (*str)
            {
                didMatch = TRUE;
                pattern++;
                str++;
            }
            break;

        case '*':
            didMatch = TRUE;
            pattern++;
            if (!*pattern)
                isEnd = TRUE;
            isAnyMode = TRUE;
            tmpPattern = pattern;
            break;

        case 0:
            if (isAnyMode)
            {
                if (*str)
                    str++;
                else
                    isEnd = TRUE;
            }
            else
            {
                if (*str)
                {
                    if (tmpPattern)
                    {
                        isAnyMode = TRUE;
                        pattern = tmpPattern;
                    }
                    else
                        didMatch = FALSE;
                }
                else
                    isEnd = TRUE;
            }
            break;
        default:
            if (isAnyMode)
            {
                if (*pattern == *str)
                {
                    isAnyMode = FALSE;
                    didMatch = TRUE;
                }
                else
                {
                    if (*str)
                    {
                        didMatch = TRUE;
                        str++;
                    }
                }
            }
            else
            {
                if (*pattern == *str)
                {
                    didMatch = TRUE;
                    if (*pattern)
                        pattern++;
                    if (*str)
                        str++;
                }
                else
                {
                    if (tmpPattern)
                    {
                        didMatch = TRUE;
                        isAnyMode = TRUE;
                        pattern = tmpPattern;
                    }
                }
            }

            if (!*str && !*pattern)
                isEnd = TRUE;
            break;

        }        // switch

    } while (didMatch && !isEnd);

    return didMatch;
}


int dmAddFileToPack(DMPackFile *pack, const char *filename, int compression, int resFlags)
{
    DMPackEntry *node;
    int res = dm_pack_add_file(pack, filename, compression, resFlags, &node);

    if (res != DMERR_OK)
    {
        dmPrint(1, "%-32s [ERROR:%d]\n",
            filename, res);
    }
    else
    {
        char tmp[16];
        dmres_flags_to_symbolic(tmp, sizeof(tmp), node->resFlags);
        dmPrint(1, "%-32s ['%s', s=%d, c=%d, o=%ld, f=%s]\n",
            filename, node->filename,
            node->size, node->length, node->offset,
            tmp);
    }

    return res;
}


int main(int argc, char *argv[])
{
    int i, res = 0;
    DMPackFile *pack = NULL;

#ifndef __WIN32
    stderr = stdout;
#endif

    // Parse arguments
    dmInitProg("packed", "Pack File Editor", "0.4", NULL, NULL);
    dmVerbosity = 1;
    
    if (!dmArgsProcess(argc, argv, optList, optListN,
        argHandleOpt, argHandleFile, TRUE))
        exit(1);

    // Check PACK filename
    if (optPackFilename == NULL)
        optPackFilename = SET_DEFAULT_PACK;

    if (optCommand == CMD_NONE)
    {
        argShowHelp();
        dmError("Nothing to do.\n");
        exit(0);
        return 0;
    }
    
    dmMsg(1, "Processing %s ...\n", optPackFilename);

    // Execute command
    switch (optCommand)
    {
    case CMD_CREATE:
    case CMD_ADD:
        switch (optCommand)
        {
        case CMD_CREATE:
            dmMsg(1, "Creating new PACK\n");
            res = dm_pack_create(optPackFilename, &pack);
            break;

        case CMD_ADD:
            dmMsg(1, "Opening existing PACK\n");
            res = dm_pack_open(optPackFilename, &pack, FALSE);
            break;
        }

        // Add files into PACK
        if (res == DMERR_OK)
        {
            dmMsg(1, "Adding %d files...\n", nsrcFilenames);

            for (i = 0; i < nsrcFilenames; i++)
            {
                // Handle resource definition files
                if (srcFilenames[i][0] == '@')
                {
                }
                else
                {
                    dmAddFileToPack(pack, srcFilenames[i], optCompress, optDefResFlags);
                }
            }

            dmMsg(1, "w=%d\n", dm_pack_write(pack));
            dmMsg(1, "c=%d\n", dm_pack_close(pack));
        }
        else
        {
            dmError("Could not open packfile, error #%d: %s\n", res,
                  dmErrorStr(res));
        }
        break;

    case CMD_LIST:
        // List files in PACK
        res = dm_pack_open(optPackFilename, &pack, TRUE);
        if (res == DMERR_OK)
        {
            DMPackEntry *node;
            for (i = 0, node = pack->entries; node; i++)
                node = node->next;
            dmMsg(1, "%d files total\n", i);

            dmPrint(0, "%-32s | %8s | %8s | %8s | %s\n",
                "Name", "Size", "CSize", "Offset", "ResFlags");

            for (node = pack->entries; node != NULL; node = node->next)
            {
                BOOL match;
                
                // Check for matches
                if (nsrcFilenames > 0)
                {
                    match = FALSE;
                    for (i = 0; i < nsrcFilenames && !match; i++)
                    {
                        match = dm_strmatch(node->filename, srcFilenames[i]);
                    }
                }
                else
                    match = TRUE;

                if (match)
                {
                    char flags[16];
                    dmres_flags_to_symbolic(flags, sizeof(flags), node->resFlags);

                    dmPrint(0, "%-32s | %8d | %8d | %08x | %s\n",
                        node->filename, node->size, node->length,
                        node->offset, flags);
                }
            }

            dmMsg(1, "c=%d\n", dm_pack_close(pack));
        }
        else
            dmError("Could not open packfile, error #%d: %s\n", res,
                  dmErrorStr(res));
        break;

    case CMD_EXTRACT:
        // Extract files from PACK
        res = dm_pack_open(optPackFilename, &pack, TRUE);
        if (res == DMERR_OK)
        {
            DMPackEntry *node;
            FILE *resFile = fopen(DMRES_RES_FILE, "w");
            if (resFile == NULL)
            {
                dmError("Could not create resource output file '%s' #%d: %s\n",
                    DMRES_RES_FILE, errno, strerror(errno));
            }

            for (node = pack->entries; node != NULL; node = node->next)
            {
                BOOL match;
                
                // Check for matches
                if (nsrcFilenames > 0)
                {
                    match = FALSE;
                    for (i = 0; (i < nsrcFilenames) && !match; i++)
                    {
                        match = dm_strmatch(node->filename, srcFilenames[i]);
                    }
                }
                else
                    match = TRUE;

                if (match && (node->privFlags & PACK_EXTRACTED) == 0)
                {
                    char tmp[16];

                    // Mark as done
                    node->privFlags |= PACK_EXTRACTED;

                    // Print one entry
                    dmres_flags_to_symbolic(tmp, sizeof(tmp), node->resFlags);
                    dmPrint(0, "Extracting: %-32s [siz=%d, cmp=%d, offs=0x%08x, flags=%s]\n",
                            node->filename, node->size, node->length,
                            node->offset, tmp);

                    dm_pack_extract_file(pack, node);
                    
                    if (resFile != NULL)
                    {
                        fprintf(resFile,
                        "%s|%s\n", node->filename, tmp);
                    }
                }
            }

            dmMsg(1, "c=%d\n", dm_pack_close(pack));
            
            if (resFile != NULL)
                fclose(resFile);
        }
        else
            dmError("Could not open packfile, error #%d: %s\n", res,
                  dmErrorStr(res));
        break;

    }

    return 0;
}