[Contents] [Index] [Help] [Retrace] [Browse <] [Browse >]

/* copychunks
 *
 * For Read/Modify/Write programs and other programs that need
 *   to close the IFF file but still reference gathered chunks.
 * Copies your gathered property and collection chunks
 *   from an iff context so that IFF handle may be
 *   closed right after parsing (allowing file or clipboard to
 *   to be reopened for read or write by self or other programs)
 *
 * The created list of chunks can be modified and written
 *   back out to a new handle with writechunklist().
 *
 * If you have used copychunks(), remember to free the copied
 *   chunks with freechunklist(), when ready, to deallocate them.
 *
 * Note that this implementation is flat and is suitable only
 *   for simple FORMs.
 */

#include "iffp/iff.h"

/* copychunks()
 *
 * Copies chunks specified in propchks and collectchks
 *   FROM an already-parsed IFFHandle
 *   TO a singly linked list of Chunk structures,
 * and returns a pointer to the start of the list.
 *
 * Generally you would store this pointer in parseInfo.copiedchunks.
 *
 * You must later free the list of copied chunks by calling
 *   FreeChunkList().
 *
 * Reorders collection chunks so they appear in SAME ORDER
 * in chunk list as they did in the file.
 *
 * Returns 0 for failure
 */
struct Chunk *copychunks(struct IFFHandle *iff,
                        LONG *propchks, LONG *collectchks,
                        ULONG memtype)
    {
    struct Chunk *chunk, *first=NULL, *prevchunk = NULL;
    struct StoredProperty *sp;
    struct CollectionItem *ci, *cii;
    long error;
    int k, kk, bk;

    if(!iff)    return(NULL);

    /* Copy gathered property chunks */
    error = 0;
    for(k=0; (!error) && (propchks) && (propchks[k] != TAG_DONE); k+=2)
        {
        if(sp=FindProp(iff,propchks[k],propchks[k+1]))
            {
            D(bug("copying %.4s.%.4s chunk\n",&propchks[k],&propchks[k+1]));

            if(chunk=(struct Chunk *)
                        AllocMem(sizeof(struct Chunk),memtype|MEMF_CLEAR))
                {
                chunk->ch_Type = propchks[k];
                chunk->ch_ID   = propchks[k+1];
                if(chunk->ch_Data = AllocMem(sp->sp_Size,memtype))
                    {
                    chunk->ch_Size = sp->sp_Size;
                    CopyMem(sp->sp_Data,chunk->ch_Data,sp->sp_Size);
                    if(prevchunk)       prevchunk->ch_Next = chunk;
                    else                first = chunk;
                    prevchunk = chunk;
                    }
                else
                    {
                    FreeMem(chunk,sizeof(struct Chunk));
                    chunk=NULL;
                    error = 1;
                    }
                }
            else error = 1;
            }
        }

    /* Copy gathered collection chunks in reverse order */
    for(k=0; (!error) && (collectchks) && (collectchks[k] != TAG_DONE); k+=2)
        {
        if(ci=FindCollection(iff,collectchks[k],collectchks[k+1]))
            {
            D(bug("copying %.4s.%.4s collection\n",&collectchks[k],&collectchks[k+1]));
            for(cii=ci, bk=0; cii; cii=cii->ci_Next)       bk++;

            D(bug(" There are %ld of these, first is at $%lx\n",bk,ci));

            for( bk; bk; bk--)
                {
                for(kk=1, cii=ci; kk<bk; kk++) cii=cii->ci_Next;

                D(bug("  copying number %ld\n",kk));

                if(chunk=(struct Chunk *)
                    AllocMem(sizeof(struct Chunk),memtype|MEMF_CLEAR))
                    {
                    chunk->ch_Type = collectchks[k];
                    chunk->ch_ID   = collectchks[k+1];
                    if(chunk->ch_Data = AllocMem(cii->ci_Size,memtype))
                        {
                        chunk->ch_Size = cii->ci_Size;
                        CopyMem(cii->ci_Data,chunk->ch_Data,cii->ci_Size);
                        if(prevchunk)   prevchunk->ch_Next = chunk;
                        else            first = chunk;
                        prevchunk = chunk;
                        }
                    else
                        {
                        FreeMem(chunk,sizeof(struct Chunk));
                        chunk=NULL;
                        error = 1;
                        }
                    }
                else error = 1;
                }
            }
        }

    if(error)
        {
        if(first) freechunklist(first);
        first = NULL;
        }

    return(first);
    }

/* freechunklist - Free a dynamically allocated Chunk list and
 *   all of its ch_Data.
 *
 * Note - if a chunk's ch_Size is IFFSIZE_UNKNOWN, its ch_Data
 *   will not be deallocated.
 */
void freechunklist(struct Chunk *first)
    {
    struct Chunk *chunk, *next;

    chunk = first;
    while(chunk)
        {
        next = chunk->ch_Next;
        if((chunk->ch_Data)&&(chunk->ch_Size != IFFSIZE_UNKNOWN))
                FreeMem(chunk->ch_Data,chunk->ch_Size);
        FreeMem(chunk, sizeof(struct Chunk));
        chunk = next;
        }
    }


/* findchunk - find first matching chunk in list of struct Chunks
 *    example  finchunk(pi->copiedchunks,ID_ILBM,ID_CRNG);
 *
 * returns struct Chunk *, or NULL if none found
 */
struct Chunk *findchunk(struct Chunk *first, long type, long id)
    {
    struct Chunk *chunk;

    for(chunk=first; chunk; chunk=chunk->ch_Next)
        {
        if((chunk->ch_Type == type)&&(chunk->ch_ID == id)) return(chunk);
        }
    return(NULL);
    }


/* writechunklist - write out list of struct Chunk's
 * If data is a null terminated string, you may use
 * IFFSIZE_UNKNOWN as the ch_Szie and strlen(chunk->ch_Data)
 * will be used here as size.
 *
 * Returns 0 for success or an IFFERR
 */
long writechunklist(struct IFFHandle *iff, struct Chunk *first)
    {
    struct Chunk *chunk;
    long size, error = 0;

    D(bug("writechunklist: first chunk pointer = $%lx\n",first));

    for(chunk=first; chunk && (!error); chunk=chunk->ch_Next)
        {
        size  = (chunk->ch_Size == IFFSIZE_UNKNOWN) ?
                        strlen(chunk->ch_Data) :  chunk->ch_Size;
        error = PutCk(iff, chunk->ch_ID, size, chunk->ch_Data);
        D(bug("writechunklist: put %.4s size=%ld, error=%ld\n",
                                &chunk->ch_ID,size, error));
        }
    return(error);
    }