view dmsimple.c @ 510:43ea59887c69

Start work on making C64 formats encoding possible by changing DMDecodeOps to DMEncDecOps and adding fields and op enums for custom encode functions, renaming, etc. Split generic op sanity checking into a separate function in preparation for its use in generic encoding function.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 19 Nov 2012 15:06:01 +0200
parents e1c984404b6b
children 8a98037c5cf7
line wrap: on
line source

#include <SDL.h>
#include "dmengine.h"
#include "dmargs.h"

static DMEngineData engine;

static DMOptArg optList[] =
{
    { 0, '?', "help",       "Show this help", OPT_NONE },
    { 1, 'v', "verbose",    "Be more verbose", OPT_NONE },
    { 2, 'f', "fs",         "Fullscreen", OPT_NONE },
};

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



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


static 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:
            engine.optVFlags |= SDL_FULLSCREEN;
            break;

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


static void engineAudioCallback(void *userdata, Uint8 *stream, int len)
{
    (void) 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, engine.audioRes->rdata + engine.audioPos, len);
        engine.audioPos += len;
    }
#endif
}


static int engineShowProgress(int loaded, int total)
{
    int dx = 60,
        dh = 20,
        dw = engine.screen->w - (2 * dx),
        dy = (engine.screen->h - dh) / 2;
    
    if (SDL_MUSTLOCK(engine.screen) != 0 && SDL_LockSurface(engine.screen) != 0)
        return DMERR_INIT_FAIL;
    
    // Draw the progress bar
    dmClearSurface(engine.screen, dmMapRGBA(engine.screen, 0,0,0,0));
    dmFillRect(engine.screen, dx, dy, dx+dw, dy+dh, dmMapRGB(engine.screen, 255,255,255));
    dmFillRect(engine.screen, dx+1, dy+1, dx+dw-1, dy+dh-1, dmMapRGB(engine.screen, 0,0,0));

    if (total > 0)
    {
        dmFillRect(engine.screen,
            dx+3, dy+3,
            dx + 3 + ((dw - 3) * loaded) / total,
            dy + dh - 3,
            dmMapRGB(engine.screen, 200,200,200));
    }

    // Flip screen
    if (SDL_MUSTLOCK(engine.screen) != 0)
        SDL_UnlockSurface(engine.screen);

    SDL_Flip(engine.screen);
    return DMERR_OK;
}


static int engineLoadResources()
{
    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 err;
}


static BOOL engineInitializeVideo()
{
    dmPrint(1, "Initializing SDL video %d x %d x %dbpp, flags=0x%08x\n",
        engine.optScrWidth, engine.optScrHeight, engine.optBitDepth, engine.optVFlags);

    SDL_FreeSurface(engine.screen);

    engine.screen = SDL_SetVideoMode(engine.optScrWidth, engine.optScrHeight, engine.optBitDepth, engine.optVFlags);
    if (engine.screen == NULL)
    {
        dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError());
        return FALSE;
    }
    return TRUE;
}


