view dmengine.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 a7ee3567f718
children 14640d0688d3
line wrap: on
line source

#include "dmengine.h"
#include "dmimage.h"


#ifdef DM_USE_TREMOR
#include <tremor/ivorbiscodec.h>
#include <tremor/ivorbisfile.h>
#endif


DMEngineData engine;
DMEffect *engineEffects = NULL;
int nengineEffects = 0, nengineEffectsAlloc = 0;


int engineRegisterEffect(const DMEffect *ef)
{
    if (ef == NULL)
        return DMERR_NULLPTR;

    // Allocate more space for effects
    if (nengineEffects + 1 >= nengineEffectsAlloc)
    {
        nengineEffectsAlloc += 16;
        engineEffects = dmRealloc(engineEffects, sizeof(DMEffect) * nengineEffectsAlloc);
        if (engineEffects == NULL)
        {
            dmError("Could not expand effects structure.\n"); 
            return DMERR_INIT_FAIL;
        }
    }

    // Copy effects structure
    memcpy(engineEffects + nengineEffects, ef, sizeof(DMEffect));
    nengineEffects++;
    
    return DMERR_OK;
}


int engineInitializeEffects(DMEngineData *engine)
{
    int i, res;

    dmFree(engine->effectData);
    engine->effectData = dmCalloc(nengineEffectsAlloc, sizeof(void *));
    if (engine->effectData == NULL)
    {
        dmError("Could not expand effects data structure.\n"); 
        return DMERR_INIT_FAIL;
    }

    for (i = 0; i < nengineEffects; i++)
    {
        if (engineEffects[i].init != NULL &&
            (res = engineEffects[i].init(engine, &(engine->effectData[i]))) != DMERR_OK)
            return res;
    }

    return DMERR_OK;
}


void engineShutdownEffects(DMEngineData *engine)
{
    if (engine != NULL && engine->effectData != NULL)
    {
        int i;
        for (i = 0; i < nengineEffects; i++)
        {
            if (engineEffects[i].shutdown != NULL)
                engineEffects[i].shutdown(engine, engine->effectData[i]);
        }
        dmFree(engine->effectData);
        engine->effectData = NULL;
    }
}


DMEffect *engineFindEffect(const char *name, const int nparams)
{
    int i;
    for (i = 0; i < nengineEffects; i++)
    {
        if (strcmp(engineEffects[i].name, name) == 0 &&
            engineEffects[i].nparams == nparams)
            return &engineEffects[i];
    }
    return NULL;
}


DMEffect *engineFindEffectByName(const char *name)
{
    int i;
    for (i = 0; i < nengineEffects; i++)
    {
        if (strcmp(engineEffects[i].name, name) == 0)
            return &engineEffects[i];
    }
    return NULL;
}


static int engineResImageLoad(DMResource *res)
{
    SDL_Surface *img = dmLoadImage(res);
    if (res != NULL)
    {
        res->rdata = img;
        return DMERR_OK;
    }
    else
        return dmferror(res);
}


static void engineResImageFree(DMResource *res)
{
    SDL_FreeSurface((SDL_Surface *)res->rdata);
}

static BOOL engineResImageProbe(DMResource *res, const char *fext)
{
    (void) res;
    return fext != NULL && (strcasecmp(fext, ".jpg") == 0 || strcasecmp(fext, ".png") == 0);
}


#ifdef JSS_SUP_XM
static int engineResModuleLoad(DMResource *res)
{
    return jssLoadXM(res, (JSSModule **) &(res->rdata));
}

static void engineResModuleFree(DMResource *res)
{
    jssFreeModule((JSSModule *) res->rdata);
}

static BOOL engineResModuleProbe(DMResource *res, const char *fext)
{
    (void) res;
    return fext != NULL && (strcasecmp(fext, ".xm") == 0 || strcasecmp(fext, ".jmod") == 0);
}
#endif


#ifdef DM_USE_TREMOR
static size_t vorbisFileRead(void *ptr, size_t size, size_t nmemb, void *datasource)
{
    return dmfread(ptr, size, nmemb, (DMResource *) datasource);
}

static int vorbisFileSeek(void *datasource, ogg_int64_t offset, int whence)
{
    return dmfseek((DMResource *) datasource, offset, whence);
}

static int vorbisFileClose(void *datasource)
{
    (void) datasource;
    return 0;
}

static long vorbisFileTell(void *datasource)
{
    return dmftell((DMResource *) datasource);
}
      

