view tools/dumpmod.c @ 2208:90ec1ec89c56

Revamp the palette handling in lib64gfx somewhat, add helper functions to lib64util for handling external palette file options and add support for specifying one of the "internal" palettes or external (.act) palette file to gfxconv and 64vw.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 14 Jun 2019 05:01:12 +0300
parents e3f0eaf23f4f
children b7cd5dd0b82e
line wrap: on
line source

/*
 * dumpmod - View information about given module file
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2006-2015 Tecnic Software productions (TNSP)
 *
 * Please read file 'COPYING' for information on license and distribution.
 */
#include "dmtool.h"
#include "jss.h"
#include "jssmod.h"
#include "dmargs.h"
#include "dmmutex.h"


char    *optFilename = NULL;
BOOL    optViewPatterns = FALSE,
        optViewInstruments = FALSE,
        optViewExtInstruments = FALSE,
        optViewGeneralInfo = FALSE,
        optDump = FALSE;


static const DMOptArg optList[] =
{
    { 0, '?', "help",            "Show this help and exit", OPT_NONE },
    { 1, 'p', "patterns",        "View patterns", OPT_NONE },
    { 2, 'i', "instruments",     "View instruments", OPT_NONE },
    { 3, 'e', "extinstruments",  "View extended instruments", OPT_NONE },
    { 4, 'g', "general",         "General information", OPT_NONE },
    { 5, 'v', "verbose",         "Be more verbose", OPT_NONE },
    { 6, 'd', "dump",            "Dump mode", OPT_NONE },
    { 7, 'a', "all",             "Dump all information (pat, ext, gen, inst)", OPT_NONE },
};

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


void argShowHelp()
{
    dmPrintBanner(stdout, dmProgName, "[options] [modfile]");
    dmArgsPrintHelp(stdout, optList, optListN, 0);
}


BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
{
    (void) optArg;

    switch (optN)
    {
        case 0:
            argShowHelp();
            exit(0);
            break;

        case 1:
            optViewPatterns = TRUE;
            break;

        case 2:
            optViewInstruments = TRUE;
            break;

        case 3:
            optViewExtInstruments = TRUE;
            break;

        case 4:
            optViewGeneralInfo = TRUE;
            break;

        case 5:
            dmVerbosity++;
            break;

        case 6:
            optDump = TRUE;
            break;

        case 7:
            optViewPatterns = TRUE;
            optViewInstruments = TRUE;
            optViewGeneralInfo = TRUE;
            optViewExtInstruments = TRUE;
            break;

        default:
            dmErrorMsg("Unimplemented option argument '%s'.\n", currArg);
            return FALSE;
    }

    return TRUE;
}


BOOL argHandleFile(char *currArg)
{
    // Was not option argument
    if (!optFilename)
        optFilename = currArg;
    else
    {
        dmErrorMsg("Oh noes, we can only hand one file and '%s' looks like a second one!\n", currArg);
        return FALSE;
    }

    return TRUE;
}


const char patNoteTable[12][3] =
{
    "C-", "C#", "D-",
    "D#", "E-", "F-",
    "F#", "G-", "G#",
    "A-", "A#", "B-"
};


#define jmpNMODEffectTable (36)
static const char jmpMODEffectTable[jmpNMODEffectTable] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";


void printEscaped(FILE *f, const char *str)
{
    while (*str)
    {
        fputc(isprint(*str) ? *str : '*', f);
        str++;
    }
}


const char *getNote(const int note)
{
    static char tmp[16];
    snprintf(tmp, sizeof(tmp), "%s%d",
        patNoteTable[note % 12], note / 12);
    return tmp;
}


/* Print a given pattern
 */