int main(int argc, char *argv[])
{
    int err;
    BOOL initSDL = FALSE;

    memset(&engine, 0, sizeof(engine));

    // Pre-initialization
    if ((err = demoPreInit(&engine)) != DMERR_OK)
        goto error_exit;

    if (!dmArgsProcess(argc, argv, optList, optListN,
        argHandleOpt, NULL, FALSE))
        return DMERR_INIT_FAIL;

    dmPrint(0,
    "%s\n"
    "%s\n"
    "TNSP simple demoengine initializing.\n",
    dmProgDesc, dmProgAuthor);

    dmPrint(0,
    "Using libSDL, "
#ifdef DM_USE_TREMOR
    "Tremor Vorbis codec"
#endif
#ifdef DM_USE_PACKFS
    ", zlib and modified stb_image.\n"
#else
    " and modified stb_image.\n"
#endif
    "See README.txt for more information.\n");

    // Initialize resource subsystem
    dmPrint(1, "Initializing resources subsystem.\n");
    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));
        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;
    }

    if (!engineInitializeVideo())
        goto error_exit;

    SDL_ShowCursor(SDL_DISABLE);
    SDL_WM_SetCaption(dmProgDesc, dmProgName);

    if (engine.demoInitPostVideo != NULL &&
       (err = engine.demoInitPostVideo(&engine)) != DMERR_OK)
    {
        dmError("demoInitPostVideo() failed, %d: %s\n", err, dmErrorStr(err));
        goto error_exit;
    }

    // Load resources
    dmPrint(1, "Loading resources, please wait...\n");
    if ((err = engineLoadResources()) != DMERR_OK)
    {
        dmError("Error loading resources, %d: %s.\n",
            err, dmErrorStr(err));
        goto error_exit;
    }

    // Final initializations
    if ((err = engine.demoInit(&engine)) != DMERR_OK)
    {
        dmError("Failure in demoInit(), %d: %s\n",
            err, dmErrorStr(err));
        goto error_exit;
    }

    // Initialize effects
    if ((err = engineInitializeEffects(&engine)) != DMERR_OK)
    {
        dmError("Effects initialization failed, %d: %s\n",
            err, dmErrorStr(err));
        goto error_exit;
    }

    // Use a timeline, if set
    if (engine.timeline != NULL)
    {
        if ((err = dmLoadTimeline(engine.timeline, &engine.tl)) != DMERR_OK)
        {
            dmError("Error loading timeline, %d: %s\n", err,
                dmErrorStr(err));
            goto error_exit;
        }

        if ((err = dmPrepareTimeline(engine.tl, engine.ptl)) != DMERR_OK)
        {
            dmError("Error creating prepared timeline, %d: %s\n",
                err, dmErrorStr(err));
            goto error_exit;
        }
    }

    dmPrint(1, "Starting up.\n");

    SDL_LockAudio();
    SDL_PauseAudio(0);
    SDL_UnlockAudio();

    engine.startTime = SDL_GetTicks();

    while (!engine.exitFlag)
    {
        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;
                        break;

                    case SDLK_f:
                        engine.optVFlags ^= SDL_FULLSCREEN;
                        if (!engineInitializeVideo())
                            goto error_exit;
                        break;

                    case SDLK_RETURN:
                        if (engine.event.key.keysym.mod & KMOD_ALT)
                        {
                            engine.optVFlags ^= SDL_FULLSCREEN;
                            if (!engineInitializeVideo())
                                goto error_exit;
                        }
                        break;

                    default:
                        break;
                }

                break;

            case SDL_VIDEOEXPOSE:
                break;

            case SDL_QUIT:
                engine.exitFlag = TRUE;
                break;
        }

        // Draw frame
        engine.frameTime = SDL_GetTicks();
        if (engine.pauseFlag != engine.paused)
        {
            engine.paused = engine.pauseFlag;
            engine.pauseTime = engineGetTick(&engine);
        }
        
        if (engine.paused)
        {
            engine.startTime = engine.frameTime - engine.pauseTime;
        }

        if (SDL_MUSTLOCK(engine.screen) != 0 && SDL_LockSurface(engine.screen) != 0)
        {
            dmError("Can't lock surface.\n");
            goto error_exit;
        }

        // Call main tick
        if (engine.demoRender != NULL)
        {
            if ((err = engine.demoRender(&engine)) != DMERR_OK)
                goto error_exit;
        }
        else
        {
            if ((err = dmExecuteTimeline(engine.ptl, engine.screen, engineGetTick(&engine))) != DMERR_OK)
                goto error_exit;
        }

        // Flip screen
        if (SDL_MUSTLOCK(engine.screen) != 0)
            SDL_UnlockSurface(engine.screen);

        SDL_Flip(engine.screen);
        SDL_Delay(engine.paused ? 100 : 20);

        engine.frameCount++;
    }

    // Print benchmark results
    engine.endTime = SDL_GetTicks();
    dmPrint(1, "%d frames in %d ms, fps = %1.3f\n",
        engine.frameCount, engine.endTime - engine.startTime,
        (float) (engine.frameCount * 1000.0f) / (float) (engine.endTime - engine.startTime));


error_exit:

    dmPrint(1, "Shutting down.\n");
    SDL_ShowCursor(SDL_ENABLE);

    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();

    dmFreeTimeline(engine.tl);
    dmFreePreparedTimelineData(engine.ptl);
    engineShutdownEffects(&engine);
    dmres_close(engine.resources);

    if (engine.demoShutdown != NULL)
        engine.demoShutdown(&engine);

    if (initSDL)
        SDL_Quit();

    if (engine.demoQuit != NULL)
        engine.demoQuit(&engine);
    
    return 0;
}