static ov_callbacks vorbisFileCBS =
{
    vorbisFileRead,
    vorbisFileSeek,
    vorbisFileClose,
    vorbisFileTell
};

static int engineResVorbisLoad(DMResource *res)
{
    OggVorbis_File vf;

    dmMsg(1, "vorbisfile '%s', %d bytes resource loading\n",
        res->filename, res->dataSize);

    if (ov_open_callbacks(res, &vf, NULL, 0, vorbisFileCBS) < 0)
        return DMERR_FOPEN;

    res->rdataSize = ov_pcm_total(&vf, -1) * 2 * 2;
    if ((res->rdata = dmMalloc(res->rdataSize + 16)) == NULL)
    {
        ov_clear(&vf);
        return DMERR_MALLOC;
    }

    dmMsg(1, "rdataSize=%d bytes?\n", res->rdataSize);

    BOOL eof = FALSE;
    int left = res->rdataSize;
    char *ptr = res->rdata;
    int current_section;
    while (!eof && left > 0)
    {
        int ret = ov_read(&vf, ptr, left > 4096 ? 4096 : left, &current_section);
        if (ret == 0)
            eof = TRUE;
        else
        if (ret < 0)
        {
            ov_clear(&vf);
            return DMERR_INVALID_DATA;
        }
        else
        {
            left -= ret;
            ptr += ret;
        }
    }

    ov_clear(&vf);
    return DMERR_OK;
}

static void engineResVorbisFree(DMResource *res)
{
    dmFree(res->rdata);
}

static BOOL engineResVorbisProbe(DMResource *res, const char *fext)
{
    (void) res;
    return fext != NULL && (strcasecmp(fext, ".ogg") == 0);
}
#endif


static DMResourceDataOps engineResOps[] =
{
    {
        engineResImageProbe,
        engineResImageLoad,
        engineResImageFree
    },

#ifdef JSS_SUP_XM
    {
        engineResModuleProbe,
        engineResModuleLoad,
        engineResModuleFree
    },
#endif

#ifdef DM_USE_TREMOR
    {
        engineResVorbisProbe,
        engineResVorbisLoad,
        engineResVorbisFree
    },
#endif
};

static const int nengineResOps = sizeof(engineResOps) / sizeof(engineResOps[0]);


int engineClassifier(DMResource *res)
{
    int i;
    char *fext;

    if (res == NULL)
        return DMERR_NULLPTR;
    
    fext = strrchr(res->filename, '.');
    for (i = 0; i < nengineResOps; i++)
    {
        DMResourceDataOps *rops = &engineResOps[i];
        if (rops->probe != NULL && rops->probe(res, fext))
        {
            res->rops = rops;
            return DMERR_OK;
        }
    }
    
    return DMERR_OK;
}


void *engineGetResource(DMEngineData *eng, const char *name)
{
    DMResource *res;
    if (eng != NULL &&
        (res = dmres_find(eng->resources, name)) != NULL &&
        res->rdata != NULL)
        return res->rdata;
    else
    {
        dmError("Could not find resource '%s'.\n", name);
        return NULL;
    }
}


#ifdef DM_USE_JSS
void engineGetJSSInfo(DMEngineData *eng, BOOL *playing, int *order, JSSPattern **pat, int *npattern, int *row)
{
    JSS_LOCK(eng->plr);

    *playing = eng->plr->isPlaying;
    *row = eng->plr->row;
    *pat = eng->plr->pattern;
    *npattern = eng->plr->npattern;
    *order = eng->plr->order;

    JSS_UNLOCK(eng->plr);
}

void engineGetJSSChannelInfo(DMEngineData *eng, const int channel, int *ninst, int *nextInst, int *freq, int *note)
{
    JSS_LOCK(eng->plr);
    JSSPlayerChannel *chn = &(eng->plr->channels[channel]);
    *ninst = chn->ninstrument;
    *nextInst = chn->nextInstrument;
    *freq = chn->freq;
    *note = chn->note;
    JSS_UNLOCK(eng->plr);
}
#endif


int engineGetTick(DMEngineData *engine)
{
    return engine->frameTime - engine->startTime;
}


float engineGetTimeDT(DMEngineData *engine)
{
    return (float) engineGetTick(engine) / 1000.0f;
}


int engineGetTimeDTi(DMEngineData *engine)
{
    return (float) engineGetTick(engine) / 1000;
}


int engineGetTime(DMEngineData *engine, int t)
{
    return engineGetTick(engine) - (1000 * t);
}


int engineGetDT(DMEngineData *engine, int t)
{
    return engineGetTime(engine, t) / 1000;
}