void printPattern(FILE *f, const JSSPattern *p)
{
    int i, j;
    char c;
    JSSNote *n = p->data;

    for (i = 0; i < p->nrows; i++)
    {
        fprintf(f, "%.2x: ", i);

        for (j = 0; j < p->nchannels; j++)
        {
            switch (n->note)
            {
            case jsetNotSet:
                fprintf(f, "... ");
                break;
            case jsetNoteOff:
                fprintf(f, "=== ");
                break;
            default:
                fprintf(f, "%s ", getNote(n->note));
                break;
            }

            if (n->instrument != jsetNotSet)
                fprintf(f, "%.2x ", n->instrument + 1); // Because FT2 is 1-based and we use 0 internally
            else
                fprintf(f, ".. ");

            if (n->volume == jsetNotSet)
                fprintf(f, ".. ");
            else if (n->volume >= 0x00 && n->volume <= 0x40)
                fprintf(f, "%.2x ", n->volume);
            else
            {
                switch (n->volume & 0xf0)
                {
                    case 0x50: c = '-'; break;
                    case 0x60: c = '+'; break;
                    case 0x70: c = '/'; break;
                    case 0x80: c = '\\'; break;
                    case 0x90: c = 'S'; break;
                    case 0xa0: c = 'V'; break;
                    case 0xb0: c = 'P'; break;
                    case 0xc0: c = '<'; break;
                    case 0xd0: c = '>'; break;
                    case 0xe0: c = 'M'; break;
                    default:   c = '?'; break;
                }
                fprintf(f, "%c%x ", c, (n->volume & 0x0f));
            }

            if (n->effect >= 0 && n->effect < jmpNMODEffectTable)
                fprintf(f, "%c", jmpMODEffectTable[n->effect]);
            else if (n->effect == jsetNotSet)
                fprintf(f, ".");
            else
                fprintf(f, "?");

            if (n->param != jsetNotSet)
                fprintf(f, "%.2x|", n->param);
            else
                fprintf(f, "..|");

            n++;
        }

        fprintf(f, "\n");
    }
}


/*
 * Print given extended instrument
 */
void printEnvelope(FILE *f, const JSSEnvelope *e, const char *name)
{
    int i;

    fprintf(f,
        "    %s-envelope:\n"
        "      flags......: %.4x%s%s%s\n"
        "      npoints....: %d\n"
        "      sustain....: %d\n"
        "      loop.......: %d - %d\n",
        name, e->flags,
        (e->flags & jenvfUsed) ? " [used]" : "",
        (e->flags & jenvfSustain) ? " [sust]" : "",
        (e->flags & jenvfLooped) ? " [loop]" : "",
        e->npoints, e->sustain,
        e->loopS, e->loopE);

    if (dmVerbosity >= 2)
    {
        fprintf(f,
        "      Points.....:");

        for (i = 0; i < e->npoints; i++)
        {
            fprintf(f, " [%i:%i]",
            e->points[i].frame, e->points[i].value);
        }

        fprintf(f, "\n");
    }
}


void printExtInstrument(FILE *f, const JSSExtInstrument *inst)
{
#ifndef JSS_LIGHT
    if (inst->desc && !optDump)
    {
        fprintf(f, "'");
        printEscaped(f, inst->desc);
        fprintf(f, "'");
    }
#endif
    fprintf(f,
        "\n"
        "  nsamples.......: %i\n"
        "  vibratoType....: %i\n"
        "  vibratoSweep...: %i\n"
        "  vibratoDepth...: %i\n"
        "  vibratoRate....: %i\n"
        "  fadeOut........: %i\n",
        inst->nsamples, inst->vibratoType, inst->vibratoSweep,
        inst->vibratoDepth, inst->vibratoRate, inst->fadeOut);

    if (dmVerbosity >= 1)
    {
        printEnvelope(f, &inst->volumeEnv, "Volume");
        printEnvelope(f, &inst->panningEnv, "Panning");
    }

    if (dmVerbosity >= 2)
    {
        int n;
        fprintf(f,
        "  sNumForNotes: ");
        for (n = 0; n < jsetNNotes; n++)
        {
            int snum = inst->sNumForNotes[n];
            if (snum != jsetNotSet)
            {
                fprintf(f, "%s:%d ", getNote(n), snum);
            }
        }
    }
    fprintf(f, "\n");
}


