view dmtimeline.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 525f7af644c4
children d4b84101e480
line wrap: on
line source

#include "dmengine.h"


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

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


static int dmLoadTimelinePoints(DMResource *res, DMTimelinePoints *points, int type)
{
    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(points->nallocated, 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))
            return DMERR_FREAD;
        
        switch (type)
        {
            case EFPT_INT:
                {
                    Uint32 tmp;
                    if (!dmf_read_le32(res, &tmp))
                        return DMERR_FREAD;
                    pt->vint = tmp;
                }
                break;

            case EFPT_FLOAT:
                if (!dmLoadFloatValue(res, &pt->vfloat))
                    return DMERR_FREAD;
                break;
            
            case EFPT_VECTOR:
                if (!dmLoadFloatValue(res, &pt->vector.x) ||
                    !dmLoadFloatValue(res, &pt->vector.y) ||
                    !dmLoadFloatValue(res, &pt->vector.z) ||
                    !dmLoadFloatValue(res, &pt->vector.W))
                    return DMERR_FREAD;
                break;

            case EFPT_MATRIX:
                {
                    int x, y;
                    for (y = 0; y < DM_MATRIX_SIZE; y++)
                    for (x = 0; x < DM_MATRIX_SIZE; x++)
                    {
                        if (!dmLoadFloatValue(res, &(pt->matrix.m[y][x])))
                            return DMERR_FREAD;
                    }
                }
                break;
        }
        
        pt->type = ptype;
        pt->time = ptime;
    }
    return DMERR_OK;
}


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

    if (!dmf_read_str(res, (Uint8 *) &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 (param->type)
    {
        case EFPT_INT:
        case EFPT_FLOAT:
        case EFPT_VECTOR:
        case EFPT_MATRIX:
            if ((err = dmLoadTimelinePoints(res, &param->pts, param->type)) != DMERR_OK)
                return err;
            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))
                return DMERR_FREAD;

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

    return DMERR_OK;
}


static 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)) ||
        !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    = engineFindEffect(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;
}


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

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


static 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)))
        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)))
        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)) ||
        !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->pts));
}


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


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


void dmFreePreparedTimelineData(DMPreparedTimeline *ptl)
{
    if (ptl != NULL)
    {
        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;
}