view dmtimeline.c @ 340:1c5e7d66312d

Work on prepared timeline code.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 15 Oct 2012 18:06:48 +0300
parents af04394e9620
children 882503ef7ab8
line wrap: on
line source

#include "dmtimeline.h"


DMEffect *dmEffects = NULL;
int ndmEffects = 0, ndmEffectsAlloc = 0;
void **dmEffectData = NULL;


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

    // Allocate more space for effects
    if (ndmEffects + 1 >= ndmEffectsAlloc)
    {
        ndmEffectsAlloc += 16;
        dmEffects = dmRealloc(dmEffects, sizeof(DMEffect) * ndmEffectsAlloc);
        if (dmEffects == NULL)
        {
            dmError("Could not expand effects structure.\n"); 
            return DMERR_INIT_FAIL;
        }
        
        dmEffectData = dmRealloc(dmEffectData, sizeof(void *) * ndmEffectsAlloc);
        if (dmEffectData == NULL)
        {
            dmError("Could not expand effects data structure.\n"); 
            return DMERR_INIT_FAIL;
        }
    }

    // Copy effects structure
    memcpy(dmEffects + ndmEffects, ef, sizeof(DMEffect));
    dmEffectData[ndmEffects] = NULL;

    ndmEffects++;
    
    return DMERR_OK;
}


int dmInitializeEffects()
{
    int i, res;
    for (i = 0; i < ndmEffects; i++)
    {
        if (dmEffects[i].init != NULL &&
            (res = dmEffects[i].init(&dmEffectData[i])) != DMERR_OK)
            return res;
    }

    return DMERR_OK;
}


void dmShutdownEffects()
{
    int i;
    for (i = 0; i < ndmEffects; i++)
    {
        if (dmEffects[i].shutdown != NULL)
            dmEffects[i].shutdown(dmEffectData[i]);
    }
}


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


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


BOOL dmLoadFloatValue(DMResource *res, DMFloat *val)
{
    char tmp[DT_FLOAT_STORE_SIZE + 1];
    if (dmf_read_str(res, (Uint8 *) tmp, DT_FLOAT_STORE_SIZE) != DT_FLOAT_STORE_SIZE)
        return FALSE;

    tmp[DT_FLOAT_STORE_SIZE] = 0;
    *val = atof(tmp);
    return TRUE;
}


int dmLoadTimelinePoints(DMResource *res, DMTimelinePoints *points)
{
    int point;
    Uint32 npoints;

    // Get number of points
    if (!dmf_read_le32(res, &npoints))
        return DMERR_FREAD;

    // Calculate and allocate space
    points->npoints = npoints;
    points->nallocated = ((npoints / 16) + 1) * 16;
    
    points->points = dmCalloc(npoints, sizeof(DMTimelinePoint));
    if (points->points == NULL)
        return DMERR_MALLOC;
    
    // Read points
    for (point = 0; point < points->npoints; point++)
    {
        DMTimelinePoint *pt = &(points->points[point]);
        Uint32 ptype, ptime;
        if (!dmf_read_le32(res, &ptype) ||
            !dmf_read_le32(res, &ptime) ||
            !dmLoadFloatValue(res, &pt->value))
            return DMERR_FREAD;
        
        pt->type = ptype;
        pt->time = ptime;
    }
    return DMERR_OK;
}


int dmLoadTimelineEventParam(DMResource *res, DMTimelineEventParam *param)
{
    int err;
    DMFTimelineEventParam hdr;
    Uint16 len;
    Uint32 tmp32;

    if (dmf_read_str(res, (Uint8 *) &hdr.name, sizeof(hdr.name)) != sizeof(hdr.name) ||
        !dmf_read_le32(res, &hdr.type))
        return DMERR_FREAD;

    hdr.name[sizeof(hdr.name) - 1] = 0;

    param->name = dm_strdup(hdr.name);
    param->type = hdr.type;

    switch (hdr.type)
    {
        case EFPT_POINTS:
            if ((err = dmLoadTimelinePoints(res, &param->vpts)) != DMERR_OK)
                return err;
            break;
        
        case EFPT_INT:
            if (!dmf_read_le32(res, &tmp32))
                return DMERR_FREAD;
            param->vint = tmp32;
            break;
        
        case EFPT_FLOAT:
            if (!dmLoadFloatValue(res, &param->vfloat))
                return DMERR_FREAD;
            break;
        
        case EFPT_STRING:
            if (!dmf_read_le16(res, &len))
                return DMERR_FREAD;

            param->vstr = dmMalloc((unsigned int)len + 1);
            if (dmf_read_str(res, (Uint8 *) param->vstr, len) != len)
                return DMERR_FREAD;

            param->vstr[len] = 0;
            break;
    }

    return DMERR_OK;
}