void printInstrument(FILE *f, const JSSInstrument *inst)
{
    if (dmVerbosity >= 1)
    {
#ifndef JSS_LIGHT
        if (inst->desc && !optDump)
        {
            fprintf(f, "'");
            printEscaped(f, inst->desc);
            fprintf(f, "'");
        }
#endif
        fprintf(f,
            "\n"
            "  size...........: %ld (0x%lx)\n"
            "  loop...........: %ld - %ld (0x%lx - 0x%lx)\n"
            "  volume.........: %d (0x%x)\n"
            "  flags..........: 0x%x %s%s%s\n"
            "  C4BaseSpeed....: %d (0x%x)\n"
            "  ERelNote.......: %s (%d)\n"
            "  EFineTune......: %d\n"
            "  EPanning.......: %d (0x%x)\n\n",
            (unsigned long) inst->size, (unsigned long) inst->size,
            (unsigned long) inst->loopS, (unsigned long) inst->loopE,
            (unsigned long) inst->loopS, (unsigned long) inst->loopE,
            inst->volume, inst->volume,
            inst->flags,
            (inst->flags & jsfLooped) ? "[loop]" : "",
            (inst->flags & jsfBiDi)   ? "[bi-di]" : "",
            (inst->flags & jsf16bit)  ? "[16 bit]" : "[8 bit]",
            inst->C4BaseSpeed, inst->C4BaseSpeed,
            getNote(inst->ERelNote + 48), inst->ERelNote,
            inst->EFineTune, inst->EPanning, inst->EPanning);
    }
    else
    {
#ifndef JSS_LIGHT
        if (inst->desc && !optDump)
        {
            printEscaped(f, inst->desc);
            fprintf(f, "|");
        }
#endif
        fprintf(f,
            "%8ld|%8ld..%-8ld|%03d|%-2s %-2s %-2s|"
            "%4d|%s%d|%4d|%4d\n",
            (unsigned long) inst->size,
            (unsigned long) inst->loopS,
            (unsigned long) inst->loopE,
            inst->volume,

            (inst->flags & jsfLooped) ? "lp" : "",
            (inst->flags & jsfBiDi)   ? "bi" : "",
            (inst->flags & jsf16bit)  ? "16" : "8",

            inst->C4BaseSpeed,
            patNoteTable[(48 + inst->ERelNote) % 12],
            (48 + inst->ERelNote) / 12,
            inst->EFineTune,
            inst->EPanning);
    }
}


void printGeneralInfo(FILE *f, const JSSModule *m)
{
    int i;

    if (!m)
        return;

    if (!optDump)
    {
    fprintf(f, "Module type.....: %i\n", m->moduleType);
#ifndef JSS_LIGHT
    if (m->moduleName)
        fprintf(f, "Module name.....: '%s'\n", m->moduleName);
    if (m->trackerName)
        fprintf(f, "Tracker name....: '%s'\n", m->trackerName);
#endif
    }
    fprintf(f,
        "Speed...........: %d ticks\n"
        "Tempo...........: %d bpm\n"
        "Flags...........: %x ",
        m->defSpeed, m->defTempo, m->defFlags);

    if (m->defFlags & jmdfAmigaPeriods) fprintf(f, "[Amiga periods] ");
    if (m->defFlags & jmdfAmigaLimits)  fprintf(f, "[Amiga limits] ");
    if (m->defFlags & jmdfStereo)       fprintf(f, "[stereo] ");
    if (m->defFlags & jmdfFT2Replay)    fprintf(f, "[FT2 replay] ");
    if (m->defFlags & jmdfST300Slides)  fprintf(f, "[ST300 slides] ");
    if (m->defFlags & jmdfByteLStart)   fprintf(f, "[ByteStart] ");

    fprintf(f, "\n"
        "Restart pos.....: %d (order)\n"
        "IntVersion......: %x\n"
        "Channels........: %d\n"
        "Instruments.....: %d\n"
        "Ext.instruments.: %d\n"
        "Patterns........: %d\n"
        "Orders..........: %d\n",
        m->defRestartPos, m->intVersion, m->nchannels,
        m->ninstruments, m->nextInstruments, m->npatterns,
        m->norders);

    if (dmVerbosity >= 1)
    {
        fprintf(f, "Orderlist: ");
        for (i = 0; i < m->norders - 1; i++)
            fprintf(f, "%d, ", m->orderList[i]);
        if (i < m->norders)
            fprintf(f, "%d", m->orderList[i]);
        fprintf(f, "\n");
    }
}



