changeset 651:e2ac08228a0f

Move files to various subdirectories.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 16 Apr 2013 06:01:42 +0300
parents 107468492257
children d9888292f971
files blittest.c data2inc.c edgui.cpp editor/edgui.cpp editor/edmain.cpp editor/edmain.h editor/edres.cpp editor/edres.h editor/edtimeline.cpp editor/edtimeline.h editor/edtlobj.cpp editor/edtlobj.h editor/edview.cpp editor/edview.h editor/edwaveform.cpp editor/edwaveform.h edmain.cpp edmain.h edres.cpp edres.h edtimeline.cpp edtimeline.h edtlobj.cpp edtlobj.h edview.cpp edview.h edwaveform.cpp edwaveform.h efu.c gentab.c gfxconv.c mod2wav.c objlink.c packed.c ppl.c svg2qd.py testpl.c tests/blittest.c tests/efu.c tests/testpl.c tests/vecmattest.c tests/vptest.c utils/data2inc.c utils/gentab.c utils/gfxconv.c utils/mod2wav.c utils/objlink.c utils/packed.c utils/ppl.c utils/svg2qd.py utils/view64.c utils/viewmod.c utils/xm2jss.c vecmattest.c view64.c viewmod.c vptest.c xm2jss.c
diffstat 58 files changed, 11416 insertions(+), 11416 deletions(-) [+]
line wrap: on
line diff
--- a/blittest.c	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,458 +0,0 @@
-#include "dmlib.h"
-#include "dmargs.h"
-#include "dmvecmat.h"
-#include "dmtext.h"
-#include <math.h>
-
-#define DM_COLORS (256)
-
-char *optFontFile = "font.ttf";
-BOOL optBenchmark = FALSE;
-int optVFlags = SDL_SWSURFACE | SDL_HWPALETTE;
-int optScrWidth = 640, optScrHeight = 480, optFontSize = 20, optScrDepth = 32;
-int optBenchmarkLen = 20;
-
-DMOptArg optList[] = {
-    { 0, '?', "help",       "Show this help", OPT_NONE },
-    { 2, 'v', "verbose",    "Be more verbose", OPT_NONE },
-    { 3, 'f', "full",       "Fullscreen", OPT_NONE },
-    { 4, 'h', "hw",         "Use SDL hardware surface", OPT_NONE },
-    { 5, 's', "size",       "Initial window size/resolution -s 640x480", OPT_ARGREQ },
-    { 6, 'd', "depth",      "Color depth of mode/window in bits (8/32)", OPT_ARGREQ },
-    { 7, 'b', "bench",      "Run in benchmark mode", OPT_NONE },
-};
-
-const int optListN = sizeof(optList) / sizeof(optList[0]);
-
-
-void argShowHelp()
-{
-    dmArgsPrintHelp(stdout, optList, optListN);
-}
-
-
-BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
-{
-    switch (optN) {
-    case 0:
-        argShowHelp();
-        exit(0);
-        break;
-
-    case 2:
-        dmVerbosity++;
-        break;
-    
-    case 3:
-        optVFlags |= SDL_FULLSCREEN;
-        break;
-
-    case 6:
-        if (optArg)
-            optScrDepth = atoi(optArg);
-        break;
-
-    case 5:
-        {
-            int w, h;
-            if (sscanf(optArg, "%dx%d", &w, &h) == 2)
-            {
-                if (w < 320 || h < 200 || w > 3200 || h > 3200)
-                {
-                    dmError("Invalid width or height: %d x %d\n", w, h);
-                    return FALSE;
-                }
-                optScrWidth = w;
-                optScrHeight = h;
-            }
-            else 
-            {
-                dmError("Invalid size argument '%s'.\n", optArg);
-                return FALSE;
-            }
-        }
-        break;
-
-    case 7:
-        optBenchmark = TRUE;
-        break;
-
-    default:
-        dmError("Unknown option '%s'.\n", currArg);
-        return FALSE;
-    }
-    
-    return TRUE;
-}
-
-
-void DM_MakePalette(SDL_Surface *scr)
-{
-    SDL_Color pal[DM_COLORS];
-    int n;
-
-    for (n = 0; n < 256; n++)
-    {
-        pal[n].r = n;
-        pal[n].g = n;
-        pal[n].b = n;
-    }
-
-    SDL_SetColors(scr, pal, 0, DM_COLORS);
-}
-
-
-void DM_PrintRect(FILE *f, SDL_Rect *r)
-{
-    fprintf(f, "SDL_Rect <%d, %d : %d, %d>\n",
-        r->x, r->y, r->w, r->h);
-}
-
-BOOL DM_InitializeVideo(SDL_Surface **screen)
-{
-    *screen = SDL_SetVideoMode(optScrWidth, optScrHeight, optScrDepth, optVFlags | SDL_RESIZABLE);
-    if (*screen == NULL)
-    {
-        dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError());
-        return FALSE;
-    }
-
-#if 0
-    SDL_Rect r;
-    r.x = -50;
-    r.y = 50;
-    r.w = 700;
-    r.h = 300;
-    DM_PrintRect(stderr, &r);
-    SDL_SetClipRect(*screen, &r);
-    DM_PrintRect(stderr, &r);
-    DM_PrintRect(stderr, &((*screen)->clip_rect));
-#endif
-
-    return TRUE;
-}
-
-void DM_Random(SDL_Surface *screen, int q)
-{
-    Uint8 *pix = screen->pixels;
-    int xc, yc;
-    
-    for (yc = 0; yc < screen->h; yc++)
-    {
-        Uint8 *dp = pix;
-
-        for (xc = 0; xc < screen->w; xc++)
-            *dp++ = yc + (xc ^ q) + (yc & q);
-
-        pix += screen->pitch;
-    }
-}
-
-#define QWIDTH	256
-#define QHEIGHT	160
-
-typedef Uint8 DMBlockMap[QHEIGHT][QWIDTH];
-
-
-static DMFloat dmClip(DMFloat a)
-{
-    return (a < 0.0f ? 0.0f : (a > 1.0f ? 1.0f : a));
-}
-
-
-void dmMakeBumpMap(DMBlockMap map, DMFloat q, DMFloat m)
-{
-    int x, y;
-    for (y = 0; y < QHEIGHT; y++)
-        for (x = 0; x < QWIDTH; x++)
-        {
-            DMFloat f = 0.40f + dmPerlinNoise2D(x, y, 1.1f, q, 2);
-            map[y][x] = (int) (dmClip(f) * m);
-        }
-}
-
-
-void dmShadowTraceHeightMap(DMBlockMap lightMap, DMBlockMap pheightMap, DMVector *light)
-{
-    int i, j;
-
-    for (j = 0; j < QHEIGHT; j++)
-        for (i = 0; i < QWIDTH; i++)
-        {
-            DMVector vr, vl, va;
-            DMFloat vrayLen, vfactor;
-            int vlen;
-            BOOL wasHit;
-
-            // Perform shadow occlusion via simplistic raytracing
-            vr.x = i;
-            vr.y = j;
-            vr.z = light->z; // - 10.0;
-//            vr.z = pheightMap[j][i];
-            
-            // Calculate light vector vector
-            dm_vector_sub_r(&vl, &vr, light);
-            vrayLen = dm_vector_length(&vl);
-            
-#if 1
-            dm_vector_copy(&va, &vl);
-            dm_vector_normalize(&va);
-            dm_vector_scale(&va, 0.6f);
-            dm_vector_copy(&vr, light);
-
-            vlen = 0;
-            wasHit = FALSE;
-            do
-            {
-                float h;
-                
-                // If ray is inside the heightmap, get value
-                if (vr.x >= 0 && vr.y >= 0 && vr.x < QWIDTH && vr.y < QHEIGHT)
-                    h = pheightMap[(int) vr.y][(int) vr.x];
-                else
-                    break;
-                
-                // Check for hits
-                if (h > vr.z)
-                    wasHit = TRUE;
-                else
-                {
-                    // Move forwards
-                    dm_vector_add(&vr, &va);
-                    vlen++;
-                }
-            }
-            while (!wasHit && vlen <= vrayLen);
-
-            // Check if the ray hit something, e.g. is this point occluded?
-            if (wasHit && vlen < vrayLen)
-            {
-                vfactor = vlen * 0.01;
-            }
-            else
-                vfactor = vlen * 0.02;
-#endif
-
-#if 1
-            {
-                /* Calculate light's intensity based on the angle it "hits"
-                 *
-                 * 1) Calculate the vectors that form the imaginary "plane"
-                 * 2) Cross-product -> normal vector of the plane
-                 * 2) Normalize the normal vector
-                 * 3) Calculate light vector's hit angle by dot product
-                 */
-                DMVector v1, v2;
-                DMFloat c;
-
-                v1.x = 2.0f;
-                v1.y = 0.0f;
-                v1.z = (DMFloat) (pheightMap[j][i] - pheightMap[j][i + 1]);
-
-                v2.x = 0.0f;
-                v2.y = 2.0f;
-                v2.z = (DMFloat) (pheightMap[j][i] - pheightMap[j + 1][i]);
-
-                dm_vector_cross(&vr, &v1, &v2);
-                dm_vector_normalize(&vr);
-                dm_vector_normalize(&vl);
-                c = dm_vector_dot(&vl, &vr);
-
-		vrayLen = 255 - (vrayLen * 0.1) * vrayLen + (c * 128.0f) + (vfactor * vfactor * 1255);
-            }
-#else
-            vrayLen = 255 - vrayLen * vrayLen * (vfactor * vfactor);
-            if (vrayLen < 0) vrayLen = 0;
-            vrayLen += pheightMap[j][i];
-#endif
-
-            // Clip result
-            if (vrayLen < 0)
-                vrayLen = 0;
-            else if (vrayLen > 255.0f)
-                vrayLen = 255.0f;
-
-            lightMap[j][i] = vrayLen;
-        }
-}
-
-
-int main(int argc, char *argv[])
-{
-    SDL_Surface *screen = NULL, *bmap = NULL;
-    TTF_Font *font = NULL;
-    SDL_Color fontcol={255,155,155,0};
-    SDL_Event event;
-    int mouseX, mouseY;
-    BOOL initSDL = FALSE, initTTF = FALSE, exitFlag, showMap = FALSE;
-
-    dmInitProg("blittest", "dmlib blittest", "0.2", NULL, NULL);
-    if (!dmArgsProcess(argc, argv, optList, optListN,
-        argHandleOpt, NULL, FALSE))
-        exit(1);
-
-    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
-    {
-        dmError("Could not initialize SDL: %s\n", SDL_GetError());
-        goto error_exit;
-    }
-    initSDL = TRUE;
-
-
-    if (TTF_Init() < 0)
-    {
-        dmError("Could not initialize FreeType/TTF: %s\n", SDL_GetError());
-        goto error_exit;
-    }
-    initTTF = TRUE;
-
-    font = TTF_OpenFont(optFontFile, optFontSize);
-    if (font == NULL)
-    {
-        dmError("Could not load TTF font '%s' (%d): %s\n",
-            optFontFile, optFontSize, SDL_GetError());
-        goto error_exit;
-    }
-    TTF_SetFontStyle(font, TTF_STYLE_NORMAL);
-
-    if (optBenchmark)
-    {
-        screen = SDL_CreateRGBSurface(SDL_SWSURFACE, optScrWidth, optScrHeight, optScrDepth, 0, 0, 0, 0);
-        if (screen == NULL)
-        {
-            dmError("Could not create screen surface.\n");
-            goto error_exit;
-        }
-        
-        dmMsg(0, "Benchmark mode, not opening window.\n");
-    }
-    else
-    {
-        if (!DM_InitializeVideo(&screen))
-            goto error_exit;
-
-        SDL_WM_SetCaption("Halleluja", "DMT");
-    }
-
-    dmPerlinInit();
-
-    bmap = SDL_CreateRGBSurface(SDL_SWSURFACE, QWIDTH, QHEIGHT, 8, 0, 0, 0, 0);
-    DM_MakePalette(bmap);
-    DM_Random(bmap, 15);
-    
-    DMVector light;
-    DMBlockMap heightMap;
-    light.x = light.y = 128;
-    light.z = 128;
-    dmMakeBumpMap(heightMap, 0.06, 254);
-
-
-
-    int numFrames = 0, startTime = SDL_GetTicks(), endTime = 0;
-    exitFlag = FALSE;
-
-    if (optBenchmark)
-        dmMsg(0, "Starting benchmark, running for %d seconds.\n", optBenchmarkLen);
-
-    while (!exitFlag)
-    {
-        dmMakeBumpMap(heightMap, 0.05 + sin(SDL_GetTicks() * 0.001) * 0.002 , 254);
-        if (!optBenchmark)
-        {
-            while (SDL_PollEvent(&event))
-            switch (event.type)
-            {
-                case SDL_KEYDOWN:
-                    switch (event.key.keysym.sym)
-                    {
-                        case SDLK_ESCAPE: exitFlag = TRUE; break;
-                        
-                        case SDLK_F5:
-                            showMap = !showMap;
-                            break;
-                            
-                        default:
-                            break;
-                    }
-
-                    break;
-                
-                case SDL_VIDEORESIZE:
-                    optScrWidth = event.resize.w;
-                    optScrHeight = event.resize.h;
-
-                    if (!DM_InitializeVideo(&screen))
-                        goto error_exit;
-
-                    break;
-                
-                case SDL_VIDEOEXPOSE:
-                    break;
-
-                case SDL_QUIT:
-                    exit(0);
-            }
-
-            SDL_GetMouseState(&mouseX, &mouseY);
-            light.x = ((DMFloat) mouseX * QWIDTH) / (DMFloat) optScrWidth;
-            light.y = ((DMFloat) mouseY * QHEIGHT) / (DMFloat) optScrHeight;
-        }
-
-        if (!optBenchmark && SDL_MUSTLOCK(screen) != 0 && SDL_LockSurface(screen) != 0)
-        {
-            dmError("Can't lock surface.\n");
-            goto error_exit;
-        }
-
-
-        if (showMap)
-            memcpy(bmap->pixels, heightMap, QWIDTH * QHEIGHT);
-        else
-            dmShadowTraceHeightMap(bmap->pixels, heightMap, &light);
-        
-        dmScaledBlitSurfaceAny(bmap, 0, 0, screen->w, screen->h, screen, DMD_NONE);
-
-        if (!optBenchmark)
-        {
-            dmDrawTTFText(screen, font, fontcol, 0, 0, "%3.1f FPS", 
-                (float) (numFrames * 1000.0f) / (float) (endTime - startTime));
-
-            if (SDL_MUSTLOCK(screen) != 0)
-                SDL_UnlockSurface(screen);
-
-            SDL_Flip(screen);
-            SDL_Delay(10);
-        }
-
-        endTime = SDL_GetTicks();
-        numFrames++;
-
-        if (optBenchmark)
-        {
-            if (endTime - startTime > optBenchmarkLen * 1000)
-                exitFlag = TRUE;
-        }
-    }
-
-    // Print benchmark results
-    dmMsg(0, "%d frames in %d ms, fps = %1.3f\n",
-        numFrames, endTime - startTime,
-        (float) (numFrames * 1000.0f) / (float) (endTime - startTime));
-
-
-error_exit:
-    dmMsg(0, "Shutting down dmlib.\n");
-    if (screen)
-        SDL_FreeSurface(screen);
-
-    if (font)
-        TTF_CloseFont(font);
-
-    if (initSDL)
-        SDL_Quit();
-
-    if (initTTF)
-        TTF_Quit();
-
-    return 0;
-}
--- a/data2inc.c	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,404 +0,0 @@
-/*
- * data2inc - Convert binary data to "C"-source or XA-compatible include file
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2003,2009-2012 Tecnic Software productions (TNSP)
- *
- * Please read file 'COPYING' for information on license and distribution.
- */
-#include <errno.h>
-#include "dmlib.h"
-#include "dmargs.h"
-#include "dmmutex.h"
-
-#define RA_LINEBUF    (16)
-
-enum
-{
-    FMT_AUTO = 0,
-    FMT_C,
-    FMT_ASM
-};
-
-char    *optInFilename = NULL,
-        *optOutFilename = NULL,
-        *optObjName = "default_object",
-        *optDataType = NULL,
-        *optAddLine = NULL;
-
-int     optIndentation = -1;
-int     optFormat = FMT_AUTO;
-BOOL    optHexMode = FALSE,
-        optQuiet = FALSE,
-        optExtraData = FALSE,
-        optFormatting = TRUE;
-
-
-void (*writeHeader) (FILE *, char *) = NULL;
-void (*writeDecl) (FILE *, size_t, char *) = NULL;
-void (*writeData) (FILE *, Uint8 *, size_t) = NULL;
-void (*writeFooter) (FILE *, size_t, char *) = NULL;
-
-
-static DMOptArg optList[] =
-{
-    {  0, '?', "help",           "Show this help", OPT_NONE },
-    {  4, 'A', "format-asm",     "Output in XA-compatible asm", OPT_NONE },
-    {  5, 'C', "format-c",       "Output in ANSI C", OPT_NONE },
-    {  1, 'n', "name",           "Set object name", OPT_ARGREQ },
-    {  2, 't', "type",           "Set datatype (unsigned char/byte)", OPT_ARGREQ },
-    {  3, 'a', "add-line",       "Add this line to start of file", OPT_ARGREQ },
-    {  6, 'x', "hexadecimal",    "Use hexadecimal output", OPT_NONE },
-    {  7, 'q', "quiet",          "Do not add comments", OPT_NONE },
-    {  8, 'f', "no-formatting",  "Disable additional output formatting", OPT_NONE },
-    {  9, 'i', "indentation",    "Set indentation (negative value = tab)", OPT_ARGREQ },
-    { 10, 'e', "extra-data",     "Add object end labels and size in asm output", OPT_NONE },
-};
-
-static const int optListN = sizeof(optList) / sizeof(optList[0]);
-
-
-void argShowHelp()
-{
-    dmPrintBanner(stdout, dmProgName,
-        "[options] [sourcefile] [destfile]");
-
-    dmArgsPrintHelp(stdout, optList, optListN);
-    
-    printf(
-    "\n"
-    "To convert a data file to a C structure using 'Uint8' as type:\n"
-    "$ data2inc -C -n variable_name -t Uint8 input.bin output.h\n"
-    "\n"
-    );
-}
-
-
-BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
-{
-    switch (optN)
-    {
-        case 0:
-            argShowHelp();
-            exit(0);
-            break;
-
-        case 1:
-            optObjName = optArg;
-            break;
-
-        case 2:
-            optDataType = optArg;
-            break;
-
-        case 3:
-            optAddLine = optArg;
-            break;
-
-        case 4:
-            optFormat = FMT_ASM;
-            break;
-        case 5:
-            optFormat = FMT_C;
-            break;
-        case 6:
-            optHexMode = TRUE;
-            break;
-        case 7:
-            optQuiet = TRUE;
-            break;
-        case 8:
-            optFormatting = FALSE;
-            break;
-
-        case 9:
-            optIndentation = atoi(optArg);
-            break;
-
-        case 10:
-            optExtraData = TRUE;
-            break;
-
-        default:
-            dmError("Unknown option '%s'.\n", currArg);
-            return FALSE;
-    }
-
-    return TRUE;
-}
-
-
-BOOL argHandleFile(char * currArg)
-{
-    if (!optInFilename)
-        optInFilename = currArg;
-    else
-    if (!optOutFilename)
-        optOutFilename = currArg;
-    else
-        dmError("Source and destination filenames already specified, extraneous argument '%s'.\n", currArg);
-
-    return TRUE;
-}
-
-
-/* Assembler include data output functions
- */
-void writeHeader_ASM(FILE * f, char *name)
-{
-    if (name)
-        fprintf(f, "; '%s'", name);
-    else
-        fprintf(f, "; Generated");
-    fprintf(f, " by %s v%s\n",
-        dmProgName, dmProgVersion);
-}
-
-void writeDecl_ASM(FILE * f, size_t len, char *name)
-{
-    if (optExtraData)
-        fprintf(f, "%s_size = %u\n", name, len);
-    fprintf(f, "%s:\n", name);
-}
-
-void writeData_ASM(FILE * f, Uint8 * buf, size_t len)
-{
-    size_t i;
-
-    fprintf(f, "%s ", optDataType);
-    for (i = 0; i < len; i++)
-    {
-        if (optFormatting)
-        {
-            if (optHexMode)
-                fprintf(f, "$%.2x", buf[i]);
-            else
-                fprintf(f, "%3d", buf[i]);
-        }
-        else
-        {
-            if (optHexMode)
-                fprintf(f, "$%x", buf[i]);
-            else
-                fprintf(f, "%d", buf[i]);
-        }
-
-        if (i < (len - 1))
-            fprintf(f, ",");
-    }
-}
-
-void writeFooter_ASM(FILE * f, size_t len, char *name)
-{
-    (void) len;
-    if (optExtraData)
-        fprintf(f, "%s_end: \n", name);
-    else
-        fprintf(f, "\n");
-}
-
-
-/* ANSI-C include data output functions
- */
-void writeHeader_C(FILE * f, char *name)
-{
-    if (name)
-        fprintf(f, "/* '%s' generated", name);
-    else
-        fprintf(f, "/* Generated");
-
-    fprintf(f, " by %s v%s\n" " */\n",
-        dmProgName, dmProgVersion);
-}
-
-void writeDecl_C(FILE * f, size_t len, char *name)
-{
-    fprintf(f, "%s %s[%u] = {\n",
-        optDataType, name, len);
-
-    printf("extern %s %s[%u];\n",
-        optDataType, name, len);
-}
-
-void writeData_C(FILE * f, Uint8 * buf, size_t len)
-{
-    size_t i;
-
-    for (i = 0; i < len; i++)
-    {
-        if (optFormatting)
-        {
-            if (optHexMode)
-                fprintf(f, "0x%.2x", buf[i]);
-            else
-                fprintf(f, "%3d", buf[i]);
-        }
-        else
-        {
-            if (optHexMode)
-                fprintf(f, "0x%x", buf[i]);
-            else
-                fprintf(f, "%d", buf[i]);
-        }
-
-        fprintf(f, ",");
-    }
-}
-
-void writeFooter_C(FILE * f, size_t len, char *name)
-{
-    (void) len;
-    (void) name;
-    fprintf(f, "};\n");
-}
-
-
-off_t dmGetFileSize(FILE *f)
-{
-    off_t len, pos = ftell(f);
-    fseeko(f, 0, SEEK_END);
-    len = ftell(f);
-    fseek(f, pos, SEEK_SET);
-    return len;
-}
-
-
-int main(int argc, char *argv[])
-{
-    FILE *sfile = NULL, *dfile = NULL;
-    off_t inSize;
-    Uint8 inBuf[RA_LINEBUF];
-    int tmpRes;
-
-    /* Initialize */
-    dmInitProg("data2inc", "Data to include converter", "0.6", NULL, NULL);
-    dmVerbosity = 0;
-
-    /* Parse arguments */
-    if (!dmArgsProcess(argc, argv, optList, optListN,
-        argHandleOpt, argHandleFile, TRUE))
-        exit(1);
-
-    /* Determine output type, if not specified */
-    if (optFormat == FMT_AUTO)
-    {
-        char *dext;
-
-        if (optOutFilename == NULL)
-        {
-            dmError("Output format not specified and no output filename given (try --help)\n");
-            exit(1);
-        }
-
-        /* Check filename extension */
-        dext = strrchr(optOutFilename, '.');
-        if (dext)
-        {
-            dext++;
-            if (!strcasecmp(dext, "c") || !strcasecmp(dext, "h") ||
-                !strcasecmp(dext, "cc") || !strcasecmp(dext, "cpp") ||
-                !strcasecmp(dext, "hpp") || !strcasecmp(dext, "c++"))
-                optFormat = FMT_C;
-            else
-                optFormat = FMT_ASM;
-        } else
-            optFormat = FMT_ASM;
-    }
-
-    /* Set functions */
-    switch (optFormat)
-    {
-        case FMT_ASM:
-            if (!optDataType)
-                optDataType = ".byte";
-
-            writeHeader = writeHeader_ASM;
-            writeDecl = writeDecl_ASM;
-            writeData = writeData_ASM;
-            writeFooter = writeFooter_ASM;
-            break;
-
-        case FMT_C:
-            if (!optDataType)
-                optDataType = "unsigned char";
-
-            writeHeader = writeHeader_C;
-            writeDecl = writeDecl_C;
-            writeData = writeData_C;
-            writeFooter = writeFooter_C;
-            break;
-
-        case FMT_AUTO:
-        default:
-            dmError("Internal error, FMT_AUTO at output function init.\n");
-            exit(2);
-    }
-
-    /* Open the files */
-    if (optInFilename == NULL)
-        sfile = stdin;
-    else
-    if ((sfile = fopen(optInFilename, "rb")) == NULL)
-    {
-        tmpRes = errno;
-        dmError("Error opening input file '%s'. (%s)\n",
-            optInFilename, strerror(tmpRes));
-        exit(3);
-    }
-
-    if (optOutFilename == NULL)
-        dfile = stdout;
-    else
-    if ((dfile = fopen(optOutFilename, "wa")) == NULL)
-    {
-        tmpRes = errno;
-        dmError("Error creating output file '%s'. (%s)\n",
-            optOutFilename, strerror(tmpRes));
-        exit(4);
-    }
-
-    /* Get sourcefile size */
-    inSize = dmGetFileSize(sfile);
-
-    /* Output header */
-    if (!optQuiet)
-        writeHeader(dfile, optOutFilename);
-
-    if (optAddLine)
-        fprintf(dfile, "%s\n", optAddLine);
-
-    /* Output declaration */
-    writeDecl(dfile, inSize, optObjName);
-
-    /* Output data */
-    while (!feof(sfile))
-    {
-        tmpRes = fread(inBuf, sizeof(Uint8), RA_LINEBUF, sfile);
-        if (tmpRes > 0)
-        {
-            if (optIndentation < 0)
-                fprintf(dfile, "\t");
-            else
-            if (optIndentation > 0)
-            {
-                int i;
-                for (i = 0; i < optIndentation; i++)
-                    fputs(" ", dfile);
-            }
-
-            writeData(dfile, inBuf, tmpRes);
-
-            fprintf(dfile, "\n");
-        }
-    }
-
-
-    /* Output footer */
-    writeFooter(dfile, inSize, optObjName);
-
-    /* Exit */
-    fclose(sfile);
-    fclose(dfile);
-
-    exit(0);
-    return 0;
-}
--- a/edgui.cpp	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,522 +0,0 @@
-//
-// Demo Editor -- Qt GUI setup parts and callbacks
-// (C) Copyright 2012 Matti 'ccr' Hämäläinen <ccr@tnsp.org>
-//
-#include "edmain.h"
-
-#include <QCloseEvent>
-#include <QToolBar>
-#include <QMenuBar>
-#include <QStatusBar>
-#include <QMenu>
-#include <QProgressBar>
-#include <QFileDialog>
-#include <QHBoxLayout>
-#include <QVBoxLayout>
-
-
-
-void DemoEditor::updateMenuStates()
-{
-    // Set window title based on document filename and changed status
-    QString name;
-
-    if (!currTimeline || currTimeline->filename.isEmpty())
-        name = DOC_DEF_FILENAME;
-    else
-        name = currTimeline->filename;
-    
-    if (currTimeline != NULL)
-    {
-        if (currTimeline->tl != NULL && currTimeline->tl->name != NULL)
-            name += " (" + QString(currTimeline->tl->name) + ")";
-
-        if (currTimeline->touched())
-            name = "*" + name;
-    }
-
-    setWindowTitle(name + " - " + QCoreApplication::applicationName());
-
-    // Enable menu items based on states
-    menuActSave->setEnabled(currTimeline != NULL&& currTimeline->touched());
-    menuActSaveAs->setEnabled(currTimeline != NULL);
-    
-    // Enable undo/redo items and set their texts based on history status
-    int historyLevels = undoHistory.size();
-    QString itemText;
-    bool itemEnabled;
-    
-    if (undoHistoryPos >= 0 && undoHistoryPos < historyLevels)
-    {
-        itemText = " " + undoHistory.at(undoHistoryPos)->state();
-        itemEnabled = true;
-    }
-    else
-    {
-        itemText = "";
-        itemEnabled = false;
-    }
-    
-    menuActRedo->setEnabled(itemEnabled);
-    menuActRedo->setText("&Redo" + itemText);
-
-    if (undoHistoryPos > 0 && historyLevels > 0)
-    {
-        itemText = " " + undoHistory.at(undoHistoryPos - 1)->state();
-        itemEnabled = true;
-    }
-    else
-    {
-        itemText = "";
-        itemEnabled = false;
-    }
-    
-    menuActUndo->setEnabled(itemEnabled);
-    menuActUndo->setText("&Undo" + itemText);
-
-    update();
-}
-
-
-//
-// Show about dialog
-//
-void DemoEditor::actionAboutBox()
-{
-    QMessageBox::about(this,
-    "About "+ QCoreApplication::applicationName(),
-    "<h1>" + QCoreApplication::applicationName() +
-    " v"+ QCoreApplication::applicationVersion() +"</h1><br>\n"
-    "(C) Copyright 2012 Matti 'ccr' H&auml;m&auml;l&auml;inen<br>\n"
-    "<br>\n"
-    "<b>A demo editor TNSP dmlib engine.</b><br>\n");
-}
-
-
-//
-// Show a dialog inquiring the user whether to save the current
-// document if it has been modified since last save/load.
-//
-QMessageBox::StandardButton DemoEditor::showDocumentModifiedDialog()
-{
-    return QMessageBox::question(this,
-        "The document has been modified.",
-        "Do you want to save your changes?",
-        QMessageBox::Discard | QMessageBox::Cancel | QMessageBox::Save,
-        QMessageBox::Save);
-}
-
-
-//
-// Generic error/warning dialog
-//
-void DemoEditor::showFileErrorDialog(QString operation, int code)
-{
-/*
-    QString msg;
-    
-    QMessageBox::error(this,
-    "A non-critical error occured",
-    operation
-*/
-}
-
-
-void DemoEditor::actionFileNew()
-{
-    bool okToCreate = true;
-
-    if (currTimeline != NULL && currTimeline->touched())
-    {
-        okToCreate = false;
-        switch (showDocumentModifiedDialog())
-        {
-            case QMessageBox::Discard:
-                okToCreate = true;
-                break;
-            
-            case QMessageBox::Save:
-                actionFileSave();
-                if (!currTimeline->touched())
-                {
-                    QMessageBox::information(this, 
-                        "Document saved",
-                        "The document was saved as " + currTimeline->filename);
-
-                    okToCreate = true;
-                }
-                break;
-
-            default:
-                break;
-        }
-    }
-    
-    if (okToCreate)
-        createNewFile();
-}
-
-
-void DemoEditor::actionFileOpen()
-{
-    bool okToOpen = true;
-
-    if (currTimeline != NULL && currTimeline->touched())
-    {
-        okToOpen = false;
-
-        switch (showDocumentModifiedDialog())
-        {
-            case QMessageBox::Discard:
-                okToOpen = true;
-                break;
-            
-            case QMessageBox::Save:
-                actionFileSave();
-                if (!currTimeline->touched())
-                {
-                    QMessageBox::information(this, 
-                        "Document saved",
-                        "The document was saved as " + currTimeline->filename);
-                    
-                    okToOpen = true;
-                }
-                break;
-
-            default:
-                break;
-        }
-    }
-        
-    if (okToOpen)
-    {
-        QFileDialog fdialog(this);
-
-        fdialog.setAcceptMode(QFileDialog::AcceptOpen);
-        fdialog.setFileMode(QFileDialog::ExistingFile);
-        fdialog.setNameFilter("Demo timeline files (*.demo)");
-        fdialog.setDefaultSuffix("demo");
-
-        if (fdialog.exec())
-            readFromFile(fdialog.selectedFiles()[0]);
-    }
-}
-
-
-void DemoEditor::actionFileSaveAs()
-{
-    if (!currTimeline)
-    {
-        qDebug() << "DemoEditor::actionFileSaveAs(): currTimeline == null";
-        return;
-    }
-
-    QFileDialog fdialog(this);
-
-    fdialog.setAcceptMode(QFileDialog::AcceptSave);
-    fdialog.setFileMode(QFileDialog::AnyFile);
-    fdialog.setNameFilter("Demo files (*.demo)");
-    fdialog.setDefaultSuffix("demo");
-    fdialog.setConfirmOverwrite(true);
-
-    fdialog.selectFile(currTimeline->filename.isEmpty() ? currTimeline->filename : DOC_DEF_FILENAME);
-    
-    if (fdialog.exec())
-        saveToFile(fdialog.selectedFiles()[0]);
-}
-
-
-void DemoEditor::actionFileSave()
-{
-    if (!currTimeline)
-    {
-        qDebug() << "DemoEditor::actionFileSave(): currTimeline == null";
-        return;
-    }
-
-    // If filename has been set, save .. otherwise go to save as
-    if (!currTimeline->filename.isEmpty())
-        saveToFile(currTimeline->filename);
-    else
-        actionFileSaveAs();
-}
-
-
-void DemoEditor::closeEvent(QCloseEvent *event)
-{
-    bool okToClose = true;
-
-    if (currTimeline && currTimeline->touched())
-    {
-        okToClose = false;
-        switch (showDocumentModifiedDialog())
-        {
-            case QMessageBox::Discard:
-                okToClose = true;
-                break;
-            
-            case QMessageBox::Save:
-                actionFileSave();
-                if (!currTimeline->touched())
-                    okToClose = true;
-                break;
-            
-            default:
-                break;
-        }
-    }
-    
-    if (okToClose)
-        event->accept();
-}
-
-
-//
-// Various menu actions
-//
-void DemoEditor::actionControlChanged(QAction *act)
-{
-//    demoView->setToolMode(act->data().toInt());
-}
-
-
-//
-// Update statusbar message text
-//
-void DemoEditor::statusMsg(QString message)
-{
-    statusBar()->showMessage(message, 0);
-}
-
-
-//
-// Set active element of an action group based on matching the data
-//
-void DemoEditor::setActionGroupChecked(QActionGroup *group, QVariant data)
-{
-    QList<QAction *> items = group->actions();
-
-    for (int i = 0; i < items.size(); i++)
-    {
-        QAction *act = items.at(i);
-        if (act->data() == data)
-        {
-            act->setChecked(true);
-            return;
-        }
-    }
-}
-
-
-//
-// Helper functions for creating GUI elements
-//
-QAction * DemoEditor::createToolButton(QActionGroup *group, QString name, QIcon icon, QString statustip, QVariant data)
-{
-    QAction *action = new QAction(icon, name, group);
-    
-    action->setStatusTip(statustip + ".");
-    action->setCheckable(true);
-    action->setData(data);
-    
-    return action;
-}
-
-
-QAction * DemoEditor::createMenuAction(QString name, const QKeySequence &shortcut, QString tooltip)
-{
-    QAction *action = new QAction(name, this);
-    
-    if (shortcut != QKeySequence(QKeySequence::UnknownKey))
-        action->setShortcut(shortcut);
-    
-    if (!tooltip.isNull())
-        action->setStatusTip(tooltip + ".");
-
-    return action;
-}
-
-
-QAction * DemoEditor::createMenuGroupAction(QMenu *menu, QActionGroup *group, QString name, const QKeySequence &shortcut, QString tooltip, QVariant data)
-{
-    QAction *action = createMenuAction(name, shortcut, tooltip);
-    
-    action->setCheckable(true);
-    action->setData(data);
-
-    menu->addAction(action);
-    group->addAction(action);
-
-    return action;
-}
-
-
-#define MCONNECT(menu, act, slot) do { connect(act, SIGNAL(triggered()), this, SLOT(slot)); menu->addAction(act); } while(0)
-
-
-//
-// Create GUI elements
-//
-void DemoEditor::createMainGUI()
-{
-    QAction *act;
-    QMenu *fileMenu, *editMenu, *viewMenu, *helpMenu;
-
-    qDebug() << "- Constructing menus";
-
-    //
-    // File menu
-    //
-    fileMenu = menuBar()->addMenu("&File");
-
-    act = createMenuAction("&New", QKeySequence::New, "Create a new demo timeline");
-    MCONNECT(fileMenu, act, actionFileNew());
-
-    menuActOpen = createMenuAction("&Open", QKeySequence::Open, "Open a demo timeline file");
-    MCONNECT(fileMenu, menuActOpen, actionFileOpen());
-
-    menuActSave = createMenuAction("&Save", QKeySequence::Save, "Save demo timeline");
-    MCONNECT(fileMenu, menuActSave, actionFileSave());
-
-    menuActSaveAs = createMenuAction("Save &as", QKeySequence::SaveAs, "Save demo timeline as a new file");
-    MCONNECT(fileMenu, menuActSaveAs, actionFileSaveAs());
-
-    fileMenu->addSeparator();
-
-    QKeySequence qseq(Qt::CTRL + Qt::Key_Q);
-    act = createMenuAction("&Quit", qseq, "Exit application");
-    MCONNECT(fileMenu, act, close());
-
-
-    //
-    // Edit menu
-    //
-    editMenu = menuBar()->addMenu("&Edit");
-
-    menuActUndo = createMenuAction("&Undo",  QKeySequence::Undo, "Undo last change");
-    MCONNECT(editMenu, menuActUndo, performUndo());
-
-    menuActRedo = createMenuAction("&Redo",  QKeySequence::Redo, "Redo last change");
-    MCONNECT(editMenu, menuActRedo, performRedo());
-
-#if 0
-    editMenu->addSeparator();
-
-    menuActCut = createMenuAction("Cu&t",  QKeySequence::Cut, "Cut object");
-    MCONNECT(editMenu, menuActCut, actionCut());
-
-    menuActCopy = createMenuAction("&Copy",  QKeySequence::Copy, "Copy object");
-    MCONNECT(editMenu, menuActCopy, actionCopy());
-
-    menuActPaste = createMenuAction("&Paste", QKeySequence::Paste, "Paste object");
-    MCONNECT(editMenu, menuActPaste, actionPaste());
-
-    menuActDelete = createMenuAction("&Delete", QKeySequence::Delete, "Delete object");
-    MCONNECT(editMenu, menuActDelete, actionDelete());
-#endif
-
-    //
-    // Help menu
-    //
-    helpMenu = menuBar()->addMenu("&Help");
-    act = createMenuAction("About", 0, "Show information about application");
-    MCONNECT(helpMenu, act, actionAboutBox());
-
-
-
-    //
-    // Controls toolbar
-    //
-    qDebug() << "- Constructing toolbars";
-
-    actGroupControls = new QActionGroup(this);
-    actGroupControls->setExclusive(true);
-    connect(actGroupControls, SIGNAL(triggered(QAction*)), this, SLOT(actionControlChanged(QAction *)));
-    
-    createToolButton(actGroupControls, "Rewind", QIcon("rewind.png"),
-        "Rewind to start of the timeline", CTRL_REWIND);
-
-    createToolButton(actGroupControls, "Play start", QIcon("play1.png"),
-        "Play from start", CTRL_PLAY_START);
-
-    createToolButton(actGroupControls, "Play current", QIcon("play2.png"),
-        "Play from current position", CTRL_PLAY_CURRENT);
-
-    createToolButton(actGroupControls, "Pause", QIcon("pause.png"),
-        "Pause", CTRL_PAUSE);
-
-
-    QToolBar *controlButtons = new QToolBar("Player controls", this);
-    controlButtons->setMovable(false);
-    controlButtons->setFloatable(false);
-    controlButtons->setIconSize(QSize(CTRL_ICON_SIZE, CTRL_ICON_SIZE));
-    controlButtons->setToolButtonStyle(Qt::ToolButtonIconOnly);
-    controlButtons->addActions(actGroupControls->actions());
-
-
-    //
-    // Effect resource view
-    //
-    qDebug() << "- Constructing effects resource view";
-
-    resourceView = new QTableView(this);
-    resourceView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
-    resourceView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
-    resourceView->setSelectionMode(QAbstractItemView::SingleSelection);
-    resourceView->setSelectionBehavior(QAbstractItemView::SelectRows);
-    resourceModel = new QEDResourceModel(this);
-    resourceView->setModel(resourceModel);
-
-    //
-    // Construct the main screen
-    //
-    qDebug() << "- Constructing main screen layout";
-
-    QWidget *sideVBoxContainer = new QWidget();
-    QSizePolicy sideVBoxPolicy(QSizePolicy::Fixed, QSizePolicy::Ignored);
-    sideVBoxPolicy.setHeightForWidth(sideVBoxContainer->sizePolicy().hasHeightForWidth());
-    sideVBoxContainer->setSizePolicy(sideVBoxPolicy);
-
-    QVBoxLayout *sideVBox = new QVBoxLayout(sideVBoxContainer);
-    sideVBox->setSpacing(0);
-    sideVBox->setContentsMargins(0, 0, 0, 0);
-    sideVBox->addWidget(resourceView);
-    sideVBox->addWidget(controlButtons);
-
-
-    QWidget *holder = new QWidget();
-    QVBoxLayout *verticalSplitter = new QVBoxLayout(holder);
-    QHBoxLayout *horizSplitter = new QHBoxLayout();
-
-
-    timelineScrollBar = new QScrollBar(Qt::Horizontal);
-    connect(timelineScrollBar, SIGNAL(valueChanged(int)), this, SLOT(actionTimelineScrollChanged(int)));
-    
-    timelineAudioTrack = new QEDWaveTrackView();
-    connect(timelineAudioTrack, SIGNAL(offsetChanged(float)), this, SLOT(actionOffsetChanged(float)));
-    connect(timelineAudioTrack, SIGNAL(timeChanged(float)), this, SLOT(actionTimeChanged(float)));
-
-    timelineView = new QEDTimelineView();
-    connect(timelineView, SIGNAL(timelineChanged()), this, SLOT(actionTimelineChanged()));
-
-    QScrollArea *scrollArea = new QScrollArea();
-    scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
-    scrollArea->setWidget(timelineView);
-    scrollArea->setWidgetResizable(true);
-
-
-    demoView = new QEDSWDemoView(this);
-
-    verticalSplitter->addLayout(horizSplitter);
-    verticalSplitter->addWidget(scrollArea);
-    verticalSplitter->addWidget(timelineAudioTrack);
-    verticalSplitter->addWidget(timelineScrollBar);
-
-    horizSplitter->addWidget(sideVBoxContainer);
-    horizSplitter->addWidget(demoView);
-
-
-    updateTimelineView();
-
-    setCentralWidget(holder);
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/editor/edgui.cpp	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,522 @@
+//
+// Demo Editor -- Qt GUI setup parts and callbacks
+// (C) Copyright 2012 Matti 'ccr' Hämäläinen <ccr@tnsp.org>
+//
+#include "edmain.h"
+
+#include <QCloseEvent>
+#include <QToolBar>
+#include <QMenuBar>
+#include <QStatusBar>
+#include <QMenu>
+#include <QProgressBar>
+#include <QFileDialog>
+#include <QHBoxLayout>
+#include <QVBoxLayout>
+
+
+
+void DemoEditor::updateMenuStates()
+{
+    // Set window title based on document filename and changed status
+    QString name;
+
+    if (!currTimeline || currTimeline->filename.isEmpty())
+        name = DOC_DEF_FILENAME;
+    else
+        name = currTimeline->filename;
+    
+    if (currTimeline != NULL)
+    {
+        if (currTimeline->tl != NULL && currTimeline->tl->name != NULL)
+            name += " (" + QString(currTimeline->tl->name) + ")";
+
+        if (currTimeline->touched())
+            name = "*" + name;
+    }
+
+    setWindowTitle(name + " - " + QCoreApplication::applicationName());
+
+    // Enable menu items based on states
+    menuActSave->setEnabled(currTimeline != NULL&& currTimeline->touched());
+    menuActSaveAs->setEnabled(currTimeline != NULL);
+    
+    // Enable undo/redo items and set their texts based on history status
+    int historyLevels = undoHistory.size();
+    QString itemText;
+    bool itemEnabled;
+    
+    if (undoHistoryPos >= 0 && undoHistoryPos < historyLevels)
+    {
+        itemText = " " + undoHistory.at(undoHistoryPos)->state();
+        itemEnabled = true;
+    }
+    else
+    {
+        itemText = "";
+        itemEnabled = false;
+    }
+    
+    menuActRedo->setEnabled(itemEnabled);
+    menuActRedo->setText("&Redo" + itemText);
+
+    if (undoHistoryPos > 0 && historyLevels > 0)
+    {
+        itemText = " " + undoHistory.at(undoHistoryPos - 1)->state();
+        itemEnabled = true;
+    }
+    else
+    {
+        itemText = "";
+        itemEnabled = false;
+    }
+    
+    menuActUndo->setEnabled(itemEnabled);
+    menuActUndo->setText("&Undo" + itemText);
+
+    update();
+}
+
+
+//
+// Show about dialog
+//
+void DemoEditor::actionAboutBox()
+{
+    QMessageBox::about(this,
+    "About "+ QCoreApplication::applicationName(),
+    "<h1>" + QCoreApplication::applicationName() +
+    " v"+ QCoreApplication::applicationVersion() +"</h1><br>\n"
+    "(C) Copyright 2012 Matti 'ccr' H&auml;m&auml;l&auml;inen<br>\n"
+    "<br>\n"
+    "<b>A demo editor TNSP dmlib engine.</b><br>\n");
+}
+
+
+//
+// Show a dialog inquiring the user whether to save the current
+// document if it has been modified since last save/load.
+//
+QMessageBox::StandardButton DemoEditor::showDocumentModifiedDialog()
+{
+    return QMessageBox::question(this,
+        "The document has been modified.",
+        "Do you want to save your changes?",
+        QMessageBox::Discard | QMessageBox::Cancel | QMessageBox::Save,
+        QMessageBox::Save);
+}
+
+
+//
+// Generic error/warning dialog
+//
+void DemoEditor::showFileErrorDialog(QString operation, int code)
+{
+/*
+    QString msg;
+    
+    QMessageBox::error(this,
+    "A non-critical error occured",
+    operation
+*/
+}
+
+
+void DemoEditor::actionFileNew()
+{
+    bool okToCreate = true;
+
+    if (currTimeline != NULL && currTimeline->touched())
+    {
+        okToCreate = false;
+        switch (showDocumentModifiedDialog())
+        {
+            case QMessageBox::Discard:
+                okToCreate = true;
+                break;
+            
+            case QMessageBox::Save:
+                actionFileSave();
+                if (!currTimeline->touched())
+                {
+                    QMessageBox::information(this, 
+                        "Document saved",
+                        "The document was saved as " + currTimeline->filename);
+
+                    okToCreate = true;
+                }
+                break;
+
+            default:
+                break;
+        }
+    }
+    
+    if (okToCreate)
+        createNewFile();
+}
+
+
+void DemoEditor::actionFileOpen()
+{
+    bool okToOpen = true;
+
+    if (currTimeline != NULL && currTimeline->touched())
+    {
+        okToOpen = false;
+
+        switch (showDocumentModifiedDialog())
+        {
+            case QMessageBox::Discard:
+                okToOpen = true;
+                break;
+            
+            case QMessageBox::Save:
+                actionFileSave();
+                if (!currTimeline->touched())
+                {
+                    QMessageBox::information(this, 
+                        "Document saved",
+                        "The document was saved as " + currTimeline->filename);
+                    
+                    okToOpen = true;
+                }
+                break;
+
+            default:
+                break;
+        }
+    }
+        
+    if (okToOpen)
+    {
+        QFileDialog fdialog(this);
+
+        fdialog.setAcceptMode(QFileDialog::AcceptOpen);
+        fdialog.setFileMode(QFileDialog::ExistingFile);
+        fdialog.setNameFilter("Demo timeline files (*.demo)");
+        fdialog.setDefaultSuffix("demo");
+
+        if (fdialog.exec())
+            readFromFile(fdialog.selectedFiles()[0]);
+    }
+}
+
+
+void DemoEditor::actionFileSaveAs()
+{
+    if (!currTimeline)
+    {
+        qDebug() << "DemoEditor::actionFileSaveAs(): currTimeline == null";
+        return;
+    }
+
+    QFileDialog fdialog(this);
+
+    fdialog.setAcceptMode(QFileDialog::AcceptSave);
+    fdialog.setFileMode(QFileDialog::AnyFile);
+    fdialog.setNameFilter("Demo files (*.demo)");
+    fdialog.setDefaultSuffix("demo");
+    fdialog.setConfirmOverwrite(true);
+
+    fdialog.selectFile(currTimeline->filename.isEmpty() ? currTimeline->filename : DOC_DEF_FILENAME);
+    
+    if (fdialog.exec())
+        saveToFile(fdialog.selectedFiles()[0]);
+}
+
+
+void DemoEditor::actionFileSave()
+{
+    if (!currTimeline)
+    {
+        qDebug() << "DemoEditor::actionFileSave(): currTimeline == null";
+        return;
+    }
+
+    // If filename has been set, save .. otherwise go to save as
+    if (!currTimeline->filename.isEmpty())
+        saveToFile(currTimeline->filename);
+    else
+        actionFileSaveAs();
+}
+
+
+void DemoEditor::closeEvent(QCloseEvent *event)
+{
+    bool okToClose = true;
+
+    if (currTimeline && currTimeline->touched())
+    {
+        okToClose = false;
+        switch (showDocumentModifiedDialog())
+        {
+            case QMessageBox::Discard:
+                okToClose = true;
+                break;
+            
+            case QMessageBox::Save:
+                actionFileSave();
+                if (!currTimeline->touched())
+                    okToClose = true;
+                break;
+            
+            default:
+                break;
+        }
+    }
+    
+    if (okToClose)
+        event->accept();
+}
+
+
+//
+// Various menu actions
+//
+void DemoEditor::actionControlChanged(QAction *act)
+{
+//    demoView->setToolMode(act->data().toInt());
+}
+
+
+//
+// Update statusbar message text
+//
+void DemoEditor::statusMsg(QString message)
+{
+    statusBar()->showMessage(message, 0);
+}
+
+
+//
+// Set active element of an action group based on matching the data
+//
+void DemoEditor::setActionGroupChecked(QActionGroup *group, QVariant data)
+{
+    QList<QAction *> items = group->actions();
+
+    for (int i = 0; i < items.size(); i++)
+    {
+        QAction *act = items.at(i);
+        if (act->data() == data)
+        {
+            act->setChecked(true);
+            return;
+        }
+    }
+}
+
+
+//
+// Helper functions for creating GUI elements
+//
+QAction * DemoEditor::createToolButton(QActionGroup *group, QString name, QIcon icon, QString statustip, QVariant data)
+{
+    QAction *action = new QAction(icon, name, group);
+    
+    action->setStatusTip(statustip + ".");
+    action->setCheckable(true);
+    action->setData(data);
+    
+    return action;
+}
+
+
+QAction * DemoEditor::createMenuAction(QString name, const QKeySequence &shortcut, QString tooltip)
+{
+    QAction *action = new QAction(name, this);
+    
+    if (shortcut != QKeySequence(QKeySequence::UnknownKey))
+        action->setShortcut(shortcut);
+    
+    if (!tooltip.isNull())
+        action->setStatusTip(tooltip + ".");
+
+    return action;
+}
+
+
+QAction * DemoEditor::createMenuGroupAction(QMenu *menu, QActionGroup *group, QString name, const QKeySequence &shortcut, QString tooltip, QVariant data)
+{
+    QAction *action = createMenuAction(name, shortcut, tooltip);
+    
+    action->setCheckable(true);
+    action->setData(data);
+
+    menu->addAction(action);
+    group->addAction(action);
+
+    return action;
+}
+
+
+#define MCONNECT(menu, act, slot) do { connect(act, SIGNAL(triggered()), this, SLOT(slot)); menu->addAction(act); } while(0)
+
+
+//
+// Create GUI elements
+//
+void DemoEditor::createMainGUI()
+{
+    QAction *act;
+    QMenu *fileMenu, *editMenu, *viewMenu, *helpMenu;
+
+    qDebug() << "- Constructing menus";
+
+    //
+    // File menu
+    //
+    fileMenu = menuBar()->addMenu("&File");
+
+    act = createMenuAction("&New", QKeySequence::New, "Create a new demo timeline");
+    MCONNECT(fileMenu, act, actionFileNew());
+
+    menuActOpen = createMenuAction("&Open", QKeySequence::Open, "Open a demo timeline file");
+    MCONNECT(fileMenu, menuActOpen, actionFileOpen());
+
+    menuActSave = createMenuAction("&Save", QKeySequence::Save, "Save demo timeline");
+    MCONNECT(fileMenu, menuActSave, actionFileSave());
+
+    menuActSaveAs = createMenuAction("Save &as", QKeySequence::SaveAs, "Save demo timeline as a new file");
+    MCONNECT(fileMenu, menuActSaveAs, actionFileSaveAs());
+
+    fileMenu->addSeparator();
+
+    QKeySequence qseq(Qt::CTRL + Qt::Key_Q);
+    act = createMenuAction("&Quit", qseq, "Exit application");
+    MCONNECT(fileMenu, act, close());
+
+
+    //
+    // Edit menu
+    //
+    editMenu = menuBar()->addMenu("&Edit");
+
+    menuActUndo = createMenuAction("&Undo",  QKeySequence::Undo, "Undo last change");
+    MCONNECT(editMenu, menuActUndo, performUndo());
+
+    menuActRedo = createMenuAction("&Redo",  QKeySequence::Redo, "Redo last change");
+    MCONNECT(editMenu, menuActRedo, performRedo());
+
+#if 0
+    editMenu->addSeparator();
+
+    menuActCut = createMenuAction("Cu&t",  QKeySequence::Cut, "Cut object");
+    MCONNECT(editMenu, menuActCut, actionCut());
+
+    menuActCopy = createMenuAction("&Copy",  QKeySequence::Copy, "Copy object");
+    MCONNECT(editMenu, menuActCopy, actionCopy());
+
+    menuActPaste = createMenuAction("&Paste", QKeySequence::Paste, "Paste object");
+    MCONNECT(editMenu, menuActPaste, actionPaste());
+
+    menuActDelete = createMenuAction("&Delete", QKeySequence::Delete, "Delete object");
+    MCONNECT(editMenu, menuActDelete, actionDelete());
+#endif
+
+    //
+    // Help menu
+    //
+    helpMenu = menuBar()->addMenu("&Help");
+    act = createMenuAction("About", 0, "Show information about application");
+    MCONNECT(helpMenu, act, actionAboutBox());
+
+
+
+    //
+    // Controls toolbar
+    //
+    qDebug() << "- Constructing toolbars";
+
+    actGroupControls = new QActionGroup(this);
+    actGroupControls->setExclusive(true);
+    connect(actGroupControls, SIGNAL(triggered(QAction*)), this, SLOT(actionControlChanged(QAction *)));
+    
+    createToolButton(actGroupControls, "Rewind", QIcon("rewind.png"),
+        "Rewind to start of the timeline", CTRL_REWIND);
+
+    createToolButton(actGroupControls, "Play start", QIcon("play1.png"),
+        "Play from start", CTRL_PLAY_START);
+
+    createToolButton(actGroupControls, "Play current", QIcon("play2.png"),
+        "Play from current position", CTRL_PLAY_CURRENT);
+
+    createToolButton(actGroupControls, "Pause", QIcon("pause.png"),
+        "Pause", CTRL_PAUSE);
+
+
+    QToolBar *controlButtons = new QToolBar("Player controls", this);
+    controlButtons->setMovable(false);
+    controlButtons->setFloatable(false);
+    controlButtons->setIconSize(QSize(CTRL_ICON_SIZE, CTRL_ICON_SIZE));
+    controlButtons->setToolButtonStyle(Qt::ToolButtonIconOnly);
+    controlButtons->addActions(actGroupControls->actions());
+
+
+    //
+    // Effect resource view
+    //
+    qDebug() << "- Constructing effects resource view";
+
+    resourceView = new QTableView(this);
+    resourceView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+    resourceView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+    resourceView->setSelectionMode(QAbstractItemView::SingleSelection);
+    resourceView->setSelectionBehavior(QAbstractItemView::SelectRows);
+    resourceModel = new QEDResourceModel(this);
+    resourceView->setModel(resourceModel);
+
+    //
+    // Construct the main screen
+    //
+    qDebug() << "- Constructing main screen layout";
+
+    QWidget *sideVBoxContainer = new QWidget();
+    QSizePolicy sideVBoxPolicy(QSizePolicy::Fixed, QSizePolicy::Ignored);
+    sideVBoxPolicy.setHeightForWidth(sideVBoxContainer->sizePolicy().hasHeightForWidth());
+    sideVBoxContainer->setSizePolicy(sideVBoxPolicy);
+
+    QVBoxLayout *sideVBox = new QVBoxLayout(sideVBoxContainer);
+    sideVBox->setSpacing(0);
+    sideVBox->setContentsMargins(0, 0, 0, 0);
+    sideVBox->addWidget(resourceView);
+    sideVBox->addWidget(controlButtons);
+
+
+    QWidget *holder = new QWidget();
+    QVBoxLayout *verticalSplitter = new QVBoxLayout(holder);
+    QHBoxLayout *horizSplitter = new QHBoxLayout();
+
+
+    timelineScrollBar = new QScrollBar(Qt::Horizontal);
+    connect(timelineScrollBar, SIGNAL(valueChanged(int)), this, SLOT(actionTimelineScrollChanged(int)));
+    
+    timelineAudioTrack = new QEDWaveTrackView();
+    connect(timelineAudioTrack, SIGNAL(offsetChanged(float)), this, SLOT(actionOffsetChanged(float)));
+    connect(timelineAudioTrack, SIGNAL(timeChanged(float)), this, SLOT(actionTimeChanged(float)));
+
+    timelineView = new QEDTimelineView();
+    connect(timelineView, SIGNAL(timelineChanged()), this, SLOT(actionTimelineChanged()));
+
+    QScrollArea *scrollArea = new QScrollArea();
+    scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
+    scrollArea->setWidget(timelineView);
+    scrollArea->setWidgetResizable(true);
+
+
+    demoView = new QEDSWDemoView(this);
+
+    verticalSplitter->addLayout(horizSplitter);
+    verticalSplitter->addWidget(scrollArea);
+    verticalSplitter->addWidget(timelineAudioTrack);
+    verticalSplitter->addWidget(timelineScrollBar);
+
+    horizSplitter->addWidget(sideVBoxContainer);
+    horizSplitter->addWidget(demoView);
+
+
+    updateTimelineView();
+
+    setCentralWidget(holder);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/editor/edmain.cpp	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,531 @@
+//
+// Demo Editor -- Main program
+// (C) Copyright 2012 Matti 'ccr' Hämäläinen <ccr@tnsp.org>
+//
+#include <SDL.h>
+#include "dmengine.h"
+#include "edmain.h"
+#include <QSettings>
+#include <QGLWidget>
+
+
+int main(int argc, char *argv[])
+{
+    dmVerbosity = 5;
+
+    QApplication app(argc, argv);
+
+    app.setOrganizationName("TNSP");
+    app.setOrganizationDomain("tnsp.org");
+    app.setApplicationName(PROGRAM_NAME);
+    app.setApplicationVersion(PROGRAM_VERSION);
+
+    DemoEditor mainWin;
+
+    mainWin.show();
+          
+    return app.exec();
+}
+
+
+void engineAudioCallback(void *userdata, Uint8 * stream, int len)
+{
+    DMEngineData *engine = (DMEngineData *) userdata;
+
+    if (engine->paused)
+    {
+        memset(stream, 0, len);
+    }
+    else
+#ifdef DM_USE_JSS
+    {
+        if (engine->dev != NULL)
+            jvmRenderAudio(engine->dev, stream,
+                           len / jvmGetSampleSize(engine->dev));
+    }
+#endif
+#ifdef DM_USE_TREMOR
+    if (engine->audioPos + len >= engine->audioRes->rdataSize)
+    {
+        engine->exitFlag = true;
+    }
+    else
+    {
+        memcpy(stream, (Uint8 *) engine->audioRes->rdata + engine->audioPos, len);
+        engine->audioPos += len;
+    }
+#endif
+}
+
+
+int DemoEditor::reopenResources()
+{
+    int err;
+
+    if ((err = dmres_init(&engine.resources, engine.optPackFilename, engine.optDataPath,
+                           engine.optResFlags, engineClassifier)) != DMERR_OK)
+    {
+        dmError("Could not initialize resource manager: %d, %s.\n",
+            err, dmErrorStr(err));
+    }
+    return err;
+}
+
+
+int DemoEditor::loadResources()
+{
+    int err, loaded, total;
+    err = dmres_preload(engine.resources, true, &loaded, &total);
+
+    while ((err = dmres_preload(engine.resources, false, &loaded, &total)) == DMERR_PROGRESS)
+    {
+        // Show a nice progress bar while loading
+        if (total > 0 && (loaded % 2) == 0)
+        {
+/*
+            if ((err = engineShowProgress(loaded, total)) != DMERR_OK)
+                return err;
+*/
+        }
+    }
+    return DMERR_OK;
+}
+
+
+DemoEditor::DemoEditor()
+{
+    int err;
+
+    resize(1024, 768);
+    setWindowTitle(QCoreApplication::applicationName());
+
+    memset(&engine, 0, sizeof(engine));
+    initSDL = FALSE;
+    currTimeline = NULL;
+
+    // Pre-initialization
+    if ((err = demoPreInit(&engine)) != DMERR_OK)
+        goto error_exit;
+
+    // Initialize SDL components
+    dmPrint(1, "Initializing libSDL.\n");
+    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) != 0)
+    {
+        dmError("Could not initialize SDL: %s\n", SDL_GetError());
+        goto error_exit;
+    }
+    initSDL = true;
+
+    // Initialize audio parts
+    if (engine.optAfmt.freq == 0 && engine.optAfmt.channels == 0)
+    {
+        // Defaults, if none seem to be set
+        engine.optAfmt.freq = 44100;
+        engine.optAfmt.format = AUDIO_S16SYS;
+        engine.optAfmt.channels = 2;
+        engine.optAfmt.samples = engine.optAfmt.freq / 16;
+    }
+
+#ifdef DM_USE_JSS
+    jssInit();
+
+    switch (engine.optAfmt.format)
+    {
+        case AUDIO_S16SYS:
+            engine.jss_format = JSS_AUDIO_S16;
+            break;
+        case AUDIO_U16SYS:
+            engine.jss_format = JSS_AUDIO_U16;
+            break;
+        case AUDIO_S8:
+            engine.jss_format = JSS_AUDIO_S8;
+            break;
+        case AUDIO_U8:
+            engine.jss_format = JSS_AUDIO_U8;
+            break;
+    }
+
+    dmPrint(1, "Initializing miniJSS mixer with fmt=%d, chn=%d, freq=%d\n",
+            engine.jss_format, engine.optAfmt.channels, engine.optAfmt.freq);
+
+    if ((engine.dev =
+         jvmInit(engine.jss_format, engine.optAfmt.channels,
+                 engine.optAfmt.freq, JMIX_AUTO)) == NULL)
+    {
+        dmError("jvmInit() returned NULL, voi perkele.\n");
+        goto error_exit;
+    }
+
+    if ((engine.plr = jmpInit(engine.dev)) == NULL)
+    {
+        dmError("jmpInit() returned NULL\n");
+        goto error_exit;
+    }
+#endif
+
+    // Initialize SDL audio
+    dmPrint(1, "Trying to init SDL audio with: fmt=%d, chn=%d, freq=%d\n",
+            engine.optAfmt.format, engine.optAfmt.channels,
+            engine.optAfmt.freq);
+
+    engine.optAfmt.callback = engineAudioCallback;
+
+    if (SDL_OpenAudio(&engine.optAfmt, NULL) < 0)
+    {
+        dmError("Couldn't open SDL audio: %s\n", SDL_GetError());
+        goto error_exit;
+    }
+
+    // Initialize SDL video
+    if (engine.demoInitPreVideo != NULL &&
+        (err = engine.demoInitPreVideo(&engine)) != DMERR_OK)
+    {
+        dmError("demoInitPreVideo() failed, %d: %s\n", err, dmErrorStr(err));
+        goto error_exit;
+    }
+
+    dmPrint(1, "Initializing SDL video %d x %d x %dbpp, flags=0x%08x\n",
+        engine.optVidWidth, engine.optVidHeight, engine.optVidDepth, engine.optVFlags);
+
+    engine.screen = SDL_CreateRGBSurface(SDL_SWSURFACE, engine.optVidWidth, engine.optVidHeight, engine.optVidDepth, 0, 0, 0, 0);
+    if (engine.screen == NULL)
+    {
+        dmError("Could not allocate video backbuffer surface.\n");
+        goto error_exit;
+    }
+
+    if (engine.demoInitPostVideo != NULL &&
+        (err = engine.demoInitPostVideo(&engine)) != DMERR_OK)
+    {
+        dmError("demoInitPostVideo() failed, %d: %s\n", err, dmErrorStr(err));
+        goto error_exit;
+    }
+
+
+error_exit:
+
+    // Setup GUI elements
+    createMainGUI();
+    createNewFile();
+    timelineView->setTimeline(currTimeline);
+    settingsRestore();
+    initEffectsAndResources();
+    statusMsg("Application started.");
+}
+
+
+DemoEditor::~DemoEditor()
+{
+    statusMsg("Shutting down.");
+
+    settingsSave();
+    delete demoView;
+    historyReset();
+
+    if (engine.screen)
+        SDL_FreeSurface(engine.screen);
+
+    SDL_LockAudio();
+    SDL_PauseAudio(1);
+#ifdef DM_USE_JSS
+    jmpClose(engine.plr);
+    jvmClose(engine.dev);
+    jssClose();
+#endif
+    SDL_UnlockAudio();
+
+    shutdownEffectsAndResources();
+
+    if (initSDL)
+        SDL_Quit();
+}
+
+
+int DemoEditor::getTimelineDuration()
+{
+    return timelineAudioTrack->getDuration();
+}
+
+
+void DemoEditor::updateResourceView()
+{
+}
+
+
+void DemoEditor::updateTimelineView()
+{
+    demoView->setEngineData(&engine);    
+
+    if (engine.audioRes != NULL)
+    {
+        timelineAudioTrack->setWaveform(
+            engine.audioRes->rdata, engine.audioRes->rdataSize,
+            engine.optAfmt.format, engine.optAfmt.channels,
+            engine.optAfmt.freq);
+    }
+
+    timelineAudioTrack->setOffset(currViewOffset);
+    timelineAudioTrack->setScale(currViewScale);
+    
+
+    timelineView->setTime(currFrameTime);
+    timelineView->setOffset(currViewOffset);
+    timelineView->setScale(currViewScale);
+
+    timelineScrollBar->setRange(0, getTimelineDuration());
+    timelineScrollBar->setValue(currViewOffset);
+}
+
+
+void DemoEditor::actionTimelineScrollChanged(int value)
+{   
+    currViewOffset = value;
+    updateTimelineView();
+}
+
+
+void DemoEditor::actionOffsetChanged(float value)
+{   
+    currViewOffset = value;
+    updateTimelineView();
+}
+
+
+void DemoEditor::actionTimeChanged(float value)
+{   
+    currFrameTime = value;
+    updateTimelineView();
+}
+
+
+void DemoEditor::actionTimelineChanged()
+{
+    updateMenuStates();
+    update();
+}
+
+
+int DemoEditor::initEffectsAndResources()
+{
+    int err;
+
+    currViewOffset = 0;
+    currFrameTime = 0;
+    currViewScale = 1.0f;
+    
+    // Initialize resource subsystem
+    statusMsg("Initializing resources subsystem.");
+    if ((err = reopenResources()) != DMERR_OK)
+        return err;
+
+    // Load resources
+    statusMsg("Loading resources, please wait...");
+    if ((err = loadResources()) != DMERR_OK)
+    {
+        dmError("Error loading resources, %d: %s.\n", err, dmErrorStr(err));
+        return err;
+    }
+
+
+    // Final initializations
+    statusMsg("Initializing custom demo data.");
+    if ((err = engine.demoInit(&engine)) != DMERR_OK)
+    {
+        dmError("Failure in demoInit(), %d: %s\n",
+            err, dmErrorStr(err));
+        return err;
+    }
+
+    // Initialize effects
+    statusMsg("Initializing effects ...");
+    if ((err = engineInitializeEffects(&engine)) != DMERR_OK)
+    {
+        dmError("Effects initialization failed, %d: %s\n",
+            err, dmErrorStr(err));
+        return err;
+    }
+
+    // Etc.
+    rehash();
+    return DMERR_OK;
+}
+
+
+void DemoEditor::shutdownEffectsAndResources()
+{
+    delete currTimeline;
+    dmFreePreparedTimelineData(engine.ptl);
+    engineShutdownEffects(&engine);
+    dmres_close(engine.resources);
+
+    if (engine.demoShutdown != NULL)
+        engine.demoShutdown(&engine);
+}
+
+
+void DemoEditor::rehash()
+{
+    timelineView->setTimeline(currTimeline);
+    updateResourceView();
+    updateTimelineView();
+    updateMenuStates();
+    update();
+}
+
+
+void DemoEditor::createNewFile()
+{
+    delete currTimeline;
+    currTimeline = new EDTimelineObject();
+
+    DMTimelineTrack *tr;
+    dmTimelineAddTrack(currTimeline->tl, &tr, "Penis");
+    
+    DMTimelineEvent *ev;
+    dmTimelineTrackAddEvent(tr, 500, 100, &ev);
+    
+    dmTimelineEventSetEffectByIndex(ev, 0);
+
+    historyReset();
+    updateMenuStates();
+    update();
+}
+
+
+void DemoEditor::readFromFile(QString filename)
+{
+    EDTimelineObject *tmp = new EDTimelineObject();
+    int ret = tmp->load(filename);
+    if (ret != DMERR_OK)
+    {
+        showFileErrorDialog("Loading demo blob file "+ filename, ret);
+        delete tmp;
+    }
+    else
+    {
+        delete currTimeline;
+        currTimeline = tmp;
+        rehash();
+    }
+}
+
+
+void DemoEditor::saveToFile(QString filename)
+{
+    int ret = currTimeline->save(filename);
+    if (ret != DMERR_OK)
+    {
+        showFileErrorDialog("Saving demo blob file "+ filename, ret);
+    }
+
+    updateMenuStates();
+}
+
+
+void DemoEditor::settingsRestore()
+{
+    QSettings s;
+
+    restoreGeometry(s.value("windowGeometry").toByteArray());
+    restoreState(s.value("windowState").toByteArray());
+}
+
+
+void DemoEditor::settingsSave()
+{
+    QSettings s;
+
+    s.setValue("windowGeometry", saveGeometry());
+    s.setValue("windowState", saveState());
+}
+
+
+//
+// Edit history functionality
+//
+void DemoEditor::historyReset()
+{
+    if (currTimeline != NULL)
+        currTimeline->scrub();
+
+    undoHistoryPos = -1;
+    undoHistoryMax = DOC_UNDO_MAX;
+    undoHistory.clear();
+}
+
+
+void DemoEditor::historyPush(QString description)
+{
+    if (currTimeline == NULL)
+        return;
+
+    if (!undoHistory.isEmpty() && undoHistory.last()->state() == "-")
+    {
+        delete undoHistory.takeLast();
+    }
+    
+    while (undoHistory.size() >= undoHistoryMax)
+    {
+        delete undoHistory.takeFirst();
+    }
+    
+    EDTimelineObject *copy = new EDTimelineObject(currTimeline);
+    copy->setState(description);
+    undoHistory.append(copy);
+}
+
+
+void DemoEditor::historyTop()
+{
+    if (currTimeline == NULL)
+        return;
+
+    EDTimelineObject *copy = new EDTimelineObject(currTimeline);
+    copy->setState("-");
+    undoHistory.append(copy);
+
+    undoHistoryPos = undoHistory.size() - 1;
+    currTimeline->touch();
+    updateTimelineView();
+    updateMenuStates();
+    update();
+}
+
+
+void DemoEditor::historyPop()
+{
+    if (!undoHistory.isEmpty())
+    {
+        delete undoHistory.takeLast();
+    }
+}
+
+
+void DemoEditor::performRedo()
+{
+    if (undoHistoryPos >= 0 && undoHistoryPos < undoHistory.size() - 1)
+    {
+        undoHistoryPos++;
+        delete currTimeline;
+        currTimeline = new EDTimelineObject(undoHistory.at(undoHistoryPos));
+        currTimeline->touch();
+        updateTimelineView();
+        updateMenuStates();
+        update();
+    }
+}
+
+
+void DemoEditor::performUndo()
+{
+    if (undoHistoryPos > 0 && undoHistory.size() > 1)
+    {
+        undoHistoryPos--;
+        delete currTimeline;
+        currTimeline = new EDTimelineObject(undoHistory.at(undoHistoryPos));
+        currTimeline->touch();
+        updateTimelineView();
+        updateMenuStates();
+        update();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/editor/edmain.h	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,132 @@
+#ifndef EDMAIN_H
+#define EDMAIN_H
+
+// Program name etc
+#define PROGRAM_NAME      "DMPE Editor"
+#define PROGRAM_VERSION   "0.1"
+
+// Defaults
+#define DOC_DEF_FILENAME  "Untitled"
+#define DOC_UNDO_MAX	  15
+
+enum
+{
+    CTRL_REWIND,
+    CTRL_PLAY_START,
+    CTRL_PLAY_CURRENT,
+    CTRL_PAUSE
+};
+
+#define CTRL_ICON_SIZE   32
+
+#include "edtlobj.h"
+#include "edview.h"
+#include "edwaveform.h"
+#include "edtimeline.h"
+#include "edres.h"
+#include <QDebug>
+#include <QFile>
+#include <QApplication>
+#include <QMainWindow>
+#include <QTableView>
+#include <QAction>
+#include <QActionGroup>
+#include <QSlider>
+#include <QMessageBox>
+#include <QCheckBox>
+#include <QScrollBar>
+
+class DemoEditor : public QMainWindow
+{
+    Q_OBJECT
+
+public:
+    DemoEditor();
+    ~DemoEditor();
+
+    void settingsRestore();
+    void settingsSave();
+
+
+private slots:
+    void actionFileNew();
+    void actionFileOpen();
+    void actionFileSave();
+    void actionFileSaveAs();
+
+    void actionAboutBox();
+    
+    //void actionCut();
+    //void actionCopy();
+    //void actionPaste();
+    //void actionDelete();
+    
+    void performUndo();
+    void performRedo();
+
+    void actionControlChanged(QAction *);
+    void actionTimelineScrollChanged(int);
+    void actionOffsetChanged(float);
+    void actionTimeChanged(float);
+    void actionTimelineChanged();
+
+private:
+    QAction *menuActUndo, *menuActRedo, *menuActOpen, *menuActSave, *menuActSaveAs;
+//    QAction *menuActCut, *menuActCopy, *menuActPaste, *menuActDelete;
+    QActionGroup *actGroupControls;
+    QScrollBar *timelineScrollBar;
+    QTableView *resourceView;
+    QEDResourceModel *resourceModel;
+    QEDWaveTrackView *timelineAudioTrack;
+    QEDTimelineView *timelineView;
+    QEDGLDemoView *demoView;
+    
+    
+
+    QAction * createToolButton(QActionGroup *group, QString name, QIcon icon, QString statustip, QVariant data);
+    QAction * createMenuAction(QString name, const QKeySequence &shortcut, QString tooltip);
+    QAction * createMenuGroupAction(QMenu *, QActionGroup *, QString name, const QKeySequence &shortcut, QString tooltip, QVariant data);
+    void setActionGroupChecked(QActionGroup *group, QVariant data);
+
+
+    void showFileErrorDialog(QString operation, int code);
+    QMessageBox::StandardButton showDocumentModifiedDialog();
+    void statusMsg(QString message);
+    void closeEvent(QCloseEvent *event);
+    void createMainGUI();
+    void updateResourceView();
+    void updateMenuStates();
+    void updateTimelineView();
+
+    int initEffectsAndResources();
+    void shutdownEffectsAndResources();
+    void rehash();
+    int reopenResources();
+    int loadResources();
+    bool initializeVideo();
+    int getTimelineDuration();    
+
+    void createNewFile();
+    void readFromFile(QString filename);
+    void saveToFile(QString filename);
+
+
+    bool initSDL;
+    float currViewScale;
+    int currViewOffset;
+    int currFrameTime;
+
+    EDTimelineObject *currTimeline;
+    DMEngineData engine;
+
+    QList<EDTimelineObject *> undoHistory;
+    int undoHistoryPos, undoHistoryMax;
+
+    void historyReset();
+    void historyPush(QString description);
+    void historyTop();
+    void historyPop();
+};
+
+
+#endif // EDMAIN_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/editor/edres.cpp	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,74 @@
+#include "edres.h"
+#include "dmengine.h"
+
+
+QEDResourceModel::QEDResourceModel(QObject *parent)
+    : QAbstractTableModel(parent)
+{
+}
+
+
+int QEDResourceModel::rowCount(const QModelIndex &parent) const
+{
+    Q_UNUSED(parent);
+    return nengineEffects;
+}
+
+
+int QEDResourceModel::columnCount(const QModelIndex &parent) const
+{
+    Q_UNUSED(parent);
+    return 2;
+}
+
+
+QVariant QEDResourceModel::data(const QModelIndex &index, int role) const
+{
+    if (!index.isValid())
+        return QVariant();
+
+    if (index.row() >= nengineEffects || index.row() < 0)
+        return QVariant();
+
+    if (role == Qt::DisplayRole)
+    {
+        DMEffect *ef = &engineEffects[index.row()];
+        switch (index.column())
+        {
+            case 0:
+                return QString(ef->name);
+
+            case 1:
+                return QVariant(ef->nparams);
+        }
+    }
+    return QVariant();
+}
+
+
+QVariant QEDResourceModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+    if (role != Qt::DisplayRole)
+        return QVariant();
+
+    if (orientation == Qt::Horizontal)
+    {
+        switch (section) {
+            case 0:
+                return "Name";
+
+            case 1:
+                return "# params";
+        }
+    }
+    return QVariant();
+}
+
+
+Qt::ItemFlags QEDResourceModel::flags(const QModelIndex &index) const
+{
+    if (!index.isValid())
+        return Qt::ItemIsEnabled;
+
+    return QAbstractTableModel::flags(index); // | Qt::ItemIsEditable;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/editor/edres.h	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,26 @@
+#ifndef RESOURCEMODEL_H
+#define RESOURCEMODEL_H
+
+#include <QAbstractTableModel>
+#include <QPair>
+#include <QList>
+
+class QEDResourceModel : public QAbstractTableModel
+{
+    Q_OBJECT
+    
+public:
+    QEDResourceModel(QObject *parent = 0);
+
+    int rowCount(const QModelIndex &parent) const;
+    int columnCount(const QModelIndex &parent) const;
+    QVariant data(const QModelIndex &index, int role) const;
+    QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+    Qt::ItemFlags flags(const QModelIndex &index) const;
+//    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
+//    QList< QPair<QString, QString> > getList();
+
+private:
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/editor/edtimeline.cpp	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,471 @@
+#include <QtGui>
+#include "edtimeline.h"
+
+
+QEDTimelineTrackDisplay::QEDTimelineTrackDisplay(QWidget *parent) : QWidget(parent)
+{
+    track = NULL;
+    time = offs = 0;
+    scale = 1.0f;
+    
+    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+}
+
+
+QSize QEDTimelineTrackDisplay::minimumSizeHint() const
+{
+    return QSize(100, 60);
+}
+
+
+QSize QEDTimelineTrackDisplay::sizeHint() const
+{
+    return QSize(600, 60);
+}
+
+
+void QEDTimelineTrackDisplay::setTrack(DMTimelineTrack *mtrack)
+{
+    track = mtrack;
+}
+
+
+float QEDTimelineTrackDisplay::getTimeScale(float value)
+{
+    return value * scale;
+}
+
+
+float QEDTimelineTrackDisplay::getTimeFromCoord(float value)
+{
+    return value * scale * 1000.0f;
+}
+
+
+void QEDTimelineTrackDisplay::setTime(const float mtime)
+{
+    if (time != mtime && mtime >= 0)
+    {
+        time = mtime;
+        emit timeChanged(time);
+    }
+}
+
+
+void QEDTimelineTrackDisplay::setOffset(const float moffs)
+{
+    if (offs != moffs && moffs >= 0)
+    {
+        offs = moffs;
+        emit offsetChanged(offs);
+    }
+}
+
+
+void QEDTimelineTrackDisplay::setScale(const float mscale)
+{
+    if (mscale > 0.05)
+        scale = mscale;
+}
+
+
+void QEDTimelineTrackDisplay::setSelection(const float mstart, const float mend)
+{
+    if (mstart >= 0 && mend >= 0 && fabs(mend - mstart) > 0)
+    {
+        selectionValid = true;
+        if (mend > mstart)
+        {
+            selectionStart    = mstart;
+            selectionDuration = mend - mstart + 1;
+        }
+        else
+        {
+            selectionStart    = mend;
+            selectionDuration = mstart - mend + 1;
+        }
+        emit selectionChanged(selectionStart, selectionDuration);
+    }
+}
+
+
+void QEDTimelineTrackDisplay::clearSelection()
+{
+    selectionValid    = false;
+    selectionStart    = 0;
+    selectionDuration = 0;
+    emit selectionChanged(selectionStart, selectionDuration);
+}
+
+
+bool QEDTimelineTrackDisplay::getSelection(float *mstart, float *mduration)
+{
+    if (selectionValid)
+    {
+        *mstart = selectionStart;
+        *mduration = selectionDuration;
+    }
+    return selectionValid;
+}
+
+
+QList<DMTimelineEvent *> QEDTimelineTrackDisplay::getEventsAt(const int time)
+{
+    QList<DMTimelineEvent *> list;
+
+    for (int event = 0; event < track->nevents; event++)
+    {
+        DMTimelineEvent *ev = track->events[event];
+        if (time >= ev->start && time <= ev->start + ev->duration)
+            list.append(ev);
+    }
+
+    return list;
+}
+
+
+
+QList<DMTimelineEvent *> QEDTimelineTrackDisplay::getEventsForRange(const int start, const int duration)
+{
+    QList<DMTimelineEvent *> list;
+
+    for (int event = 0; event < track->nevents; event++)
+    {
+        DMTimelineEvent *ev = track->events[event];
+    }
+
+    return list;
+}
+
+
+void QEDTimelineTrackDisplay::paintEvent(QPaintEvent *)
+{
+    if (track == NULL)
+        return;
+
+    QColor eventColor(150, 150, 150, 128);
+    QColor invalidEventColor(250, 150, 150, 128);
+    QColor eventBorder(200, 250, 200, 200);
+    QColor eventParam(200, 150, 100);
+    QColor eventText(255, 255, 255);
+    QColor markerColor(255,255,255);
+    QColor selectionColor(0,255,0, 150);
+    QColor selectionEnd(0,255,0, 200);
+
+    QFont fantti;
+    fantti.setFamily("Arial");
+    fantti.setPointSizeF(8.0f);
+    fantti.setStyleHint(QFont::SansSerif);
+
+    QPainter painter(this);
+    painter.setRenderHint(QPainter::Antialiasing);
+
+
+    painter.save();
+    painter.scale(scale, 1);
+
+    float wd = getTimeScale(width());
+    for (int event = 0; event < track->nevents; event++)
+    {
+        DMTimelineEvent *ev = track->events[event];
+
+        float x0 = getTimeScale(ev->start - offs),
+              x1 = getTimeScale(ev->start + ev->duration - offs);
+
+        if ((x0 >= 0 && x0 < wd) || (x0 < 0 && x1 >= 0))
+        {
+            painter.setFont(fantti);
+            painter.setBrush(ev->effect != NULL ? eventColor : invalidEventColor);
+            painter.setPen(eventBorder);
+            x0 = ev->start - offs;
+            x1 = ev->duration;
+            painter.fillRect(x0, 0, x1, height(), eventColor);
+
+            QPainterPath path;
+            path.addText(QPointF(x0 + 2, 10), fantti, ev->effect != NULL ? QString(ev->effect->name) : "INVALID");
+
+            painter.save();
+            painter.translate(1,1);
+            painter.setPen(Qt::black);
+            painter.setBrush(Qt::black);
+            painter.drawPath(path);
+            painter.restore();
+
+            painter.setPen(eventText);
+            painter.setBrush(eventText);
+            painter.drawPath(path);
+        }
+
+    }
+
+    painter.restore();
+
+    if (selectionValid)
+    {
+        float x0 = getTimeScale(selectionStart - offs),
+              x1 = getTimeScale(selectionStart + selectionDuration - offs);
+
+        if ((x0 >= 0 && x0 < wd) || (x0 < 0 && x1 >= 0))
+        {
+            painter.setBrush(selectionColor);
+            painter.setPen(selectionEnd);
+            x0 = selectionStart - offs;
+            x1 = selectionDuration;
+            painter.fillRect(x0, 0, x1, height(), eventColor);
+            
+            painter.drawLine(x0, 0, x0, height());
+            painter.drawLine(x1, 0, x1, height());
+        }
+    }
+
+
+    if (time >= offs * scale && time - offs <= width() * scale)
+    {
+        int xc = time - offs;
+        painter.save();
+        painter.scale(scale, 1);
+        painter.setPen(markerColor);
+        painter.drawLine(xc, 0, xc, height());
+        painter.restore();
+    }
+}
+
+
+void QEDTimelineTrackDisplay::mousePressEvent(QMouseEvent *ev)
+{
+    switch (ev->button())
+    {
+        case Qt::LeftButton:
+            if (parent->getActiveTrack() != this)
+                emit trackActivated(this);
+
+            selectionPoint = ev->pos();
+            selectionOffs = offs / scale;
+            selecting = false;
+            break;
+
+        case Qt::RightButton:
+            dragPoint = ev->pos();
+            dragOffs = offs / scale;
+            dragging = false;
+            break;
+
+        default:
+            break;
+    }
+}
+
+
+void QEDTimelineTrackDisplay::mouseMoveEvent(QMouseEvent *ev)
+{
+    if ((ev->buttons() & Qt::LeftButton) && ev->pos().x() != selectionPoint.x())
+    {
+        selecting = true;
+        setSelection(selectionOffs, offs + (ev->pos().x() - selectionPoint.x()) / scale);
+    }
+    if ((ev->buttons() & Qt::RightButton) && ev->pos().x() != dragPoint.x())
+    {
+        dragging = true;
+        setOffset(dragOffs - (ev->pos().x() - dragPoint.x()) / scale);
+    }
+}
+
+
+void QEDTimelineTrackDisplay::mouseReleaseEvent(QMouseEvent *ev)
+{
+    if (ev->button() == Qt::LeftButton)
+    {
+        if (selecting)
+        {
+            selecting = false;
+            setSelection(selectionOffs + (ev->pos().x() - selectionPoint.x()) / scale);
+        }
+    }
+    else
+    if (ev->button() == Qt::RightButton && !dragging)
+    {
+        setTime(offs + getTimeFromCoord(ev->pos().x()));
+    }
+}
+
+
+QEDTimelineTrackView::QEDTimelineTrackView(QWidget *parent) : QWidget(parent)
+{
+    QHBoxLayout *mainLayout = new QHBoxLayout(this);
+    mainLayout->setMargin(0);
+    track = new QEDTimelineTrackDisplay(this);
+
+    QFrame *infoLayoutContainer = new QFrame(this);
+    infoLayoutContainer->setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
+    infoLayoutContainer->setLineWidth(2);
+    infoLayoutContainer->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
+    infoLayoutContainer->setFixedWidth(200);
+
+    QVBoxLayout *infoLayout = new QVBoxLayout(infoLayoutContainer);
+    infoLayout->setMargin(0);
+    infoName = new QLineEdit();
+    infoName->setFrame(false);
+    infoName->setMaxLength(DT_MAX_NAME_LENGTH);
+    infoName->setStyleSheet("QLineEdit { background-color: black; color: white; padding: 2px; }");
+    connect(infoName, SIGNAL(textEdited(const QString&)), this, SLOT(slotTrackNameChanged(const QString&)));
+    infoLayout->addWidget(infoName);
+
+
+    enabledCheck = new QCheckBox("Enabled");
+    infoLayout->addWidget(enabledCheck);
+    connect(enabledCheck, SIGNAL(toggled(bool)), this, SLOT(slotTrackEnabledChanged(bool)));
+    
+    infoData = new QLabel();
+    infoData->setStyleSheet("QLabel { padding: 2px; }");
+    infoLayout->addWidget(infoData);
+
+    mainLayout->addWidget(infoLayoutContainer);
+    mainLayout->addWidget(track);
+}
+
+
+void QEDTimelineTrackView::update()
+{
+    if (track != NULL && track->track)
+    {
+        infoName->setText(QString(track->track->name));
+        enabledCheck->setChecked(track->track->enabled);
+        infoData->setText(QString("<b>%1</b> events").arg(track->track->nevents));
+    }
+    else
+    {
+        infoName->setText("");
+        infoData->setText("-");
+        enabledCheck->setChecked(false);
+    }
+
+    QWidget::update();
+}
+
+
+void QEDTimelineTrackView::setTrack(DMTimelineTrack *mtrack)
+{
+    track->setTrack(mtrack);
+    update();
+}
+
+
+void QEDTimelineTrackView::slotTrackEnabledChanged(bool value)
+{
+    track->track->enabled = value;
+    emit trackChanged();
+}
+
+
+void QEDTimelineTrackView::slotTrackNameChanged(const QString & text)
+{
+    QByteArray ba = text.toUtf8();
+    track->track->name = dm_strdup(ba.constData());
+    emit trackChanged();
+}
+
+
+
+QEDTimelineView::QEDTimelineView(QWidget *parent) : QWidget(parent)
+{
+    layout = new QVBoxLayout(this);
+    tl = NULL;
+}
+
+
+void QEDTimelineView::setTimeline(EDTimelineObject *mtl)
+{
+    tl = mtl;
+
+    delete layout;
+    layout = new QVBoxLayout(this);
+    layout->setMargin(0);
+    
+    tracks.clear();
+
+    if (tl != NULL && tl->tl != NULL)
+    {
+        for (int track = 0; track < tl->tl->ntracks; track++)
+        {
+            QEDTimelineTrackView *vtr = new QEDTimelineTrackView(this);
+            vtr->setTrack(tl->tl->tracks[track]);
+            tracks.append(vtr);
+            layout->addWidget(vtr);
+            connect(vtr, SIGNAL(trackChanged()), this, SLOT(slotTimelineChanged()));
+            connect(vtr, SIGNAL(timeChanged(float)), this, SLOT(slotTimeChanged(float)));
+            connect(vtr, SIGNAL(offsetChanged(float)), this, SLOT(slotOffsetChanged(float)));
+            connect(vtr, SIGNAL(selectionChanged(float,float)), this, SLOT(slotSelectionChanged(float,float)));
+        }
+    }
+    update();
+}
+
+
+void QEDTimelineView::slotTimelineChanged()
+{
+    if (tl != NULL)
+    {
+        tl->touch();
+        emit timelineChanged();
+    }
+}
+
+
+void QEDTimelineView::setTime(const int mtime)
+{
+    if (tl != NULL && tl->tl != NULL)
+    {
+        QList<QEDTimelineTrackView *>::iterator track;
+        for (track = tracks.begin(); track != tracks.end(); track++)
+        {
+            (*track)->track->setTime(mtime);
+        }
+        update();
+    }
+}
+
+
+void QEDTimelineView::setOffset(const int moffs)
+{
+    if (tl != NULL && tl->tl != NULL)
+    {
+        QList<QEDTimelineTrackView *>::iterator track;
+        for (track = tracks.begin(); track != tracks.end(); track++)
+        {
+            (*track)->track->setOffset(moffs);
+        }
+        update();
+    }
+}
+
+
+void QEDTimelineView::setScale(const float mscale)
+{
+    if (tl != NULL && tl->tl != NULL)
+    {
+        QList<QEDTimelineTrackView *>::iterator track;
+        for (track = tracks.begin(); track != tracks.end(); track++)
+        {
+            (*track)->track->setScale(mscale);
+        }
+        update();
+    }
+}
+
+
+QList<DMTimelineEvent *> QEDTimelineView::getEventsAt(const int time)
+{
+    if (tl != NULL && tl->tl != NULL &&
+        activeTrack >= 0 && activeTrack < tl->tl->ntracks)
+    {
+        return tracks[activeTrack]->tl->getEventsAt(time);
+    }
+    else
+        return QList<DMTimelineEvent *>();
+}
+
+
+QList<DMTimelineEvent *> QEDTimelineView::getEventsForRange(const int start, const int duration)
+{
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/editor/edtimeline.h	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,121 @@
+#ifndef EDTIMELINE_H
+#define EDTIMELINE_H
+
+#include <QWidget>
+#include <QCheckBox>
+#include <QVBoxLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include "edtlobj.h"
+#include "dmengine.h"
+
+
+class QEDTimelineTrackDisplay : public QWidget
+{
+    Q_OBJECT
+
+public:
+    DMTimelineTrack *track;
+
+    QEDTimelineTrackDisplay(QWidget *parent = 0);
+    void setTrack(DMTimelineTrack *mtrack);
+
+    float getTimeScale(float value);
+    float getTimeFromCoord(float value);
+
+    void setScale(const float mscale);
+
+    bool getSelection(float *mstart, float *mduration);
+    QList<DMTimelineEvent *> getEventsAt(const int time);
+    QList<DMTimelineEvent *> getEventsForRange(const int start, const int duration);
+
+    QSize minimumSizeHint() const;
+    QSize sizeHint() const;
+
+public slots:
+    void setTime(const float mtime);
+    void setOffset(const float moffs);
+    void setSelection(const float mstart, const float mend);
+    void clearSelection();
+
+signals:
+    void selectionChanged(float mstart, float mduration);
+    void timeChanged(float value);
+    void offsetChanged(float value);
+
+protected:
+    void mousePressEvent(QMouseEvent *event);
+    void mouseMoveEvent(QMouseEvent *event);
+    void mouseReleaseEvent(QMouseEvent *event);
+
+    void paintEvent(QPaintEvent *event);
+
+private:
+    float scale, time, offs;
+
+    bool selectionValid;
+    float selectionStart, selectionDuration;
+
+    QPoint selectionPoint, dragPoint;
+    bool selecting, dragging;
+    float selectionOffs, dragOffs;
+};
+
+
+class QEDTimelineTrackView : public QWidget
+{
+    Q_OBJECT
+
+private:
+    QLineEdit *infoName;
+    QLabel *infoData;
+    QCheckBox *enabledCheck;
+
+public:
+    QEDTimelineTrackDisplay *track;
+
+    QEDTimelineTrackView(QWidget *parent = 0);
+    void setTrack(DMTimelineTrack *mtrack);
+    void update();
+
+private slots:
+    void slotTrackEnabledChanged(bool);
+    void slotTrackNameChanged(const QString & text);
+
+signals:
+    void trackChanged();
+    void selectionChanged(float mstart, float mduration);
+    void timeChanged(float value);
+    void offsetChanged(float value);
+};
+
+
+class QEDTimelineView : public QWidget
+{
+    Q_OBJECT
+
+private:
+    QVBoxLayout *layout;
+
+    EDTimelineObject *tl;
+    QList<QEDTimelineTrackView *> tracks;
+    int activeTrack;
+
+public:
+    QEDTimelineView(QWidget *parent = 0);
+    void setTimeline(EDTimelineObject *mtl);
+
+    void setTime(const float mtime);
+    void setOffset(const float moffs);
+    void setScale(const float mscale);
+
+private slots:
+    void slotTimelineChanged();
+
+signals:
+    void timelineChanged();
+    void timeChanged(float value);
+    void offsetChanged(float value);
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/editor/edtlobj.cpp	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,102 @@
+//
+// Demo Editor -- Timeline state object
+// Wrapper class for DMTimeline data
+// (C) Copyright 2012 Matti 'ccr' Hämäläinen <ccr@tnsp.org>
+//
+#include "edtlobj.h"
+#include "dmres.h"
+
+
+EDTimelineObject::EDTimelineObject()
+{
+    dmTimelineNew(&tl, "Demo");
+    scrub();
+}
+
+
+EDTimelineObject::EDTimelineObject(EDTimelineObject *obj)
+{
+    scrub();
+    filename = obj->filename;
+    dmCopyTimeline(obj->tl, &tl);
+}
+
+
+EDTimelineObject::~EDTimelineObject()
+{
+    dmFreeTimeline(tl);
+}
+
+
+int EDTimelineObject::load(QString mfilename)
+{
+    QByteArray fnba = mfilename.toUtf8();
+    DMResource *res;
+    DMTimeline *tmp;
+    if ((res = dmf_create_stdio(fnba.data(), "rb")) == NULL)
+        return DMERR_FOPEN;
+
+    int err = dmLoadTimeline(res, &tmp);
+    dmf_close(res);
+
+    if (err == DMERR_OK)
+    {
+        dmFreeTimeline(tl);
+        tl = tmp;
+        scrub();
+        if (tl->name != NULL)
+            filename = QString(tl->name);
+        else
+            filename = mfilename;
+    }
+    else
+    {
+        dmFreeTimeline(tmp);
+    }
+    return err;
+}
+
+
+int EDTimelineObject::save(QString mfilename)
+{
+    QByteArray fnba = mfilename.toUtf8();
+    DMResource *res;
+    if ((res = dmf_create_stdio(fnba.data(), "wb")) == NULL)
+        return DMERR_FOPEN;
+
+    int err = dmSaveTimeline(res, tl);
+    scrub();
+
+    dmf_close(res);
+    return err;
+}
+
+
+void EDTimelineObject::touch()
+{
+    ntouches++;
+}
+
+
+void EDTimelineObject::scrub()
+{
+    ntouches = 0;
+}
+
+
+bool EDTimelineObject::touched()
+{
+    return ntouches;
+}
+
+
+void EDTimelineObject::setState(const QString &mstate)
+{
+    cstate = mstate;
+}
+
+
+QString EDTimelineObject::state() const
+{
+    return cstate;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/editor/edtlobj.h	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,38 @@
+//
+// Demo Editor -- Timeline state object
+// Wrapper class for DMTimeline data
+// (C) Copyright 2012 Matti 'ccr' Hämäläinen <ccr@tnsp.org>
+//
+#ifndef EDTIMELINEOBJECT_H
+#define EDTIMELINEOBJECT_H
+
+#include "dmengine.h"
+#include <QString>
+
+class EDTimelineObject
+{
+private:
+    QString cstate;
+    int ntouches;
+
+public:
+    QString filename;
+    DMTimeline *tl;
+
+    EDTimelineObject();
+    EDTimelineObject(EDTimelineObject *);
+    ~EDTimelineObject();
+
+    int load(QString filename);
+    int save(QString filename);
+    
+    void scrub();
+    void touch();
+    bool touched();
+
+    void setState(const QString &mstate);
+    QString state() const;
+};
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/editor/edview.cpp	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,148 @@
+#include "edview.h"
+#include <QtGui>
+
+
+QEDGLDemoView::QEDGLDemoView(QWidget *parent) :
+    QGLWidget(QGLFormat(QGL::SampleBuffers|QGL::AlphaChannel), parent)
+{
+    makeCurrent();
+
+    if (QGLFramebufferObject::hasOpenGLFramebufferBlit())
+    {
+        QGLFramebufferObjectFormat format;
+        format.setSamples(4);
+        format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+
+//        render_fbo = new QGLFramebufferObject(512, 512, format);
+//        texture_fbo = new QGLFramebufferObject(512, 512);
+    }
+    else
+    {
+//        render_fbo = new QGLFramebufferObject(1024, 1024);
+//        texture_fbo = render_fbo;
+    }
+
+    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+}
+
+
+QEDGLDemoView::~QEDGLDemoView()
+{
+}
+
+
+QSize QEDGLDemoView::minimumSizeHint() const
+{
+    return QSize(320, 240);
+}
+
+
+QSize QEDGLDemoView::sizeHint() const
+{
+    return QSize(640, 480);
+}
+
+
+void QEDGLDemoView::setEngineData(DMEngineData *mengine)
+{
+    engine = mengine;
+}
+
+
+void QEDGLDemoView::render(int frameTime)
+{
+    if (engine != NULL)
+    {
+        engine->frameTime = frameTime;
+
+        if (engine->demoRender != NULL)
+        {
+            engine->demoRender(engine);
+        }
+        else
+        {
+            dmExecuteTimeline(engine->ptl, engine->screen, engineGetTick(engine));
+        }
+        
+        engine->frameCount++;
+    }
+}
+
+
+void QEDGLDemoView::paintEvent(QPaintEvent *)
+{
+    // save the GL state set for QPainter
+    saveGLState();
+
+    // restore the GL state that QPainter expects
+    restoreGLState();
+}
+
+
+void QEDGLDemoView::saveGLState()
+{
+    glPushAttrib(GL_ALL_ATTRIB_BITS);
+    glMatrixMode(GL_PROJECTION);
+    glPushMatrix();
+    glMatrixMode(GL_MODELVIEW);
+    glPushMatrix();
+}
+
+
+void QEDGLDemoView::restoreGLState()
+{
+    glMatrixMode(GL_PROJECTION);
+    glPopMatrix();
+    glMatrixMode(GL_MODELVIEW);
+    glPopMatrix();
+    glPopAttrib();
+}
+
+
+QEDSWDemoView::QEDSWDemoView(QWidget *parent) : QEDGLDemoView(parent)
+{
+    img = NULL;
+}
+
+
+QEDSWDemoView::~QEDSWDemoView()
+{
+    delete img;
+}
+
+
+void QEDSWDemoView::setEngineData(DMEngineData *mengine)
+{
+    engine = mengine;
+    delete img;
+
+    img = new QImage((const uchar *)mengine->screen->pixels,
+        mengine->screen->w, mengine->screen->h,
+        mengine->screen->pitch, QImage::Format_RGB32);
+}
+
+
+void QEDSWDemoView::paintEvent(QPaintEvent *)
+{
+    if (img != NULL)
+    {
+        QPainter painter(this);
+        painter.drawImage(QPoint(0, 0), *img);
+    }
+}
+
+
+void QEDSWDemoView::render(int frameTime)
+{
+    if (SDL_MUSTLOCK(engine->screen) != 0 && SDL_LockSurface(engine->screen) != 0)
+        return;
+
+    QEDGLDemoView::render(frameTime);
+
+    if (SDL_MUSTLOCK(engine->screen) != 0)
+        SDL_UnlockSurface(engine->screen);
+
+    update();
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/editor/edview.h	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,50 @@
+#ifndef EDVIEW_H
+#define EDVIEW_H
+
+#include <QtOpenGL>
+#include <QImage>
+#include "dmengine.h"
+
+
+class QEDGLDemoView : public QGLWidget
+{
+    Q_OBJECT
+
+public:
+    QEDGLDemoView(QWidget *parent);
+    ~QEDGLDemoView();
+
+    QSize minimumSizeHint() const;
+    QSize sizeHint() const;
+
+    virtual void setEngineData(DMEngineData *mengine);
+    virtual void render(int frameTime);
+    void paintEvent(QPaintEvent *);
+
+private:
+    void saveGLState();
+    void restoreGLState();
+
+protected:
+    DMEngineData *engine;
+};
+
+
+class QEDSWDemoView : public QEDGLDemoView
+{
+    Q_OBJECT
+
+public:
+    QEDSWDemoView(QWidget *parent);
+    ~QEDSWDemoView();
+
+    void setEngineData(DMEngineData *mengine);
+    void render(int frameTime);
+    void paintEvent(QPaintEvent *);
+
+private:
+    QImage *img;
+};
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/editor/edwaveform.cpp	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,418 @@
+#include <QtGui>
+#include <SDL_audio.h>
+#include "edwaveform.h"
+
+
+QEDWaveTrackDisplay::QEDWaveTrackDisplay(QWidget *parent) : QWidget(parent)
+{
+    data      = NULL;
+    size      = 0;
+    format    = AUDIO_S16SYS;
+    channels  = 1;
+    freq      = 1;
+    scale     = 1.0f;
+    time      = offs = 0;
+    duration  = 0;
+    sduration = 0;
+
+    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+}
+
+
+void QEDWaveTrackDisplay::setWaveform(void *mdata, int msize, int mformat, int mchannels, int mfreq)
+{
+    data     = mdata;
+    size     = msize;
+    format   = mformat;
+    channels = mchannels;
+    freq     = mfreq;
+
+    int bps = getBps();
+    if (bps != 0)
+    {
+        // Duration in milliseconds
+        duration  = ((float) (size / bps) / (float) freq) * 1000.0f;
+        
+        // Duration in samples
+        sduration = msize / bps;
+    }
+    else
+    {
+        duration  = 0;
+        sduration = 0;
+    }
+
+    update();
+}
+
+
+int QEDWaveTrackDisplay::getBps()
+{
+    int bps = channels;
+    switch (format)
+    {
+        case AUDIO_S16SYS:
+        case AUDIO_U16SYS:
+            bps *= sizeof(quint16);
+            break;
+        case AUDIO_S8:
+        case AUDIO_U8:
+            bps *= sizeof(quint8);
+            break;
+    }
+    return bps;
+}
+
+
+float QEDWaveTrackDisplay::getDuration()
+{
+    return duration;
+}
+
+
+float QEDWaveTrackDisplay::getTimeScale(float value)
+{
+    return (value * scale * (float) freq) / 1000.0f;
+}
+
+
+float QEDWaveTrackDisplay::getTimeFromCoord(float value)
+{
+    return value * scale;
+}
+
+
+void QEDWaveTrackDisplay::setTime(const float mtime)
+{
+    if (time != mtime && mtime >= 0 && mtime < duration)
+    {
+        time = mtime;
+        emit timeChanged(time);
+    }
+}
+
+
+void QEDWaveTrackDisplay::setOffset(const float moffs)
+{
+    if (offs != moffs && moffs >= 0 && moffs < getDuration())
+    {
+        offs = moffs;
+        emit offsetChanged(offs);
+    }
+}
+
+
+void QEDWaveTrackDisplay::setScale(const float mscale)
+{
+    if (mscale > 0.05)
+        scale = mscale;
+    emit scaleChanged(scale);
+}
+
+
+void QEDWaveTrackDisplay::setSelection(const float mstart, const float mend)
+{
+    if (mstart >= 0 && mend >= 0 && fabs(mend - mstart) > 0)
+    {
+        selectionValid = true;
+        if (mend > mstart)
+        {
+            selectionStart    = mstart;
+            selectionDuration = mend - mstart + 1;
+        }
+        else
+        {
+            selectionStart    = mend;
+            selectionDuration = mstart - mend + 1;
+        }
+        emit selectionChanged(selectionStart, selectionDuration);
+    }
+}
+
+
+void QEDWaveTrackDisplay::clearSelection()
+{
+    selectionValid    = false;
+    selectionStart    = 0;
+    selectionDuration = 0;
+    emit selectionChanged(selectionStart, selectionDuration);
+}
+
+
+bool QEDTimelineTrackDisplay::getSelection(float *mstart, float *mduration)
+{
+    if (selectionValid)
+    {
+        *mstart = selectionStart;
+        *mduration = selectionDuration;
+    }
+    return selectionValid;
+}
+
+
+float QEDWaveTrackDisplay::getScaledWidth()
+{
+    return getTimeScale(width());
+}
+
+
+float QEDWaveTrackDisplay::getTime()
+{
+    return time;
+}
+
+
+float QEDWaveTrackDisplay::getOffset()
+{
+    return offs;
+}
+
+
+void QEDWaveTrackDisplay::paintEvent(QPaintEvent *)
+{
+    QColor waveColor(0, 150, 0);
+    QColor waveCenterLine(100, 100, 100);
+    QColor markerColor(255,255,255);
+    QColor bgColor(0, 0, 0);//255, 255, 255);
+
+    QPainter painter(this);
+    painter.setRenderHint(QPainter::Antialiasing);
+    painter.fillRect(QRect(0, 0, width(), height()), bgColor);
+
+    if (data != NULL)
+    {
+        int voffs = 0;
+
+        painter.save();
+        painter.translate(0, height() / 2);
+
+        painter.setPen(waveCenterLine);
+        painter.drawLine(0, 0, width(), 0);
+
+        switch (format)
+        {
+            case AUDIO_S16SYS:
+                painter.scale(1.0f, (float) height() / 32768.0f);
+                break;
+            case AUDIO_U16SYS:
+                voffs = -32768;
+                painter.scale(1.0f, height() / 32768.0f);
+                break;
+
+            case AUDIO_S8:
+                painter.scale(1.0f, height() / 128.0f);
+                break;
+            case AUDIO_U8:
+                voffs = -128;
+                painter.scale(1.0f, height() / 128.0f);
+                break;
+        }
+
+        painter.scale(1.0f, 0.5f);
+        painter.setPen(waveColor);
+
+        float mscale = (scale * (float)freq) / 1000.0f;
+        int prevY = 0, prevX = 0;
+        if (format == AUDIO_S16SYS || format == AUDIO_U16SYS)
+        {
+            qint16 *buf = (qint16 *) data;
+            for (int xc = 0; xc < width(); xc++)
+            {
+                int moffs = (int) (((offs + xc) * mscale));
+                if (moffs >= sduration) break;
+                int value = buf[moffs * channels] + voffs;
+                painter.drawLine(prevX, prevY, xc, value);
+                prevY = value;
+                prevX = xc;
+            }
+        }
+        else
+        if (format == AUDIO_S8 || format == AUDIO_U8)
+        {
+            qint8 *buf = (qint8 *) data;
+            for (int xc = 0; xc < width(); xc++)
+            {
+                int moffs = (int) (((offs + xc) * mscale));
+                if (moffs >= sduration) break;
+                int value = buf[moffs * channels] + voffs;
+                painter.drawLine(prevX, prevY, xc, value);
+                prevY = value;
+                prevX = xc;
+            }
+        }
+        
+        painter.restore();
+    }
+    
+    float xc = getTimeScale(time - offs), wd = getTimeScale(width());
+    if (xc >= 0 && xc <= wd)
+    {
+        xc = time - offs;
+        painter.scale(scale, 1.0f);
+        painter.setPen(markerColor);
+        painter.drawLine(xc, 0, xc, height());
+    }
+}
+
+
+void QEDWaveTrackDisplay::mousePressEvent(QMouseEvent *ev)
+{
+    switch (ev->button())
+    {
+        case Qt::LeftButton:
+            selectionPoint = ev->pos();
+            selectionOffs = offs;
+            selecting = false;
+            break;
+
+        case Qt::RightButton:
+            dragPoint = ev->pos();
+            dragOffs = offs;
+            dragging = false;
+            break;
+
+        default:
+            break;
+    }
+}
+
+
+void QEDWaveTrackDisplay::mouseMoveEvent(QMouseEvent *ev)
+{
+    if ((ev->buttons() & Qt::LeftButton) && ev->pos().x() != selPoint.x())
+    {
+        selecting = true;
+        setSelection(selectionOffs + (ev->pos().x() - selectionPoint.x()) / scale);
+    }
+    if ((ev->buttons() & Qt::RightButton) && ev->pos().x() != dragPoint.x())
+    {
+        dragging = true;
+        setOffset(dragOffs - (ev->pos().x() - dragPoint.x()) / scale);
+    }
+}
+
+
+void QEDWaveTrackDisplay::mouseReleaseEvent(QMouseEvent *ev)
+{
+    if (ev->button() == Qt::LeftButton)
+    {
+        if (selecting)
+        {
+            selecting = false;
+            setSelection(selOffs + (ev->pos().x() - selPoint.x()) / scale);
+        }
+    }
+    else
+    if (ev->button() == Qt::RightButton && !dragging)
+    {
+        setTime(offs + getTimeFromCoord(ev->pos().x()));
+    }
+}
+
+
+QEDWaveTrackView::QEDWaveTrackView(QWidget *parent) : QWidget(parent)
+{
+    QHBoxLayout *mainLayout = new QHBoxLayout(this);
+    mainLayout->setMargin(0);
+    wave = new QEDWaveTrackDisplay(this);
+
+    QFrame *infoLayoutContainer = new QFrame(this);
+    infoLayoutContainer->setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
+    infoLayoutContainer->setLineWidth(2);
+    infoLayoutContainer->setFixedWidth(200);
+
+    QVBoxLayout *infoLayout = new QVBoxLayout(infoLayoutContainer);
+    infoLayout->setMargin(0);
+    infoName = new QLabel("Audio");
+    infoName->setStyleSheet("QLabel { background-color: black; color: white; padding: 2px; }");
+    infoLayout->addWidget(infoName);
+
+    infoData = new QLabel();
+    infoData->setStyleSheet("QLabel { padding: 2px; }");
+    infoLayout->addWidget(infoData);
+
+    mainLayout->addWidget(infoLayoutContainer);
+    mainLayout->addWidget(wave);
+
+    connect(wave, SIGNAL(timeChanged(float)), this, SLOT(slotTimeChanged(float)));
+    connect(wave, SIGNAL(offsetChanged(float)), this, SLOT(slotOffsetChanged(float)));
+    connect(wave, SIGNAL(selectionChanged(float,float)), this, SLOT(slotSelectionChanged(float,float)));
+}
+
+
+void QEDWaveTrackView::setWaveform(void *mdata, int msize, int mformat, int mchannels, int mfreq)
+{
+    QString fmt;
+    switch (mformat)
+    {
+        case AUDIO_S16SYS: fmt = "16bit (S)"; break;
+        case AUDIO_U16SYS: fmt = "16bit (U)"; break;
+        case AUDIO_S8:     fmt = "8bit (S)"; break;
+        case AUDIO_U8:     fmt = "8bit (U)"; break;
+        default:           fmt = "?"; break;
+    }
+    infoData->setText(QString("<b>%1</b>, <b>%2</b> ch, <b>%3</b> Hz").arg(fmt).arg(mchannels).arg(mfreq));
+    wave->setWaveform(mdata, msize, mformat, mchannels, mfreq);
+    update();
+}
+
+
+void QEDWaveTrackView::setName(QString name)
+{
+    infoName->setText(name);
+    update();
+}
+
+
+void QEDWaveTrackView::setTime(const float mtime)
+{
+    wave->setTime(mtime);
+}
+
+
+void QEDWaveTrackView::setOffset(const float moffs)
+{
+    wave->setOffset(moffs);
+}
+
+
+void QEDWaveTrackView::setScale(const float mscale)
+{
+    wave->setScale(mscale);
+}
+
+
+float QEDWaveTrackView::getTime()
+{
+    return wave->getTime();
+}
+
+
+float QEDWaveTrackView::getOffset()
+{
+    return wave->getOffset();
+}
+
+
+float QEDWaveTrackView::getScaledWidth()
+{
+    return wave->getScaledWidth();
+}
+
+
+void QEDWaveTrackView::slotTimeChanged(float value)
+{
+    emit timeChanged(value);
+}
+
+
+void QEDWaveTrackView::slotOffsetChanged(float value)
+{
+    emit offsetChanged(value);
+}
+
+
+float QEDWaveTrackView::getDuration()
+{
+    return wave->getDuration();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/editor/edwaveform.h	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,97 @@
+#ifndef EDWAVEFORM_H
+#define EDWAVEFORM_H
+
+#include <QWidget>
+#include <QLabel>
+#include "dmengine.h"
+
+class QEDWaveTrackDisplay : public QWidget
+{
+    Q_OBJECT
+
+public:
+    QEDWaveTrackDisplay(QWidget *parent = 0);
+
+    void setWaveform(void *mdata, int msize, int mformat, int mchannels, int mfreq);
+    float getScaledWidth();
+    void setScale(const float mscale);
+    int getBps();
+    float getDuration();
+    float getTimeScale(float value);
+    float getTimeFromCoord(float value);
+    float getTime();
+    float getOffset();
+
+    QSize minimumSizeHint() const
+    {
+        return QSize(100, 60);
+    }
+
+    QSize sizeHint() const
+    {
+        return QSize(600, 60);
+    }
+
+public slots:
+    void setTime(const float mtime);
+    void setOffset(const float moffs);
+    void setSelection(const float mstart, const float mend);
+    void clearSelection();
+
+signals:
+    void selectionChanged(float mstart, float mduration);
+    void timeChanged(float value);
+    void offsetChanged(float value);
+
+protected:
+    void mousePressEvent(QMouseEvent *event);
+    void mouseMoveEvent(QMouseEvent *event);
+    void mouseReleaseEvent(QMouseEvent *event);
+
+    void paintEvent(QPaintEvent *event);
+
+private:
+    float scale, time, offs, duration; // in milliseconds
+    int size, channels, format, freq, sduration;
+    void *data;
+
+    bool selectionValid;
+    float selectionStart, selectionDuration;
+
+    QPoint selectionPoint, dragPoint;
+    bool selecting, dragging;
+    float selectionOffs, dragOffs;
+};
+
+
+class QEDWaveTrackView : public QWidget
+{
+    Q_OBJECT
+
+private:
+    QEDWaveTrackDisplay *wave;
+    QLabel *infoName, *infoData;
+
+public:
+
+    QEDWaveTrackView(QWidget *parent = 0);
+    void setWaveform(void *mdata, int mlen, int mformat, int mchannels, int mfreq);
+    void setName(QString name);
+    void setTime(const float mtime);
+    void setOffset(const float moffs);
+    float getScaledWidth();
+    void setScale(const float mscale);
+    float getDuration();
+    float getTime();
+    float getOffset();
+
+private slots:
+    void slotTimeChanged(float value);
+    void slotOffsetChanged(float value);
+
+signals:
+    void timeChanged(float value);
+    void offsetChanged(float value);
+};
+
+#endif
--- a/edmain.cpp	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,531 +0,0 @@
-//
-// Demo Editor -- Main program
-// (C) Copyright 2012 Matti 'ccr' Hämäläinen <ccr@tnsp.org>
-//
-#include <SDL.h>
-#include "dmengine.h"
-#include "edmain.h"
-#include <QSettings>
-#include <QGLWidget>
-
-
-int main(int argc, char *argv[])
-{
-    dmVerbosity = 5;
-
-    QApplication app(argc, argv);
-
-    app.setOrganizationName("TNSP");
-    app.setOrganizationDomain("tnsp.org");
-    app.setApplicationName(PROGRAM_NAME);
-    app.setApplicationVersion(PROGRAM_VERSION);
-
-    DemoEditor mainWin;
-
-    mainWin.show();
-          
-    return app.exec();
-}
-
-
-void engineAudioCallback(void *userdata, Uint8 * stream, int len)
-{
-    DMEngineData *engine = (DMEngineData *) userdata;
-
-    if (engine->paused)
-    {
-        memset(stream, 0, len);
-    }
-    else
-#ifdef DM_USE_JSS
-    {
-        if (engine->dev != NULL)
-            jvmRenderAudio(engine->dev, stream,
-                           len / jvmGetSampleSize(engine->dev));
-    }
-#endif
-#ifdef DM_USE_TREMOR
-    if (engine->audioPos + len >= engine->audioRes->rdataSize)
-    {
-        engine->exitFlag = true;
-    }
-    else
-    {
-        memcpy(stream, (Uint8 *) engine->audioRes->rdata + engine->audioPos, len);
-        engine->audioPos += len;
-    }
-#endif
-}
-
-
-int DemoEditor::reopenResources()
-{
-    int err;
-
-    if ((err = dmres_init(&engine.resources, engine.optPackFilename, engine.optDataPath,
-                           engine.optResFlags, engineClassifier)) != DMERR_OK)
-    {
-        dmError("Could not initialize resource manager: %d, %s.\n",
-            err, dmErrorStr(err));
-    }
-    return err;
-}
-
-
-int DemoEditor::loadResources()
-{
-    int err, loaded, total;
-    err = dmres_preload(engine.resources, true, &loaded, &total);
-
-    while ((err = dmres_preload(engine.resources, false, &loaded, &total)) == DMERR_PROGRESS)
-    {
-        // Show a nice progress bar while loading
-        if (total > 0 && (loaded % 2) == 0)
-        {
-/*
-            if ((err = engineShowProgress(loaded, total)) != DMERR_OK)
-                return err;
-*/
-        }
-    }
-    return DMERR_OK;
-}
-
-
-DemoEditor::DemoEditor()
-{
-    int err;
-
-    resize(1024, 768);
-    setWindowTitle(QCoreApplication::applicationName());
-
-    memset(&engine, 0, sizeof(engine));
-    initSDL = FALSE;
-    currTimeline = NULL;
-
-    // Pre-initialization
-    if ((err = demoPreInit(&engine)) != DMERR_OK)
-        goto error_exit;
-
-    // Initialize SDL components
-    dmPrint(1, "Initializing libSDL.\n");
-    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) != 0)
-    {
-        dmError("Could not initialize SDL: %s\n", SDL_GetError());
-        goto error_exit;
-    }
-    initSDL = true;
-
-    // Initialize audio parts
-    if (engine.optAfmt.freq == 0 && engine.optAfmt.channels == 0)
-    {
-        // Defaults, if none seem to be set
-        engine.optAfmt.freq = 44100;
-        engine.optAfmt.format = AUDIO_S16SYS;
-        engine.optAfmt.channels = 2;
-        engine.optAfmt.samples = engine.optAfmt.freq / 16;
-    }
-
-#ifdef DM_USE_JSS
-    jssInit();
-
-    switch (engine.optAfmt.format)
-    {
-        case AUDIO_S16SYS:
-            engine.jss_format = JSS_AUDIO_S16;
-            break;
-        case AUDIO_U16SYS:
-            engine.jss_format = JSS_AUDIO_U16;
-            break;
-        case AUDIO_S8:
-            engine.jss_format = JSS_AUDIO_S8;
-            break;
-        case AUDIO_U8:
-            engine.jss_format = JSS_AUDIO_U8;
-            break;
-    }
-
-    dmPrint(1, "Initializing miniJSS mixer with fmt=%d, chn=%d, freq=%d\n",
-            engine.jss_format, engine.optAfmt.channels, engine.optAfmt.freq);
-
-    if ((engine.dev =
-         jvmInit(engine.jss_format, engine.optAfmt.channels,
-                 engine.optAfmt.freq, JMIX_AUTO)) == NULL)
-    {
-        dmError("jvmInit() returned NULL, voi perkele.\n");
-        goto error_exit;
-    }
-
-    if ((engine.plr = jmpInit(engine.dev)) == NULL)
-    {
-        dmError("jmpInit() returned NULL\n");
-        goto error_exit;
-    }
-#endif
-
-    // Initialize SDL audio
-    dmPrint(1, "Trying to init SDL audio with: fmt=%d, chn=%d, freq=%d\n",
-            engine.optAfmt.format, engine.optAfmt.channels,
-            engine.optAfmt.freq);
-
-    engine.optAfmt.callback = engineAudioCallback;
-
-    if (SDL_OpenAudio(&engine.optAfmt, NULL) < 0)
-    {
-        dmError("Couldn't open SDL audio: %s\n", SDL_GetError());
-        goto error_exit;
-    }
-
-    // Initialize SDL video
-    if (engine.demoInitPreVideo != NULL &&
-        (err = engine.demoInitPreVideo(&engine)) != DMERR_OK)
-    {
-        dmError("demoInitPreVideo() failed, %d: %s\n", err, dmErrorStr(err));
-        goto error_exit;
-    }
-
-    dmPrint(1, "Initializing SDL video %d x %d x %dbpp, flags=0x%08x\n",
-        engine.optVidWidth, engine.optVidHeight, engine.optVidDepth, engine.optVFlags);
-
-    engine.screen = SDL_CreateRGBSurface(SDL_SWSURFACE, engine.optVidWidth, engine.optVidHeight, engine.optVidDepth, 0, 0, 0, 0);
-    if (engine.screen == NULL)
-    {
-        dmError("Could not allocate video backbuffer surface.\n");
-        goto error_exit;
-    }
-
-    if (engine.demoInitPostVideo != NULL &&
-        (err = engine.demoInitPostVideo(&engine)) != DMERR_OK)
-    {
-        dmError("demoInitPostVideo() failed, %d: %s\n", err, dmErrorStr(err));
-        goto error_exit;
-    }
-
-
-error_exit:
-
-    // Setup GUI elements
-    createMainGUI();
-    createNewFile();
-    timelineView->setTimeline(currTimeline);
-    settingsRestore();
-    initEffectsAndResources();
-    statusMsg("Application started.");
-}
-
-
-DemoEditor::~DemoEditor()
-{
-    statusMsg("Shutting down.");
-
-    settingsSave();
-    delete demoView;
-    historyReset();
-
-    if (engine.screen)
-        SDL_FreeSurface(engine.screen);
-
-    SDL_LockAudio();
-    SDL_PauseAudio(1);
-#ifdef DM_USE_JSS
-    jmpClose(engine.plr);
-    jvmClose(engine.dev);
-    jssClose();
-#endif
-    SDL_UnlockAudio();
-
-    shutdownEffectsAndResources();
-
-    if (initSDL)
-        SDL_Quit();
-}
-
-
-int DemoEditor::getTimelineDuration()
-{
-    return timelineAudioTrack->getDuration();
-}
-
-
-void DemoEditor::updateResourceView()
-{
-}
-
-
-void DemoEditor::updateTimelineView()
-{
-    demoView->setEngineData(&engine);    
-
-    if (engine.audioRes != NULL)
-    {
-        timelineAudioTrack->setWaveform(
-            engine.audioRes->rdata, engine.audioRes->rdataSize,
-            engine.optAfmt.format, engine.optAfmt.channels,
-            engine.optAfmt.freq);
-    }
-
-    timelineAudioTrack->setOffset(currViewOffset);
-    timelineAudioTrack->setScale(currViewScale);
-    
-
-    timelineView->setTime(currFrameTime);
-    timelineView->setOffset(currViewOffset);
-    timelineView->setScale(currViewScale);
-
-    timelineScrollBar->setRange(0, getTimelineDuration());
-    timelineScrollBar->setValue(currViewOffset);
-}
-
-
-void DemoEditor::actionTimelineScrollChanged(int value)
-{   
-    currViewOffset = value;
-    updateTimelineView();
-}
-
-
-void DemoEditor::actionOffsetChanged(float value)
-{   
-    currViewOffset = value;
-    updateTimelineView();
-}
-
-
-void DemoEditor::actionTimeChanged(float value)
-{   
-    currFrameTime = value;
-    updateTimelineView();
-}
-
-
-void DemoEditor::actionTimelineChanged()
-{
-    updateMenuStates();
-    update();
-}
-
-
-int DemoEditor::initEffectsAndResources()
-{
-    int err;
-
-    currViewOffset = 0;
-    currFrameTime = 0;
-    currViewScale = 1.0f;
-    
-    // Initialize resource subsystem
-    statusMsg("Initializing resources subsystem.");
-    if ((err = reopenResources()) != DMERR_OK)
-        return err;
-
-    // Load resources
-    statusMsg("Loading resources, please wait...");
-    if ((err = loadResources()) != DMERR_OK)
-    {
-        dmError("Error loading resources, %d: %s.\n", err, dmErrorStr(err));
-        return err;
-    }
-
-
-    // Final initializations
-    statusMsg("Initializing custom demo data.");
-    if ((err = engine.demoInit(&engine)) != DMERR_OK)
-    {
-        dmError("Failure in demoInit(), %d: %s\n",
-            err, dmErrorStr(err));
-        return err;
-    }
-
-    // Initialize effects
-    statusMsg("Initializing effects ...");
-    if ((err = engineInitializeEffects(&engine)) != DMERR_OK)
-    {
-        dmError("Effects initialization failed, %d: %s\n",
-            err, dmErrorStr(err));
-        return err;
-    }
-
-    // Etc.
-    rehash();
-    return DMERR_OK;
-}
-
-
-void DemoEditor::shutdownEffectsAndResources()
-{
-    delete currTimeline;
-    dmFreePreparedTimelineData(engine.ptl);
-    engineShutdownEffects(&engine);
-    dmres_close(engine.resources);
-
-    if (engine.demoShutdown != NULL)
-        engine.demoShutdown(&engine);
-}
-
-
-void DemoEditor::rehash()
-{
-    timelineView->setTimeline(currTimeline);
-    updateResourceView();
-    updateTimelineView();
-    updateMenuStates();
-    update();
-}
-
-
-void DemoEditor::createNewFile()
-{
-    delete currTimeline;
-    currTimeline = new EDTimelineObject();
-
-    DMTimelineTrack *tr;
-    dmTimelineAddTrack(currTimeline->tl, &tr, "Penis");
-    
-    DMTimelineEvent *ev;
-    dmTimelineTrackAddEvent(tr, 500, 100, &ev);
-    
-    dmTimelineEventSetEffectByIndex(ev, 0);
-
-    historyReset();
-    updateMenuStates();
-    update();
-}
-
-
-void DemoEditor::readFromFile(QString filename)
-{
-    EDTimelineObject *tmp = new EDTimelineObject();
-    int ret = tmp->load(filename);
-    if (ret != DMERR_OK)
-    {
-        showFileErrorDialog("Loading demo blob file "+ filename, ret);
-        delete tmp;
-    }
-    else
-    {
-        delete currTimeline;
-        currTimeline = tmp;
-        rehash();
-    }
-}
-
-
-void DemoEditor::saveToFile(QString filename)
-{
-    int ret = currTimeline->save(filename);
-    if (ret != DMERR_OK)
-    {
-        showFileErrorDialog("Saving demo blob file "+ filename, ret);
-    }
-
-    updateMenuStates();
-}
-
-
-void DemoEditor::settingsRestore()
-{
-    QSettings s;
-
-    restoreGeometry(s.value("windowGeometry").toByteArray());
-    restoreState(s.value("windowState").toByteArray());
-}
-
-
-void DemoEditor::settingsSave()
-{
-    QSettings s;
-
-    s.setValue("windowGeometry", saveGeometry());
-    s.setValue("windowState", saveState());
-}
-
-
-//
-// Edit history functionality
-//
-void DemoEditor::historyReset()
-{
-    if (currTimeline != NULL)
-        currTimeline->scrub();
-
-    undoHistoryPos = -1;
-    undoHistoryMax = DOC_UNDO_MAX;
-    undoHistory.clear();
-}
-
-
-void DemoEditor::historyPush(QString description)
-{
-    if (currTimeline == NULL)
-        return;
-
-    if (!undoHistory.isEmpty() && undoHistory.last()->state() == "-")
-    {
-        delete undoHistory.takeLast();
-    }
-    
-    while (undoHistory.size() >= undoHistoryMax)
-    {
-        delete undoHistory.takeFirst();
-    }
-    
-    EDTimelineObject *copy = new EDTimelineObject(currTimeline);
-    copy->setState(description);
-    undoHistory.append(copy);
-}
-
-
-void DemoEditor::historyTop()
-{
-    if (currTimeline == NULL)
-        return;
-
-    EDTimelineObject *copy = new EDTimelineObject(currTimeline);
-    copy->setState("-");
-    undoHistory.append(copy);
-
-    undoHistoryPos = undoHistory.size() - 1;
-    currTimeline->touch();
-    updateTimelineView();
-    updateMenuStates();
-    update();
-}
-
-
-void DemoEditor::historyPop()
-{
-    if (!undoHistory.isEmpty())
-    {
-        delete undoHistory.takeLast();
-    }
-}
-
-
-void DemoEditor::performRedo()
-{
-    if (undoHistoryPos >= 0 && undoHistoryPos < undoHistory.size() - 1)
-    {
-        undoHistoryPos++;
-        delete currTimeline;
-        currTimeline = new EDTimelineObject(undoHistory.at(undoHistoryPos));
-        currTimeline->touch();
-        updateTimelineView();
-        updateMenuStates();
-        update();
-    }
-}
-
-
-void DemoEditor::performUndo()
-{
-    if (undoHistoryPos > 0 && undoHistory.size() > 1)
-    {
-        undoHistoryPos--;
-        delete currTimeline;
-        currTimeline = new EDTimelineObject(undoHistory.at(undoHistoryPos));
-        currTimeline->touch();
-        updateTimelineView();
-        updateMenuStates();
-        update();
-    }
-}
--- a/edmain.h	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,132 +0,0 @@
-#ifndef EDMAIN_H
-#define EDMAIN_H
-
-// Program name etc
-#define PROGRAM_NAME      "DMPE Editor"
-#define PROGRAM_VERSION   "0.1"
-
-// Defaults
-#define DOC_DEF_FILENAME  "Untitled"
-#define DOC_UNDO_MAX	  15
-
-enum
-{
-    CTRL_REWIND,
-    CTRL_PLAY_START,
-    CTRL_PLAY_CURRENT,
-    CTRL_PAUSE
-};
-
-#define CTRL_ICON_SIZE   32
-
-#include "edtlobj.h"
-#include "edview.h"
-#include "edwaveform.h"
-#include "edtimeline.h"
-#include "edres.h"
-#include <QDebug>
-#include <QFile>
-#include <QApplication>
-#include <QMainWindow>
-#include <QTableView>
-#include <QAction>
-#include <QActionGroup>
-#include <QSlider>
-#include <QMessageBox>
-#include <QCheckBox>
-#include <QScrollBar>
-
-class DemoEditor : public QMainWindow
-{
-    Q_OBJECT
-
-public:
-    DemoEditor();
-    ~DemoEditor();
-
-    void settingsRestore();
-    void settingsSave();
-
-
-private slots:
-    void actionFileNew();
-    void actionFileOpen();
-    void actionFileSave();
-    void actionFileSaveAs();
-
-    void actionAboutBox();
-    
-    //void actionCut();
-    //void actionCopy();
-    //void actionPaste();
-    //void actionDelete();
-    
-    void performUndo();
-    void performRedo();
-
-    void actionControlChanged(QAction *);
-    void actionTimelineScrollChanged(int);
-    void actionOffsetChanged(float);
-    void actionTimeChanged(float);
-    void actionTimelineChanged();
-
-private:
-    QAction *menuActUndo, *menuActRedo, *menuActOpen, *menuActSave, *menuActSaveAs;
-//    QAction *menuActCut, *menuActCopy, *menuActPaste, *menuActDelete;
-    QActionGroup *actGroupControls;
-    QScrollBar *timelineScrollBar;
-    QTableView *resourceView;
-    QEDResourceModel *resourceModel;
-    QEDWaveTrackView *timelineAudioTrack;
-    QEDTimelineView *timelineView;
-    QEDGLDemoView *demoView;
-    
-    
-
-    QAction * createToolButton(QActionGroup *group, QString name, QIcon icon, QString statustip, QVariant data);
-    QAction * createMenuAction(QString name, const QKeySequence &shortcut, QString tooltip);
-    QAction * createMenuGroupAction(QMenu *, QActionGroup *, QString name, const QKeySequence &shortcut, QString tooltip, QVariant data);
-    void setActionGroupChecked(QActionGroup *group, QVariant data);
-
-
-    void showFileErrorDialog(QString operation, int code);
-    QMessageBox::StandardButton showDocumentModifiedDialog();
-    void statusMsg(QString message);
-    void closeEvent(QCloseEvent *event);
-    void createMainGUI();
-    void updateResourceView();
-    void updateMenuStates();
-    void updateTimelineView();
-
-    int initEffectsAndResources();
-    void shutdownEffectsAndResources();
-    void rehash();
-    int reopenResources();
-    int loadResources();
-    bool initializeVideo();
-    int getTimelineDuration();    
-
-    void createNewFile();
-    void readFromFile(QString filename);
-    void saveToFile(QString filename);
-
-
-    bool initSDL;
-    float currViewScale;
-    int currViewOffset;
-    int currFrameTime;
-
-    EDTimelineObject *currTimeline;
-    DMEngineData engine;
-
-    QList<EDTimelineObject *> undoHistory;
-    int undoHistoryPos, undoHistoryMax;
-
-    void historyReset();
-    void historyPush(QString description);
-    void historyTop();
-    void historyPop();
-};
-
-
-#endif // EDMAIN_H
--- a/edres.cpp	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-#include "edres.h"
-#include "dmengine.h"
-
-
-QEDResourceModel::QEDResourceModel(QObject *parent)
-    : QAbstractTableModel(parent)
-{
-}
-
-
-int QEDResourceModel::rowCount(const QModelIndex &parent) const
-{
-    Q_UNUSED(parent);
-    return nengineEffects;
-}
-
-
-int QEDResourceModel::columnCount(const QModelIndex &parent) const
-{
-    Q_UNUSED(parent);
-    return 2;
-}
-
-
-QVariant QEDResourceModel::data(const QModelIndex &index, int role) const
-{
-    if (!index.isValid())
-        return QVariant();
-
-    if (index.row() >= nengineEffects || index.row() < 0)
-        return QVariant();
-
-    if (role == Qt::DisplayRole)
-    {
-        DMEffect *ef = &engineEffects[index.row()];
-        switch (index.column())
-        {
-            case 0:
-                return QString(ef->name);
-
-            case 1:
-                return QVariant(ef->nparams);
-        }
-    }
-    return QVariant();
-}
-
-
-QVariant QEDResourceModel::headerData(int section, Qt::Orientation orientation, int role) const
-{
-    if (role != Qt::DisplayRole)
-        return QVariant();
-
-    if (orientation == Qt::Horizontal)
-    {
-        switch (section) {
-            case 0:
-                return "Name";
-
-            case 1:
-                return "# params";
-        }
-    }
-    return QVariant();
-}
-
-
-Qt::ItemFlags QEDResourceModel::flags(const QModelIndex &index) const
-{
-    if (!index.isValid())
-        return Qt::ItemIsEnabled;
-
-    return QAbstractTableModel::flags(index); // | Qt::ItemIsEditable;
-}
--- a/edres.h	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-#ifndef RESOURCEMODEL_H
-#define RESOURCEMODEL_H
-
-#include <QAbstractTableModel>
-#include <QPair>
-#include <QList>
-
-class QEDResourceModel : public QAbstractTableModel
-{
-    Q_OBJECT
-    
-public:
-    QEDResourceModel(QObject *parent = 0);
-
-    int rowCount(const QModelIndex &parent) const;
-    int columnCount(const QModelIndex &parent) const;
-    QVariant data(const QModelIndex &index, int role) const;
-    QVariant headerData(int section, Qt::Orientation orientation, int role) const;
-    Qt::ItemFlags flags(const QModelIndex &index) const;
-//    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
-//    QList< QPair<QString, QString> > getList();
-
-private:
-};
-
-#endif
--- a/edtimeline.cpp	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,471 +0,0 @@
-#include <QtGui>
-#include "edtimeline.h"
-
-
-QEDTimelineTrackDisplay::QEDTimelineTrackDisplay(QWidget *parent) : QWidget(parent)
-{
-    track = NULL;
-    time = offs = 0;
-    scale = 1.0f;
-    
-    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
-}
-
-
-QSize QEDTimelineTrackDisplay::minimumSizeHint() const
-{
-    return QSize(100, 60);
-}
-
-
-QSize QEDTimelineTrackDisplay::sizeHint() const
-{
-    return QSize(600, 60);
-}
-
-
-void QEDTimelineTrackDisplay::setTrack(DMTimelineTrack *mtrack)
-{
-    track = mtrack;
-}
-
-
-float QEDTimelineTrackDisplay::getTimeScale(float value)
-{
-    return value * scale;
-}
-
-
-float QEDTimelineTrackDisplay::getTimeFromCoord(float value)
-{
-    return value * scale * 1000.0f;
-}
-
-
-void QEDTimelineTrackDisplay::setTime(const float mtime)
-{
-    if (time != mtime && mtime >= 0)
-    {
-        time = mtime;
-        emit timeChanged(time);
-    }
-}
-
-
-void QEDTimelineTrackDisplay::setOffset(const float moffs)
-{
-    if (offs != moffs && moffs >= 0)
-    {
-        offs = moffs;
-        emit offsetChanged(offs);
-    }
-}
-
-
-void QEDTimelineTrackDisplay::setScale(const float mscale)
-{
-    if (mscale > 0.05)
-        scale = mscale;
-}
-
-
-void QEDTimelineTrackDisplay::setSelection(const float mstart, const float mend)
-{
-    if (mstart >= 0 && mend >= 0 && fabs(mend - mstart) > 0)
-    {
-        selectionValid = true;
-        if (mend > mstart)
-        {
-            selectionStart    = mstart;
-            selectionDuration = mend - mstart + 1;
-        }
-        else
-        {
-            selectionStart    = mend;
-            selectionDuration = mstart - mend + 1;
-        }
-        emit selectionChanged(selectionStart, selectionDuration);
-    }
-}
-
-
-void QEDTimelineTrackDisplay::clearSelection()
-{
-    selectionValid    = false;
-    selectionStart    = 0;
-    selectionDuration = 0;
-    emit selectionChanged(selectionStart, selectionDuration);
-}
-
-
-bool QEDTimelineTrackDisplay::getSelection(float *mstart, float *mduration)
-{
-    if (selectionValid)
-    {
-        *mstart = selectionStart;
-        *mduration = selectionDuration;
-    }
-    return selectionValid;
-}
-
-
-QList<DMTimelineEvent *> QEDTimelineTrackDisplay::getEventsAt(const int time)
-{
-    QList<DMTimelineEvent *> list;
-
-    for (int event = 0; event < track->nevents; event++)
-    {
-        DMTimelineEvent *ev = track->events[event];
-        if (time >= ev->start && time <= ev->start + ev->duration)
-            list.append(ev);
-    }
-
-    return list;
-}
-
-
-
-QList<DMTimelineEvent *> QEDTimelineTrackDisplay::getEventsForRange(const int start, const int duration)
-{
-    QList<DMTimelineEvent *> list;
-
-    for (int event = 0; event < track->nevents; event++)
-    {
-        DMTimelineEvent *ev = track->events[event];
-    }
-
-    return list;
-}
-
-
-void QEDTimelineTrackDisplay::paintEvent(QPaintEvent *)
-{
-    if (track == NULL)
-        return;
-
-    QColor eventColor(150, 150, 150, 128);
-    QColor invalidEventColor(250, 150, 150, 128);
-    QColor eventBorder(200, 250, 200, 200);
-    QColor eventParam(200, 150, 100);
-    QColor eventText(255, 255, 255);
-    QColor markerColor(255,255,255);
-    QColor selectionColor(0,255,0, 150);
-    QColor selectionEnd(0,255,0, 200);
-
-    QFont fantti;
-    fantti.setFamily("Arial");
-    fantti.setPointSizeF(8.0f);
-    fantti.setStyleHint(QFont::SansSerif);
-
-    QPainter painter(this);
-    painter.setRenderHint(QPainter::Antialiasing);
-
-
-    painter.save();
-    painter.scale(scale, 1);
-
-    float wd = getTimeScale(width());
-    for (int event = 0; event < track->nevents; event++)
-    {
-        DMTimelineEvent *ev = track->events[event];
-
-        float x0 = getTimeScale(ev->start - offs),
-              x1 = getTimeScale(ev->start + ev->duration - offs);
-
-        if ((x0 >= 0 && x0 < wd) || (x0 < 0 && x1 >= 0))
-        {
-            painter.setFont(fantti);
-            painter.setBrush(ev->effect != NULL ? eventColor : invalidEventColor);
-            painter.setPen(eventBorder);
-            x0 = ev->start - offs;
-            x1 = ev->duration;
-            painter.fillRect(x0, 0, x1, height(), eventColor);
-
-            QPainterPath path;
-            path.addText(QPointF(x0 + 2, 10), fantti, ev->effect != NULL ? QString(ev->effect->name) : "INVALID");
-
-            painter.save();
-            painter.translate(1,1);
-            painter.setPen(Qt::black);
-            painter.setBrush(Qt::black);
-            painter.drawPath(path);
-            painter.restore();
-
-            painter.setPen(eventText);
-            painter.setBrush(eventText);
-            painter.drawPath(path);
-        }
-
-    }
-
-    painter.restore();
-
-    if (selectionValid)
-    {
-        float x0 = getTimeScale(selectionStart - offs),
-              x1 = getTimeScale(selectionStart + selectionDuration - offs);
-
-        if ((x0 >= 0 && x0 < wd) || (x0 < 0 && x1 >= 0))
-        {
-            painter.setBrush(selectionColor);
-            painter.setPen(selectionEnd);
-            x0 = selectionStart - offs;
-            x1 = selectionDuration;
-            painter.fillRect(x0, 0, x1, height(), eventColor);
-            
-            painter.drawLine(x0, 0, x0, height());
-            painter.drawLine(x1, 0, x1, height());
-        }
-    }
-
-
-    if (time >= offs * scale && time - offs <= width() * scale)
-    {
-        int xc = time - offs;
-        painter.save();
-        painter.scale(scale, 1);
-        painter.setPen(markerColor);
-        painter.drawLine(xc, 0, xc, height());
-        painter.restore();
-    }
-}
-
-
-void QEDTimelineTrackDisplay::mousePressEvent(QMouseEvent *ev)
-{
-    switch (ev->button())
-    {
-        case Qt::LeftButton:
-            if (parent->getActiveTrack() != this)
-                emit trackActivated(this);
-
-            selectionPoint = ev->pos();
-            selectionOffs = offs / scale;
-            selecting = false;
-            break;
-
-        case Qt::RightButton:
-            dragPoint = ev->pos();
-            dragOffs = offs / scale;
-            dragging = false;
-            break;
-
-        default:
-            break;
-    }
-}
-
-
-void QEDTimelineTrackDisplay::mouseMoveEvent(QMouseEvent *ev)
-{
-    if ((ev->buttons() & Qt::LeftButton) && ev->pos().x() != selectionPoint.x())
-    {
-        selecting = true;
-        setSelection(selectionOffs, offs + (ev->pos().x() - selectionPoint.x()) / scale);
-    }
-    if ((ev->buttons() & Qt::RightButton) && ev->pos().x() != dragPoint.x())
-    {
-        dragging = true;
-        setOffset(dragOffs - (ev->pos().x() - dragPoint.x()) / scale);
-    }
-}
-
-
-void QEDTimelineTrackDisplay::mouseReleaseEvent(QMouseEvent *ev)
-{
-    if (ev->button() == Qt::LeftButton)
-    {
-        if (selecting)
-        {
-            selecting = false;
-            setSelection(selectionOffs + (ev->pos().x() - selectionPoint.x()) / scale);
-        }
-    }
-    else
-    if (ev->button() == Qt::RightButton && !dragging)
-    {
-        setTime(offs + getTimeFromCoord(ev->pos().x()));
-    }
-}
-
-
-QEDTimelineTrackView::QEDTimelineTrackView(QWidget *parent) : QWidget(parent)
-{
-    QHBoxLayout *mainLayout = new QHBoxLayout(this);
-    mainLayout->setMargin(0);
-    track = new QEDTimelineTrackDisplay(this);
-
-    QFrame *infoLayoutContainer = new QFrame(this);
-    infoLayoutContainer->setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
-    infoLayoutContainer->setLineWidth(2);
-    infoLayoutContainer->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
-    infoLayoutContainer->setFixedWidth(200);
-
-    QVBoxLayout *infoLayout = new QVBoxLayout(infoLayoutContainer);
-    infoLayout->setMargin(0);
-    infoName = new QLineEdit();
-    infoName->setFrame(false);
-    infoName->setMaxLength(DT_MAX_NAME_LENGTH);
-    infoName->setStyleSheet("QLineEdit { background-color: black; color: white; padding: 2px; }");
-    connect(infoName, SIGNAL(textEdited(const QString&)), this, SLOT(slotTrackNameChanged(const QString&)));
-    infoLayout->addWidget(infoName);
-
-
-    enabledCheck = new QCheckBox("Enabled");
-    infoLayout->addWidget(enabledCheck);
-    connect(enabledCheck, SIGNAL(toggled(bool)), this, SLOT(slotTrackEnabledChanged(bool)));
-    
-    infoData = new QLabel();
-    infoData->setStyleSheet("QLabel { padding: 2px; }");
-    infoLayout->addWidget(infoData);
-
-    mainLayout->addWidget(infoLayoutContainer);
-    mainLayout->addWidget(track);
-}
-
-
-void QEDTimelineTrackView::update()
-{
-    if (track != NULL && track->track)
-    {
-        infoName->setText(QString(track->track->name));
-        enabledCheck->setChecked(track->track->enabled);
-        infoData->setText(QString("<b>%1</b> events").arg(track->track->nevents));
-    }
-    else
-    {
-        infoName->setText("");
-        infoData->setText("-");
-        enabledCheck->setChecked(false);
-    }
-
-    QWidget::update();
-}
-
-
-void QEDTimelineTrackView::setTrack(DMTimelineTrack *mtrack)
-{
-    track->setTrack(mtrack);
-    update();
-}
-
-
-void QEDTimelineTrackView::slotTrackEnabledChanged(bool value)
-{
-    track->track->enabled = value;
-    emit trackChanged();
-}
-
-
-void QEDTimelineTrackView::slotTrackNameChanged(const QString & text)
-{
-    QByteArray ba = text.toUtf8();
-    track->track->name = dm_strdup(ba.constData());
-    emit trackChanged();
-}
-
-
-
-QEDTimelineView::QEDTimelineView(QWidget *parent) : QWidget(parent)
-{
-    layout = new QVBoxLayout(this);
-    tl = NULL;
-}
-
-
-void QEDTimelineView::setTimeline(EDTimelineObject *mtl)
-{
-    tl = mtl;
-
-    delete layout;
-    layout = new QVBoxLayout(this);
-    layout->setMargin(0);
-    
-    tracks.clear();
-
-    if (tl != NULL && tl->tl != NULL)
-    {
-        for (int track = 0; track < tl->tl->ntracks; track++)
-        {
-            QEDTimelineTrackView *vtr = new QEDTimelineTrackView(this);
-            vtr->setTrack(tl->tl->tracks[track]);
-            tracks.append(vtr);
-            layout->addWidget(vtr);
-            connect(vtr, SIGNAL(trackChanged()), this, SLOT(slotTimelineChanged()));
-            connect(vtr, SIGNAL(timeChanged(float)), this, SLOT(slotTimeChanged(float)));
-            connect(vtr, SIGNAL(offsetChanged(float)), this, SLOT(slotOffsetChanged(float)));
-            connect(vtr, SIGNAL(selectionChanged(float,float)), this, SLOT(slotSelectionChanged(float,float)));
-        }
-    }
-    update();
-}
-
-
-void QEDTimelineView::slotTimelineChanged()
-{
-    if (tl != NULL)
-    {
-        tl->touch();
-        emit timelineChanged();
-    }
-}
-
-
-void QEDTimelineView::setTime(const int mtime)
-{
-    if (tl != NULL && tl->tl != NULL)
-    {
-        QList<QEDTimelineTrackView *>::iterator track;
-        for (track = tracks.begin(); track != tracks.end(); track++)
-        {
-            (*track)->track->setTime(mtime);
-        }
-        update();
-    }
-}
-
-
-void QEDTimelineView::setOffset(const int moffs)
-{
-    if (tl != NULL && tl->tl != NULL)
-    {
-        QList<QEDTimelineTrackView *>::iterator track;
-        for (track = tracks.begin(); track != tracks.end(); track++)
-        {
-            (*track)->track->setOffset(moffs);
-        }
-        update();
-    }
-}
-
-
-void QEDTimelineView::setScale(const float mscale)
-{
-    if (tl != NULL && tl->tl != NULL)
-    {
-        QList<QEDTimelineTrackView *>::iterator track;
-        for (track = tracks.begin(); track != tracks.end(); track++)
-        {
-            (*track)->track->setScale(mscale);
-        }
-        update();
-    }
-}
-
-
-QList<DMTimelineEvent *> QEDTimelineView::getEventsAt(const int time)
-{
-    if (tl != NULL && tl->tl != NULL &&
-        activeTrack >= 0 && activeTrack < tl->tl->ntracks)
-    {
-        return tracks[activeTrack]->tl->getEventsAt(time);
-    }
-    else
-        return QList<DMTimelineEvent *>();
-}
-
-
-QList<DMTimelineEvent *> QEDTimelineView::getEventsForRange(const int start, const int duration)
-{
-}
-
--- a/edtimeline.h	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,121 +0,0 @@
-#ifndef EDTIMELINE_H
-#define EDTIMELINE_H
-
-#include <QWidget>
-#include <QCheckBox>
-#include <QVBoxLayout>
-#include <QLabel>
-#include <QLineEdit>
-#include "edtlobj.h"
-#include "dmengine.h"
-
-
-class QEDTimelineTrackDisplay : public QWidget
-{
-    Q_OBJECT
-
-public:
-    DMTimelineTrack *track;
-
-    QEDTimelineTrackDisplay(QWidget *parent = 0);
-    void setTrack(DMTimelineTrack *mtrack);
-
-    float getTimeScale(float value);
-    float getTimeFromCoord(float value);
-
-    void setScale(const float mscale);
-
-    bool getSelection(float *mstart, float *mduration);
-    QList<DMTimelineEvent *> getEventsAt(const int time);
-    QList<DMTimelineEvent *> getEventsForRange(const int start, const int duration);
-
-    QSize minimumSizeHint() const;
-    QSize sizeHint() const;
-
-public slots:
-    void setTime(const float mtime);
-    void setOffset(const float moffs);
-    void setSelection(const float mstart, const float mend);
-    void clearSelection();
-
-signals:
-    void selectionChanged(float mstart, float mduration);
-    void timeChanged(float value);
-    void offsetChanged(float value);
-
-protected:
-    void mousePressEvent(QMouseEvent *event);
-    void mouseMoveEvent(QMouseEvent *event);
-    void mouseReleaseEvent(QMouseEvent *event);
-
-    void paintEvent(QPaintEvent *event);
-
-private:
-    float scale, time, offs;
-
-    bool selectionValid;
-    float selectionStart, selectionDuration;
-
-    QPoint selectionPoint, dragPoint;
-    bool selecting, dragging;
-    float selectionOffs, dragOffs;
-};
-
-
-class QEDTimelineTrackView : public QWidget
-{
-    Q_OBJECT
-
-private:
-    QLineEdit *infoName;
-    QLabel *infoData;
-    QCheckBox *enabledCheck;
-
-public:
-    QEDTimelineTrackDisplay *track;
-
-    QEDTimelineTrackView(QWidget *parent = 0);
-    void setTrack(DMTimelineTrack *mtrack);
-    void update();
-
-private slots:
-    void slotTrackEnabledChanged(bool);
-    void slotTrackNameChanged(const QString & text);
-
-signals:
-    void trackChanged();
-    void selectionChanged(float mstart, float mduration);
-    void timeChanged(float value);
-    void offsetChanged(float value);
-};
-
-
-class QEDTimelineView : public QWidget
-{
-    Q_OBJECT
-
-private:
-    QVBoxLayout *layout;
-
-    EDTimelineObject *tl;
-    QList<QEDTimelineTrackView *> tracks;
-    int activeTrack;
-
-public:
-    QEDTimelineView(QWidget *parent = 0);
-    void setTimeline(EDTimelineObject *mtl);
-
-    void setTime(const float mtime);
-    void setOffset(const float moffs);
-    void setScale(const float mscale);
-
-private slots:
-    void slotTimelineChanged();
-
-signals:
-    void timelineChanged();
-    void timeChanged(float value);
-    void offsetChanged(float value);
-};
-
-#endif
--- a/edtlobj.cpp	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-//
-// Demo Editor -- Timeline state object
-// Wrapper class for DMTimeline data
-// (C) Copyright 2012 Matti 'ccr' Hämäläinen <ccr@tnsp.org>
-//
-#include "edtlobj.h"
-#include "dmres.h"
-
-
-EDTimelineObject::EDTimelineObject()
-{
-    dmTimelineNew(&tl, "Demo");
-    scrub();
-}
-
-
-EDTimelineObject::EDTimelineObject(EDTimelineObject *obj)
-{
-    scrub();
-    filename = obj->filename;
-    dmCopyTimeline(obj->tl, &tl);
-}
-
-
-EDTimelineObject::~EDTimelineObject()
-{
-    dmFreeTimeline(tl);
-}
-
-
-int EDTimelineObject::load(QString mfilename)
-{
-    QByteArray fnba = mfilename.toUtf8();
-    DMResource *res;
-    DMTimeline *tmp;
-    if ((res = dmf_create_stdio(fnba.data(), "rb")) == NULL)
-        return DMERR_FOPEN;
-
-    int err = dmLoadTimeline(res, &tmp);
-    dmf_close(res);
-
-    if (err == DMERR_OK)
-    {
-        dmFreeTimeline(tl);
-        tl = tmp;
-        scrub();
-        if (tl->name != NULL)
-            filename = QString(tl->name);
-        else
-            filename = mfilename;
-    }
-    else
-    {
-        dmFreeTimeline(tmp);
-    }
-    return err;
-}
-
-
-int EDTimelineObject::save(QString mfilename)
-{
-    QByteArray fnba = mfilename.toUtf8();
-    DMResource *res;
-    if ((res = dmf_create_stdio(fnba.data(), "wb")) == NULL)
-        return DMERR_FOPEN;
-
-    int err = dmSaveTimeline(res, tl);
-    scrub();
-
-    dmf_close(res);
-    return err;
-}
-
-
-void EDTimelineObject::touch()
-{
-    ntouches++;
-}
-
-
-void EDTimelineObject::scrub()
-{
-    ntouches = 0;
-}
-
-
-bool EDTimelineObject::touched()
-{
-    return ntouches;
-}
-
-
-void EDTimelineObject::setState(const QString &mstate)
-{
-    cstate = mstate;
-}
-
-
-QString EDTimelineObject::state() const
-{
-    return cstate;
-}
--- a/edtlobj.h	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-//
-// Demo Editor -- Timeline state object
-// Wrapper class for DMTimeline data
-// (C) Copyright 2012 Matti 'ccr' Hämäläinen <ccr@tnsp.org>
-//
-#ifndef EDTIMELINEOBJECT_H
-#define EDTIMELINEOBJECT_H
-
-#include "dmengine.h"
-#include <QString>
-
-class EDTimelineObject
-{
-private:
-    QString cstate;
-    int ntouches;
-
-public:
-    QString filename;
-    DMTimeline *tl;
-
-    EDTimelineObject();
-    EDTimelineObject(EDTimelineObject *);
-    ~EDTimelineObject();
-
-    int load(QString filename);
-    int save(QString filename);
-    
-    void scrub();
-    void touch();
-    bool touched();
-
-    void setState(const QString &mstate);
-    QString state() const;
-};
-
-
-#endif
--- a/edview.cpp	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,148 +0,0 @@
-#include "edview.h"
-#include <QtGui>
-
-
-QEDGLDemoView::QEDGLDemoView(QWidget *parent) :
-    QGLWidget(QGLFormat(QGL::SampleBuffers|QGL::AlphaChannel), parent)
-{
-    makeCurrent();
-
-    if (QGLFramebufferObject::hasOpenGLFramebufferBlit())
-    {
-        QGLFramebufferObjectFormat format;
-        format.setSamples(4);
-        format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
-
-//        render_fbo = new QGLFramebufferObject(512, 512, format);
-//        texture_fbo = new QGLFramebufferObject(512, 512);
-    }
-    else
-    {
-//        render_fbo = new QGLFramebufferObject(1024, 1024);
-//        texture_fbo = render_fbo;
-    }
-
-    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
-}
-
-
-QEDGLDemoView::~QEDGLDemoView()
-{
-}
-
-
-QSize QEDGLDemoView::minimumSizeHint() const
-{
-    return QSize(320, 240);
-}
-
-
-QSize QEDGLDemoView::sizeHint() const
-{
-    return QSize(640, 480);
-}
-
-
-void QEDGLDemoView::setEngineData(DMEngineData *mengine)
-{
-    engine = mengine;
-}
-
-
-void QEDGLDemoView::render(int frameTime)
-{
-    if (engine != NULL)
-    {
-        engine->frameTime = frameTime;
-
-        if (engine->demoRender != NULL)
-        {
-            engine->demoRender(engine);
-        }
-        else
-        {
-            dmExecuteTimeline(engine->ptl, engine->screen, engineGetTick(engine));
-        }
-        
-        engine->frameCount++;
-    }
-}
-
-
-void QEDGLDemoView::paintEvent(QPaintEvent *)
-{
-    // save the GL state set for QPainter
-    saveGLState();
-
-    // restore the GL state that QPainter expects
-    restoreGLState();
-}
-
-
-void QEDGLDemoView::saveGLState()
-{
-    glPushAttrib(GL_ALL_ATTRIB_BITS);
-    glMatrixMode(GL_PROJECTION);
-    glPushMatrix();
-    glMatrixMode(GL_MODELVIEW);
-    glPushMatrix();
-}
-
-
-void QEDGLDemoView::restoreGLState()
-{
-    glMatrixMode(GL_PROJECTION);
-    glPopMatrix();
-    glMatrixMode(GL_MODELVIEW);
-    glPopMatrix();
-    glPopAttrib();
-}
-
-
-QEDSWDemoView::QEDSWDemoView(QWidget *parent) : QEDGLDemoView(parent)
-{
-    img = NULL;
-}
-
-
-QEDSWDemoView::~QEDSWDemoView()
-{
-    delete img;
-}
-
-
-void QEDSWDemoView::setEngineData(DMEngineData *mengine)
-{
-    engine = mengine;
-    delete img;
-
-    img = new QImage((const uchar *)mengine->screen->pixels,
-        mengine->screen->w, mengine->screen->h,
-        mengine->screen->pitch, QImage::Format_RGB32);
-}
-
-
-void QEDSWDemoView::paintEvent(QPaintEvent *)
-{
-    if (img != NULL)
-    {
-        QPainter painter(this);
-        painter.drawImage(QPoint(0, 0), *img);
-    }
-}
-
-
-void QEDSWDemoView::render(int frameTime)
-{
-    if (SDL_MUSTLOCK(engine->screen) != 0 && SDL_LockSurface(engine->screen) != 0)
-        return;
-
-    QEDGLDemoView::render(frameTime);
-
-    if (SDL_MUSTLOCK(engine->screen) != 0)
-        SDL_UnlockSurface(engine->screen);
-
-    update();
-}
-
-
--- a/edview.h	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-#ifndef EDVIEW_H
-#define EDVIEW_H
-
-#include <QtOpenGL>
-#include <QImage>
-#include "dmengine.h"
-
-
-class QEDGLDemoView : public QGLWidget
-{
-    Q_OBJECT
-
-public:
-    QEDGLDemoView(QWidget *parent);
-    ~QEDGLDemoView();
-
-    QSize minimumSizeHint() const;
-    QSize sizeHint() const;
-
-    virtual void setEngineData(DMEngineData *mengine);
-    virtual void render(int frameTime);
-    void paintEvent(QPaintEvent *);
-
-private:
-    void saveGLState();
-    void restoreGLState();
-
-protected:
-    DMEngineData *engine;
-};
-
-
-class QEDSWDemoView : public QEDGLDemoView
-{
-    Q_OBJECT
-
-public:
-    QEDSWDemoView(QWidget *parent);
-    ~QEDSWDemoView();
-
-    void setEngineData(DMEngineData *mengine);
-    void render(int frameTime);
-    void paintEvent(QPaintEvent *);
-
-private:
-    QImage *img;
-};
-
-
-#endif
--- a/edwaveform.cpp	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,418 +0,0 @@
-#include <QtGui>
-#include <SDL_audio.h>
-#include "edwaveform.h"
-
-
-QEDWaveTrackDisplay::QEDWaveTrackDisplay(QWidget *parent) : QWidget(parent)
-{
-    data      = NULL;
-    size      = 0;
-    format    = AUDIO_S16SYS;
-    channels  = 1;
-    freq      = 1;
-    scale     = 1.0f;
-    time      = offs = 0;
-    duration  = 0;
-    sduration = 0;
-
-    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
-}
-
-
-void QEDWaveTrackDisplay::setWaveform(void *mdata, int msize, int mformat, int mchannels, int mfreq)
-{
-    data     = mdata;
-    size     = msize;
-    format   = mformat;
-    channels = mchannels;
-    freq     = mfreq;
-
-    int bps = getBps();
-    if (bps != 0)
-    {
-        // Duration in milliseconds
-        duration  = ((float) (size / bps) / (float) freq) * 1000.0f;
-        
-        // Duration in samples
-        sduration = msize / bps;
-    }
-    else
-    {
-        duration  = 0;
-        sduration = 0;
-    }
-
-    update();
-}
-
-
-int QEDWaveTrackDisplay::getBps()
-{
-    int bps = channels;
-    switch (format)
-    {
-        case AUDIO_S16SYS:
-        case AUDIO_U16SYS:
-            bps *= sizeof(quint16);
-            break;
-        case AUDIO_S8:
-        case AUDIO_U8:
-            bps *= sizeof(quint8);
-            break;
-    }
-    return bps;
-}
-
-
-float QEDWaveTrackDisplay::getDuration()
-{
-    return duration;
-}
-
-
-float QEDWaveTrackDisplay::getTimeScale(float value)
-{
-    return (value * scale * (float) freq) / 1000.0f;
-}
-
-
-float QEDWaveTrackDisplay::getTimeFromCoord(float value)
-{
-    return value * scale;
-}
-
-
-void QEDWaveTrackDisplay::setTime(const float mtime)
-{
-    if (time != mtime && mtime >= 0 && mtime < duration)
-    {
-        time = mtime;
-        emit timeChanged(time);
-    }
-}
-
-
-void QEDWaveTrackDisplay::setOffset(const float moffs)
-{
-    if (offs != moffs && moffs >= 0 && moffs < getDuration())
-    {
-        offs = moffs;
-        emit offsetChanged(offs);
-    }
-}
-
-
-void QEDWaveTrackDisplay::setScale(const float mscale)
-{
-    if (mscale > 0.05)
-        scale = mscale;
-    emit scaleChanged(scale);
-}
-
-
-void QEDWaveTrackDisplay::setSelection(const float mstart, const float mend)
-{
-    if (mstart >= 0 && mend >= 0 && fabs(mend - mstart) > 0)
-    {
-        selectionValid = true;
-        if (mend > mstart)
-        {
-            selectionStart    = mstart;
-            selectionDuration = mend - mstart + 1;
-        }
-        else
-        {
-            selectionStart    = mend;
-            selectionDuration = mstart - mend + 1;
-        }
-        emit selectionChanged(selectionStart, selectionDuration);
-    }
-}
-
-
-void QEDWaveTrackDisplay::clearSelection()
-{
-    selectionValid    = false;
-    selectionStart    = 0;
-    selectionDuration = 0;
-    emit selectionChanged(selectionStart, selectionDuration);
-}
-
-
-bool QEDTimelineTrackDisplay::getSelection(float *mstart, float *mduration)
-{
-    if (selectionValid)
-    {
-        *mstart = selectionStart;
-        *mduration = selectionDuration;
-    }
-    return selectionValid;
-}
-
-
-float QEDWaveTrackDisplay::getScaledWidth()
-{
-    return getTimeScale(width());
-}
-
-
-float QEDWaveTrackDisplay::getTime()
-{
-    return time;
-}
-
-
-float QEDWaveTrackDisplay::getOffset()
-{
-    return offs;
-}
-
-
-void QEDWaveTrackDisplay::paintEvent(QPaintEvent *)
-{
-    QColor waveColor(0, 150, 0);
-    QColor waveCenterLine(100, 100, 100);
-    QColor markerColor(255,255,255);
-    QColor bgColor(0, 0, 0);//255, 255, 255);
-
-    QPainter painter(this);
-    painter.setRenderHint(QPainter::Antialiasing);
-    painter.fillRect(QRect(0, 0, width(), height()), bgColor);
-
-    if (data != NULL)
-    {
-        int voffs = 0;
-
-        painter.save();
-        painter.translate(0, height() / 2);
-
-        painter.setPen(waveCenterLine);
-        painter.drawLine(0, 0, width(), 0);
-
-        switch (format)
-        {
-            case AUDIO_S16SYS:
-                painter.scale(1.0f, (float) height() / 32768.0f);
-                break;
-            case AUDIO_U16SYS:
-                voffs = -32768;
-                painter.scale(1.0f, height() / 32768.0f);
-                break;
-
-            case AUDIO_S8:
-                painter.scale(1.0f, height() / 128.0f);
-                break;
-            case AUDIO_U8:
-                voffs = -128;
-                painter.scale(1.0f, height() / 128.0f);
-                break;
-        }
-
-        painter.scale(1.0f, 0.5f);
-        painter.setPen(waveColor);
-
-        float mscale = (scale * (float)freq) / 1000.0f;
-        int prevY = 0, prevX = 0;
-        if (format == AUDIO_S16SYS || format == AUDIO_U16SYS)
-        {
-            qint16 *buf = (qint16 *) data;
-            for (int xc = 0; xc < width(); xc++)
-            {
-                int moffs = (int) (((offs + xc) * mscale));
-                if (moffs >= sduration) break;
-                int value = buf[moffs * channels] + voffs;
-                painter.drawLine(prevX, prevY, xc, value);
-                prevY = value;
-                prevX = xc;
-            }
-        }
-        else
-        if (format == AUDIO_S8 || format == AUDIO_U8)
-        {
-            qint8 *buf = (qint8 *) data;
-            for (int xc = 0; xc < width(); xc++)
-            {
-                int moffs = (int) (((offs + xc) * mscale));
-                if (moffs >= sduration) break;
-                int value = buf[moffs * channels] + voffs;
-                painter.drawLine(prevX, prevY, xc, value);
-                prevY = value;
-                prevX = xc;
-            }
-        }
-        
-        painter.restore();
-    }
-    
-    float xc = getTimeScale(time - offs), wd = getTimeScale(width());
-    if (xc >= 0 && xc <= wd)
-    {
-        xc = time - offs;
-        painter.scale(scale, 1.0f);
-        painter.setPen(markerColor);
-        painter.drawLine(xc, 0, xc, height());
-    }
-}
-
-
-void QEDWaveTrackDisplay::mousePressEvent(QMouseEvent *ev)
-{
-    switch (ev->button())
-    {
-        case Qt::LeftButton:
-            selectionPoint = ev->pos();
-            selectionOffs = offs;
-            selecting = false;
-            break;
-
-        case Qt::RightButton:
-            dragPoint = ev->pos();
-            dragOffs = offs;
-            dragging = false;
-            break;
-
-        default:
-            break;
-    }
-}
-
-
-void QEDWaveTrackDisplay::mouseMoveEvent(QMouseEvent *ev)
-{
-    if ((ev->buttons() & Qt::LeftButton) && ev->pos().x() != selPoint.x())
-    {
-        selecting = true;
-        setSelection(selectionOffs + (ev->pos().x() - selectionPoint.x()) / scale);
-    }
-    if ((ev->buttons() & Qt::RightButton) && ev->pos().x() != dragPoint.x())
-    {
-        dragging = true;
-        setOffset(dragOffs - (ev->pos().x() - dragPoint.x()) / scale);
-    }
-}
-
-
-void QEDWaveTrackDisplay::mouseReleaseEvent(QMouseEvent *ev)
-{
-    if (ev->button() == Qt::LeftButton)
-    {
-        if (selecting)
-        {
-            selecting = false;
-            setSelection(selOffs + (ev->pos().x() - selPoint.x()) / scale);
-        }
-    }
-    else
-    if (ev->button() == Qt::RightButton && !dragging)
-    {
-        setTime(offs + getTimeFromCoord(ev->pos().x()));
-    }
-}
-
-
-QEDWaveTrackView::QEDWaveTrackView(QWidget *parent) : QWidget(parent)
-{
-    QHBoxLayout *mainLayout = new QHBoxLayout(this);
-    mainLayout->setMargin(0);
-    wave = new QEDWaveTrackDisplay(this);
-
-    QFrame *infoLayoutContainer = new QFrame(this);
-    infoLayoutContainer->setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
-    infoLayoutContainer->setLineWidth(2);
-    infoLayoutContainer->setFixedWidth(200);
-
-    QVBoxLayout *infoLayout = new QVBoxLayout(infoLayoutContainer);
-    infoLayout->setMargin(0);
-    infoName = new QLabel("Audio");
-    infoName->setStyleSheet("QLabel { background-color: black; color: white; padding: 2px; }");
-    infoLayout->addWidget(infoName);
-
-    infoData = new QLabel();
-    infoData->setStyleSheet("QLabel { padding: 2px; }");
-    infoLayout->addWidget(infoData);
-
-    mainLayout->addWidget(infoLayoutContainer);
-    mainLayout->addWidget(wave);
-
-    connect(wave, SIGNAL(timeChanged(float)), this, SLOT(slotTimeChanged(float)));
-    connect(wave, SIGNAL(offsetChanged(float)), this, SLOT(slotOffsetChanged(float)));
-    connect(wave, SIGNAL(selectionChanged(float,float)), this, SLOT(slotSelectionChanged(float,float)));
-}
-
-
-void QEDWaveTrackView::setWaveform(void *mdata, int msize, int mformat, int mchannels, int mfreq)
-{
-    QString fmt;
-    switch (mformat)
-    {
-        case AUDIO_S16SYS: fmt = "16bit (S)"; break;
-        case AUDIO_U16SYS: fmt = "16bit (U)"; break;
-        case AUDIO_S8:     fmt = "8bit (S)"; break;
-        case AUDIO_U8:     fmt = "8bit (U)"; break;
-        default:           fmt = "?"; break;
-    }
-    infoData->setText(QString("<b>%1</b>, <b>%2</b> ch, <b>%3</b> Hz").arg(fmt).arg(mchannels).arg(mfreq));
-    wave->setWaveform(mdata, msize, mformat, mchannels, mfreq);
-    update();
-}
-
-
-void QEDWaveTrackView::setName(QString name)
-{
-    infoName->setText(name);
-    update();
-}
-
-
-void QEDWaveTrackView::setTime(const float mtime)
-{
-    wave->setTime(mtime);
-}
-
-
-void QEDWaveTrackView::setOffset(const float moffs)
-{
-    wave->setOffset(moffs);
-}
-
-
-void QEDWaveTrackView::setScale(const float mscale)
-{
-    wave->setScale(mscale);
-}
-
-
-float QEDWaveTrackView::getTime()
-{
-    return wave->getTime();
-}
-
-
-float QEDWaveTrackView::getOffset()
-{
-    return wave->getOffset();
-}
-
-
-float QEDWaveTrackView::getScaledWidth()
-{
-    return wave->getScaledWidth();
-}
-
-
-void QEDWaveTrackView::slotTimeChanged(float value)
-{
-    emit timeChanged(value);
-}
-
-
-void QEDWaveTrackView::slotOffsetChanged(float value)
-{
-    emit offsetChanged(value);
-}
-
-
-float QEDWaveTrackView::getDuration()
-{
-    return wave->getDuration();
-}
--- a/edwaveform.h	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,97 +0,0 @@
-#ifndef EDWAVEFORM_H
-#define EDWAVEFORM_H
-
-#include <QWidget>
-#include <QLabel>
-#include "dmengine.h"
-
-class QEDWaveTrackDisplay : public QWidget
-{
-    Q_OBJECT
-
-public:
-    QEDWaveTrackDisplay(QWidget *parent = 0);
-
-    void setWaveform(void *mdata, int msize, int mformat, int mchannels, int mfreq);
-    float getScaledWidth();
-    void setScale(const float mscale);
-    int getBps();
-    float getDuration();
-    float getTimeScale(float value);
-    float getTimeFromCoord(float value);
-    float getTime();
-    float getOffset();
-
-    QSize minimumSizeHint() const
-    {
-        return QSize(100, 60);
-    }
-
-    QSize sizeHint() const
-    {
-        return QSize(600, 60);
-    }
-
-public slots:
-    void setTime(const float mtime);
-    void setOffset(const float moffs);
-    void setSelection(const float mstart, const float mend);
-    void clearSelection();
-
-signals:
-    void selectionChanged(float mstart, float mduration);
-    void timeChanged(float value);
-    void offsetChanged(float value);
-
-protected:
-    void mousePressEvent(QMouseEvent *event);
-    void mouseMoveEvent(QMouseEvent *event);
-    void mouseReleaseEvent(QMouseEvent *event);
-
-    void paintEvent(QPaintEvent *event);
-
-private:
-    float scale, time, offs, duration; // in milliseconds
-    int size, channels, format, freq, sduration;
-    void *data;
-
-    bool selectionValid;
-    float selectionStart, selectionDuration;
-
-    QPoint selectionPoint, dragPoint;
-    bool selecting, dragging;
-    float selectionOffs, dragOffs;
-};
-
-
-class QEDWaveTrackView : public QWidget
-{
-    Q_OBJECT
-
-private:
-    QEDWaveTrackDisplay *wave;
-    QLabel *infoName, *infoData;
-
-public:
-
-    QEDWaveTrackView(QWidget *parent = 0);
-    void setWaveform(void *mdata, int mlen, int mformat, int mchannels, int mfreq);
-    void setName(QString name);
-    void setTime(const float mtime);
-    void setOffset(const float moffs);
-    float getScaledWidth();
-    void setScale(const float mscale);
-    float getDuration();
-    float getTime();
-    float getOffset();
-
-private slots:
-    void slotTimeChanged(float value);
-    void slotOffsetChanged(float value);
-
-signals:
-    void timeChanged(float value);
-    void offsetChanged(float value);
-};
-
-#endif
--- a/efu.c	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,478 +0,0 @@
-#include "dmlib.h"
-#include "dmargs.h"
-#include "dmvecmat.h"
-#include "dmimage.h"
-#include "dmtext.h"
-#include <math.h>
-
-#define DM_COLORS (256)
-
-char *optFontFile = "font.ttf",
-     *optBitmapFilename = "tnsp.png";
-BOOL optBenchmark = FALSE;
-int optVFlags = SDL_SWSURFACE | SDL_HWPALETTE;
-int optScrWidth = 640, optScrHeight = 480, optFontSize = 20, optScrDepth = 32;
-int optBenchmarkLen = 20;
-
-DMOptArg optList[] = {
-    { 0, '?', "help",       "Show this help", OPT_NONE },
-    { 2, 'v', "verbose",    "Be more verbose", OPT_NONE },
-    { 3, 'f', "full",       "Fullscreen", OPT_NONE },
-    { 4, 'h', "hw",         "Use SDL hardware surface", OPT_NONE },
-    { 5, 's', "size",       "Initial window size/resolution -s 640x480", OPT_ARGREQ },
-    { 6, 'd', "depth",      "Color depth of mode/window in bits (8/15/16/32)", OPT_ARGREQ },
-    { 7, 'b', "bench",      "Run in benchmark mode", OPT_NONE },
-};
-
-const int optListN = sizeof(optList) / sizeof(optList[0]);
-
-
-void argShowHelp()
-{
-    dmArgsPrintHelp(stdout, optList, optListN);
-}
-
-
-BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
-{
-    switch (optN) {
-    case 0:
-        argShowHelp();
-        exit(0);
-        break;
-
-    case 2:
-        dmVerbosity++;
-        break;
-    
-    case 3:
-        optVFlags |= SDL_FULLSCREEN;
-        break;
-
-    case 6:
-        if (optArg)
-            optScrDepth = atoi(optArg);
-        break;
-
-    case 5:
-        {
-            int w, h;
-            if (sscanf(optArg, "%dx%d", &w, &h) == 2)
-            {
-                if (w < 320 || h < 200 || w > 3200 || h > 3200)
-                {
-                    dmError("Invalid width or height: %d x %d\n", w, h);
-                    return FALSE;
-                }
-                optScrWidth = w;
-                optScrHeight = h;
-            }
-            else 
-            {
-                dmError("Invalid size argument '%s'.\n", optArg);
-                return FALSE;
-            }
-        }
-        break;
-
-    case 7:
-        optBenchmark = TRUE;
-        break;
-
-    default:
-        dmError("Unknown option '%s'.\n", currArg);
-        return FALSE;
-    }
-    
-    return TRUE;
-}
-
-
-void DM_MakePalette(SDL_Surface *scr)
-{
-    SDL_Color pal[DM_COLORS];
-    int n;
-
-    for (n = 0; n < 256; n++)
-    {
-        pal[n].r = n;
-        pal[n].g = n;
-        pal[n].b = n;
-    }
-
-    SDL_SetColors(scr, pal, 0, DM_COLORS);
-}
-
-
-void DM_PrintRect(FILE *f, SDL_Rect *r)
-{
-    fprintf(f, "SDL_Rect <%d, %d : %d, %d>\n",
-        r->x, r->y, r->w, r->h);
-}
-
-BOOL DM_InitializeVideo(SDL_Surface **screen)
-{
-    *screen = SDL_SetVideoMode(optScrWidth, optScrHeight, optScrDepth, optVFlags | SDL_RESIZABLE);
-    if (*screen == NULL)
-    {
-        dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError());
-        return FALSE;
-    }
-
-#if 0
-    SDL_Rect r;
-    r.x = -50;
-    r.y = 50;
-    r.w = 700;
-    r.h = 300;
-    DM_PrintRect(stderr, &r);
-    SDL_SetClipRect(*screen, &r);
-    DM_PrintRect(stderr, &r);
-    DM_PrintRect(stderr, &((*screen)->clip_rect));
-#endif
-
-    return TRUE;
-}
-
-void DM_Random(SDL_Surface *screen, int q)
-{
-    Uint8 *pix = screen->pixels;
-    int xc, yc;
-    
-    for (yc = 0; yc < screen->h; yc++)
-    {
-        Uint8 *dp = pix;
-
-        for (xc = 0; xc < screen->w; xc++)
-            *dp++ = yc + (xc ^ q) + (yc & q);
-
-        pix += screen->pitch;
-    }
-}
-
-
-#define QWIDTH	256
-#define QHEIGHT	160
-
-typedef Uint8 DMBlockMap[QHEIGHT][QWIDTH];
-
-
-void dmMakeBumpMap(DMBlockMap map, DMFloat q, DMFloat m)
-{
-    int x, y;
-    for (y = 0; y < QHEIGHT; y++)
-        for (x = 0; x < QWIDTH; x++)
-        {
-            DMFloat f = 0.40f + dmPerlinNoise2D(x, y, 1.1f, q, 2);
-            map[y][x] = (int) (dmClamp10(f) * m);
-        }
-}
-
-
-void dmShadowTraceHeightMap(DMBlockMap lightMap, DMBlockMap pheightMap, DMVector *light)
-{
-    int i, j;
-
-    light->z = 150;
-
-    for (j = 0; j < QHEIGHT; j++)
-        for (i = 0; i < QWIDTH; i++)
-        {
-            DMVector vr, vl, va;
-            DMFloat vrayLen, vfactor;
-            int vlen;
-            BOOL wasHit;
-
-            // Perform shadow occlusion via simplistic raytracing
-            vr.x = i;
-            vr.y = j;
-            vr.z = 200; //light->z; // - 10.0;
-            
-            // Calculate light vector vector
-            dm_vector_sub_r(&vl, &vr, light);
-            vrayLen = dm_vector_length(&vl);
-            
-#if 1
-            dm_vector_copy(&va, &vl);
-            dm_vector_normalize(&va);
-            dm_vector_copy(&vr, light);
-
-            vlen = 0;
-            wasHit = FALSE;
-            do
-            {
-                float h;
-                
-                // If ray is inside the heightmap, get value
-                if (vr.x >= 0 && vr.y >= 0 && vr.x < QWIDTH && vr.y < QHEIGHT)
-                    h = pheightMap[(int) vr.y][(int) vr.x];
-                else
-                    break;
-                
-                // Check for hits
-                if (h > vr.z)
-                    wasHit = TRUE;
-                else
-                {
-                    // Move forwards
-                    dm_vector_add(&vr, &va);
-                    vlen++;
-                }
-            }
-            while (!wasHit && vlen <= vrayLen);
-
-            // Check if the ray hit something, e.g. is this point occluded?
-            if (wasHit && vlen < vrayLen)
-            {
-                vfactor = vlen * 0.05;
-            }
-            else
-                vfactor = vlen * 0.001;
-#endif
-
-#if 0
-            {
-                /* Calculate light's intensity based on the angle it "hits"
-                 *
-                 * 1) Calculate the vectors that form the imaginary "plane"
-                 * 2) Cross-product -> normal vector of the plane
-                 * 2) Normalize the normal vector
-                 * 3) Calculate light vector's hit angle by dot product
-                 */
-                DMVector v1, v2;
-                DMFloat c;
-
-                v1.x = 2.0f;
-                v1.y = 0.0f;
-                v1.z = (DMFloat) (pheightMap[j][i] - pheightMap[j][i + 1]);
-
-                v2.x = 0.0f;
-                v2.y = 2.0f;
-                v2.z = (DMFloat) (pheightMap[j][i] - pheightMap[j + 1][i]);
-
-                dm_vector_cross(&vr, &v1, &v2);
-                dm_vector_normalize(&vr);
-                dm_vector_normalize(&vl);
-                c = dm_vector_dot(&vl, &vr);
-
-		vrayLen = 255 - (vrayLen * 0.1) * vrayLen + (c * 128.0f) + (vfactor * vfactor * 1255);
-            }
-#else
-            vrayLen = 255 - vrayLen * vrayLen * (vfactor * vfactor);
-            if (vrayLen < 0) vrayLen = 0;
-            vrayLen -= pheightMap[j][i];
-#endif
-
-            // Clip result
-            if (vrayLen < 0)
-                vrayLen = 0;
-            else if (vrayLen > 255.0f)
-                vrayLen = 255.0f;
-
-            lightMap[j][i] = vrayLen;
-        }
-}
-
-
-int main(int argc, char *argv[])
-{
-    SDL_Surface *screen = NULL, *bmap = NULL, *logo = NULL;
-    TTF_Font *font = NULL;
-    SDL_Color fontcol={255,155,155,0};
-    SDL_Event event;
-    int mouseX, mouseY;
-    BOOL initSDL = FALSE, initTTF = FALSE, exitFlag, showMap = FALSE;
-    
-    if (!dmArgsProcess(argc, argv, optList, optListN,
-        argHandleOpt, NULL, FALSE))
-        exit(1);
-
-    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
-    {
-        dmError("Could not initialize SDL: %s\n", SDL_GetError());
-        goto error_exit;
-    }
-    initSDL = TRUE;
-
-
-    if (TTF_Init() < 0)
-    {
-        dmError("Could not initialize FreeType/TTF: %s\n", SDL_GetError());
-        goto error_exit;
-    }
-    initTTF = TRUE;
-
-    font = TTF_OpenFont(optFontFile, optFontSize);
-    if (font == NULL)
-    {
-        dmError("Could not load TTF font '%s' (%d): %s\n",
-            optFontFile, optFontSize, SDL_GetError());
-        goto error_exit;
-    }
-    TTF_SetFontStyle(font, TTF_STYLE_NORMAL);
-
-
-    DMResource *res = dmf_create_stdio(optBitmapFilename, "rb");
-    if (res == NULL)
-    {
-        dmError("Could not open resource file '%s'.\n", optBitmapFilename);
-        goto error_exit;
-    }
-    logo = dmLoadImage(res);
-    dmf_close(res);
-    if (logo == NULL)
-    {
-        dmError("Could not load image file '%s'.\n", optBitmapFilename);
-        goto error_exit;
-    }
-
-
-    if (optBenchmark)
-    {
-        screen = SDL_CreateRGBSurface(SDL_SWSURFACE, optScrWidth, optScrHeight, optScrDepth, 0, 0, 0, 0);
-        if (screen == NULL)
-        {
-            dmError("Could not create screen surface.\n");
-            goto error_exit;
-        }
-        
-        dmMsg(0, "Benchmark mode, not opening window.\n");
-    }
-    else
-    {
-        if (!DM_InitializeVideo(&screen))
-            goto error_exit;
-
-        SDL_WM_SetCaption("Halleluja", "DMT");
-    }
-
-    dmPerlinInit();
-
-    bmap = SDL_CreateRGBSurface(SDL_SWSURFACE, QWIDTH, QHEIGHT, 8, 0, 0, 0, 0);
-    DM_MakePalette(bmap);
-    DM_Random(bmap, 15);
-    
-    DMVector light;
-    DMBlockMap heightMap;
-    light.x = light.y = 128;
-    light.z = 128;
-    dmMakeBumpMap(heightMap, 0.06, 254);
-
-
-
-    int numFrames = 0, startTime = SDL_GetTicks(), endTime = 0;
-    exitFlag = FALSE;
-
-    if (optBenchmark)
-        dmMsg(0, "Starting benchmark, running for %d seconds.\n", optBenchmarkLen);
-
-    while (!exitFlag)
-    {
-        if (!optBenchmark)
-        {
-            while (SDL_PollEvent(&event))
-            switch (event.type)
-            {
-                case SDL_KEYDOWN:
-                    switch (event.key.keysym.sym)
-                    {
-                        case SDLK_ESCAPE: exitFlag = TRUE; break;
-                        
-                        case SDLK_F1:
-                            DM_Random(bmap, (SDL_GetTicks() / 10) % 1000);
-                            break;
-
-                        case SDLK_F5:
-                            showMap = !showMap;
-                            break;
-                            
-                        default:
-                            break;
-                    }
-
-                    break;
-                
-                case SDL_VIDEORESIZE:
-                    optScrWidth = event.resize.w;
-                    optScrHeight = event.resize.h;
-
-                    if (!DM_InitializeVideo(&screen))
-                        goto error_exit;
-
-                    break;
-                
-                case SDL_VIDEOEXPOSE:
-                    break;
-
-                case SDL_QUIT:
-                    exit(0);
-            }
-
-            SDL_GetMouseState(&mouseX, &mouseY);
-            light.x = ((DMFloat) mouseX * QWIDTH) / (DMFloat) optScrWidth;
-            light.y = ((DMFloat) mouseY * QHEIGHT) / (DMFloat) optScrHeight;
-        }
-
-        if (!optBenchmark && SDL_MUSTLOCK(screen) != 0 && SDL_LockSurface(screen) != 0)
-        {
-            dmError("Can't lock surface.\n");
-            goto error_exit;
-        }
-
-
-        if (showMap)
-            memcpy(bmap->pixels, heightMap, QWIDTH * QHEIGHT);
-        else
-            dmShadowTraceHeightMap(bmap->pixels, logo->pixels, &light);
-        
-        dmScaledBlitSurfaceAny(bmap, 0, 0, screen->w, screen->h, screen, DMD_NONE);
-
-
-
-        if (!optBenchmark)
-        {
-            dmDrawTTFText(screen, font, fontcol, 0, 0, "%3.1f FPS", 
-                (float) (numFrames * 1000.0f) / (float) (endTime - startTime));
-
-            if (SDL_MUSTLOCK(screen) != 0)
-                SDL_UnlockSurface(screen);
-
-            SDL_Flip(screen);
-            SDL_Delay(20);
-        }
-
-        endTime = SDL_GetTicks();
-        numFrames++;
-
-        if (optBenchmark)
-        {
-            if (endTime - startTime > optBenchmarkLen * 1000)
-                exitFlag = TRUE;
-        }
-    }
-
-    // Print benchmark results
-    dmMsg(0, "%d frames in %d ms, fps = %1.3f\n",
-        numFrames, endTime - startTime,
-        (float) (numFrames * 1000.0f) / (float) (endTime - startTime));
-
-
-error_exit:
-    dmMsg(0, "Shutting down dmlib.\n");
-    if (screen)
-        SDL_FreeSurface(screen);
-
-    if (logo)
-        SDL_FreeSurface(logo);
-
-    if (font)
-        TTF_CloseFont(font);
-
-    if (initSDL)
-        SDL_Quit();
-
-    if (initTTF)
-        TTF_Quit();
-
-    return 0;
-}
--- a/gentab.c	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,264 +0,0 @@
-#include "dmlib.h"
-#include "dmargs.h"
-#include <math.h>
-
-enum
-{
-    MT_SIN,
-    MT_COS,
-    MT_SMOOTH1,
-    MT_SCURVE,
-    MT_SMOOTH1_CLAMP,
-    MT_SCURVE_CLAMP,
-    MT_SIN_SCURVE,
-
-    MT_LAST
-};
-
-
-typedef struct
-{
-    char *name;
-    char *desc;
-} DMTransType;
-
-static DMTransType dmTransTypes[MT_LAST] =
-{
-    { "sin", "Sine" },
-    { "cos", "Cosine" },
-    { "smooth1", "Smoothstep1 LERP" },
-    { "scurve", "S-curve LERP" },
-    { "smooth1-clamp", "Clamped smoothstep1 LERP" },
-    { "scurve-clamp", "Clamped S-curve LERP" },
-    { "sin-scurve", "Sine S-curve" },
-};
-
-
-DMFloat
-    optSOffset     = 0.0f,
-    optSAmplitude  = 1.0f,
-    optSOmega      = 1.0f,
-    optStartValue  = 0.0f,
-    optEndValue    = 1.0f;
-
-int optNSteps      = 64,
-    optPerLine     = 16,
-    optTransType   = -1;
-
-char
-    *optObjectName = NULL,
-    *optOutFilename = NULL;
-
-
-static DMOptArg optList[] =
-{
-    {  0, '?', "help",        "Show this help", OPT_NONE },
-    {  1, 'v', "verbose",     "Increase verbosity", OPT_NONE },
-    {  2, 'o', "output",      "Set output file (default stdout)", OPT_ARGREQ },
-    {  3, 'n', "name",        "Set output object name", OPT_ARGREQ },
-
-    {  4, 's', "steps",       "Number of steps/values in output table", OPT_ARGREQ },
-    {  5, 't', "type",        "Curve/interpolation type (see list)", OPT_ARGREQ },
-
-    {  6, 'O', "offset",      "Output data offset", OPT_ARGREQ },
-    {  7, 'A', "amplitude",   "Output amplitude scale", OPT_ARGREQ },
-    {  8, 'W', "omega",       "Omega (w) multiplier", OPT_ARGREQ },
-
-    {  9, 'S', "start",       "Start value (only smooth/scurve)", OPT_ARGREQ },
-    { 10, 'E', "end",         "End value (only smooth/scurve)", OPT_ARGREQ },
-};
-
-static const int optListN = sizeof(optList) / sizeof(optList[0]);
-
-
-void argShowHelp()
-{
-    int index;
-    dmPrintBanner(stdout, dmProgName, "[options]");
-    dmArgsPrintHelp(stdout, optList, optListN);
-
-    printf("\nAvailable types:\n");
-    for (index = 0; index < MT_LAST; index++)
-    {
-        DMTransType *tm = &dmTransTypes[index];
-        printf("%-15s | %s\n", tm->name, tm->desc);
-    }
-    printf("\n");
-}
-
-
-BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
-{
-    switch (optN)
-    {
-        case 0:
-            argShowHelp();
-            exit(0);
-            break;
-
-        case 1:
-            dmVerbosity++;
-            break;
-        
-        case 2:
-            optOutFilename = optArg;
-            break;
-        
-        case 3:
-            optObjectName = optArg;
-            break;
-        
-        case 4:
-            {
-                int tmp;
-                if (sscanf(optArg, "%d", &tmp) != 1)
-                {
-                    dmError("Invalid number of steps argument '%s'.\n", optArg);
-                    return FALSE;
-                }
-                optNSteps = tmp;
-            }
-            break;
-
-        case 5:
-            {
-                int index;
-                for (index = 0; index < MT_LAST; index++)
-                {
-                    DMTransType *tm = &dmTransTypes[index];
-                    if (strcasecmp(tm->name, optArg) == 0)
-                    {
-                        optTransType = index;
-                        return TRUE;
-                    }
-                }
-                dmError("Invalid transformation type option '%s'.\n",
-                    optArg);
-                return FALSE;
-            }
-            break;
-
-        case 6:
-        case 7:
-        case 8:
-        case 9:
-        case 10:
-            {
-                DMFloat tmp;
-                if (sscanf(optArg, "%f", &tmp) != 1)
-                {
-                    dmError("Invalid %s option argument '%s', expected a floating point value.\n",
-                        currArg, optArg);
-                    return FALSE;
-                }
-                switch (optN)
-                {
-                    case  6: optSOffset = tmp; break;
-                    case  7: optSAmplitude = tmp; break;
-                    case  8: optSOmega = tmp; break;
-                    case  9: optStartValue = tmp; break;
-                    case 10: optEndValue = tmp; break;
-                }
-            }
-            break;
-
-        default:
-            dmError("Unknown argument '%s'.\n", currArg);
-            return FALSE;
-    }
-
-    return TRUE;
-}
-
-
-int main(int argc, char *argv[])
-{
-    FILE *outFile;
-    DMLerpContext ctx;
-    int step, n;
-
-    dmInitProg("gentab", "Sine, etc. table generator", "0.1", NULL, NULL);
-    dmVerbosity = 1;
-
-    // Parse arguments
-    if (!dmArgsProcess(argc, argv, optList, optListN,
-        argHandleOpt, NULL, TRUE))
-        exit(1);
-
-    // Check settings
-    if (optTransType < 0)
-    {
-        dmError("No transformation type set, perhaps try --help\n");
-        return -1;
-    }
-    
-    if (optObjectName == NULL)
-    {
-        dmError("Object name not specified, try --help\n");
-        return -2;
-    }
-
-    if (optOutFilename == NULL)
-        outFile = stdout;
-    else
-    if ((outFile = fopen(optOutFilename, "w")) == NULL)
-    {
-        int err = dmGetErrno();
-        dmError("Could not open output file '%s', %d: %s\n",
-            optOutFilename, err, dmErrorStr(err));
-        return -2;
-    }
-    
-
-    // Generate table
-    dmLerpInit(&ctx, optStartValue, optEndValue, optNSteps);
-
-    fprintf(outFile,
-        "cnt_%s = %d\n"
-        "vtab_%s: ",
-        optObjectName,
-        optNSteps,
-        optObjectName
-        );
-
-    for (n = 0, step = 0; step < optNSteps; step++)
-    {
-        DMFloat t = ((DMFloat) step * optSOmega) / (DMFloat) optNSteps, delta, value;
-        
-        switch (optTransType)
-        {
-            case MT_SIN:           delta = sin(t * 2 * DM_PI); break;
-            case MT_COS:           delta = cos(t * 2 * DM_PI); break;
-
-            case MT_SMOOTH1:       delta = dmLerp1(&ctx, step); break;
-            case MT_SCURVE:        delta = dmLerpSCurve(&ctx, step); break;
-            case MT_SMOOTH1_CLAMP: delta = dmLerp1Clamp(&ctx, step); break;
-            case MT_SCURVE_CLAMP:  delta = dmLerpSCurveClamp(&ctx, step); break;
-            case MT_SIN_SCURVE:    delta = dmLerpSCurveClamp(&ctx, step); break;
-            
-            default: delta = 0;
-        }
-        
-        value = optSOffset + delta * optSAmplitude;
-        
-        // Print the value
-        if (n == 0)
-            fprintf(outFile, "\t.byte ");
-
-        fprintf(outFile, "%ld%s",
-            lrint(value),
-            (n < optPerLine - 1) ? "," : "");
-
-        if (++n >= optPerLine)
-        {
-            fprintf(outFile, "\n");
-            n = 0;
-        }
-    }
-    if (n > 0)
-        fprintf(outFile, "\n");
-    
-    fprintf(outFile, "\n");
-
-    return 0;
-}
--- a/gfxconv.c	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1697 +0,0 @@
-/*
- * gfxconv - Convert various graphics formats
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2012 Tecnic Software productions (TNSP)
- *
- * Please read file 'COPYING' for information on license and distribution.
- */
-#include "dmlib.h"
-#include "dmargs.h"
-#include "dmfile.h"
-#include "dmmutex.h"
-#include "libgfx.h"
-#include "lib64gfx.h"
-
-//#define UNFINISHED 1
-
-#define DM_MAX_COLORS 256
-
-#define ASC_NBITS    8
-#define ASC_NCOLORS  4
-static const char dmASCIIPalette[ASC_NCOLORS] = ".:X#";
-
-enum
-{
-    FFMT_AUTO = 0,
-
-    FFMT_ASCII,
-    FFMT_ANSI,
-    FFMT_IMAGE,
-
-    FFMT_CHAR,
-    FFMT_SPRITE,
-    FFMT_BITMAP,
-
-    FFMT_LAST
-};
-
-
-typedef struct
-{
-    char *name;
-    char *fext;
-    BOOL in, out;
-    int format;
-    int subformat;
-} DMConvFormat;
-
-
-static DMConvFormat convFormatList[] =
-{
-    {
-        "ASCII text", "asc", FALSE, TRUE,
-        FFMT_ASCII  , 0,
-    },
-    {
-        "ANSI colored text", "ansi", FALSE, TRUE,
-        FFMT_ANSI   , 0,
-    },
-    {
-        "PNG image file", "png", TRUE, TRUE,
-        FFMT_IMAGE  , IMGFMT_PNG,
-    },
-    {
-        "PPM image file", "ppm", FALSE, TRUE,
-        FFMT_IMAGE  , IMGFMT_PPM,
-    },
-    {
-        "PCX image file", "pcx", TRUE, TRUE,
-        FFMT_IMAGE  , IMGFMT_PCX,
-    },
-    {
-        "IFF ILBM file", "lbm", TRUE, FALSE,
-        FFMT_IMAGE  , IMGFMT_ILBM,
-    },
-    {
-        "Bitplaned RAW (intl/non-intl) image file", "raw", FALSE, TRUE,
-        FFMT_IMAGE  , IMGFMT_RAW,
-    },
-    {
-        "IFFMaster RAW image file", "araw", FALSE, TRUE,
-        FFMT_IMAGE  , IMGFMT_ARAW,
-    },
-
-    {
-        "C64 bitmap image file", NULL, TRUE, TRUE,
-        FFMT_BITMAP , -1,
-    },
-
-    {
-        "C64 character/font data", "chr", TRUE, TRUE,
-        FFMT_CHAR   , 0
-    },
-    {
-        "C64 sprite data", "spr", TRUE, TRUE,
-        FFMT_SPRITE , 0
-    },
-};
-
-static const int nconvFormatList = sizeof(convFormatList) / sizeof(convFormatList[0]);
-
-
-typedef struct
-{
-    BOOL triplet, alpha;
-    DMColor color;
-    int from, to;
-} DMMapValue;
-
-
-
-char    *optInFilename = NULL,
-        *optOutFilename = NULL;
-int     optInFormat = FFMT_AUTO,
-        optOutFormat = FFMT_ASCII,
-        optInSubFormat = IMGFMT_PNG,
-        optOutSubFormat = IMGFMT_PNG,
-        optItemCount = -1,
-        optPlanedWidth = 1,
-        optForcedFormat = -1;
-int     optInSkip = 0;
-BOOL    optInMulticolor = FALSE,
-        optSequential = FALSE,
-        optRemapColors = FALSE,
-        optRemapRemove = FALSE;
-int     optNRemapTable = 0;
-DMMapValue optRemapTable[DM_MAX_COLORS];
-int     optColors[C64_MAX_COLORS];
-
-DMImageSpec optSpec =
-{
-    .scale = 1,
-    .nplanes = 4,
-    .interleave = FALSE,
-    .paletted = FALSE,
-    .format = 0,
-};
-
-static DMOptArg optList[] =
-{
-    {  0, '?', "help",         "Show this help", OPT_NONE },
-    { 15, 'v', "verbose",      "Increase verbosity", OPT_NONE },
-    {  3, 'o', "output",       "Output filename", OPT_ARGREQ },
-    {  1, 'i', "informat",     "Set input format ([s]prite, [c]har, [b]itmap, [i]mage)", OPT_ARGREQ },
-    {  2, 'm', "multicolor",   "Input is multicolor / output in multicolor", OPT_NONE },
-    {  4, 's', "skip",         "Skip bytes in input", OPT_ARGREQ },
-    {  5, 'f', "format",       "Output format (see --formats)", OPT_ARGREQ },
-    { 17, 'F', "formats",      "Output format (see list below)", OPT_NONE },
-    {  8, 'q', "sequential",   "Output sequential files (image output only)", OPT_NONE },
-    {  6, 'c', "colormap",     "Color mappings (see below for information)", OPT_ARGREQ },
-    {  7, 'n', "numitems",     "How many 'items' to view (default: all)", OPT_ARGREQ },
-    {  9, 'S', "scale",        "Scale output by x (image output only)", OPT_ARGREQ },
-    { 10, 'b', "bformat",      "Force input bitmap format (see below)", OPT_ARGREQ },
-    { 11, 'w', "width",        "Item width (number of items per row, min 1)", OPT_ARGREQ },
-    { 12, 'P', "paletted",     "Use indexed/paletted output (png, pcx output only)", OPT_NONE },
-    { 13, 'B', "bplanes",      "Bits per pixel OR # of bitplanes (certain output formats)", OPT_ARGREQ },
-    { 14, 'I', "interleave",   "Interleave scanlines (default: output whole planes)", OPT_NONE },
-    { 16, 'R', "remap",        "Remap output image colors (-R <(#RRGGBB|index):index>[,<..>] | -R @map.txt)", OPT_ARGREQ },
-    { 18, 'r', "remap-remove", "Remove unused colors from remapped palette (requires -R)", OPT_NONE },
-};
-
-static const int optListN = sizeof(optList) / sizeof(optList[0]);
-
-
-void argShowFormats()
-{
-    int i;
-
-    printf(
-    "Available input/output formats:\n"
-    "  Ext | I | O | Description\n"
-    "------+---+---+-----------------------------------------------\n"
-    );
-    
-    for (i = 0; i < nconvFormatList; i++)
-    {
-        DMConvFormat *fmt = &convFormatList[i];
-        printf("%-5s | %c | %c | %s\n",
-            fmt->fext ? fmt->fext : "",
-            fmt->in ? 'X' : ' ',
-            fmt->out ? 'X' : ' ',
-            fmt->name);
-    }
-
-    printf(
-    "\n"
-    "(Not all input->output combinations are actually supported.)\n"
-    "\n"
-    "Available bitmap formats:\n"
-    "  Ext | Type            | Description\n"
-    "------+-----------------+-------------------------------------\n"
-    );
-
-    for (i = 0; i < ndmC64ImageFormats; i++)
-    {
-        const DMC64ImageFormat *fmt = &dmC64ImageFormats[i];
-        char buf[64];
-        printf("%-5s | %-15s | %s\n",
-            fmt->fext,
-            dmC64GetImageTypeString(buf, sizeof(buf), fmt->type),
-            fmt->name);
-    }
-}
-
-
-void argShowHelp()
-{
-    dmPrintBanner(stdout, dmProgName, "[options] <input file>");
-    dmArgsPrintHelp(stdout, optList, optListN);
-
-    printf(
-    "\n"
-    "Color map definitions are used for ANSI and image output, to declare what\n"
-    "output colors of the C64 palette are used for each single color/multi color\n"
-    "bit-combination. For example, if the input is multi color sprite or char,\n"
-    "you can define colors like: -c 0,8,3,15 .. for single color: -c 0,1\n"
-    "The numbers are palette indexes, and the order is for bit(pair)-values\n"
-    "00, 01, 10, 11 (multi color) and 0, 1 (single color). NOTICE! 255 is the\n"
-    "special color that can be used for transparency.\n"
-    );
-}
-
-
-int dmGetConvFormat(int format, int subformat)
-{
-    int i;
-    for (i = 0; i < nconvFormatList; i++)
-    {
-        DMConvFormat *fmt = &convFormatList[i];
-        if (fmt->format == format &&
-            fmt->subformat == subformat)
-            return i;
-    }
-    return -1;
-}
-
-
-BOOL dmGetFormatByExt(const char *fext, int *format, int *subformat)
-{
-    int i;
-    if (fext == NULL)
-        return FALSE;
-
-    for (i = 0; i < nconvFormatList; i++)
-    {
-        DMConvFormat *fmt = &convFormatList[i];
-        if (fmt->fext != NULL &&
-            strcasecmp(fext, fmt->fext) == 0)
-        {
-            *format = fmt->format;
-            *subformat = fmt->subformat;
-            return TRUE;
-        }
-    }
-
-    for (i = 0; i < ndmC64ImageFormats; i++)
-    {
-        const DMC64ImageFormat *fmt = &dmC64ImageFormats[i];
-        if (fmt->fext != NULL &&
-            strcasecmp(fext, fmt->fext) == 0)
-        {
-            *format = FFMT_BITMAP;
-            *subformat = i;
-            return TRUE;
-        }
-    }
-
-    return FALSE;
-}
-
-
-static BOOL dmParseMapOptionMapItem(const char *popt, DMMapValue *value, const int nmax, const char *msg)
-{
-    char *end, *split, *opt = dm_strdup(popt);
-
-    if (opt == NULL)
-        goto error;
-
-    if ((end = split = strchr(opt, ':')) == NULL)
-    {
-        dmError("Invalid %s value '%s', expected <(#|%)RRGGBB|[$|0x]index>:<[$|0x]index>.\n", msg, opt);
-        goto error;
-    }
-
-    // Trim whitespace
-    *end = 0;
-    for (end--; end > opt && *end && isspace(*end); end--)
-        *end = 0;
-
-    // Parse either a hex triplet color definition or a normal value
-    if (*opt == '#' || *opt == '%')
-    {
-        int colR, colG, colB, colA;
-
-        if (sscanf(opt + 1, "%2x%2x%2x%2x", &colR, &colG, &colB, &colA) == 4 ||
-            sscanf(opt + 1, "%2X%2X%2X%2X", &colR, &colG, &colB, &colA) == 4)
-        {
-            value->alpha = TRUE;
-            value->color.a = colA;
-        }
-        else
-        if (sscanf(opt + 1, "%2x%2x%2x", &colR, &colG, &colB) != 3 &&
-            sscanf(opt + 1, "%2X%2X%2X", &colR, &colG, &colB) != 3)
-        {
-            dmError("Invalid %s value '%s', expected a hex triplet, got '%s'.\n", msg, popt, opt + 1);
-            goto error;
-        }
-
-        value->color.r = colR;
-        value->color.g = colG;
-        value->color.b = colB;
-        value->triplet = TRUE;
-    }
-    else
-    {
-        if (!dmGetIntVal(opt, &value->from))
-        {
-            dmError("Invalid %s value '%s', could not parse source value '%s'.\n", msg, popt, opt);
-            goto error;
-        }
-        value->triplet = FALSE;
-    }
-    
-    // Trim whitespace
-    split++;
-    while (*split && isspace(*split)) split++;
-    
-    // Parse destination value
-    if (!dmGetIntVal(split, &value->to))
-    {
-        dmError("Invalid %s value '%s', could not parse destination value '%s'.\n", msg, popt, split);
-        goto error;
-    }
-
-    if (!value->triplet && (value->from < 0 || value->from > 255))
-    {
-        dmError("Invalid %s map source color index value %d, must be [0..255].\n", msg, value->from);
-        goto error;
-    }
-
-    if (value->to < 0 || value->to > nmax)
-    {
-        dmError("Invalid %s map destination color index value %d, must be [0..%d].\n", msg, value->to, nmax);
-        goto error;
-    }
-
-    dmFree(opt);
-    return TRUE;
-
-error:
-    dmFree(opt);
-    return FALSE;
-}
-
-
-static BOOL dmParseMapOptionItem(char *opt, char *end, void *pvalue, const int index, const int nmax, const BOOL requireIndex, const char *msg)
-{
-    // Trim whitespace
-    if (end != NULL)
-    {
-        *end = 0;
-        for (end--; end > opt && *end && isspace(*end); end--)
-            *end = 0;
-    }
-    while (*opt && isspace(*opt)) opt++;
-
-    // Parse item based on mode
-    if (requireIndex)
-    {
-        DMMapValue *value = (DMMapValue *) pvalue;
-        if (!dmParseMapOptionMapItem(opt, &value[index], nmax, msg))
-            return FALSE;
-    }
-    else
-    {
-        int *value = (int *) pvalue;
-        char *split = strchr(opt, ':');
-        if (split != NULL)
-        {
-            dmError("Unexpected ':' in indexed %s '%s'.\n", msg, opt);
-            return FALSE;
-        }
-
-        if (!dmGetIntVal(opt, &value[index]))
-        {
-            dmError("Invalid %s value '%s', could not parse.\n", msg, opt);
-            return FALSE;
-        }
-    }
-    
-    return TRUE;
-}
-
-
-BOOL dmParseMapOptionString(char *opt, void *values, int *nvalues, const int nmax, const BOOL requireIndex, const char *msg)
-{
-    char *end, *start = opt;
-
-    *nvalues = 0;
-    while (*nvalues < nmax && *start && (end = strchr(start, ',')) != NULL)
-    {
-        if (!dmParseMapOptionItem(start, end, values, *nvalues, nmax, requireIndex, msg))
-            return FALSE;
-
-        start = end + 1;
-        (*nvalues)++;
-    }
-    
-    if (*start && *nvalues < nmax)
-    {
-        if (!dmParseMapOptionItem(start, NULL, values, *nvalues, nmax, requireIndex, msg))
-            return FALSE;
-
-        (*nvalues)++;
-    }
-
-    return TRUE;   
-}
-
-
-int dmParseColorRemapFile(const char *filename, DMMapValue *values, int *nvalue, const int nmax)
-{
-    FILE *fp;
-    char line[512];
-    int res = DMERR_OK;
-
-    if ((fp = fopen(filename, "r")) == NULL)
-    {
-        res = dmGetErrno();
-        dmError("Could not open color remap file '%s' for reading, %d: %s\n",
-            res, dmErrorStr(res));
-        return res;
-    }
-
-    while (fgets(line, sizeof(line), fp))
-    {
-        char *start = line;
-        while (*start && isspace(*start)) start++;
-        
-        if (*start != 0 && *start != ';')
-        {
-            if (!dmParseMapOptionMapItem(line, &values[*nvalue], nmax, "mapping file"))
-                goto error;
-            else
-            {
-                (*nvalue)++;
-                if (*nvalue >= nmax)
-                {
-                    dmError("Too many mapping pairs in '%s', maximum is %d.\n",
-                        filename, nmax);
-                    goto error;
-                }
-            }
-        }
-    }
-
-error:
-    fclose(fp);
-    return res;
-}
-
-
-BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
-{
-    switch (optN)
-    {
-        case 0:
-            argShowHelp();
-            exit(0);
-            break;
-
-        case 17:
-            argShowFormats();
-            exit(0);
-            break;
-
-        case 15:
-            dmVerbosity++;
-            break;
-
-        case 1:
-            switch (tolower(optArg[0]))
-            {
-                case 's':
-                    optInFormat = FFMT_SPRITE;
-                    break;
-                case 'c':
-                    optInFormat = FFMT_CHAR;
-                    break;
-                case 'b':
-                    optInFormat = FFMT_BITMAP;
-                    break;
-                case 'i':
-                    optInFormat = FFMT_IMAGE;
-                    break;
-                default:
-                    dmError("Invalid input format '%s'.\n", optArg);
-                    return FALSE;
-            }
-            break;
-        
-        case 2:
-            optInMulticolor = TRUE;
-            break;
-
-        case 3:
-            optOutFilename = optArg;
-            break;
-        
-        case 4:
-            if (!dmGetIntVal(optArg, &optInSkip))
-            {
-                dmError("Invalid skip value argument '%s'.\n", optArg);
-                return FALSE;
-            }
-            break;
-
-        case 5:
-            if (!dmGetFormatByExt(optArg, &optOutFormat, &optOutSubFormat))
-            {
-                dmError("Invalid output format '%s'.\n", optArg);
-                return FALSE;
-            }
-            break;
-
-        case 6:
-            {
-                int index, ncolors;
-                if (!dmParseMapOptionString(optArg, optColors,
-                    &ncolors, C64_MAX_COLORS, FALSE, "color table option"))
-                    return FALSE;
-
-                dmMsg(1, "Set color table: ");
-                for (index = 0; index < ncolors; index++)
-                {
-                    dmPrint(1, "[%d:%d]%s",
-                        index, optColors[index],
-                        (index < ncolors) ? ", " : "");
-                }
-                dmPrint(1, "\n");
-            }
-            break;
-
-        case 7:
-            if (sscanf(optArg, "%d", &optItemCount) != 1)
-            {
-                dmError("Invalid count value argument '%s'.\n", optArg);
-                return FALSE;
-            }
-            break;
-
-        case 8:
-            optSequential = TRUE;
-            break;
-
-        case 9:
-            {
-                int tmp = atoi(optArg);
-                if (tmp < 1 || tmp > 50)
-                {
-                    dmError("Invalid scale value '%s'.\n", optArg);
-                    return FALSE;
-                }
-                optSpec.scale = tmp;
-            }
-            break;
-
-        case 11:
-            {
-                int tmp = atoi(optArg);
-                if (tmp < 1 || tmp > 512)
-                {
-                    dmError("Invalid width value '%s'.\n", optArg);
-                    return FALSE;
-                }
-                optPlanedWidth = tmp;
-            }
-            break;
-
-        case 12:
-            optSpec.paletted = TRUE;
-            break;
-
-        case 13:
-            {
-                int tmp = atoi(optArg);
-                if (tmp < 1 || tmp > 8)
-                {
-                    dmError("Invalid bitplanes/bpp value '%s'.\n", optArg);
-                    return FALSE;
-                }
-                optSpec.nplanes = tmp;
-            }
-            break;
-
-        case 14:
-            optSpec.interleave = TRUE;
-            break;
-
-
-        case 16:
-            if (optArg[0] == '@')
-            {
-                if (optArg[1] != 0)
-                {
-                    int res;
-                    if ((res = dmParseColorRemapFile(optArg + 1,
-                        optRemapTable, &optNRemapTable, DM_MAX_COLORS)) != DMERR_OK)
-                        return FALSE;
-                }
-                else
-                {
-                    dmError("No remap filename given.\n");
-                    return FALSE;
-                }
-            }
-            else
-            {
-                if (!dmParseMapOptionString(optArg, optRemapTable,
-                    &optNRemapTable, DM_MAX_COLORS, TRUE, "color remap option"))
-                    return FALSE;
-            }
-
-            optRemapColors = TRUE;
-            break;
-
-        case 18:
-            optRemapRemove = TRUE;
-            break;
-
-        default:
-            dmError("Unknown option '%s'.\n", currArg);
-            return FALSE;
-    }
-
-    return TRUE;
-}
-
-
-BOOL argHandleFile(char *currArg)
-{
-    if (!optInFilename)
-        optInFilename = currArg;
-    else
-    {
-        dmError("Source filename already specified, extraneous argument '%s'.\n",
-             currArg);
-        return FALSE;
-    }
-
-    return TRUE;
-}
-
-
-void dmPrintByte(FILE *out, int byte, int format, BOOL multicolor)
-{
-    int i;
-    
-    if (multicolor)
-    {
-        for (i = ASC_NBITS; i; i -= 2)
-        {
-            int val = (byte & (3ULL << (i - 2))) >> (i - 2);
-            char ch;
-            switch (format)
-            {
-                case FFMT_ASCII:
-                    ch = dmASCIIPalette[val];
-                    fprintf(out, "%c%c", ch, ch);
-                    break;
-                case FFMT_ANSI:
-                    fprintf(out, "%c[0;%d;%dm##%c[0m",
-                        0x1b,
-                        1,
-                        31 + optColors[val],
-                        0x1b);
-                    break;
-            }
-        }
-    }
-    else
-    {
-        for (i = ASC_NBITS; i; i--)
-        {
-            int val = (byte & (1ULL << (i - 1))) >> (i - 1);
-            char ch;
-            switch (format)
-            {
-                case FFMT_ASCII:
-                    ch = val ? '#' : '.';
-                    fputc(ch, out);
-                    break;
-                case FFMT_ANSI:
-                    fprintf(out, "%c[0;%d;%dm %c[0m",
-                        0x1b,
-                        1,
-                        31 + optColors[val],
-                        0x1b);
-                    break;
-            }
-        }
-    }
-}
-
-
-void dmDumpCharASCII(FILE *outFile, const Uint8 *buf, int *offs, int format, BOOL multicolor)
-{
-    int yc;
-
-    for (yc = 0; yc < C64_CHR_HEIGHT; yc++)
-    {
-        fprintf(outFile, "%04x : ", *offs);
-        dmPrintByte(outFile, buf[yc], format, multicolor);
-        fprintf(outFile, "\n");
-        (*offs)++;
-    }
-}
-
-
-void dmDumpSpriteASCII(FILE *outFile, const Uint8 *buf, int *offs, int format, BOOL multicolor)
-{
-    int bufOffs, xc, yc;
-
-    for (bufOffs = yc = 0; yc < C64_SPR_HEIGHT; yc++)
-    {
-        fprintf(outFile, "%04x : ", *offs);
-        for (xc = 0; xc < C64_SPR_WIDTH; xc++)
-        {
-            dmPrintByte(outFile, buf[bufOffs], format, multicolor);
-            fprintf(outFile, " ");
-            bufOffs++;
-            (*offs)++;
-        }
-        fprintf(outFile, "\n");
-    }
-    (*offs)++;
-}
-
-
-#ifdef UNFINISHED
-int dmConvertBMP2(DMImage *screen, const DM64Image *img)
-{
-    int yc;
-    Uint8 *dp = screen->data;
-    
-    for (yc = 0; yc < screen->height; yc++)
-    {
-        Uint8 *d = dp;
-        const int y = yc / 8, yb = yc & 7;
-        const int scroffsy = y * C64_SCR_CH_WIDTH;
-        const int bmoffsy = y * C64_SCR_WIDTH;
-        int xc;
-
-        for (xc = 0; xc < screen->width / 2; xc++)
-        {
-            const int x = xc / 4;
-            const int scroffs = scroffsy + x;
-            const int b = img->bitmap[0][bmoffsy + (x * 8) + yb];
-            const int v = 6 - ((xc * 2) & 6);
-            Uint8 c;
-
-            switch ((b >> v) & 3)
-            {
-                case 0: c = img->bgcolor; break;
-                case 1: c = img->screen[0][scroffs] >> 4; break;
-                case 2: c = img->screen[0][scroffs] & 15; break;
-                case 3: c = img->color[0][scroffs] & 15; break;
-            }
-            
-            *d++ = c;
-            *d++ = c;
-        }
-
-        dp += screen->pitch;
-    }
-    
-    return 0;
-}
-#endif
-
-
-int dmRemapImageColors(DMImage *image)
-{
-    DMColor *npal = dmCalloc(image->ncolors, sizeof(DMColor));
-    int  *mapping = dmMalloc(image->ncolors * sizeof(int));
-    BOOL *mapped  = dmMalloc(image->ncolors * sizeof(BOOL));
-    BOOL *used    = dmMalloc(image->ncolors * sizeof(BOOL));
-    int n, index, xc, yc, ncolors;
-
-    dmMsg(1, "Remapping %d output image colors of %d colors.\n", optNRemapTable, image->ncolors);
-
-    if (npal == NULL || mapping == NULL || mapped == NULL || used == NULL)
-    {
-        dmError("Could not allocate memory for reused palette.\n");
-        return DMERR_MALLOC;
-    }
-
-    for (index = 0; index < image->ncolors; index++)
-    {
-        mapping[index] = -1;
-        mapped[index] = used[index] = FALSE;
-    }
-
-    // Find used colors
-    dmMsg(2, "Scanning image for used colors...\n");
-    for (ncolors = yc = 0; yc < image->height; yc++)
-    {
-        Uint8 *dp = image->data + image->pitch * yc;
-        for (xc = 0; xc < image->width; xc++)
-        {
-            Uint8 col = dp[xc];
-            if (col < image->ncolors && !used[col])
-            {
-                used[col] = TRUE;
-                ncolors++;
-            }
-        }
-    }
-    dmMsg(2, "Found %d used colors, creating remap-table.\n", ncolors);
-
-    // Match and mark mapped colors
-    for (index = 0; index < optNRemapTable; index++)
-    {
-        DMMapValue *map = &optRemapTable[index];
-        if (map->triplet)
-        {
-            BOOL found = FALSE;
-            for (n = 0; n < image->ncolors; n++)
-            {
-                if (dmCompareColor(&(image->pal[n]), &(map->color), map->alpha))
-                {
-                    dmMsg(3, "RGBA match #%02x%02x%02x%02x: %d -> %d\n",
-                        map->color.r, map->color.g, map->color.b, map->color.a,
-                        n,
-                        map->to);
-
-                    mapping[n] = map->to;
-                    mapped[map->to] = TRUE;
-                    found = TRUE;
-                }
-            }
-            
-            if (!found)
-            {
-                dmMsg(3, "No RGBA match found for map index %d, #%02x%02x%02x%02x\n",
-                    index,
-                    map->color.r, map->color.g, map->color.b, map->color.a);
-            }
-        }
-        else
-        {
-            dmMsg(3, "Map index: %d -> %d\n",
-                map->from, map->to);
-
-            mapping[map->from] = map->to;
-            mapped[map->to] = TRUE;
-        }
-    }
-
-    
-    // Fill in the rest
-    if (optRemapRemove)
-    {
-        dmMsg(2, "Removing unused colors.\n");
-        for (index = 0; index < image->ncolors; index++)
-        if (mapping[index] < 0 && used[index])
-        {
-            for (n = 0; n < image->ncolors; n++)
-            if (!mapped[n])
-            {
-                mapping[index] = n;
-                mapped[n] = TRUE;
-                break;
-            }
-        }
-    }
-    else
-    {
-        for (index = 0; index < image->ncolors; index++)
-        if (mapping[index] < 0)
-        {
-            for (n = 0; n < image->ncolors; n++)
-            if (!mapped[n])
-            {
-                mapping[index] = n;
-                mapped[n] = TRUE;
-                break;
-            }
-        }
-    }
-
-    // Calculate final number of palette colors
-    ncolors = 0;
-    for (index = 0; index < image->ncolors; index++)
-    {
-        if (mapping[index] + 1 > ncolors)
-            ncolors = mapping[index] + 1;
-    }
-    
-    // Copy palette entries
-    for (index = 0; index < image->ncolors; index++)
-    {
-        if (mapping[index] >= 0)
-        {
-            memcpy(&npal[mapping[index]], &(image->pal[index]), sizeof(DMColor));
-        }
-    }
-
-    // Remap image
-    dmMsg(1, "Remapping image to %d colors...\n", ncolors);
-    for (yc = 0; yc < image->height; yc++)
-    {
-        Uint8 *dp = image->data + image->pitch * yc;
-        for (xc = 0; xc < image->width; xc++)
-        {
-            Uint8 col = dp[xc];
-            if (col < image->ncolors && mapping[col] >= 0 && mapping[col] < image->ncolors)
-                dp[xc] = mapping[col];
-            else
-                dp[xc] = 0;
-        }
-    }
-
-    // Set new palette, free memory
-    dmFree(image->pal);
-    image->pal = npal;
-    image->ncolors = ncolors;
-
-    dmFree(mapping);
-    dmFree(mapped);
-    dmFree(used);
-    return DMERR_OK;
-}
-
-
-int dmWriteBitmap(const char *filename, DMC64Image *image, int iformat, BOOL enableFixUps)
-{
-    FILE *outFile = NULL;
-    Uint8 *buf = NULL;
-    size_t bufSize;
-    int res = DMERR_OK;
-    const DMC64ImageFormat *fmt = &dmC64ImageFormats[iformat];
-
-    dmMsg(1, "Converting to %s format bitmap.\n", fmt->name);
-    if (image->type != fmt->type && enableFixUps)
-    {
-        // Try to do some simple fixups
-        if ((fmt->type & D64_FMT_FLI) && (image->type & D64_FMT_FLI) == 0)
-        {
-            dmMsg(1, "Upconverting multicolor to FLI.\n");
-            int i;
-            for (i = 1; i < C64_SCR_MAX_BANK; i++)
-            {
-                memcpy(image->color[i], image->color[0], C64_SCR_COLOR_SIZE);
-                memcpy(image->screen[i], image->screen[0], C64_SCR_SCREEN_SIZE);
-            }
-        }
-    }
-
-
-    if ((res = dmC64EncodeGenericBMP(&buf, &bufSize, image, fmt)) != DMERR_OK)
-        goto error;
-
-    dmMsg(2, "Result: %d bytes\n", bufSize);
-
-    if ((outFile = fopen(filename, "wb")) == NULL)
-    {
-        res = dmGetErrno();
-        dmError("Error opening output file '%s', %d: %s\n",
-            filename, res, dmErrorStr(res));
-        goto error;
-    }
-
-    if (!dm_fwrite_str(outFile, buf, bufSize))
-    {
-        res = dmGetErrno();
-        dmError("Error writing image data to '%s', %d: %s\n",
-            filename, res, dmErrorStr(res));
-    }
-
-error:
-    if (outFile != NULL)
-        fclose(outFile);
-    dmFree(buf);
-    return res;
-}
-
-
-int dmWriteImage(const char *filename, DMImage *image, DMImageSpec *spec, int iformat, BOOL info)
-{
-    if (info)
-    {
-        dmMsg(1, "Outputting %s image %d x %d -> %d x %d [%d]\n",
-            dmImageFormatList[iformat].fext,
-            image->width, image->height,
-            image->width * spec->scale, image->height * spec->scale,
-            spec->scale);
-    }
-
-    // Perform color remapping
-    if (optRemapColors)
-    {
-        int res;
-        if ((res = dmRemapImageColors(image)) != DMERR_OK)
-            return res;
-    }
-
-    switch (iformat)
-    {
-#ifdef DM_USE_LIBPNG
-        case IMGFMT_PNG:
-            if (info) dmMsg(2, "%s output.\n", spec->paletted ? "Indexed 8bpp" : "32bit RGBA");
-            spec->format = spec->paletted ? DM_IFMT_PALETTE : DM_IFMT_RGBA;
-            return dmWritePNGImage(filename, image, spec);
-#endif
-
-        case IMGFMT_PPM:
-            if (info) dmMsg(2, "24bit RGB output.\n");
-            spec->format = DM_IFMT_RGB;
-            return dmWritePPMImage(filename, image, spec);
-
-        case IMGFMT_PCX:
-            if (info) dmMsg(2, "%s output.\n", spec->paletted ? "Indexed 8bpp" : "24bit RGB");
-            return dmWritePCXImage(filename, image, spec);
-
-        case IMGFMT_RAW:
-        case IMGFMT_ARAW:
-            {
-                FILE *fp;
-                char *dataFilename, *fext, *tmpFilename = dm_strdup(filename);
-                
-                // Form data file filename
-                if (tmpFilename == NULL)
-                    return DMERR_MALLOC;
-
-                fext = strrchr(tmpFilename, '.');
-                if (fext != NULL)
-                    *fext = 0;
-                dataFilename = dm_strdup_printf("%s.inc", tmpFilename);
-                dmFree(tmpFilename);
-
-                // Open data file for writing
-                if ((fp = fopen(dataFilename, "w")) == NULL)
-                    dmError("Could not create '%s'.\n", dataFilename);
-                dmFree(dataFilename);
-
-                if (fp != NULL)
-                {
-                    // Strip extension
-                    int i;
-                    char *palID = dm_strdup_printf("img_%s", filename);
-                    char *fext = strrchr(palID, '.');
-                    if (fext != NULL)
-                        *fext = 0;
-
-                    // Replace any non-alphanumerics
-                    for (i = 0; palID[i]; i++)
-                    {
-                        if (isalnum(palID[i]))
-                            palID[i] = tolower(palID[i]);
-                        else
-                            palID[i] = '_';
-                    }
-
-                    if (iformat == IMGFMT_ARAW)
-                    {
-                        fprintf(fp,
-                        "%s_width: dw.w %d\n"
-                        "%s_height: dw.w %d\n"
-                        "%s_nplanes: dw.w %d\n"
-                        "%s_ncolors: dw.w %d\n"
-                        "%s_palette:\n",
-                        palID, image->width,
-                        palID, image->height,
-                        palID, spec->nplanes,
-                        palID, image->ncolors,
-                        palID);
-
-                        dmWriteIFFMasterRAWPalette(fp, image, 1 << optSpec.nplanes, NULL, NULL);
-
-                        fprintf(fp,
-                        "%s: incbin \"%s\"\n",
-                        palID, filename);
-                    }
-                    else
-                    {
-                        fprintf(fp,
-                        "%s_width: dw.w %d\n"
-                        "%s_height: dw.w %d\n"
-                        "%s_nplanes: dw.w %d\n",
-                        palID, image->width,
-                        palID, image->height,
-                        palID, spec->nplanes);
-                    }
-
-                    fclose(fp);
-                    dmFree(palID);
-                }
-
-                if (info) dmMsg(2, "%d bitplanes, %s interleave.\n", spec->nplanes, spec->interleave ? "with" : "without");
-                return dmWriteRAWImage(filename, image, spec);
-            }
-            break;
-
-        default:
-            return DMERR_INVALID_DATA;
-    }
-}
-
-
-static Uint8 dmConvertByte(const Uint8 *sp, const BOOL multicolor)
-{
-    Uint8 byte = 0;
-    int xc;
-
-    if (multicolor)
-    {
-        for (xc = 0; xc < 8 / 2; xc++)
-        {
-            Uint8 pixel = sp[xc * 2] & 3;
-            byte |= pixel << (6 - (xc * 2));
-        }
-    }
-    else
-    {
-        for (xc = 0; xc < 8; xc++)
-        {
-            Uint8 pixel = sp[xc] == 0 ? 0 : 1;
-            byte |= pixel << (7 - xc);
-        }
-    }
-
-    return byte;
-}
-
-
-BOOL dmConvertImage2Char(Uint8 *buf, const DMImage *image,
-    const int xoffs, const int yoffs, const BOOL multicolor)
-{
-    int yc;
-
-    if (xoffs < 0 || yoffs < 0 ||
-        xoffs + C64_CHR_WIDTH_PX > image->width ||
-        yoffs + C64_CHR_HEIGHT > image->height)
-        return FALSE;
-
-    for (yc = 0; yc < C64_CHR_HEIGHT; yc++)
-    {
-        const Uint8 *sp = image->data + ((yc + yoffs) * image->pitch) + xoffs;
-        buf[yc] = dmConvertByte(sp, multicolor);
-    }
-
-    return TRUE;
-}
-
-
-BOOL dmConvertImage2Sprite(Uint8 *buf, const DMImage *image,
-    const int xoffs, const int yoffs, const BOOL multicolor)
-{
-    int yc, xc;
-
-    if (xoffs < 0 || yoffs < 0 ||
-        xoffs + C64_SPR_WIDTH_PX > image->width ||
-        yoffs + C64_SPR_HEIGHT > image->height)
-        return FALSE;
-
-    for (yc = 0; yc < C64_SPR_HEIGHT; yc++)
-    {
-        for (xc = 0; xc < C64_SPR_WIDTH_PX / C64_SPR_WIDTH; xc++)
-        {
-            const Uint8 *sp = image->data + ((yc + yoffs) * image->pitch) + (xc * 8) + xoffs;
-            buf[(yc * C64_SPR_WIDTH) + xc] = dmConvertByte(sp, multicolor);
-        }
-    }
-
-    return TRUE;
-}
-
-
-int dmWriteSpritesAndChars(const char *filename, DMImage *image, int outFormat, BOOL multicolor)
-{
-    int outBlockW, outBlockH, bx, by;
-    FILE *outFile = NULL;
-    Uint8 *buf = NULL;
-    size_t bufSize;
-    char *outType;
-
-    switch (outFormat)
-    {
-        case FFMT_CHAR:
-            bufSize = C64_CHR_SIZE;
-            outBlockW = image->width / C64_CHR_WIDTH_PX;
-            outBlockH = image->height / C64_CHR_HEIGHT;
-            outType = "char";
-            break;
-
-        case FFMT_SPRITE:
-            bufSize = C64_SPR_SIZE;
-            outBlockW = image->width / C64_SPR_WIDTH_PX;
-            outBlockH = image->height / C64_SPR_HEIGHT;
-            outType = "sprite";
-            break;
-
-        default:
-            dmError("Invalid output format %d, internal error.\n", outFormat);
-            goto error;
-    }
-
-    if (outBlockW <= 0 || outBlockH <= 0)
-    {
-        dmError("Source image dimensions too small for conversion, block dimensions %d x %d.\n",
-            outBlockW, outBlockH);
-        goto error;
-    }
-
-    if ((outFile = fopen(filename, "wb")) == NULL)
-    {
-        int err = dmGetErrno();
-        dmError("Could not open '%s' for writing, %d: %s.\n",
-            filename, err, dmErrorStr(err));
-        goto error;
-    }
-
-    if ((buf = dmMalloc(bufSize)) == NULL)
-    {
-        dmError("Could not allocate %d bytes for conversion buffer.\n",
-            bufSize);
-        goto error;
-    }
-
-    dmMsg(1, "Writing %d x %d = %d blocks of %s data...\n",
-        outBlockW, outBlockH, outBlockW * outBlockH, outType);
-
-    for (by = 0; by < outBlockH; by++)
-    for (bx = 0; bx < outBlockW; bx++)
-    {
-        switch (outFormat)
-        {
-            case FFMT_CHAR:
-                if (!dmConvertImage2Char(buf, image,
-                    bx * C64_CHR_WIDTH_PX, by * C64_CHR_HEIGHT,
-                    multicolor))
-                    goto error;
-                break;
-
-            case FFMT_SPRITE:
-                if (!dmConvertImage2Sprite(buf, image,
-                    bx * C64_SPR_WIDTH_PX, by * C64_SPR_HEIGHT,
-                    multicolor))
-                    goto error;
-        }
-
-        if (!dm_fwrite_str(outFile, buf, bufSize))
-        {
-            int err = dmGetErrno();
-            dmError("Error writing data block %d,%d to '%s', %d: %s\n",
-                bx, by, filename, err, dmErrorStr(err));
-            goto error;
-        }
-    }
-
-    fclose(outFile);
-    dmFree(buf);
-    return 0;
-
-error:
-    if (outFile != NULL)
-        fclose(outFile);
-    dmFree(buf);
-    return -1;
-}
-
-
-int dmDumpSpritesAndChars(FILE *inFile)
-{
-    int dataOffs, itemCount, outWidth, outWidthPX, outHeight;
-    size_t bufSize;
-    Uint8 *bufData;
-
-    switch (optInFormat)
-    {
-        case FFMT_CHAR:
-            bufSize = C64_CHR_SIZE;
-            outWidth = C64_CHR_WIDTH;
-            outWidthPX = C64_CHR_WIDTH_PX;
-            outHeight = C64_CHR_HEIGHT;
-            break;
-
-        case FFMT_SPRITE:
-            bufSize = C64_SPR_SIZE;
-            outWidth = C64_SPR_WIDTH;
-            outWidthPX = C64_SPR_WIDTH_PX;
-            outHeight = C64_SPR_HEIGHT;
-            break;
-
-        default:
-            dmError("Invalid input format %d, internal error.\n", optInFormat);
-            return -1;
-    }
-
-    if ((bufData = dmMalloc(bufSize)) == NULL)
-    {
-        dmError("Could not allocate temporary buffer of %d bytes.\n", bufSize);
-        return -2;
-    }
-
-
-    dataOffs = optInSkip;
-    itemCount = 0;
-
-    if (optOutFormat == FFMT_ANSI || optOutFormat == FFMT_ASCII)
-    {
-        BOOL error = FALSE;
-        FILE *outFile;
-
-        if (optOutFilename == NULL)
-            outFile = stdout;
-        else
-        if ((outFile = fopen(optOutFilename, "w")) == NULL)
-        {
-            int res = dmGetErrno();
-            dmError("Error opening output file '%s', %d: %s\n",
-                  optOutFilename, res, dmErrorStr(res));
-            goto error;
-        }
-
-        while (!feof(inFile) && !error && (optItemCount < 0 || itemCount < optItemCount))
-        {
-            memset(bufData, 0, bufSize);
-
-            if (fread(bufData, 1, bufSize, inFile) != bufSize)
-            {
-                dmError("Could not read full bufferful (%d bytes) of data at 0x%x.\n",
-                    bufSize, dataOffs);
-                error = TRUE;
-            }
-            
-            fprintf(outFile, "---- : -------------- #%d\n", itemCount);
-
-            switch (optInFormat)
-            {
-                case FFMT_CHAR:
-                    dmDumpCharASCII(outFile, bufData, &dataOffs, optOutFormat, optInMulticolor);
-                    break;
-                case FFMT_SPRITE:
-                    dmDumpSpriteASCII(outFile, bufData, &dataOffs, optOutFormat, optInMulticolor);
-                    break;
-            }
-            itemCount++;
-        }
-
-        fclose(outFile);
-    }
-    else
-    if (optOutFormat == FFMT_IMAGE)
-    {
-        DMImage *outImage = NULL;
-        char *outFilename = NULL;
-        int outX = 0, outY = 0, err;
-
-        if (optSequential)
-        {
-            if (optOutFilename == NULL)
-            {
-                dmError("Sequential image output requires filename template.\n");
-                goto error;
-            }
-
-            outImage = dmImageAlloc(outWidthPX, outHeight);
-            dmMsg(1, "Outputting sequence of %d images @ %d x %d -> %d x %d.\n",
-                optItemCount,
-                outImage->width, outImage->height,
-                outImage->width * optSpec.scale, outImage->height * optSpec.scale);
-        }
-        else
-        {
-            int outIWidth, outIHeight;
-            if (optItemCount <= 0)
-            {
-                dmError("Single-image output requires count to be set (-n).\n");
-                goto error;
-            }
-            
-            outIWidth = optPlanedWidth;
-            outIHeight = (optItemCount / optPlanedWidth);
-            if (optItemCount % optPlanedWidth)
-                outIHeight++;
-            
-            outImage = dmImageAlloc(outWidthPX * outIWidth, outIHeight * outHeight);
-        }
-
-        outImage->constpal = TRUE;
-        outImage->pal      = dmC64Palette;
-        outImage->ncolors  = C64_NCOLORS;
-        outImage->ctransp  = 255;
-        
-        while (!feof(inFile) && (optItemCount < 0 || itemCount < optItemCount))
-        {
-            memset(bufData, 0, bufSize);
-
-            if (fread(bufData, 1, bufSize, inFile) != bufSize)
-            {
-                dmError("Could not read full bufferful (%d bytes) of data at 0x%x.\n",
-                    bufSize, dataOffs);
-                break;
-            }
-
-            if ((err = dmC64ConvertCSData(outImage, outX * outWidthPX, outY * outHeight,
-                bufData, outWidth, outHeight, optInMulticolor, optColors)) != DMERR_OK)
-            {
-                dmError("Internal error in conversion of raw data to bitmap: %d.\n", err);
-                break;
-            }
-
-            if (optSequential)
-            {
-                outFilename = dm_strdup_printf("%s%04d.%s", optOutFilename, itemCount, convFormatList[optOutFormat].fext);
-                if (outFilename == NULL)
-                {
-                    dmError("Could not allocate memory for filename template?\n");
-                    goto error;
-                }
-                
-                dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE);
-                dmFree(outFilename);
-            }
-            else
-            {
-                if (++outX >= optPlanedWidth)
-                {
-                    outX = 0;
-                    outY++;
-                }
-            }
-            
-            itemCount++;
-        }
-
-        if (!optSequential)
-        {
-            dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE);
-        }
-        
-        dmImageFree(outImage);
-    }
-    else
-    if (optOutFormat == FFMT_BITMAP)
-    {
-        if (optSequential)
-        {
-            dmError("Sequential output not supported for spr/char -> bitmap conversion.\n");
-            goto error;
-        }
-    }
-
-    dmFree(bufData);
-    return 0;
-
-error:
-    dmFree(bufData);
-    return -1;
-}
-
-
-int main(int argc, char *argv[])
-{
-    FILE *inFile;
-    const DMC64ImageFormat *cfmt;
-    DMC64Image cimage;
-    Uint8 *dataBuf = NULL;
-    size_t dataSize;
-    int i;
-
-    // Default colors
-    for (i = 0; i < C64_MAX_COLORS; i++)
-        optColors[i] = i;
-
-    // Initialize and parse commandline
-    dmInitProg("gfxconv", "Simple graphics converter", "0.75", NULL, NULL);
-
-    if (!dmArgsProcess(argc, argv, optList, optListN,
-        argHandleOpt, argHandleFile, TRUE))
-        exit(1);
-
-#ifndef DM_USE_LIBPNG
-    if (optOutFormat == IMGFMT_PNG)
-    {
-        dmError("PNG output format support not compiled in, sorry.\n");
-        goto error;
-    }
-#endif
-
-    // Determine input format, if not specified'
-    if (optInFormat == FFMT_AUTO && optInFilename != NULL)
-    {
-        char *dext = strrchr(optInFilename, '.');
-        dmMsg(4, "Trying to determine file format by extension.\n");
-        if (dext)
-        {
-            dmGetFormatByExt(dext + 1, &optInFormat, &optInSubFormat);
-        }
-    }
-
-    if (optInFilename == NULL)
-    {
-        if (optInFormat == FFMT_AUTO)
-        {
-            dmError("Standard input cannot be used without specifying input format.\n");
-            dmError("Perhaps you should try using --help\n");
-            goto error;
-        }
-        inFile = stdin;
-    }
-    else
-    if ((inFile = fopen(optInFilename, "rb")) == NULL)
-    {
-        int res = dmGetErrno();
-        dmError("Error opening input file '%s', %d: %s\n",
-              optInFilename, res, dmErrorStr(res));
-        goto error;
-    }
-
-    if (dmReadDataFile(inFile, NULL, &dataBuf, &dataSize) != 0)
-        goto error;
-
-    if (optInFormat == FFMT_AUTO || optInFormat == FFMT_BITMAP)
-    {
-        // Probe for format
-        const DMC64ImageFormat *forced = NULL;
-        int res;
-
-        if (optForcedFormat >= 0)
-        {
-            forced = &dmC64ImageFormats[optForcedFormat];
-            dmMsg(0,"Forced %s format image, type %d, %s\n",
-                forced->name, forced->type, forced->fext);
-        }
-
-        res = dmC64DecodeBMP(&cimage, dataBuf, dataSize, optInSkip, optInSkip + 2, &cfmt, forced);
-        if (forced == NULL && cfmt != NULL)
-        {
-            dmMsg(1,"Probed %s format image, type %d, %s\n",
-                cfmt->name, cfmt->type, cfmt->fext);
-        }
-        
-        if (res == 0)
-            optInFormat = FFMT_BITMAP;
-    }
-
-    if (optInFormat == FFMT_AUTO || optInFormat == FFMT_IMAGE)
-    {
-        DMImageFormat *ifmt = NULL;
-        int index;
-        dmMsg(4, "Trying to probe image formats.\n");
-        if (dmImageProbeGeneric(dataBuf + optInSkip, dataSize - optInSkip, &ifmt, &index) > 0)
-        {
-            optInFormat = FFMT_IMAGE;
-            optInSubFormat = index;
-            dmMsg(2, "Probed %s format image.\n", ifmt->fext);
-        }
-    }
-
-    if (optInFormat == FFMT_AUTO)
-    {
-        dmError("No input format specified, and could not be determined automatically.\n");
-        exit(1);
-    }
-
-    // Skip, if needed
-    if (fseek(inFile, optInSkip, SEEK_SET) != 0)
-    {
-        int res = dmGetErrno();
-        dmError("Could not seek to file position %d (0x%x): %s\n",
-            optInSkip, optInSkip, dmErrorStr(res));
-        goto error;
-    }
-    
-    int inFormat = dmGetConvFormat(optInFormat, optInSubFormat),
-        outFormat = dmGetConvFormat(optOutFormat, optOutSubFormat);
-    
-    if (inFormat != -1 && outFormat != -1)
-    {
-        char *inFmtName = convFormatList[inFormat].name,
-             *inFmtExt = convFormatList[inFormat].fext,
-             *outFmtName = convFormatList[outFormat].name,
-             *outFmtExt = convFormatList[outFormat].fext;
-
-        if (optInFormat == FFMT_BITMAP)
-            inFmtExt = cfmt->name;
-
-        dmMsg(1, "Attempting conversion %s (%s) -> %s (%s)\n",
-            inFmtName, inFmtExt, outFmtName, outFmtExt);
-    }
-
-    switch (optInFormat)
-    {
-        case FFMT_SPRITE:
-        case FFMT_CHAR:
-            dmDumpSpritesAndChars(inFile);
-            break;
-        
-        case FFMT_BITMAP:
-            {
-                DMImage *outImage = NULL;
-                int res = DMERR_OK;
-
-                if (optOutFilename == NULL)
-                {
-                    dmError("Output filename not set, required for image formats.\n");
-                    goto error;
-                }
-
-                switch (optOutFormat)
-                {
-                    case FFMT_IMAGE:
-                        res = dmC64ConvertBMP2Image(&outImage, &cimage, cfmt, TRUE);
-
-                        if (res != DMERR_OK || outImage == NULL)
-                        {
-                            dmError("Error in bitmap to image conversion.\n");
-                            goto error;
-                        }
-
-                        res = dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE);
-                        break;
-
-
-                    case FFMT_BITMAP:
-                        res = dmWriteBitmap(optOutFilename, &cimage, optOutSubFormat, TRUE);
-                        break;
-
-                    case FFMT_CHAR:
-                    case FFMT_SPRITE:
-                        res = dmWriteSpritesAndChars(optOutFilename, outImage, optOutFormat, optInMulticolor);
-                        break;
-
-                    default:
-                        dmError("Unsupported output format for bitmap/image conversion.\n");
-                        break;
-                }
-                
-                dmImageFree(outImage);
-            }
-            break;
-
-        case FFMT_IMAGE:
-            {
-                DMImage *outImage = NULL;
-                int res = DMERR_OK;
-
-                if (optOutFilename == NULL)
-                {
-                    dmError("Output filename not set, required for image formats.\n");
-                    goto error;
-                }
-
-                // Read input
-                DMImageFormat *ifmt = &dmImageFormatList[optInSubFormat];
-                if (ifmt->readFILE != NULL)
-                    res = ifmt->readFILE(inFile, &outImage);
-                else
-                    dmError("Unsupported input image format for bitmap/image conversion.\n");
-
-                if (res != DMERR_OK || outImage == NULL)
-                    break;
-                
-                switch (optOutFormat)
-                {
-                    case FFMT_IMAGE:
-                        res = dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE);
-                        break;
-
-                    case FFMT_CHAR:
-                    case FFMT_SPRITE:
-                        res = dmWriteSpritesAndChars(optOutFilename, outImage, optOutFormat, optInMulticolor);
-                        break;
-
-                    default:
-                        dmError("Unsupported output format for bitmap/image conversion.\n");
-                        break;
-                }
-                
-                dmImageFree(outImage);
-            }
-            break;
-    }
-
-    fclose(inFile);
-
-    dmFree(dataBuf);
-    exit(0);
-    return 0;
-
-error:
-    dmFree(dataBuf);
-    return -3;
-    exit(3);
-}
--- a/mod2wav.c	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,314 +0,0 @@
-/*
- * mod2wav - Render XM/JSSMOD module to WAV waveform file
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2007 Tecnic Software productions (TNSP)
- *
- * Please read file 'COPYING' for information on license and distribution.
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include "jss.h"
-#include "jssmod.h"
-#include "jssmix.h"
-#include "jssplr.h"
-#include "dmlib.h"
-#include "dmargs.h"
-#include "dmwav.h"
-#include "dmmutex.h"
-
-
-char    *optInFilename = NULL, *optOutFilename = NULL;
-int     optOutFormat = JSS_AUDIO_S16,
-        optOutChannels = 2,
-        optOutFreq = 44100,
-        optMuteOChannels = -1,
-        optStartOrder = -1;
-BOOL    optUsePlayTime = FALSE;
-size_t  optPlayTime;
-
-
-DMOptArg optList[] =
-{
-    {  0, '?', "help",     "Show this help", OPT_NONE },
-    {  2, 'v', "verbose",  "Be more verbose", OPT_NONE },
-    {  3, '1', "16bit",    "16-bit output", OPT_NONE },
-    {  4, '8', "8bit",     "8-bit output", OPT_NONE },
-    {  5, 'm', "mono",     "Mono output", OPT_NONE },
-    {  6, 's', "stereo",   "Stereo output", OPT_NONE },
-    {  7, 'f', "freq",     "Output frequency", OPT_ARGREQ },
-    {  8, 'M', "mute",     "Mute other channels than #", OPT_ARGREQ },
-    {  9, 'o', "order",    "Start from order #", OPT_ARGREQ },
-    { 10, 't', "time",     "Play for # seconds", OPT_ARGREQ },
-//    {10, 'l', "loop",    "Loop for # times", OPT_ARGREQ },
-};
-
-const int optListN = sizeof(optList) / sizeof(optList[0]);
-
-
-BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
-{
-    (void) optArg;
-    
-    switch (optN)
-    {
-        case 0:
-            dmPrintBanner(stdout, dmProgName,
-                "[options] [sourcefile] [destfile]");
-                
-            dmArgsPrintHelp(stdout, optList, optListN);
-            exit(0);
-            break;
-
-        case 2:
-            dmVerbosity++;
-            break;
-        
-        case 3:
-            optOutFormat = JSS_AUDIO_S16;
-            break;
-
-        case 4:
-            optOutFormat = JSS_AUDIO_U8;
-            break;
-
-        case 5:
-            optOutChannels = JSS_AUDIO_MONO;
-            break;
-
-        case 6:
-            optOutChannels = JSS_AUDIO_STEREO;
-            break;
-
-        case 7:
-            optOutFreq = atoi(optArg);
-            break;
-
-        case 8:
-            optMuteOChannels = atoi(optArg);
-            break;
-
-        case 9:
-            optStartOrder = atoi(optArg);
-            break;
-
-        case 10:
-            optPlayTime = atoi(optArg);
-            optUsePlayTime = TRUE;
-            break;
-
-        default:
-            dmError("Unknown argument '%s'.\n", currArg);
-            return FALSE;
-    }
-    
-    return TRUE;
-}
-
-
-BOOL argHandleFile(char *currArg)
-{
-    if (!optInFilename)
-        optInFilename = currArg;
-    else
-    if (!optOutFilename)
-        optOutFilename = currArg;
-    else
-    {
-        dmError("Too many filename arguments (only source and dest needed) '%s'\n", currArg);
-        return FALSE;
-    }
-    
-    return TRUE;
-}
-
-
-int main(int argc, char *argv[])
-{
-    DMResource *inFile = NULL;
-    FILE *outFile = NULL;
-    JSSModule *mod = NULL;
-    JSSMixer *dev = NULL;
-    JSSPlayer *plr = NULL;
-    int result = -1;
-    size_t bufLen = 1024*4, dataTotal, dataWritten, sampSize;
-    Uint8 *mb = NULL;
-
-    dmInitProg("mod2wav", "XM/JSSMOD to WAV renderer", "0.2", NULL, NULL);
-    dmVerbosity = 1;
-
-    // Parse arguments
-    if (!dmArgsProcess(argc, argv, optList, optListN,
-        argHandleOpt, argHandleFile, TRUE))
-        exit(1);
-
-    // Check arguments
-    if (optInFilename == NULL || optOutFilename == NULL)
-    {
-        dmError("Input or output file not specified. Try --help.\n");
-        return 1;
-    }
-    
-    // Initialize miniJSS
-    jssInit();
-    
-    // Open the source file
-    if ((inFile = dmf_create_stdio(optInFilename, "rb")) == NULL)
-    {
-        dmError("Error opening input file '%s', %d: %s\n",
-            optInFilename, errno, strerror(errno));
-        return 1;
-    }
-
-    // Read module file
-    fprintf(stderr, "Reading file: %s\n", optInFilename);
-#ifdef JSS_SUP_XM
-    fprintf(stderr, "* Trying XM...\n");
-    result = jssLoadXM(inFile, &mod);
-#endif
-#ifdef JSS_SUP_JSSMOD
-    if (result != 0)
-    {
-        size_t bufgot, bufsize = dmfsize(inFile);
-        Uint8 *buf = dmMalloc(bufsize);
-        dmfseek(inFile, 0L, SEEK_SET);
-        fprintf(stderr, "* Trying JSSMOD (%d bytes, %p)...\n", bufsize, buf);
-        if ((bufgot = dmfread(buf, 1, bufsize, inFile)) != bufsize)
-        {
-            fprintf(stderr, "Error reading file (not enough data %d), #%d: %s\n",
-                bufgot, dmferror(inFile), dmErrorStr(dmferror(inFile)));
-            return 2;
-        }
-        result = jssLoadJSSMOD(buf, bufsize, &mod);
-        dmFree(buf);
-    }
-#endif
-    dmf_close(inFile);
-    if (result != DMERR_OK)
-    {
-        dmError("Error loading module file, %d: %s\n",
-            result, dmErrorStr(result));
-        return 3;
-    }
-
-    // Try to convert it
-    if ((result = jssConvertModuleForPlaying(mod)) != DMERR_OK)
-    {
-        dmError("Could not convert module for playing, %d: %s\n",
-            result, dmErrorStr(result));
-        return 3;
-    }
-
-    // Open mixer
-    dev = jvmInit(optOutFormat, optOutChannels, optOutFreq, JMIX_AUTO);
-    if (dev == NULL)
-    {
-        dmError("jvmInit() returned NULL\n");
-        return 4;
-    }
-
-    sampSize = jvmGetSampleSize(dev);
-    if ((mb = dmMalloc(bufLen * sampSize)) == NULL)
-    {
-        dmError("Could not allocate mixing buffer\n");
-        return 5;
-    }
-    
-    dmMsg(1, "Using fmt=%d, bits=%d, channels=%d, freq=%d [%d / sample]\n",
-        optOutFormat, jvmGetSampleRes(dev), optOutChannels, optOutFreq,
-        sampSize);
-    
-    // Initialize player
-    if ((plr = jmpInit(dev)) == NULL)
-    {
-        dmError("jmpInit() returned NULL.\n");
-        return 6;
-    }
-    
-    // Set callback
-    jvmSetCallback(dev, jmpExec, plr);
-    
-    // Initialize playing
-    jmpSetModule(plr, mod);
-    if (optStartOrder >= 0)
-    { 
-        dmMsg(1, "Starting from song order #%d\n", optStartOrder);
-    } else
-        optStartOrder = 0;
-
-    jmpPlayOrder(plr, optStartOrder);
-    jvmSetGlobalVol(dev, 150);
-    
-    if (optMuteOChannels > 0 && optMuteOChannels <= mod->nchannels)
-    {
-        int i;
-        for (i = 0; i < mod->nchannels; i++)
-            jvmMute(dev, i, TRUE);
-        jvmMute(dev, optMuteOChannels - 1, FALSE);
-    }
-    
-    // Open output file
-    if ((outFile = fopen(optOutFilename, "wb")) == NULL)
-    {
-        dmError("Error opening output file '%s'. (%s)\n", optInFilename, strerror(errno));
-        return 7;
-    }
-
-    // Write initial header
-    dmWriteWAVHeader(outFile, jvmGetSampleRes(dev), optOutFreq, optOutChannels, 1024);
-
-    // Render audio data and output to file
-    if (optUsePlayTime)
-        dmMsg(1, "Rendering module (%d seconds) ...\n", optPlayTime);
-    else
-        dmMsg(1, "Rendering module ...\n");
-    
-    optPlayTime *= optOutFreq;
-    dataTotal = 0;
-    dataWritten = 1;
-    while (plr->isPlaying && dataWritten > 0)
-    {
-        size_t writeLen = bufLen;
-        if (optUsePlayTime && (writeLen + dataTotal) > optPlayTime)
-            writeLen = optPlayTime - dataTotal;
-        
-        if (writeLen > 0)
-        {
-            jvmRenderAudio(dev, mb, writeLen);
-#if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
-            jssEncodeSample16((Uint16 *)mb, writeLen * optOutChannels, jsampSwapEndianess);
-#endif
-            dataWritten = fwrite(mb, sampSize, writeLen, outFile);
-            if (dataWritten < writeLen)
-            {
-                dmError("Error writing data!\n");
-                fclose(outFile);
-                return 8;
-            }
-            dataTotal += dataWritten;
-        }
-        
-        if (optUsePlayTime && dataTotal >= optPlayTime)
-            break;
-    }
-    
-    // Write the correct header
-    if (fseek(outFile, 0L, SEEK_SET) != 0)
-    {
-        dmError("Error rewinding to header position!\n");
-        return 9;
-    }
-    
-    dmWriteWAVHeader(outFile, jvmGetSampleRes(dev), optOutFreq, optOutChannels, dataTotal);
-    
-    // Done!
-    fclose(outFile);
-
-    jmpClose(plr);
-    jvmClose(dev);
-    jssFreeModule(mod);
-    jssClose();
-
-    dmMsg(1, "OK.\n");
-    return 0;
-}
--- a/objlink.c	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,990 +0,0 @@
-/*
- * objlink - Link files (RAW and PRG) into one PRG object
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2002-2012 Tecnic Software productions (TNSP)
- *
- * Please read file 'COPYING' for information on license and distribution.
- */
-#include <errno.h>
-#include "dmlib.h"
-#include "dmargs.h"
-#include "dmfile.h"
-#include "dmmutex.h"
-
-#define MAX_FILENAMES    (128)
-#define MAX_MEMBLOCKS    (128)
-
-
-/* Typedefs
- */
-typedef struct
-{
-    ssize_t start, end;	// Start and end address
-    int type;           // Type
-    char *name;         // Name of the block
-    int placement;
-} DMMemBlock;
-
-typedef struct
-{
-    char *name;         // Description of memory model
-    char *desc;
-    ssize_t size;        // Total addressable memory size
-    ssize_t nmemBlocks;  // Defined memory areas
-    DMMemBlock memBlocks[MAX_MEMBLOCKS];
-} DMMemModel;
-
-typedef struct
-{
-    char *filename;
-    int type;
-    int placement;
-    ssize_t addr;
-} DMSourceFile;
-
-// Source file type
-enum
-{
-    STYPE_RAW = 1,
-    STYPE_PRG,
-    STYPE_PRGA
-};
-
-// How to determine block placement / address
-enum
-{
-    PLACE_STATIC = 1,  // Already known
-    PLACE_ARGUMENT,   // Commandline argument
-    PLACE_FILE,       // From file
-};
-
-enum
-{
-    FMT_GENERIC = 1,
-    FMT_PLAIN,
-    FMT_DECIMAL
-};
-
-enum
-{
-    MTYPE_NONE = 0,
-    MTYPE_ROM,        // Hard ROM
-    MTYPE_ROM_WT,     // Write to RAM through ROM
-    MTYPE_IO,         // I/O lines
-    MTYPE_RES         // RESERVED
-};
-
-enum
-{
-    LA_NONE = -2,
-    LA_AUTO = -1
-};
-
-/* Memory models
- */
-const DMMemModel memoryModels[] = {
-    { "C64 unrestricted", "$01 = $34", (64*1024), 0, {
-    { 0, 0, 0, NULL, 0 }
-    }},
-
-    { "C64 normal (IO+Basic+Kernal)", "$01 = $37", (64*1024), 3, {
-    { 0xA000, 0xBFFF,    MTYPE_ROM_WT,    "Basic ROM",  PLACE_STATIC },
-    { 0xD000, 0xDFFF,    MTYPE_IO,        "I/O",  PLACE_STATIC },
-    { 0xE000, 0xFFFF,    MTYPE_ROM_WT,    "Kernal ROM",  PLACE_STATIC },
-    }},
-
-    { "C64 modified (IO+Kernal)", "$01 = $36", (64*1024), 2, {
-    { 0xD000, 0xDFFF,    MTYPE_IO,        "I/O",  PLACE_STATIC },
-    { 0xE000, 0xFFFF,    MTYPE_ROM_WT,    "Kernal ROM",  PLACE_STATIC },
-    }},
-
-    { "C64 modified (IO only)", "$01 = $35", (64*1024), 1, {
-    { 0xD000, 0xDFFF,    MTYPE_IO,        "I/O",  PLACE_STATIC },
-    }},
-
-    { "C64 modified (Char+Kernal+Basic)", "$01 = $33", (64*1024), 3, {
-    { 0xA000, 0xBFFF,    MTYPE_ROM_WT,    "Basic ROM",  PLACE_STATIC },
-    { 0xD000, 0xDFFF,    MTYPE_ROM,       "Char ROM", PLACE_STATIC },
-    { 0xE000, 0xFFFF,    MTYPE_ROM_WT,    "Kernal ROM", PLACE_STATIC },
-    }},
-
-/*
-    { "C64 normal", "$01 = $37", (64*1024), 0, {
-    { 0x0000, 0x0000,    MTYPE_RAM,    "" },
-    }},
-*/
-};
-
-static const int nmemoryModels = sizeof(memoryModels) / sizeof(memoryModels[0]);
-
-
-/* Global variables
- */
-int           nsrcFiles = 0;              // Number of source files
-DMSourceFile  srcFiles[MAX_FILENAMES];    // Source file names
-
-int        nmemBlocks = 0;
-DMMemBlock memBlocks[MAX_FILENAMES];
-
-char       *optLinkFileName = NULL;
-int        optLinkFileFormat = FMT_GENERIC;
-
-BOOL       optDescribe = FALSE,
-           optAllowOverlap = FALSE;
-
-Uint32     optInitValue = 0;
-int        optInitValueType = 1;
-ssize_t    optCropStart, optCropEnd;
-BOOL       optCropOutput = FALSE;
-
-ssize_t    optLoadAddress = LA_AUTO;
-
-int        optMemModel = 0;
-const DMMemModel *memModel = NULL;
-Uint8      *memory = NULL;
-
-char       *optDestName = NULL;
-
-
-/* Arguments
- */
-static DMOptArg optList[] = {
-    {  0, '?', "help",        "Show this help", OPT_NONE },
-    {  1, 'r', "input-raw",   "RAW input: -r <file>:<addr>", OPT_ARGREQ },
-    {  2, 'p', "input-prg",   "PRG input: -p <file>[:<addr>]", OPT_ARGREQ },
-    { 12, 's', "section",     "Reserved section: -s <start>-<end>[,name] or <start>:<len>[,name]", OPT_ARGREQ },
-    {  5, 'o', "output",      "Specify output file, -o <file>", OPT_ARGREQ },
-    {  6, 'O', "overlap",     "Allow overlapping memory areas", OPT_NONE },
-    {  7, 'm', "model",       "Set memory model", OPT_ARGREQ },
-    {  8, 'l', "link-file",   "Output addresses and labels into file", OPT_ARGREQ },
-    {  9, 'f', "format",      "Format of link-file: (g)eneric, (p)lain, (d)ecimal", OPT_ARGREQ },
-    { 10, 'i', "initvalue",   "Initialize memory with: -i <byte/word/dword>:[bwd]", OPT_ARGREQ },
-    { 11, 'd', "describe",    "Output ASCII memory map description", OPT_NONE },
-    { 13, 'c', "crop",        "Crop output file to: -c <start>-<end> or <start>:<len>", OPT_ARGREQ },
-    { 14, 'L', "load-address","Set output file load address (or 'none' for 'raw' output)", OPT_ARGREQ },
-};
-
-static const int optListN = sizeof(optList) / sizeof(optList[0]);
-
-
-void argShowHelp()
-{
-    int i;
-
-    dmPrintBanner(stdout, dmProgName, "[options]");
-    dmArgsPrintHelp(stdout, optList, optListN);
-
-    printf(
-    "\n"
-    "Each numeric argument can be prefixed with $ or 0x for hexadecimal values.\n"
-    "NOTICE! -p filename:<addr> will ignore load address and use <addr> instead!\n"
-    "\n"
-    "Available memory models:\n");
-
-    for (i = 0; i < nmemoryModels; i++)
-    {
-        const DMMemModel *m = &memoryModels[i];
-        printf("  %d = %-40s [%s] (%d kB)\n",
-            i, m->name, m->desc, m->size / 1024);
-    }
-}
-
-
-off_t dmGetFileSize(FILE *f)
-{
-    off_t len, pos = ftell(f);
-    fseeko(f, 0, SEEK_END);
-    len = ftell(f);
-    fseek(f, pos, SEEK_SET);
-    return len;
-}
-
-
-/* Memory block handling
- */
-void reserveMemBlock(ssize_t startAddr, ssize_t endAddr, const char *blockName, int blockType)
-{
-    if (startAddr > endAddr)
-    {
-        dmError("ERROR! Block '%s' has startAddr=$%.4x > endAddr=$%.4x!\n",
-            blockName, startAddr, endAddr);
-        exit(4);
-    }
-
-    if (nmemBlocks < MAX_FILENAMES)
-    {
-        memBlocks[nmemBlocks].start = startAddr;
-        memBlocks[nmemBlocks].end   = endAddr;
-        memBlocks[nmemBlocks].name  = dm_strdup(blockName);
-        memBlocks[nmemBlocks].type  = blockType;
-        nmemBlocks++;
-    }
-    else
-    {
-        dmError("Maximum number of memBlock definitions (%d) exceeded!\n",
-            MAX_FILENAMES);
-        exit(4);
-    }
-}
-
-
-int compareMemBlock(const void *cva, const void *cvb)
-{
-    const DMMemBlock *a = cva, *b = cvb;
-    return a->start - b->start;
-}
-
-
-BOOL dmParseSection(const char *arg, ssize_t *sectStart, ssize_t *sectEnd, char **sectName, BOOL canHasName)
-{
-    char sectMode, *sep, *str, *namesep;
-    ssize_t tmpi;
-
-    // Define reserved section
-    // Create a copy of the argument
-    if ((str = dm_strdup(arg)) == NULL)
-    {
-        dmError("Could not allocate temporary string!\n");
-        exit(128);
-    }
-
-    // Get start address
-    if ((sep = strchr(str, '-')) == NULL &&
-        (sep = strchr(str, ':')) == NULL)
-    {
-        dmError("Section definition '%s' invalid.\n", arg);
-        goto error;
-    }
-    sectMode = *sep;
-    *sep = 0;
-
-    // Get value
-    if (!dmGetIntVal(str, sectStart))
-    {
-        dmError("Section start address '%s' in '%s' invalid.\n", str, arg);
-        goto error;
-    }
-    
-    // Check for name
-    namesep = strchr(sep + 1, ',');
-    if (canHasName && namesep != NULL)
-    {
-        *namesep = 0;
-        namesep++;
-        if (*namesep == 0)
-        {
-            dmError("Section definition '%s' name is empty. Either specify name or leave it out.\n",
-                arg);
-            goto error;
-        }
-        *sectName = dm_strdup(namesep);
-    }
-    else
-    if (namesep != NULL)
-    {
-        dmError("Section definition does not allow a name, syntax error in '%s' at '%s'.\n",
-            arg, namesep);
-        goto error;
-    }
-
-    // Get end address or length
-    if (!dmGetIntVal(sep + 1, &tmpi))
-    {
-        dmError("Section %s '%s' in '%s' invalid.\n",
-            sectMode == '-' ? "end address" : "length",
-            sep + 1, arg);
-        goto error;
-    }
-
-    if (sectMode == ':')
-    {
-        *sectEnd = *sectStart + tmpi - 1;
-    }
-    else
-    {
-        if (tmpi < *sectStart)
-        {
-            dmError("Section start address > end address in '%s'.\n",
-                arg);
-            goto error;
-        }
-        *sectEnd = tmpi;
-    }
-
-    dmFree(str);
-    return TRUE;
-
-error:
-    dmFree(str);
-    return FALSE;
-}
-
-
-BOOL dmParseInputFile(char *arg, const int type1, const int type2, const char *desc, BOOL requireAddr)
-{
-    ssize_t tmpi = 0;
-    BOOL hasAddr = FALSE;
-    char *sep;
-
-    if ((sep = strrchr(arg, ':')) != NULL)
-    {
-        *sep = 0;
-        if (!dmGetIntVal(sep + 1, &tmpi))
-        {
-            dmError("Invalid %s address '%s' specified for '%s'.\n",
-                desc, sep + 1, arg);
-            return FALSE;
-        }
-        hasAddr = TRUE;
-    }
-    else
-    if (requireAddr)
-    {
-        dmError("No %s loading address specified for '%s'.\n", desc, arg);
-        return FALSE;
-    }
-
-    srcFiles[nsrcFiles].filename = arg;
-    srcFiles[nsrcFiles].type = hasAddr ? type1 : type2;
-    srcFiles[nsrcFiles].addr = tmpi;
-    nsrcFiles++;
-    return TRUE;
-}
-
-
-BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
-{
-    char *p;
-    ssize_t tmpi;
-
-    switch (optN) {
-    case 0:
-        argShowHelp();
-        exit(0);
-        break;
-
-    case 1:
-        // Add RAW
-        if (!dmParseInputFile(optArg, STYPE_RAW, STYPE_RAW, "RAW", TRUE))
-            return FALSE;
-        break;
-
-    case 2:
-        // Add PRG
-        if (!dmParseInputFile(optArg, STYPE_PRGA, STYPE_PRG, "PRG", FALSE))
-            return FALSE;
-        break;
-
-    case 5:
-        // Set output file name
-        optDestName = optArg;
-        break;
-
-    case 6:
-        // Allow overlapping segments
-        optAllowOverlap = TRUE;
-        dmError("Warning, allowing overlapping data.\n");
-        break;
-
-    case 7:
-        // Set memory model
-        optMemModel = atoi(optArg);
-        if (optMemModel < 0 || optMemModel >= nmemoryModels)
-        {
-            dmError("Invalid memory model number %i!\n", optMemModel);
-            return FALSE;
-        }
-        break;
-
-    case 8:
-        // Linker file
-        optLinkFileName = optArg;
-        break;
-
-    case 9:
-        // Linker file format
-        switch (tolower(optArg[0]))
-        {
-            case 'g':
-                optLinkFileFormat = FMT_GENERIC;
-                break;
-            case 'p':
-                optLinkFileFormat = FMT_PLAIN;
-                break;
-            case 'd':
-                optLinkFileFormat = FMT_DECIMAL;
-                break;
-
-            default:
-                dmError("Invalid/unknown linker file format '%s'!\n",
-                    optArg);
-                return FALSE;
-        }
-        break;
-
-    case 10:
-        // Initialization value
-        optInitValueType = 1;
-        if ((p = strrchr(optArg, ':')) != NULL)
-        {
-            *p = 0;
-            switch (tolower(p[1]))
-            {
-                case 'b': optInitValueType = 1; break;
-                case 'w': optInitValueType = 2; break;
-                case 'd': optInitValueType = 4; break;
-                default:
-                    dmError("Invalid init value type '%c' specified for '%s'.\n",
-                        p[1], optArg);
-                    return FALSE; 
-            }
-        }
-        if (!dmGetIntVal(optArg, &tmpi))
-        {
-            dmError("Invalid initvalue '%s'.\n", optArg);
-            return FALSE;
-        }
-        optInitValue = tmpi;
-        break;
-
-    case 11:
-        // Set describe mode
-        optDescribe = TRUE;
-        break;
-
-    case 12:
-        {
-            char *sectName = "Clear";
-            ssize_t sectStart, sectEnd, sectLen;
-            if (!dmParseSection(optArg, &sectStart, &sectEnd, &sectName, TRUE))
-                return FALSE;
-
-            // Allocate memory block
-            sectLen = sectEnd - sectStart + 1;
-            dmMsg(1, "Reserve $%.4x - $%.4x ($%x, %d bytes) as '%s'\n",
-                sectStart, sectEnd, sectLen, sectLen, sectName);
-
-            reserveMemBlock(sectStart, sectEnd, sectName, MTYPE_RES);
-        }
-        break;
-
-    case 13:
-        {
-            size_t cropLen;
-            if (!dmParseSection(optArg, &optCropStart, &optCropEnd, NULL, FALSE))
-                return FALSE;
-
-            cropLen = optCropEnd - optCropEnd + 1;
-            dmMsg(1, "Cutting output to $%.4x - $%.4x ($%x, %d bytes)\n",
-                optCropStart, optCropEnd, cropLen, cropLen);
-
-            optCropOutput = TRUE;
-        }
-        break;
-
-    case 14:
-        // Set loading address
-        if (strcasecmp(optArg, "none") == 0)
-            optLoadAddress = LA_NONE;
-        else
-        {
-            if (!dmGetIntVal(optArg, &tmpi))
-            {
-                dmError("Invalid loading address '%s'.\n", optArg);
-                return FALSE;
-            }
-            if (tmpi < 0 || tmpi >= 64*1024)
-            {
-                dmError("Invalid or insane loading address %d/$%x!\n",
-                    tmpi);
-                return FALSE;
-            }
-            optLoadAddress = tmpi;
-        }
-        break;
-
-    default:
-        dmError("Unknown argument '%s'.\n", currArg);
-        return FALSE;
-    }
-
-    return TRUE;
-}
-
-
-int dmLoadPRG(const char *filename, BOOL forceAddr, const ssize_t destAddr)
-{
-    FILE *f;
-    ssize_t dataSize, loadAddr, endAddr;
-    Uint16 tmpAddr;
-
-    // Open the input file
-    if ((f = fopen(filename, "rb")) == NULL)
-    {
-        dmError("Error opening input file '%s' (%s).\n",
-            filename, strerror(errno));
-        return 1;
-    }
-
-    // Get filesize
-    if ((dataSize = dmGetFileSize(f) - 2) < 0)
-    {
-        dmError("Error getting file size for '%s'.\n", filename);
-        return 6;
-    }
-
-    // Get loading address
-    if (!dm_fread_le16(f, &tmpAddr))
-    {
-        dmError("Error reading input file '%s' (%s).\n",
-            filename, strerror(errno));
-        return 2;
-    }
-
-    // Show information
-    loadAddr = forceAddr ? destAddr : tmpAddr;
-    endAddr = loadAddr + dataSize - 1;
-
-    dmPrint(1, "* Loading '%s', %s at $%.4x-$%.4x",
-        filename, forceAddr ? "PRGA" : "PRG", loadAddr, endAddr);
-
-    if (endAddr >= memModel->size)
-    {
-        dmPrint(1, " .. Does not fit into the memory!\n");
-        return 5;
-    }
-
-    // Load data
-    if (fread(&memory[loadAddr], dataSize, 1, f) < 1)
-    {
-        dmPrint(1, " .. Error: %s.\n",
-            strerror(errno));
-        return 4;
-    }
-
-    dmPrint(1, " .. OK\n");
-
-    // Add to list of blocks
-    reserveMemBlock(loadAddr, endAddr, filename, MTYPE_RES);
-
-    return 0;
-}
-
-
-int dmLoadRAW(const char *filename, const ssize_t destAddr)
-{
-    FILE *f;
-    ssize_t dataSize, endAddr;
-
-    // Open the input file
-    if ((f = fopen(filename, "rb")) == NULL)
-    {
-        dmError("Error opening input file '%s' (%s).\n",
-            filename, strerror(errno));
-        return 1;
-    }
-
-    // Get filesize
-    if ((dataSize = dmGetFileSize(f)) < 0)
-    {
-        dmError("Error getting file size for '%s'.\n", filename);
-        return 6;
-    }
-
-    // Show information
-    endAddr = destAddr + dataSize - 1;
-    dmPrint(1, "* Loading '%s', RAW at $%.4x-$%.4x",
-        filename, destAddr, endAddr);
-
-    if (endAddr >= memModel->size)
-    {
-        dmPrint(1, " .. Does not fit into the memory!\n");
-        return 5;
-    }
-
-    // Load data
-    if (fread(&memory[destAddr], dataSize, 1, f) < 1)
-    {
-        dmPrint(1, " .. Error: %s.\n",
-            strerror(errno));
-        return 4;
-    }
-
-    dmPrint(1, " .. OK\n");
-
-    // Add info to list
-    reserveMemBlock(destAddr, endAddr, filename, MTYPE_RES);
-
-    return 0;
-}
-
-
-int outputLinkData(FILE *dfile, const char *blockName, const int blockStart, const int blockEnd)
-{
-    char *tmpStr, *s, *t;
-    int blockSize;
-
-    blockSize = (blockEnd - blockStart + 1);
-
-    // Create label name from filename
-    tmpStr = dm_strdup(blockName);
-    if (tmpStr == NULL)
-    {
-        dmError("Could not allocate memory for string '%s'!\n",
-            blockName);
-        return -1;
-    }
-
-    if ((t = strrchr(tmpStr, '/')))
-        s = (t + 1);
-    else if ((t = strrchr(tmpStr, '\\')))
-        s = (t + 1);
-    else
-        s = tmpStr;
-
-    if ((t = strrchr(s, '.')))
-        *t = 0;
-
-    for (t = s; *t; t++)
-    {
-        if (!isalnum(*t))
-            *t = '_';
-    }
-
-    // Print the label line
-    switch (optLinkFileFormat)
-    {
-        case FMT_PLAIN:
-            fprintf(dfile, "%s = $%.4x\n", tmpStr, blockStart);
-            break;
-
-        case FMT_DECIMAL:
-            fprintf(dfile, "%s = %d\n", tmpStr, blockStart);
-            break;
-
-        case FMT_GENERIC:
-        default:
-            fprintf(dfile, "; %s ($%.4x - $%.4x, %d/$%x bytes)\n",
-                blockName, blockStart, blockEnd, blockSize, blockSize);
-            fprintf(dfile, "%s = $%.4x\n", s, blockStart);
-            break;
-    }
-
-    dmFree(tmpStr);
-    return 0;
-}
-
-
-/* Print out an ASCII presentation of memory map
- */
-void memPrintLine(FILE *f)
-{
-    fprintf(f, "              +------------------------------------------+\n");
-}
-
-void memPrintEmpty(FILE *f, ssize_t n)
-{
-    ssize_t i;
-    for (i = 0; i < n; i++)
-    fprintf(f, "              |                                          |\n");
-}
-
-void dmDescribeMemory(FILE *f)
-{
-    int i;
-    DMMemBlock *prev = NULL;
-
-    memPrintLine(f);
-
-    for (i = 0; i < nmemBlocks; i++)
-    {
-        DMMemBlock *curr = &memBlocks[i];
-        char desc[512], *s;
-        ssize_t siz, kz;
-
-        // Check for empty, unreserved areas
-        siz = (curr->start - 1) - (prev->end + 1) + 1;
-        if (prev != NULL && siz > 1)
-        {
-            kz = siz / (1024 * 2);
-
-            if (kz > 1) memPrintEmpty(f, kz);
-
-            snprintf(desc, sizeof(desc), "EMPTY (%d)", siz);
-            fprintf(f, "$%.4x - $%.4x | %-40s |\n", prev->end + 1, curr->start - 1, desc);
-
-            if (kz > 1) memPrintEmpty(f, kz);
-            memPrintLine(f);
-        }
-        prev = curr;
-
-        // Print current block
-        switch (curr->type)
-        {
-            case MTYPE_NONE:   s = "N/A (NC)"; break;
-            case MTYPE_ROM:    s = "ROM"; break;
-            case MTYPE_ROM_WT: s = "ROM/WT"; break;
-            case MTYPE_IO:     s = "I/O"; break;
-            case MTYPE_RES:    s = "RSVD"; break;
-            default:           s = "????"; break;
-        }
-
-        siz = curr->end - curr->start + 1;
-        kz = siz / (1024 * 2);
-
-        if (kz > 1) memPrintEmpty(f, kz);
-        snprintf(desc, sizeof(desc), "%s (%s, %d)", curr->name, s, siz);
-        fprintf(f, "$%.4x - $%.4x | %-40s |\n", curr->start, curr->end, desc);
-        if (kz > 1) memPrintEmpty(f, kz);
-        memPrintLine(f);
-
-    }
-
-    fprintf(f,
-    "\n"
-    "NC     = Not Connected\n"
-    "RSVD   = Reserved\n"
-    "ROM/WT = RAM under 'write-through' ROM\n"
-    "\n"
-    );
-}
-
-
-/*
- * The main program
- */
-int main(int argc, char *argv[])
-{
-    FILE *dfile = NULL;
-    BOOL hasOverlaps;
-    int i, j;
-    ssize_t startAddr, endAddr, dataSize, totalSize;
-
-    dmInitProg("objlink", "Simple file-linker", "0.80", NULL, NULL);
-    dmVerbosity = 1;
-
-    // Parse arguments
-    if (!dmArgsProcess(argc, argv, optList, optListN,
-        argHandleOpt, NULL, TRUE))
-        exit(1);
-
-    if (nsrcFiles < 1)
-    {
-        dmError("Nothing to do. (try --help)\n");
-        exit(0);
-    }
-
-    // Allocate memory
-    memModel = &memoryModels[optMemModel];
-    dmMsg(1, "Using memory model #%d '%s', %d bytes.\n",
-        optMemModel, memModel->name, memModel->size);
-
-    memory = (Uint8 *) dmMalloc(memModel->size + 32);
-    if (memory == NULL)
-    {
-        dmError("Could not allocate memory.\n");
-        exit(2);
-    }
-
-    // Initialize memory
-    dmMsg(1, "Initializing memory with ");
-
-    if (optInitValueType == 1 || optInitValue <= 0xff)
-    {
-        dmPrint(1, "BYTE 0x%.2x\n", optInitValue);
-        memset(memory, optInitValue, memModel->size);
-    }
-    else
-    if (optInitValueType == 2 || optInitValue <= 0xffff)
-    {
-        uint16_t *mp = (uint16_t *) memory;
-        dmPrint(1, "WORD 0x%.4x\n", optInitValue);
-        for (i = memModel->size / sizeof(*mp); i; i--)
-        {
-            *mp++ = optInitValue;
-        }
-    }
-    else
-    {
-        Uint32 *mp = (Uint32 *) memory;
-        dmPrint(1, "DWORD 0x%.8x\n", optInitValue);
-        for (i = memModel->size / sizeof(*mp); i; i--)
-        {
-            *mp++ = optInitValue;
-        }
-    }
-
-    // Load the datafiles
-    for (i = 0; i < nsrcFiles; i++)
-    switch (srcFiles[i].type)
-    {
-        case STYPE_RAW:
-            dmLoadRAW(srcFiles[i].filename, srcFiles[i].addr);
-            break;
-
-        case STYPE_PRG:
-            dmLoadPRG(srcFiles[i].filename, FALSE, 0);
-            break;
-
-        case STYPE_PRGA:
-            dmLoadPRG(srcFiles[i].filename, TRUE, srcFiles[i].addr);
-            break;
-    }
-
-    // Add memory model blocks
-    dmMsg(1, "Applying memory model restrictions...\n");
-    for (i = 0; i < memModel->nmemBlocks; i++)
-    {
-        reserveMemBlock(
-            memModel->memBlocks[i].start,
-            memModel->memBlocks[i].end,
-            memModel->memBlocks[i].name,
-            memModel->memBlocks[i].type);
-    }
-
-    // Sort the blocks
-    qsort(memBlocks, nmemBlocks, sizeof(DMMemBlock), compareMemBlock);
-
-    // Check for overlapping conflicts
-    hasOverlaps = FALSE;
-    for (i = 0; i < nmemBlocks; i++)
-    for (j = 0; j < nmemBlocks; j++)
-    if (j != i && memBlocks[i].type == MTYPE_RES)
-    {
-        DMMemBlock *mbi = &memBlocks[i],
-                   *mbj = &memBlocks[j];
-
-        // Check for per-file conflicts
-        if ((mbj->start >= mbi->start && mbj->start <= mbi->end) ||
-            (mbj->end >= mbi->start && mbj->end <= mbi->end))
-        {
-            dmPrint(1, "* '%s' and '%s' overlap ($%.4x-$%.4x  vs  $%.4x-$%.4x)\n",
-                mbi->name, mbj->name, mbi->start,
-                mbi->end, mbj->start, mbj->end);
-            hasOverlaps = TRUE;
-        }
-    }
-
-    if (!optAllowOverlap && hasOverlaps)
-    {
-        dmError("Error occured, overlaps not allowed.\n");
-        exit(5);
-    }
-
-    // Find out start and end-addresses
-    startAddr = memModel->size;
-    totalSize = endAddr = 0;
-    for (i = 0; i < nmemBlocks; i++)
-    {
-        DMMemBlock *mbi = &memBlocks[i];
-        if (mbi->type == MTYPE_RES)
-        {
-            if (mbi->start < startAddr)
-                startAddr = mbi->start;
-
-            if (mbi->end > endAddr)
-                endAddr = mbi->end;
-
-            totalSize += (mbi->end - mbi->start + 1);
-        }
-    }
-
-    if (startAddr >= memModel->size || endAddr < startAddr)
-    {
-        dmError("Invalid saveblock addresses (start=$%.4x, end=$%.4x)!\n", startAddr, endAddr);
-        exit(8);
-    }
-
-    // Output linkfile
-    if (optLinkFileName)
-    {
-        dmMsg(1, "Writing linkfile to '%s'\n", optLinkFileName);
-        if ((dfile = fopen(optLinkFileName, "wb")) == NULL)
-        {
-            dmError("Error creating file '%s' (%s).\n", optLinkFileName, strerror(errno));
-            exit(1);
-        }
-
-        switch (optLinkFileFormat)
-        {
-            case FMT_GENERIC:
-            default:
-                fprintf(dfile, "; Definitions generated by %s v%s\n",
-                    dmProgName, dmProgVersion);
-                break;
-        }
-
-        for (i = 0; i < nmemBlocks; i++)
-        {
-            DMMemBlock *mbi = &memBlocks[i];
-            outputLinkData(dfile, mbi->name, mbi->start, mbi->end);
-        }
-
-        fclose(dfile);
-    }
-
-    // Show some information
-    if (optCropOutput)
-    {
-        startAddr = optCropStart;
-        endAddr = optCropEnd;
-    }
-
-    dataSize = endAddr - startAddr + 1;
-
-    if (dataSize - totalSize > 0)
-    {
-        dmMsg(1, "Total of %d/$%x bytes unused(?) areas.\n",
-            dataSize - totalSize, dataSize - totalSize);
-    }
-
-    dmMsg(1, "Writing $%.4x - $%.4x (%d/$%x bytes) ",
-        startAddr, endAddr, dataSize, dataSize);
-
-
-    // Open the destination file
-    if (optDestName == NULL)
-    {
-        dfile = stdout;
-        dmPrint(1, "...\n");
-    }
-    else if ((dfile = fopen(optDestName, "wb")) == NULL)
-    {
-        dmError("Error creating output file '%s' (%s).\n", optDestName, strerror(errno));
-        exit(1);
-    }
-    else
-        dmPrint(1, "to '%s'\n", optDestName);
-
-    // Save loading address
-    if (optLoadAddress >= 0)
-    {
-        dmMsg(1, "Using specified loading address $%.4x\n", optLoadAddress);
-        dm_fwrite_le16(dfile, optLoadAddress);
-    }
-    else
-    if (optLoadAddress == LA_AUTO)
-    {
-        dmMsg(1, "Using automatic loading address $%.4x\n", startAddr);
-        dm_fwrite_le16(dfile, startAddr);
-    }
-    else
-    {
-        dmMsg(1, "Writing raw output, without loading address.\n");
-    }
-
-    // Save the data
-    if (fwrite(&memory[startAddr], dataSize, 1, dfile) < 1)
-    {
-        dmError("Error writing to file (%s)\n", strerror(errno));
-    }
-
-    fclose(dfile);
-
-    // Describe
-    if (optDescribe)
-        dmDescribeMemory(stdout);
-
-    exit(0);
-    return 0;
-}
--- a/packed.c	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,465 +0,0 @@
-/*
- * 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;
-}
--- a/ppl.c	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,932 +0,0 @@
-/*
- * Cyrbe Pasci Player - A simple SDL-based UI for XM module playing
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2012 Tecnic Software productions (TNSP)
- *
- * Please read file 'COPYING' for information on license and distribution.
- */
-#include <SDL.h>
-#include "dmlib.h"
-
-#include "jss.h"
-#include "jssmod.h"
-#include "jssmix.h"
-#include "jssplr.h"
-
-#include "dmargs.h"
-#include "dmimage.h"
-#include "dmtext.h"
-
-#include "setupfont.h"
-
-
-struct
-{
-    BOOL exitFlag;
-    SDL_Surface *screen;
-    SDL_Event event;
-    int optScrWidth, optScrHeight, optVFlags, optScrDepth;
-
-    int actChannel;
-    BOOL pauseFlag;
-
-    JSSModule *mod;
-    JSSMixer *dev;
-    JSSPlayer *plr;
-    SDL_AudioSpec afmt;
-} engine;
-
-struct
-{
-    Uint32 boxBg, inboxBg, box1, box2, viewDiv, activeRow, activeChannel;
-} col;
-
-
-DMBitmapFont *font = NULL;
-
-char    *optFilename = NULL;
-int     optOutFormat = JSS_AUDIO_S16,
-        optOutChannels = 2,
-        optOutFreq = 48000,
-        optMuteOChannels = -1,
-        optStartOrder = 0;
-BOOL    optUsePlayTime = FALSE;
-size_t  optPlayTime;
-
-
-DMOptArg optList[] =
-{
-    {  0, '?', "help",     "Show this help", OPT_NONE },
-    {  1, 'v', "verbose",  "Be more verbose", OPT_NONE },
-    {  2,   0, "fs",       "Fullscreen", OPT_NONE },
-    {  3, 'w', "window",   "Initial window size/resolution -w 640x480", OPT_ARGREQ },
-
-    {  4, '1', "16bit",    "16-bit output", OPT_NONE },
-    {  5, '8', "8bit",     "8-bit output", OPT_NONE },
-    {  6, 'm', "mono",     "Mono output", OPT_NONE },
-    {  7, 's', "stereo",   "Stereo output", OPT_NONE },
-    {  8, 'f', "freq",     "Output frequency", OPT_ARGREQ },
-
-    {  9, 'M', "mute",     "Mute other channels than #", OPT_ARGREQ },
-    { 10, 'o', "order",    "Start from order #", OPT_ARGREQ },
-    { 11, 't', "time",     "Play for # seconds", OPT_ARGREQ },
-};
-
-const int optListN = sizeof(optList) / sizeof(optList[0]);
-
-
-void argShowHelp()
-{
-    dmPrintBanner(stdout, dmProgName, "[options] <module>");
-    dmArgsPrintHelp(stdout, optList, optListN);
-}
-
-
-BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
-{
-    switch (optN) {
-        case 0:
-            argShowHelp();
-            exit(0);
-            break;
-
-        case 1:
-            dmVerbosity++;
-            break;
-        
-        case 2:
-            engine.optVFlags |= SDL_FULLSCREEN;
-            break;
-
-        case 3:
-            {
-                int w, h;
-                if (sscanf(optArg, "%dx%d", &w, &h) == 2)
-                {
-                    if (w < 320 || h < 200 || w > 3200 || h > 3200)
-                    {
-                        dmError("Invalid width or height: %d x %d\n", w, h);
-                        return FALSE;
-                    }
-                    engine.optScrWidth = w;
-                    engine.optScrHeight = h;
-                }
-                else 
-                {
-                    dmError("Invalid size argument '%s'.\n", optArg);
-                    return FALSE;
-                }
-            }
-            break;
-
-        case 4:
-            optOutFormat = JSS_AUDIO_S16;
-            break;
-
-        case 5:
-            optOutFormat = JSS_AUDIO_U8;
-            break;
-
-        case 6:
-            optOutChannels = JSS_AUDIO_MONO;
-            break;
-
-        case 7:
-            optOutChannels = JSS_AUDIO_STEREO;
-            break;
-
-        case 8:
-            optOutFreq = atoi(optArg);
-            break;
-
-        case 9:
-            optMuteOChannels = atoi(optArg);
-            break;
-
-        case 10:
-            optStartOrder = atoi(optArg);
-            break;
-
-        case 11:
-            optPlayTime = atoi(optArg);
-            optUsePlayTime = TRUE;
-            break;
-
-        default:
-            dmError("Unknown option '%s'.\n", currArg);
-            return FALSE;
-    }
-    
-    return TRUE;
-}
-
-
-BOOL argHandleFile(char *currArg)
-{
-    if (!optFilename)
-        optFilename = currArg;
-    else
-    {
-        dmError("Too many filename arguments '%s'\n", currArg);
-        return FALSE;
-    }
-    
-    return TRUE;
-}
-
-
-void dmDrawBMTextConstQ(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt)
-{
-    const char *ptr = fmt;
-    DMUnscaledBlitFunc blit = NULL;
-
-    while (*ptr)
-    {
-        int ch = *ptr++;
-        SDL_Surface *glyph;
-
-        if (ch == '_')
-        {
-            xc += 4;
-            continue;
-        }
-        
-        if (ch >= 0 && ch < font->nglyphs && (glyph = font->glyphs[ch]) != NULL)
-        {
-            if (blit == NULL)
-                blit = dmGetUnscaledBlitFunc(glyph->format, screen->format, mode);
-            
-            blit(glyph, xc, yc, screen);
-            xc += font->width;
-        }
-        else
-            xc += font->width;
-    }
-}
-
-
-void dmDrawBMTextVAQ(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt, va_list ap)
-{
-    char tmp[512];
-    vsnprintf(tmp, sizeof(tmp), fmt, ap);
-    dmDrawBMTextConstQ(screen, font, mode, xc, yc, tmp);
-}
-
-
-void dmDrawBMTextQ(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt, ...)
-{
-    va_list ap;
-    
-    va_start(ap, fmt);
-    dmDrawBMTextVAQ(screen, font, mode, xc, yc, fmt, ap);
-    va_end(ap);
-}
-
-
-Uint32 dmCol(float r, float g, float b)
-{
-    return dmMapRGB(engine.screen, 255.0f * r, 255.0f * g, 255.0f * b);
-}
-
-
-BOOL dmInitializeVideo()
-{
-    SDL_FreeSurface(engine.screen);
-
-    engine.screen = SDL_SetVideoMode(
-        engine.optScrWidth, engine.optScrHeight, engine.optScrDepth,
-        engine.optVFlags | SDL_RESIZABLE | SDL_SWSURFACE | SDL_HWPALETTE);
-
-    if (engine.screen == NULL)
-    {
-        dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError());
-        return FALSE;
-    }
-
-    col.inboxBg    = dmCol(0.6, 0.5, 0.2);
-    col.boxBg      = dmCol(0.7, 0.6, 0.3);
-    col.box1       = dmCol(1.0, 0.9, 0.6);
-    col.box2       = dmCol(0.3, 0.3, 0.15);
-    col.viewDiv    = dmCol(0,0,0);
-    col.activeRow  = dmCol(0.5,0.4,0.1);
-    col.activeChannel = dmCol(0.6, 0.8, 0.2);
-
-    return TRUE;
-}
-
-
-void dmDisplayChn(SDL_Surface *screen, int x0, int y0, int x1, int y1, int nchannel, JSSChannel *chn)
-{
-    int yh = y1 - y0 - 2;
-    if (yh < 10 || chn == NULL)
-        return;
-
-    int xc, ym = y0 + (y1 - y0) / 2, vol = FP_GETH(chn->chVolume);
-    int pitch = screen->pitch / sizeof(Uint32);
-    int len = FP_GETH(chn->chSize);
-    DMFixedPoint offs = chn->chPos;
-    Uint32 coln = dmCol(0.0, 0.8, 0.0), colx = dmCol(1.0, 0, 0);
-    Uint32 *pix = screen->pixels;
-    Sint16 *data = chn->chData;
-
-
-    dmFillBox3D(screen, x0, y0, x1, y1,
-        (chn->chMute ? dmCol(0.3,0.1,0.1) : dmCol(0,0,0)),
-        nchannel == engine.actChannel ? colx : col.box2,
-        nchannel == engine.actChannel ? colx : col.box1);
-
-    if (chn->chData == NULL || !chn->chPlaying)
-        return;
-
-    if (chn->chDirection)
-    {
-        for (xc = x0 + 1; xc < x1 - 1; xc++)
-        {
-            if (FP_GETH(offs) >= len)
-                break;
-            Sint16 val = ym + (data[FP_GETH(offs)] * yh * vol) / (65535 * 255);
-            pix[xc + val * pitch] = coln;
-            FP_ADD(offs, chn->chDeltaO);
-        }
-    }
-    else
-    {
-        for (xc = x0 + 1; xc < x1 - 1; xc++)
-        {
-            if (FP_GETH(offs) < 0)
-                break;
-            Sint16 val = ym + (data[FP_GETH(offs)] * yh * vol) / (65535 * 255);
-            pix[xc + val * pitch] = coln;
-            FP_SUB(offs, chn->chDeltaO);
-        }
-    }
-}
-
-
-void dmDisplayChannels(SDL_Surface *screen, int x0, int y0, int x1, int y1, JSSMixer *dev)
-{
-    int nchannel, qx, qy,
-        qwidth = x1 - x0,
-        qheight = y1 - y0,
-        nwidth = jsetNChannels,
-        nheight = 1;
-    
-    if (qheight < 40)
-        return;
-    
-    while (qwidth / nwidth <= 60 && qheight / nheight >= 40)
-    {
-        nheight++;
-        nwidth /= nheight;
-    }
-
-//    fprintf(stderr, "%d x %d\n", nwidth, nheight);
-    
-    if (qheight / nheight <= 40)
-    {
-        nwidth = qwidth / 60;
-        nheight = qheight / 40;
-    }
-
-    qwidth /= nwidth;
-    qheight /= nheight;
-        
-    for (nchannel = qy = 0; qy < nheight && nchannel < jsetNChannels; qy++)
-    {
-        for (qx = 0; qx < nwidth && nchannel < jsetNChannels; qx++)
-        {
-            int xc = x0 + qx * qwidth,
-                yc = y0 + qy * qheight;
-
-            dmDisplayChn(screen, xc + 1, yc + 1,
-                xc + qwidth - 1, yc + qheight - 1,
-                nchannel, &dev->channels[nchannel]);
-
-            nchannel++;
-        }
-    }
-}
-
-
-static 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";
-
-static const char jmpHexTab[16] = "0123456789ABCDEF";
-
-static inline char dmHexVal(int v)
-{
-    return jmpHexTab[v & 15];
-}
-
-void dmPrintNote(SDL_Surface *screen, int xc, int yc, JSSNote *n)
-{
-    char text[32];
-    char *ptr = text;
-    
-    switch (n->note)
-    {
-        case jsetNotSet:
-            strcpy(ptr, "..._");
-            break;
-        case jsetNoteOff:
-            strcpy(ptr, "===_");
-            break;
-        default:
-            sprintf(ptr, "%s%i_",
-                patNoteTable[n->note % 12],
-                n->note / 12);
-            break;
-    }
-
-    ptr += 4;
-
-    if (n->instrument != jsetNotSet)
-    {
-        int v = n->instrument + 1;
-        *ptr++ = dmHexVal(v >> 4);
-        *ptr++ = dmHexVal(v);
-    }
-    else
-    {
-        *ptr++ = '.';
-        *ptr++ = '.';
-    }
-    *ptr++ = '_';
-
-    if (n->volume == jsetNotSet)
-    {
-        *ptr++ = '.';
-        *ptr++ = '.';
-    }
-    else
-    if (n->volume >= 0x00 && n->volume <= 0x40)
-    {
-        *ptr++ = dmHexVal(n->volume >> 4);
-        *ptr++ = dmHexVal(n->volume);
-    }
-    else
-    {
-        char c;
-        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;
-        }
-        *ptr++ = c;
-        *ptr++ = dmHexVal(n->volume);
-    }
-    *ptr++ = '_';
-    
-    if (n->effect >= 0 && n->effect < jmpNMODEffectTable)
-        *ptr++ = jmpMODEffectTable[n->effect];
-    else
-        *ptr++ = (n->effect == jsetNotSet ? '.' : '?');
-
-    if (n->param != jsetNotSet)
-    {
-        *ptr++ = dmHexVal(n->param >> 4);
-        *ptr++ = dmHexVal(n->param);
-    }
-    else
-    {
-        *ptr++ = '.';
-        *ptr++ = '.';
-    }
-
-    *ptr = 0;
-
-    dmDrawBMTextConstQ(screen, font, DMD_TRANSPARENT, xc, yc, text);
-}
-
-
-void dmDisplayPattern(SDL_Surface *screen, int x0, int y0, int x1, int y1, JSSPattern *pat, int row)
-{
-    int cwidth = (font->width * 10 + 3 * 4 + 5),
-        lwidth = 6 + font->width * 3,
-        qy0 = y0 + font->height + 2,
-        qy1 = y1 - font->height - 2,
-        qwidth  = ((x1 - x0 - lwidth) / cwidth),
-        qheight = ((qy1 - qy0 - 4) / (font->height + 1)),
-        nrow, nchannel, yc, choffs,
-        midrow = qheight / 2;
-
-    if (engine.actChannel < qwidth / 2)
-        choffs = 0;
-    else
-    if (engine.actChannel >= pat->nchannels - qwidth/2)
-        choffs = pat->nchannels - qwidth;
-    else
-        choffs = engine.actChannel - qwidth/2;
-
-    dmDrawBox3D(screen, x0 + lwidth, qy0, x1, qy1, col.box2, col.box1);
-
-    for (nchannel = 0; nchannel < qwidth; nchannel++)
-    {
-        int bx0 = x0 + lwidth + 1 + nchannel * cwidth, 
-            bx1 = bx0 + cwidth;
-            
-        if (engine.actChannel == nchannel + choffs)
-        {
-            dmFillRect(screen, bx0+1, qy0 + 1, bx1-1, qy1 - 1, col.activeChannel);
-        }
-        else
-        {
-            dmFillRect(screen, bx0+1, qy0 + 1, bx1-1, qy1 - 1, col.inboxBg);
-        }
-    }
-
-    yc = qy0 + 2 + (font->height + 1) * midrow;
-    dmFillRect(screen, x0 + lwidth + 1, yc - 1, x1 - 1, yc + font->height, col.activeRow);
-
-    for (nchannel = 0; nchannel < qwidth; nchannel++)
-    {
-        int bx0 = x0 + lwidth + 1 + nchannel * cwidth, 
-            bx1 = bx0 + cwidth;
-
-        dmDrawVLine(screen, qy0 + 1, qy1 - 1, bx1, col.viewDiv);
-
-        if (jvmGetMute(engine.dev, nchannel + choffs))
-        {
-            dmDrawBMTextConstQ(screen, font, DMD_TRANSPARENT,
-                bx0 + (cwidth - font->width * 5) / 2, qy1 + 3, "MUTED");
-        }
-        
-        dmDrawBMTextQ(screen, font, DMD_TRANSPARENT,
-            bx0 + (cwidth - font->width * 3) / 2, y0 + 1, "%3d",
-            nchannel + choffs);
-    }
-    
-    for (nrow = 0; nrow < qheight; nrow++)
-    {
-        int crow = nrow - midrow + row;
-        yc = qy0 + 2 + (font->height + 1) * nrow;
-        
-        if (crow >= 0 && crow < pat->nrows)
-        {
-            dmDrawBMTextQ(screen, font, DMD_TRANSPARENT, x0, yc, "%03d", crow);
-
-            for (nchannel = 0; nchannel < qwidth; nchannel++)
-            {
-                if (choffs + nchannel >= pat->nchannels)
-                    break;
-
-                dmPrintNote(screen, x0 + lwidth + 4 + nchannel * cwidth, yc,
-                    pat->data + (pat->nchannels * crow) + choffs + nchannel);
-            }
-        }
-    }
-}
-
-
-void audioCallback(void *userdata, Uint8 *stream, int len)
-{
-    JSSMixer *d = (JSSMixer *) userdata;
-
-    if (d != NULL)
-    {
-        jvmRenderAudio(d, stream, len / jvmGetSampleSize(d));
-    }
-}
-
-
-void dmMuteChannels(BOOL mute)
-{
-    int i;
-    for (i = 0; i < engine.mod->nchannels; i++)
-        jvmMute(engine.dev, i, mute);
-}
-
-int main(int argc, char *argv[])
-{
-    BOOL initSDL = FALSE, audioInit = FALSE;
-    DMResource *file = NULL;
-    int result = -1;
-    BOOL muteState = FALSE;
-
-    memset(&engine, 0, sizeof(engine));
-
-    engine.optScrWidth = 640;
-    engine.optScrHeight = 480;
-    engine.optScrDepth = 32;
-
-    dmInitProg("CBP", "Cyrbe Basci Player", "0.1", NULL, NULL);
-
-    // Parse arguments
-    if (!dmArgsProcess(argc, argv, optList, optListN,
-        argHandleOpt, argHandleFile, TRUE))
-        exit(1);
-
-    // Open the files
-    if (optFilename == NULL)
-    {
-        dmError("No filename specified.\n");
-        return 1;
-    }
-    
-    if ((file = dmf_create_stdio(optFilename, "rb")) == NULL)
-    {
-        int err = dmGetErrno();
-        dmError("Error opening file '%s', %d: (%s)\n",
-            optFilename, err, dmErrorStr(err));
-        return 1;
-    }
-
-    // Initialize miniJSS
-    jssInit();
-
-    // Read module file
-    dmMsg(1, "Reading file: %s\n", optFilename);
-#ifdef JSS_SUP_XM
-    dmMsg(2, "* Trying XM...\n");
-    result = jssLoadXM(file, &engine.mod);
-#endif
-#ifdef JSS_SUP_JSSMOD
-    if (result != 0)
-    {
-        size_t bufgot, bufsize = dmfsize(file);
-        Uint8 *buf = dmMalloc(bufsize);
-        dmfseek(file, 0L, SEEK_SET);
-        dmMsg(2, "* Trying JSSMOD (%d bytes, %p)...\n", bufsize, buf);
-        if ((bufgot = dmfread(buf, 1, bufsize, file)) != bufsize)
-        {
-            dmf_close(file);
-            dmError("Error reading file (not enough data %d), #%d: %s\n",
-                bufgot, dmferror(file), dmErrorStr(dmferror(file)));
-            goto error_exit;
-        }
-        result = jssLoadJSSMOD(buf, bufsize, &engine.mod);
-        dmFree(buf);
-    }
-#endif
-    dmf_close(file);
-
-    if (result != DMERR_OK)
-    {
-        dmError("Error loading module file, %d: %s\n",
-            result, dmErrorStr(result));
-        goto error_exit;
-    }
-
-    // Try to convert it
-    if ((result = jssConvertModuleForPlaying(engine.mod)) != DMERR_OK)
-    {
-        dmError("Could not convert module for playing, %d: %s\n",
-            result, dmErrorStr(result));
-        goto error_exit;
-    }
-
-    // Get font
-//    file = dmf_create_stdio("fnsmall.fnt", "rb");
-    file = dmf_create_memio(NULL, "pplfont.fnt", engineSetupFont, sizeof(engineSetupFont));
-    if (file == NULL)
-    {
-        dmError("Error opening font file 'pplfont.fnt'.\n");
-        goto error_exit;
-    }
-    result = dmLoadBitmapFont(file, &font);
-    dmf_close(file);
-    if (result != DMERR_OK)
-    {
-        dmError("Could not load font from file, %d: %s\n",
-            result, dmErrorStr(result));
-        goto error_exit;
-    }
-
-    // Initialize SDL components
-    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) != 0)
-    {
-        dmError("Could not initialize SDL: %s\n", SDL_GetError());
-        goto error_exit;
-    }
-    initSDL = TRUE;
-
-
-    // Initialize mixing device
-    dmMsg(2, "Initializing miniJSS mixer with: %d, %d, %d\n",
-        optOutFormat, optOutChannels, optOutFreq);
-
-    engine.dev = jvmInit(optOutFormat, optOutChannels, optOutFreq, JMIX_AUTO);
-    if (engine.dev == NULL)
-    {
-        dmError("jvmInit() returned NULL\n");
-        goto error_exit;
-    }
-    
-    switch (optOutFormat)
-    {
-        case JSS_AUDIO_S16: engine.afmt.format = AUDIO_S16SYS; break;
-        case JSS_AUDIO_U16: engine.afmt.format = AUDIO_U16SYS; break;
-        case JSS_AUDIO_S8:  engine.afmt.format = AUDIO_S8; break;
-        case JSS_AUDIO_U8:  engine.afmt.format = AUDIO_U8; break;
-        default:
-            dmError("Unsupported audio format %d (could not set matching SDL format)\n",
-                optOutFormat);
-            goto error_exit;
-    }
-
-    engine.afmt.freq     = optOutFreq;
-    engine.afmt.channels = optOutChannels;
-    engine.afmt.samples  = optOutFreq / 16;
-    engine.afmt.callback = audioCallback;
-    engine.afmt.userdata = (void *) engine.dev;
-
-    // Open the audio device
-    if (SDL_OpenAudio(&engine.afmt, NULL) < 0)
-    {
-        dmError("Couldn't open SDL audio: %s\n",
-            SDL_GetError());
-        goto error_exit;
-    }
-    audioInit = TRUE;
-    
-    // Initialize player
-    if ((engine.plr = jmpInit(engine.dev)) == NULL)
-    {
-        dmError("jmpInit() returned NULL\n");
-        goto error_exit;
-    }
-    
-    jvmSetCallback(engine.dev, jmpExec, engine.plr);
-    jmpSetModule(engine.plr, engine.mod);
-    jmpPlayOrder(engine.plr, optStartOrder);
-    jvmSetGlobalVol(engine.dev, 64);
-
-    if (optMuteOChannels >= 0 && optMuteOChannels < engine.mod->nchannels)
-    {
-        dmMuteChannels(TRUE);
-        jvmMute(engine.dev, optMuteOChannels, FALSE);
-        engine.actChannel = optMuteOChannels;
-        muteState = TRUE;
-    }
-
-    // Initialize video
-    if (!dmInitializeVideo())
-        goto error_exit;
-
-    SDL_WM_SetCaption(dmProgDesc, dmProgName);
-
-    SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
-
-    // okay, main loop here ... "play" module and print out info
-    SDL_LockAudio();
-    SDL_PauseAudio(0);
-    SDL_UnlockAudio();
-
-    int currTick, prevTick = 0, prevRow = -1;
-    
-    while (!engine.exitFlag)
-    {
-        currTick = SDL_GetTicks();
-        BOOL force = (currTick - prevTick > 500), updated = FALSE;
-
-        while (SDL_PollEvent(&engine.event))
-        switch (engine.event.type)
-        {
-            case SDL_KEYDOWN:
-                switch (engine.event.key.keysym.sym)
-                {
-                    case SDLK_ESCAPE:
-                        engine.exitFlag = TRUE;
-                        break;
-                    
-                    case SDLK_SPACE:
-                        engine.pauseFlag = !engine.pauseFlag;
-                        SDL_PauseAudio(engine.pauseFlag);
-                        break;
-
-                    case SDLK_LEFT:
-                        if (engine.actChannel > 0)
-                        {
-                            engine.actChannel--;
-                            force = TRUE;
-                        }
-                        break;
-
-                    case SDLK_RIGHT:
-                        if (engine.actChannel < engine.mod->nchannels)
-                        {
-                            engine.actChannel++;
-                            force = TRUE;
-                        }
-                        break;
-
-                    case SDLK_m:
-                        if (engine.event.key.keysym.mod & KMOD_SHIFT)
-                        {
-                            muteState = !muteState;
-                            dmMuteChannels(muteState);
-                        }
-                        else
-                        if (engine.event.key.keysym.mod & KMOD_CTRL)
-                        {
-                            dmMuteChannels(FALSE);
-                        }
-                        else
-                        {
-                            jvmMute(engine.dev, engine.actChannel, !jvmGetMute(engine.dev, engine.actChannel));
-                        }
-                        force = TRUE;
-                        break;
-
-                    case SDLK_PAGEUP:
-                        JSS_LOCK(engine.dev);
-                        JSS_LOCK(engine.plr);
-                        jmpChangeOrder(engine.plr, dmClamp(engine.plr->order - 1, 0, engine.mod->norders));
-                        JSS_UNLOCK(engine.plr);
-                        JSS_UNLOCK(engine.dev);
-                        force = TRUE;
-                        break;
-
-                    case SDLK_PAGEDOWN:
-                        JSS_LOCK(engine.dev);
-                        JSS_LOCK(engine.plr);
-                        jmpChangeOrder(engine.plr, dmClamp(engine.plr->order + 1, 0, engine.mod->norders));
-                        JSS_UNLOCK(engine.plr);
-                        JSS_UNLOCK(engine.dev);
-                        force = TRUE;
-                        break;
-
-                    case SDLK_f:
-                        engine.optVFlags ^= SDL_FULLSCREEN;
-                        if (!dmInitializeVideo())
-                            goto error_exit;
-                        force = TRUE;
-                        break;
-
-                    default:
-                        break;
-                }
-
-                break;
-
-            case SDL_VIDEORESIZE:
-                engine.optScrWidth = engine.event.resize.w;
-                engine.optScrHeight = engine.event.resize.h;
-
-                if (!dmInitializeVideo())
-                    goto error_exit;
-
-                break;
-
-            case SDL_VIDEOEXPOSE:
-                break;
-
-            case SDL_QUIT:
-                engine.exitFlag = TRUE;
-                break;
-        }
-
-
-#if 1
-        JSS_LOCK(engine.plr);
-        JSSPattern *currPattern = engine.plr->pattern;
-        int currRow = engine.plr->row;
-        if (!engine.plr->isPlaying)
-            engine.exitFlag = TRUE;
-        JSS_UNLOCK(engine.plr);
-        
-        if (currRow != prevRow || force)
-        {
-            prevRow = currRow;
-            force = TRUE;
-        }
-        
-        // Draw frame
-        if (SDL_MUSTLOCK(engine.screen) != 0 && SDL_LockSurface(engine.screen) != 0)
-        {
-            dmError("Can't lock surface.\n");
-            goto error_exit;
-        }
-
-        if (force)
-        {
-            dmClearSurface(engine.screen, col.boxBg);
-            
-            dmDrawBMTextQ(engine.screen, font, DMD_TRANSPARENT, 5, 5, "%s v%s by ccr/TNSP - (c) Copyright 2012 TNSP", dmProgDesc, dmProgVersion);
-
-            dmDrawBMTextQ(engine.screen, font, DMD_TRANSPARENT, 5, 5 + 12 + 11,
-                "Song: '%s'",
-                engine.mod->moduleName);
-
-            dmDisplayPattern(engine.screen, 5, 40,
-                engine.screen->w - 6, engine.screen->h * 0.8,
-                currPattern, currRow);
-            
-            JSS_LOCK(engine.plr);
-            dmDrawBMTextQ(engine.screen, font, DMD_TRANSPARENT, 5, 5 + 12,
-            "Tempo: %3d | Speed: %3d | Row: %3d/%-3d | Order: %3d/%-3d | Pattern: %3d/%-3d",
-            engine.plr->tempo, engine.plr->speed,
-            engine.plr->row, engine.plr->pattern->nrows,
-            engine.plr->order, engine.mod->norders,
-            engine.plr->npattern, engine.mod->npatterns);
-            JSS_UNLOCK(engine.plr);
-            updated = TRUE;
-        }
-
-        if (force || currTick - prevTick >= (engine.pauseFlag ? 100 : 20))
-        {
-            JSS_LOCK(engine.dev);
-            dmDisplayChannels(engine.screen, 5, engine.screen->h * 0.8 + 5,
-                engine.screen->w - 5, engine.screen->h - 5, engine.dev);
-            JSS_UNLOCK(engine.dev);
-            updated = TRUE;
-        }
-
-        if (force)
-            prevTick = currTick;
-
-#endif
-        // Flip screen
-        if (SDL_MUSTLOCK(engine.screen) != 0)
-            SDL_UnlockSurface(engine.screen);
-
-        if (updated)
-            SDL_Flip(engine.screen);
-
-        SDL_Delay(engine.pauseFlag ? 100 : 30);
-    }
-
-error_exit:
-    if (engine.screen)
-        SDL_FreeSurface(engine.screen);
-
-    dmMsg(0, "Audio shutdown.\n");
-    if (audioInit)
-    {
-        SDL_LockAudio();
-        SDL_PauseAudio(1);
-        SDL_UnlockAudio();
-        SDL_CloseAudio();
-    }
-
-    jmpClose(engine.plr);
-    jvmClose(engine.dev);
-    jssFreeModule(engine.mod);
-
-    dmFreeBitmapFont(font);
-
-    if (initSDL)
-        SDL_Quit();
-
-    jssClose();
-
-    return 0;
-}
--- a/svg2qd.py	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-#!/usr/bin/python
-import sys
-import re
-import xml.etree.ElementTree as ET
-
-
-def bf(x) :
-   return int(round(float(x)))
-
-
-def printVertex(v) :
-   if type(v) is list :
-      return "{:.2f},{:.2f},{:.2f}".format(v[0], v[1], v[2])
-   else :
-      return v
-
-
-def getTransform(elem) :
-   if "transform" in elem.attrib :
-      ntrans = elem.attrib["transform"]
-      tmatch = re.compile(r"translate\((.*?)\)", re.IGNORECASE)
-      for trns in tmatch.finditer(ntrans) :
-         coord = trns.group(1).split(",")
-         return [float(coord[0]), float(coord[1]), 0]
-   return None
-
-
-def getStyle(elem) :
-   style = {}
-   if "style" in elem.attrib :
-      for elem in elem.attrib["style"].split(";") :
-         kv = elem.split(":")
-         style[kv[0]] = kv[1]
-   return style
-
-
-def printVertices(type, vertices, width, level) :
-   if len(vertices) > 0 :
-      list = map(lambda v:printVertex(v), vertices)
-      str = "# "+ type
-      if type == "m" :
-         str = "R"
-      elif type == "M" :
-         str = "L"
-      elif type == "c" :
-         str = "R"
-      print "{}{}{} {} {}".format("  "*level, str, len(vertices)-1, " ".join(list), width)
-
-
-def printPath(path, level) :
-   style = getStyle(path)
-   width = bf(style["stroke-width"])
-
-   trans = getTransform(path)
-   if trans :
-      print "{}G{}".format("  "*level, printVertex(trans))
-
-   vertices = []
-   type = ""
-   for elem in path.attrib["d"].split(" ") :
-      if elem == "m" or elem == "M" :
-         printVertices(type, vertices, width, level)
-         vertices = []
-         type = elem
-      elif elem == "z" :
-         vertices.append("Z")
-      elif elem == "c" or elem == "C" :
-         print "Curves not supported! Path ID '{}':\n{}".format(path.attrib["id"], path.attrib["d"])
-         sys.exit(0)
-      else :
-         tmp = elem.split(",")
-         px = float(tmp[0])
-         py = float(tmp[1])
-         vertices.append([px, py, 0])
-
-   printVertices(type, vertices, width, level)
-   if trans :
-      print "{}E\n".format("  "*level)
-
-
-def iterateDocument(elems, level) :
-   for elem in elems:
-      if elem.tag == "{http://www.w3.org/2000/svg}g" :
-         print "\n{}# GROUP".format("  "*level)
-         tmp = getTransform(elem)
-         if tmp :
-            print "{}G{}".format("  "*level, printVertex(getTransform(elem)))
-            iterateDocument(elem, level + 1)
-            print "{}E\n".format("  "*level)
-         else :
-            iterateDocument(elem, level)
-      elif elem.tag == "{http://www.w3.org/2000/svg}path" :
-         printPath(elem, level)
-
-
-# Ns. paaohjelma
-if len(sys.argv) != 2 :
-   print "Usage: "+sys.argv[0]+" <input.svg>"
-   sys.exit(1)
-   
-tree = ET.parse(sys.argv[1])
-iterateDocument(tree.getroot(), 0)
--- a/testpl.c	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,267 +0,0 @@
-#include "jss.h"
-#include "jssmod.h"
-#include "jssmix.h"
-#include "jssplr.h"
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-#include <SDL.h>
-
-
-static 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 printRow(FILE * f, JSSPattern * p, int row)
-{
-    int j, k;
-    char c;
-    JSSNote *n;
-
-    if (!p)
-        return;
-
-    n = &(p->data[p->nchannels * row]);
-
-    fprintf(f, "%.2x: ", row);
-    
-    k = p->nchannels < 5 ? p->nchannels : 5;
-
-    for (j = 0; j < k; j++)
-    {
-        switch (n->note)
-        {
-            case jsetNotSet:
-                fprintf(f, "... ");
-                break;
-            case jsetNoteOff:
-                fprintf(f, "=== ");
-                break;
-            default:
-                fprintf(f, "%s%i ", patNoteTable[n->note % 12], n->note / 12);
-                break;
-        }
-
-        if (n->instrument != jsetNotSet)
-            fprintf(f, "%.2x ", n->instrument + 1);
-        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++;
-    }
-}
-
-
-void audioCallback(void *userdata, Uint8 *stream, int len)
-{
-    JSSMixer *d = (JSSMixer *) userdata;
-
-    if (d != NULL)
-    {
-        jvmRenderAudio(d, stream, len / jvmGetSampleSize(d));
-    }
-}
-
-
-int main(int argc, char *argv[])
-{
-    SDL_AudioSpec afmt;
-    DMResource *file = NULL;
-    char *sname = NULL;
-    int result = -1;
-    JSSModule *mod = NULL;
-    JSSMixer *dev = NULL;
-    JSSPlayer *plr = NULL;
-
-    if (argc > 1)
-        sname = argv[1];
-
-    // Open the files
-    if (sname == NULL)
-        file = dmf_create_stdio_stream(stdin);
-    else if ((file = dmf_create_stdio(sname, "rb")) == NULL)
-    {
-        fprintf(stderr, "Error opening input file '%s'. (%s)\n",
-            sname, strerror(errno));
-        return 1;
-    }
-        
-    // Initialize miniJSS
-    fprintf(stderr, "Initializing miniJSS\n");
-    jssInit();
-
-    
-    // Read module file
-    fprintf(stderr, "Reading file: %s\n", sname);
-#ifdef JSS_SUP_XM
-    fprintf(stderr, "* Trying XM...\n");
-    result = jssLoadXM(file, &mod);
-#endif
-#ifdef JSS_SUP_JSSMOD
-    if (result != 0)
-    {
-        size_t bufgot, bufsize = dmfsize(file);
-        Uint8 *buf = dmMalloc(bufsize);
-        dmfseek(file, 0L, SEEK_SET);
-        fprintf(stderr, "* Trying JSSMOD (%d bytes, %p)...\n", bufsize, buf);
-        if ((bufgot = dmfread(buf, 1, bufsize, file)) != bufsize)
-        {
-            fprintf(stderr, "Error reading file (not enough data %d), #%d: %s\n",
-                bufgot, dmferror(file), dmErrorStr(dmferror(file)));
-            return 2;
-        }
-        result = jssLoadJSSMOD(buf, bufsize, &mod);
-        dmFree(buf);
-    }
-#endif
-    dmf_close(file);
-
-    if (result != DMERR_OK)
-    {
-        fprintf(stderr, "Error loading module file, %d: %s\n",
-            result, dmErrorStr(result));
-        return 3;
-    }
-
-    // Try to convert it
-    if ((result = jssConvertModuleForPlaying(mod)) != DMERR_OK)
-    {
-        fprintf(stderr, "Could not convert module for playing, %d: %s\n",
-            result, dmErrorStr(result));
-        return 3;
-    }
-
-    // Initialize SDL audio
-    afmt.freq     = 48000;
-    afmt.format   = AUDIO_S16SYS;
-    afmt.channels = 2;
-
-    // Initialize mixing device
-    fprintf(stderr, "Initializing miniJSS mixer with: %d, %d, %d\n",
-        JSS_AUDIO_S16, afmt.channels, afmt.freq);
-
-    dev = jvmInit(JSS_AUDIO_S16, afmt.channels, afmt.freq, JMIX_AUTO);
-    if (dev == NULL)
-    {
-        fprintf(stderr, "jvmInit() returned NULL\n");
-        return 3;
-    }
-    
-    afmt.samples  = afmt.freq / 4;
-    afmt.callback = audioCallback;
-    afmt.userdata = (void *) dev;
-
-    // Open the audio device
-    fprintf(stderr, "Trying to init SDL with: %d, %d, %d\n",
-        afmt.format, afmt.channels, afmt.freq);
-    
-    if (SDL_OpenAudio(&afmt, NULL) < 0)
-    {
-        fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
-        return 4;
-    }
-    
-    // Initialize player
-    if ((plr = jmpInit(dev)) == NULL)
-    {
-        fprintf(stderr, "jmpInit() returned NULL\n");
-        return 4;
-    }
-    
-    // Initialize playing
-    jvmSetCallback(dev, jmpExec, plr);
-    jmpSetModule(plr, mod);
-    jmpPlayOrder(plr, 0);
-    jvmSetGlobalVol(dev, 60);
-
-    // okay, main loop here ... "play" module and print out info
-    printf("----------------------------------------------------\n");
-    SDL_LockAudio();
-    SDL_PauseAudio(0);
-    SDL_UnlockAudio();
-    BOOL playing = TRUE;
-    while (playing)
-    {
-        JSSPattern *pattern;
-        int currRow, prevRow;
-
-        JSS_LOCK(plr);
-        currRow = prevRow = plr->row;
-        JSS_UNLOCK(plr);
-
-        while (currRow == prevRow && playing)
-        {
-            JSS_LOCK(plr);
-            currRow = plr->row;
-            playing = plr->isPlaying;
-            pattern = plr->pattern;
-            JSS_UNLOCK(plr);
-            SDL_Delay(50);
-        }
-
-        if (playing)
-        {
-            printRow(stdout, pattern, currRow);
-            printf("\n");
-        }
-    }
-    
-    printf("----------------------------------------------------\n");
-
-    SDL_LockAudio();
-    SDL_PauseAudio(1);
-    jmpClose(plr);
-    jvmClose(dev);
-    jssFreeModule(mod);
-    SDL_UnlockAudio();
-
-    SDL_Quit();
-
-    jssClose();
-
-    return 0;
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/blittest.c	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,458 @@
+#include "dmlib.h"
+#include "dmargs.h"
+#include "dmvecmat.h"
+#include "dmtext.h"
+#include <math.h>
+
+#define DM_COLORS (256)
+
+char *optFontFile = "font.ttf";
+BOOL optBenchmark = FALSE;
+int optVFlags = SDL_SWSURFACE | SDL_HWPALETTE;
+int optScrWidth = 640, optScrHeight = 480, optFontSize = 20, optScrDepth = 32;
+int optBenchmarkLen = 20;
+
+DMOptArg optList[] = {
+    { 0, '?', "help",       "Show this help", OPT_NONE },
+    { 2, 'v', "verbose",    "Be more verbose", OPT_NONE },
+    { 3, 'f', "full",       "Fullscreen", OPT_NONE },
+    { 4, 'h', "hw",         "Use SDL hardware surface", OPT_NONE },
+    { 5, 's', "size",       "Initial window size/resolution -s 640x480", OPT_ARGREQ },
+    { 6, 'd', "depth",      "Color depth of mode/window in bits (8/32)", OPT_ARGREQ },
+    { 7, 'b', "bench",      "Run in benchmark mode", OPT_NONE },
+};
+
+const int optListN = sizeof(optList) / sizeof(optList[0]);
+
+
+void argShowHelp()
+{
+    dmArgsPrintHelp(stdout, optList, optListN);
+}
+
+
+BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
+{
+    switch (optN) {
+    case 0:
+        argShowHelp();
+        exit(0);
+        break;
+
+    case 2:
+        dmVerbosity++;
+        break;
+    
+    case 3:
+        optVFlags |= SDL_FULLSCREEN;
+        break;
+
+    case 6:
+        if (optArg)
+            optScrDepth = atoi(optArg);
+        break;
+
+    case 5:
+        {
+            int w, h;
+            if (sscanf(optArg, "%dx%d", &w, &h) == 2)
+            {
+                if (w < 320 || h < 200 || w > 3200 || h > 3200)
+                {
+                    dmError("Invalid width or height: %d x %d\n", w, h);
+                    return FALSE;
+                }
+                optScrWidth = w;
+                optScrHeight = h;
+            }
+            else 
+            {
+                dmError("Invalid size argument '%s'.\n", optArg);
+                return FALSE;
+            }
+        }
+        break;
+
+    case 7:
+        optBenchmark = TRUE;
+        break;
+
+    default:
+        dmError("Unknown option '%s'.\n", currArg);
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+void DM_MakePalette(SDL_Surface *scr)
+{
+    SDL_Color pal[DM_COLORS];
+    int n;
+
+    for (n = 0; n < 256; n++)
+    {
+        pal[n].r = n;
+        pal[n].g = n;
+        pal[n].b = n;
+    }
+
+    SDL_SetColors(scr, pal, 0, DM_COLORS);
+}
+
+
+void DM_PrintRect(FILE *f, SDL_Rect *r)
+{
+    fprintf(f, "SDL_Rect <%d, %d : %d, %d>\n",
+        r->x, r->y, r->w, r->h);
+}
+
+BOOL DM_InitializeVideo(SDL_Surface **screen)
+{
+    *screen = SDL_SetVideoMode(optScrWidth, optScrHeight, optScrDepth, optVFlags | SDL_RESIZABLE);
+    if (*screen == NULL)
+    {
+        dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError());
+        return FALSE;
+    }
+
+#if 0
+    SDL_Rect r;
+    r.x = -50;
+    r.y = 50;
+    r.w = 700;
+    r.h = 300;
+    DM_PrintRect(stderr, &r);
+    SDL_SetClipRect(*screen, &r);
+    DM_PrintRect(stderr, &r);
+    DM_PrintRect(stderr, &((*screen)->clip_rect));
+#endif
+
+    return TRUE;
+}
+
+void DM_Random(SDL_Surface *screen, int q)
+{
+    Uint8 *pix = screen->pixels;
+    int xc, yc;
+    
+    for (yc = 0; yc < screen->h; yc++)
+    {
+        Uint8 *dp = pix;
+
+        for (xc = 0; xc < screen->w; xc++)
+            *dp++ = yc + (xc ^ q) + (yc & q);
+
+        pix += screen->pitch;
+    }
+}
+
+#define QWIDTH	256
+#define QHEIGHT	160
+
+typedef Uint8 DMBlockMap[QHEIGHT][QWIDTH];
+
+
+static DMFloat dmClip(DMFloat a)
+{
+    return (a < 0.0f ? 0.0f : (a > 1.0f ? 1.0f : a));
+}
+
+
+void dmMakeBumpMap(DMBlockMap map, DMFloat q, DMFloat m)
+{
+    int x, y;
+    for (y = 0; y < QHEIGHT; y++)
+        for (x = 0; x < QWIDTH; x++)
+        {
+            DMFloat f = 0.40f + dmPerlinNoise2D(x, y, 1.1f, q, 2);
+            map[y][x] = (int) (dmClip(f) * m);
+        }
+}
+
+
+void dmShadowTraceHeightMap(DMBlockMap lightMap, DMBlockMap pheightMap, DMVector *light)
+{
+    int i, j;
+
+    for (j = 0; j < QHEIGHT; j++)
+        for (i = 0; i < QWIDTH; i++)
+        {
+            DMVector vr, vl, va;
+            DMFloat vrayLen, vfactor;
+            int vlen;
+            BOOL wasHit;
+
+            // Perform shadow occlusion via simplistic raytracing
+            vr.x = i;
+            vr.y = j;
+            vr.z = light->z; // - 10.0;
+//            vr.z = pheightMap[j][i];
+            
+            // Calculate light vector vector
+            dm_vector_sub_r(&vl, &vr, light);
+            vrayLen = dm_vector_length(&vl);
+            
+#if 1
+            dm_vector_copy(&va, &vl);
+            dm_vector_normalize(&va);
+            dm_vector_scale(&va, 0.6f);
+            dm_vector_copy(&vr, light);
+
+            vlen = 0;
+            wasHit = FALSE;
+            do
+            {
+                float h;
+                
+                // If ray is inside the heightmap, get value
+                if (vr.x >= 0 && vr.y >= 0 && vr.x < QWIDTH && vr.y < QHEIGHT)
+                    h = pheightMap[(int) vr.y][(int) vr.x];
+                else
+                    break;
+                
+                // Check for hits
+                if (h > vr.z)
+                    wasHit = TRUE;
+                else
+                {
+                    // Move forwards
+                    dm_vector_add(&vr, &va);
+                    vlen++;
+                }
+            }
+            while (!wasHit && vlen <= vrayLen);
+
+            // Check if the ray hit something, e.g. is this point occluded?
+            if (wasHit && vlen < vrayLen)
+            {
+                vfactor = vlen * 0.01;
+            }
+            else
+                vfactor = vlen * 0.02;
+#endif
+
+#if 1
+            {
+                /* Calculate light's intensity based on the angle it "hits"
+                 *
+                 * 1) Calculate the vectors that form the imaginary "plane"
+                 * 2) Cross-product -> normal vector of the plane
+                 * 2) Normalize the normal vector
+                 * 3) Calculate light vector's hit angle by dot product
+                 */
+                DMVector v1, v2;
+                DMFloat c;
+
+                v1.x = 2.0f;
+                v1.y = 0.0f;
+                v1.z = (DMFloat) (pheightMap[j][i] - pheightMap[j][i + 1]);
+
+                v2.x = 0.0f;
+                v2.y = 2.0f;
+                v2.z = (DMFloat) (pheightMap[j][i] - pheightMap[j + 1][i]);
+
+                dm_vector_cross(&vr, &v1, &v2);
+                dm_vector_normalize(&vr);
+                dm_vector_normalize(&vl);
+                c = dm_vector_dot(&vl, &vr);
+
+		vrayLen = 255 - (vrayLen * 0.1) * vrayLen + (c * 128.0f) + (vfactor * vfactor * 1255);
+            }
+#else
+            vrayLen = 255 - vrayLen * vrayLen * (vfactor * vfactor);
+            if (vrayLen < 0) vrayLen = 0;
+            vrayLen += pheightMap[j][i];
+#endif
+
+            // Clip result
+            if (vrayLen < 0)
+                vrayLen = 0;
+            else if (vrayLen > 255.0f)
+                vrayLen = 255.0f;
+
+            lightMap[j][i] = vrayLen;
+        }
+}
+
+
+int main(int argc, char *argv[])
+{
+    SDL_Surface *screen = NULL, *bmap = NULL;
+    TTF_Font *font = NULL;
+    SDL_Color fontcol={255,155,155,0};
+    SDL_Event event;
+    int mouseX, mouseY;
+    BOOL initSDL = FALSE, initTTF = FALSE, exitFlag, showMap = FALSE;
+
+    dmInitProg("blittest", "dmlib blittest", "0.2", NULL, NULL);
+    if (!dmArgsProcess(argc, argv, optList, optListN,
+        argHandleOpt, NULL, FALSE))
+        exit(1);
+
+    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
+    {
+        dmError("Could not initialize SDL: %s\n", SDL_GetError());
+        goto error_exit;
+    }
+    initSDL = TRUE;
+
+
+    if (TTF_Init() < 0)
+    {
+        dmError("Could not initialize FreeType/TTF: %s\n", SDL_GetError());
+        goto error_exit;
+    }
+    initTTF = TRUE;
+
+    font = TTF_OpenFont(optFontFile, optFontSize);
+    if (font == NULL)
+    {
+        dmError("Could not load TTF font '%s' (%d): %s\n",
+            optFontFile, optFontSize, SDL_GetError());
+        goto error_exit;
+    }
+    TTF_SetFontStyle(font, TTF_STYLE_NORMAL);
+
+    if (optBenchmark)
+    {
+        screen = SDL_CreateRGBSurface(SDL_SWSURFACE, optScrWidth, optScrHeight, optScrDepth, 0, 0, 0, 0);
+        if (screen == NULL)
+        {
+            dmError("Could not create screen surface.\n");
+            goto error_exit;
+        }
+        
+        dmMsg(0, "Benchmark mode, not opening window.\n");
+    }
+    else
+    {
+        if (!DM_InitializeVideo(&screen))
+            goto error_exit;
+
+        SDL_WM_SetCaption("Halleluja", "DMT");
+    }
+
+    dmPerlinInit();
+
+    bmap = SDL_CreateRGBSurface(SDL_SWSURFACE, QWIDTH, QHEIGHT, 8, 0, 0, 0, 0);
+    DM_MakePalette(bmap);
+    DM_Random(bmap, 15);
+    
+    DMVector light;
+    DMBlockMap heightMap;
+    light.x = light.y = 128;
+    light.z = 128;
+    dmMakeBumpMap(heightMap, 0.06, 254);
+
+
+
+    int numFrames = 0, startTime = SDL_GetTicks(), endTime = 0;
+    exitFlag = FALSE;
+
+    if (optBenchmark)
+        dmMsg(0, "Starting benchmark, running for %d seconds.\n", optBenchmarkLen);
+
+    while (!exitFlag)
+    {
+        dmMakeBumpMap(heightMap, 0.05 + sin(SDL_GetTicks() * 0.001) * 0.002 , 254);
+        if (!optBenchmark)
+        {
+            while (SDL_PollEvent(&event))
+            switch (event.type)
+            {
+                case SDL_KEYDOWN:
+                    switch (event.key.keysym.sym)
+                    {
+                        case SDLK_ESCAPE: exitFlag = TRUE; break;
+                        
+                        case SDLK_F5:
+                            showMap = !showMap;
+                            break;
+                            
+                        default:
+                            break;
+                    }
+
+                    break;
+                
+                case SDL_VIDEORESIZE:
+                    optScrWidth = event.resize.w;
+                    optScrHeight = event.resize.h;
+
+                    if (!DM_InitializeVideo(&screen))
+                        goto error_exit;
+
+                    break;
+                
+                case SDL_VIDEOEXPOSE:
+                    break;
+
+                case SDL_QUIT:
+                    exit(0);
+            }
+
+            SDL_GetMouseState(&mouseX, &mouseY);
+            light.x = ((DMFloat) mouseX * QWIDTH) / (DMFloat) optScrWidth;
+            light.y = ((DMFloat) mouseY * QHEIGHT) / (DMFloat) optScrHeight;
+        }
+
+        if (!optBenchmark && SDL_MUSTLOCK(screen) != 0 && SDL_LockSurface(screen) != 0)
+        {
+            dmError("Can't lock surface.\n");
+            goto error_exit;
+        }
+
+
+        if (showMap)
+            memcpy(bmap->pixels, heightMap, QWIDTH * QHEIGHT);
+        else
+            dmShadowTraceHeightMap(bmap->pixels, heightMap, &light);
+        
+        dmScaledBlitSurfaceAny(bmap, 0, 0, screen->w, screen->h, screen, DMD_NONE);
+
+        if (!optBenchmark)
+        {
+            dmDrawTTFText(screen, font, fontcol, 0, 0, "%3.1f FPS", 
+                (float) (numFrames * 1000.0f) / (float) (endTime - startTime));
+
+            if (SDL_MUSTLOCK(screen) != 0)
+                SDL_UnlockSurface(screen);
+
+            SDL_Flip(screen);
+            SDL_Delay(10);
+        }
+
+        endTime = SDL_GetTicks();
+        numFrames++;
+
+        if (optBenchmark)
+        {
+            if (endTime - startTime > optBenchmarkLen * 1000)
+                exitFlag = TRUE;
+        }
+    }
+
+    // Print benchmark results
+    dmMsg(0, "%d frames in %d ms, fps = %1.3f\n",
+        numFrames, endTime - startTime,
+        (float) (numFrames * 1000.0f) / (float) (endTime - startTime));
+
+
+error_exit:
+    dmMsg(0, "Shutting down dmlib.\n");
+    if (screen)
+        SDL_FreeSurface(screen);
+
+    if (font)
+        TTF_CloseFont(font);
+
+    if (initSDL)
+        SDL_Quit();
+
+    if (initTTF)
+        TTF_Quit();
+
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/efu.c	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,478 @@
+#include "dmlib.h"
+#include "dmargs.h"
+#include "dmvecmat.h"
+#include "dmimage.h"
+#include "dmtext.h"
+#include <math.h>
+
+#define DM_COLORS (256)
+
+char *optFontFile = "font.ttf",
+     *optBitmapFilename = "tnsp.png";
+BOOL optBenchmark = FALSE;
+int optVFlags = SDL_SWSURFACE | SDL_HWPALETTE;
+int optScrWidth = 640, optScrHeight = 480, optFontSize = 20, optScrDepth = 32;
+int optBenchmarkLen = 20;
+
+DMOptArg optList[] = {
+    { 0, '?', "help",       "Show this help", OPT_NONE },
+    { 2, 'v', "verbose",    "Be more verbose", OPT_NONE },
+    { 3, 'f', "full",       "Fullscreen", OPT_NONE },
+    { 4, 'h', "hw",         "Use SDL hardware surface", OPT_NONE },
+    { 5, 's', "size",       "Initial window size/resolution -s 640x480", OPT_ARGREQ },
+    { 6, 'd', "depth",      "Color depth of mode/window in bits (8/15/16/32)", OPT_ARGREQ },
+    { 7, 'b', "bench",      "Run in benchmark mode", OPT_NONE },
+};
+
+const int optListN = sizeof(optList) / sizeof(optList[0]);
+
+
+void argShowHelp()
+{
+    dmArgsPrintHelp(stdout, optList, optListN);
+}
+
+
+BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
+{
+    switch (optN) {
+    case 0:
+        argShowHelp();
+        exit(0);
+        break;
+
+    case 2:
+        dmVerbosity++;
+        break;
+    
+    case 3:
+        optVFlags |= SDL_FULLSCREEN;
+        break;
+
+    case 6:
+        if (optArg)
+            optScrDepth = atoi(optArg);
+        break;
+
+    case 5:
+        {
+            int w, h;
+            if (sscanf(optArg, "%dx%d", &w, &h) == 2)
+            {
+                if (w < 320 || h < 200 || w > 3200 || h > 3200)
+                {
+                    dmError("Invalid width or height: %d x %d\n", w, h);
+                    return FALSE;
+                }
+                optScrWidth = w;
+                optScrHeight = h;
+            }
+            else 
+            {
+                dmError("Invalid size argument '%s'.\n", optArg);
+                return FALSE;
+            }
+        }
+        break;
+
+    case 7:
+        optBenchmark = TRUE;
+        break;
+
+    default:
+        dmError("Unknown option '%s'.\n", currArg);
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+void DM_MakePalette(SDL_Surface *scr)
+{
+    SDL_Color pal[DM_COLORS];
+    int n;
+
+    for (n = 0; n < 256; n++)
+    {
+        pal[n].r = n;
+        pal[n].g = n;
+        pal[n].b = n;
+    }
+
+    SDL_SetColors(scr, pal, 0, DM_COLORS);
+}
+
+
+void DM_PrintRect(FILE *f, SDL_Rect *r)
+{
+    fprintf(f, "SDL_Rect <%d, %d : %d, %d>\n",
+        r->x, r->y, r->w, r->h);
+}
+
+BOOL DM_InitializeVideo(SDL_Surface **screen)
+{
+    *screen = SDL_SetVideoMode(optScrWidth, optScrHeight, optScrDepth, optVFlags | SDL_RESIZABLE);
+    if (*screen == NULL)
+    {
+        dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError());
+        return FALSE;
+    }
+
+#if 0
+    SDL_Rect r;
+    r.x = -50;
+    r.y = 50;
+    r.w = 700;
+    r.h = 300;
+    DM_PrintRect(stderr, &r);
+    SDL_SetClipRect(*screen, &r);
+    DM_PrintRect(stderr, &r);
+    DM_PrintRect(stderr, &((*screen)->clip_rect));
+#endif
+
+    return TRUE;
+}
+
+void DM_Random(SDL_Surface *screen, int q)
+{
+    Uint8 *pix = screen->pixels;
+    int xc, yc;
+    
+    for (yc = 0; yc < screen->h; yc++)
+    {
+        Uint8 *dp = pix;
+
+        for (xc = 0; xc < screen->w; xc++)
+            *dp++ = yc + (xc ^ q) + (yc & q);
+
+        pix += screen->pitch;
+    }
+}
+
+
+#define QWIDTH	256
+#define QHEIGHT	160
+
+typedef Uint8 DMBlockMap[QHEIGHT][QWIDTH];
+
+
+void dmMakeBumpMap(DMBlockMap map, DMFloat q, DMFloat m)
+{
+    int x, y;
+    for (y = 0; y < QHEIGHT; y++)
+        for (x = 0; x < QWIDTH; x++)
+        {
+            DMFloat f = 0.40f + dmPerlinNoise2D(x, y, 1.1f, q, 2);
+            map[y][x] = (int) (dmClamp10(f) * m);
+        }
+}
+
+
+void dmShadowTraceHeightMap(DMBlockMap lightMap, DMBlockMap pheightMap, DMVector *light)
+{
+    int i, j;
+
+    light->z = 150;
+
+    for (j = 0; j < QHEIGHT; j++)
+        for (i = 0; i < QWIDTH; i++)
+        {
+            DMVector vr, vl, va;
+            DMFloat vrayLen, vfactor;
+            int vlen;
+            BOOL wasHit;
+
+            // Perform shadow occlusion via simplistic raytracing
+            vr.x = i;
+            vr.y = j;
+            vr.z = 200; //light->z; // - 10.0;
+            
+            // Calculate light vector vector
+            dm_vector_sub_r(&vl, &vr, light);
+            vrayLen = dm_vector_length(&vl);
+            
+#if 1
+            dm_vector_copy(&va, &vl);
+            dm_vector_normalize(&va);
+            dm_vector_copy(&vr, light);
+
+            vlen = 0;
+            wasHit = FALSE;
+            do
+            {
+                float h;
+                
+                // If ray is inside the heightmap, get value
+                if (vr.x >= 0 && vr.y >= 0 && vr.x < QWIDTH && vr.y < QHEIGHT)
+                    h = pheightMap[(int) vr.y][(int) vr.x];
+                else
+                    break;
+                
+                // Check for hits
+                if (h > vr.z)
+                    wasHit = TRUE;
+                else
+                {
+                    // Move forwards
+                    dm_vector_add(&vr, &va);
+                    vlen++;
+                }
+            }
+            while (!wasHit && vlen <= vrayLen);
+
+            // Check if the ray hit something, e.g. is this point occluded?
+            if (wasHit && vlen < vrayLen)
+            {
+                vfactor = vlen * 0.05;
+            }
+            else
+                vfactor = vlen * 0.001;
+#endif
+
+#if 0
+            {
+                /* Calculate light's intensity based on the angle it "hits"
+                 *
+                 * 1) Calculate the vectors that form the imaginary "plane"
+                 * 2) Cross-product -> normal vector of the plane
+                 * 2) Normalize the normal vector
+                 * 3) Calculate light vector's hit angle by dot product
+                 */
+                DMVector v1, v2;
+                DMFloat c;
+
+                v1.x = 2.0f;
+                v1.y = 0.0f;
+                v1.z = (DMFloat) (pheightMap[j][i] - pheightMap[j][i + 1]);
+
+                v2.x = 0.0f;
+                v2.y = 2.0f;
+                v2.z = (DMFloat) (pheightMap[j][i] - pheightMap[j + 1][i]);
+
+                dm_vector_cross(&vr, &v1, &v2);
+                dm_vector_normalize(&vr);
+                dm_vector_normalize(&vl);
+                c = dm_vector_dot(&vl, &vr);
+
+		vrayLen = 255 - (vrayLen * 0.1) * vrayLen + (c * 128.0f) + (vfactor * vfactor * 1255);
+            }
+#else
+            vrayLen = 255 - vrayLen * vrayLen * (vfactor * vfactor);
+            if (vrayLen < 0) vrayLen = 0;
+            vrayLen -= pheightMap[j][i];
+#endif
+
+            // Clip result
+            if (vrayLen < 0)
+                vrayLen = 0;
+            else if (vrayLen > 255.0f)
+                vrayLen = 255.0f;
+
+            lightMap[j][i] = vrayLen;
+        }
+}
+
+
+int main(int argc, char *argv[])
+{
+    SDL_Surface *screen = NULL, *bmap = NULL, *logo = NULL;
+    TTF_Font *font = NULL;
+    SDL_Color fontcol={255,155,155,0};
+    SDL_Event event;
+    int mouseX, mouseY;
+    BOOL initSDL = FALSE, initTTF = FALSE, exitFlag, showMap = FALSE;
+    
+    if (!dmArgsProcess(argc, argv, optList, optListN,
+        argHandleOpt, NULL, FALSE))
+        exit(1);
+
+    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
+    {
+        dmError("Could not initialize SDL: %s\n", SDL_GetError());
+        goto error_exit;
+    }
+    initSDL = TRUE;
+
+
+    if (TTF_Init() < 0)
+    {
+        dmError("Could not initialize FreeType/TTF: %s\n", SDL_GetError());
+        goto error_exit;
+    }
+    initTTF = TRUE;
+
+    font = TTF_OpenFont(optFontFile, optFontSize);
+    if (font == NULL)
+    {
+        dmError("Could not load TTF font '%s' (%d): %s\n",
+            optFontFile, optFontSize, SDL_GetError());
+        goto error_exit;
+    }
+    TTF_SetFontStyle(font, TTF_STYLE_NORMAL);
+
+
+    DMResource *res = dmf_create_stdio(optBitmapFilename, "rb");
+    if (res == NULL)
+    {
+        dmError("Could not open resource file '%s'.\n", optBitmapFilename);
+        goto error_exit;
+    }
+    logo = dmLoadImage(res);
+    dmf_close(res);
+    if (logo == NULL)
+    {
+        dmError("Could not load image file '%s'.\n", optBitmapFilename);
+        goto error_exit;
+    }
+
+
+    if (optBenchmark)
+    {
+        screen = SDL_CreateRGBSurface(SDL_SWSURFACE, optScrWidth, optScrHeight, optScrDepth, 0, 0, 0, 0);
+        if (screen == NULL)
+        {
+            dmError("Could not create screen surface.\n");
+            goto error_exit;
+        }
+        
+        dmMsg(0, "Benchmark mode, not opening window.\n");
+    }
+    else
+    {
+        if (!DM_InitializeVideo(&screen))
+            goto error_exit;
+
+        SDL_WM_SetCaption("Halleluja", "DMT");
+    }
+
+    dmPerlinInit();
+
+    bmap = SDL_CreateRGBSurface(SDL_SWSURFACE, QWIDTH, QHEIGHT, 8, 0, 0, 0, 0);
+    DM_MakePalette(bmap);
+    DM_Random(bmap, 15);
+    
+    DMVector light;
+    DMBlockMap heightMap;
+    light.x = light.y = 128;
+    light.z = 128;
+    dmMakeBumpMap(heightMap, 0.06, 254);
+
+
+
+    int numFrames = 0, startTime = SDL_GetTicks(), endTime = 0;
+    exitFlag = FALSE;
+
+    if (optBenchmark)
+        dmMsg(0, "Starting benchmark, running for %d seconds.\n", optBenchmarkLen);
+
+    while (!exitFlag)
+    {
+        if (!optBenchmark)
+        {
+            while (SDL_PollEvent(&event))
+            switch (event.type)
+            {
+                case SDL_KEYDOWN:
+                    switch (event.key.keysym.sym)
+                    {
+                        case SDLK_ESCAPE: exitFlag = TRUE; break;
+                        
+                        case SDLK_F1:
+                            DM_Random(bmap, (SDL_GetTicks() / 10) % 1000);
+                            break;
+
+                        case SDLK_F5:
+                            showMap = !showMap;
+                            break;
+                            
+                        default:
+                            break;
+                    }
+
+                    break;
+                
+                case SDL_VIDEORESIZE:
+                    optScrWidth = event.resize.w;
+                    optScrHeight = event.resize.h;
+
+                    if (!DM_InitializeVideo(&screen))
+                        goto error_exit;
+
+                    break;
+                
+                case SDL_VIDEOEXPOSE:
+                    break;
+
+                case SDL_QUIT:
+                    exit(0);
+            }
+
+            SDL_GetMouseState(&mouseX, &mouseY);
+            light.x = ((DMFloat) mouseX * QWIDTH) / (DMFloat) optScrWidth;
+            light.y = ((DMFloat) mouseY * QHEIGHT) / (DMFloat) optScrHeight;
+        }
+
+        if (!optBenchmark && SDL_MUSTLOCK(screen) != 0 && SDL_LockSurface(screen) != 0)
+        {
+            dmError("Can't lock surface.\n");
+            goto error_exit;
+        }
+
+
+        if (showMap)
+            memcpy(bmap->pixels, heightMap, QWIDTH * QHEIGHT);
+        else
+            dmShadowTraceHeightMap(bmap->pixels, logo->pixels, &light);
+        
+        dmScaledBlitSurfaceAny(bmap, 0, 0, screen->w, screen->h, screen, DMD_NONE);
+
+
+
+        if (!optBenchmark)
+        {
+            dmDrawTTFText(screen, font, fontcol, 0, 0, "%3.1f FPS", 
+                (float) (numFrames * 1000.0f) / (float) (endTime - startTime));
+
+            if (SDL_MUSTLOCK(screen) != 0)
+                SDL_UnlockSurface(screen);
+
+            SDL_Flip(screen);
+            SDL_Delay(20);
+        }
+
+        endTime = SDL_GetTicks();
+        numFrames++;
+
+        if (optBenchmark)
+        {
+            if (endTime - startTime > optBenchmarkLen * 1000)
+                exitFlag = TRUE;
+        }
+    }
+
+    // Print benchmark results
+    dmMsg(0, "%d frames in %d ms, fps = %1.3f\n",
+        numFrames, endTime - startTime,
+        (float) (numFrames * 1000.0f) / (float) (endTime - startTime));
+
+
+error_exit:
+    dmMsg(0, "Shutting down dmlib.\n");
+    if (screen)
+        SDL_FreeSurface(screen);
+
+    if (logo)
+        SDL_FreeSurface(logo);
+
+    if (font)
+        TTF_CloseFont(font);
+
+    if (initSDL)
+        SDL_Quit();
+
+    if (initTTF)
+        TTF_Quit();
+
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/testpl.c	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,267 @@
+#include "jss.h"
+#include "jssmod.h"
+#include "jssmix.h"
+#include "jssplr.h"
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <SDL.h>
+
+
+static 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 printRow(FILE * f, JSSPattern * p, int row)
+{
+    int j, k;
+    char c;
+    JSSNote *n;
+
+    if (!p)
+        return;
+
+    n = &(p->data[p->nchannels * row]);
+
+    fprintf(f, "%.2x: ", row);
+    
+    k = p->nchannels < 5 ? p->nchannels : 5;
+
+    for (j = 0; j < k; j++)
+    {
+        switch (n->note)
+        {
+            case jsetNotSet:
+                fprintf(f, "... ");
+                break;
+            case jsetNoteOff:
+                fprintf(f, "=== ");
+                break;
+            default:
+                fprintf(f, "%s%i ", patNoteTable[n->note % 12], n->note / 12);
+                break;
+        }
+
+        if (n->instrument != jsetNotSet)
+            fprintf(f, "%.2x ", n->instrument + 1);
+        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++;
+    }
+}
+
+
+void audioCallback(void *userdata, Uint8 *stream, int len)
+{
+    JSSMixer *d = (JSSMixer *) userdata;
+
+    if (d != NULL)
+    {
+        jvmRenderAudio(d, stream, len / jvmGetSampleSize(d));
+    }
+}
+
+
+int main(int argc, char *argv[])
+{
+    SDL_AudioSpec afmt;
+    DMResource *file = NULL;
+    char *sname = NULL;
+    int result = -1;
+    JSSModule *mod = NULL;
+    JSSMixer *dev = NULL;
+    JSSPlayer *plr = NULL;
+
+    if (argc > 1)
+        sname = argv[1];
+
+    // Open the files
+    if (sname == NULL)
+        file = dmf_create_stdio_stream(stdin);
+    else if ((file = dmf_create_stdio(sname, "rb")) == NULL)
+    {
+        fprintf(stderr, "Error opening input file '%s'. (%s)\n",
+            sname, strerror(errno));
+        return 1;
+    }
+        
+    // Initialize miniJSS
+    fprintf(stderr, "Initializing miniJSS\n");
+    jssInit();
+
+    
+    // Read module file
+    fprintf(stderr, "Reading file: %s\n", sname);
+#ifdef JSS_SUP_XM
+    fprintf(stderr, "* Trying XM...\n");
+    result = jssLoadXM(file, &mod);
+#endif
+#ifdef JSS_SUP_JSSMOD
+    if (result != 0)
+    {
+        size_t bufgot, bufsize = dmfsize(file);
+        Uint8 *buf = dmMalloc(bufsize);
+        dmfseek(file, 0L, SEEK_SET);
+        fprintf(stderr, "* Trying JSSMOD (%d bytes, %p)...\n", bufsize, buf);
+        if ((bufgot = dmfread(buf, 1, bufsize, file)) != bufsize)
+        {
+            fprintf(stderr, "Error reading file (not enough data %d), #%d: %s\n",
+                bufgot, dmferror(file), dmErrorStr(dmferror(file)));
+            return 2;
+        }
+        result = jssLoadJSSMOD(buf, bufsize, &mod);
+        dmFree(buf);
+    }
+#endif
+    dmf_close(file);
+
+    if (result != DMERR_OK)
+    {
+        fprintf(stderr, "Error loading module file, %d: %s\n",
+            result, dmErrorStr(result));
+        return 3;
+    }
+
+    // Try to convert it
+    if ((result = jssConvertModuleForPlaying(mod)) != DMERR_OK)
+    {
+        fprintf(stderr, "Could not convert module for playing, %d: %s\n",
+            result, dmErrorStr(result));
+        return 3;
+    }
+
+    // Initialize SDL audio
+    afmt.freq     = 48000;
+    afmt.format   = AUDIO_S16SYS;
+    afmt.channels = 2;
+
+    // Initialize mixing device
+    fprintf(stderr, "Initializing miniJSS mixer with: %d, %d, %d\n",
+        JSS_AUDIO_S16, afmt.channels, afmt.freq);
+
+    dev = jvmInit(JSS_AUDIO_S16, afmt.channels, afmt.freq, JMIX_AUTO);
+    if (dev == NULL)
+    {
+        fprintf(stderr, "jvmInit() returned NULL\n");
+        return 3;
+    }
+    
+    afmt.samples  = afmt.freq / 4;
+    afmt.callback = audioCallback;
+    afmt.userdata = (void *) dev;
+
+    // Open the audio device
+    fprintf(stderr, "Trying to init SDL with: %d, %d, %d\n",
+        afmt.format, afmt.channels, afmt.freq);
+    
+    if (SDL_OpenAudio(&afmt, NULL) < 0)
+    {
+        fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
+        return 4;
+    }
+    
+    // Initialize player
+    if ((plr = jmpInit(dev)) == NULL)
+    {
+        fprintf(stderr, "jmpInit() returned NULL\n");
+        return 4;
+    }
+    
+    // Initialize playing
+    jvmSetCallback(dev, jmpExec, plr);
+    jmpSetModule(plr, mod);
+    jmpPlayOrder(plr, 0);
+    jvmSetGlobalVol(dev, 60);
+
+    // okay, main loop here ... "play" module and print out info
+    printf("----------------------------------------------------\n");
+    SDL_LockAudio();
+    SDL_PauseAudio(0);
+    SDL_UnlockAudio();
+    BOOL playing = TRUE;
+    while (playing)
+    {
+        JSSPattern *pattern;
+        int currRow, prevRow;
+
+        JSS_LOCK(plr);
+        currRow = prevRow = plr->row;
+        JSS_UNLOCK(plr);
+
+        while (currRow == prevRow && playing)
+        {
+            JSS_LOCK(plr);
+            currRow = plr->row;
+            playing = plr->isPlaying;
+            pattern = plr->pattern;
+            JSS_UNLOCK(plr);
+            SDL_Delay(50);
+        }
+
+        if (playing)
+        {
+            printRow(stdout, pattern, currRow);
+            printf("\n");
+        }
+    }
+    
+    printf("----------------------------------------------------\n");
+
+    SDL_LockAudio();
+    SDL_PauseAudio(1);
+    jmpClose(plr);
+    jvmClose(dev);
+    jssFreeModule(mod);
+    SDL_UnlockAudio();
+
+    SDL_Quit();
+
+    jssClose();
+
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/vecmattest.c	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,85 @@
+#include "dmlib.h"
+#include "dmvecmat.h"
+#include "dmmutex.h"
+
+void printTest(const char *test, int expected, int result)
+{
+    fprintf(stderr, "Test '%s': %s\n", test,
+        expected == result ? "OK" : "FAILED!");
+}
+
+#define tst(X, R) printTest(# X, (X), (R))
+
+
+void dm_vector_fprintf(FILE *f, const char *name, DMVector *v)
+{
+    if (name != NULL)
+        fprintf(f, "%s=", name);
+
+    fprintf(f, "[<%1.3f, %1.3f, %1.3f>=%1.3f]", v->x, v->y, v->z, dm_vector_length(v));
+
+    if (name != NULL)
+        fprintf(f, "\n");
+}
+
+
+void dm_vector_printf(const char *name, DMVector *v)
+{
+    dm_vector_fprintf(stdout, name, v);
+}
+
+
+void dm_matrix_fprintf(FILE *f, const char *name, DMMatrix *mat)
+{
+    int i, j, k, pad = 0;
+    char *tmp = NULL;
+
+    if (name != NULL)
+    {
+        tmp = dm_strdup_printf("%s=", name);
+        pad = strlen(tmp);
+    }
+
+    for (i = 0; i < DM_MATRIX_SIZE; i++)
+    {
+        if (i == 1)
+            fputs(tmp, f);
+        else
+            for (k = 0; k < pad; k++)
+                fputc(' ', f);
+
+        fprintf(f, "[");
+        for (j = 0; j < DM_MATRIX_SIZE; j++)
+            fprintf(f, "% 8.3f%s", mat->m[i][j], j < DM_MATRIX_SIZE - 1 ? " " : "");
+        
+        fprintf(f, "]\n");
+    }
+}
+
+
+void dm_matrix_printf(const char *name, DMMatrix *mat)
+{
+    dm_matrix_fprintf(stdout, name, mat);
+}
+
+
+int main(int argc, char *argv[])
+{
+    DMVector a = { -5, 1, 17, 0 }, b = { 1, 2, 0.5, 0 };
+    DMMatrix m;
+
+    (void) argc;
+    (void) argv;    
+
+    dm_vector_printf("a", &a);
+    dm_vector_printf("b", &a);
+    
+    dm_matrix_rot_a(&m, 0.5, 0.9, 0.1);
+    dm_matrix_printf("m", &m);
+
+    dm_vector_mul_by_mat(&b, &a, &m);
+    
+    dm_vector_printf("nb", &b);
+    
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/vptest.c	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,394 @@
+#include "dmlib.h"
+#include "dmargs.h"
+#include "dmres.h"
+#include "dmimage.h"
+#include "dmtext.h"
+#include "dmq3d.h"
+#include "dmvecmat.h"
+#include <math.h>
+
+#define DM_COLORS (256)
+
+char *optFontFile = "font.ttf",
+     *optBitmapFilename = "blurri.png";
+BOOL optBenchmark = FALSE;
+int optVFlags = SDL_SWSURFACE | SDL_HWPALETTE;
+int optScrWidth = 640, optScrHeight = 480, optFontSize = 8, optScrDepth = 32;
+int optBenchmarkLen = 20;
+
+DMOptArg optList[] = {
+    { 0, '?', "help",       "Show this help", OPT_NONE },
+    { 2, 'v', "verbose",    "Be more verbose", OPT_NONE },
+    { 3, 'f', "full",       "Fullscreen", OPT_NONE },
+    { 4, 'h', "hw",         "Use SDL hardware surface", OPT_NONE },
+    { 5, 's', "size",       "Initial window size/resolution -s 640x480", OPT_ARGREQ },
+    { 6, 'd', "depth",      "Color depth of mode/window in bits (8/15/16/32)", OPT_ARGREQ },
+    { 7, 'b', "bench",      "Run in benchmark mode", OPT_NONE },
+};
+
+const int optListN = sizeof(optList) / sizeof(optList[0]);
+
+
+void argShowHelp()
+{
+    dmArgsPrintHelp(stdout, optList, optListN);
+}
+
+
+BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
+{
+    switch (optN) {
+    case 0:
+        argShowHelp();
+        exit(0);
+        break;
+
+    case 2:
+        dmVerbosity++;
+        break;
+    
+    case 3:
+        optVFlags |= SDL_FULLSCREEN;
+        break;
+
+    case 6:
+        if (optArg)
+            optScrDepth = atoi(optArg);
+        break;
+
+    case 5:
+        {
+            int w, h;
+            if (sscanf(optArg, "%dx%d", &w, &h) == 2)
+            {
+                if (w < 320 || h < 200 || w > 3200 || h > 3200)
+                {
+                    dmError("Invalid width or height: %d x %d\n", w, h);
+                    return FALSE;
+                }
+                optScrWidth = w;
+                optScrHeight = h;
+            }
+            else 
+            {
+                dmError("Invalid size argument '%s'.\n", optArg);
+                return FALSE;
+            }
+        }
+        break;
+
+    case 7:
+        optBenchmark = TRUE;
+        break;
+
+    default:
+        dmError("Unknown option '%s'.\n", currArg);
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+void DM_PrintRect(FILE *f, SDL_Rect *r)
+{
+    fprintf(f, "SDL_Rect <%d, %d : %d, %d>\n",
+        r->x, r->y, r->w, r->h);
+}
+
+
+BOOL DM_InitializeVideo(SDL_Surface **screen)
+{
+    *screen = SDL_SetVideoMode(optScrWidth, optScrHeight, optScrDepth, optVFlags | SDL_RESIZABLE);
+    if (*screen == NULL)
+    {
+        dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError());
+        return FALSE;
+    }
+    return TRUE;
+}
+
+
+int main(int argc, char *argv[])
+{
+    SDL_Surface *screen = NULL, *bmap = NULL, *fbmap = NULL, *screen2 = NULL;
+    SDL_Color fontcol = { 255, 155, 155, 0 };
+    SDL_Event event;
+    TTF_Font *font = NULL;
+    int mouseX, mouseY, bx, by, err;
+    BOOL initSDL = FALSE, initTTF = FALSE, exitFlag;
+    DM3DVectorSpriteModel *model, *model2;
+    
+    dmVerbosity = 5;
+    if (!dmArgsProcess(argc, argv, optList, optListN,
+        argHandleOpt, NULL, FALSE))
+        exit(1);
+
+    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
+    {
+        dmError("Could not initialize SDL: %s\n", SDL_GetError());
+        goto error_exit;
+    }
+    initSDL = TRUE;
+
+
+    if (TTF_Init() < 0)
+    {
+        dmError("Could not initialize FreeType/TTF: %s\n", SDL_GetError());
+        goto error_exit;
+    }
+    initTTF = TRUE;
+
+    font = TTF_OpenFont(optFontFile, optFontSize);
+    if (font == NULL)
+    {
+        dmError("Could not load TTF font '%s' (%d): %s\n",
+            optFontFile, optFontSize, SDL_GetError());
+        goto error_exit;
+    }
+    TTF_SetFontStyle(font, TTF_STYLE_NORMAL);
+
+    DMResource *res = dmf_create_stdio(optBitmapFilename, "rb");
+    if (res == NULL)
+    {
+        dmError("Could not open resource file '%s'.\n", optBitmapFilename);
+        goto error_exit;
+    }
+    bmap = dmLoadImage(res);
+    dmf_close(res);
+    if (bmap == NULL)
+    {
+        dmError("Could not load image file '%s'.\n", optBitmapFilename);
+        goto error_exit;
+    }
+
+    res = dmf_create_stdio("trans6x6.png", "rb");
+    if (res == NULL)
+    {
+        dmError("Could not open resource file '%s'.\n", optBitmapFilename);
+        goto error_exit;
+    }
+    fbmap = dmLoadImage(res);
+    dmf_close(res);
+    if (fbmap == NULL)
+    {
+        dmError("Could not load image file '%s'.\n", optBitmapFilename);
+        goto error_exit;
+    }
+    
+    
+    res = dmf_create_stdio("mole.3d", "r");
+    if (res == NULL)
+    {
+        dmError("Could not open resource file '%s'.\n", optBitmapFilename);
+        goto error_exit;
+    }
+    err = dmRead3DVectorSpriteModel(res, &model);
+    dmf_close(res);
+    dmMsg(0, "Loaded, %d: %s\n", err, dmErrorStr(err));
+
+    res = dmf_create_stdio("roto.3d", "r");
+    if (res == NULL)
+    {
+        dmError("Could not open resource file '%s'.\n", optBitmapFilename);
+        goto error_exit;
+    }
+    err = dmRead3DVectorSpriteModel(res, &model2);
+    dmf_close(res);
+    dmMsg(0, "Loaded, %d: %s\n", err, dmErrorStr(err));
+    
+    if (optBenchmark)
+    {
+        screen = SDL_CreateRGBSurface(SDL_SWSURFACE, optScrWidth, optScrHeight, optScrDepth, 0, 0, 0, 0);
+        if (screen == NULL)
+        {
+            dmError("Could not create screen surface.\n");
+            goto error_exit;
+        }
+        
+        dmMsg(0, "Benchmark mode, not opening window.\n");
+    }
+    else
+    {
+        if (!DM_InitializeVideo(&screen))
+            goto error_exit;
+
+        SDL_WM_SetCaption("Halleluja", "DMT");
+    }
+
+    screen2 = SDL_CreateRGBSurface(SDL_SWSURFACE, optScrWidth, optScrHeight, optScrDepth, 0, 0, 0, 0);
+    Uint32 lcol = dmMapRGB(screen, 255,255,255);
+
+    int numFrames = 0, startTime = SDL_GetTicks(), endTime = 0;
+    exitFlag = FALSE;
+
+    if (optBenchmark)
+        dmMsg(0, "Starting benchmark, running for %d seconds.\n", optBenchmarkLen);
+
+    while (!exitFlag)
+    {
+        if (!optBenchmark)
+        {
+            while (SDL_PollEvent(&event))
+            switch (event.type)
+            {
+                case SDL_KEYDOWN:
+                    switch (event.key.keysym.sym)
+                    {
+                        case SDLK_ESCAPE: exitFlag = TRUE; break;
+                            
+                        default:
+                            break;
+                    }
+
+                    break;
+                
+                case SDL_VIDEORESIZE:
+                    optScrWidth = event.resize.w;
+                    optScrHeight = event.resize.h;
+
+                    if (!DM_InitializeVideo(&screen))
+                        goto error_exit;
+
+                    break;
+                
+                case SDL_VIDEOEXPOSE:
+                    break;
+
+                case SDL_QUIT:
+                    exit(0);
+            }
+
+            SDL_GetMouseState(&mouseX, &mouseY);
+            bx = 300 - ((DMFloat) mouseX * 500.0f ) / (DMFloat) optScrWidth;
+            by = 300 - ((DMFloat) mouseY * 500.0f ) / (DMFloat) optScrHeight;
+        }
+        else
+        {
+            bx = 0;
+            by = 0;
+        }
+
+        if (!optBenchmark && SDL_MUSTLOCK(screen) != 0 && SDL_LockSurface(screen) != 0)
+        {
+            dmError("Can't lock surface.\n");
+            goto error_exit;
+        }
+
+
+        dmClearSurface(screen, 0);
+
+        float f = SDL_GetTicks() / 150.0f,
+              qw2 = (float) 132.0 * (1.0 + sin(f) * 0.1),
+              qh2 = (float) 132.0 * (1.0 + sin(f) * 0.1);
+
+#if 1
+        dmScaledBlitSurfaceAny(bmap, bx-qw2, by-qh2, bmap->w+qw2, bmap->h+qh2, screen,
+        DMD_TRANSPARENT
+//        DMD_SATURATE
+//        DMD_NONE
+        );
+#endif
+#if 0
+        float qw = (float) 32.0 * (1.0 + sin(f) * 0.1),
+              qh = (float) 32.0 * (1.0 + sin(f) * 0.1),
+        dmScaledBlitSurface32to32TransparentGA(bmap, bx*2-qw, by*2-qh, bmap->w+qw, bmap->h+qh, screen,
+            128 + sin(f*0.1) * 120.0f);
+#endif
+
+#if 1
+        DMVector pos;
+        
+        DMMatrix mat;
+//        dm_matrix_rot_a(&mat, f*0.1, 0, (3.1415926535f * 2.0f * ((DMFloat) mouseX + (DMFloat) mouseY) ) / 500.0f);
+//        dm_matrix_rot_a(&mat, f*0.1, f*0.1, f*0.1);
+        dm_matrix_rot_a(&mat, 0, 0, f*0.1);
+
+        pos.x = -118;
+        pos.y = 0;
+        pos.z = 100;
+ 
+        dmDraw3DVectorSpriteModel(screen, model2, &pos, &mat, fbmap, lcol);
+
+        pos.x = 118;
+        pos.y = 0;
+        pos.z = 100;
+
+        dm_matrix_rot_a(&mat, 0, 0, -f*0.1+0.125);
+        dmDraw3DVectorSpriteModel(screen, model2, &pos, &mat, fbmap, lcol);
+
+
+#endif
+
+#if 0
+        int np;
+        dmClearSurface(screen2, 0);
+        for (np = 1; np <= 5; np++)
+        {
+            float n = ((float) np - 0.5f) * np;
+            dmScaledBlitSurface32to32TransparentGA(screen, -n, -n, screen->w + n*2.0f, screen->h + n*2.0f, screen2,
+            210 - np * 10
+            );
+            dmDirectBlitSurface(screen2, screen);
+        }
+
+        dmDraw3DVectorSpriteModel(screen, model, &pos, &mat, fbmap, lcol);
+
+        for (np = 1; np <= 5; np++)
+        {
+            float n = ((float) np - 0.5f) / 2.0f;
+            dmScaledBlitSurface32to32TransparentGA(screen, -n, -n, screen->w + n*2.0f, screen->h + n*2.0f, screen2,
+            210 - np * 10
+            );
+            dmDirectBlitSurface(screen2, screen);
+        }
+
+#endif
+
+        if (!optBenchmark)
+        {
+            dmDrawTTFText(screen, font, fontcol, 0, 0, "%3.1f FPS", 
+                (float) (numFrames * 1000.0f) / (float) (endTime - startTime));
+
+            if (SDL_MUSTLOCK(screen) != 0)
+                SDL_UnlockSurface(screen);
+
+            SDL_Flip(screen);
+            SDL_Delay(25);
+        }
+
+        endTime = SDL_GetTicks();
+        numFrames++;
+
+        if (optBenchmark)
+        {
+            if (endTime - startTime > optBenchmarkLen * 1000)
+                exitFlag = TRUE;
+        }
+    }
+
+    // Print benchmark results
+    dmMsg(0, "%d frames in %d ms, fps = %1.3f\n",
+        numFrames, endTime - startTime,
+        (float) (numFrames * 1000.0f) / (float) (endTime - startTime));
+
+
+error_exit:
+    dmMsg(0, "Shutting down dmlib.\n");
+    if (screen)
+        SDL_FreeSurface(screen);
+
+    if (bmap)
+        SDL_FreeSurface(bmap);
+
+    if (font)
+        TTF_CloseFont(font);
+
+    if (initSDL)
+        SDL_Quit();
+
+    if (initTTF)
+        TTF_Quit();
+
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utils/data2inc.c	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,404 @@
+/*
+ * data2inc - Convert binary data to "C"-source or XA-compatible include file
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2003,2009-2012 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#include <errno.h>
+#include "dmlib.h"
+#include "dmargs.h"
+#include "dmmutex.h"
+
+#define RA_LINEBUF    (16)
+
+enum
+{
+    FMT_AUTO = 0,
+    FMT_C,
+    FMT_ASM
+};
+
+char    *optInFilename = NULL,
+        *optOutFilename = NULL,
+        *optObjName = "default_object",
+        *optDataType = NULL,
+        *optAddLine = NULL;
+
+int     optIndentation = -1;
+int     optFormat = FMT_AUTO;
+BOOL    optHexMode = FALSE,
+        optQuiet = FALSE,
+        optExtraData = FALSE,
+        optFormatting = TRUE;
+
+
+void (*writeHeader) (FILE *, char *) = NULL;
+void (*writeDecl) (FILE *, size_t, char *) = NULL;
+void (*writeData) (FILE *, Uint8 *, size_t) = NULL;
+void (*writeFooter) (FILE *, size_t, char *) = NULL;
+
+
+static DMOptArg optList[] =
+{
+    {  0, '?', "help",           "Show this help", OPT_NONE },
+    {  4, 'A', "format-asm",     "Output in XA-compatible asm", OPT_NONE },
+    {  5, 'C', "format-c",       "Output in ANSI C", OPT_NONE },
+    {  1, 'n', "name",           "Set object name", OPT_ARGREQ },
+    {  2, 't', "type",           "Set datatype (unsigned char/byte)", OPT_ARGREQ },
+    {  3, 'a', "add-line",       "Add this line to start of file", OPT_ARGREQ },
+    {  6, 'x', "hexadecimal",    "Use hexadecimal output", OPT_NONE },
+    {  7, 'q', "quiet",          "Do not add comments", OPT_NONE },
+    {  8, 'f', "no-formatting",  "Disable additional output formatting", OPT_NONE },
+    {  9, 'i', "indentation",    "Set indentation (negative value = tab)", OPT_ARGREQ },
+    { 10, 'e', "extra-data",     "Add object end labels and size in asm output", OPT_NONE },
+};
+
+static const int optListN = sizeof(optList) / sizeof(optList[0]);
+
+
+void argShowHelp()
+{
+    dmPrintBanner(stdout, dmProgName,
+        "[options] [sourcefile] [destfile]");
+
+    dmArgsPrintHelp(stdout, optList, optListN);
+    
+    printf(
+    "\n"
+    "To convert a data file to a C structure using 'Uint8' as type:\n"
+    "$ data2inc -C -n variable_name -t Uint8 input.bin output.h\n"
+    "\n"
+    );
+}
+
+
+BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
+{
+    switch (optN)
+    {
+        case 0:
+            argShowHelp();
+            exit(0);
+            break;
+
+        case 1:
+            optObjName = optArg;
+            break;
+
+        case 2:
+            optDataType = optArg;
+            break;
+
+        case 3:
+            optAddLine = optArg;
+            break;
+
+        case 4:
+            optFormat = FMT_ASM;
+            break;
+        case 5:
+            optFormat = FMT_C;
+            break;
+        case 6:
+            optHexMode = TRUE;
+            break;
+        case 7:
+            optQuiet = TRUE;
+            break;
+        case 8:
+            optFormatting = FALSE;
+            break;
+
+        case 9:
+            optIndentation = atoi(optArg);
+            break;
+
+        case 10:
+            optExtraData = TRUE;
+            break;
+
+        default:
+            dmError("Unknown option '%s'.\n", currArg);
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+BOOL argHandleFile(char * currArg)
+{
+    if (!optInFilename)
+        optInFilename = currArg;
+    else
+    if (!optOutFilename)
+        optOutFilename = currArg;
+    else
+        dmError("Source and destination filenames already specified, extraneous argument '%s'.\n", currArg);
+
+    return TRUE;
+}
+
+
+/* Assembler include data output functions
+ */
+void writeHeader_ASM(FILE * f, char *name)
+{
+    if (name)
+        fprintf(f, "; '%s'", name);
+    else
+        fprintf(f, "; Generated");
+    fprintf(f, " by %s v%s\n",
+        dmProgName, dmProgVersion);
+}
+
+void writeDecl_ASM(FILE * f, size_t len, char *name)
+{
+    if (optExtraData)
+        fprintf(f, "%s_size = %u\n", name, len);
+    fprintf(f, "%s:\n", name);
+}
+
+void writeData_ASM(FILE * f, Uint8 * buf, size_t len)
+{
+    size_t i;
+
+    fprintf(f, "%s ", optDataType);
+    for (i = 0; i < len; i++)
+    {
+        if (optFormatting)
+        {
+            if (optHexMode)
+                fprintf(f, "$%.2x", buf[i]);
+            else
+                fprintf(f, "%3d", buf[i]);
+        }
+        else
+        {
+            if (optHexMode)
+                fprintf(f, "$%x", buf[i]);
+            else
+                fprintf(f, "%d", buf[i]);
+        }
+
+        if (i < (len - 1))
+            fprintf(f, ",");
+    }
+}
+
+void writeFooter_ASM(FILE * f, size_t len, char *name)
+{
+    (void) len;
+    if (optExtraData)
+        fprintf(f, "%s_end: \n", name);
+    else
+        fprintf(f, "\n");
+}
+
+
+/* ANSI-C include data output functions
+ */
+void writeHeader_C(FILE * f, char *name)
+{
+    if (name)
+        fprintf(f, "/* '%s' generated", name);
+    else
+        fprintf(f, "/* Generated");
+
+    fprintf(f, " by %s v%s\n" " */\n",
+        dmProgName, dmProgVersion);
+}
+
+void writeDecl_C(FILE * f, size_t len, char *name)
+{
+    fprintf(f, "%s %s[%u] = {\n",
+        optDataType, name, len);
+
+    printf("extern %s %s[%u];\n",
+        optDataType, name, len);
+}
+
+void writeData_C(FILE * f, Uint8 * buf, size_t len)
+{
+    size_t i;
+
+    for (i = 0; i < len; i++)
+    {
+        if (optFormatting)
+        {
+            if (optHexMode)
+                fprintf(f, "0x%.2x", buf[i]);
+            else
+                fprintf(f, "%3d", buf[i]);
+        }
+        else
+        {
+            if (optHexMode)
+                fprintf(f, "0x%x", buf[i]);
+            else
+                fprintf(f, "%d", buf[i]);
+        }
+
+        fprintf(f, ",");
+    }
+}
+
+void writeFooter_C(FILE * f, size_t len, char *name)
+{
+    (void) len;
+    (void) name;
+    fprintf(f, "};\n");
+}
+
+
+off_t dmGetFileSize(FILE *f)
+{
+    off_t len, pos = ftell(f);
+    fseeko(f, 0, SEEK_END);
+    len = ftell(f);
+    fseek(f, pos, SEEK_SET);
+    return len;
+}
+
+
+int main(int argc, char *argv[])
+{
+    FILE *sfile = NULL, *dfile = NULL;
+    off_t inSize;
+    Uint8 inBuf[RA_LINEBUF];
+    int tmpRes;
+
+    /* Initialize */
+    dmInitProg("data2inc", "Data to include converter", "0.6", NULL, NULL);
+    dmVerbosity = 0;
+
+    /* Parse arguments */
+    if (!dmArgsProcess(argc, argv, optList, optListN,
+        argHandleOpt, argHandleFile, TRUE))
+        exit(1);
+
+    /* Determine output type, if not specified */
+    if (optFormat == FMT_AUTO)
+    {
+        char *dext;
+
+        if (optOutFilename == NULL)
+        {
+            dmError("Output format not specified and no output filename given (try --help)\n");
+            exit(1);
+        }
+
+        /* Check filename extension */
+        dext = strrchr(optOutFilename, '.');
+        if (dext)
+        {
+            dext++;
+            if (!strcasecmp(dext, "c") || !strcasecmp(dext, "h") ||
+                !strcasecmp(dext, "cc") || !strcasecmp(dext, "cpp") ||
+                !strcasecmp(dext, "hpp") || !strcasecmp(dext, "c++"))
+                optFormat = FMT_C;
+            else
+                optFormat = FMT_ASM;
+        } else
+            optFormat = FMT_ASM;
+    }
+
+    /* Set functions */
+    switch (optFormat)
+    {
+        case FMT_ASM:
+            if (!optDataType)
+                optDataType = ".byte";
+
+            writeHeader = writeHeader_ASM;
+            writeDecl = writeDecl_ASM;
+            writeData = writeData_ASM;
+            writeFooter = writeFooter_ASM;
+            break;
+
+        case FMT_C:
+            if (!optDataType)
+                optDataType = "unsigned char";
+
+            writeHeader = writeHeader_C;
+            writeDecl = writeDecl_C;
+            writeData = writeData_C;
+            writeFooter = writeFooter_C;
+            break;
+
+        case FMT_AUTO:
+        default:
+            dmError("Internal error, FMT_AUTO at output function init.\n");
+            exit(2);
+    }
+
+    /* Open the files */
+    if (optInFilename == NULL)
+        sfile = stdin;
+    else
+    if ((sfile = fopen(optInFilename, "rb")) == NULL)
+    {
+        tmpRes = errno;
+        dmError("Error opening input file '%s'. (%s)\n",
+            optInFilename, strerror(tmpRes));
+        exit(3);
+    }
+
+    if (optOutFilename == NULL)
+        dfile = stdout;
+    else
+    if ((dfile = fopen(optOutFilename, "wa")) == NULL)
+    {
+        tmpRes = errno;
+        dmError("Error creating output file '%s'. (%s)\n",
+            optOutFilename, strerror(tmpRes));
+        exit(4);
+    }
+
+    /* Get sourcefile size */
+    inSize = dmGetFileSize(sfile);
+
+    /* Output header */
+    if (!optQuiet)
+        writeHeader(dfile, optOutFilename);
+
+    if (optAddLine)
+        fprintf(dfile, "%s\n", optAddLine);
+
+    /* Output declaration */
+    writeDecl(dfile, inSize, optObjName);
+
+    /* Output data */
+    while (!feof(sfile))
+    {
+        tmpRes = fread(inBuf, sizeof(Uint8), RA_LINEBUF, sfile);
+        if (tmpRes > 0)
+        {
+            if (optIndentation < 0)
+                fprintf(dfile, "\t");
+            else
+            if (optIndentation > 0)
+            {
+                int i;
+                for (i = 0; i < optIndentation; i++)
+                    fputs(" ", dfile);
+            }
+
+            writeData(dfile, inBuf, tmpRes);
+
+            fprintf(dfile, "\n");
+        }
+    }
+
+
+    /* Output footer */
+    writeFooter(dfile, inSize, optObjName);
+
+    /* Exit */
+    fclose(sfile);
+    fclose(dfile);
+
+    exit(0);
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utils/gentab.c	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,264 @@
+#include "dmlib.h"
+#include "dmargs.h"
+#include <math.h>
+
+enum
+{
+    MT_SIN,
+    MT_COS,
+    MT_SMOOTH1,
+    MT_SCURVE,
+    MT_SMOOTH1_CLAMP,
+    MT_SCURVE_CLAMP,
+    MT_SIN_SCURVE,
+
+    MT_LAST
+};
+
+
+typedef struct
+{
+    char *name;
+    char *desc;
+} DMTransType;
+
+static DMTransType dmTransTypes[MT_LAST] =
+{
+    { "sin", "Sine" },
+    { "cos", "Cosine" },
+    { "smooth1", "Smoothstep1 LERP" },
+    { "scurve", "S-curve LERP" },
+    { "smooth1-clamp", "Clamped smoothstep1 LERP" },
+    { "scurve-clamp", "Clamped S-curve LERP" },
+    { "sin-scurve", "Sine S-curve" },
+};
+
+
+DMFloat
+    optSOffset     = 0.0f,
+    optSAmplitude  = 1.0f,
+    optSOmega      = 1.0f,
+    optStartValue  = 0.0f,
+    optEndValue    = 1.0f;
+
+int optNSteps      = 64,
+    optPerLine     = 16,
+    optTransType   = -1;
+
+char
+    *optObjectName = NULL,
+    *optOutFilename = NULL;
+
+
+static DMOptArg optList[] =
+{
+    {  0, '?', "help",        "Show this help", OPT_NONE },
+    {  1, 'v', "verbose",     "Increase verbosity", OPT_NONE },
+    {  2, 'o', "output",      "Set output file (default stdout)", OPT_ARGREQ },
+    {  3, 'n', "name",        "Set output object name", OPT_ARGREQ },
+
+    {  4, 's', "steps",       "Number of steps/values in output table", OPT_ARGREQ },
+    {  5, 't', "type",        "Curve/interpolation type (see list)", OPT_ARGREQ },
+
+    {  6, 'O', "offset",      "Output data offset", OPT_ARGREQ },
+    {  7, 'A', "amplitude",   "Output amplitude scale", OPT_ARGREQ },
+    {  8, 'W', "omega",       "Omega (w) multiplier", OPT_ARGREQ },
+
+    {  9, 'S', "start",       "Start value (only smooth/scurve)", OPT_ARGREQ },
+    { 10, 'E', "end",         "End value (only smooth/scurve)", OPT_ARGREQ },
+};
+
+static const int optListN = sizeof(optList) / sizeof(optList[0]);
+
+
+void argShowHelp()
+{
+    int index;
+    dmPrintBanner(stdout, dmProgName, "[options]");
+    dmArgsPrintHelp(stdout, optList, optListN);
+
+    printf("\nAvailable types:\n");
+    for (index = 0; index < MT_LAST; index++)
+    {
+        DMTransType *tm = &dmTransTypes[index];
+        printf("%-15s | %s\n", tm->name, tm->desc);
+    }
+    printf("\n");
+}
+
+
+BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
+{
+    switch (optN)
+    {
+        case 0:
+            argShowHelp();
+            exit(0);
+            break;
+
+        case 1:
+            dmVerbosity++;
+            break;
+        
+        case 2:
+            optOutFilename = optArg;
+            break;
+        
+        case 3:
+            optObjectName = optArg;
+            break;
+        
+        case 4:
+            {
+                int tmp;
+                if (sscanf(optArg, "%d", &tmp) != 1)
+                {
+                    dmError("Invalid number of steps argument '%s'.\n", optArg);
+                    return FALSE;
+                }
+                optNSteps = tmp;
+            }
+            break;
+
+        case 5:
+            {
+                int index;
+                for (index = 0; index < MT_LAST; index++)
+                {
+                    DMTransType *tm = &dmTransTypes[index];
+                    if (strcasecmp(tm->name, optArg) == 0)
+                    {
+                        optTransType = index;
+                        return TRUE;
+                    }
+                }
+                dmError("Invalid transformation type option '%s'.\n",
+                    optArg);
+                return FALSE;
+            }
+            break;
+
+        case 6:
+        case 7:
+        case 8:
+        case 9:
+        case 10:
+            {
+                DMFloat tmp;
+                if (sscanf(optArg, "%f", &tmp) != 1)
+                {
+                    dmError("Invalid %s option argument '%s', expected a floating point value.\n",
+                        currArg, optArg);
+                    return FALSE;
+                }
+                switch (optN)
+                {
+                    case  6: optSOffset = tmp; break;
+                    case  7: optSAmplitude = tmp; break;
+                    case  8: optSOmega = tmp; break;
+                    case  9: optStartValue = tmp; break;
+                    case 10: optEndValue = tmp; break;
+                }
+            }
+            break;
+
+        default:
+            dmError("Unknown argument '%s'.\n", currArg);
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+int main(int argc, char *argv[])
+{
+    FILE *outFile;
+    DMLerpContext ctx;
+    int step, n;
+
+    dmInitProg("gentab", "Sine, etc. table generator", "0.1", NULL, NULL);
+    dmVerbosity = 1;
+
+    // Parse arguments
+    if (!dmArgsProcess(argc, argv, optList, optListN,
+        argHandleOpt, NULL, TRUE))
+        exit(1);
+
+    // Check settings
+    if (optTransType < 0)
+    {
+        dmError("No transformation type set, perhaps try --help\n");
+        return -1;
+    }
+    
+    if (optObjectName == NULL)
+    {
+        dmError("Object name not specified, try --help\n");
+        return -2;
+    }
+
+    if (optOutFilename == NULL)
+        outFile = stdout;
+    else
+    if ((outFile = fopen(optOutFilename, "w")) == NULL)
+    {
+        int err = dmGetErrno();
+        dmError("Could not open output file '%s', %d: %s\n",
+            optOutFilename, err, dmErrorStr(err));
+        return -2;
+    }
+    
+
+    // Generate table
+    dmLerpInit(&ctx, optStartValue, optEndValue, optNSteps);
+
+    fprintf(outFile,
+        "cnt_%s = %d\n"
+        "vtab_%s: ",
+        optObjectName,
+        optNSteps,
+        optObjectName
+        );
+
+    for (n = 0, step = 0; step < optNSteps; step++)
+    {
+        DMFloat t = ((DMFloat) step * optSOmega) / (DMFloat) optNSteps, delta, value;
+        
+        switch (optTransType)
+        {
+            case MT_SIN:           delta = sin(t * 2 * DM_PI); break;
+            case MT_COS:           delta = cos(t * 2 * DM_PI); break;
+
+            case MT_SMOOTH1:       delta = dmLerp1(&ctx, step); break;
+            case MT_SCURVE:        delta = dmLerpSCurve(&ctx, step); break;
+            case MT_SMOOTH1_CLAMP: delta = dmLerp1Clamp(&ctx, step); break;
+            case MT_SCURVE_CLAMP:  delta = dmLerpSCurveClamp(&ctx, step); break;
+            case MT_SIN_SCURVE:    delta = dmLerpSCurveClamp(&ctx, step); break;
+            
+            default: delta = 0;
+        }
+        
+        value = optSOffset + delta * optSAmplitude;
+        
+        // Print the value
+        if (n == 0)
+            fprintf(outFile, "\t.byte ");
+
+        fprintf(outFile, "%ld%s",
+            lrint(value),
+            (n < optPerLine - 1) ? "," : "");
+
+        if (++n >= optPerLine)
+        {
+            fprintf(outFile, "\n");
+            n = 0;
+        }
+    }
+    if (n > 0)
+        fprintf(outFile, "\n");
+    
+    fprintf(outFile, "\n");
+
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utils/gfxconv.c	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,1697 @@
+/*
+ * gfxconv - Convert various graphics formats
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2012 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#include "dmlib.h"
+#include "dmargs.h"
+#include "dmfile.h"
+#include "dmmutex.h"
+#include "libgfx.h"
+#include "lib64gfx.h"
+
+//#define UNFINISHED 1
+
+#define DM_MAX_COLORS 256
+
+#define ASC_NBITS    8
+#define ASC_NCOLORS  4
+static const char dmASCIIPalette[ASC_NCOLORS] = ".:X#";
+
+enum
+{
+    FFMT_AUTO = 0,
+
+    FFMT_ASCII,
+    FFMT_ANSI,
+    FFMT_IMAGE,
+
+    FFMT_CHAR,
+    FFMT_SPRITE,
+    FFMT_BITMAP,
+
+    FFMT_LAST
+};
+
+
+typedef struct
+{
+    char *name;
+    char *fext;
+    BOOL in, out;
+    int format;
+    int subformat;
+} DMConvFormat;
+
+
+static DMConvFormat convFormatList[] =
+{
+    {
+        "ASCII text", "asc", FALSE, TRUE,
+        FFMT_ASCII  , 0,
+    },
+    {
+        "ANSI colored text", "ansi", FALSE, TRUE,
+        FFMT_ANSI   , 0,
+    },
+    {
+        "PNG image file", "png", TRUE, TRUE,
+        FFMT_IMAGE  , IMGFMT_PNG,
+    },
+    {
+        "PPM image file", "ppm", FALSE, TRUE,
+        FFMT_IMAGE  , IMGFMT_PPM,
+    },
+    {
+        "PCX image file", "pcx", TRUE, TRUE,
+        FFMT_IMAGE  , IMGFMT_PCX,
+    },
+    {
+        "IFF ILBM file", "lbm", TRUE, FALSE,
+        FFMT_IMAGE  , IMGFMT_ILBM,
+    },
+    {
+        "Bitplaned RAW (intl/non-intl) image file", "raw", FALSE, TRUE,
+        FFMT_IMAGE  , IMGFMT_RAW,
+    },
+    {
+        "IFFMaster RAW image file", "araw", FALSE, TRUE,
+        FFMT_IMAGE  , IMGFMT_ARAW,
+    },
+
+    {
+        "C64 bitmap image file", NULL, TRUE, TRUE,
+        FFMT_BITMAP , -1,
+    },
+
+    {
+        "C64 character/font data", "chr", TRUE, TRUE,
+        FFMT_CHAR   , 0
+    },
+    {
+        "C64 sprite data", "spr", TRUE, TRUE,
+        FFMT_SPRITE , 0
+    },
+};
+
+static const int nconvFormatList = sizeof(convFormatList) / sizeof(convFormatList[0]);
+
+
+typedef struct
+{
+    BOOL triplet, alpha;
+    DMColor color;
+    int from, to;
+} DMMapValue;
+
+
+
+char    *optInFilename = NULL,
+        *optOutFilename = NULL;
+int     optInFormat = FFMT_AUTO,
+        optOutFormat = FFMT_ASCII,
+        optInSubFormat = IMGFMT_PNG,
+        optOutSubFormat = IMGFMT_PNG,
+        optItemCount = -1,
+        optPlanedWidth = 1,
+        optForcedFormat = -1;
+int     optInSkip = 0;
+BOOL    optInMulticolor = FALSE,
+        optSequential = FALSE,
+        optRemapColors = FALSE,
+        optRemapRemove = FALSE;
+int     optNRemapTable = 0;
+DMMapValue optRemapTable[DM_MAX_COLORS];
+int     optColors[C64_MAX_COLORS];
+
+DMImageSpec optSpec =
+{
+    .scale = 1,
+    .nplanes = 4,
+    .interleave = FALSE,
+    .paletted = FALSE,
+    .format = 0,
+};
+
+static DMOptArg optList[] =
+{
+    {  0, '?', "help",         "Show this help", OPT_NONE },
+    { 15, 'v', "verbose",      "Increase verbosity", OPT_NONE },
+    {  3, 'o', "output",       "Output filename", OPT_ARGREQ },
+    {  1, 'i', "informat",     "Set input format ([s]prite, [c]har, [b]itmap, [i]mage)", OPT_ARGREQ },
+    {  2, 'm', "multicolor",   "Input is multicolor / output in multicolor", OPT_NONE },
+    {  4, 's', "skip",         "Skip bytes in input", OPT_ARGREQ },
+    {  5, 'f', "format",       "Output format (see --formats)", OPT_ARGREQ },
+    { 17, 'F', "formats",      "Output format (see list below)", OPT_NONE },
+    {  8, 'q', "sequential",   "Output sequential files (image output only)", OPT_NONE },
+    {  6, 'c', "colormap",     "Color mappings (see below for information)", OPT_ARGREQ },
+    {  7, 'n', "numitems",     "How many 'items' to view (default: all)", OPT_ARGREQ },
+    {  9, 'S', "scale",        "Scale output by x (image output only)", OPT_ARGREQ },
+    { 10, 'b', "bformat",      "Force input bitmap format (see below)", OPT_ARGREQ },
+    { 11, 'w', "width",        "Item width (number of items per row, min 1)", OPT_ARGREQ },
+    { 12, 'P', "paletted",     "Use indexed/paletted output (png, pcx output only)", OPT_NONE },
+    { 13, 'B', "bplanes",      "Bits per pixel OR # of bitplanes (certain output formats)", OPT_ARGREQ },
+    { 14, 'I', "interleave",   "Interleave scanlines (default: output whole planes)", OPT_NONE },
+    { 16, 'R', "remap",        "Remap output image colors (-R <(#RRGGBB|index):index>[,<..>] | -R @map.txt)", OPT_ARGREQ },
+    { 18, 'r', "remap-remove", "Remove unused colors from remapped palette (requires -R)", OPT_NONE },
+};
+
+static const int optListN = sizeof(optList) / sizeof(optList[0]);
+
+
+void argShowFormats()
+{
+    int i;
+
+    printf(
+    "Available input/output formats:\n"
+    "  Ext | I | O | Description\n"
+    "------+---+---+-----------------------------------------------\n"
+    );
+    
+    for (i = 0; i < nconvFormatList; i++)
+    {
+        DMConvFormat *fmt = &convFormatList[i];
+        printf("%-5s | %c | %c | %s\n",
+            fmt->fext ? fmt->fext : "",
+            fmt->in ? 'X' : ' ',
+            fmt->out ? 'X' : ' ',
+            fmt->name);
+    }
+
+    printf(
+    "\n"
+    "(Not all input->output combinations are actually supported.)\n"
+    "\n"
+    "Available bitmap formats:\n"
+    "  Ext | Type            | Description\n"
+    "------+-----------------+-------------------------------------\n"
+    );
+
+    for (i = 0; i < ndmC64ImageFormats; i++)
+    {
+        const DMC64ImageFormat *fmt = &dmC64ImageFormats[i];
+        char buf[64];
+        printf("%-5s | %-15s | %s\n",
+            fmt->fext,
+            dmC64GetImageTypeString(buf, sizeof(buf), fmt->type),
+            fmt->name);
+    }
+}
+
+
+void argShowHelp()
+{
+    dmPrintBanner(stdout, dmProgName, "[options] <input file>");
+    dmArgsPrintHelp(stdout, optList, optListN);
+
+    printf(
+    "\n"
+    "Color map definitions are used for ANSI and image output, to declare what\n"
+    "output colors of the C64 palette are used for each single color/multi color\n"
+    "bit-combination. For example, if the input is multi color sprite or char,\n"
+    "you can define colors like: -c 0,8,3,15 .. for single color: -c 0,1\n"
+    "The numbers are palette indexes, and the order is for bit(pair)-values\n"
+    "00, 01, 10, 11 (multi color) and 0, 1 (single color). NOTICE! 255 is the\n"
+    "special color that can be used for transparency.\n"
+    );
+}
+
+
+int dmGetConvFormat(int format, int subformat)
+{
+    int i;
+    for (i = 0; i < nconvFormatList; i++)
+    {
+        DMConvFormat *fmt = &convFormatList[i];
+        if (fmt->format == format &&
+            fmt->subformat == subformat)
+            return i;
+    }
+    return -1;
+}
+
+
+BOOL dmGetFormatByExt(const char *fext, int *format, int *subformat)
+{
+    int i;
+    if (fext == NULL)
+        return FALSE;
+
+    for (i = 0; i < nconvFormatList; i++)
+    {
+        DMConvFormat *fmt = &convFormatList[i];
+        if (fmt->fext != NULL &&
+            strcasecmp(fext, fmt->fext) == 0)
+        {
+            *format = fmt->format;
+            *subformat = fmt->subformat;
+            return TRUE;
+        }
+    }
+
+    for (i = 0; i < ndmC64ImageFormats; i++)
+    {
+        const DMC64ImageFormat *fmt = &dmC64ImageFormats[i];
+        if (fmt->fext != NULL &&
+            strcasecmp(fext, fmt->fext) == 0)
+        {
+            *format = FFMT_BITMAP;
+            *subformat = i;
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+
+static BOOL dmParseMapOptionMapItem(const char *popt, DMMapValue *value, const int nmax, const char *msg)
+{
+    char *end, *split, *opt = dm_strdup(popt);
+
+    if (opt == NULL)
+        goto error;
+
+    if ((end = split = strchr(opt, ':')) == NULL)
+    {
+        dmError("Invalid %s value '%s', expected <(#|%)RRGGBB|[$|0x]index>:<[$|0x]index>.\n", msg, opt);
+        goto error;
+    }
+
+    // Trim whitespace
+    *end = 0;
+    for (end--; end > opt && *end && isspace(*end); end--)
+        *end = 0;
+
+    // Parse either a hex triplet color definition or a normal value
+    if (*opt == '#' || *opt == '%')
+    {
+        int colR, colG, colB, colA;
+
+        if (sscanf(opt + 1, "%2x%2x%2x%2x", &colR, &colG, &colB, &colA) == 4 ||
+            sscanf(opt + 1, "%2X%2X%2X%2X", &colR, &colG, &colB, &colA) == 4)
+        {
+            value->alpha = TRUE;
+            value->color.a = colA;
+        }
+        else
+        if (sscanf(opt + 1, "%2x%2x%2x", &colR, &colG, &colB) != 3 &&
+            sscanf(opt + 1, "%2X%2X%2X", &colR, &colG, &colB) != 3)
+        {
+            dmError("Invalid %s value '%s', expected a hex triplet, got '%s'.\n", msg, popt, opt + 1);
+            goto error;
+        }
+
+        value->color.r = colR;
+        value->color.g = colG;
+        value->color.b = colB;
+        value->triplet = TRUE;
+    }
+    else
+    {
+        if (!dmGetIntVal(opt, &value->from))
+        {
+            dmError("Invalid %s value '%s', could not parse source value '%s'.\n", msg, popt, opt);
+            goto error;
+        }
+        value->triplet = FALSE;
+    }
+    
+    // Trim whitespace
+    split++;
+    while (*split && isspace(*split)) split++;
+    
+    // Parse destination value
+    if (!dmGetIntVal(split, &value->to))
+    {
+        dmError("Invalid %s value '%s', could not parse destination value '%s'.\n", msg, popt, split);
+        goto error;
+    }
+
+    if (!value->triplet && (value->from < 0 || value->from > 255))
+    {
+        dmError("Invalid %s map source color index value %d, must be [0..255].\n", msg, value->from);
+        goto error;
+    }
+
+    if (value->to < 0 || value->to > nmax)
+    {
+        dmError("Invalid %s map destination color index value %d, must be [0..%d].\n", msg, value->to, nmax);
+        goto error;
+    }
+
+    dmFree(opt);
+    return TRUE;
+
+error:
+    dmFree(opt);
+    return FALSE;
+}
+
+
+static BOOL dmParseMapOptionItem(char *opt, char *end, void *pvalue, const int index, const int nmax, const BOOL requireIndex, const char *msg)
+{
+    // Trim whitespace
+    if (end != NULL)
+    {
+        *end = 0;
+        for (end--; end > opt && *end && isspace(*end); end--)
+            *end = 0;
+    }
+    while (*opt && isspace(*opt)) opt++;
+
+    // Parse item based on mode
+    if (requireIndex)
+    {
+        DMMapValue *value = (DMMapValue *) pvalue;
+        if (!dmParseMapOptionMapItem(opt, &value[index], nmax, msg))
+            return FALSE;
+    }
+    else
+    {
+        int *value = (int *) pvalue;
+        char *split = strchr(opt, ':');
+        if (split != NULL)
+        {
+            dmError("Unexpected ':' in indexed %s '%s'.\n", msg, opt);
+            return FALSE;
+        }
+
+        if (!dmGetIntVal(opt, &value[index]))
+        {
+            dmError("Invalid %s value '%s', could not parse.\n", msg, opt);
+            return FALSE;
+        }
+    }
+    
+    return TRUE;
+}
+
+
+BOOL dmParseMapOptionString(char *opt, void *values, int *nvalues, const int nmax, const BOOL requireIndex, const char *msg)
+{
+    char *end, *start = opt;
+
+    *nvalues = 0;
+    while (*nvalues < nmax && *start && (end = strchr(start, ',')) != NULL)
+    {
+        if (!dmParseMapOptionItem(start, end, values, *nvalues, nmax, requireIndex, msg))
+            return FALSE;
+
+        start = end + 1;
+        (*nvalues)++;
+    }
+    
+    if (*start && *nvalues < nmax)
+    {
+        if (!dmParseMapOptionItem(start, NULL, values, *nvalues, nmax, requireIndex, msg))
+            return FALSE;
+
+        (*nvalues)++;
+    }
+
+    return TRUE;   
+}
+
+
+int dmParseColorRemapFile(const char *filename, DMMapValue *values, int *nvalue, const int nmax)
+{
+    FILE *fp;
+    char line[512];
+    int res = DMERR_OK;
+
+    if ((fp = fopen(filename, "r")) == NULL)
+    {
+        res = dmGetErrno();
+        dmError("Could not open color remap file '%s' for reading, %d: %s\n",
+            res, dmErrorStr(res));
+        return res;
+    }
+
+    while (fgets(line, sizeof(line), fp))
+    {
+        char *start = line;
+        while (*start && isspace(*start)) start++;
+        
+        if (*start != 0 && *start != ';')
+        {
+            if (!dmParseMapOptionMapItem(line, &values[*nvalue], nmax, "mapping file"))
+                goto error;
+            else
+            {
+                (*nvalue)++;
+                if (*nvalue >= nmax)
+                {
+                    dmError("Too many mapping pairs in '%s', maximum is %d.\n",
+                        filename, nmax);
+                    goto error;
+                }
+            }
+        }
+    }
+
+error:
+    fclose(fp);
+    return res;
+}
+
+
+BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
+{
+    switch (optN)
+    {
+        case 0:
+            argShowHelp();
+            exit(0);
+            break;
+
+        case 17:
+            argShowFormats();
+            exit(0);
+            break;
+
+        case 15:
+            dmVerbosity++;
+            break;
+
+        case 1:
+            switch (tolower(optArg[0]))
+            {
+                case 's':
+                    optInFormat = FFMT_SPRITE;
+                    break;
+                case 'c':
+                    optInFormat = FFMT_CHAR;
+                    break;
+                case 'b':
+                    optInFormat = FFMT_BITMAP;
+                    break;
+                case 'i':
+                    optInFormat = FFMT_IMAGE;
+                    break;
+                default:
+                    dmError("Invalid input format '%s'.\n", optArg);
+                    return FALSE;
+            }
+            break;
+        
+        case 2:
+            optInMulticolor = TRUE;
+            break;
+
+        case 3:
+            optOutFilename = optArg;
+            break;
+        
+        case 4:
+            if (!dmGetIntVal(optArg, &optInSkip))
+            {
+                dmError("Invalid skip value argument '%s'.\n", optArg);
+                return FALSE;
+            }
+            break;
+
+        case 5:
+            if (!dmGetFormatByExt(optArg, &optOutFormat, &optOutSubFormat))
+            {
+                dmError("Invalid output format '%s'.\n", optArg);
+                return FALSE;
+            }
+            break;
+
+        case 6:
+            {
+                int index, ncolors;
+                if (!dmParseMapOptionString(optArg, optColors,
+                    &ncolors, C64_MAX_COLORS, FALSE, "color table option"))
+                    return FALSE;
+
+                dmMsg(1, "Set color table: ");
+                for (index = 0; index < ncolors; index++)
+                {
+                    dmPrint(1, "[%d:%d]%s",
+                        index, optColors[index],
+                        (index < ncolors) ? ", " : "");
+                }
+                dmPrint(1, "\n");
+            }
+            break;
+
+        case 7:
+            if (sscanf(optArg, "%d", &optItemCount) != 1)
+            {
+                dmError("Invalid count value argument '%s'.\n", optArg);
+                return FALSE;
+            }
+            break;
+
+        case 8:
+            optSequential = TRUE;
+            break;
+
+        case 9:
+            {
+                int tmp = atoi(optArg);
+                if (tmp < 1 || tmp > 50)
+                {
+                    dmError("Invalid scale value '%s'.\n", optArg);
+                    return FALSE;
+                }
+                optSpec.scale = tmp;
+            }
+            break;
+
+        case 11:
+            {
+                int tmp = atoi(optArg);
+                if (tmp < 1 || tmp > 512)
+                {
+                    dmError("Invalid width value '%s'.\n", optArg);
+                    return FALSE;
+                }
+                optPlanedWidth = tmp;
+            }
+            break;
+
+        case 12:
+            optSpec.paletted = TRUE;
+            break;
+
+        case 13:
+            {
+                int tmp = atoi(optArg);
+                if (tmp < 1 || tmp > 8)
+                {
+                    dmError("Invalid bitplanes/bpp value '%s'.\n", optArg);
+                    return FALSE;
+                }
+                optSpec.nplanes = tmp;
+            }
+            break;
+
+        case 14:
+            optSpec.interleave = TRUE;
+            break;
+
+
+        case 16:
+            if (optArg[0] == '@')
+            {
+                if (optArg[1] != 0)
+                {
+                    int res;
+                    if ((res = dmParseColorRemapFile(optArg + 1,
+                        optRemapTable, &optNRemapTable, DM_MAX_COLORS)) != DMERR_OK)
+                        return FALSE;
+                }
+                else
+                {
+                    dmError("No remap filename given.\n");
+                    return FALSE;
+                }
+            }
+            else
+            {
+                if (!dmParseMapOptionString(optArg, optRemapTable,
+                    &optNRemapTable, DM_MAX_COLORS, TRUE, "color remap option"))
+                    return FALSE;
+            }
+
+            optRemapColors = TRUE;
+            break;
+
+        case 18:
+            optRemapRemove = TRUE;
+            break;
+
+        default:
+            dmError("Unknown option '%s'.\n", currArg);
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+BOOL argHandleFile(char *currArg)
+{
+    if (!optInFilename)
+        optInFilename = currArg;
+    else
+    {
+        dmError("Source filename already specified, extraneous argument '%s'.\n",
+             currArg);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+void dmPrintByte(FILE *out, int byte, int format, BOOL multicolor)
+{
+    int i;
+    
+    if (multicolor)
+    {
+        for (i = ASC_NBITS; i; i -= 2)
+        {
+            int val = (byte & (3ULL << (i - 2))) >> (i - 2);
+            char ch;
+            switch (format)
+            {
+                case FFMT_ASCII:
+                    ch = dmASCIIPalette[val];
+                    fprintf(out, "%c%c", ch, ch);
+                    break;
+                case FFMT_ANSI:
+                    fprintf(out, "%c[0;%d;%dm##%c[0m",
+                        0x1b,
+                        1,
+                        31 + optColors[val],
+                        0x1b);
+                    break;
+            }
+        }
+    }
+    else
+    {
+        for (i = ASC_NBITS; i; i--)
+        {
+            int val = (byte & (1ULL << (i - 1))) >> (i - 1);
+            char ch;
+            switch (format)
+            {
+                case FFMT_ASCII:
+                    ch = val ? '#' : '.';
+                    fputc(ch, out);
+                    break;
+                case FFMT_ANSI:
+                    fprintf(out, "%c[0;%d;%dm %c[0m",
+                        0x1b,
+                        1,
+                        31 + optColors[val],
+                        0x1b);
+                    break;
+            }
+        }
+    }
+}
+
+
+void dmDumpCharASCII(FILE *outFile, const Uint8 *buf, int *offs, int format, BOOL multicolor)
+{
+    int yc;
+
+    for (yc = 0; yc < C64_CHR_HEIGHT; yc++)
+    {
+        fprintf(outFile, "%04x : ", *offs);
+        dmPrintByte(outFile, buf[yc], format, multicolor);
+        fprintf(outFile, "\n");
+        (*offs)++;
+    }
+}
+
+
+void dmDumpSpriteASCII(FILE *outFile, const Uint8 *buf, int *offs, int format, BOOL multicolor)
+{
+    int bufOffs, xc, yc;
+
+    for (bufOffs = yc = 0; yc < C64_SPR_HEIGHT; yc++)
+    {
+        fprintf(outFile, "%04x : ", *offs);
+        for (xc = 0; xc < C64_SPR_WIDTH; xc++)
+        {
+            dmPrintByte(outFile, buf[bufOffs], format, multicolor);
+            fprintf(outFile, " ");
+            bufOffs++;
+            (*offs)++;
+        }
+        fprintf(outFile, "\n");
+    }
+    (*offs)++;
+}
+
+
+#ifdef UNFINISHED
+int dmConvertBMP2(DMImage *screen, const DM64Image *img)
+{
+    int yc;
+    Uint8 *dp = screen->data;
+    
+    for (yc = 0; yc < screen->height; yc++)
+    {
+        Uint8 *d = dp;
+        const int y = yc / 8, yb = yc & 7;
+        const int scroffsy = y * C64_SCR_CH_WIDTH;
+        const int bmoffsy = y * C64_SCR_WIDTH;
+        int xc;
+
+        for (xc = 0; xc < screen->width / 2; xc++)
+        {
+            const int x = xc / 4;
+            const int scroffs = scroffsy + x;
+            const int b = img->bitmap[0][bmoffsy + (x * 8) + yb];
+            const int v = 6 - ((xc * 2) & 6);
+            Uint8 c;
+
+            switch ((b >> v) & 3)
+            {
+                case 0: c = img->bgcolor; break;
+                case 1: c = img->screen[0][scroffs] >> 4; break;
+                case 2: c = img->screen[0][scroffs] & 15; break;
+                case 3: c = img->color[0][scroffs] & 15; break;
+            }
+            
+            *d++ = c;
+            *d++ = c;
+        }
+
+        dp += screen->pitch;
+    }
+    
+    return 0;
+}
+#endif
+
+
+int dmRemapImageColors(DMImage *image)
+{
+    DMColor *npal = dmCalloc(image->ncolors, sizeof(DMColor));
+    int  *mapping = dmMalloc(image->ncolors * sizeof(int));
+    BOOL *mapped  = dmMalloc(image->ncolors * sizeof(BOOL));
+    BOOL *used    = dmMalloc(image->ncolors * sizeof(BOOL));
+    int n, index, xc, yc, ncolors;
+
+    dmMsg(1, "Remapping %d output image colors of %d colors.\n", optNRemapTable, image->ncolors);
+
+    if (npal == NULL || mapping == NULL || mapped == NULL || used == NULL)
+    {
+        dmError("Could not allocate memory for reused palette.\n");
+        return DMERR_MALLOC;
+    }
+
+    for (index = 0; index < image->ncolors; index++)
+    {
+        mapping[index] = -1;
+        mapped[index] = used[index] = FALSE;
+    }
+
+    // Find used colors
+    dmMsg(2, "Scanning image for used colors...\n");
+    for (ncolors = yc = 0; yc < image->height; yc++)
+    {
+        Uint8 *dp = image->data + image->pitch * yc;
+        for (xc = 0; xc < image->width; xc++)
+        {
+            Uint8 col = dp[xc];
+            if (col < image->ncolors && !used[col])
+            {
+                used[col] = TRUE;
+                ncolors++;
+            }
+        }
+    }
+    dmMsg(2, "Found %d used colors, creating remap-table.\n", ncolors);
+
+    // Match and mark mapped colors
+    for (index = 0; index < optNRemapTable; index++)
+    {
+        DMMapValue *map = &optRemapTable[index];
+        if (map->triplet)
+        {
+            BOOL found = FALSE;
+            for (n = 0; n < image->ncolors; n++)
+            {
+                if (dmCompareColor(&(image->pal[n]), &(map->color), map->alpha))
+                {
+                    dmMsg(3, "RGBA match #%02x%02x%02x%02x: %d -> %d\n",
+                        map->color.r, map->color.g, map->color.b, map->color.a,
+                        n,
+                        map->to);
+
+                    mapping[n] = map->to;
+                    mapped[map->to] = TRUE;
+                    found = TRUE;
+                }
+            }
+            
+            if (!found)
+            {
+                dmMsg(3, "No RGBA match found for map index %d, #%02x%02x%02x%02x\n",
+                    index,
+                    map->color.r, map->color.g, map->color.b, map->color.a);
+            }
+        }
+        else
+        {
+            dmMsg(3, "Map index: %d -> %d\n",
+                map->from, map->to);
+
+            mapping[map->from] = map->to;
+            mapped[map->to] = TRUE;
+        }
+    }
+
+    
+    // Fill in the rest
+    if (optRemapRemove)
+    {
+        dmMsg(2, "Removing unused colors.\n");
+        for (index = 0; index < image->ncolors; index++)
+        if (mapping[index] < 0 && used[index])
+        {
+            for (n = 0; n < image->ncolors; n++)
+            if (!mapped[n])
+            {
+                mapping[index] = n;
+                mapped[n] = TRUE;
+                break;
+            }
+        }
+    }
+    else
+    {
+        for (index = 0; index < image->ncolors; index++)
+        if (mapping[index] < 0)
+        {
+            for (n = 0; n < image->ncolors; n++)
+            if (!mapped[n])
+            {
+                mapping[index] = n;
+                mapped[n] = TRUE;
+                break;
+            }
+        }
+    }
+
+    // Calculate final number of palette colors
+    ncolors = 0;
+    for (index = 0; index < image->ncolors; index++)
+    {
+        if (mapping[index] + 1 > ncolors)
+            ncolors = mapping[index] + 1;
+    }
+    
+    // Copy palette entries
+    for (index = 0; index < image->ncolors; index++)
+    {
+        if (mapping[index] >= 0)
+        {
+            memcpy(&npal[mapping[index]], &(image->pal[index]), sizeof(DMColor));
+        }
+    }
+
+    // Remap image
+    dmMsg(1, "Remapping image to %d colors...\n", ncolors);
+    for (yc = 0; yc < image->height; yc++)
+    {
+        Uint8 *dp = image->data + image->pitch * yc;
+        for (xc = 0; xc < image->width; xc++)
+        {
+            Uint8 col = dp[xc];
+            if (col < image->ncolors && mapping[col] >= 0 && mapping[col] < image->ncolors)
+                dp[xc] = mapping[col];
+            else
+                dp[xc] = 0;
+        }
+    }
+
+    // Set new palette, free memory
+    dmFree(image->pal);
+    image->pal = npal;
+    image->ncolors = ncolors;
+
+    dmFree(mapping);
+    dmFree(mapped);
+    dmFree(used);
+    return DMERR_OK;
+}
+
+
+int dmWriteBitmap(const char *filename, DMC64Image *image, int iformat, BOOL enableFixUps)
+{
+    FILE *outFile = NULL;
+    Uint8 *buf = NULL;
+    size_t bufSize;
+    int res = DMERR_OK;
+    const DMC64ImageFormat *fmt = &dmC64ImageFormats[iformat];
+
+    dmMsg(1, "Converting to %s format bitmap.\n", fmt->name);
+    if (image->type != fmt->type && enableFixUps)
+    {
+        // Try to do some simple fixups
+        if ((fmt->type & D64_FMT_FLI) && (image->type & D64_FMT_FLI) == 0)
+        {
+            dmMsg(1, "Upconverting multicolor to FLI.\n");
+            int i;
+            for (i = 1; i < C64_SCR_MAX_BANK; i++)
+            {
+                memcpy(image->color[i], image->color[0], C64_SCR_COLOR_SIZE);
+                memcpy(image->screen[i], image->screen[0], C64_SCR_SCREEN_SIZE);
+            }
+        }
+    }
+
+
+    if ((res = dmC64EncodeGenericBMP(&buf, &bufSize, image, fmt)) != DMERR_OK)
+        goto error;
+
+    dmMsg(2, "Result: %d bytes\n", bufSize);
+
+    if ((outFile = fopen(filename, "wb")) == NULL)
+    {
+        res = dmGetErrno();
+        dmError("Error opening output file '%s', %d: %s\n",
+            filename, res, dmErrorStr(res));
+        goto error;
+    }
+
+    if (!dm_fwrite_str(outFile, buf, bufSize))
+    {
+        res = dmGetErrno();
+        dmError("Error writing image data to '%s', %d: %s\n",
+            filename, res, dmErrorStr(res));
+    }
+
+error:
+    if (outFile != NULL)
+        fclose(outFile);
+    dmFree(buf);
+    return res;
+}
+
+
+int dmWriteImage(const char *filename, DMImage *image, DMImageSpec *spec, int iformat, BOOL info)
+{
+    if (info)
+    {
+        dmMsg(1, "Outputting %s image %d x %d -> %d x %d [%d]\n",
+            dmImageFormatList[iformat].fext,
+            image->width, image->height,
+            image->width * spec->scale, image->height * spec->scale,
+            spec->scale);
+    }
+
+    // Perform color remapping
+    if (optRemapColors)
+    {
+        int res;
+        if ((res = dmRemapImageColors(image)) != DMERR_OK)
+            return res;
+    }
+
+    switch (iformat)
+    {
+#ifdef DM_USE_LIBPNG
+        case IMGFMT_PNG:
+            if (info) dmMsg(2, "%s output.\n", spec->paletted ? "Indexed 8bpp" : "32bit RGBA");
+            spec->format = spec->paletted ? DM_IFMT_PALETTE : DM_IFMT_RGBA;
+            return dmWritePNGImage(filename, image, spec);
+#endif
+
+        case IMGFMT_PPM:
+            if (info) dmMsg(2, "24bit RGB output.\n");
+            spec->format = DM_IFMT_RGB;
+            return dmWritePPMImage(filename, image, spec);
+
+        case IMGFMT_PCX:
+            if (info) dmMsg(2, "%s output.\n", spec->paletted ? "Indexed 8bpp" : "24bit RGB");
+            return dmWritePCXImage(filename, image, spec);
+
+        case IMGFMT_RAW:
+        case IMGFMT_ARAW:
+            {
+                FILE *fp;
+                char *dataFilename, *fext, *tmpFilename = dm_strdup(filename);
+                
+                // Form data file filename
+                if (tmpFilename == NULL)
+                    return DMERR_MALLOC;
+
+                fext = strrchr(tmpFilename, '.');
+                if (fext != NULL)
+                    *fext = 0;
+                dataFilename = dm_strdup_printf("%s.inc", tmpFilename);
+                dmFree(tmpFilename);
+
+                // Open data file for writing
+                if ((fp = fopen(dataFilename, "w")) == NULL)
+                    dmError("Could not create '%s'.\n", dataFilename);
+                dmFree(dataFilename);
+
+                if (fp != NULL)
+                {
+                    // Strip extension
+                    int i;
+                    char *palID = dm_strdup_printf("img_%s", filename);
+                    char *fext = strrchr(palID, '.');
+                    if (fext != NULL)
+                        *fext = 0;
+
+                    // Replace any non-alphanumerics
+                    for (i = 0; palID[i]; i++)
+                    {
+                        if (isalnum(palID[i]))
+                            palID[i] = tolower(palID[i]);
+                        else
+                            palID[i] = '_';
+                    }
+
+                    if (iformat == IMGFMT_ARAW)
+                    {
+                        fprintf(fp,
+                        "%s_width: dw.w %d\n"
+                        "%s_height: dw.w %d\n"
+                        "%s_nplanes: dw.w %d\n"
+                        "%s_ncolors: dw.w %d\n"
+                        "%s_palette:\n",
+                        palID, image->width,
+                        palID, image->height,
+                        palID, spec->nplanes,
+                        palID, image->ncolors,
+                        palID);
+
+                        dmWriteIFFMasterRAWPalette(fp, image, 1 << optSpec.nplanes, NULL, NULL);
+
+                        fprintf(fp,
+                        "%s: incbin \"%s\"\n",
+                        palID, filename);
+                    }
+                    else
+                    {
+                        fprintf(fp,
+                        "%s_width: dw.w %d\n"
+                        "%s_height: dw.w %d\n"
+                        "%s_nplanes: dw.w %d\n",
+                        palID, image->width,
+                        palID, image->height,
+                        palID, spec->nplanes);
+                    }
+
+                    fclose(fp);
+                    dmFree(palID);
+                }
+
+                if (info) dmMsg(2, "%d bitplanes, %s interleave.\n", spec->nplanes, spec->interleave ? "with" : "without");
+                return dmWriteRAWImage(filename, image, spec);
+            }
+            break;
+
+        default:
+            return DMERR_INVALID_DATA;
+    }
+}
+
+
+static Uint8 dmConvertByte(const Uint8 *sp, const BOOL multicolor)
+{
+    Uint8 byte = 0;
+    int xc;
+
+    if (multicolor)
+    {
+        for (xc = 0; xc < 8 / 2; xc++)
+        {
+            Uint8 pixel = sp[xc * 2] & 3;
+            byte |= pixel << (6 - (xc * 2));
+        }
+    }
+    else
+    {
+        for (xc = 0; xc < 8; xc++)
+        {
+            Uint8 pixel = sp[xc] == 0 ? 0 : 1;
+            byte |= pixel << (7 - xc);
+        }
+    }
+
+    return byte;
+}
+
+
+BOOL dmConvertImage2Char(Uint8 *buf, const DMImage *image,
+    const int xoffs, const int yoffs, const BOOL multicolor)
+{
+    int yc;
+
+    if (xoffs < 0 || yoffs < 0 ||
+        xoffs + C64_CHR_WIDTH_PX > image->width ||
+        yoffs + C64_CHR_HEIGHT > image->height)
+        return FALSE;
+
+    for (yc = 0; yc < C64_CHR_HEIGHT; yc++)
+    {
+        const Uint8 *sp = image->data + ((yc + yoffs) * image->pitch) + xoffs;
+        buf[yc] = dmConvertByte(sp, multicolor);
+    }
+
+    return TRUE;
+}
+
+
+BOOL dmConvertImage2Sprite(Uint8 *buf, const DMImage *image,
+    const int xoffs, const int yoffs, const BOOL multicolor)
+{
+    int yc, xc;
+
+    if (xoffs < 0 || yoffs < 0 ||
+        xoffs + C64_SPR_WIDTH_PX > image->width ||
+        yoffs + C64_SPR_HEIGHT > image->height)
+        return FALSE;
+
+    for (yc = 0; yc < C64_SPR_HEIGHT; yc++)
+    {
+        for (xc = 0; xc < C64_SPR_WIDTH_PX / C64_SPR_WIDTH; xc++)
+        {
+            const Uint8 *sp = image->data + ((yc + yoffs) * image->pitch) + (xc * 8) + xoffs;
+            buf[(yc * C64_SPR_WIDTH) + xc] = dmConvertByte(sp, multicolor);
+        }
+    }
+
+    return TRUE;
+}
+
+
+int dmWriteSpritesAndChars(const char *filename, DMImage *image, int outFormat, BOOL multicolor)
+{
+    int outBlockW, outBlockH, bx, by;
+    FILE *outFile = NULL;
+    Uint8 *buf = NULL;
+    size_t bufSize;
+    char *outType;
+
+    switch (outFormat)
+    {
+        case FFMT_CHAR:
+            bufSize = C64_CHR_SIZE;
+            outBlockW = image->width / C64_CHR_WIDTH_PX;
+            outBlockH = image->height / C64_CHR_HEIGHT;
+            outType = "char";
+            break;
+
+        case FFMT_SPRITE:
+            bufSize = C64_SPR_SIZE;
+            outBlockW = image->width / C64_SPR_WIDTH_PX;
+            outBlockH = image->height / C64_SPR_HEIGHT;
+            outType = "sprite";
+            break;
+
+        default:
+            dmError("Invalid output format %d, internal error.\n", outFormat);
+            goto error;
+    }
+
+    if (outBlockW <= 0 || outBlockH <= 0)
+    {
+        dmError("Source image dimensions too small for conversion, block dimensions %d x %d.\n",
+            outBlockW, outBlockH);
+        goto error;
+    }
+
+    if ((outFile = fopen(filename, "wb")) == NULL)
+    {
+        int err = dmGetErrno();
+        dmError("Could not open '%s' for writing, %d: %s.\n",
+            filename, err, dmErrorStr(err));
+        goto error;
+    }
+
+    if ((buf = dmMalloc(bufSize)) == NULL)
+    {
+        dmError("Could not allocate %d bytes for conversion buffer.\n",
+            bufSize);
+        goto error;
+    }
+
+    dmMsg(1, "Writing %d x %d = %d blocks of %s data...\n",
+        outBlockW, outBlockH, outBlockW * outBlockH, outType);
+
+    for (by = 0; by < outBlockH; by++)
+    for (bx = 0; bx < outBlockW; bx++)
+    {
+        switch (outFormat)
+        {
+            case FFMT_CHAR:
+                if (!dmConvertImage2Char(buf, image,
+                    bx * C64_CHR_WIDTH_PX, by * C64_CHR_HEIGHT,
+                    multicolor))
+                    goto error;
+                break;
+
+            case FFMT_SPRITE:
+                if (!dmConvertImage2Sprite(buf, image,
+                    bx * C64_SPR_WIDTH_PX, by * C64_SPR_HEIGHT,
+                    multicolor))
+                    goto error;
+        }
+
+        if (!dm_fwrite_str(outFile, buf, bufSize))
+        {
+            int err = dmGetErrno();
+            dmError("Error writing data block %d,%d to '%s', %d: %s\n",
+                bx, by, filename, err, dmErrorStr(err));
+            goto error;
+        }
+    }
+
+    fclose(outFile);
+    dmFree(buf);
+    return 0;
+
+error:
+    if (outFile != NULL)
+        fclose(outFile);
+    dmFree(buf);
+    return -1;
+}
+
+
+int dmDumpSpritesAndChars(FILE *inFile)
+{
+    int dataOffs, itemCount, outWidth, outWidthPX, outHeight;
+    size_t bufSize;
+    Uint8 *bufData;
+
+    switch (optInFormat)
+    {
+        case FFMT_CHAR:
+            bufSize = C64_CHR_SIZE;
+            outWidth = C64_CHR_WIDTH;
+            outWidthPX = C64_CHR_WIDTH_PX;
+            outHeight = C64_CHR_HEIGHT;
+            break;
+
+        case FFMT_SPRITE:
+            bufSize = C64_SPR_SIZE;
+            outWidth = C64_SPR_WIDTH;
+            outWidthPX = C64_SPR_WIDTH_PX;
+            outHeight = C64_SPR_HEIGHT;
+            break;
+
+        default:
+            dmError("Invalid input format %d, internal error.\n", optInFormat);
+            return -1;
+    }
+
+    if ((bufData = dmMalloc(bufSize)) == NULL)
+    {
+        dmError("Could not allocate temporary buffer of %d bytes.\n", bufSize);
+        return -2;
+    }
+
+
+    dataOffs = optInSkip;
+    itemCount = 0;
+
+    if (optOutFormat == FFMT_ANSI || optOutFormat == FFMT_ASCII)
+    {
+        BOOL error = FALSE;
+        FILE *outFile;
+
+        if (optOutFilename == NULL)
+            outFile = stdout;
+        else
+        if ((outFile = fopen(optOutFilename, "w")) == NULL)
+        {
+            int res = dmGetErrno();
+            dmError("Error opening output file '%s', %d: %s\n",
+                  optOutFilename, res, dmErrorStr(res));
+            goto error;
+        }
+
+        while (!feof(inFile) && !error && (optItemCount < 0 || itemCount < optItemCount))
+        {
+            memset(bufData, 0, bufSize);
+
+            if (fread(bufData, 1, bufSize, inFile) != bufSize)
+            {
+                dmError("Could not read full bufferful (%d bytes) of data at 0x%x.\n",
+                    bufSize, dataOffs);
+                error = TRUE;
+            }
+            
+            fprintf(outFile, "---- : -------------- #%d\n", itemCount);
+
+            switch (optInFormat)
+            {
+                case FFMT_CHAR:
+                    dmDumpCharASCII(outFile, bufData, &dataOffs, optOutFormat, optInMulticolor);
+                    break;
+                case FFMT_SPRITE:
+                    dmDumpSpriteASCII(outFile, bufData, &dataOffs, optOutFormat, optInMulticolor);
+                    break;
+            }
+            itemCount++;
+        }
+
+        fclose(outFile);
+    }
+    else
+    if (optOutFormat == FFMT_IMAGE)
+    {
+        DMImage *outImage = NULL;
+        char *outFilename = NULL;
+        int outX = 0, outY = 0, err;
+
+        if (optSequential)
+        {
+            if (optOutFilename == NULL)
+            {
+                dmError("Sequential image output requires filename template.\n");
+                goto error;
+            }
+
+            outImage = dmImageAlloc(outWidthPX, outHeight);
+            dmMsg(1, "Outputting sequence of %d images @ %d x %d -> %d x %d.\n",
+                optItemCount,
+                outImage->width, outImage->height,
+                outImage->width * optSpec.scale, outImage->height * optSpec.scale);
+        }
+        else
+        {
+            int outIWidth, outIHeight;
+            if (optItemCount <= 0)
+            {
+                dmError("Single-image output requires count to be set (-n).\n");
+                goto error;
+            }
+            
+            outIWidth = optPlanedWidth;
+            outIHeight = (optItemCount / optPlanedWidth);
+            if (optItemCount % optPlanedWidth)
+                outIHeight++;
+            
+            outImage = dmImageAlloc(outWidthPX * outIWidth, outIHeight * outHeight);
+        }
+
+        outImage->constpal = TRUE;
+        outImage->pal      = dmC64Palette;
+        outImage->ncolors  = C64_NCOLORS;
+        outImage->ctransp  = 255;
+        
+        while (!feof(inFile) && (optItemCount < 0 || itemCount < optItemCount))
+        {
+            memset(bufData, 0, bufSize);
+
+            if (fread(bufData, 1, bufSize, inFile) != bufSize)
+            {
+                dmError("Could not read full bufferful (%d bytes) of data at 0x%x.\n",
+                    bufSize, dataOffs);
+                break;
+            }
+
+            if ((err = dmC64ConvertCSData(outImage, outX * outWidthPX, outY * outHeight,
+                bufData, outWidth, outHeight, optInMulticolor, optColors)) != DMERR_OK)
+            {
+                dmError("Internal error in conversion of raw data to bitmap: %d.\n", err);
+                break;
+            }
+
+            if (optSequential)
+            {
+                outFilename = dm_strdup_printf("%s%04d.%s", optOutFilename, itemCount, convFormatList[optOutFormat].fext);
+                if (outFilename == NULL)
+                {
+                    dmError("Could not allocate memory for filename template?\n");
+                    goto error;
+                }
+                
+                dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE);
+                dmFree(outFilename);
+            }
+            else
+            {
+                if (++outX >= optPlanedWidth)
+                {
+                    outX = 0;
+                    outY++;
+                }
+            }
+            
+            itemCount++;
+        }
+
+        if (!optSequential)
+        {
+            dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE);
+        }
+        
+        dmImageFree(outImage);
+    }
+    else
+    if (optOutFormat == FFMT_BITMAP)
+    {
+        if (optSequential)
+        {
+            dmError("Sequential output not supported for spr/char -> bitmap conversion.\n");
+            goto error;
+        }
+    }
+
+    dmFree(bufData);
+    return 0;
+
+error:
+    dmFree(bufData);
+    return -1;
+}
+
+
+int main(int argc, char *argv[])
+{
+    FILE *inFile;
+    const DMC64ImageFormat *cfmt;
+    DMC64Image cimage;
+    Uint8 *dataBuf = NULL;
+    size_t dataSize;
+    int i;
+
+    // Default colors
+    for (i = 0; i < C64_MAX_COLORS; i++)
+        optColors[i] = i;
+
+    // Initialize and parse commandline
+    dmInitProg("gfxconv", "Simple graphics converter", "0.75", NULL, NULL);
+
+    if (!dmArgsProcess(argc, argv, optList, optListN,
+        argHandleOpt, argHandleFile, TRUE))
+        exit(1);
+
+#ifndef DM_USE_LIBPNG
+    if (optOutFormat == IMGFMT_PNG)
+    {
+        dmError("PNG output format support not compiled in, sorry.\n");
+        goto error;
+    }
+#endif
+
+    // Determine input format, if not specified'
+    if (optInFormat == FFMT_AUTO && optInFilename != NULL)
+    {
+        char *dext = strrchr(optInFilename, '.');
+        dmMsg(4, "Trying to determine file format by extension.\n");
+        if (dext)
+        {
+            dmGetFormatByExt(dext + 1, &optInFormat, &optInSubFormat);
+        }
+    }
+
+    if (optInFilename == NULL)
+    {
+        if (optInFormat == FFMT_AUTO)
+        {
+            dmError("Standard input cannot be used without specifying input format.\n");
+            dmError("Perhaps you should try using --help\n");
+            goto error;
+        }
+        inFile = stdin;
+    }
+    else
+    if ((inFile = fopen(optInFilename, "rb")) == NULL)
+    {
+        int res = dmGetErrno();
+        dmError("Error opening input file '%s', %d: %s\n",
+              optInFilename, res, dmErrorStr(res));
+        goto error;
+    }
+
+    if (dmReadDataFile(inFile, NULL, &dataBuf, &dataSize) != 0)
+        goto error;
+
+    if (optInFormat == FFMT_AUTO || optInFormat == FFMT_BITMAP)
+    {
+        // Probe for format
+        const DMC64ImageFormat *forced = NULL;
+        int res;
+
+        if (optForcedFormat >= 0)
+        {
+            forced = &dmC64ImageFormats[optForcedFormat];
+            dmMsg(0,"Forced %s format image, type %d, %s\n",
+                forced->name, forced->type, forced->fext);
+        }
+
+        res = dmC64DecodeBMP(&cimage, dataBuf, dataSize, optInSkip, optInSkip + 2, &cfmt, forced);
+        if (forced == NULL && cfmt != NULL)
+        {
+            dmMsg(1,"Probed %s format image, type %d, %s\n",
+                cfmt->name, cfmt->type, cfmt->fext);
+        }
+        
+        if (res == 0)
+            optInFormat = FFMT_BITMAP;
+    }
+
+    if (optInFormat == FFMT_AUTO || optInFormat == FFMT_IMAGE)
+    {
+        DMImageFormat *ifmt = NULL;
+        int index;
+        dmMsg(4, "Trying to probe image formats.\n");
+        if (dmImageProbeGeneric(dataBuf + optInSkip, dataSize - optInSkip, &ifmt, &index) > 0)
+        {
+            optInFormat = FFMT_IMAGE;
+            optInSubFormat = index;
+            dmMsg(2, "Probed %s format image.\n", ifmt->fext);
+        }
+    }
+
+    if (optInFormat == FFMT_AUTO)
+    {
+        dmError("No input format specified, and could not be determined automatically.\n");
+        exit(1);
+    }
+
+    // Skip, if needed
+    if (fseek(inFile, optInSkip, SEEK_SET) != 0)
+    {
+        int res = dmGetErrno();
+        dmError("Could not seek to file position %d (0x%x): %s\n",
+            optInSkip, optInSkip, dmErrorStr(res));
+        goto error;
+    }
+    
+    int inFormat = dmGetConvFormat(optInFormat, optInSubFormat),
+        outFormat = dmGetConvFormat(optOutFormat, optOutSubFormat);
+    
+    if (inFormat != -1 && outFormat != -1)
+    {
+        char *inFmtName = convFormatList[inFormat].name,
+             *inFmtExt = convFormatList[inFormat].fext,
+             *outFmtName = convFormatList[outFormat].name,
+             *outFmtExt = convFormatList[outFormat].fext;
+
+        if (optInFormat == FFMT_BITMAP)
+            inFmtExt = cfmt->name;
+
+        dmMsg(1, "Attempting conversion %s (%s) -> %s (%s)\n",
+            inFmtName, inFmtExt, outFmtName, outFmtExt);
+    }
+
+    switch (optInFormat)
+    {
+        case FFMT_SPRITE:
+        case FFMT_CHAR:
+            dmDumpSpritesAndChars(inFile);
+            break;
+        
+        case FFMT_BITMAP:
+            {
+                DMImage *outImage = NULL;
+                int res = DMERR_OK;
+
+                if (optOutFilename == NULL)
+                {
+                    dmError("Output filename not set, required for image formats.\n");
+                    goto error;
+                }
+
+                switch (optOutFormat)
+                {
+                    case FFMT_IMAGE:
+                        res = dmC64ConvertBMP2Image(&outImage, &cimage, cfmt, TRUE);
+
+                        if (res != DMERR_OK || outImage == NULL)
+                        {
+                            dmError("Error in bitmap to image conversion.\n");
+                            goto error;
+                        }
+
+                        res = dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE);
+                        break;
+
+
+                    case FFMT_BITMAP:
+                        res = dmWriteBitmap(optOutFilename, &cimage, optOutSubFormat, TRUE);
+                        break;
+
+                    case FFMT_CHAR:
+                    case FFMT_SPRITE:
+                        res = dmWriteSpritesAndChars(optOutFilename, outImage, optOutFormat, optInMulticolor);
+                        break;
+
+                    default:
+                        dmError("Unsupported output format for bitmap/image conversion.\n");
+                        break;
+                }
+                
+                dmImageFree(outImage);
+            }
+            break;
+
+        case FFMT_IMAGE:
+            {
+                DMImage *outImage = NULL;
+                int res = DMERR_OK;
+
+                if (optOutFilename == NULL)
+                {
+                    dmError("Output filename not set, required for image formats.\n");
+                    goto error;
+                }
+
+                // Read input
+                DMImageFormat *ifmt = &dmImageFormatList[optInSubFormat];
+                if (ifmt->readFILE != NULL)
+                    res = ifmt->readFILE(inFile, &outImage);
+                else
+                    dmError("Unsupported input image format for bitmap/image conversion.\n");
+
+                if (res != DMERR_OK || outImage == NULL)
+                    break;
+                
+                switch (optOutFormat)
+                {
+                    case FFMT_IMAGE:
+                        res = dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE);
+                        break;
+
+                    case FFMT_CHAR:
+                    case FFMT_SPRITE:
+                        res = dmWriteSpritesAndChars(optOutFilename, outImage, optOutFormat, optInMulticolor);
+                        break;
+
+                    default:
+                        dmError("Unsupported output format for bitmap/image conversion.\n");
+                        break;
+                }
+                
+                dmImageFree(outImage);
+            }
+            break;
+    }
+
+    fclose(inFile);
+
+    dmFree(dataBuf);
+    exit(0);
+    return 0;
+
+error:
+    dmFree(dataBuf);
+    return -3;
+    exit(3);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utils/mod2wav.c	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,314 @@
+/*
+ * mod2wav - Render XM/JSSMOD module to WAV waveform file
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2007 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "jss.h"
+#include "jssmod.h"
+#include "jssmix.h"
+#include "jssplr.h"
+#include "dmlib.h"
+#include "dmargs.h"
+#include "dmwav.h"
+#include "dmmutex.h"
+
+
+char    *optInFilename = NULL, *optOutFilename = NULL;
+int     optOutFormat = JSS_AUDIO_S16,
+        optOutChannels = 2,
+        optOutFreq = 44100,
+        optMuteOChannels = -1,
+        optStartOrder = -1;
+BOOL    optUsePlayTime = FALSE;
+size_t  optPlayTime;
+
+
+DMOptArg optList[] =
+{
+    {  0, '?', "help",     "Show this help", OPT_NONE },
+    {  2, 'v', "verbose",  "Be more verbose", OPT_NONE },
+    {  3, '1', "16bit",    "16-bit output", OPT_NONE },
+    {  4, '8', "8bit",     "8-bit output", OPT_NONE },
+    {  5, 'm', "mono",     "Mono output", OPT_NONE },
+    {  6, 's', "stereo",   "Stereo output", OPT_NONE },
+    {  7, 'f', "freq",     "Output frequency", OPT_ARGREQ },
+    {  8, 'M', "mute",     "Mute other channels than #", OPT_ARGREQ },
+    {  9, 'o', "order",    "Start from order #", OPT_ARGREQ },
+    { 10, 't', "time",     "Play for # seconds", OPT_ARGREQ },
+//    {10, 'l', "loop",    "Loop for # times", OPT_ARGREQ },
+};
+
+const int optListN = sizeof(optList) / sizeof(optList[0]);
+
+
+BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
+{
+    (void) optArg;
+    
+    switch (optN)
+    {
+        case 0:
+            dmPrintBanner(stdout, dmProgName,
+                "[options] [sourcefile] [destfile]");
+                
+            dmArgsPrintHelp(stdout, optList, optListN);
+            exit(0);
+            break;
+
+        case 2:
+            dmVerbosity++;
+            break;
+        
+        case 3:
+            optOutFormat = JSS_AUDIO_S16;
+            break;
+
+        case 4:
+            optOutFormat = JSS_AUDIO_U8;
+            break;
+
+        case 5:
+            optOutChannels = JSS_AUDIO_MONO;
+            break;
+
+        case 6:
+            optOutChannels = JSS_AUDIO_STEREO;
+            break;
+
+        case 7:
+            optOutFreq = atoi(optArg);
+            break;
+
+        case 8:
+            optMuteOChannels = atoi(optArg);
+            break;
+
+        case 9:
+            optStartOrder = atoi(optArg);
+            break;
+
+        case 10:
+            optPlayTime = atoi(optArg);
+            optUsePlayTime = TRUE;
+            break;
+
+        default:
+            dmError("Unknown argument '%s'.\n", currArg);
+            return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+BOOL argHandleFile(char *currArg)
+{
+    if (!optInFilename)
+        optInFilename = currArg;
+    else
+    if (!optOutFilename)
+        optOutFilename = currArg;
+    else
+    {
+        dmError("Too many filename arguments (only source and dest needed) '%s'\n", currArg);
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+int main(int argc, char *argv[])
+{
+    DMResource *inFile = NULL;
+    FILE *outFile = NULL;
+    JSSModule *mod = NULL;
+    JSSMixer *dev = NULL;
+    JSSPlayer *plr = NULL;
+    int result = -1;
+    size_t bufLen = 1024*4, dataTotal, dataWritten, sampSize;
+    Uint8 *mb = NULL;
+
+    dmInitProg("mod2wav", "XM/JSSMOD to WAV renderer", "0.2", NULL, NULL);
+    dmVerbosity = 1;
+
+    // Parse arguments
+    if (!dmArgsProcess(argc, argv, optList, optListN,
+        argHandleOpt, argHandleFile, TRUE))
+        exit(1);
+
+    // Check arguments
+    if (optInFilename == NULL || optOutFilename == NULL)
+    {
+        dmError("Input or output file not specified. Try --help.\n");
+        return 1;
+    }
+    
+    // Initialize miniJSS
+    jssInit();
+    
+    // Open the source file
+    if ((inFile = dmf_create_stdio(optInFilename, "rb")) == NULL)
+    {
+        dmError("Error opening input file '%s', %d: %s\n",
+            optInFilename, errno, strerror(errno));
+        return 1;
+    }
+
+    // Read module file
+    fprintf(stderr, "Reading file: %s\n", optInFilename);
+#ifdef JSS_SUP_XM
+    fprintf(stderr, "* Trying XM...\n");
+    result = jssLoadXM(inFile, &mod);
+#endif
+#ifdef JSS_SUP_JSSMOD
+    if (result != 0)
+    {
+        size_t bufgot, bufsize = dmfsize(inFile);
+        Uint8 *buf = dmMalloc(bufsize);
+        dmfseek(inFile, 0L, SEEK_SET);
+        fprintf(stderr, "* Trying JSSMOD (%d bytes, %p)...\n", bufsize, buf);
+        if ((bufgot = dmfread(buf, 1, bufsize, inFile)) != bufsize)
+        {
+            fprintf(stderr, "Error reading file (not enough data %d), #%d: %s\n",
+                bufgot, dmferror(inFile), dmErrorStr(dmferror(inFile)));
+            return 2;
+        }
+        result = jssLoadJSSMOD(buf, bufsize, &mod);
+        dmFree(buf);
+    }
+#endif
+    dmf_close(inFile);
+    if (result != DMERR_OK)
+    {
+        dmError("Error loading module file, %d: %s\n",
+            result, dmErrorStr(result));
+        return 3;
+    }
+
+    // Try to convert it
+    if ((result = jssConvertModuleForPlaying(mod)) != DMERR_OK)
+    {
+        dmError("Could not convert module for playing, %d: %s\n",
+            result, dmErrorStr(result));
+        return 3;
+    }
+
+    // Open mixer
+    dev = jvmInit(optOutFormat, optOutChannels, optOutFreq, JMIX_AUTO);
+    if (dev == NULL)
+    {
+        dmError("jvmInit() returned NULL\n");
+        return 4;
+    }
+
+    sampSize = jvmGetSampleSize(dev);
+    if ((mb = dmMalloc(bufLen * sampSize)) == NULL)
+    {
+        dmError("Could not allocate mixing buffer\n");
+        return 5;
+    }
+    
+    dmMsg(1, "Using fmt=%d, bits=%d, channels=%d, freq=%d [%d / sample]\n",
+        optOutFormat, jvmGetSampleRes(dev), optOutChannels, optOutFreq,
+        sampSize);
+    
+    // Initialize player
+    if ((plr = jmpInit(dev)) == NULL)
+    {
+        dmError("jmpInit() returned NULL.\n");
+        return 6;
+    }
+    
+    // Set callback
+    jvmSetCallback(dev, jmpExec, plr);
+    
+    // Initialize playing
+    jmpSetModule(plr, mod);
+    if (optStartOrder >= 0)
+    { 
+        dmMsg(1, "Starting from song order #%d\n", optStartOrder);
+    } else
+        optStartOrder = 0;
+
+    jmpPlayOrder(plr, optStartOrder);
+    jvmSetGlobalVol(dev, 150);
+    
+    if (optMuteOChannels > 0 && optMuteOChannels <= mod->nchannels)
+    {
+        int i;
+        for (i = 0; i < mod->nchannels; i++)
+            jvmMute(dev, i, TRUE);
+        jvmMute(dev, optMuteOChannels - 1, FALSE);
+    }
+    
+    // Open output file
+    if ((outFile = fopen(optOutFilename, "wb")) == NULL)
+    {
+        dmError("Error opening output file '%s'. (%s)\n", optInFilename, strerror(errno));
+        return 7;
+    }
+
+    // Write initial header
+    dmWriteWAVHeader(outFile, jvmGetSampleRes(dev), optOutFreq, optOutChannels, 1024);
+
+    // Render audio data and output to file
+    if (optUsePlayTime)
+        dmMsg(1, "Rendering module (%d seconds) ...\n", optPlayTime);
+    else
+        dmMsg(1, "Rendering module ...\n");
+    
+    optPlayTime *= optOutFreq;
+    dataTotal = 0;
+    dataWritten = 1;
+    while (plr->isPlaying && dataWritten > 0)
+    {
+        size_t writeLen = bufLen;
+        if (optUsePlayTime && (writeLen + dataTotal) > optPlayTime)
+            writeLen = optPlayTime - dataTotal;
+        
+        if (writeLen > 0)
+        {
+            jvmRenderAudio(dev, mb, writeLen);
+#if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
+            jssEncodeSample16((Uint16 *)mb, writeLen * optOutChannels, jsampSwapEndianess);
+#endif
+            dataWritten = fwrite(mb, sampSize, writeLen, outFile);
+            if (dataWritten < writeLen)
+            {
+                dmError("Error writing data!\n");
+                fclose(outFile);
+                return 8;
+            }
+            dataTotal += dataWritten;
+        }
+        
+        if (optUsePlayTime && dataTotal >= optPlayTime)
+            break;
+    }
+    
+    // Write the correct header
+    if (fseek(outFile, 0L, SEEK_SET) != 0)
+    {
+        dmError("Error rewinding to header position!\n");
+        return 9;
+    }
+    
+    dmWriteWAVHeader(outFile, jvmGetSampleRes(dev), optOutFreq, optOutChannels, dataTotal);
+    
+    // Done!
+    fclose(outFile);
+
+    jmpClose(plr);
+    jvmClose(dev);
+    jssFreeModule(mod);
+    jssClose();
+
+    dmMsg(1, "OK.\n");
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utils/objlink.c	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,990 @@
+/*
+ * objlink - Link files (RAW and PRG) into one PRG object
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2002-2012 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#include <errno.h>
+#include "dmlib.h"
+#include "dmargs.h"
+#include "dmfile.h"
+#include "dmmutex.h"
+
+#define MAX_FILENAMES    (128)
+#define MAX_MEMBLOCKS    (128)
+
+
+/* Typedefs
+ */
+typedef struct
+{
+    ssize_t start, end;	// Start and end address
+    int type;           // Type
+    char *name;         // Name of the block
+    int placement;
+} DMMemBlock;
+
+typedef struct
+{
+    char *name;         // Description of memory model
+    char *desc;
+    ssize_t size;        // Total addressable memory size
+    ssize_t nmemBlocks;  // Defined memory areas
+    DMMemBlock memBlocks[MAX_MEMBLOCKS];
+} DMMemModel;
+
+typedef struct
+{
+    char *filename;
+    int type;
+    int placement;
+    ssize_t addr;
+} DMSourceFile;
+
+// Source file type
+enum
+{
+    STYPE_RAW = 1,
+    STYPE_PRG,
+    STYPE_PRGA
+};
+
+// How to determine block placement / address
+enum
+{
+    PLACE_STATIC = 1,  // Already known
+    PLACE_ARGUMENT,   // Commandline argument
+    PLACE_FILE,       // From file
+};
+
+enum
+{
+    FMT_GENERIC = 1,
+    FMT_PLAIN,
+    FMT_DECIMAL
+};
+
+enum
+{
+    MTYPE_NONE = 0,
+    MTYPE_ROM,        // Hard ROM
+    MTYPE_ROM_WT,     // Write to RAM through ROM
+    MTYPE_IO,         // I/O lines
+    MTYPE_RES         // RESERVED
+};
+
+enum
+{
+    LA_NONE = -2,
+    LA_AUTO = -1
+};
+
+/* Memory models
+ */
+const DMMemModel memoryModels[] = {
+    { "C64 unrestricted", "$01 = $34", (64*1024), 0, {
+    { 0, 0, 0, NULL, 0 }
+    }},
+
+    { "C64 normal (IO+Basic+Kernal)", "$01 = $37", (64*1024), 3, {
+    { 0xA000, 0xBFFF,    MTYPE_ROM_WT,    "Basic ROM",  PLACE_STATIC },
+    { 0xD000, 0xDFFF,    MTYPE_IO,        "I/O",  PLACE_STATIC },
+    { 0xE000, 0xFFFF,    MTYPE_ROM_WT,    "Kernal ROM",  PLACE_STATIC },
+    }},
+
+    { "C64 modified (IO+Kernal)", "$01 = $36", (64*1024), 2, {
+    { 0xD000, 0xDFFF,    MTYPE_IO,        "I/O",  PLACE_STATIC },
+    { 0xE000, 0xFFFF,    MTYPE_ROM_WT,    "Kernal ROM",  PLACE_STATIC },
+    }},
+
+    { "C64 modified (IO only)", "$01 = $35", (64*1024), 1, {
+    { 0xD000, 0xDFFF,    MTYPE_IO,        "I/O",  PLACE_STATIC },
+    }},
+
+    { "C64 modified (Char+Kernal+Basic)", "$01 = $33", (64*1024), 3, {
+    { 0xA000, 0xBFFF,    MTYPE_ROM_WT,    "Basic ROM",  PLACE_STATIC },
+    { 0xD000, 0xDFFF,    MTYPE_ROM,       "Char ROM", PLACE_STATIC },
+    { 0xE000, 0xFFFF,    MTYPE_ROM_WT,    "Kernal ROM", PLACE_STATIC },
+    }},
+
+/*
+    { "C64 normal", "$01 = $37", (64*1024), 0, {
+    { 0x0000, 0x0000,    MTYPE_RAM,    "" },
+    }},
+*/
+};
+
+static const int nmemoryModels = sizeof(memoryModels) / sizeof(memoryModels[0]);
+
+
+/* Global variables
+ */
+int           nsrcFiles = 0;              // Number of source files
+DMSourceFile  srcFiles[MAX_FILENAMES];    // Source file names
+
+int        nmemBlocks = 0;
+DMMemBlock memBlocks[MAX_FILENAMES];
+
+char       *optLinkFileName = NULL;
+int        optLinkFileFormat = FMT_GENERIC;
+
+BOOL       optDescribe = FALSE,
+           optAllowOverlap = FALSE;
+
+Uint32     optInitValue = 0;
+int        optInitValueType = 1;
+ssize_t    optCropStart, optCropEnd;
+BOOL       optCropOutput = FALSE;
+
+ssize_t    optLoadAddress = LA_AUTO;
+
+int        optMemModel = 0;
+const DMMemModel *memModel = NULL;
+Uint8      *memory = NULL;
+
+char       *optDestName = NULL;
+
+
+/* Arguments
+ */
+static DMOptArg optList[] = {
+    {  0, '?', "help",        "Show this help", OPT_NONE },
+    {  1, 'r', "input-raw",   "RAW input: -r <file>:<addr>", OPT_ARGREQ },
+    {  2, 'p', "input-prg",   "PRG input: -p <file>[:<addr>]", OPT_ARGREQ },
+    { 12, 's', "section",     "Reserved section: -s <start>-<end>[,name] or <start>:<len>[,name]", OPT_ARGREQ },
+    {  5, 'o', "output",      "Specify output file, -o <file>", OPT_ARGREQ },
+    {  6, 'O', "overlap",     "Allow overlapping memory areas", OPT_NONE },
+    {  7, 'm', "model",       "Set memory model", OPT_ARGREQ },
+    {  8, 'l', "link-file",   "Output addresses and labels into file", OPT_ARGREQ },
+    {  9, 'f', "format",      "Format of link-file: (g)eneric, (p)lain, (d)ecimal", OPT_ARGREQ },
+    { 10, 'i', "initvalue",   "Initialize memory with: -i <byte/word/dword>:[bwd]", OPT_ARGREQ },
+    { 11, 'd', "describe",    "Output ASCII memory map description", OPT_NONE },
+    { 13, 'c', "crop",        "Crop output file to: -c <start>-<end> or <start>:<len>", OPT_ARGREQ },
+    { 14, 'L', "load-address","Set output file load address (or 'none' for 'raw' output)", OPT_ARGREQ },
+};
+
+static const int optListN = sizeof(optList) / sizeof(optList[0]);
+
+
+void argShowHelp()
+{
+    int i;
+
+    dmPrintBanner(stdout, dmProgName, "[options]");
+    dmArgsPrintHelp(stdout, optList, optListN);
+
+    printf(
+    "\n"
+    "Each numeric argument can be prefixed with $ or 0x for hexadecimal values.\n"
+    "NOTICE! -p filename:<addr> will ignore load address and use <addr> instead!\n"
+    "\n"
+    "Available memory models:\n");
+
+    for (i = 0; i < nmemoryModels; i++)
+    {
+        const DMMemModel *m = &memoryModels[i];
+        printf("  %d = %-40s [%s] (%d kB)\n",
+            i, m->name, m->desc, m->size / 1024);
+    }
+}
+
+
+off_t dmGetFileSize(FILE *f)
+{
+    off_t len, pos = ftell(f);
+    fseeko(f, 0, SEEK_END);
+    len = ftell(f);
+    fseek(f, pos, SEEK_SET);
+    return len;
+}
+
+
+/* Memory block handling
+ */
+void reserveMemBlock(ssize_t startAddr, ssize_t endAddr, const char *blockName, int blockType)
+{
+    if (startAddr > endAddr)
+    {
+        dmError("ERROR! Block '%s' has startAddr=$%.4x > endAddr=$%.4x!\n",
+            blockName, startAddr, endAddr);
+        exit(4);
+    }
+
+    if (nmemBlocks < MAX_FILENAMES)
+    {
+        memBlocks[nmemBlocks].start = startAddr;
+        memBlocks[nmemBlocks].end   = endAddr;
+        memBlocks[nmemBlocks].name  = dm_strdup(blockName);
+        memBlocks[nmemBlocks].type  = blockType;
+        nmemBlocks++;
+    }
+    else
+    {
+        dmError("Maximum number of memBlock definitions (%d) exceeded!\n",
+            MAX_FILENAMES);
+        exit(4);
+    }
+}
+
+
+int compareMemBlock(const void *cva, const void *cvb)
+{
+    const DMMemBlock *a = cva, *b = cvb;
+    return a->start - b->start;
+}
+
+
+BOOL dmParseSection(const char *arg, ssize_t *sectStart, ssize_t *sectEnd, char **sectName, BOOL canHasName)
+{
+    char sectMode, *sep, *str, *namesep;
+    ssize_t tmpi;
+
+    // Define reserved section
+    // Create a copy of the argument
+    if ((str = dm_strdup(arg)) == NULL)
+    {
+        dmError("Could not allocate temporary string!\n");
+        exit(128);
+    }
+
+    // Get start address
+    if ((sep = strchr(str, '-')) == NULL &&
+        (sep = strchr(str, ':')) == NULL)
+    {
+        dmError("Section definition '%s' invalid.\n", arg);
+        goto error;
+    }
+    sectMode = *sep;
+    *sep = 0;
+
+    // Get value
+    if (!dmGetIntVal(str, sectStart))
+    {
+        dmError("Section start address '%s' in '%s' invalid.\n", str, arg);
+        goto error;
+    }
+    
+    // Check for name
+    namesep = strchr(sep + 1, ',');
+    if (canHasName && namesep != NULL)
+    {
+        *namesep = 0;
+        namesep++;
+        if (*namesep == 0)
+        {
+            dmError("Section definition '%s' name is empty. Either specify name or leave it out.\n",
+                arg);
+            goto error;
+        }
+        *sectName = dm_strdup(namesep);
+    }
+    else
+    if (namesep != NULL)
+    {
+        dmError("Section definition does not allow a name, syntax error in '%s' at '%s'.\n",
+            arg, namesep);
+        goto error;
+    }
+
+    // Get end address or length
+    if (!dmGetIntVal(sep + 1, &tmpi))
+    {
+        dmError("Section %s '%s' in '%s' invalid.\n",
+            sectMode == '-' ? "end address" : "length",
+            sep + 1, arg);
+        goto error;
+    }
+
+    if (sectMode == ':')
+    {
+        *sectEnd = *sectStart + tmpi - 1;
+    }
+    else
+    {
+        if (tmpi < *sectStart)
+        {
+            dmError("Section start address > end address in '%s'.\n",
+                arg);
+            goto error;
+        }
+        *sectEnd = tmpi;
+    }
+
+    dmFree(str);
+    return TRUE;
+
+error:
+    dmFree(str);
+    return FALSE;
+}
+
+
+BOOL dmParseInputFile(char *arg, const int type1, const int type2, const char *desc, BOOL requireAddr)
+{
+    ssize_t tmpi = 0;
+    BOOL hasAddr = FALSE;
+    char *sep;
+
+    if ((sep = strrchr(arg, ':')) != NULL)
+    {
+        *sep = 0;
+        if (!dmGetIntVal(sep + 1, &tmpi))
+        {
+            dmError("Invalid %s address '%s' specified for '%s'.\n",
+                desc, sep + 1, arg);
+            return FALSE;
+        }
+        hasAddr = TRUE;
+    }
+    else
+    if (requireAddr)
+    {
+        dmError("No %s loading address specified for '%s'.\n", desc, arg);
+        return FALSE;
+    }
+
+    srcFiles[nsrcFiles].filename = arg;
+    srcFiles[nsrcFiles].type = hasAddr ? type1 : type2;
+    srcFiles[nsrcFiles].addr = tmpi;
+    nsrcFiles++;
+    return TRUE;
+}
+
+
+BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
+{
+    char *p;
+    ssize_t tmpi;
+
+    switch (optN) {
+    case 0:
+        argShowHelp();
+        exit(0);
+        break;
+
+    case 1:
+        // Add RAW
+        if (!dmParseInputFile(optArg, STYPE_RAW, STYPE_RAW, "RAW", TRUE))
+            return FALSE;
+        break;
+
+    case 2:
+        // Add PRG
+        if (!dmParseInputFile(optArg, STYPE_PRGA, STYPE_PRG, "PRG", FALSE))
+            return FALSE;
+        break;
+
+    case 5:
+        // Set output file name
+        optDestName = optArg;
+        break;
+
+    case 6:
+        // Allow overlapping segments
+        optAllowOverlap = TRUE;
+        dmError("Warning, allowing overlapping data.\n");
+        break;
+
+    case 7:
+        // Set memory model
+        optMemModel = atoi(optArg);
+        if (optMemModel < 0 || optMemModel >= nmemoryModels)
+        {
+            dmError("Invalid memory model number %i!\n", optMemModel);
+            return FALSE;
+        }
+        break;
+
+    case 8:
+        // Linker file
+        optLinkFileName = optArg;
+        break;
+
+    case 9:
+        // Linker file format
+        switch (tolower(optArg[0]))
+        {
+            case 'g':
+                optLinkFileFormat = FMT_GENERIC;
+                break;
+            case 'p':
+                optLinkFileFormat = FMT_PLAIN;
+                break;
+            case 'd':
+                optLinkFileFormat = FMT_DECIMAL;
+                break;
+
+            default:
+                dmError("Invalid/unknown linker file format '%s'!\n",
+                    optArg);
+                return FALSE;
+        }
+        break;
+
+    case 10:
+        // Initialization value
+        optInitValueType = 1;
+        if ((p = strrchr(optArg, ':')) != NULL)
+        {
+            *p = 0;
+            switch (tolower(p[1]))
+            {
+                case 'b': optInitValueType = 1; break;
+                case 'w': optInitValueType = 2; break;
+                case 'd': optInitValueType = 4; break;
+                default:
+                    dmError("Invalid init value type '%c' specified for '%s'.\n",
+                        p[1], optArg);
+                    return FALSE; 
+            }
+        }
+        if (!dmGetIntVal(optArg, &tmpi))
+        {
+            dmError("Invalid initvalue '%s'.\n", optArg);
+            return FALSE;
+        }
+        optInitValue = tmpi;
+        break;
+
+    case 11:
+        // Set describe mode
+        optDescribe = TRUE;
+        break;
+
+    case 12:
+        {
+            char *sectName = "Clear";
+            ssize_t sectStart, sectEnd, sectLen;
+            if (!dmParseSection(optArg, &sectStart, &sectEnd, &sectName, TRUE))
+                return FALSE;
+
+            // Allocate memory block
+            sectLen = sectEnd - sectStart + 1;
+            dmMsg(1, "Reserve $%.4x - $%.4x ($%x, %d bytes) as '%s'\n",
+                sectStart, sectEnd, sectLen, sectLen, sectName);
+
+            reserveMemBlock(sectStart, sectEnd, sectName, MTYPE_RES);
+        }
+        break;
+
+    case 13:
+        {
+            size_t cropLen;
+            if (!dmParseSection(optArg, &optCropStart, &optCropEnd, NULL, FALSE))
+                return FALSE;
+
+            cropLen = optCropEnd - optCropEnd + 1;
+            dmMsg(1, "Cutting output to $%.4x - $%.4x ($%x, %d bytes)\n",
+                optCropStart, optCropEnd, cropLen, cropLen);
+
+            optCropOutput = TRUE;
+        }
+        break;
+
+    case 14:
+        // Set loading address
+        if (strcasecmp(optArg, "none") == 0)
+            optLoadAddress = LA_NONE;
+        else
+        {
+            if (!dmGetIntVal(optArg, &tmpi))
+            {
+                dmError("Invalid loading address '%s'.\n", optArg);
+                return FALSE;
+            }
+            if (tmpi < 0 || tmpi >= 64*1024)
+            {
+                dmError("Invalid or insane loading address %d/$%x!\n",
+                    tmpi);
+                return FALSE;
+            }
+            optLoadAddress = tmpi;
+        }
+        break;
+
+    default:
+        dmError("Unknown argument '%s'.\n", currArg);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+int dmLoadPRG(const char *filename, BOOL forceAddr, const ssize_t destAddr)
+{
+    FILE *f;
+    ssize_t dataSize, loadAddr, endAddr;
+    Uint16 tmpAddr;
+
+    // Open the input file
+    if ((f = fopen(filename, "rb")) == NULL)
+    {
+        dmError("Error opening input file '%s' (%s).\n",
+            filename, strerror(errno));
+        return 1;
+    }
+
+    // Get filesize
+    if ((dataSize = dmGetFileSize(f) - 2) < 0)
+    {
+        dmError("Error getting file size for '%s'.\n", filename);
+        return 6;
+    }
+
+    // Get loading address
+    if (!dm_fread_le16(f, &tmpAddr))
+    {
+        dmError("Error reading input file '%s' (%s).\n",
+            filename, strerror(errno));
+        return 2;
+    }
+
+    // Show information
+    loadAddr = forceAddr ? destAddr : tmpAddr;
+    endAddr = loadAddr + dataSize - 1;
+
+    dmPrint(1, "* Loading '%s', %s at $%.4x-$%.4x",
+        filename, forceAddr ? "PRGA" : "PRG", loadAddr, endAddr);
+
+    if (endAddr >= memModel->size)
+    {
+        dmPrint(1, " .. Does not fit into the memory!\n");
+        return 5;
+    }
+
+    // Load data
+    if (fread(&memory[loadAddr], dataSize, 1, f) < 1)
+    {
+        dmPrint(1, " .. Error: %s.\n",
+            strerror(errno));
+        return 4;
+    }
+
+    dmPrint(1, " .. OK\n");
+
+    // Add to list of blocks
+    reserveMemBlock(loadAddr, endAddr, filename, MTYPE_RES);
+
+    return 0;
+}
+
+
+int dmLoadRAW(const char *filename, const ssize_t destAddr)
+{
+    FILE *f;
+    ssize_t dataSize, endAddr;
+
+    // Open the input file
+    if ((f = fopen(filename, "rb")) == NULL)
+    {
+        dmError("Error opening input file '%s' (%s).\n",
+            filename, strerror(errno));
+        return 1;
+    }
+
+    // Get filesize
+    if ((dataSize = dmGetFileSize(f)) < 0)
+    {
+        dmError("Error getting file size for '%s'.\n", filename);
+        return 6;
+    }
+
+    // Show information
+    endAddr = destAddr + dataSize - 1;
+    dmPrint(1, "* Loading '%s', RAW at $%.4x-$%.4x",
+        filename, destAddr, endAddr);
+
+    if (endAddr >= memModel->size)
+    {
+        dmPrint(1, " .. Does not fit into the memory!\n");
+        return 5;
+    }
+
+    // Load data
+    if (fread(&memory[destAddr], dataSize, 1, f) < 1)
+    {
+        dmPrint(1, " .. Error: %s.\n",
+            strerror(errno));
+        return 4;
+    }
+
+    dmPrint(1, " .. OK\n");
+
+    // Add info to list
+    reserveMemBlock(destAddr, endAddr, filename, MTYPE_RES);
+
+    return 0;
+}
+
+
+int outputLinkData(FILE *dfile, const char *blockName, const int blockStart, const int blockEnd)
+{
+    char *tmpStr, *s, *t;
+    int blockSize;
+
+    blockSize = (blockEnd - blockStart + 1);
+
+    // Create label name from filename
+    tmpStr = dm_strdup(blockName);
+    if (tmpStr == NULL)
+    {
+        dmError("Could not allocate memory for string '%s'!\n",
+            blockName);
+        return -1;
+    }
+
+    if ((t = strrchr(tmpStr, '/')))
+        s = (t + 1);
+    else if ((t = strrchr(tmpStr, '\\')))
+        s = (t + 1);
+    else
+        s = tmpStr;
+
+    if ((t = strrchr(s, '.')))
+        *t = 0;
+
+    for (t = s; *t; t++)
+    {
+        if (!isalnum(*t))
+            *t = '_';
+    }
+
+    // Print the label line
+    switch (optLinkFileFormat)
+    {
+        case FMT_PLAIN:
+            fprintf(dfile, "%s = $%.4x\n", tmpStr, blockStart);
+            break;
+
+        case FMT_DECIMAL:
+            fprintf(dfile, "%s = %d\n", tmpStr, blockStart);
+            break;
+
+        case FMT_GENERIC:
+        default:
+            fprintf(dfile, "; %s ($%.4x - $%.4x, %d/$%x bytes)\n",
+                blockName, blockStart, blockEnd, blockSize, blockSize);
+            fprintf(dfile, "%s = $%.4x\n", s, blockStart);
+            break;
+    }
+
+    dmFree(tmpStr);
+    return 0;
+}
+
+
+/* Print out an ASCII presentation of memory map
+ */
+void memPrintLine(FILE *f)
+{
+    fprintf(f, "              +------------------------------------------+\n");
+}
+
+void memPrintEmpty(FILE *f, ssize_t n)
+{
+    ssize_t i;
+    for (i = 0; i < n; i++)
+    fprintf(f, "              |                                          |\n");
+}
+
+void dmDescribeMemory(FILE *f)
+{
+    int i;
+    DMMemBlock *prev = NULL;
+
+    memPrintLine(f);
+
+    for (i = 0; i < nmemBlocks; i++)
+    {
+        DMMemBlock *curr = &memBlocks[i];
+        char desc[512], *s;
+        ssize_t siz, kz;
+
+        // Check for empty, unreserved areas
+        siz = (curr->start - 1) - (prev->end + 1) + 1;
+        if (prev != NULL && siz > 1)
+        {
+            kz = siz / (1024 * 2);
+
+            if (kz > 1) memPrintEmpty(f, kz);
+
+            snprintf(desc, sizeof(desc), "EMPTY (%d)", siz);
+            fprintf(f, "$%.4x - $%.4x | %-40s |\n", prev->end + 1, curr->start - 1, desc);
+
+            if (kz > 1) memPrintEmpty(f, kz);
+            memPrintLine(f);
+        }
+        prev = curr;
+
+        // Print current block
+        switch (curr->type)
+        {
+            case MTYPE_NONE:   s = "N/A (NC)"; break;
+            case MTYPE_ROM:    s = "ROM"; break;
+            case MTYPE_ROM_WT: s = "ROM/WT"; break;
+            case MTYPE_IO:     s = "I/O"; break;
+            case MTYPE_RES:    s = "RSVD"; break;
+            default:           s = "????"; break;
+        }
+
+        siz = curr->end - curr->start + 1;
+        kz = siz / (1024 * 2);
+
+        if (kz > 1) memPrintEmpty(f, kz);
+        snprintf(desc, sizeof(desc), "%s (%s, %d)", curr->name, s, siz);
+        fprintf(f, "$%.4x - $%.4x | %-40s |\n", curr->start, curr->end, desc);
+        if (kz > 1) memPrintEmpty(f, kz);
+        memPrintLine(f);
+
+    }
+
+    fprintf(f,
+    "\n"
+    "NC     = Not Connected\n"
+    "RSVD   = Reserved\n"
+    "ROM/WT = RAM under 'write-through' ROM\n"
+    "\n"
+    );
+}
+
+
+/*
+ * The main program
+ */
+int main(int argc, char *argv[])
+{
+    FILE *dfile = NULL;
+    BOOL hasOverlaps;
+    int i, j;
+    ssize_t startAddr, endAddr, dataSize, totalSize;
+
+    dmInitProg("objlink", "Simple file-linker", "0.80", NULL, NULL);
+    dmVerbosity = 1;
+
+    // Parse arguments
+    if (!dmArgsProcess(argc, argv, optList, optListN,
+        argHandleOpt, NULL, TRUE))
+        exit(1);
+
+    if (nsrcFiles < 1)
+    {
+        dmError("Nothing to do. (try --help)\n");
+        exit(0);
+    }
+
+    // Allocate memory
+    memModel = &memoryModels[optMemModel];
+    dmMsg(1, "Using memory model #%d '%s', %d bytes.\n",
+        optMemModel, memModel->name, memModel->size);
+
+    memory = (Uint8 *) dmMalloc(memModel->size + 32);
+    if (memory == NULL)
+    {
+        dmError("Could not allocate memory.\n");
+        exit(2);
+    }
+
+    // Initialize memory
+    dmMsg(1, "Initializing memory with ");
+
+    if (optInitValueType == 1 || optInitValue <= 0xff)
+    {
+        dmPrint(1, "BYTE 0x%.2x\n", optInitValue);
+        memset(memory, optInitValue, memModel->size);
+    }
+    else
+    if (optInitValueType == 2 || optInitValue <= 0xffff)
+    {
+        uint16_t *mp = (uint16_t *) memory;
+        dmPrint(1, "WORD 0x%.4x\n", optInitValue);
+        for (i = memModel->size / sizeof(*mp); i; i--)
+        {
+            *mp++ = optInitValue;
+        }
+    }
+    else
+    {
+        Uint32 *mp = (Uint32 *) memory;
+        dmPrint(1, "DWORD 0x%.8x\n", optInitValue);
+        for (i = memModel->size / sizeof(*mp); i; i--)
+        {
+            *mp++ = optInitValue;
+        }
+    }
+
+    // Load the datafiles
+    for (i = 0; i < nsrcFiles; i++)
+    switch (srcFiles[i].type)
+    {
+        case STYPE_RAW:
+            dmLoadRAW(srcFiles[i].filename, srcFiles[i].addr);
+            break;
+
+        case STYPE_PRG:
+            dmLoadPRG(srcFiles[i].filename, FALSE, 0);
+            break;
+
+        case STYPE_PRGA:
+            dmLoadPRG(srcFiles[i].filename, TRUE, srcFiles[i].addr);
+            break;
+    }
+
+    // Add memory model blocks
+    dmMsg(1, "Applying memory model restrictions...\n");
+    for (i = 0; i < memModel->nmemBlocks; i++)
+    {
+        reserveMemBlock(
+            memModel->memBlocks[i].start,
+            memModel->memBlocks[i].end,
+            memModel->memBlocks[i].name,
+            memModel->memBlocks[i].type);
+    }
+
+    // Sort the blocks
+    qsort(memBlocks, nmemBlocks, sizeof(DMMemBlock), compareMemBlock);
+
+    // Check for overlapping conflicts
+    hasOverlaps = FALSE;
+    for (i = 0; i < nmemBlocks; i++)
+    for (j = 0; j < nmemBlocks; j++)
+    if (j != i && memBlocks[i].type == MTYPE_RES)
+    {
+        DMMemBlock *mbi = &memBlocks[i],
+                   *mbj = &memBlocks[j];
+
+        // Check for per-file conflicts
+        if ((mbj->start >= mbi->start && mbj->start <= mbi->end) ||
+            (mbj->end >= mbi->start && mbj->end <= mbi->end))
+        {
+            dmPrint(1, "* '%s' and '%s' overlap ($%.4x-$%.4x  vs  $%.4x-$%.4x)\n",
+                mbi->name, mbj->name, mbi->start,
+                mbi->end, mbj->start, mbj->end);
+            hasOverlaps = TRUE;
+        }
+    }
+
+    if (!optAllowOverlap && hasOverlaps)
+    {
+        dmError("Error occured, overlaps not allowed.\n");
+        exit(5);
+    }
+
+    // Find out start and end-addresses
+    startAddr = memModel->size;
+    totalSize = endAddr = 0;
+    for (i = 0; i < nmemBlocks; i++)
+    {
+        DMMemBlock *mbi = &memBlocks[i];
+        if (mbi->type == MTYPE_RES)
+        {
+            if (mbi->start < startAddr)
+                startAddr = mbi->start;
+
+            if (mbi->end > endAddr)
+                endAddr = mbi->end;
+
+            totalSize += (mbi->end - mbi->start + 1);
+        }
+    }
+
+    if (startAddr >= memModel->size || endAddr < startAddr)
+    {
+        dmError("Invalid saveblock addresses (start=$%.4x, end=$%.4x)!\n", startAddr, endAddr);
+        exit(8);
+    }
+
+    // Output linkfile
+    if (optLinkFileName)
+    {
+        dmMsg(1, "Writing linkfile to '%s'\n", optLinkFileName);
+        if ((dfile = fopen(optLinkFileName, "wb")) == NULL)
+        {
+            dmError("Error creating file '%s' (%s).\n", optLinkFileName, strerror(errno));
+            exit(1);
+        }
+
+        switch (optLinkFileFormat)
+        {
+            case FMT_GENERIC:
+            default:
+                fprintf(dfile, "; Definitions generated by %s v%s\n",
+                    dmProgName, dmProgVersion);
+                break;
+        }
+
+        for (i = 0; i < nmemBlocks; i++)
+        {
+            DMMemBlock *mbi = &memBlocks[i];
+            outputLinkData(dfile, mbi->name, mbi->start, mbi->end);
+        }
+
+        fclose(dfile);
+    }
+
+    // Show some information
+    if (optCropOutput)
+    {
+        startAddr = optCropStart;
+        endAddr = optCropEnd;
+    }
+
+    dataSize = endAddr - startAddr + 1;
+
+    if (dataSize - totalSize > 0)
+    {
+        dmMsg(1, "Total of %d/$%x bytes unused(?) areas.\n",
+            dataSize - totalSize, dataSize - totalSize);
+    }
+
+    dmMsg(1, "Writing $%.4x - $%.4x (%d/$%x bytes) ",
+        startAddr, endAddr, dataSize, dataSize);
+
+
+    // Open the destination file
+    if (optDestName == NULL)
+    {
+        dfile = stdout;
+        dmPrint(1, "...\n");
+    }
+    else if ((dfile = fopen(optDestName, "wb")) == NULL)
+    {
+        dmError("Error creating output file '%s' (%s).\n", optDestName, strerror(errno));
+        exit(1);
+    }
+    else
+        dmPrint(1, "to '%s'\n", optDestName);
+
+    // Save loading address
+    if (optLoadAddress >= 0)
+    {
+        dmMsg(1, "Using specified loading address $%.4x\n", optLoadAddress);
+        dm_fwrite_le16(dfile, optLoadAddress);
+    }
+    else
+    if (optLoadAddress == LA_AUTO)
+    {
+        dmMsg(1, "Using automatic loading address $%.4x\n", startAddr);
+        dm_fwrite_le16(dfile, startAddr);
+    }
+    else
+    {
+        dmMsg(1, "Writing raw output, without loading address.\n");
+    }
+
+    // Save the data
+    if (fwrite(&memory[startAddr], dataSize, 1, dfile) < 1)
+    {
+        dmError("Error writing to file (%s)\n", strerror(errno));
+    }
+
+    fclose(dfile);
+
+    // Describe
+    if (optDescribe)
+        dmDescribeMemory(stdout);
+
+    exit(0);
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utils/packed.c	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,465 @@
+/*
+ * 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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utils/ppl.c	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,932 @@
+/*
+ * Cyrbe Pasci Player - A simple SDL-based UI for XM module playing
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2012 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#include <SDL.h>
+#include "dmlib.h"
+
+#include "jss.h"
+#include "jssmod.h"
+#include "jssmix.h"
+#include "jssplr.h"
+
+#include "dmargs.h"
+#include "dmimage.h"
+#include "dmtext.h"
+
+#include "setupfont.h"
+
+
+struct
+{
+    BOOL exitFlag;
+    SDL_Surface *screen;
+    SDL_Event event;
+    int optScrWidth, optScrHeight, optVFlags, optScrDepth;
+
+    int actChannel;
+    BOOL pauseFlag;
+
+    JSSModule *mod;
+    JSSMixer *dev;
+    JSSPlayer *plr;
+    SDL_AudioSpec afmt;
+} engine;
+
+struct
+{
+    Uint32 boxBg, inboxBg, box1, box2, viewDiv, activeRow, activeChannel;
+} col;
+
+
+DMBitmapFont *font = NULL;
+
+char    *optFilename = NULL;
+int     optOutFormat = JSS_AUDIO_S16,
+        optOutChannels = 2,
+        optOutFreq = 48000,
+        optMuteOChannels = -1,
+        optStartOrder = 0;
+BOOL    optUsePlayTime = FALSE;
+size_t  optPlayTime;
+
+
+DMOptArg optList[] =
+{
+    {  0, '?', "help",     "Show this help", OPT_NONE },
+    {  1, 'v', "verbose",  "Be more verbose", OPT_NONE },
+    {  2,   0, "fs",       "Fullscreen", OPT_NONE },
+    {  3, 'w', "window",   "Initial window size/resolution -w 640x480", OPT_ARGREQ },
+
+    {  4, '1', "16bit",    "16-bit output", OPT_NONE },
+    {  5, '8', "8bit",     "8-bit output", OPT_NONE },
+    {  6, 'm', "mono",     "Mono output", OPT_NONE },
+    {  7, 's', "stereo",   "Stereo output", OPT_NONE },
+    {  8, 'f', "freq",     "Output frequency", OPT_ARGREQ },
+
+    {  9, 'M', "mute",     "Mute other channels than #", OPT_ARGREQ },
+    { 10, 'o', "order",    "Start from order #", OPT_ARGREQ },
+    { 11, 't', "time",     "Play for # seconds", OPT_ARGREQ },
+};
+
+const int optListN = sizeof(optList) / sizeof(optList[0]);
+
+
+void argShowHelp()
+{
+    dmPrintBanner(stdout, dmProgName, "[options] <module>");
+    dmArgsPrintHelp(stdout, optList, optListN);
+}
+
+
+BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
+{
+    switch (optN) {
+        case 0:
+            argShowHelp();
+            exit(0);
+            break;
+
+        case 1:
+            dmVerbosity++;
+            break;
+        
+        case 2:
+            engine.optVFlags |= SDL_FULLSCREEN;
+            break;
+
+        case 3:
+            {
+                int w, h;
+                if (sscanf(optArg, "%dx%d", &w, &h) == 2)
+                {
+                    if (w < 320 || h < 200 || w > 3200 || h > 3200)
+                    {
+                        dmError("Invalid width or height: %d x %d\n", w, h);
+                        return FALSE;
+                    }
+                    engine.optScrWidth = w;
+                    engine.optScrHeight = h;
+                }
+                else 
+                {
+                    dmError("Invalid size argument '%s'.\n", optArg);
+                    return FALSE;
+                }
+            }
+            break;
+
+        case 4:
+            optOutFormat = JSS_AUDIO_S16;
+            break;
+
+        case 5:
+            optOutFormat = JSS_AUDIO_U8;
+            break;
+
+        case 6:
+            optOutChannels = JSS_AUDIO_MONO;
+            break;
+
+        case 7:
+            optOutChannels = JSS_AUDIO_STEREO;
+            break;
+
+        case 8:
+            optOutFreq = atoi(optArg);
+            break;
+
+        case 9:
+            optMuteOChannels = atoi(optArg);
+            break;
+
+        case 10:
+            optStartOrder = atoi(optArg);
+            break;
+
+        case 11:
+            optPlayTime = atoi(optArg);
+            optUsePlayTime = TRUE;
+            break;
+
+        default:
+            dmError("Unknown option '%s'.\n", currArg);
+            return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+BOOL argHandleFile(char *currArg)
+{
+    if (!optFilename)
+        optFilename = currArg;
+    else
+    {
+        dmError("Too many filename arguments '%s'\n", currArg);
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+void dmDrawBMTextConstQ(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt)
+{
+    const char *ptr = fmt;
+    DMUnscaledBlitFunc blit = NULL;
+
+    while (*ptr)
+    {
+        int ch = *ptr++;
+        SDL_Surface *glyph;
+
+        if (ch == '_')
+        {
+            xc += 4;
+            continue;
+        }
+        
+        if (ch >= 0 && ch < font->nglyphs && (glyph = font->glyphs[ch]) != NULL)
+        {
+            if (blit == NULL)
+                blit = dmGetUnscaledBlitFunc(glyph->format, screen->format, mode);
+            
+            blit(glyph, xc, yc, screen);
+            xc += font->width;
+        }
+        else
+            xc += font->width;
+    }
+}
+
+
+void dmDrawBMTextVAQ(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt, va_list ap)
+{
+    char tmp[512];
+    vsnprintf(tmp, sizeof(tmp), fmt, ap);
+    dmDrawBMTextConstQ(screen, font, mode, xc, yc, tmp);
+}
+
+
+void dmDrawBMTextQ(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt, ...)
+{
+    va_list ap;
+    
+    va_start(ap, fmt);
+    dmDrawBMTextVAQ(screen, font, mode, xc, yc, fmt, ap);
+    va_end(ap);
+}
+
+
+Uint32 dmCol(float r, float g, float b)
+{
+    return dmMapRGB(engine.screen, 255.0f * r, 255.0f * g, 255.0f * b);
+}
+
+
+BOOL dmInitializeVideo()
+{
+    SDL_FreeSurface(engine.screen);
+
+    engine.screen = SDL_SetVideoMode(
+        engine.optScrWidth, engine.optScrHeight, engine.optScrDepth,
+        engine.optVFlags | SDL_RESIZABLE | SDL_SWSURFACE | SDL_HWPALETTE);
+
+    if (engine.screen == NULL)
+    {
+        dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError());
+        return FALSE;
+    }
+
+    col.inboxBg    = dmCol(0.6, 0.5, 0.2);
+    col.boxBg      = dmCol(0.7, 0.6, 0.3);
+    col.box1       = dmCol(1.0, 0.9, 0.6);
+    col.box2       = dmCol(0.3, 0.3, 0.15);
+    col.viewDiv    = dmCol(0,0,0);
+    col.activeRow  = dmCol(0.5,0.4,0.1);
+    col.activeChannel = dmCol(0.6, 0.8, 0.2);
+
+    return TRUE;
+}
+
+
+void dmDisplayChn(SDL_Surface *screen, int x0, int y0, int x1, int y1, int nchannel, JSSChannel *chn)
+{
+    int yh = y1 - y0 - 2;
+    if (yh < 10 || chn == NULL)
+        return;
+
+    int xc, ym = y0 + (y1 - y0) / 2, vol = FP_GETH(chn->chVolume);
+    int pitch = screen->pitch / sizeof(Uint32);
+    int len = FP_GETH(chn->chSize);
+    DMFixedPoint offs = chn->chPos;
+    Uint32 coln = dmCol(0.0, 0.8, 0.0), colx = dmCol(1.0, 0, 0);
+    Uint32 *pix = screen->pixels;
+    Sint16 *data = chn->chData;
+
+
+    dmFillBox3D(screen, x0, y0, x1, y1,
+        (chn->chMute ? dmCol(0.3,0.1,0.1) : dmCol(0,0,0)),
+        nchannel == engine.actChannel ? colx : col.box2,
+        nchannel == engine.actChannel ? colx : col.box1);
+
+    if (chn->chData == NULL || !chn->chPlaying)
+        return;
+
+    if (chn->chDirection)
+    {
+        for (xc = x0 + 1; xc < x1 - 1; xc++)
+        {
+            if (FP_GETH(offs) >= len)
+                break;
+            Sint16 val = ym + (data[FP_GETH(offs)] * yh * vol) / (65535 * 255);
+            pix[xc + val * pitch] = coln;
+            FP_ADD(offs, chn->chDeltaO);
+        }
+    }
+    else
+    {
+        for (xc = x0 + 1; xc < x1 - 1; xc++)
+        {
+            if (FP_GETH(offs) < 0)
+                break;
+            Sint16 val = ym + (data[FP_GETH(offs)] * yh * vol) / (65535 * 255);
+            pix[xc + val * pitch] = coln;
+            FP_SUB(offs, chn->chDeltaO);
+        }
+    }
+}
+
+
+void dmDisplayChannels(SDL_Surface *screen, int x0, int y0, int x1, int y1, JSSMixer *dev)
+{
+    int nchannel, qx, qy,
+        qwidth = x1 - x0,
+        qheight = y1 - y0,
+        nwidth = jsetNChannels,
+        nheight = 1;
+    
+    if (qheight < 40)
+        return;
+    
+    while (qwidth / nwidth <= 60 && qheight / nheight >= 40)
+    {
+        nheight++;
+        nwidth /= nheight;
+    }
+
+//    fprintf(stderr, "%d x %d\n", nwidth, nheight);
+    
+    if (qheight / nheight <= 40)
+    {
+        nwidth = qwidth / 60;
+        nheight = qheight / 40;
+    }
+
+    qwidth /= nwidth;
+    qheight /= nheight;
+        
+    for (nchannel = qy = 0; qy < nheight && nchannel < jsetNChannels; qy++)
+    {
+        for (qx = 0; qx < nwidth && nchannel < jsetNChannels; qx++)
+        {
+            int xc = x0 + qx * qwidth,
+                yc = y0 + qy * qheight;
+
+            dmDisplayChn(screen, xc + 1, yc + 1,
+                xc + qwidth - 1, yc + qheight - 1,
+                nchannel, &dev->channels[nchannel]);
+
+            nchannel++;
+        }
+    }
+}
+
+
+static 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";
+
+static const char jmpHexTab[16] = "0123456789ABCDEF";
+
+static inline char dmHexVal(int v)
+{
+    return jmpHexTab[v & 15];
+}
+
+void dmPrintNote(SDL_Surface *screen, int xc, int yc, JSSNote *n)
+{
+    char text[32];
+    char *ptr = text;
+    
+    switch (n->note)
+    {
+        case jsetNotSet:
+            strcpy(ptr, "..._");
+            break;
+        case jsetNoteOff:
+            strcpy(ptr, "===_");
+            break;
+        default:
+            sprintf(ptr, "%s%i_",
+                patNoteTable[n->note % 12],
+                n->note / 12);
+            break;
+    }
+
+    ptr += 4;
+
+    if (n->instrument != jsetNotSet)
+    {
+        int v = n->instrument + 1;
+        *ptr++ = dmHexVal(v >> 4);
+        *ptr++ = dmHexVal(v);
+    }
+    else
+    {
+        *ptr++ = '.';
+        *ptr++ = '.';
+    }
+    *ptr++ = '_';
+
+    if (n->volume == jsetNotSet)
+    {
+        *ptr++ = '.';
+        *ptr++ = '.';
+    }
+    else
+    if (n->volume >= 0x00 && n->volume <= 0x40)
+    {
+        *ptr++ = dmHexVal(n->volume >> 4);
+        *ptr++ = dmHexVal(n->volume);
+    }
+    else
+    {
+        char c;
+        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;
+        }
+        *ptr++ = c;
+        *ptr++ = dmHexVal(n->volume);
+    }
+    *ptr++ = '_';
+    
+    if (n->effect >= 0 && n->effect < jmpNMODEffectTable)
+        *ptr++ = jmpMODEffectTable[n->effect];
+    else
+        *ptr++ = (n->effect == jsetNotSet ? '.' : '?');
+
+    if (n->param != jsetNotSet)
+    {
+        *ptr++ = dmHexVal(n->param >> 4);
+        *ptr++ = dmHexVal(n->param);
+    }
+    else
+    {
+        *ptr++ = '.';
+        *ptr++ = '.';
+    }
+
+    *ptr = 0;
+
+    dmDrawBMTextConstQ(screen, font, DMD_TRANSPARENT, xc, yc, text);
+}
+
+
+void dmDisplayPattern(SDL_Surface *screen, int x0, int y0, int x1, int y1, JSSPattern *pat, int row)
+{
+    int cwidth = (font->width * 10 + 3 * 4 + 5),
+        lwidth = 6 + font->width * 3,
+        qy0 = y0 + font->height + 2,
+        qy1 = y1 - font->height - 2,
+        qwidth  = ((x1 - x0 - lwidth) / cwidth),
+        qheight = ((qy1 - qy0 - 4) / (font->height + 1)),
+        nrow, nchannel, yc, choffs,
+        midrow = qheight / 2;
+
+    if (engine.actChannel < qwidth / 2)
+        choffs = 0;
+    else
+    if (engine.actChannel >= pat->nchannels - qwidth/2)
+        choffs = pat->nchannels - qwidth;
+    else
+        choffs = engine.actChannel - qwidth/2;
+
+    dmDrawBox3D(screen, x0 + lwidth, qy0, x1, qy1, col.box2, col.box1);
+
+    for (nchannel = 0; nchannel < qwidth; nchannel++)
+    {
+        int bx0 = x0 + lwidth + 1 + nchannel * cwidth, 
+            bx1 = bx0 + cwidth;
+            
+        if (engine.actChannel == nchannel + choffs)
+        {
+            dmFillRect(screen, bx0+1, qy0 + 1, bx1-1, qy1 - 1, col.activeChannel);
+        }
+        else
+        {
+            dmFillRect(screen, bx0+1, qy0 + 1, bx1-1, qy1 - 1, col.inboxBg);
+        }
+    }
+
+    yc = qy0 + 2 + (font->height + 1) * midrow;
+    dmFillRect(screen, x0 + lwidth + 1, yc - 1, x1 - 1, yc + font->height, col.activeRow);
+
+    for (nchannel = 0; nchannel < qwidth; nchannel++)
+    {
+        int bx0 = x0 + lwidth + 1 + nchannel * cwidth, 
+            bx1 = bx0 + cwidth;
+
+        dmDrawVLine(screen, qy0 + 1, qy1 - 1, bx1, col.viewDiv);
+
+        if (jvmGetMute(engine.dev, nchannel + choffs))
+        {
+            dmDrawBMTextConstQ(screen, font, DMD_TRANSPARENT,
+                bx0 + (cwidth - font->width * 5) / 2, qy1 + 3, "MUTED");
+        }
+        
+        dmDrawBMTextQ(screen, font, DMD_TRANSPARENT,
+            bx0 + (cwidth - font->width * 3) / 2, y0 + 1, "%3d",
+            nchannel + choffs);
+    }
+    
+    for (nrow = 0; nrow < qheight; nrow++)
+    {
+        int crow = nrow - midrow + row;
+        yc = qy0 + 2 + (font->height + 1) * nrow;
+        
+        if (crow >= 0 && crow < pat->nrows)
+        {
+            dmDrawBMTextQ(screen, font, DMD_TRANSPARENT, x0, yc, "%03d", crow);
+
+            for (nchannel = 0; nchannel < qwidth; nchannel++)
+            {
+                if (choffs + nchannel >= pat->nchannels)
+                    break;
+
+                dmPrintNote(screen, x0 + lwidth + 4 + nchannel * cwidth, yc,
+                    pat->data + (pat->nchannels * crow) + choffs + nchannel);
+            }
+        }
+    }
+}
+
+
+void audioCallback(void *userdata, Uint8 *stream, int len)
+{
+    JSSMixer *d = (JSSMixer *) userdata;
+
+    if (d != NULL)
+    {
+        jvmRenderAudio(d, stream, len / jvmGetSampleSize(d));
+    }
+}
+
+
+void dmMuteChannels(BOOL mute)
+{
+    int i;
+    for (i = 0; i < engine.mod->nchannels; i++)
+        jvmMute(engine.dev, i, mute);
+}
+
+int main(int argc, char *argv[])
+{
+    BOOL initSDL = FALSE, audioInit = FALSE;
+    DMResource *file = NULL;
+    int result = -1;
+    BOOL muteState = FALSE;
+
+    memset(&engine, 0, sizeof(engine));
+
+    engine.optScrWidth = 640;
+    engine.optScrHeight = 480;
+    engine.optScrDepth = 32;
+
+    dmInitProg("CBP", "Cyrbe Basci Player", "0.1", NULL, NULL);
+
+    // Parse arguments
+    if (!dmArgsProcess(argc, argv, optList, optListN,
+        argHandleOpt, argHandleFile, TRUE))
+        exit(1);
+
+    // Open the files
+    if (optFilename == NULL)
+    {
+        dmError("No filename specified.\n");
+        return 1;
+    }
+    
+    if ((file = dmf_create_stdio(optFilename, "rb")) == NULL)
+    {
+        int err = dmGetErrno();
+        dmError("Error opening file '%s', %d: (%s)\n",
+            optFilename, err, dmErrorStr(err));
+        return 1;
+    }
+
+    // Initialize miniJSS
+    jssInit();
+
+    // Read module file
+    dmMsg(1, "Reading file: %s\n", optFilename);
+#ifdef JSS_SUP_XM
+    dmMsg(2, "* Trying XM...\n");
+    result = jssLoadXM(file, &engine.mod);
+#endif
+#ifdef JSS_SUP_JSSMOD
+    if (result != 0)
+    {
+        size_t bufgot, bufsize = dmfsize(file);
+        Uint8 *buf = dmMalloc(bufsize);
+        dmfseek(file, 0L, SEEK_SET);
+        dmMsg(2, "* Trying JSSMOD (%d bytes, %p)...\n", bufsize, buf);
+        if ((bufgot = dmfread(buf, 1, bufsize, file)) != bufsize)
+        {
+            dmf_close(file);
+            dmError("Error reading file (not enough data %d), #%d: %s\n",
+                bufgot, dmferror(file), dmErrorStr(dmferror(file)));
+            goto error_exit;
+        }
+        result = jssLoadJSSMOD(buf, bufsize, &engine.mod);
+        dmFree(buf);
+    }
+#endif
+    dmf_close(file);
+
+    if (result != DMERR_OK)
+    {
+        dmError("Error loading module file, %d: %s\n",
+            result, dmErrorStr(result));
+        goto error_exit;
+    }
+
+    // Try to convert it
+    if ((result = jssConvertModuleForPlaying(engine.mod)) != DMERR_OK)
+    {
+        dmError("Could not convert module for playing, %d: %s\n",
+            result, dmErrorStr(result));
+        goto error_exit;
+    }
+
+    // Get font
+//    file = dmf_create_stdio("fnsmall.fnt", "rb");
+    file = dmf_create_memio(NULL, "pplfont.fnt", engineSetupFont, sizeof(engineSetupFont));
+    if (file == NULL)
+    {
+        dmError("Error opening font file 'pplfont.fnt'.\n");
+        goto error_exit;
+    }
+    result = dmLoadBitmapFont(file, &font);
+    dmf_close(file);
+    if (result != DMERR_OK)
+    {
+        dmError("Could not load font from file, %d: %s\n",
+            result, dmErrorStr(result));
+        goto error_exit;
+    }
+
+    // Initialize SDL components
+    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) != 0)
+    {
+        dmError("Could not initialize SDL: %s\n", SDL_GetError());
+        goto error_exit;
+    }
+    initSDL = TRUE;
+
+
+    // Initialize mixing device
+    dmMsg(2, "Initializing miniJSS mixer with: %d, %d, %d\n",
+        optOutFormat, optOutChannels, optOutFreq);
+
+    engine.dev = jvmInit(optOutFormat, optOutChannels, optOutFreq, JMIX_AUTO);
+    if (engine.dev == NULL)
+    {
+        dmError("jvmInit() returned NULL\n");
+        goto error_exit;
+    }
+    
+    switch (optOutFormat)
+    {
+        case JSS_AUDIO_S16: engine.afmt.format = AUDIO_S16SYS; break;
+        case JSS_AUDIO_U16: engine.afmt.format = AUDIO_U16SYS; break;
+        case JSS_AUDIO_S8:  engine.afmt.format = AUDIO_S8; break;
+        case JSS_AUDIO_U8:  engine.afmt.format = AUDIO_U8; break;
+        default:
+            dmError("Unsupported audio format %d (could not set matching SDL format)\n",
+                optOutFormat);
+            goto error_exit;
+    }
+
+    engine.afmt.freq     = optOutFreq;
+    engine.afmt.channels = optOutChannels;
+    engine.afmt.samples  = optOutFreq / 16;
+    engine.afmt.callback = audioCallback;
+    engine.afmt.userdata = (void *) engine.dev;
+
+    // Open the audio device
+    if (SDL_OpenAudio(&engine.afmt, NULL) < 0)
+    {
+        dmError("Couldn't open SDL audio: %s\n",
+            SDL_GetError());
+        goto error_exit;
+    }
+    audioInit = TRUE;
+    
+    // Initialize player
+    if ((engine.plr = jmpInit(engine.dev)) == NULL)
+    {
+        dmError("jmpInit() returned NULL\n");
+        goto error_exit;
+    }
+    
+    jvmSetCallback(engine.dev, jmpExec, engine.plr);
+    jmpSetModule(engine.plr, engine.mod);
+    jmpPlayOrder(engine.plr, optStartOrder);
+    jvmSetGlobalVol(engine.dev, 64);
+
+    if (optMuteOChannels >= 0 && optMuteOChannels < engine.mod->nchannels)
+    {
+        dmMuteChannels(TRUE);
+        jvmMute(engine.dev, optMuteOChannels, FALSE);
+        engine.actChannel = optMuteOChannels;
+        muteState = TRUE;
+    }
+
+    // Initialize video
+    if (!dmInitializeVideo())
+        goto error_exit;
+
+    SDL_WM_SetCaption(dmProgDesc, dmProgName);
+
+    SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
+
+    // okay, main loop here ... "play" module and print out info
+    SDL_LockAudio();
+    SDL_PauseAudio(0);
+    SDL_UnlockAudio();
+
+    int currTick, prevTick = 0, prevRow = -1;
+    
+    while (!engine.exitFlag)
+    {
+        currTick = SDL_GetTicks();
+        BOOL force = (currTick - prevTick > 500), updated = FALSE;
+
+        while (SDL_PollEvent(&engine.event))
+        switch (engine.event.type)
+        {
+            case SDL_KEYDOWN:
+                switch (engine.event.key.keysym.sym)
+                {
+                    case SDLK_ESCAPE:
+                        engine.exitFlag = TRUE;
+                        break;
+                    
+                    case SDLK_SPACE:
+                        engine.pauseFlag = !engine.pauseFlag;
+                        SDL_PauseAudio(engine.pauseFlag);
+                        break;
+
+                    case SDLK_LEFT:
+                        if (engine.actChannel > 0)
+                        {
+                            engine.actChannel--;
+                            force = TRUE;
+                        }
+                        break;
+
+                    case SDLK_RIGHT:
+                        if (engine.actChannel < engine.mod->nchannels)
+                        {
+                            engine.actChannel++;
+                            force = TRUE;
+                        }
+                        break;
+
+                    case SDLK_m:
+                        if (engine.event.key.keysym.mod & KMOD_SHIFT)
+                        {
+                            muteState = !muteState;
+                            dmMuteChannels(muteState);
+                        }
+                        else
+                        if (engine.event.key.keysym.mod & KMOD_CTRL)
+                        {
+                            dmMuteChannels(FALSE);
+                        }
+                        else
+                        {
+                            jvmMute(engine.dev, engine.actChannel, !jvmGetMute(engine.dev, engine.actChannel));
+                        }
+                        force = TRUE;
+                        break;
+
+                    case SDLK_PAGEUP:
+                        JSS_LOCK(engine.dev);
+                        JSS_LOCK(engine.plr);
+                        jmpChangeOrder(engine.plr, dmClamp(engine.plr->order - 1, 0, engine.mod->norders));
+                        JSS_UNLOCK(engine.plr);
+                        JSS_UNLOCK(engine.dev);
+                        force = TRUE;
+                        break;
+
+                    case SDLK_PAGEDOWN:
+                        JSS_LOCK(engine.dev);
+                        JSS_LOCK(engine.plr);
+                        jmpChangeOrder(engine.plr, dmClamp(engine.plr->order + 1, 0, engine.mod->norders));
+                        JSS_UNLOCK(engine.plr);
+                        JSS_UNLOCK(engine.dev);
+                        force = TRUE;
+                        break;
+
+                    case SDLK_f:
+                        engine.optVFlags ^= SDL_FULLSCREEN;
+                        if (!dmInitializeVideo())
+                            goto error_exit;
+                        force = TRUE;
+                        break;
+
+                    default:
+                        break;
+                }
+
+                break;
+
+            case SDL_VIDEORESIZE:
+                engine.optScrWidth = engine.event.resize.w;
+                engine.optScrHeight = engine.event.resize.h;
+
+                if (!dmInitializeVideo())
+                    goto error_exit;
+
+                break;
+
+            case SDL_VIDEOEXPOSE:
+                break;
+
+            case SDL_QUIT:
+                engine.exitFlag = TRUE;
+                break;
+        }
+
+
+#if 1
+        JSS_LOCK(engine.plr);
+        JSSPattern *currPattern = engine.plr->pattern;
+        int currRow = engine.plr->row;
+        if (!engine.plr->isPlaying)
+            engine.exitFlag = TRUE;
+        JSS_UNLOCK(engine.plr);
+        
+        if (currRow != prevRow || force)
+        {
+            prevRow = currRow;
+            force = TRUE;
+        }
+        
+        // Draw frame
+        if (SDL_MUSTLOCK(engine.screen) != 0 && SDL_LockSurface(engine.screen) != 0)
+        {
+            dmError("Can't lock surface.\n");
+            goto error_exit;
+        }
+
+        if (force)
+        {
+            dmClearSurface(engine.screen, col.boxBg);
+            
+            dmDrawBMTextQ(engine.screen, font, DMD_TRANSPARENT, 5, 5, "%s v%s by ccr/TNSP - (c) Copyright 2012 TNSP", dmProgDesc, dmProgVersion);
+
+            dmDrawBMTextQ(engine.screen, font, DMD_TRANSPARENT, 5, 5 + 12 + 11,
+                "Song: '%s'",
+                engine.mod->moduleName);
+
+            dmDisplayPattern(engine.screen, 5, 40,
+                engine.screen->w - 6, engine.screen->h * 0.8,
+                currPattern, currRow);
+            
+            JSS_LOCK(engine.plr);
+            dmDrawBMTextQ(engine.screen, font, DMD_TRANSPARENT, 5, 5 + 12,
+            "Tempo: %3d | Speed: %3d | Row: %3d/%-3d | Order: %3d/%-3d | Pattern: %3d/%-3d",
+            engine.plr->tempo, engine.plr->speed,
+            engine.plr->row, engine.plr->pattern->nrows,
+            engine.plr->order, engine.mod->norders,
+            engine.plr->npattern, engine.mod->npatterns);
+            JSS_UNLOCK(engine.plr);
+            updated = TRUE;
+        }
+
+        if (force || currTick - prevTick >= (engine.pauseFlag ? 100 : 20))
+        {
+            JSS_LOCK(engine.dev);
+            dmDisplayChannels(engine.screen, 5, engine.screen->h * 0.8 + 5,
+                engine.screen->w - 5, engine.screen->h - 5, engine.dev);
+            JSS_UNLOCK(engine.dev);
+            updated = TRUE;
+        }
+
+        if (force)
+            prevTick = currTick;
+
+#endif
+        // Flip screen
+        if (SDL_MUSTLOCK(engine.screen) != 0)
+            SDL_UnlockSurface(engine.screen);
+
+        if (updated)
+            SDL_Flip(engine.screen);
+
+        SDL_Delay(engine.pauseFlag ? 100 : 30);
+    }
+
+error_exit:
+    if (engine.screen)
+        SDL_FreeSurface(engine.screen);
+
+    dmMsg(0, "Audio shutdown.\n");
+    if (audioInit)
+    {
+        SDL_LockAudio();
+        SDL_PauseAudio(1);
+        SDL_UnlockAudio();
+        SDL_CloseAudio();
+    }
+
+    jmpClose(engine.plr);
+    jvmClose(engine.dev);
+    jssFreeModule(engine.mod);
+
+    dmFreeBitmapFont(font);
+
+    if (initSDL)
+        SDL_Quit();
+
+    jssClose();
+
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utils/svg2qd.py	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,102 @@
+#!/usr/bin/python
+import sys
+import re
+import xml.etree.ElementTree as ET
+
+
+def bf(x) :
+   return int(round(float(x)))
+
+
+def printVertex(v) :
+   if type(v) is list :
+      return "{:.2f},{:.2f},{:.2f}".format(v[0], v[1], v[2])
+   else :
+      return v
+
+
+def getTransform(elem) :
+   if "transform" in elem.attrib :
+      ntrans = elem.attrib["transform"]
+      tmatch = re.compile(r"translate\((.*?)\)", re.IGNORECASE)
+      for trns in tmatch.finditer(ntrans) :
+         coord = trns.group(1).split(",")
+         return [float(coord[0]), float(coord[1]), 0]
+   return None
+
+
+def getStyle(elem) :
+   style = {}
+   if "style" in elem.attrib :
+      for elem in elem.attrib["style"].split(";") :
+         kv = elem.split(":")
+         style[kv[0]] = kv[1]
+   return style
+
+
+def printVertices(type, vertices, width, level) :
+   if len(vertices) > 0 :
+      list = map(lambda v:printVertex(v), vertices)
+      str = "# "+ type
+      if type == "m" :
+         str = "R"
+      elif type == "M" :
+         str = "L"
+      elif type == "c" :
+         str = "R"
+      print "{}{}{} {} {}".format("  "*level, str, len(vertices)-1, " ".join(list), width)
+
+
+def printPath(path, level) :
+   style = getStyle(path)
+   width = bf(style["stroke-width"])
+
+   trans = getTransform(path)
+   if trans :
+      print "{}G{}".format("  "*level, printVertex(trans))
+
+   vertices = []
+   type = ""
+   for elem in path.attrib["d"].split(" ") :
+      if elem == "m" or elem == "M" :
+         printVertices(type, vertices, width, level)
+         vertices = []
+         type = elem
+      elif elem == "z" :
+         vertices.append("Z")
+      elif elem == "c" or elem == "C" :
+         print "Curves not supported! Path ID '{}':\n{}".format(path.attrib["id"], path.attrib["d"])
+         sys.exit(0)
+      else :
+         tmp = elem.split(",")
+         px = float(tmp[0])
+         py = float(tmp[1])
+         vertices.append([px, py, 0])
+
+   printVertices(type, vertices, width, level)
+   if trans :
+      print "{}E\n".format("  "*level)
+
+
+def iterateDocument(elems, level) :
+   for elem in elems:
+      if elem.tag == "{http://www.w3.org/2000/svg}g" :
+         print "\n{}# GROUP".format("  "*level)
+         tmp = getTransform(elem)
+         if tmp :
+            print "{}G{}".format("  "*level, printVertex(getTransform(elem)))
+            iterateDocument(elem, level + 1)
+            print "{}E\n".format("  "*level)
+         else :
+            iterateDocument(elem, level)
+      elif elem.tag == "{http://www.w3.org/2000/svg}path" :
+         printPath(elem, level)
+
+
+# Ns. paaohjelma
+if len(sys.argv) != 2 :
+   print "Usage: "+sys.argv[0]+" <input.svg>"
+   sys.exit(1)
+   
+tree = ET.parse(sys.argv[1])
+iterateDocument(tree.getroot(), 0)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utils/view64.c	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,321 @@
+/*
+ * view64 - Display some C64 etc graphics formats via libSDL
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2012 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#include "dmlib.h"
+#include "dmargs.h"
+#include "dmfile.h"
+#include "lib64gfx.h"
+#include <SDL.h>
+
+
+char * optFilename = NULL;
+int    optVFlags = SDL_SWSURFACE | SDL_HWPALETTE;
+int    optScrWidth, optScrHeight;
+int    optForcedFormat = -1;
+
+
+static DMOptArg optList[] =
+{
+    { 0, '?', "help",       "Show this help", OPT_NONE },
+    { 1, 'v', "verbose",    "Be more verbose", OPT_NONE },
+    { 2,   0, "fs",         "Fullscreen", OPT_NONE },
+    { 3, 'S', "scale",      "Scale image by factor (1-10)", OPT_ARGREQ },
+    { 4, 'f', "format",     "Force input format (see list below)", OPT_ARGREQ },
+};
+
+const int optListN = sizeof(optList) / sizeof(optList[0]);
+
+
+void dmSetScaleFactor(float factor)
+{
+    optScrWidth = (int) ((float) C64_SCR_WIDTH * factor * C64_SCR_PAR_XY);
+    optScrHeight = (int) ((float) C64_SCR_HEIGHT * factor);
+}
+
+
+void argShowHelp()
+{
+    int i;
+
+    dmPrintBanner(stdout, dmProgName, "[options] <input image file>");
+    dmArgsPrintHelp(stdout, optList, optListN);
+
+    printf("\nAvailable bitmap formats:\n");
+    for (i = 0; i < ndmC64ImageFormats; i++)
+    {
+        const DMC64ImageFormat *fmt = &dmC64ImageFormats[i];
+        char buf[64];
+        printf("%3d | %-5s | %-15s | %s\n",
+            i, fmt->fext,
+            dmC64GetImageTypeString(buf, sizeof(buf), fmt->type),
+            fmt->name);
+    }
+}
+
+
+BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
+{
+    switch (optN)
+    {
+        case 0:
+            argShowHelp();
+            exit(0);
+            break;
+
+        case 1:
+            dmVerbosity++;
+            break;
+        
+        case 2:
+            optVFlags |= SDL_FULLSCREEN;
+            break;
+
+        case 3:
+            {
+                float factor;
+                if (sscanf(optArg, "%f", &factor) == 1)
+                {
+                    if (factor < 1 || factor >= 10)
+                    {
+                        dmError("Invalid scale factor %1.0f, see help for valid values.\n", factor);
+                        return FALSE;
+                    }
+
+                    dmSetScaleFactor(factor);
+                }
+                else
+                {
+                    dmError("Invalid scale factor '%s'.\n", optArg);
+                    return FALSE;
+                }
+            }
+            break;
+
+        case 4:
+            {
+                int i;
+                if (sscanf(optArg, "%d", &i) == 1)
+                {
+                    if (i < 0 || i >= ndmC64ImageFormats)
+                    {
+                        dmError("Invalid image format index %d, see help for valid values.\n", i);
+                        return FALSE;
+                    }
+                    optForcedFormat = i;
+                }
+                else
+                {
+                    dmError("Invalid image format argument '%s'.\n", optArg);
+                    return FALSE;
+                }
+            }
+            break;
+        
+        default:
+            dmError("Unknown option '%s'.\n", currArg);
+            return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+BOOL argHandleFile(char *filename)
+{
+    if (optFilename == NULL)
+    {
+        optFilename = dm_strdup(filename);
+        return TRUE;
+    }
+    else
+    {
+        dmError("Too many filenames specified ('%s')\n", filename);
+        return FALSE;
+    }
+}
+
+
+BOOL dmInitializeVideo(SDL_Surface **screen)
+{
+    *screen = SDL_SetVideoMode(optScrWidth, optScrHeight, 8, optVFlags | SDL_RESIZABLE);
+    if (*screen == NULL)
+    {
+        dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError());
+        return FALSE;
+    }
+    return TRUE;
+}
+
+
+int main(int argc, char *argv[])
+{
+    SDL_Surface *screen = NULL, *surf = NULL;
+    DMImage bmap;
+    BOOL initSDL = FALSE, exitFlag, needRedraw;
+    const DMC64ImageFormat *fmt = NULL, *forced;
+    DMC64Image image;
+    char *windowTitle;
+    Uint8 *dataBuf = NULL;
+    size_t dataSize;
+    int ret;
+
+    dmSetScaleFactor(2.0);
+    
+    dmInitProg("view64", "Display some C64 bitmap graphics formats", "0.2", NULL, NULL);
+
+    /* Parse arguments */
+    if (!dmArgsProcess(argc, argv, optList, optListN,
+        argHandleOpt, argHandleFile, FALSE))
+        exit(1);
+
+
+    if (optFilename == NULL)
+    {
+        dmError("No input file specified, perhaps you need some --help\n");
+        goto error;
+    }
+        
+    if ((ret = dmReadDataFile(NULL, optFilename, &dataBuf, &dataSize)) != DMERR_OK)
+        goto error;
+
+    dmMsg(1, "Read %d bytes of input.\n", dataSize);
+
+    // Probe for format
+    if (optForcedFormat >= 0)
+    {
+        forced = &dmC64ImageFormats[optForcedFormat];
+        dmMsg(0,"Forced %s format image, type %d, %s\n",
+            forced->name, forced->type, forced->fext);
+    }
+    else
+        forced = NULL;
+
+    ret = dmC64DecodeBMP(&image, dataBuf, dataSize, 0, 2, &fmt, forced);
+    if (forced == NULL && fmt != NULL)
+    {
+        dmMsg(0,"Probed %s format image, type %d, %s\n",
+            fmt->name, fmt->type, fmt->fext);
+    }
+
+    if (ret < 0)
+    {
+        dmError("Probing could not find any matching image format (%d). Perhaps try forcing a format via -f\n", ret);
+        return -1;
+    }
+
+
+    // Initialize libSDL
+    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
+    {
+        dmError("Could not initialize SDL: %s\n", SDL_GetError());
+        goto error;
+    }
+    initSDL = TRUE;
+
+
+    // Open window/set video mode
+    screen = SDL_SetVideoMode(optScrWidth, optScrHeight, 8, optVFlags | SDL_RESIZABLE);
+    if (screen == NULL)
+    {
+        dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError());
+        goto error;
+    }
+
+    // Create surface (we are lazy and ugly)
+    surf = SDL_CreateRGBSurface(SDL_SWSURFACE, C64_SCR_WIDTH, C64_SCR_HEIGHT, 8, 0, 0, 0, 0);
+    SDL_SetColors(surf, (SDL_Color *)dmC64Palette, 0, C64_NCOLORS);
+    SDL_SetColors(screen, (SDL_Color *)dmC64Palette, 0, C64_NCOLORS);
+
+    // Convert bitmap (this is a bit ugly and lazy here)
+    bmap.data = surf->pixels;
+    bmap.pitch = surf->pitch;
+    bmap.width = surf->w;
+    bmap.height = surf->h;
+    bmap.constpal = TRUE;
+
+    if (fmt->convertFrom != NULL)
+        ret = fmt->convertFrom(&bmap, &image, TRUE);
+    else
+        ret = dmC64ConvertGenericBMP2Image(&bmap, &image, TRUE);
+
+
+    // Set window title and caption
+    windowTitle = dm_strdup_printf("%s - %s", dmProgName, optFilename);
+    SDL_WM_SetCaption(windowTitle, dmProgName);
+    dmFree(windowTitle);
+
+
+    // Start main loop
+    needRedraw = TRUE;
+    exitFlag = FALSE;
+    while (!exitFlag)
+    {
+        SDL_Event event;
+        while (SDL_PollEvent(&event))
+        switch (event.type)
+        {
+            case SDL_KEYDOWN:
+                switch (event.key.keysym.sym)
+                {
+                    case SDLK_ESCAPE: exitFlag = TRUE; break;
+                    
+                    default:
+                        break;
+                }
+
+                needRedraw = TRUE;
+                break;
+            
+            case SDL_VIDEORESIZE:
+                optScrWidth = event.resize.w;
+                optScrHeight = event.resize.h;
+
+                if (!dmInitializeVideo(&screen))
+                    goto error;
+
+                needRedraw = TRUE;
+                break;
+            
+            case SDL_VIDEOEXPOSE:
+                needRedraw = TRUE;
+                break;
+
+            case SDL_QUIT:
+                exit(0);
+        }
+        
+        if (needRedraw)
+        {
+            if (SDL_MUSTLOCK(screen) != 0 && SDL_LockSurface(screen) != 0)
+            {
+                dmError("Can't lock surface.\n");
+                goto error;
+            }
+            
+            dmScaledBlitSurface8to8(surf, 0, 0, screen->w, screen->h, screen);
+            SDL_SetColors(screen, (SDL_Color *)dmC64Palette, 0, C64_NCOLORS);
+
+            if (SDL_MUSTLOCK(screen) != 0)
+                SDL_UnlockSurface(screen);
+
+            SDL_Flip(screen);
+            needRedraw = FALSE;
+        }
+        
+        SDL_Delay(100);
+    }
+
+
+error:
+    if (screen)
+        SDL_FreeSurface(screen);
+
+    if (initSDL)
+        SDL_Quit();
+
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utils/viewmod.c	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,475 @@
+/*
+ * viewmod - View information about given module file
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2006-2007 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#include "jss.h"
+#include "jssmod.h"
+#include <errno.h>
+#include <string.h>
+#include "dmargs.h"
+#include "dmmutex.h"
+
+
+char    *optFilename = NULL;
+BOOL    optViewPatterns = FALSE,
+        optViewInstruments = FALSE,
+        optViewExtInstruments = FALSE,
+        optViewGeneralInfo = FALSE;
+
+
+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 },
+    { 5, 'e', "extinstruments", "View extended instruments", OPT_NONE },
+    { 3, 'g', "general", "General information", OPT_NONE },
+    { 4, 'v', "verbose", "Be more verbose", OPT_NONE },
+};
+
+const int optListN = sizeof(optList) / sizeof(optList[0]);
+
+
+void argShowHelp()
+{
+    dmPrintBanner(stdout, dmProgName, "[options] [modfile]");
+    dmArgsPrintHelp(stdout, optList, optListN);
+}
+
+
+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:
+            optViewGeneralInfo = TRUE;
+            break;
+
+        case 4:
+            dmVerbosity++;
+            break;
+
+        case 5:
+            optViewExtInstruments = TRUE;
+            break;
+
+        default:
+            dmError("Unknown argument '%s'.\n", currArg);
+            return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+BOOL argHandleFile(char *currArg)
+{
+    // Was not option argument
+    if (!optFilename)
+        optFilename = currArg;
+    else {
+        dmError("Gay error '%s'!\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";
+
+
+/* Print a given pattern
+ */
+void printPattern(FILE *f, JSSPattern *p)
+{
+    int i, j;
+    char c;
+    JSSNote *n;
+
+    if (!p)
+        return;
+
+    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%i ", patNoteTable[n->note % 12], n->note / 12);
+                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, JSSEnvelope *e, char *s)
+{
+    int i;
+
+    fprintf(f,
+        "\t%s-envelope:\n"
+        "\t - flags.....: %.4x", s, e->flags);
+
+    if (e->flags & jenvfUsed)
+        fprintf(f, " [used]");
+    if (e->flags & jenvfSustain)
+        fprintf(f, " [sust]");
+    if (e->flags & jenvfLooped)
+        fprintf(f, " [loop]");
+
+    fprintf(f, "\n"
+        "\t - npoints...: %i\n"
+        "\t - sustain...: %i\n"
+        "\t - loopS.....: %i\n"
+        "\t - loopE.....: %i\n",
+        e->npoints, e->sustain, e->loopS, e->loopE);
+
+    if (dmVerbosity >= 2)
+    {
+        fprintf(f, "\t - 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, JSSExtInstrument *i)
+{
+    if (!i)
+    {
+        fprintf(f, "\n");
+        return;
+    }
+
+#ifndef JSS_LIGHT
+    if (i->desc)
+        fprintf(f,
+        "Description: '%s'\n", i->desc);
+#endif
+    fprintf(f,
+        "nsamples.......: %i\n"
+        "vibratoType....: %i\n"
+        "vibratoSweep...: %i\n"
+        "vibratoDepth...: %i\n"
+        "vibratoRate....: %i\n"
+        "fadeOut........: %i\n",
+        i->nsamples, i->vibratoType, i->vibratoSweep,
+        i->vibratoDepth, i->vibratoRate, i->fadeOut);
+
+    if (dmVerbosity >= 1)
+    {
+        printEnvelope(f, &i->volumeEnv, "Volume");
+        printEnvelope(f, &i->panningEnv, "Panning");
+    }
+    fprintf(f, "\n");
+}
+
+
+void printInstrument(FILE *f, JSSInstrument *i)
+{
+    if (!i)
+    {
+        fprintf(f, "\n");
+        return;
+    }
+
+    if (dmVerbosity >= 1)
+    {
+#ifndef JSS_LIGHT
+        if (i->desc)
+            fprintf(f, "Description: '%s'\n", i->desc);
+#endif
+        fprintf(f,
+            "size...........: %ld (0x%lx)\n"
+            "loopStart......: %ld (0x%lx)\n"
+            "loopEnd........: %ld (0x%lx)\n"
+            "volume.........: %d (0x%x)\n"
+            "flags..........: 0x%x ",
+            (unsigned long) i->size, (unsigned long) i->size,
+            (unsigned long) i->loopS, (unsigned long) i->loopE,
+            (unsigned long) i->loopS, (unsigned long) i->loopE,
+            i->volume, i->volume,
+            i->flags);
+        
+        if (i->flags & jsfLooped)  fprintf(f, "[loop] ");
+        if (i->flags & jsfBiDi)    fprintf(f, "[bi-di] ");
+        if (i->flags & jsf16bit)   fprintf(f, "[16bit] ");
+        
+        fprintf(f,
+            "\nC4BaseSpeed....: %d (0x%x)\n"
+            "ERelNote.......: %d (%s%d)\n"
+            "EFineTune......: %d\n"
+            "EPanning,,,....: %d (0x%x)\n\n",
+            i->C4BaseSpeed, i->C4BaseSpeed,
+            i->ERelNote, patNoteTable[(48 + i->ERelNote) % 12], (48 + i->ERelNote) / 12,
+            i->EFineTune, i->EPanning, i->EPanning);
+    }
+    else
+    {
+#ifndef JSS_LIGHT
+        if (i->desc)
+            fprintf(f, "'%s', ", i->desc);
+#endif
+        fprintf(f,
+        "s=%ld (%lx), l=%ld-%ld (%lx-%lx), v=%i (%x), f=0x%x, c4=%i (%x), rn=%i (%s%i), ft=%i, pn=%i (%x)\n",
+        (unsigned long) i->size, (unsigned long) i->size,
+        (unsigned long) i->loopS, (unsigned long) i->loopE,
+        (unsigned long) i->loopS, (unsigned long) i->loopE,
+        i->volume, i->volume, i->flags, i->C4BaseSpeed,
+        i->C4BaseSpeed, i->ERelNote,
+        patNoteTable[(48 + i->ERelNote) % 12],
+        (48 + i->ERelNote) / 12, i->EFineTune,
+        i->EPanning, i->EPanning);
+    }
+}
+
+
+void printGeneralInfo(FILE *f, JSSModule *m)
+{
+    int i;
+    
+    if (!m)
+        return;
+
+    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;
+    JSSModule *mod;
+
+    dmInitProg("viewmod", "miniJSS Module Viewer", "0.4", NULL, NULL);
+    dmVerbosity = 0;
+
+    // Parse arguments
+    if (!dmArgsProcess(argc, argv, optList, optListN,
+        argHandleOpt, argHandleFile, TRUE))
+        exit(1);
+
+    // Initialize miniJSS
+    jssInit();
+
+    // Open the file
+    dmMsg(1, "Reading module file '%s'\n", optFilename);
+    if (optFilename == NULL)
+        file = dmf_create_stdio_stream(stdin);
+    else if ((file = dmf_create_stdio(optFilename, "rb")) == NULL)
+    {
+        dmError("Error opening input file '%s'. (%s)\n",
+            optFilename, strerror(errno));
+        return 1;
+    }
+
+    // Read module file
+    dmMsg(1, "Reading file: %s\n", optFilename);
+#ifdef JSS_SUP_XM
+    dmMsg(1, "* Trying XM...\n");
+    result = jssLoadXM(file, &mod);
+#endif
+#ifdef JSS_SUP_JSSMOD
+    if (result != 0)
+    {
+        size_t bufgot, bufsize = dmfsize(file);
+        Uint8 *buf = dmMalloc(bufsize);
+        dmfseek(file, 0L, SEEK_SET);
+        dmMsg(1, "* Trying JSSMOD (%d bytes, %p)...\n", bufsize, buf);
+        if ((bufgot = dmfread(buf, 1, bufsize, file)) != bufsize)
+        {
+            dmError("Error reading file (not enough data %d), #%d: %s\n",
+                bufgot, dmferror(file), dmErrorStr(dmferror(file)));
+            return 2;
+        }
+        result = jssLoadJSSMOD(buf, bufsize, &mod);
+        dmFree(buf);
+    }
+#endif
+    dmf_close(file);
+    if (result != DMERR_OK)
+    {
+        dmError("Error loading module file, %d: %s\n",
+            result, dmErrorStr(result));
+        return 3;
+    }
+
+    // Print out information
+    if (optViewGeneralInfo)
+        printGeneralInfo(stdout, mod);
+
+    if (optViewPatterns)
+    {
+        for (i = 0; i < mod->npatterns; i++)
+        {
+            printf("\nPattern #%03i:\n", i);
+            printPattern(stdout, mod->patterns[i]);
+        }
+    }
+
+    if (optViewExtInstruments)
+    {
+        printf("\n"
+        "ExtInstruments:\n"
+        "---------------\n"
+        );
+        for (i = 0; i < mod->nextInstruments; i++)
+        {
+            printf("#%03i: ", i + 1);
+            printExtInstrument(stdout, mod->extInstruments[i]);
+        }
+    }
+
+    if (optViewInstruments)
+    {
+        printf("\n"
+        "Instruments:\n"
+        "------------\n"
+        );
+        for (i = 0; i < mod->ninstruments; i++)
+        {
+            printf("#%03i: ", i + 1);
+            printInstrument(stdout, mod->instruments[i]);
+        }
+    }
+
+    // Free module data
+    jssFreeModule(mod);
+    jssClose();
+
+    exit(0);
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utils/xm2jss.c	Tue Apr 16 06:01:42 2013 +0300
@@ -0,0 +1,1040 @@
+/*
+ * xm2jss - Convert XM module to JSSMOD
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2006-2009 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#include <stdio.h>
+#include <errno.h>
+#include "jss.h"
+#include "jssmod.h"
+#include "jssplr.h"
+#include "dmlib.h"
+#include "dmargs.h"
+#include "dmres.h"
+#include "dmmutex.h"
+
+
+#define jmpNMODEffectTable (36)
+static const char jmpMODEffectTable[jmpNMODEffectTable] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+
+char  *optInFilename = NULL, *optOutFilename = NULL;
+BOOL  optIgnoreErrors = FALSE,
+      optStripExtInstr = FALSE,
+      optStripInstr = FALSE,
+      optStripSamples = FALSE,
+      optOptimize = FALSE;
+
+int   optPatternMode = PATMODE_COMP_HORIZ,
+      optSampMode16 = jsampDelta,
+      optSampMode8 = jsampFlipSign | jsampDelta;
+
+#define SAMPMODE_MASK (jsampFlipSign | jsampSwapEndianess | jsampSplit | jsampDelta)
+
+
+static const char* patModeTable[PATMODE_LAST] =
+{
+    "Raw horizontal",
+    "Compressed horizontal (similar to XM modules)",
+    "Raw vertical",
+    "Compressed vertical",
+    "Raw vertical for each element",
+};
+
+
+DMOptArg optList[] = {
+    { 0, '?', "help",           "Show this help", OPT_NONE },
+    { 1, 'v', "verbose",        "Be more verbose", OPT_NONE },
+    { 2, 'i', "ignore",         "Ignore errors", OPT_NONE },
+    { 3, 'p', "patterns",       "Pattern storage mode", OPT_ARGREQ },
+    { 4, 'E', "strip-ext-instr","Strip ext. instruments (implies -I -S)", OPT_NONE },
+    { 5, 'I', "strip-instr",    "Strip instruments (implies -S)", OPT_NONE },
+    { 6, 'S', "strip-samples",  "Strip instr. sampledata", OPT_NONE },
+    { 7, '8', "smode8",         "8-bit sample conversion flags", OPT_ARGREQ },
+    { 8, '1', "smode16",        "16-bit sample conversion flags", OPT_ARGREQ },
+    { 9, 'O', "optimize",       "Optimize module", OPT_NONE },
+};
+
+const int optListN = sizeof(optList) / sizeof(optList[0]);
+
+
+void argShowHelp()
+{
+    int i;
+
+    dmPrintBanner(stdout, dmProgName, "[options] <input.xm> <output.jmod>");
+    dmArgsPrintHelp(stdout, optList, optListN);
+
+    printf("\n"
+    "Pattern storage modes:\n");
+    
+    for (i = 1; i < PATMODE_LAST; i++)
+        printf("  %d = %s\n", i, patModeTable[i-1]);
+    
+    printf(
+    "\n"
+    "Sample data conversion flags (summative):\n"
+    "  1 = Delta encoding (DEF 8 & 16)\n"
+    "  2 = Flip signedness (DEF 8)\n"
+    "  4 = Swap endianess (affects 16-bit only)\n"
+    "  8 = Split and de-interleave hi/lo bytes (affects 16-bit only)\n"
+    "\n"
+    );
+}
+
+BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
+{
+    (void) optArg;
+    
+    switch (optN)
+    {
+    case 0:
+        argShowHelp();
+        exit(0);
+        break;
+
+    case 1:
+        dmVerbosity++;
+        break;
+
+    case 2:
+        optIgnoreErrors = TRUE;
+        break;
+
+    case 3:
+        optPatternMode = atoi(optArg);
+        if (optPatternMode <= 0 || optPatternMode >= PATMODE_LAST)
+        {
+            dmError("Unknown pattern conversion mode %d\n", optPatternMode);
+            return FALSE;
+        }
+        break;
+
+    case 4: optStripExtInstr = TRUE; break;
+    case 5: optStripInstr = TRUE; break;
+    case 6: optStripSamples = TRUE; break;
+
+    case 7: optSampMode8 = atoi(optArg) & SAMPMODE_MASK; break;
+    case 8: optSampMode16 = atoi(optArg) & SAMPMODE_MASK; break;
+
+    case 9: optOptimize = TRUE; break;
+
+    default:
+        dmError("Unknown argument '%s'.\n", currArg);
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+BOOL argHandleFile(char *currArg)
+{
+    // Was not option argument
+    if (!optInFilename)
+        optInFilename = currArg;
+    else
+    if (!optOutFilename)
+        optOutFilename = currArg;
+    else
+    {
+        dmError("Too many filename arguments specified, '%s'.\n", currArg);
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+/* These functions and the macro mess are meant to make the
+ * conversion routines themselves clearer and simpler.
+ */
+BOOL jsPutByte(Uint8 *patBuf, size_t patBufSize, size_t *npatBuf, Uint8 val)
+{
+    if (*npatBuf >= patBufSize)
+        return FALSE;
+    else
+    {
+        patBuf[*npatBuf] = val;
+        (*npatBuf)++;
+        return TRUE;
+    }
+}
+
+#define JSPUTBYTE(x) do { if (!jsPutByte(patBuf, patBufSize, patSize, x)) return DMERR_BOUNDS; } while (0)
+
+#define JSCOMP(x,z) do { if ((x) != jsetNotSet) { qflags |= (z); qcomp++; } } while (0)
+
+#define JSCOMPPUT(xf,xv,qv)  do {                       \
+    if (qflags & (xf)) {                                \
+        if ((xv) < 0 || (xv) > 255)                     \
+            JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS,        \
+            "%s value out of bounds %d.\n", qv, (xv));  \
+        JSPUTBYTE(xv);                                  \
+    }                                                   \
+} while (0)
+
+#define JSCONVPUT(xv,qv)    do {                        \
+    if ((xv) != jsetNotSet) {                           \
+        if ((xv) < 0 || (xv) > 254)                     \
+            JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS,        \
+            "%s value out of bounds %d.\n", qv, (xv));  \
+        JSPUTBYTE((xv) + 1);                            \
+    } else {                                            \
+        JSPUTBYTE(0);                                   \
+    }                                                   \
+} while (0)
+
+
+/* Convert a note
+ */
+static int jssConvertNote(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSNote *note)
+{
+    Uint8 tmp;
+    if (note->note == jsetNotSet)
+        tmp = 0;
+    else if (note->note == jsetNoteOff)
+        tmp = 127;
+    else
+        tmp = note->note + 1;
+
+    if (tmp > 0x7f)
+        JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS, "Note value out of bounds %d > 0x7f.\n", tmp);
+
+    JSPUTBYTE(tmp & 0x7f);
+    
+    JSCONVPUT(note->instrument, "Instrument");
+    JSCONVPUT(note->volume, "Volume");
+    JSCONVPUT(note->effect, "Effect");
+    
+    tmp = (note->param != jsetNotSet) ? note->param : 0;
+    JSPUTBYTE(tmp);
+    
+    return DMERR_OK;
+}
+
+
+/* Compress a note
+ */
+static int jssCompressNote(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSNote *note)
+{
+    Uint8 qflags = 0;
+    int qcomp = 0;
+    
+    JSCOMP(note->note,       COMP_NOTE);
+    JSCOMP(note->instrument, COMP_INSTRUMENT);
+    JSCOMP(note->volume,     COMP_VOLUME);
+    JSCOMP(note->effect,     COMP_EFFECT);
+    if (note->param != jsetNotSet && note->param != 0)
+    {
+        qflags |= COMP_PARAM;
+        qcomp++;
+    }
+    
+    if (qcomp < 4)
+    {
+        JSPUTBYTE(qflags | 0x80);
+        
+        if (note->note != jsetNotSet)
+        {
+            Uint8 tmp = (note->note != jsetNoteOff) ? note->note : 127;
+            if (tmp > 0x7f)
+                JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS, "Note value out of bounds %d > 0x7f.\n", tmp);
+            JSPUTBYTE(tmp);
+        }
+        
+        JSCOMPPUT(COMP_INSTRUMENT, note->instrument, "Instrument");
+        JSCOMPPUT(COMP_VOLUME, note->volume, "Volume");
+        JSCOMPPUT(COMP_EFFECT, note->effect, "Effect");
+        JSCOMPPUT(COMP_PARAM, note->param, "Param");
+    } else
+        return jssConvertNote(patBuf, patBufSize, patSize, note);
+    
+    return DMERR_OK;
+}
+
+
+/* Compress pattern
+ */
+static int jssConvertPatternCompHoriz(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern)
+{
+    int row, channel;
+    *patSize = 0;
+    
+    for (row = 0; row < pattern->nrows; row++)
+    for (channel = 0; channel < pattern->nchannels; channel++)
+    {
+        const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
+        const int res = jssCompressNote(patBuf, patBufSize, patSize, note);
+        if (res != DMERR_OK)
+        {
+            JSSERROR(res, res, "Note compression failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n",
+            patBuf, patBufSize, *patSize, row, channel);
+            return res;
+        }
+    }
+    
+    return DMERR_OK;
+}
+
+
+static int jssConvertPatternCompVert(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern)
+{
+    int row, channel;
+    *patSize = 0;
+    
+    for (channel = 0; channel < pattern->nchannels; channel++)
+    for (row = 0; row < pattern->nrows; row++)
+    {
+        const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
+        const int res = jssCompressNote(patBuf, patBufSize, patSize, note);
+        if (res != DMERR_OK)
+        {
+            JSSERROR(res, res, "Note compression failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n",
+            patBuf, patBufSize, *patSize, row, channel);
+            return res;
+        }
+    }
+    
+    return DMERR_OK;
+}
+
+
+/* Convert a pattern
+ */
+static int jssConvertPatternRawHoriz(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern)
+{
+    int row, channel;
+    *patSize = 0;
+    
+    for (row = 0; row < pattern->nrows; row++)
+    for (channel = 0; channel < pattern->nchannels; channel++)
+    {
+        const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
+        const int res = jssConvertNote(patBuf, patBufSize, patSize, note);
+        if (res != DMERR_OK)
+        {
+            JSSERROR(res, res, "Note conversion failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n",
+            patBuf, patBufSize, *patSize, row, channel);
+            return res;
+        }
+    }
+    
+    return DMERR_OK;
+}
+
+
+static int jssConvertPatternRawVert(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern)
+{
+    int row, channel;
+    *patSize = 0;
+    
+    for (channel = 0; channel < pattern->nchannels; channel++)
+    for (row = 0; row < pattern->nrows; row++)
+    {
+        const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
+        const int res = jssConvertNote(patBuf, patBufSize, patSize, note);
+        if (res != DMERR_OK)
+        {
+            JSSERROR(res, res, "Note conversion failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n",
+            patBuf, patBufSize, *patSize, row, channel);
+            return res;
+        }
+    }
+    
+    return DMERR_OK;
+}
+
+
+#define JSFOREACHNOTE1                                                              \
+  for (channel = 0; channel < pattern->nchannels; channel++)                        \
+  for (row = 0; row < pattern->nrows; row++) {                                      \
+  const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
+
+#define JSFOREACHNOTE2 }
+
+static int jssConvertPatternRawElem(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern)
+{
+    Uint8 tmp;
+    int row, channel;
+    *patSize = 0;
+    
+    JSFOREACHNOTE1;
+    if (note->note == jsetNotSet)
+        tmp = 0;
+    else if (note->note == jsetNoteOff)
+        tmp = 127;
+    else
+        tmp = note->note + 1;
+    if (tmp > 0x7f)
+        JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS, "Note value out of bounds %d > 0x7f.\n", tmp);
+    JSPUTBYTE(tmp);
+    JSFOREACHNOTE2;
+    
+    JSFOREACHNOTE1;
+    JSCONVPUT(note->instrument, "Instrument");
+    JSFOREACHNOTE2;
+    
+    JSFOREACHNOTE1;
+    JSCONVPUT(note->volume, "Volume");
+    JSFOREACHNOTE2;
+    
+    JSFOREACHNOTE1;
+    JSCONVPUT(note->effect, "Effect");
+    JSFOREACHNOTE2;
+    
+    JSFOREACHNOTE1;
+    tmp = (note->param != jsetNotSet) ? note->param : 0;
+    JSPUTBYTE(tmp);
+    JSFOREACHNOTE2;
+    
+    return DMERR_OK;
+}
+
+#undef JSFOREACHNOTE1
+#undef JSFOREACHNOTE2
+
+
+static void jssCopyEnvelope(JSSMODEnvelope *je, JSSEnvelope *e)
+{
+    int i;
+    
+    je->flags   = e->flags;
+    je->npoints = e->npoints;
+    je->sustain = e->sustain;
+    je->loopS   = e->loopS;
+    je->loopE   = e->loopE;
+    
+    for (i = 0; i < e->npoints; i++)
+    {
+        je->points[i].frame = e->points[i].frame;
+        je->points[i].value = e->points[i].value;
+    }
+}
+
+
+/* Save a JSSMOD file
+ */
+int jssSaveJSSMOD(FILE *outFile, JSSModule *m, int patMode, int flags8, int flags16)
+{
+    JSSMODHeader jssH;
+    int i, pattern, order, instr, totalSize;
+    const size_t patBufSize = 64*1024; // 64kB pattern buffer
+    Uint8 *patBuf;
+
+    // Check the module
+    if (m == NULL)
+        JSSERROR(DMERR_NULLPTR, DMERR_NULLPTR, "Module pointer was NULL\n");
+
+    if ((m->nchannels < 1) || (m->npatterns < 1) || (m->norders < 1))
+        JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS,
+        "Module had invalid values (nchannels=%i, npatterns=%i, norders=%i)\n",
+        m->nchannels, m->npatterns, m->norders);
+
+    // Create the JSSMOD header
+    jssH.idMagic[0]         = 'J';
+    jssH.idMagic[1]         = 'M';
+    jssH.idVersion          = JSSMOD_VERSION;
+    jssH.norders            = m->norders;
+    jssH.npatterns          = m->npatterns;
+    jssH.nchannels          = m->nchannels;
+    jssH.nextInstruments    = m->nextInstruments;
+    jssH.ninstruments       = m->ninstruments;
+    jssH.defFlags           = m->defFlags;
+    jssH.intVersion         = m->intVersion;
+    jssH.defRestartPos      = m->defRestartPos;
+    jssH.defSpeed           = m->defSpeed;
+    jssH.defTempo           = m->defTempo;
+    jssH.patMode            = patMode;
+    
+    // Write header
+    totalSize = sizeof(jssH);
+    if (fwrite(&jssH, sizeof(jssH), 1, outFile) != 1)
+        JSSERROR(DMERR_FWRITE, DMERR_FWRITE, "Could not write JSSMOD header!\n");
+
+    dmMsg(1," * JSSMOD-header 0x%04x, %d bytes.\n", JSSMOD_VERSION, totalSize);
+
+    // Write orders list
+    for (totalSize = order = 0; order < m->norders; order++)
+    {
+        Uint16 tmp = m->orderList[order];
+        totalSize += sizeof(tmp);
+        if (fwrite(&tmp, sizeof(tmp), 1, outFile) != 1)
+            JSSERROR(DMERR_FWRITE, DMERR_FWRITE, "Could not write JSSMOD orders list.\n");
+    }
+
+    dmMsg(1," * %d item orders list, %d bytes.\n",
+        m->norders, totalSize);
+
+    // Allocate pattern compression buffer
+    if ((patBuf = dmMalloc(patBufSize)) == NULL)
+        JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
+        "Error allocating memory for pattern compression buffer.\n");
+
+    
+    // Write patterns
+    for (totalSize = pattern = 0; pattern < m->npatterns; pattern++)
+    {
+        JSSMODPattern patHead;
+        size_t finalSize = 0;
+        
+        switch (patMode)
+        {
+            case PATMODE_RAW_HORIZ:
+                i = jssConvertPatternRawHoriz(patBuf, patBufSize, &finalSize, m->patterns[pattern]);
+                break;
+            case PATMODE_COMP_HORIZ:
+                i = jssConvertPatternCompHoriz(patBuf, patBufSize, &finalSize, m->patterns[pattern]);
+                break;
+            case PATMODE_RAW_VERT:
+                i = jssConvertPatternRawVert(patBuf, patBufSize, &finalSize, m->patterns[pattern]);
+                break;
+            case PATMODE_COMP_VERT:
+                i = jssConvertPatternCompVert(patBuf, patBufSize, &finalSize, m->patterns[pattern]);
+                break;
+            case PATMODE_RAW_ELEM:
+                i = jssConvertPatternRawElem(patBuf, patBufSize, &finalSize, m->patterns[pattern]);
+                break;
+            default:
+                i = DMERR_INVALID_DATA;
+                dmFree(patBuf);
+                JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+                "Unsupported pattern conversion mode %d.\n", patMode);
+                break;
+        }
+
+        if (i != DMERR_OK)
+        {
+            dmFree(patBuf);
+            JSSERROR(i, i, "Error converting pattern data #%i\n", pattern);
+        }
+        else
+        {
+            dmMsg(3, " - Pattern %d size %d bytes\n", pattern, finalSize);
+            patHead.nrows = m->patterns[pattern]->nrows;
+            patHead.size = finalSize;
+            totalSize += finalSize + sizeof(patHead);
+
+            if (fwrite(&patHead, sizeof(patHead), 1, outFile) != 1)
+            {
+                dmFree(patBuf);
+                JSSERROR(DMERR_FWRITE, DMERR_FWRITE,
+                "Error writing pattern #%d header\n", pattern);
+            }
+
+            if (fwrite(patBuf, sizeof(Uint8), finalSize, outFile) != finalSize)
+            {
+                dmFree(patBuf);
+                JSSERROR(DMERR_FWRITE, DMERR_FWRITE,
+                "Error writing pattern #%d data\n", pattern);
+            }
+        }
+    }
+    dmFree(patBuf);
+    dmMsg(1," * %d patterns, %d bytes.\n", m->npatterns, totalSize);
+
+    // Write extended instruments
+    for (totalSize = instr = 0; instr < m->nextInstruments; instr++)
+    {
+        JSSMODExtInstrument jssE;
+        JSSExtInstrument *einst = m->extInstruments[instr];
+        
+        memset(&jssE, 0, sizeof(jssE));
+
+        if (einst)
+        {
+            // Create header
+            jssE.nsamples = einst->nsamples;
+            for (i = 0; i < jsetNNotes; i++)
+            {
+                int snum = einst->sNumForNotes[i];
+                jssE.sNumForNotes[i] = (snum != jsetNotSet) ? snum : 0;
+            }
+            
+            jssCopyEnvelope(&jssE.volumeEnv, &(einst->volumeEnv));
+            jssCopyEnvelope(&jssE.panningEnv, &(einst->panningEnv));
+            jssE.vibratoType  = einst->vibratoType;
+            jssE.vibratoSweep = einst->vibratoSweep;
+            jssE.vibratoDepth = einst->vibratoDepth;
+            jssE.fadeOut      = einst->fadeOut;
+        } else
+            JSSWARNING(DMERR_NULLPTR, DMERR_NULLPTR, "Extended instrument #%i NULL!\n", instr);
+        
+        // Write to file
+        totalSize += sizeof(jssE);
+        if (fwrite(&jssE, sizeof(jssE), 1, outFile) != 1)
+            JSSERROR(DMERR_FWRITE, DMERR_FWRITE,
+            "Could not write JSSMOD extended instrument #%i to file!\n", instr);
+    }
+    dmMsg(1," * %d Extended Instruments, %d bytes.\n", m->nextInstruments, totalSize);
+
+
+    // Write sample instrument headers
+    for (totalSize = instr = 0; instr < m->ninstruments; instr++)
+    {
+        JSSMODInstrument jssI;
+        JSSInstrument *pInst = m->instruments[instr];
+
+        memset(&jssI, 0, sizeof(jssI));
+
+        // Create header
+        if (pInst)
+        {
+            jssI.size         = pInst->size;
+            jssI.loopS        = pInst->loopS;
+            jssI.loopE        = pInst->loopE;
+            jssI.volume       = pInst->volume;
+            jssI.flags        = pInst->flags;
+            jssI.C4BaseSpeed  = pInst->C4BaseSpeed;
+            jssI.ERelNote     = pInst->ERelNote;
+            jssI.EFineTune    = pInst->EFineTune;
+            jssI.EPanning     = pInst->EPanning;
+            jssI.hasData      = (pInst->data != NULL) ? TRUE : FALSE;
+            jssI.convFlags = (pInst->flags & jsf16bit) ? flags16 : flags8;
+        }
+        else 
+            JSSWARNING(DMERR_NULLPTR, DMERR_NULLPTR, "Instrument #%i NULL!\n", instr);
+        
+        // Write to file
+        totalSize += sizeof(jssI);
+        if (fwrite(&jssI, sizeof(jssI), 1, outFile) != 1)
+            JSSERROR(DMERR_FWRITE, DMERR_FWRITE,
+             "Could not write JSSMOD instrument #%i to file!\n", instr);
+    }
+    dmMsg(1," * %d Instrument headers, %d bytes.\n", m->ninstruments, totalSize);
+    
+    // Write sample data
+    for (totalSize = instr = 0; instr < m->ninstruments; instr++)
+    if (m->instruments[instr])
+    {
+        JSSInstrument *inst = m->instruments[instr];
+        if (inst->data != NULL)
+        {
+            size_t res;
+            if (inst->flags & jsf16bit)
+            {
+                jssEncodeSample16(inst->data, inst->size, flags16);
+                res = fwrite(inst->data, sizeof(Uint16), inst->size, outFile);
+            }
+            else
+            {
+                jssEncodeSample8(inst->data, inst->size, flags8);
+                res = fwrite(inst->data, sizeof(Uint8), inst->size, outFile);
+            }
+            
+            totalSize += inst->size;
+            if (res != (size_t) inst->size)
+                JSSERROR(DMERR_FWRITE, DMERR_FWRITE,
+                 "Could not write JSSMOD sample #%i to file!\n", instr);
+        }
+    }
+    dmMsg(1," * %d samples, %d bytes.\n", m->ninstruments, totalSize);
+    
+    return DMERR_OK;
+}
+
+
+/* Optimize a given module
+ */
+JSSModule *optimizeModule(JSSModule *m)
+{
+    BOOL usedPatterns[jsetMaxPatterns + 1],
+         usedInstruments[jsetMaxInstruments + 1],
+         usedExtInstruments[jsetMaxInstruments + 1];
+    int  mapExtInstruments[jsetMaxInstruments + 1],
+         mapInstruments[jsetMaxInstruments + 1],
+         mapPatterns[jsetMaxPatterns + 1];
+    JSSModule *r = NULL;
+    int i, n8, n16;
+
+    // Allocate a new module
+    if ((r = jssAllocateModule()) == NULL)
+        return NULL;
+
+    // Allocate tables
+    
+    // Copy things
+    r->moduleType       = m->moduleType;
+    r->moduleName       = dm_strdup(m->moduleName);
+    r->trackerName      = dm_strdup(m->trackerName);
+    r->defSpeed         = m->defSpeed;
+    r->defTempo         = m->defTempo;
+    r->defFlags         = m->defFlags;
+    r->defRestartPos    = m->defRestartPos;
+    r->intVersion       = m->intVersion;
+    r->nchannels        = m->nchannels;
+    r->norders          = m->norders;
+    for (i = 0; i < jsetNChannels; i++)
+        r->defPanning[i] = m->defPanning[i];
+    
+    // Initialize values
+    for (i = 0; i <= jsetMaxInstruments; i++)
+    {
+        usedExtInstruments[i] = FALSE;
+        usedInstruments[i] = FALSE;
+        mapExtInstruments[i] = jsetNotSet;
+        mapInstruments[i] = jsetNotSet;
+    }
+    
+    for (i = 0; i <= jsetMaxPatterns; i++)
+    {
+        usedPatterns[i] = FALSE;
+        mapPatterns[i] = jsetNotSet;
+    }
+
+    // Find out all used patterns and ext.instruments
+    for (i = 0; i < m->norders; i++)
+    {
+        int pattern = m->orderList[i];
+        if (pattern >= 0 && pattern < m->npatterns)
+        {
+            JSSPattern *p = m->patterns[pattern];
+            if (p != NULL)
+            {
+                int row, channel;
+                JSSNote *n = p->data;
+                
+                // Mark pattern as used
+                usedPatterns[pattern] = TRUE;
+                
+                // Check all notes
+                for (row = 0; row < p->nrows; row++)
+                for (channel = 0; channel < p->nchannels; channel++, n++)
+                {
+                    if (n->instrument != jsetNotSet)
+                    {
+                        if (optStripExtInstr || (n->instrument >= 0 && n->instrument < m->nextInstruments))
+                            usedExtInstruments[n->instrument] = TRUE;
+                        else
+                            dmMsg(2, "Pattern 0x%x, row=0x%x, chn=%d has invalid instrument 0x%x\n",
+                            pattern, row, channel, n->instrument);
+                    }
+                }
+            }
+            else
+            {
+                dmError("Pattern 0x%x is used on order 0x%x, but has no data!\n",
+                pattern, i);
+            }
+        }
+        else
+        if (pattern != jsetMaxPatterns)
+        {
+            dmError("Order 0x%x has invalid pattern number 0x%x!\n",
+            i, pattern);
+        }
+    }
+    
+    // Find out used instruments
+    for (i = 0; i <= jsetMaxInstruments; i++)
+    if (usedExtInstruments[i] && m->extInstruments[i] != NULL)
+    {
+        int note;
+        JSSExtInstrument *e = m->extInstruments[i];
+        
+        for (note = 0; note < jsetNNotes; note++)
+        if (e->sNumForNotes[note] != jsetNotSet)
+        {
+            int q = e->sNumForNotes[note];
+            if (q >= 0 && q < m->ninstruments)
+            {
+                usedInstruments[q] = TRUE;
+            }
+            else
+            {
+                dmError("Ext.instrument #%d sNumForNotes[%d] value out range (%d < %d).\n",
+                i, m->ninstruments, q);
+            }
+        }
+    }
+    
+    // Create pattern mappings
+    r->npatterns = 0;
+    dmMsg(1, "Unused patterns: ");
+        
+    for (i = 0; i <= jsetMaxPatterns; i++)
+    if (m->patterns[i] != NULL)
+    {
+        if (!usedPatterns[i])
+        {
+            dmPrint(2, "0x%x, ", i);
+        }
+        else
+        {
+            if (i >= m->npatterns)
+                dmError("Pattern 0x%x >= 0x%x, but used!\n", i, m->npatterns);
+            
+            mapPatterns[i] = r->npatterns;
+            r->patterns[r->npatterns] = m->patterns[i];
+            (r->npatterns)++;
+        }
+    }
+    dmPrint(2, "\n");
+    
+    dmMsg(1, "%d used patterns, %d unused.\n",
+    r->npatterns, m->npatterns - r->npatterns);
+    
+    
+    // Re-map instruments
+    dmMsg(1, "Unused instruments: ");
+    for (n8 = n16 = i = 0; i <= jsetMaxInstruments; i++)
+    if (m->instruments[i] != NULL)
+    {
+        if (!usedInstruments[i] && !optStripInstr)
+        {
+            dmPrint(2, "0x%x, ", i);
+        }
+        else
+        {
+            JSSInstrument *ip = m->instruments[i];
+            if (i >= m->ninstruments)
+                dmError("Instrument 0x%x >= 0x%x, but used!\n", i, m->ninstruments);
+            
+            mapInstruments[i] = r->ninstruments;
+            r->instruments[r->ninstruments] = ip;
+            (r->ninstruments)++;
+
+            if (ip->flags & jsf16bit)
+                n16++;
+            else
+                n8++;
+        }
+    }
+    dmPrint(2, "\n");
+    dmMsg(1, "Total of (%d)  16-bit, (%d) 8-bit samples, (%d) instruments.\n",
+    n16, n8, r->ninstruments);
+    
+    // Re-map ext.instruments
+    dmMsg(1, "Unused ext.instruments: ");
+    for (i = 0; i < jsetMaxInstruments; i++)
+    if (usedExtInstruments[i])
+    {
+        if (i >= m->nextInstruments && !optStripExtInstr)
+        {
+            dmError("Ext.instrument 0x%x >= 0x%x, but used!\n",
+            i, m->nextInstruments);
+        }
+        else
+        if (m->extInstruments[i] != NULL)
+        {
+            JSSExtInstrument *e = m->extInstruments[i];
+            int note;
+            
+            mapExtInstruments[i] = r->nextInstruments;
+            r->extInstruments[r->nextInstruments] = e;
+            (r->nextInstruments)++;
+            
+            // Re-map sNumForNotes
+            for (note = 0; note < jsetNNotes; note++)
+            {
+                int q = e->sNumForNotes[note];
+                if (q != jsetNotSet)
+                {
+                    int map;
+                    if (q >= 0 && q <= jsetMaxInstruments)
+                    {
+                        map = mapInstruments[q];
+                    }
+                    else
+                    {
+                        map = jsetNotSet;
+                        dmError("e=%d, note=%d, q=%d/%d\n", i, note, q, r->ninstruments);
+                    }
+                    e->sNumForNotes[note] = map;
+                }
+            }
+        }
+        else
+        {
+            dmPrint(2, "[0x%x==NULL], ", i);
+            mapExtInstruments[i] = jsetNotSet;
+        }
+    }
+    else
+    {
+        if (i < m->nextInstruments && m->extInstruments[i] != NULL)
+        {
+            dmPrint(2, "0x%x, ", i);
+        }
+    }
+    dmPrint(2, "\n");
+    dmMsg(1, "%d extended instruments.\n", r->nextInstruments);
+    
+    
+    // Remap pattern instrument data
+    for (i = 0; i < r->npatterns; i++)
+    {
+        int row, channel;
+        JSSPattern *p = r->patterns[i];
+        JSSNote *n = p->data;
+        
+        for (row = 0; row < p->nrows; row++)
+        for (channel = 0; channel < p->nchannels; channel++, n++)
+        {
+            char effect;
+
+            if (!optStripExtInstr)
+            {
+                if (n->instrument >= 0 && n->instrument <= jsetMaxInstruments)
+                    n->instrument = mapExtInstruments[n->instrument];
+
+                if (n->instrument != jsetNotSet && r->extInstruments[n->instrument] == NULL)
+                    dmError("Non-existing instrument used #%d.\n", n->instrument);
+            }
+
+            JMPGETEFFECT(effect, n->effect);
+            
+            switch (effect)
+            {
+                case 'C': // Cxx = Set volume
+                    if (n->volume == jsetNotSet)
+                    {
+                        n->volume = n->param;
+                        n->effect = jsetNotSet;
+                        n->param = jsetNotSet;
+                    }
+                    break;
+            }
+        }
+    }
+    
+    // Remap orders list
+    for (i = 0; i < m->norders; i++)
+    {
+        r->orderList[i] = mapPatterns[m->orderList[i]];
+    }
+
+    return r;
+}
+
+
+int main(int argc, char *argv[])
+{
+    DMResource *sfile = NULL;
+    FILE *dfile = NULL;
+    JSSModule *sm, *dm;
+    int result;
+
+    dmInitProg("xm2jss", "XM to JSSMOD converter", "0.6", NULL, NULL);
+    dmVerbosity = 0;
+
+    // Parse arguments
+    if (!dmArgsProcess(argc, argv, optList, optListN,
+        argHandleOpt, argHandleFile, TRUE))
+        exit(1);
+
+    // Check arguments
+    if (optInFilename == NULL || optOutFilename == NULL)
+    {
+        dmError("Input or output file not specified. Try --help.\n");
+        return 1;
+    }
+
+    // Read the source file
+    if ((sfile = dmf_create_stdio(optInFilename, "rb")) == NULL)
+    {
+        dmError("Error opening input file '%s', %d: %s\n",
+            optInFilename, errno, strerror(errno));
+        return 1;
+    }
+
+    // Initialize miniJSS
+    jssInit();
+
+    // Read file
+    dmMsg(1, "Reading XM-format file ...\n");
+    result = jssLoadXM(sfile, &sm);
+    dmf_close(sfile);
+    if (result != 0)
+    {
+        dmError("Error while loading XM file (%i), ", result);
+        if (optIgnoreErrors)
+            fprintf(stderr, "ignoring. This may cause problems.\n");
+        else
+        {
+            fprintf(stderr, "giving up. Use --ignore if you want to try to convert anyway.\n");
+            return 2;
+        }
+    }
+
+    // Check stripping settings
+    if (optStripExtInstr) optStripInstr = TRUE;
+    if (optStripInstr) optStripSamples = TRUE;
+    
+    // Remove samples
+    if (optStripSamples)
+    {
+        int i;
+        
+        dmMsg(1, "Stripping samples...\n");
+        for (i = 0; i < sm->ninstruments; i++)
+        {
+            dmFree(sm->instruments[i]->data);
+            sm->instruments[i]->data = NULL;
+        }
+    }
+
+    // Remove instruments
+    if (optStripInstr)
+    {
+        int i;
+        
+        dmMsg(1, "Stripping instruments...\n");
+        for (i = 0; i < sm->ninstruments; i++)
+        {
+            dmFree(sm->instruments[i]);
+            sm->instruments[i] = NULL;
+        }
+        sm->ninstruments = 0;
+    }
+
+    // Remove ext.instruments
+    if (optStripExtInstr)
+    {
+        int i;
+        
+        dmMsg(1, "Stripping ext.instruments...\n");
+        for (i = 0; i < sm->nextInstruments; i++)
+        {
+            dmFree(sm->extInstruments[i]);
+            sm->extInstruments[i] = NULL;
+        }
+        sm->nextInstruments = 0;
+    }
+    // Run the optimization procedure
+    if (optOptimize)
+    {
+        dmMsg(1, "Optimizing module data...\n");
+        dm = optimizeModule(sm);
+    } else
+        dm = sm;
+
+    // Write output file
+    if ((dfile = fopen(optOutFilename, "wb")) == NULL)
+    {
+        dmError("Error creating output file '%s', %d: %s\n",
+            optOutFilename, errno, strerror(errno));
+        return 1;
+    }
+
+    dmMsg(1, "Writing JSSMOD-format file [patMode=0x%04x, samp8=0x%02x, samp16=0x%02x]\n",
+        optPatternMode, optSampMode8, optSampMode16);
+    
+    result = jssSaveJSSMOD(dfile, dm, optPatternMode, optSampMode8, optSampMode16);
+    
+    fclose(dfile);
+    
+    if (result != 0)
+    {
+        dmError("Error while saving JSSMOD file, %d: %s\n",
+            result, dmErrorStr(result));
+        dmError("WARNING: The resulting file may be broken!\n");
+    }
+    else
+    {
+        dmMsg(1, "Conversion complete.\n");
+    }
+    return 0;
+}
--- a/vecmattest.c	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-#include "dmlib.h"
-#include "dmvecmat.h"
-#include "dmmutex.h"
-
-void printTest(const char *test, int expected, int result)
-{
-    fprintf(stderr, "Test '%s': %s\n", test,
-        expected == result ? "OK" : "FAILED!");
-}
-
-#define tst(X, R) printTest(# X, (X), (R))
-
-
-void dm_vector_fprintf(FILE *f, const char *name, DMVector *v)
-{
-    if (name != NULL)
-        fprintf(f, "%s=", name);
-
-    fprintf(f, "[<%1.3f, %1.3f, %1.3f>=%1.3f]", v->x, v->y, v->z, dm_vector_length(v));
-
-    if (name != NULL)
-        fprintf(f, "\n");
-}
-
-
-void dm_vector_printf(const char *name, DMVector *v)
-{
-    dm_vector_fprintf(stdout, name, v);
-}
-
-
-void dm_matrix_fprintf(FILE *f, const char *name, DMMatrix *mat)
-{
-    int i, j, k, pad = 0;
-    char *tmp = NULL;
-
-    if (name != NULL)
-    {
-        tmp = dm_strdup_printf("%s=", name);
-        pad = strlen(tmp);
-    }
-
-    for (i = 0; i < DM_MATRIX_SIZE; i++)
-    {
-        if (i == 1)
-            fputs(tmp, f);
-        else
-            for (k = 0; k < pad; k++)
-                fputc(' ', f);
-
-        fprintf(f, "[");
-        for (j = 0; j < DM_MATRIX_SIZE; j++)
-            fprintf(f, "% 8.3f%s", mat->m[i][j], j < DM_MATRIX_SIZE - 1 ? " " : "");
-        
-        fprintf(f, "]\n");
-    }
-}
-
-
-void dm_matrix_printf(const char *name, DMMatrix *mat)
-{
-    dm_matrix_fprintf(stdout, name, mat);
-}
-
-
-int main(int argc, char *argv[])
-{
-    DMVector a = { -5, 1, 17, 0 }, b = { 1, 2, 0.5, 0 };
-    DMMatrix m;
-
-    (void) argc;
-    (void) argv;    
-
-    dm_vector_printf("a", &a);
-    dm_vector_printf("b", &a);
-    
-    dm_matrix_rot_a(&m, 0.5, 0.9, 0.1);
-    dm_matrix_printf("m", &m);
-
-    dm_vector_mul_by_mat(&b, &a, &m);
-    
-    dm_vector_printf("nb", &b);
-    
-    return 0;
-}
--- a/view64.c	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,321 +0,0 @@
-/*
- * view64 - Display some C64 etc graphics formats via libSDL
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2012 Tecnic Software productions (TNSP)
- *
- * Please read file 'COPYING' for information on license and distribution.
- */
-#include "dmlib.h"
-#include "dmargs.h"
-#include "dmfile.h"
-#include "lib64gfx.h"
-#include <SDL.h>
-
-
-char * optFilename = NULL;
-int    optVFlags = SDL_SWSURFACE | SDL_HWPALETTE;
-int    optScrWidth, optScrHeight;
-int    optForcedFormat = -1;
-
-
-static DMOptArg optList[] =
-{
-    { 0, '?', "help",       "Show this help", OPT_NONE },
-    { 1, 'v', "verbose",    "Be more verbose", OPT_NONE },
-    { 2,   0, "fs",         "Fullscreen", OPT_NONE },
-    { 3, 'S', "scale",      "Scale image by factor (1-10)", OPT_ARGREQ },
-    { 4, 'f', "format",     "Force input format (see list below)", OPT_ARGREQ },
-};
-
-const int optListN = sizeof(optList) / sizeof(optList[0]);
-
-
-void dmSetScaleFactor(float factor)
-{
-    optScrWidth = (int) ((float) C64_SCR_WIDTH * factor * C64_SCR_PAR_XY);
-    optScrHeight = (int) ((float) C64_SCR_HEIGHT * factor);
-}
-
-
-void argShowHelp()
-{
-    int i;
-
-    dmPrintBanner(stdout, dmProgName, "[options] <input image file>");
-    dmArgsPrintHelp(stdout, optList, optListN);
-
-    printf("\nAvailable bitmap formats:\n");
-    for (i = 0; i < ndmC64ImageFormats; i++)
-    {
-        const DMC64ImageFormat *fmt = &dmC64ImageFormats[i];
-        char buf[64];
-        printf("%3d | %-5s | %-15s | %s\n",
-            i, fmt->fext,
-            dmC64GetImageTypeString(buf, sizeof(buf), fmt->type),
-            fmt->name);
-    }
-}
-
-
-BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
-{
-    switch (optN)
-    {
-        case 0:
-            argShowHelp();
-            exit(0);
-            break;
-
-        case 1:
-            dmVerbosity++;
-            break;
-        
-        case 2:
-            optVFlags |= SDL_FULLSCREEN;
-            break;
-
-        case 3:
-            {
-                float factor;
-                if (sscanf(optArg, "%f", &factor) == 1)
-                {
-                    if (factor < 1 || factor >= 10)
-                    {
-                        dmError("Invalid scale factor %1.0f, see help for valid values.\n", factor);
-                        return FALSE;
-                    }
-
-                    dmSetScaleFactor(factor);
-                }
-                else
-                {
-                    dmError("Invalid scale factor '%s'.\n", optArg);
-                    return FALSE;
-                }
-            }
-            break;
-
-        case 4:
-            {
-                int i;
-                if (sscanf(optArg, "%d", &i) == 1)
-                {
-                    if (i < 0 || i >= ndmC64ImageFormats)
-                    {
-                        dmError("Invalid image format index %d, see help for valid values.\n", i);
-                        return FALSE;
-                    }
-                    optForcedFormat = i;
-                }
-                else
-                {
-                    dmError("Invalid image format argument '%s'.\n", optArg);
-                    return FALSE;
-                }
-            }
-            break;
-        
-        default:
-            dmError("Unknown option '%s'.\n", currArg);
-            return FALSE;
-    }
-    
-    return TRUE;
-}
-
-
-BOOL argHandleFile(char *filename)
-{
-    if (optFilename == NULL)
-    {
-        optFilename = dm_strdup(filename);
-        return TRUE;
-    }
-    else
-    {
-        dmError("Too many filenames specified ('%s')\n", filename);
-        return FALSE;
-    }
-}
-
-
-BOOL dmInitializeVideo(SDL_Surface **screen)
-{
-    *screen = SDL_SetVideoMode(optScrWidth, optScrHeight, 8, optVFlags | SDL_RESIZABLE);
-    if (*screen == NULL)
-    {
-        dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError());
-        return FALSE;
-    }
-    return TRUE;
-}
-
-
-int main(int argc, char *argv[])
-{
-    SDL_Surface *screen = NULL, *surf = NULL;
-    DMImage bmap;
-    BOOL initSDL = FALSE, exitFlag, needRedraw;
-    const DMC64ImageFormat *fmt = NULL, *forced;
-    DMC64Image image;
-    char *windowTitle;
-    Uint8 *dataBuf = NULL;
-    size_t dataSize;
-    int ret;
-
-    dmSetScaleFactor(2.0);
-    
-    dmInitProg("view64", "Display some C64 bitmap graphics formats", "0.2", NULL, NULL);
-
-    /* Parse arguments */
-    if (!dmArgsProcess(argc, argv, optList, optListN,
-        argHandleOpt, argHandleFile, FALSE))
-        exit(1);
-
-
-    if (optFilename == NULL)
-    {
-        dmError("No input file specified, perhaps you need some --help\n");
-        goto error;
-    }
-        
-    if ((ret = dmReadDataFile(NULL, optFilename, &dataBuf, &dataSize)) != DMERR_OK)
-        goto error;
-
-    dmMsg(1, "Read %d bytes of input.\n", dataSize);
-
-    // Probe for format
-    if (optForcedFormat >= 0)
-    {
-        forced = &dmC64ImageFormats[optForcedFormat];
-        dmMsg(0,"Forced %s format image, type %d, %s\n",
-            forced->name, forced->type, forced->fext);
-    }
-    else
-        forced = NULL;
-
-    ret = dmC64DecodeBMP(&image, dataBuf, dataSize, 0, 2, &fmt, forced);
-    if (forced == NULL && fmt != NULL)
-    {
-        dmMsg(0,"Probed %s format image, type %d, %s\n",
-            fmt->name, fmt->type, fmt->fext);
-    }
-
-    if (ret < 0)
-    {
-        dmError("Probing could not find any matching image format (%d). Perhaps try forcing a format via -f\n", ret);
-        return -1;
-    }
-
-
-    // Initialize libSDL
-    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
-    {
-        dmError("Could not initialize SDL: %s\n", SDL_GetError());
-        goto error;
-    }
-    initSDL = TRUE;
-
-
-    // Open window/set video mode
-    screen = SDL_SetVideoMode(optScrWidth, optScrHeight, 8, optVFlags | SDL_RESIZABLE);
-    if (screen == NULL)
-    {
-        dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError());
-        goto error;
-    }
-
-    // Create surface (we are lazy and ugly)
-    surf = SDL_CreateRGBSurface(SDL_SWSURFACE, C64_SCR_WIDTH, C64_SCR_HEIGHT, 8, 0, 0, 0, 0);
-    SDL_SetColors(surf, (SDL_Color *)dmC64Palette, 0, C64_NCOLORS);
-    SDL_SetColors(screen, (SDL_Color *)dmC64Palette, 0, C64_NCOLORS);
-
-    // Convert bitmap (this is a bit ugly and lazy here)
-    bmap.data = surf->pixels;
-    bmap.pitch = surf->pitch;
-    bmap.width = surf->w;
-    bmap.height = surf->h;
-    bmap.constpal = TRUE;
-
-    if (fmt->convertFrom != NULL)
-        ret = fmt->convertFrom(&bmap, &image, TRUE);
-    else
-        ret = dmC64ConvertGenericBMP2Image(&bmap, &image, TRUE);
-
-
-    // Set window title and caption
-    windowTitle = dm_strdup_printf("%s - %s", dmProgName, optFilename);
-    SDL_WM_SetCaption(windowTitle, dmProgName);
-    dmFree(windowTitle);
-
-
-    // Start main loop
-    needRedraw = TRUE;
-    exitFlag = FALSE;
-    while (!exitFlag)
-    {
-        SDL_Event event;
-        while (SDL_PollEvent(&event))
-        switch (event.type)
-        {
-            case SDL_KEYDOWN:
-                switch (event.key.keysym.sym)
-                {
-                    case SDLK_ESCAPE: exitFlag = TRUE; break;
-                    
-                    default:
-                        break;
-                }
-
-                needRedraw = TRUE;
-                break;
-            
-            case SDL_VIDEORESIZE:
-                optScrWidth = event.resize.w;
-                optScrHeight = event.resize.h;
-
-                if (!dmInitializeVideo(&screen))
-                    goto error;
-
-                needRedraw = TRUE;
-                break;
-            
-            case SDL_VIDEOEXPOSE:
-                needRedraw = TRUE;
-                break;
-
-            case SDL_QUIT:
-                exit(0);
-        }
-        
-        if (needRedraw)
-        {
-            if (SDL_MUSTLOCK(screen) != 0 && SDL_LockSurface(screen) != 0)
-            {
-                dmError("Can't lock surface.\n");
-                goto error;
-            }
-            
-            dmScaledBlitSurface8to8(surf, 0, 0, screen->w, screen->h, screen);
-            SDL_SetColors(screen, (SDL_Color *)dmC64Palette, 0, C64_NCOLORS);
-
-            if (SDL_MUSTLOCK(screen) != 0)
-                SDL_UnlockSurface(screen);
-
-            SDL_Flip(screen);
-            needRedraw = FALSE;
-        }
-        
-        SDL_Delay(100);
-    }
-
-
-error:
-    if (screen)
-        SDL_FreeSurface(screen);
-
-    if (initSDL)
-        SDL_Quit();
-
-    return 0;
-}
--- a/viewmod.c	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,475 +0,0 @@
-/*
- * viewmod - View information about given module file
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2006-2007 Tecnic Software productions (TNSP)
- *
- * Please read file 'COPYING' for information on license and distribution.
- */
-#include "jss.h"
-#include "jssmod.h"
-#include <errno.h>
-#include <string.h>
-#include "dmargs.h"
-#include "dmmutex.h"
-
-
-char    *optFilename = NULL;
-BOOL    optViewPatterns = FALSE,
-        optViewInstruments = FALSE,
-        optViewExtInstruments = FALSE,
-        optViewGeneralInfo = FALSE;
-
-
-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 },
-    { 5, 'e', "extinstruments", "View extended instruments", OPT_NONE },
-    { 3, 'g', "general", "General information", OPT_NONE },
-    { 4, 'v', "verbose", "Be more verbose", OPT_NONE },
-};
-
-const int optListN = sizeof(optList) / sizeof(optList[0]);
-
-
-void argShowHelp()
-{
-    dmPrintBanner(stdout, dmProgName, "[options] [modfile]");
-    dmArgsPrintHelp(stdout, optList, optListN);
-}
-
-
-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:
-            optViewGeneralInfo = TRUE;
-            break;
-
-        case 4:
-            dmVerbosity++;
-            break;
-
-        case 5:
-            optViewExtInstruments = TRUE;
-            break;
-
-        default:
-            dmError("Unknown argument '%s'.\n", currArg);
-            return FALSE;
-    }
-    
-    return TRUE;
-}
-
-
-BOOL argHandleFile(char *currArg)
-{
-    // Was not option argument
-    if (!optFilename)
-        optFilename = currArg;
-    else {
-        dmError("Gay error '%s'!\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";
-
-
-/* Print a given pattern
- */
-void printPattern(FILE *f, JSSPattern *p)
-{
-    int i, j;
-    char c;
-    JSSNote *n;
-
-    if (!p)
-        return;
-
-    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%i ", patNoteTable[n->note % 12], n->note / 12);
-                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, JSSEnvelope *e, char *s)
-{
-    int i;
-
-    fprintf(f,
-        "\t%s-envelope:\n"
-        "\t - flags.....: %.4x", s, e->flags);
-
-    if (e->flags & jenvfUsed)
-        fprintf(f, " [used]");
-    if (e->flags & jenvfSustain)
-        fprintf(f, " [sust]");
-    if (e->flags & jenvfLooped)
-        fprintf(f, " [loop]");
-
-    fprintf(f, "\n"
-        "\t - npoints...: %i\n"
-        "\t - sustain...: %i\n"
-        "\t - loopS.....: %i\n"
-        "\t - loopE.....: %i\n",
-        e->npoints, e->sustain, e->loopS, e->loopE);
-
-    if (dmVerbosity >= 2)
-    {
-        fprintf(f, "\t - 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, JSSExtInstrument *i)
-{
-    if (!i)
-    {
-        fprintf(f, "\n");
-        return;
-    }
-
-#ifndef JSS_LIGHT
-    if (i->desc)
-        fprintf(f,
-        "Description: '%s'\n", i->desc);
-#endif
-    fprintf(f,
-        "nsamples.......: %i\n"
-        "vibratoType....: %i\n"
-        "vibratoSweep...: %i\n"
-        "vibratoDepth...: %i\n"
-        "vibratoRate....: %i\n"
-        "fadeOut........: %i\n",
-        i->nsamples, i->vibratoType, i->vibratoSweep,
-        i->vibratoDepth, i->vibratoRate, i->fadeOut);
-
-    if (dmVerbosity >= 1)
-    {
-        printEnvelope(f, &i->volumeEnv, "Volume");
-        printEnvelope(f, &i->panningEnv, "Panning");
-    }
-    fprintf(f, "\n");
-}
-
-
-void printInstrument(FILE *f, JSSInstrument *i)
-{
-    if (!i)
-    {
-        fprintf(f, "\n");
-        return;
-    }
-
-    if (dmVerbosity >= 1)
-    {
-#ifndef JSS_LIGHT
-        if (i->desc)
-            fprintf(f, "Description: '%s'\n", i->desc);
-#endif
-        fprintf(f,
-            "size...........: %ld (0x%lx)\n"
-            "loopStart......: %ld (0x%lx)\n"
-            "loopEnd........: %ld (0x%lx)\n"
-            "volume.........: %d (0x%x)\n"
-            "flags..........: 0x%x ",
-            (unsigned long) i->size, (unsigned long) i->size,
-            (unsigned long) i->loopS, (unsigned long) i->loopE,
-            (unsigned long) i->loopS, (unsigned long) i->loopE,
-            i->volume, i->volume,
-            i->flags);
-        
-        if (i->flags & jsfLooped)  fprintf(f, "[loop] ");
-        if (i->flags & jsfBiDi)    fprintf(f, "[bi-di] ");
-        if (i->flags & jsf16bit)   fprintf(f, "[16bit] ");
-        
-        fprintf(f,
-            "\nC4BaseSpeed....: %d (0x%x)\n"
-            "ERelNote.......: %d (%s%d)\n"
-            "EFineTune......: %d\n"
-            "EPanning,,,....: %d (0x%x)\n\n",
-            i->C4BaseSpeed, i->C4BaseSpeed,
-            i->ERelNote, patNoteTable[(48 + i->ERelNote) % 12], (48 + i->ERelNote) / 12,
-            i->EFineTune, i->EPanning, i->EPanning);
-    }
-    else
-    {
-#ifndef JSS_LIGHT
-        if (i->desc)
-            fprintf(f, "'%s', ", i->desc);
-#endif
-        fprintf(f,
-        "s=%ld (%lx), l=%ld-%ld (%lx-%lx), v=%i (%x), f=0x%x, c4=%i (%x), rn=%i (%s%i), ft=%i, pn=%i (%x)\n",
-        (unsigned long) i->size, (unsigned long) i->size,
-        (unsigned long) i->loopS, (unsigned long) i->loopE,
-        (unsigned long) i->loopS, (unsigned long) i->loopE,
-        i->volume, i->volume, i->flags, i->C4BaseSpeed,
-        i->C4BaseSpeed, i->ERelNote,
-        patNoteTable[(48 + i->ERelNote) % 12],
-        (48 + i->ERelNote) / 12, i->EFineTune,
-        i->EPanning, i->EPanning);
-    }
-}
-
-
-void printGeneralInfo(FILE *f, JSSModule *m)
-{
-    int i;
-    
-    if (!m)
-        return;
-
-    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;
-    JSSModule *mod;
-
-    dmInitProg("viewmod", "miniJSS Module Viewer", "0.4", NULL, NULL);
-    dmVerbosity = 0;
-
-    // Parse arguments
-    if (!dmArgsProcess(argc, argv, optList, optListN,
-        argHandleOpt, argHandleFile, TRUE))
-        exit(1);
-
-    // Initialize miniJSS
-    jssInit();
-
-    // Open the file
-    dmMsg(1, "Reading module file '%s'\n", optFilename);
-    if (optFilename == NULL)
-        file = dmf_create_stdio_stream(stdin);
-    else if ((file = dmf_create_stdio(optFilename, "rb")) == NULL)
-    {
-        dmError("Error opening input file '%s'. (%s)\n",
-            optFilename, strerror(errno));
-        return 1;
-    }
-
-    // Read module file
-    dmMsg(1, "Reading file: %s\n", optFilename);
-#ifdef JSS_SUP_XM
-    dmMsg(1, "* Trying XM...\n");
-    result = jssLoadXM(file, &mod);
-#endif
-#ifdef JSS_SUP_JSSMOD
-    if (result != 0)
-    {
-        size_t bufgot, bufsize = dmfsize(file);
-        Uint8 *buf = dmMalloc(bufsize);
-        dmfseek(file, 0L, SEEK_SET);
-        dmMsg(1, "* Trying JSSMOD (%d bytes, %p)...\n", bufsize, buf);
-        if ((bufgot = dmfread(buf, 1, bufsize, file)) != bufsize)
-        {
-            dmError("Error reading file (not enough data %d), #%d: %s\n",
-                bufgot, dmferror(file), dmErrorStr(dmferror(file)));
-            return 2;
-        }
-        result = jssLoadJSSMOD(buf, bufsize, &mod);
-        dmFree(buf);
-    }
-#endif
-    dmf_close(file);
-    if (result != DMERR_OK)
-    {
-        dmError("Error loading module file, %d: %s\n",
-            result, dmErrorStr(result));
-        return 3;
-    }
-
-    // Print out information
-    if (optViewGeneralInfo)
-        printGeneralInfo(stdout, mod);
-
-    if (optViewPatterns)
-    {
-        for (i = 0; i < mod->npatterns; i++)
-        {
-            printf("\nPattern #%03i:\n", i);
-            printPattern(stdout, mod->patterns[i]);
-        }
-    }
-
-    if (optViewExtInstruments)
-    {
-        printf("\n"
-        "ExtInstruments:\n"
-        "---------------\n"
-        );
-        for (i = 0; i < mod->nextInstruments; i++)
-        {
-            printf("#%03i: ", i + 1);
-            printExtInstrument(stdout, mod->extInstruments[i]);
-        }
-    }
-
-    if (optViewInstruments)
-    {
-        printf("\n"
-        "Instruments:\n"
-        "------------\n"
-        );
-        for (i = 0; i < mod->ninstruments; i++)
-        {
-            printf("#%03i: ", i + 1);
-            printInstrument(stdout, mod->instruments[i]);
-        }
-    }
-
-    // Free module data
-    jssFreeModule(mod);
-    jssClose();
-
-    exit(0);
-    return 0;
-}
--- a/vptest.c	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,394 +0,0 @@
-#include "dmlib.h"
-#include "dmargs.h"
-#include "dmres.h"
-#include "dmimage.h"
-#include "dmtext.h"
-#include "dmq3d.h"
-#include "dmvecmat.h"
-#include <math.h>
-
-#define DM_COLORS (256)
-
-char *optFontFile = "font.ttf",
-     *optBitmapFilename = "blurri.png";
-BOOL optBenchmark = FALSE;
-int optVFlags = SDL_SWSURFACE | SDL_HWPALETTE;
-int optScrWidth = 640, optScrHeight = 480, optFontSize = 8, optScrDepth = 32;
-int optBenchmarkLen = 20;
-
-DMOptArg optList[] = {
-    { 0, '?', "help",       "Show this help", OPT_NONE },
-    { 2, 'v', "verbose",    "Be more verbose", OPT_NONE },
-    { 3, 'f', "full",       "Fullscreen", OPT_NONE },
-    { 4, 'h', "hw",         "Use SDL hardware surface", OPT_NONE },
-    { 5, 's', "size",       "Initial window size/resolution -s 640x480", OPT_ARGREQ },
-    { 6, 'd', "depth",      "Color depth of mode/window in bits (8/15/16/32)", OPT_ARGREQ },
-    { 7, 'b', "bench",      "Run in benchmark mode", OPT_NONE },
-};
-
-const int optListN = sizeof(optList) / sizeof(optList[0]);
-
-
-void argShowHelp()
-{
-    dmArgsPrintHelp(stdout, optList, optListN);
-}
-
-
-BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
-{
-    switch (optN) {
-    case 0:
-        argShowHelp();
-        exit(0);
-        break;
-
-    case 2:
-        dmVerbosity++;
-        break;
-    
-    case 3:
-        optVFlags |= SDL_FULLSCREEN;
-        break;
-
-    case 6:
-        if (optArg)
-            optScrDepth = atoi(optArg);
-        break;
-
-    case 5:
-        {
-            int w, h;
-            if (sscanf(optArg, "%dx%d", &w, &h) == 2)
-            {
-                if (w < 320 || h < 200 || w > 3200 || h > 3200)
-                {
-                    dmError("Invalid width or height: %d x %d\n", w, h);
-                    return FALSE;
-                }
-                optScrWidth = w;
-                optScrHeight = h;
-            }
-            else 
-            {
-                dmError("Invalid size argument '%s'.\n", optArg);
-                return FALSE;
-            }
-        }
-        break;
-
-    case 7:
-        optBenchmark = TRUE;
-        break;
-
-    default:
-        dmError("Unknown option '%s'.\n", currArg);
-        return FALSE;
-    }
-    
-    return TRUE;
-}
-
-
-void DM_PrintRect(FILE *f, SDL_Rect *r)
-{
-    fprintf(f, "SDL_Rect <%d, %d : %d, %d>\n",
-        r->x, r->y, r->w, r->h);
-}
-
-
-BOOL DM_InitializeVideo(SDL_Surface **screen)
-{
-    *screen = SDL_SetVideoMode(optScrWidth, optScrHeight, optScrDepth, optVFlags | SDL_RESIZABLE);
-    if (*screen == NULL)
-    {
-        dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError());
-        return FALSE;
-    }
-    return TRUE;
-}
-
-
-int main(int argc, char *argv[])
-{
-    SDL_Surface *screen = NULL, *bmap = NULL, *fbmap = NULL, *screen2 = NULL;
-    SDL_Color fontcol = { 255, 155, 155, 0 };
-    SDL_Event event;
-    TTF_Font *font = NULL;
-    int mouseX, mouseY, bx, by, err;
-    BOOL initSDL = FALSE, initTTF = FALSE, exitFlag;
-    DM3DVectorSpriteModel *model, *model2;
-    
-    dmVerbosity = 5;
-    if (!dmArgsProcess(argc, argv, optList, optListN,
-        argHandleOpt, NULL, FALSE))
-        exit(1);
-
-    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
-    {
-        dmError("Could not initialize SDL: %s\n", SDL_GetError());
-        goto error_exit;
-    }
-    initSDL = TRUE;
-
-
-    if (TTF_Init() < 0)
-    {
-        dmError("Could not initialize FreeType/TTF: %s\n", SDL_GetError());
-        goto error_exit;
-    }
-    initTTF = TRUE;
-
-    font = TTF_OpenFont(optFontFile, optFontSize);
-    if (font == NULL)
-    {
-        dmError("Could not load TTF font '%s' (%d): %s\n",
-            optFontFile, optFontSize, SDL_GetError());
-        goto error_exit;
-    }
-    TTF_SetFontStyle(font, TTF_STYLE_NORMAL);
-
-    DMResource *res = dmf_create_stdio(optBitmapFilename, "rb");
-    if (res == NULL)
-    {
-        dmError("Could not open resource file '%s'.\n", optBitmapFilename);
-        goto error_exit;
-    }
-    bmap = dmLoadImage(res);
-    dmf_close(res);
-    if (bmap == NULL)
-    {
-        dmError("Could not load image file '%s'.\n", optBitmapFilename);
-        goto error_exit;
-    }
-
-    res = dmf_create_stdio("trans6x6.png", "rb");
-    if (res == NULL)
-    {
-        dmError("Could not open resource file '%s'.\n", optBitmapFilename);
-        goto error_exit;
-    }
-    fbmap = dmLoadImage(res);
-    dmf_close(res);
-    if (fbmap == NULL)
-    {
-        dmError("Could not load image file '%s'.\n", optBitmapFilename);
-        goto error_exit;
-    }
-    
-    
-    res = dmf_create_stdio("mole.3d", "r");
-    if (res == NULL)
-    {
-        dmError("Could not open resource file '%s'.\n", optBitmapFilename);
-        goto error_exit;
-    }
-    err = dmRead3DVectorSpriteModel(res, &model);
-    dmf_close(res);
-    dmMsg(0, "Loaded, %d: %s\n", err, dmErrorStr(err));
-
-    res = dmf_create_stdio("roto.3d", "r");
-    if (res == NULL)
-    {
-        dmError("Could not open resource file '%s'.\n", optBitmapFilename);
-        goto error_exit;
-    }
-    err = dmRead3DVectorSpriteModel(res, &model2);
-    dmf_close(res);
-    dmMsg(0, "Loaded, %d: %s\n", err, dmErrorStr(err));
-    
-    if (optBenchmark)
-    {
-        screen = SDL_CreateRGBSurface(SDL_SWSURFACE, optScrWidth, optScrHeight, optScrDepth, 0, 0, 0, 0);
-        if (screen == NULL)
-        {
-            dmError("Could not create screen surface.\n");
-            goto error_exit;
-        }
-        
-        dmMsg(0, "Benchmark mode, not opening window.\n");
-    }
-    else
-    {
-        if (!DM_InitializeVideo(&screen))
-            goto error_exit;
-
-        SDL_WM_SetCaption("Halleluja", "DMT");
-    }
-
-    screen2 = SDL_CreateRGBSurface(SDL_SWSURFACE, optScrWidth, optScrHeight, optScrDepth, 0, 0, 0, 0);
-    Uint32 lcol = dmMapRGB(screen, 255,255,255);
-
-    int numFrames = 0, startTime = SDL_GetTicks(), endTime = 0;
-    exitFlag = FALSE;
-
-    if (optBenchmark)
-        dmMsg(0, "Starting benchmark, running for %d seconds.\n", optBenchmarkLen);
-
-    while (!exitFlag)
-    {
-        if (!optBenchmark)
-        {
-            while (SDL_PollEvent(&event))
-            switch (event.type)
-            {
-                case SDL_KEYDOWN:
-                    switch (event.key.keysym.sym)
-                    {
-                        case SDLK_ESCAPE: exitFlag = TRUE; break;
-                            
-                        default:
-                            break;
-                    }
-
-                    break;
-                
-                case SDL_VIDEORESIZE:
-                    optScrWidth = event.resize.w;
-                    optScrHeight = event.resize.h;
-
-                    if (!DM_InitializeVideo(&screen))
-                        goto error_exit;
-
-                    break;
-                
-                case SDL_VIDEOEXPOSE:
-                    break;
-
-                case SDL_QUIT:
-                    exit(0);
-            }
-
-            SDL_GetMouseState(&mouseX, &mouseY);
-            bx = 300 - ((DMFloat) mouseX * 500.0f ) / (DMFloat) optScrWidth;
-            by = 300 - ((DMFloat) mouseY * 500.0f ) / (DMFloat) optScrHeight;
-        }
-        else
-        {
-            bx = 0;
-            by = 0;
-        }
-
-        if (!optBenchmark && SDL_MUSTLOCK(screen) != 0 && SDL_LockSurface(screen) != 0)
-        {
-            dmError("Can't lock surface.\n");
-            goto error_exit;
-        }
-
-
-        dmClearSurface(screen, 0);
-
-        float f = SDL_GetTicks() / 150.0f,
-              qw2 = (float) 132.0 * (1.0 + sin(f) * 0.1),
-              qh2 = (float) 132.0 * (1.0 + sin(f) * 0.1);
-
-#if 1
-        dmScaledBlitSurfaceAny(bmap, bx-qw2, by-qh2, bmap->w+qw2, bmap->h+qh2, screen,
-        DMD_TRANSPARENT
-//        DMD_SATURATE
-//        DMD_NONE
-        );
-#endif
-#if 0
-        float qw = (float) 32.0 * (1.0 + sin(f) * 0.1),
-              qh = (float) 32.0 * (1.0 + sin(f) * 0.1),
-        dmScaledBlitSurface32to32TransparentGA(bmap, bx*2-qw, by*2-qh, bmap->w+qw, bmap->h+qh, screen,
-            128 + sin(f*0.1) * 120.0f);
-#endif
-
-#if 1
-        DMVector pos;
-        
-        DMMatrix mat;
-//        dm_matrix_rot_a(&mat, f*0.1, 0, (3.1415926535f * 2.0f * ((DMFloat) mouseX + (DMFloat) mouseY) ) / 500.0f);
-//        dm_matrix_rot_a(&mat, f*0.1, f*0.1, f*0.1);
-        dm_matrix_rot_a(&mat, 0, 0, f*0.1);
-
-        pos.x = -118;
-        pos.y = 0;
-        pos.z = 100;
- 
-        dmDraw3DVectorSpriteModel(screen, model2, &pos, &mat, fbmap, lcol);
-
-        pos.x = 118;
-        pos.y = 0;
-        pos.z = 100;
-
-        dm_matrix_rot_a(&mat, 0, 0, -f*0.1+0.125);
-        dmDraw3DVectorSpriteModel(screen, model2, &pos, &mat, fbmap, lcol);
-
-
-#endif
-
-#if 0
-        int np;
-        dmClearSurface(screen2, 0);
-        for (np = 1; np <= 5; np++)
-        {
-            float n = ((float) np - 0.5f) * np;
-            dmScaledBlitSurface32to32TransparentGA(screen, -n, -n, screen->w + n*2.0f, screen->h + n*2.0f, screen2,
-            210 - np * 10
-            );
-            dmDirectBlitSurface(screen2, screen);
-        }
-
-        dmDraw3DVectorSpriteModel(screen, model, &pos, &mat, fbmap, lcol);
-
-        for (np = 1; np <= 5; np++)
-        {
-            float n = ((float) np - 0.5f) / 2.0f;
-            dmScaledBlitSurface32to32TransparentGA(screen, -n, -n, screen->w + n*2.0f, screen->h + n*2.0f, screen2,
-            210 - np * 10
-            );
-            dmDirectBlitSurface(screen2, screen);
-        }
-
-#endif
-
-        if (!optBenchmark)
-        {
-            dmDrawTTFText(screen, font, fontcol, 0, 0, "%3.1f FPS", 
-                (float) (numFrames * 1000.0f) / (float) (endTime - startTime));
-
-            if (SDL_MUSTLOCK(screen) != 0)
-                SDL_UnlockSurface(screen);
-
-            SDL_Flip(screen);
-            SDL_Delay(25);
-        }
-
-        endTime = SDL_GetTicks();
-        numFrames++;
-
-        if (optBenchmark)
-        {
-            if (endTime - startTime > optBenchmarkLen * 1000)
-                exitFlag = TRUE;
-        }
-    }
-
-    // Print benchmark results
-    dmMsg(0, "%d frames in %d ms, fps = %1.3f\n",
-        numFrames, endTime - startTime,
-        (float) (numFrames * 1000.0f) / (float) (endTime - startTime));
-
-
-error_exit:
-    dmMsg(0, "Shutting down dmlib.\n");
-    if (screen)
-        SDL_FreeSurface(screen);
-
-    if (bmap)
-        SDL_FreeSurface(bmap);
-
-    if (font)
-        TTF_CloseFont(font);
-
-    if (initSDL)
-        SDL_Quit();
-
-    if (initTTF)
-        TTF_Quit();
-
-    return 0;
-}
--- a/xm2jss.c	Tue Apr 16 05:55:13 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1040 +0,0 @@
-/*
- * xm2jss - Convert XM module to JSSMOD
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2006-2009 Tecnic Software productions (TNSP)
- *
- * Please read file 'COPYING' for information on license and distribution.
- */
-#include <stdio.h>
-#include <errno.h>
-#include "jss.h"
-#include "jssmod.h"
-#include "jssplr.h"
-#include "dmlib.h"
-#include "dmargs.h"
-#include "dmres.h"
-#include "dmmutex.h"
-
-
-#define jmpNMODEffectTable (36)
-static const char jmpMODEffectTable[jmpNMODEffectTable] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-
-
-char  *optInFilename = NULL, *optOutFilename = NULL;
-BOOL  optIgnoreErrors = FALSE,
-      optStripExtInstr = FALSE,
-      optStripInstr = FALSE,
-      optStripSamples = FALSE,
-      optOptimize = FALSE;
-
-int   optPatternMode = PATMODE_COMP_HORIZ,
-      optSampMode16 = jsampDelta,
-      optSampMode8 = jsampFlipSign | jsampDelta;
-
-#define SAMPMODE_MASK (jsampFlipSign | jsampSwapEndianess | jsampSplit | jsampDelta)
-
-
-static const char* patModeTable[PATMODE_LAST] =
-{
-    "Raw horizontal",
-    "Compressed horizontal (similar to XM modules)",
-    "Raw vertical",
-    "Compressed vertical",
-    "Raw vertical for each element",
-};
-
-
-DMOptArg optList[] = {
-    { 0, '?', "help",           "Show this help", OPT_NONE },
-    { 1, 'v', "verbose",        "Be more verbose", OPT_NONE },
-    { 2, 'i', "ignore",         "Ignore errors", OPT_NONE },
-    { 3, 'p', "patterns",       "Pattern storage mode", OPT_ARGREQ },
-    { 4, 'E', "strip-ext-instr","Strip ext. instruments (implies -I -S)", OPT_NONE },
-    { 5, 'I', "strip-instr",    "Strip instruments (implies -S)", OPT_NONE },
-    { 6, 'S', "strip-samples",  "Strip instr. sampledata", OPT_NONE },
-    { 7, '8', "smode8",         "8-bit sample conversion flags", OPT_ARGREQ },
-    { 8, '1', "smode16",        "16-bit sample conversion flags", OPT_ARGREQ },
-    { 9, 'O', "optimize",       "Optimize module", OPT_NONE },
-};
-
-const int optListN = sizeof(optList) / sizeof(optList[0]);
-
-
-void argShowHelp()
-{
-    int i;
-
-    dmPrintBanner(stdout, dmProgName, "[options] <input.xm> <output.jmod>");
-    dmArgsPrintHelp(stdout, optList, optListN);
-
-    printf("\n"
-    "Pattern storage modes:\n");
-    
-    for (i = 1; i < PATMODE_LAST; i++)
-        printf("  %d = %s\n", i, patModeTable[i-1]);
-    
-    printf(
-    "\n"
-    "Sample data conversion flags (summative):\n"
-    "  1 = Delta encoding (DEF 8 & 16)\n"
-    "  2 = Flip signedness (DEF 8)\n"
-    "  4 = Swap endianess (affects 16-bit only)\n"
-    "  8 = Split and de-interleave hi/lo bytes (affects 16-bit only)\n"
-    "\n"
-    );
-}
-
-BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
-{
-    (void) optArg;
-    
-    switch (optN)
-    {
-    case 0:
-        argShowHelp();
-        exit(0);
-        break;
-
-    case 1:
-        dmVerbosity++;
-        break;
-
-    case 2:
-        optIgnoreErrors = TRUE;
-        break;
-
-    case 3:
-        optPatternMode = atoi(optArg);
-        if (optPatternMode <= 0 || optPatternMode >= PATMODE_LAST)
-        {
-            dmError("Unknown pattern conversion mode %d\n", optPatternMode);
-            return FALSE;
-        }
-        break;
-
-    case 4: optStripExtInstr = TRUE; break;
-    case 5: optStripInstr = TRUE; break;
-    case 6: optStripSamples = TRUE; break;
-
-    case 7: optSampMode8 = atoi(optArg) & SAMPMODE_MASK; break;
-    case 8: optSampMode16 = atoi(optArg) & SAMPMODE_MASK; break;
-
-    case 9: optOptimize = TRUE; break;
-
-    default:
-        dmError("Unknown argument '%s'.\n", currArg);
-        return FALSE;
-    }
-    
-    return TRUE;
-}
-
-
-BOOL argHandleFile(char *currArg)
-{
-    // Was not option argument
-    if (!optInFilename)
-        optInFilename = currArg;
-    else
-    if (!optOutFilename)
-        optOutFilename = currArg;
-    else
-    {
-        dmError("Too many filename arguments specified, '%s'.\n", currArg);
-        return FALSE;
-    }
-    
-    return TRUE;
-}
-
-
-/* These functions and the macro mess are meant to make the
- * conversion routines themselves clearer and simpler.
- */
-BOOL jsPutByte(Uint8 *patBuf, size_t patBufSize, size_t *npatBuf, Uint8 val)
-{
-    if (*npatBuf >= patBufSize)
-        return FALSE;
-    else
-    {
-        patBuf[*npatBuf] = val;
-        (*npatBuf)++;
-        return TRUE;
-    }
-}
-
-#define JSPUTBYTE(x) do { if (!jsPutByte(patBuf, patBufSize, patSize, x)) return DMERR_BOUNDS; } while (0)
-
-#define JSCOMP(x,z) do { if ((x) != jsetNotSet) { qflags |= (z); qcomp++; } } while (0)
-
-#define JSCOMPPUT(xf,xv,qv)  do {                       \
-    if (qflags & (xf)) {                                \
-        if ((xv) < 0 || (xv) > 255)                     \
-            JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS,        \
-            "%s value out of bounds %d.\n", qv, (xv));  \
-        JSPUTBYTE(xv);                                  \
-    }                                                   \
-} while (0)
-
-#define JSCONVPUT(xv,qv)    do {                        \
-    if ((xv) != jsetNotSet) {                           \
-        if ((xv) < 0 || (xv) > 254)                     \
-            JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS,        \
-            "%s value out of bounds %d.\n", qv, (xv));  \
-        JSPUTBYTE((xv) + 1);                            \
-    } else {                                            \
-        JSPUTBYTE(0);                                   \
-    }                                                   \
-} while (0)
-
-
-/* Convert a note
- */
-static int jssConvertNote(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSNote *note)
-{
-    Uint8 tmp;
-    if (note->note == jsetNotSet)
-        tmp = 0;
-    else if (note->note == jsetNoteOff)
-        tmp = 127;
-    else
-        tmp = note->note + 1;
-
-    if (tmp > 0x7f)
-        JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS, "Note value out of bounds %d > 0x7f.\n", tmp);
-
-    JSPUTBYTE(tmp & 0x7f);
-    
-    JSCONVPUT(note->instrument, "Instrument");
-    JSCONVPUT(note->volume, "Volume");
-    JSCONVPUT(note->effect, "Effect");
-    
-    tmp = (note->param != jsetNotSet) ? note->param : 0;
-    JSPUTBYTE(tmp);
-    
-    return DMERR_OK;
-}
-
-
-/* Compress a note
- */
-static int jssCompressNote(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSNote *note)
-{
-    Uint8 qflags = 0;
-    int qcomp = 0;
-    
-    JSCOMP(note->note,       COMP_NOTE);
-    JSCOMP(note->instrument, COMP_INSTRUMENT);
-    JSCOMP(note->volume,     COMP_VOLUME);
-    JSCOMP(note->effect,     COMP_EFFECT);
-    if (note->param != jsetNotSet && note->param != 0)
-    {
-        qflags |= COMP_PARAM;
-        qcomp++;
-    }
-    
-    if (qcomp < 4)
-    {
-        JSPUTBYTE(qflags | 0x80);
-        
-        if (note->note != jsetNotSet)
-        {
-            Uint8 tmp = (note->note != jsetNoteOff) ? note->note : 127;
-            if (tmp > 0x7f)
-                JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS, "Note value out of bounds %d > 0x7f.\n", tmp);
-            JSPUTBYTE(tmp);
-        }
-        
-        JSCOMPPUT(COMP_INSTRUMENT, note->instrument, "Instrument");
-        JSCOMPPUT(COMP_VOLUME, note->volume, "Volume");
-        JSCOMPPUT(COMP_EFFECT, note->effect, "Effect");
-        JSCOMPPUT(COMP_PARAM, note->param, "Param");
-    } else
-        return jssConvertNote(patBuf, patBufSize, patSize, note);
-    
-    return DMERR_OK;
-}
-
-
-/* Compress pattern
- */
-static int jssConvertPatternCompHoriz(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern)
-{
-    int row, channel;
-    *patSize = 0;
-    
-    for (row = 0; row < pattern->nrows; row++)
-    for (channel = 0; channel < pattern->nchannels; channel++)
-    {
-        const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
-        const int res = jssCompressNote(patBuf, patBufSize, patSize, note);
-        if (res != DMERR_OK)
-        {
-            JSSERROR(res, res, "Note compression failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n",
-            patBuf, patBufSize, *patSize, row, channel);
-            return res;
-        }
-    }
-    
-    return DMERR_OK;
-}
-
-
-static int jssConvertPatternCompVert(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern)
-{
-    int row, channel;
-    *patSize = 0;
-    
-    for (channel = 0; channel < pattern->nchannels; channel++)
-    for (row = 0; row < pattern->nrows; row++)
-    {
-        const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
-        const int res = jssCompressNote(patBuf, patBufSize, patSize, note);
-        if (res != DMERR_OK)
-        {
-            JSSERROR(res, res, "Note compression failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n",
-            patBuf, patBufSize, *patSize, row, channel);
-            return res;
-        }
-    }
-    
-    return DMERR_OK;
-}
-
-
-/* Convert a pattern
- */
-static int jssConvertPatternRawHoriz(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern)
-{
-    int row, channel;
-    *patSize = 0;
-    
-    for (row = 0; row < pattern->nrows; row++)
-    for (channel = 0; channel < pattern->nchannels; channel++)
-    {
-        const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
-        const int res = jssConvertNote(patBuf, patBufSize, patSize, note);
-        if (res != DMERR_OK)
-        {
-            JSSERROR(res, res, "Note conversion failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n",
-            patBuf, patBufSize, *patSize, row, channel);
-            return res;
-        }
-    }
-    
-    return DMERR_OK;
-}
-
-
-static int jssConvertPatternRawVert(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern)
-{
-    int row, channel;
-    *patSize = 0;
-    
-    for (channel = 0; channel < pattern->nchannels; channel++)
-    for (row = 0; row < pattern->nrows; row++)
-    {
-        const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
-        const int res = jssConvertNote(patBuf, patBufSize, patSize, note);
-        if (res != DMERR_OK)
-        {
-            JSSERROR(res, res, "Note conversion failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n",
-            patBuf, patBufSize, *patSize, row, channel);
-            return res;
-        }
-    }
-    
-    return DMERR_OK;
-}
-
-
-#define JSFOREACHNOTE1                                                              \
-  for (channel = 0; channel < pattern->nchannels; channel++)                        \
-  for (row = 0; row < pattern->nrows; row++) {                                      \
-  const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
-
-#define JSFOREACHNOTE2 }
-
-static int jssConvertPatternRawElem(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern)
-{
-    Uint8 tmp;
-    int row, channel;
-    *patSize = 0;
-    
-    JSFOREACHNOTE1;
-    if (note->note == jsetNotSet)
-        tmp = 0;
-    else if (note->note == jsetNoteOff)
-        tmp = 127;
-    else
-        tmp = note->note + 1;
-    if (tmp > 0x7f)
-        JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS, "Note value out of bounds %d > 0x7f.\n", tmp);
-    JSPUTBYTE(tmp);
-    JSFOREACHNOTE2;
-    
-    JSFOREACHNOTE1;
-    JSCONVPUT(note->instrument, "Instrument");
-    JSFOREACHNOTE2;
-    
-    JSFOREACHNOTE1;
-    JSCONVPUT(note->volume, "Volume");
-    JSFOREACHNOTE2;
-    
-    JSFOREACHNOTE1;
-    JSCONVPUT(note->effect, "Effect");
-    JSFOREACHNOTE2;
-    
-    JSFOREACHNOTE1;
-    tmp = (note->param != jsetNotSet) ? note->param : 0;
-    JSPUTBYTE(tmp);
-    JSFOREACHNOTE2;
-    
-    return DMERR_OK;
-}
-
-#undef JSFOREACHNOTE1
-#undef JSFOREACHNOTE2
-
-
-static void jssCopyEnvelope(JSSMODEnvelope *je, JSSEnvelope *e)
-{
-    int i;
-    
-    je->flags   = e->flags;
-    je->npoints = e->npoints;
-    je->sustain = e->sustain;
-    je->loopS   = e->loopS;
-    je->loopE   = e->loopE;
-    
-    for (i = 0; i < e->npoints; i++)
-    {
-        je->points[i].frame = e->points[i].frame;
-        je->points[i].value = e->points[i].value;
-    }
-}
-
-
-/* Save a JSSMOD file
- */
-int jssSaveJSSMOD(FILE *outFile, JSSModule *m, int patMode, int flags8, int flags16)
-{
-    JSSMODHeader jssH;
-    int i, pattern, order, instr, totalSize;
-    const size_t patBufSize = 64*1024; // 64kB pattern buffer
-    Uint8 *patBuf;
-
-    // Check the module
-    if (m == NULL)
-        JSSERROR(DMERR_NULLPTR, DMERR_NULLPTR, "Module pointer was NULL\n");
-
-    if ((m->nchannels < 1) || (m->npatterns < 1) || (m->norders < 1))
-        JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS,
-        "Module had invalid values (nchannels=%i, npatterns=%i, norders=%i)\n",
-        m->nchannels, m->npatterns, m->norders);
-
-    // Create the JSSMOD header
-    jssH.idMagic[0]         = 'J';
-    jssH.idMagic[1]         = 'M';
-    jssH.idVersion          = JSSMOD_VERSION;
-    jssH.norders            = m->norders;
-    jssH.npatterns          = m->npatterns;
-    jssH.nchannels          = m->nchannels;
-    jssH.nextInstruments    = m->nextInstruments;
-    jssH.ninstruments       = m->ninstruments;
-    jssH.defFlags           = m->defFlags;
-    jssH.intVersion         = m->intVersion;
-    jssH.defRestartPos      = m->defRestartPos;
-    jssH.defSpeed           = m->defSpeed;
-    jssH.defTempo           = m->defTempo;
-    jssH.patMode            = patMode;
-    
-    // Write header
-    totalSize = sizeof(jssH);
-    if (fwrite(&jssH, sizeof(jssH), 1, outFile) != 1)
-        JSSERROR(DMERR_FWRITE, DMERR_FWRITE, "Could not write JSSMOD header!\n");
-
-    dmMsg(1," * JSSMOD-header 0x%04x, %d bytes.\n", JSSMOD_VERSION, totalSize);
-
-    // Write orders list
-    for (totalSize = order = 0; order < m->norders; order++)
-    {
-        Uint16 tmp = m->orderList[order];
-        totalSize += sizeof(tmp);
-        if (fwrite(&tmp, sizeof(tmp), 1, outFile) != 1)
-            JSSERROR(DMERR_FWRITE, DMERR_FWRITE, "Could not write JSSMOD orders list.\n");
-    }
-
-    dmMsg(1," * %d item orders list, %d bytes.\n",
-        m->norders, totalSize);
-
-    // Allocate pattern compression buffer
-    if ((patBuf = dmMalloc(patBufSize)) == NULL)
-        JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
-        "Error allocating memory for pattern compression buffer.\n");
-
-    
-    // Write patterns
-    for (totalSize = pattern = 0; pattern < m->npatterns; pattern++)
-    {
-        JSSMODPattern patHead;
-        size_t finalSize = 0;
-        
-        switch (patMode)
-        {
-            case PATMODE_RAW_HORIZ:
-                i = jssConvertPatternRawHoriz(patBuf, patBufSize, &finalSize, m->patterns[pattern]);
-                break;
-            case PATMODE_COMP_HORIZ:
-                i = jssConvertPatternCompHoriz(patBuf, patBufSize, &finalSize, m->patterns[pattern]);
-                break;
-            case PATMODE_RAW_VERT:
-                i = jssConvertPatternRawVert(patBuf, patBufSize, &finalSize, m->patterns[pattern]);
-                break;
-            case PATMODE_COMP_VERT:
-                i = jssConvertPatternCompVert(patBuf, patBufSize, &finalSize, m->patterns[pattern]);
-                break;
-            case PATMODE_RAW_ELEM:
-                i = jssConvertPatternRawElem(patBuf, patBufSize, &finalSize, m->patterns[pattern]);
-                break;
-            default:
-                i = DMERR_INVALID_DATA;
-                dmFree(patBuf);
-                JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-                "Unsupported pattern conversion mode %d.\n", patMode);
-                break;
-        }
-
-        if (i != DMERR_OK)
-        {
-            dmFree(patBuf);
-            JSSERROR(i, i, "Error converting pattern data #%i\n", pattern);
-        }
-        else
-        {
-            dmMsg(3, " - Pattern %d size %d bytes\n", pattern, finalSize);
-            patHead.nrows = m->patterns[pattern]->nrows;
-            patHead.size = finalSize;
-            totalSize += finalSize + sizeof(patHead);
-
-            if (fwrite(&patHead, sizeof(patHead), 1, outFile) != 1)
-            {
-                dmFree(patBuf);
-                JSSERROR(DMERR_FWRITE, DMERR_FWRITE,
-                "Error writing pattern #%d header\n", pattern);
-            }
-
-            if (fwrite(patBuf, sizeof(Uint8), finalSize, outFile) != finalSize)
-            {
-                dmFree(patBuf);
-                JSSERROR(DMERR_FWRITE, DMERR_FWRITE,
-                "Error writing pattern #%d data\n", pattern);
-            }
-        }
-    }
-    dmFree(patBuf);
-    dmMsg(1," * %d patterns, %d bytes.\n", m->npatterns, totalSize);
-
-    // Write extended instruments
-    for (totalSize = instr = 0; instr < m->nextInstruments; instr++)
-    {
-        JSSMODExtInstrument jssE;
-        JSSExtInstrument *einst = m->extInstruments[instr];
-        
-        memset(&jssE, 0, sizeof(jssE));
-
-        if (einst)
-        {
-            // Create header
-            jssE.nsamples = einst->nsamples;
-            for (i = 0; i < jsetNNotes; i++)
-            {
-                int snum = einst->sNumForNotes[i];
-                jssE.sNumForNotes[i] = (snum != jsetNotSet) ? snum : 0;
-            }
-            
-            jssCopyEnvelope(&jssE.volumeEnv, &(einst->volumeEnv));
-            jssCopyEnvelope(&jssE.panningEnv, &(einst->panningEnv));
-            jssE.vibratoType  = einst->vibratoType;
-            jssE.vibratoSweep = einst->vibratoSweep;
-            jssE.vibratoDepth = einst->vibratoDepth;
-            jssE.fadeOut      = einst->fadeOut;
-        } else
-            JSSWARNING(DMERR_NULLPTR, DMERR_NULLPTR, "Extended instrument #%i NULL!\n", instr);
-        
-        // Write to file
-        totalSize += sizeof(jssE);
-        if (fwrite(&jssE, sizeof(jssE), 1, outFile) != 1)
-            JSSERROR(DMERR_FWRITE, DMERR_FWRITE,
-            "Could not write JSSMOD extended instrument #%i to file!\n", instr);
-    }
-    dmMsg(1," * %d Extended Instruments, %d bytes.\n", m->nextInstruments, totalSize);
-
-
-    // Write sample instrument headers
-    for (totalSize = instr = 0; instr < m->ninstruments; instr++)
-    {
-        JSSMODInstrument jssI;
-        JSSInstrument *pInst = m->instruments[instr];
-
-        memset(&jssI, 0, sizeof(jssI));
-
-        // Create header
-        if (pInst)
-        {
-            jssI.size         = pInst->size;
-            jssI.loopS        = pInst->loopS;
-            jssI.loopE        = pInst->loopE;
-            jssI.volume       = pInst->volume;
-            jssI.flags        = pInst->flags;
-            jssI.C4BaseSpeed  = pInst->C4BaseSpeed;
-            jssI.ERelNote     = pInst->ERelNote;
-            jssI.EFineTune    = pInst->EFineTune;
-            jssI.EPanning     = pInst->EPanning;
-            jssI.hasData      = (pInst->data != NULL) ? TRUE : FALSE;
-            jssI.convFlags = (pInst->flags & jsf16bit) ? flags16 : flags8;
-        }
-        else 
-            JSSWARNING(DMERR_NULLPTR, DMERR_NULLPTR, "Instrument #%i NULL!\n", instr);
-        
-        // Write to file
-        totalSize += sizeof(jssI);
-        if (fwrite(&jssI, sizeof(jssI), 1, outFile) != 1)
-            JSSERROR(DMERR_FWRITE, DMERR_FWRITE,
-             "Could not write JSSMOD instrument #%i to file!\n", instr);
-    }
-    dmMsg(1," * %d Instrument headers, %d bytes.\n", m->ninstruments, totalSize);
-    
-    // Write sample data
-    for (totalSize = instr = 0; instr < m->ninstruments; instr++)
-    if (m->instruments[instr])
-    {
-        JSSInstrument *inst = m->instruments[instr];
-        if (inst->data != NULL)
-        {
-            size_t res;
-            if (inst->flags & jsf16bit)
-            {
-                jssEncodeSample16(inst->data, inst->size, flags16);
-                res = fwrite(inst->data, sizeof(Uint16), inst->size, outFile);
-            }
-            else
-            {
-                jssEncodeSample8(inst->data, inst->size, flags8);
-                res = fwrite(inst->data, sizeof(Uint8), inst->size, outFile);
-            }
-            
-            totalSize += inst->size;
-            if (res != (size_t) inst->size)
-                JSSERROR(DMERR_FWRITE, DMERR_FWRITE,
-                 "Could not write JSSMOD sample #%i to file!\n", instr);
-        }
-    }
-    dmMsg(1," * %d samples, %d bytes.\n", m->ninstruments, totalSize);
-    
-    return DMERR_OK;
-}
-
-
-/* Optimize a given module
- */
-JSSModule *optimizeModule(JSSModule *m)
-{
-    BOOL usedPatterns[jsetMaxPatterns + 1],
-         usedInstruments[jsetMaxInstruments + 1],
-         usedExtInstruments[jsetMaxInstruments + 1];
-    int  mapExtInstruments[jsetMaxInstruments + 1],
-         mapInstruments[jsetMaxInstruments + 1],
-         mapPatterns[jsetMaxPatterns + 1];
-    JSSModule *r = NULL;
-    int i, n8, n16;
-
-    // Allocate a new module
-    if ((r = jssAllocateModule()) == NULL)
-        return NULL;
-
-    // Allocate tables
-    
-    // Copy things
-    r->moduleType       = m->moduleType;
-    r->moduleName       = dm_strdup(m->moduleName);
-    r->trackerName      = dm_strdup(m->trackerName);
-    r->defSpeed         = m->defSpeed;
-    r->defTempo         = m->defTempo;
-    r->defFlags         = m->defFlags;
-    r->defRestartPos    = m->defRestartPos;
-    r->intVersion       = m->intVersion;
-    r->nchannels        = m->nchannels;
-    r->norders          = m->norders;
-    for (i = 0; i < jsetNChannels; i++)
-        r->defPanning[i] = m->defPanning[i];
-    
-    // Initialize values
-    for (i = 0; i <= jsetMaxInstruments; i++)
-    {
-        usedExtInstruments[i] = FALSE;
-        usedInstruments[i] = FALSE;
-        mapExtInstruments[i] = jsetNotSet;
-        mapInstruments[i] = jsetNotSet;
-    }
-    
-    for (i = 0; i <= jsetMaxPatterns; i++)
-    {
-        usedPatterns[i] = FALSE;
-        mapPatterns[i] = jsetNotSet;
-    }
-
-    // Find out all used patterns and ext.instruments
-    for (i = 0; i < m->norders; i++)
-    {
-        int pattern = m->orderList[i];
-        if (pattern >= 0 && pattern < m->npatterns)
-        {
-            JSSPattern *p = m->patterns[pattern];
-            if (p != NULL)
-            {
-                int row, channel;
-                JSSNote *n = p->data;
-                
-                // Mark pattern as used
-                usedPatterns[pattern] = TRUE;
-                
-                // Check all notes
-                for (row = 0; row < p->nrows; row++)
-                for (channel = 0; channel < p->nchannels; channel++, n++)
-                {
-                    if (n->instrument != jsetNotSet)
-                    {
-                        if (optStripExtInstr || (n->instrument >= 0 && n->instrument < m->nextInstruments))
-                            usedExtInstruments[n->instrument] = TRUE;
-                        else
-                            dmMsg(2, "Pattern 0x%x, row=0x%x, chn=%d has invalid instrument 0x%x\n",
-                            pattern, row, channel, n->instrument);
-                    }
-                }
-            }
-            else
-            {
-                dmError("Pattern 0x%x is used on order 0x%x, but has no data!\n",
-                pattern, i);
-            }
-        }
-        else
-        if (pattern != jsetMaxPatterns)
-        {
-            dmError("Order 0x%x has invalid pattern number 0x%x!\n",
-            i, pattern);
-        }
-    }
-    
-    // Find out used instruments
-    for (i = 0; i <= jsetMaxInstruments; i++)
-    if (usedExtInstruments[i] && m->extInstruments[i] != NULL)
-    {
-        int note;
-        JSSExtInstrument *e = m->extInstruments[i];
-        
-        for (note = 0; note < jsetNNotes; note++)
-        if (e->sNumForNotes[note] != jsetNotSet)
-        {
-            int q = e->sNumForNotes[note];
-            if (q >= 0 && q < m->ninstruments)
-            {
-                usedInstruments[q] = TRUE;
-            }
-            else
-            {
-                dmError("Ext.instrument #%d sNumForNotes[%d] value out range (%d < %d).\n",
-                i, m->ninstruments, q);
-            }
-        }
-    }
-    
-    // Create pattern mappings
-    r->npatterns = 0;
-    dmMsg(1, "Unused patterns: ");
-        
-    for (i = 0; i <= jsetMaxPatterns; i++)
-    if (m->patterns[i] != NULL)
-    {
-        if (!usedPatterns[i])
-        {
-            dmPrint(2, "0x%x, ", i);
-        }
-        else
-        {
-            if (i >= m->npatterns)
-                dmError("Pattern 0x%x >= 0x%x, but used!\n", i, m->npatterns);
-            
-            mapPatterns[i] = r->npatterns;
-            r->patterns[r->npatterns] = m->patterns[i];
-            (r->npatterns)++;
-        }
-    }
-    dmPrint(2, "\n");
-    
-    dmMsg(1, "%d used patterns, %d unused.\n",
-    r->npatterns, m->npatterns - r->npatterns);
-    
-    
-    // Re-map instruments
-    dmMsg(1, "Unused instruments: ");
-    for (n8 = n16 = i = 0; i <= jsetMaxInstruments; i++)
-    if (m->instruments[i] != NULL)
-    {
-        if (!usedInstruments[i] && !optStripInstr)
-        {
-            dmPrint(2, "0x%x, ", i);
-        }
-        else
-        {
-            JSSInstrument *ip = m->instruments[i];
-            if (i >= m->ninstruments)
-                dmError("Instrument 0x%x >= 0x%x, but used!\n", i, m->ninstruments);
-            
-            mapInstruments[i] = r->ninstruments;
-            r->instruments[r->ninstruments] = ip;
-            (r->ninstruments)++;
-
-            if (ip->flags & jsf16bit)
-                n16++;
-            else
-                n8++;
-        }
-    }
-    dmPrint(2, "\n");
-    dmMsg(1, "Total of (%d)  16-bit, (%d) 8-bit samples, (%d) instruments.\n",
-    n16, n8, r->ninstruments);
-    
-    // Re-map ext.instruments
-    dmMsg(1, "Unused ext.instruments: ");
-    for (i = 0; i < jsetMaxInstruments; i++)
-    if (usedExtInstruments[i])
-    {
-        if (i >= m->nextInstruments && !optStripExtInstr)
-        {
-            dmError("Ext.instrument 0x%x >= 0x%x, but used!\n",
-            i, m->nextInstruments);
-        }
-        else
-        if (m->extInstruments[i] != NULL)
-        {
-            JSSExtInstrument *e = m->extInstruments[i];
-            int note;
-            
-            mapExtInstruments[i] = r->nextInstruments;
-            r->extInstruments[r->nextInstruments] = e;
-            (r->nextInstruments)++;
-            
-            // Re-map sNumForNotes
-            for (note = 0; note < jsetNNotes; note++)
-            {
-                int q = e->sNumForNotes[note];
-                if (q != jsetNotSet)
-                {
-                    int map;
-                    if (q >= 0 && q <= jsetMaxInstruments)
-                    {
-                        map = mapInstruments[q];
-                    }
-                    else
-                    {
-                        map = jsetNotSet;
-                        dmError("e=%d, note=%d, q=%d/%d\n", i, note, q, r->ninstruments);
-                    }
-                    e->sNumForNotes[note] = map;
-                }
-            }
-        }
-        else
-        {
-            dmPrint(2, "[0x%x==NULL], ", i);
-            mapExtInstruments[i] = jsetNotSet;
-        }
-    }
-    else
-    {
-        if (i < m->nextInstruments && m->extInstruments[i] != NULL)
-        {
-            dmPrint(2, "0x%x, ", i);
-        }
-    }
-    dmPrint(2, "\n");
-    dmMsg(1, "%d extended instruments.\n", r->nextInstruments);
-    
-    
-    // Remap pattern instrument data
-    for (i = 0; i < r->npatterns; i++)
-    {
-        int row, channel;
-        JSSPattern *p = r->patterns[i];
-        JSSNote *n = p->data;
-        
-        for (row = 0; row < p->nrows; row++)
-        for (channel = 0; channel < p->nchannels; channel++, n++)
-        {
-            char effect;
-
-            if (!optStripExtInstr)
-            {
-                if (n->instrument >= 0 && n->instrument <= jsetMaxInstruments)
-                    n->instrument = mapExtInstruments[n->instrument];
-
-                if (n->instrument != jsetNotSet && r->extInstruments[n->instrument] == NULL)
-                    dmError("Non-existing instrument used #%d.\n", n->instrument);
-            }
-
-            JMPGETEFFECT(effect, n->effect);
-            
-            switch (effect)
-            {
-                case 'C': // Cxx = Set volume
-                    if (n->volume == jsetNotSet)
-                    {
-                        n->volume = n->param;
-                        n->effect = jsetNotSet;
-                        n->param = jsetNotSet;
-                    }
-                    break;
-            }
-        }
-    }
-    
-    // Remap orders list
-    for (i = 0; i < m->norders; i++)
-    {
-        r->orderList[i] = mapPatterns[m->orderList[i]];
-    }
-
-    return r;
-}
-
-
-int main(int argc, char *argv[])
-{
-    DMResource *sfile = NULL;
-    FILE *dfile = NULL;
-    JSSModule *sm, *dm;
-    int result;
-
-    dmInitProg("xm2jss", "XM to JSSMOD converter", "0.6", NULL, NULL);
-    dmVerbosity = 0;
-
-    // Parse arguments
-    if (!dmArgsProcess(argc, argv, optList, optListN,
-        argHandleOpt, argHandleFile, TRUE))
-        exit(1);
-
-    // Check arguments
-    if (optInFilename == NULL || optOutFilename == NULL)
-    {
-        dmError("Input or output file not specified. Try --help.\n");
-        return 1;
-    }
-
-    // Read the source file
-    if ((sfile = dmf_create_stdio(optInFilename, "rb")) == NULL)
-    {
-        dmError("Error opening input file '%s', %d: %s\n",
-            optInFilename, errno, strerror(errno));
-        return 1;
-    }
-
-    // Initialize miniJSS
-    jssInit();
-
-    // Read file
-    dmMsg(1, "Reading XM-format file ...\n");
-    result = jssLoadXM(sfile, &sm);
-    dmf_close(sfile);
-    if (result != 0)
-    {
-        dmError("Error while loading XM file (%i), ", result);
-        if (optIgnoreErrors)
-            fprintf(stderr, "ignoring. This may cause problems.\n");
-        else
-        {
-            fprintf(stderr, "giving up. Use --ignore if you want to try to convert anyway.\n");
-            return 2;
-        }
-    }
-
-    // Check stripping settings
-    if (optStripExtInstr) optStripInstr = TRUE;
-    if (optStripInstr) optStripSamples = TRUE;
-    
-    // Remove samples
-    if (optStripSamples)
-    {
-        int i;
-        
-        dmMsg(1, "Stripping samples...\n");
-        for (i = 0; i < sm->ninstruments; i++)
-        {
-            dmFree(sm->instruments[i]->data);
-            sm->instruments[i]->data = NULL;
-        }
-    }
-
-    // Remove instruments
-    if (optStripInstr)
-    {
-        int i;
-        
-        dmMsg(1, "Stripping instruments...\n");
-        for (i = 0; i < sm->ninstruments; i++)
-        {
-            dmFree(sm->instruments[i]);
-            sm->instruments[i] = NULL;
-        }
-        sm->ninstruments = 0;
-    }
-
-    // Remove ext.instruments
-    if (optStripExtInstr)
-    {
-        int i;
-        
-        dmMsg(1, "Stripping ext.instruments...\n");
-        for (i = 0; i < sm->nextInstruments; i++)
-        {
-            dmFree(sm->extInstruments[i]);
-            sm->extInstruments[i] = NULL;
-        }
-        sm->nextInstruments = 0;
-    }
-    // Run the optimization procedure
-    if (optOptimize)
-    {
-        dmMsg(1, "Optimizing module data...\n");
-        dm = optimizeModule(sm);
-    } else
-        dm = sm;
-
-    // Write output file
-    if ((dfile = fopen(optOutFilename, "wb")) == NULL)
-    {
-        dmError("Error creating output file '%s', %d: %s\n",
-            optOutFilename, errno, strerror(errno));
-        return 1;
-    }
-
-    dmMsg(1, "Writing JSSMOD-format file [patMode=0x%04x, samp8=0x%02x, samp16=0x%02x]\n",
-        optPatternMode, optSampMode8, optSampMode16);
-    
-    result = jssSaveJSSMOD(dfile, dm, optPatternMode, optSampMode8, optSampMode16);
-    
-    fclose(dfile);
-    
-    if (result != 0)
-    {
-        dmError("Error while saving JSSMOD file, %d: %s\n",
-            result, dmErrorStr(result));
-        dmError("WARNING: The resulting file may be broken!\n");
-    }
-    else
-    {
-        dmMsg(1, "Conversion complete.\n");
-    }
-    return 0;
-}