int dmLoadTimelineEvent(DMResource *res, DMTimelineEvent **pevent)
{
    int param;
    DMFTimelineEvent hdr;
    DMTimelineEvent *event;
    if ((*pevent = event = dmMalloc0(sizeof(DMTimelineEvent))) == NULL)
        return DMERR_MALLOC;

    // Get basic event data
    if (!dmf_read_le32(res, &hdr.start) ||
        !dmf_read_le32(res, &hdr.duration) ||
        dmf_read_str(res, (Uint8 *) &hdr.effectName, sizeof(hdr.effectName)) != sizeof(hdr.effectName) ||
        !dmf_read_le32(res, &hdr.nparams))
        return DMERR_FREAD;

    hdr.effectName[sizeof(hdr.effectName) - 1] = 0;
    
    if (hdr.nparams > DT_MAX_EFFECT_PARAMS)
    {
        dmError("Invalid number of parameters, %d > %d ('%s' @ %d:%d)\n",
            hdr.nparams, DT_MAX_EFFECT_PARAMS, hdr.effectName, hdr.start, hdr.duration);
        return DMERR_INVALID_DATA;
    }

    event->start     = hdr.start;
    event->duration  = hdr.duration;
    event->nparams   = hdr.nparams;
    event->effect    = dmFindEffect(hdr.effectName, hdr.nparams);
    if (event->effect == NULL)
    {
        dmError("No matching registered effect found for '%s'.\n", hdr.effectName);
        return DMERR_INVALID_DATA;
    }

    event->params    = dmCalloc(event->nparams, sizeof(DMTimelineEventParam));
    if (event->params == NULL)
    {
        dmError("Could not allocate memory for timeline event parameters.\n");
        return DMERR_MALLOC;
    }

    for (param = 0; param < event->nparams; param++)
    {
        int err;
        if ((err = dmLoadTimelineEventParam(res, &(event->params[param]))) != DMERR_OK)
            return err;
    }

    return DMERR_OK;
}


int dmLoadTimelineCurve(DMResource *res, DMTimelineCurve *curve)
{
    int err;

    curve->enabled = dmfgetc(res);
    if ((err = dmLoadTimelinePoints(res, &(curve->points))) != DMERR_OK)
        return err;
    
    return DMERR_OK;
}


int dmLoadTimelineTrack(DMResource *res, DMTimelineTrack **ptrack)
{
    int event, err;
    DMFTimelineTrack hdr;
    DMTimelineTrack *track;
    if ((*ptrack = track = dmMalloc0(sizeof(DMTimelineTrack))) == NULL)
        return DMERR_MALLOC;

    if (dmf_read_str(res, (Uint8 *) &hdr.name, sizeof(hdr.name)) != sizeof(hdr.name))
        return DMERR_FREAD;

    hdr.enabled = dmfgetc(res);
    if (!dmf_read_le32(res, &hdr.nevents))
        return DMERR_FREAD;

    if (hdr.nevents >= 4096)
        return DMERR_INVALID_DATA;

    if ((track->events = dmCalloc(hdr.nevents, sizeof(DMTimelineEvent *))) == NULL)
        return DMERR_MALLOC;

    hdr.name[sizeof(hdr.name) - 1] = 0;
    track->name = dm_strdup(hdr.name);
    track->enabled = hdr.enabled;
    track->nevents = hdr.nevents;

    for (event = 0; event < track->nevents; event++)
    {
        if ((err = dmLoadTimelineEvent(res, &(track->events[event]))) != DMERR_OK)
            return err;
    }

    if ((err = dmLoadTimelineCurve(res, &(track->composite))) != DMERR_OK)
        return err;

    return DMERR_OK;
}


int dmLoadTimeline(DMResource *res, DMTimeline **ptl)
{
    int track, err;
    DMFTimeline hdr;
    DMTimeline *tl;
    if ((*ptl = tl = dmMalloc0(sizeof(DMTimeline))) == NULL)
        return DMERR_MALLOC;

    // Read and check header
    if (dmf_read_str(res, (Uint8 *) &hdr.magic, sizeof(hdr.magic)) != sizeof(hdr.magic))
        return DMERR_FREAD;

    if (memcmp(hdr.magic, DT_MAGIC_ID, sizeof(hdr.magic)) != 0)
        return DMERR_INVALID_DATA;

    if (dmf_read_str(res, (Uint8 *) &hdr.name, sizeof(hdr.name)) != sizeof(hdr.name) ||
        !dmf_read_le32(res, &hdr.ntracks) ||
        !dmf_read_le32(res, &hdr.duration))
        return DMERR_FREAD;

    if (hdr.ntracks >= 64)
        return DMERR_INVALID_DATA;

    // Allocate track pointers
    tl->tracks = (DMTimelineTrack **) dmCalloc(hdr.ntracks, sizeof(DMTimelineTrack *));
    if (tl->tracks == NULL)
        return DMERR_MALLOC;

    // Copy rest
    hdr.name[sizeof(hdr.name) - 1] = 0;
    tl->name     = dm_strdup(hdr.name);
    tl->duration = hdr.duration;
    tl->ntracks  = hdr.ntracks;

    // Read tracks
    for (track = 0; track < tl->ntracks; track++)
    {
        if ((err = dmLoadTimelineTrack(res, &(tl->tracks[track]))) != DMERR_OK)
            return err;
    }
    
    return DMERR_OK;
}