int main(int argc, char *argv[])
{
    int result = -1, i;
    DMResource *file = NULL;
    JSSModule *mod = NULL;

    dmInitProg("dumpmod", "miniJSS Module Viewer", "0.4", NULL, NULL);
    dmVerbosity = 0;

    // Parse arguments
    if (!dmArgsProcess(argc, argv, optList, optListN,
        argHandleOpt, argHandleFile, OPTH_BAILOUT))
        exit(1);

    // Initialize miniJSS
    jssInit();

    // Open the file
    dmMsg(1, "Reading module file '%s'\n", optFilename);
    if (optFilename == NULL)
        result = dmf_open_stdio_stream(stdin, &file);
    else
        result = dmf_open_stdio(optFilename, "rb", &file);

    if (result != DMERR_OK)
    {
        dmErrorMsg("Error opening input file '%s', #%d: %s\n",
            optFilename, result, dmErrorStr(result));
        return 1;
    }

    // Read module file
    dmMsg(1, "Reading file: %s\n", optFilename);
#ifdef JSS_SUP_XM
    result = jssLoadXM(file, &mod, TRUE);
#endif
#ifdef JSS_SUP_JSSMOD
    dmfreset(file);
    if (result != DMERR_OK)
    {
        dmMsg(1, "* Trying JSSMOD ...\n");
        result = jssLoadJSSMOD(file, &mod, TRUE);
        dmfreset(file);
        if (result == DMERR_OK)
            result = jssLoadJSSMOD(file, &mod, FALSE);
    }
    else
    {
        dmMsg(2, "* Trying XM...\n");
        result = jssLoadXM(file, &mod, FALSE);
    }
#endif
    dmf_close(file);

    // Check for errors, we still might have some data tho
    if (result != DMERR_OK)
    {
        dmErrorMsg("Error loading module file, %d: %s\n",
            result, dmErrorStr(result));
    }

    // Check if we have anything
    if (mod == NULL)
        return 3;

    // Print out information
    if (optViewGeneralInfo)
        printGeneralInfo(stdout, mod);

    if (optViewPatterns)
    {
        for (i = 0; i < mod->npatterns; i++)
        if (mod->patterns[i] != NULL)
        {
            printf("\nPattern #%02x:\n", i);
            printPattern(stdout, mod->patterns[i]);
        }
    }

    if (optViewExtInstruments)
    {
        printf("\n"
        "ExtInstruments:\n"
        "---------------\n"
        );
        for (i = 0; i < mod->nextInstruments; i++)
        if (mod->extInstruments[i] != NULL)
        {
            printf("#%02x: ", i + 1);
            printExtInstrument(stdout, mod->extInstruments[i]);
        }
    }

    if (optViewInstruments)
    {
        printf("\n"
        "Instruments:\n"
        "------------\n"
        );
        for (i = 0; i < mod->ninstruments; i++)
        if (mod->instruments[i] != NULL)
        {
            printf("#%02x: ", i + 1);
            printInstrument(stdout, mod->instruments[i]);
        }
    }

    // Free module data
    jssFreeModule(mod);
    jssClose();

    return 0;
}