void dmFreeTimelinePoints(DMTimelinePoints *points)
{
    dmFree(points->points);
    points->points = NULL;
    points->npoints = points->nallocated = 0;
}


void dmFreeTimelineEventParam(DMTimelineEventParam *param)
{
    dmFree(param->name);
    dmFree(param->vstr);
    dmFreeTimelinePoints(&(param->vpts));
}


void dmFreeTimelineEvent(DMTimelineEvent *event)
{
    if (event != NULL)
    {
        int param;
        for (param = 0; param < event->nparams; param++)
            dmFreeTimelineEventParam(&(event->params[param]));
        dmFree(event->params);
    }
}


void dmFreeTimelineTrack(DMTimelineTrack *track)
{
    if (track != NULL)
    {
        int event;

        dmFree(track->name);

        for (event = 0; event < track->nevents; event++)
            dmFreeTimelineEvent(track->events[event]);
        dmFree(track->events);

        dmFreeTimelinePoints(&(track->composite.points));
    }
}


void dmFreeTimeline(DMTimeline *tl)
{
    if (tl != NULL)
    {
        int i;
        for (i = 0; i < tl->ntracks; i++)
            dmFreeTimelineTrack(tl->tracks[i]);

        dmFree(tl->tracks);
        dmFree(tl->name);
    }
}


void dmFreePreparedEventGroup(DMPreparedEventGroup *group)
{
    if (group != NULL)
    {
        dmFree(group->events);
        dmFree(group);
    }
}


void dmFreePreparedTimelineData(DMPreparedTimeline *ptl)
{
    int group;
    for (group = 0; group < ptl->ngroups; group++)
    {
        dmFreePreparedEventGroup(ptl->groups[group]);
        ptl->groups[group] = NULL;
    }

    dmFree(ptl->groups);
    ptl->groups = NULL;
}


/* Prepare a loaded timeline for execution. Creates the "stacked" structure
 * of timeline data for efficient rendering.
 */
int dmAddSplitPreparedEventGroup(DMPreparedEventGroup **groups, DMTimelineTrack *track, DMTimelineEvent *event)
{
    DMPreparedEventGroup *node;
    
    for (node = *groups; node != NULL; node = node->next)
    {
    }
    
    return DMERR_OK;
}


int dmPrepareTimeline(DMTimeline *tl, DMPreparedTimeline *ptl)
{
    int group, ntrack, event, err;
    DMPreparedEventGroup *groups = NULL, *node;

    // Free previous data
    dmFreePreparedTimelineData(ptl);

    // Process tracks
    for (ntrack = 0; ntrack < tl->ntracks; ntrack++)
    {
        DMTimelineTrack *track = tl->tracks[ntrack];
        for (event = 0; event < track->nevents; event++)
        {
            if ((err = dmAddSplitPreparedEventGroup(&groups, track, track->events[event])) != DMERR_OK)
                return err;
        }
    }
    
    // Compute number of groups
    ptl->ngroups = 0;
    for (node = groups; node != NULL; node = node->next)
        ptl->ngroups++;

    // Allocate linear array for fast access
    ptl->groups = dmMalloc(sizeof(DMPreparedEventGroup) * ptl->ngroups);
    if (ptl->groups == NULL)
        return DMERR_MALLOC;

    // Store pointers in the array
    for (group = 0, node = groups; node != NULL; node = node->next)
        ptl->groups[group++] = node;
    
    return DMERR_OK;
}


/* Seeks to specified position in the timeline. The execution function
 * only handles monotonously increasing time, going backwards will not work
 * there correctly, thus to seek freely this function must be used.
 */
int dmSeekTimeline(DMPreparedTimeline *tl, int time)
{
    return DMERR_OK;
}


/* "Executes", or rather renders a frame on the specified timeline position.
 */
int dmExecuteTimeline(DMPreparedTimeline *tl, SDL_Surface *screen, int time)
{
    return DMERR_OK;
}