/* ** Command & Conquer Red Alert(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see <http://www.gnu.org/licenses/>. */ /**************************************************************************** * * C O N F I D E N T I A L -- W E S T W O O D S T U D I O S * *--------------------------------------------------------------------------- * * PROJECT * VQA player library. (32-Bit protected mode) * * FILE * loader.c * * DESCRIPTION * Stream loading and pre-processing. * * PROGRAMMER * Bill Randolph * Denzil E. Long, Jr. * * DATE * February 23, 1995 * *--------------------------------------------------------------------------- * * PUBLIC * VQA_Open - Open a VQA file to play. * VQA_Close - Close an opened VQA file. * VQA_LoadFrame - Load the next video frame from the VQA data stream. * * PRIVATE * AllocBuffers - Allocates the numerous VQA play buffers * FreeBuffers - Frees the VQA play buffers * Load_FINF - Loads the Frame Info Table. * Load_VQHD - Loads a VQA Header. * Load_CBF0 - Loads a full, uncompressed codebook * Load_CBFZ - Loads a full, compressed codebook * Load_CBP0 - Loads a partial uncompressed codebook * Load_CBPZ - Loads a partial compressed codebook * Load_CPL0 - Loads an uncompressed palette * Load_CPLZ - Loads a compressed palette * Load_VPT0 - Loads uncompressed pointers * Load_VPTZ - Loads compressed pointers * Load_VQF - Loads a VQ Frame chunk * Load_SND0 - Loads an uncompressed sound chunk * Load_SND1 - Loads a compressed sound chunk * Copy_SND - Copies data from Audio Temp buf into Audio play buf * Load_VOC_Block - Loads blocks from separate VOC file, if needed. * ****************************************************************************/ #include <stdio.h> #include <fcntl.h> #include <io.h> #include <malloc.h> #include <mem.h> #include <dos.h> #include "vq.h" #include "vqaplayp.h" #include <vqm32\all.h> /*--------------------------------------------------------------------------- * PRIVATE DECLARATIONS *-------------------------------------------------------------------------*/ typedef struct _ChunkHeader { unsigned long id; unsigned long size; } ChunkHeader; static VQAData *AllocBuffers(VQAHeader *header, VQAConfig *config); static void FreeBuffers(VQAData *vqa, VQAConfig *config); static long Load_VQF(VQAHandleP *vqap, unsigned long iffsize); static long Load_FINF(VQAHandleP *vqap, unsigned long iffsize); static long Load_VQHD(VQAHandleP *vqap, unsigned long iffsize); static long Load_CBF0(VQAHandleP *vqap, unsigned long iffsize); static long Load_CBFZ(VQAHandleP *vqap, unsigned long iffsize); static long Load_CBP0(VQAHandleP *vqap, unsigned long iffsize); static long Load_CBPZ(VQAHandleP *vqap, unsigned long iffsize); static long Load_CPL0(VQAHandleP *vqap, unsigned long iffsize); static long Load_CPLZ(VQAHandleP *vqap, unsigned long iffsize); static long Load_VPT0(VQAHandleP *vqap, unsigned long iffsize); static long Load_VPTZ(VQAHandleP *vqap, unsigned long iffsize); #if(VQAAUDIO_ON) static long Load_SND0(VQAHandleP *vqap, unsigned long iffsize); static long Load_SND1(VQAHandleP *vqap, unsigned long iffsize); static long Load_SND2(VQAHandleP *vqap, unsigned long iffsize); static long Copy_SND(VQAHandleP *vqap); #if(VQAVOC_ON && VQAAUDIO_ON) static void Load_VOC_Block(VQAHandleP *vqap); #endif /* VQAVOC_ON */ #endif /* VQAAUDIO_ON */ /**************************************************************************** * * NAME * VQA_Open - Open a VQA file to play. * * SYNOPSIS * Error = VQA_Open(VQAHandle, Name, Config) * * short VQA_Open(VQAHandle *, char *, VQAConfig *); * * FUNCTION * - Open a VQA file for reading. * - Validate that it is an IFF file, of the VQA type. * - Read the VQA header. * - Open a VOC file for playback, if requested. * - Set the Loader's frame rate, if the caller's Config structure's * FrameRate is set to -1 * - Set the Drawer's frame rate, if the caller's Config structure's * DrawRate is set to -1 * * INPUTS * VQAHandle - Pointer to initialized handle. Obtained by VQA_Alloc(). * Name - Pointer to name of VQA file to open. * Config - Pointer to initialized VQA configuration structure. * * RESULT * Error - 0 if successful, or VQAERR_ error code. * ****************************************************************************/ long VQA_Open(VQAHandle *vqa, char const *filename, VQAConfig *config) { VQAHandleP *vqap; VQAHeader *header; ChunkHeader chunk; long max_frm_size; long i; /* Dereference commonly used data members for quicker access. */ vqap = (VQAHandleP *)vqa; header = &vqap->Header; /*------------------------------------------------------------------------- * VERIFY VALIDITY OF VQA FILE. *-----------------------------------------------------------------------*/ /* Open the file. */ if (vqap->StreamHandler(vqa, VQACMD_OPEN, (void *)filename, 0)) { return (VQAERR_OPEN); } /* Read the file ID & Size */ if (vqap->StreamHandler(vqa, VQACMD_READ, &chunk, 8)) { VQA_Close(vqa); return (VQAERR_READ); } /* Verify an IFF FORM */ if ((chunk.id != ID_FORM) || (chunk.size == 0)) { VQA_Close(vqa); return (VQAERR_NOTVQA); } /* Read in WVQA ID */ if (vqap->StreamHandler(vqa, VQACMD_READ, &chunk, 4)) { VQA_Close(vqa); return (VQAERR_READ); } /* Verify VQA */ if (chunk.id != ID_WVQA) { VQA_Close(vqa); return (VQAERR_NOTVQA); } /*------------------------------------------------------------------------- * READ IN THE VQA HEADER. *-----------------------------------------------------------------------*/ if (vqap->StreamHandler(vqa, VQACMD_READ, &chunk, 8)) { VQA_Close(vqa); return (VQAERR_READ); } chunk.size = REVERSE_LONG(chunk.size); /* Is this a valid VQA header? */ if ((chunk.id != ID_VQHD) || (chunk.size != sizeof(VQAHeader))) { VQA_Close(vqa); return (VQAERR_NOTVQA); } /* Read the header data. */ if (vqap->StreamHandler(vqa, VQACMD_READ, header, PADSIZE(chunk.size))) { VQA_Close(vqa); return (VQAERR_READ); } /*------------------------------------------------------------------------- * INITIALIZE THE PLAYERS CONFIGURATION *-----------------------------------------------------------------------*/ /* Use the clients configuration if they provided one. */ if (config != NULL) { memcpy(&vqap->Config, config, sizeof(VQAConfig)); } else { VQA_DefaultConfig(&vqap->Config); } /* Use the internal configuration structure from now on. */ config = &vqap->Config; if (config->ImageWidth == -1) { config->ImageWidth = header->ImageWidth; } if (config->ImageHeight == -1) { config->ImageHeight = header->ImageHeight; } /*------------------------------------------------------------------------- * ALLOCATE THE BUFFERS THAT WE NEED TO PLAY THE VQA. *-----------------------------------------------------------------------*/ if ((vqap->VQABuf = AllocBuffers(header, config)) == NULL) { VQA_Close(vqa); return (VQAERR_NOMEM); } /*------------------------------------------------------------------------- * SET THE LOADER AND DRAWER PLAYBACK RATES. *-----------------------------------------------------------------------*/ /* If Loaders frame rate is -1 then use the value from the header. */ if (config->FrameRate == -1) { config->FrameRate = header->FPS; } /* If Drawers frame rate is -1 then use the value from the header, which * will result in a "variable" frame rate. */ if (config->DrawRate == -1) { config->DrawRate = header->FPS; } /* Finally, if the DrawRate was set to -1 or 0 (ie MaxRate contained bogus * values), set it to the header value. */ if ((config->DrawRate == -1) || (config->DrawRate == 0)) { config->DrawRate = header->FPS; } /*------------------------------------------------------------------------- * INITIALIZE THE VIDEO SYSTEM IF WE ARE REQUIRED TO HANDLE THAT. *-----------------------------------------------------------------------*/ #if(VQAVIDEO_ON) if ((vqap->VQABuf->Drawer.Display = SetVideoMode(config->Vmode)) == 0) { VQA_Close(vqa); return (VQAERR_VIDEO); } /* Set the VBIBit polarity. */ vqap->VQABuf->VBIBit = GetVBIBit(); #else if (config->VBIBit == -1) { config->VBIBit = TestVBIBit(); } vqap->VQABuf->VBIBit = config->VBIBit; #endif /* VQAVIDEO_ON */ /*------------------------------------------------------------------------- * AUDIO TRACK OVERRIDE (VOC FILE) *-----------------------------------------------------------------------*/ /* Open VOC file if one is requested. */ #if(VQAVOC_ON && VQAAUDIO_ON) if (config->VocFile != NULL) { vqap->vocfh = open(config->VocFile, (O_RDONLY|O_BINARY)); } else { vqap->vocfh = -1; } /* Make sure we won't try to play audio. */ if ((vqap->vocfh == -1) && ((header->Flags & VQAHDF_AUDIO) == 0)) { config->OptionFlags &= (~VQAOPTF_AUDIO); } #else /* VQAVOC_ON */ /* If the movie does not contain an audio track make sure we won't try * to play one. */ if (((header->Flags & VQAHDF_AUDIO) == 0)) { config->OptionFlags &= (~VQAOPTF_AUDIO); } #endif /* VQAVOC_ON */ /*------------------------------------------------------------------------- * INITIALIZE THE AUDIO PLAYBACK/TIMING SYSTEM. *-----------------------------------------------------------------------*/ #if(VQAAUDIO_ON) if (config->OptionFlags & VQAOPTF_AUDIO) { /* Open HMI audio resource for playback. */ if (VQA_OpenAudio(vqap)) { VQA_Close(vqa); return (VQAERR_AUDIO); } } if (!(config->OptionFlags & VQAOPTF_AUDIO) || (config->TimerMethod == VQA_TMETHOD_INT)) { /* Start HMI timer system for timing. */ if (VQA_StartTimerInt((config->OptionFlags & VQAOPTF_HMIINIT))) { VQA_Close(vqa); return (VQAERR_AUDIO); } } #endif /* VQAAUDIO_ON */ return (0); } /**************************************************************************** * * NAME * VQA_Close - Close an opened VQA file. * * SYNOPSIS * VQA_Close(VQAHandle) * * void VQA_Close(VQAHandle *); * * FUNCTION * Close the file that was opened with VQA_Open(). * * INPUTS * VQAHandle - Pointer VQAHandle to close. * * RESULT * NONE * ****************************************************************************/ void VQA_Close(VQAHandle *vqa) { /* Restore video mode to text. */ #if(VQAVIDEO_ON) SetVideoMode(TEXT); #endif /* VQAVIDEO_ON */ /* Shutdown audio/timing system. */ #if(VQAAUDIO_ON) if (((VQAHandleP *)vqa)->Config.OptionFlags & VQAOPTF_AUDIO) { VQA_CloseAudio(); } else { VQA_StopTimerInt(); } #endif /* VQAAUDIO_ON */ /* Free memory */ if (((VQAHandleP *)vqa)->VQABuf != NULL) { FreeBuffers(((VQAHandleP *)vqa)->VQABuf, &((VQAHandleP *)vqa)->Config); } /* Close the VOC override file if one was opened */ #if(VQAVOC_ON && VQAAUDIO_ON) if (((VQAHandleP *)vqa)->vocfh != -1) { close(((VQAHandleP *)vqa)->vocfh); } #endif /* VQAVOC_ON */ /* Close the VQA file */ ((VQAHandleP *)vqa)->StreamHandler(vqa, VQACMD_CLOSE, NULL, 0); } /**************************************************************************** * * NAME * VQA_LoadFrame - Load the next video frame from the VQA data stream. * * SYNOPSIS * Error = VQA_LoadFrame(VQAHandle) * * short VQA_LoadFrame(VQAHandle *); * * FUNCTION * The codebook is split up such that the last frame of every group gets * a new, complete codebook, ready for the next group. The first codebook * in the VQA is a full codebook, and goes with the first frame's data. * Partial codebooks are stored per frame after that, and they add up to * a full codebook just before the first frame for the next group is read. * * (Currently, this routine can read either the older non-frame-grouped * VQA file format, or the new frame-chunk format. For the older format, * it's assumed that the last chunk in a frame is the pointer data.) * * This routine also does a sort of "cooperative multitasking". If the * Loader hits a "wait state" where it has to wait on the audio to finish * playing before it can continue to load, it sets a "sleep" flag and * just returns. The sleep flag is checked on entry to see if it needs * to jump to the proper execution point. This may improve performance on * some platforms, but it also allows the Loader to be called regardless * of the size of the buffers; if the buffers fill up or the audio fails * to play, the Loader won't just get stuck. * * INPUTS * VQAHandle - Pointer to VQAHandle structure. * * RESULT * Error - 0 if successful or VQA_??? error code. * ****************************************************************************/ long VQA_LoadFrame(VQAHandle *vqa) { VQAHandleP *vqap; VQAData *vqabuf; VQALoader *loader; VQAFrameNode *curframe; ChunkHeader chunk; unsigned long iffsize; long frame_loaded = 0; /* Dereference commonly used data members for quicker access. */ vqap = (VQAHandleP *)vqa; vqabuf = vqap->VQABuf; loader = &vqabuf->Loader; curframe = loader->CurFrame; /* If we're reading audio from a VOC file then service that requirement. */ #if(VQAVOC_ON && VQAAUDIO_ON) if (vqap->vocfh != -1) { Load_VOC_Block(vqap); } #endif /* VQAAUDIO_ON & VQAVOC_ON */ /* If no buffer is available for loading then return. This allows the * drawer to service one of the buffers more readily. (We'll wait for one * to free up). */ if (curframe->Flags & VQAFRMF_LOADED) { loader->WaitsOnDrawer++; return (VQAERR_NOBUFFER); } /* If we're not sleeping, initialize */ if (!(vqabuf->Flags & VQADATF_LSLEEP)) { frame_loaded = 0; loader->FrameSize = 0; /* Initialize the codebook ptr for the frame we're about to load: * (This frame's codebook is the last full codebook; we have to init it * now, because if we're on the last frame in a group, we'll get a new * FullCB pointer.) */ curframe->Codebook = loader->FullCB; } /*------------------------------------------------------------------------- * THE MAIN LOADER LOOP *-----------------------------------------------------------------------*/ while (frame_loaded == 0) { /* Read new chunk, only if we're not sleeping */ if (!(vqabuf->Flags & VQADATF_LSLEEP)) { /* Read chunk ID */ if (vqap->StreamHandler(vqa, VQACMD_READ, &chunk, 8)) { return (VQAERR_EOF); } iffsize = REVERSE_LONG(chunk.size); loader->FrameSize += iffsize; } /* Handle each chunk type */ switch (chunk.id) { /* VQ Normal Frame */ case ID_VQFR: if (Load_VQF(vqap, iffsize)) { return (VQAERR_READ); } frame_loaded = 1; break; /* VQ Key Frame */ case ID_VQFK: if (Load_VQF(vqap, iffsize)) { return (VQAERR_READ); } /* Flag this frame as being key. */ curframe->Flags |= VQAFRMF_KEY; frame_loaded = 1; break; /* Frame Info Table */ case ID_FINF: if (Load_FINF(vqap, iffsize)) { return (VQAERR_READ); } break; /* VQA Header */ case ID_VQHD: if (Load_VQHD(vqap, iffsize)) { return (VQAERR_READ); } break; /* Full uncompressed codebook */ case ID_CBF0: if (Load_CBF0(vqap, iffsize)) { return (VQAERR_READ); } break; /* Full compressed codebook */ case ID_CBFZ: if (Load_CBFZ(vqap, iffsize)) { return (VQAERR_READ); } break; /* Partial uncompressed codebook */ case ID_CBP0: if (Load_CBP0(vqap, iffsize)) { return (VQAERR_READ); } break; /* Partial compressed codebook */ case ID_CBPZ: if (Load_CBPZ(vqap, iffsize)) { return (VQAERR_READ); } break; /* Uncompressed palette */ case ID_CPL0: if (Load_CPL0(vqap, iffsize)) { return (VQAERR_READ); } /* Flag this frame as having a palette. */ curframe->Flags |= VQAFRMF_PALETTE; break; /* Compressed palette */ case ID_CPLZ: if (Load_CPLZ(vqap, iffsize)) { return (VQAERR_READ); } /* Flag this frame as having a palette. */ curframe->Flags |= VQAFRMF_PALETTE; break; /* Uncompressed pointer data */ case ID_VPT0: if (Load_VPT0(vqap, iffsize)) { return (VQAERR_READ); } frame_loaded = 1; break; /* Compressed pointer data */ case ID_VPTZ: case ID_VPTD: if (Load_VPTZ(vqap, iffsize)) { return (VQAERR_READ); } frame_loaded = 1; break; /* Pointer data Key (Must draw) */ case ID_VPTK: if (Load_VPTZ(vqap, iffsize)) { return (VQAERR_READ); } /* Flag this frame as being key. */ curframe->Flags |= VQAFRMF_KEY; frame_loaded = 1; break; /* Uncompressed audio frame. * * - Make sure the sound load buffer (Audio.TempBuf) is empty; if not * go into a sleep state. * - Load the data into TempBuf. */ #if(VQAAUDIO_ON) case ID_SND0: /* Move the last audio frame to the play buffer. */ if (Copy_SND(vqap) == VQAERR_SLEEPING) { vqabuf->Flags |= VQADATF_LSLEEP; return (VQAERR_SLEEPING); } else { vqabuf->Flags &= (~VQADATF_LSLEEP); } /* Load an uncompressed audio frame. */ if (Load_SND0(vqap, iffsize) != 0) { return (VQAERR_READ); } break; /* Compressed audio frame. * * - Make sure the sound load buffer (Audio.TempBuf) is empty; if not * go into a sleep state. * - Load the data into TempBuf. */ case ID_SND1: /* Move the last audio frame to the play buffer. */ if (Copy_SND(vqap) == VQAERR_SLEEPING) { vqabuf->Flags |= VQADATF_LSLEEP; return (VQAERR_SLEEPING); } else { vqabuf->Flags &= (~VQADATF_LSLEEP); } /* Load a compressed audio frame. */ if (Load_SND1(vqap, iffsize) != 0) { return (VQAERR_READ); } break; /* HMI ADPCM compressed audio frame. * * - Make sure the sound load buffer (Audio.TempBuf) is empty; if not * go into a sleep state. * - Load the data into TempBuf. */ case ID_SND2: /* Move the last audio frame to the play buffer. */ if (Copy_SND(vqap) == VQAERR_SLEEPING) { vqabuf->Flags |= VQADATF_LSLEEP; return (VQAERR_SLEEPING); } else { vqabuf->Flags &= (~VQADATF_LSLEEP); } /* Load a compressed audio frame. */ if (Load_SND2(vqap, iffsize) != 0) { return (VQAERR_READ); } break; #endif /* Skip any unknown chunks. */ default: if (vqap->StreamHandler(vqa, VQACMD_SEEK, NULL, PADSIZE(iffsize))) { return (VQAERR_SEEK); } break; } } /* Update maximum frame size stat. */ if ((loader->CurFrameNum>0) && (loader->FrameSize>loader->MaxFrameSize)) { loader->MaxFrameSize = loader->FrameSize; } /*------------------------------------------------------------------------- * SET UP THE FRAME FOR DRAWING. *-----------------------------------------------------------------------*/ /* Set the frame # */ curframe->FrameNum = loader->CurFrameNum; loader->CurFrameNum++; /* Update data for mono output */ loader->LastFrameNum = loader->CurFrameNum; /* Loader is finished with this frame; tell Drawer to draw it */ curframe->Flags |= VQAFRMF_LOADED; loader->CurFrame = curframe->Next; return (0); } /**************************************************************************** * * NAME * AllocBuffers - Allocate VQA play buffers. * * SYNOPSIS * VQAData = AllocBuffers(VQAFile) * * VQAData *AllocBuffers(VQAFile *); * * FUNCTION * For those structures that contain buffer pointers (codebook nodes, * frame buffer nodes), enough memory is allocated for both the structure * and its associated buffers, then the buffer pointers are pointed to * the appropriate offset from the structure pointer. This allows us * to perform only one malloc & free for each node. * * Buffers allocated: * - vqa * - vqa->CBData (list) * - vqa->FrameData (list) * - vqa->Drawer.ImageBuf * - vqa->Audio.Buffer * - vqa->Audio.IsLoaded * - vqa->Foff * * INPUTS * VQAHeader - Pointer to VQAHeader structure. * VQAConfig - Pointer to VQA configuration structure. * * RESULT * VQAData - Pointer to initialized VQAData structure. * ****************************************************************************/ static VQAData *AllocBuffers(VQAHeader *header, VQAConfig *config) { VQAData *vqa; VQACBNode *cbnode; VQACBNode *this_cb; VQAFrameNode *framenode; VQAFrameNode *this_frame; long i; /* Check the configuration for valid values. */ if ((config->NumCBBufs == 0) || (config->NumFrameBufs == 0) || (config->AudioBufSize < config->HMIBufSize)) { return (NULL); } /* Allocate the master structure */ if ((vqa = (VQAData *)malloc(sizeof(VQAData))) == NULL) { return (NULL); } /*------------------------------------------------------------------------- * INITIALIZE THE VQA DATA STRUCTURES. * * Pointers are set to NULL initially, and filled in as the buffers are * allocated. The Max buffer sizes are computed with 1K of padding, * and'd with 0xFFFC to make the size divisible by 4, to ensure DWORD * alignment. *-----------------------------------------------------------------------*/ memset(vqa, 0, sizeof(VQAData)); vqa->MemUsed = sizeof(VQAData); vqa->Drawer.LastTime = (-VQA_TIMETICKS); /* Set maximum codebook size. */ vqa->Max_CB_Size = ((header->CBentries) * header->BlockWidth * header->BlockHeight + 250) & 0xFFFC; /* Set maximum palette size. */ vqa->Max_Pal_Size = (768 + 1024) & 0xFFFC; /* Set maximum vector pointers size. */ vqa->Max_Ptr_Size = ((header->ImageWidth / header->BlockWidth) * (header->ImageHeight / header->BlockHeight) * sizeof(short) + 1024) & 0xFFFC; /* Set the frame number of the frame containing the last codebook. */ vqa->Loader.LastCBFrame = (((header->Frames - 1) / header->Groupsize) * header->Groupsize); /*------------------------------------------------------------------------- * ALLOCATE THE CODEBOOK BUFFERS. *-----------------------------------------------------------------------*/ for (i = 0; i < config->NumCBBufs; i++) { /* Allocate a codebook node. */ cbnode = (VQACBNode *)malloc((sizeof(VQACBNode) + vqa->Max_CB_Size)); /* If failure then clean up and exit. */ if (cbnode == NULL) { FreeBuffers(vqa, config); return (NULL); } /* Keep count of the memory usage. */ vqa->MemUsed += (long)(sizeof(VQACBNode) + vqa->Max_CB_Size); /* Initialize the node */ memset(cbnode, 0, sizeof(VQACBNode)); cbnode->Buffer = (unsigned char *)cbnode + sizeof(VQACBNode); /* Install the node */ if (i == 0) { vqa->CBData = cbnode; this_cb = cbnode; } else { this_cb->Next = cbnode; this_cb = cbnode; } } /* Make the list circular */ cbnode->Next = vqa->CBData; /* Install the Codebook list */ vqa->Loader.CurCB = vqa->CBData; vqa->Loader.FullCB = vqa->CBData; /*------------------------------------------------------------------------- * ALLOCATE THE FRAME BUFFERS. *-----------------------------------------------------------------------*/ for (i = 0; i < config->NumFrameBufs; i++) { /* Allocate a pointer node */ framenode = (VQAFrameNode *)malloc((sizeof(VQAFrameNode) + vqa->Max_Ptr_Size + vqa->Max_Pal_Size)); /* If failure then clean up and exit. */ if (framenode == NULL) { FreeBuffers(vqa, config); return (NULL); } /* Keep count of the memory usage. */ vqa->MemUsed += (long)(sizeof(VQAFrameNode) + vqa->Max_Ptr_Size + vqa->Max_Pal_Size); /* Initialize the node */ memset(framenode, 0, sizeof(VQAFrameNode)); framenode->Pointers = (unsigned char *)framenode + sizeof(VQAFrameNode); framenode->Palette = (unsigned char *)framenode + sizeof(VQAFrameNode) + vqa->Max_Ptr_Size; framenode->Codebook = vqa->CBData; /* Install the node */ if (i == 0) { vqa->FrameData = framenode; this_frame = framenode; } else { this_frame->Next = framenode; this_frame = framenode; } } /* Make the list circular */ framenode->Next = vqa->FrameData; /* Install the Frame Buffer list */ vqa->Loader.CurFrame = vqa->FrameData; vqa->Drawer.CurFrame = vqa->FrameData; vqa->Flipper.CurFrame = vqa->FrameData; /*------------------------------------------------------------------------- * ALLOCATE THE IMAGE BUFFERS IF ONE IS NOT ALREADY PROVIDED. *-----------------------------------------------------------------------*/ if (config->ImageBuf == NULL) { /* Allocate our own buffer. */ if (config->DrawFlags & VQACFGF_BUFFER) { vqa->Drawer.ImageBuf = (unsigned char *)malloc((header->ImageWidth * header->ImageHeight)); /* If the allocation failed we must free up and exit. */ if (vqa->Drawer.ImageBuf == NULL) { FreeBuffers(vqa, config); return (NULL); } /* Plugin image buffer information. */ vqa->Drawer.ImageWidth = header->ImageWidth; vqa->Drawer.ImageHeight = header->ImageHeight; vqa->MemUsed += (long)(header->ImageWidth * header->ImageHeight); } else { vqa->Drawer.ImageWidth = config->ImageWidth; vqa->Drawer.ImageHeight = config->ImageHeight; } } else { /* Use caller provided buffer */ vqa->Drawer.ImageBuf = config->ImageBuf; vqa->Drawer.ImageWidth = config->ImageWidth; vqa->Drawer.ImageHeight = config->ImageHeight; } /*------------------------------------------------------------------------- * ALLOCATE AND INITIALIZE AUDIO BUFFERS AND STRUCTURES. *-----------------------------------------------------------------------*/ #if(VQAAUDIO_ON) /* Version 1 VQA's only supported 22050 8 bit mono audio. */ if (header->Version < VQAHD_VER2) { vqa->Audio.SampleRate = 22050U; vqa->Audio.Channels = 1; vqa->Audio.BitsPerSample = 8; } else { vqa->Audio.SampleRate = header->SampleRate; vqa->Audio.Channels = header->Channels; vqa->Audio.BitsPerSample = header->BitsPerSample; } if ((config->AudioBufSize == 0) && (config->HMIBufSize == 0)) { vqa->Audio.NumAudBlocks = 0; } else { vqa->Audio.NumAudBlocks = (config->AudioBufSize / config->HMIBufSize); } if (config->AudioBufSize > 0) { /* Allocate an audio buffer if the user did not provide one. */ if (config->AudioBuf == NULL) { vqa->Audio.Buffer = (unsigned char *)malloc(config->AudioBufSize); /* If failure then clean up and exit. */ if (vqa->Audio.Buffer == NULL) { FreeBuffers(vqa, config); return (NULL); } /* Add audio buffer size to memory usage. */ vqa->MemUsed += (long)config->AudioBufSize; } else { vqa->Audio.Buffer = config->AudioBuf; } /* Allocate IsLoaded flags */ vqa->Audio.IsLoaded = (short *)malloc(vqa->Audio.NumAudBlocks * sizeof(short)); /* If failure then clean up and exit. */ if (vqa->Audio.IsLoaded == NULL) { FreeBuffers(vqa, config); return (NULL); } /* Keep a running total of memory usage. */ vqa->MemUsed += (long)(vqa->Audio.NumAudBlocks * sizeof(short)); /* Initalize audio frames is loaded flags to false. */ memset(vqa->Audio.IsLoaded,0,vqa->Audio.NumAudBlocks * sizeof(short)); /* Temp buffer */ #if(0) // DENZIL vqa->Audio.TempBuf = (unsigned char *)malloc(VQA_AUD_TEMPSIZE); #else /* Adjust the temp buffer size for the size of audio data. */ i = VQA_AUD_TEMPSIZE; i *= vqa->Audio.Channels * (vqa->Audio.BitsPerSample >> 3); vqa->Audio.TempBuf = (unsigned char *)malloc(i); #endif if (vqa->Audio.TempBuf == NULL) { FreeBuffers(vqa, config); return (NULL); } /* Keep a running total of memory usage. */ vqa->MemUsed += i; } #endif /* VQAAUDIO_ON */ /*------------------------------------------------------------------------- * ALLOCATE THE FRAME INFORMATION TABLE IF REQUESTED. *-----------------------------------------------------------------------*/ if (config->OptionFlags & VQAOPTF_FINF) { vqa->Foff = (long *)malloc(header->Frames * sizeof(long)); if (vqa->Foff == NULL) { FreeBuffers(vqa, config); return (NULL); } /* Keep a running total of memory usage. */ vqa->MemUsed += (header->Frames * sizeof(long)); } return (vqa); } /**************************************************************************** * * NAME * FreeBuffers - Free VQA play buffers. * * SYNOPSIS * FreeBuffers(VQAData) * * void FreeBuffers(VQAData *); * * FUNCTION * Free the buffers allocated by AllocBuffers(). * * INPUTS * VQAData - Pointer to VQAData structure. * * RESULT * NONE * ****************************************************************************/ static void FreeBuffers(VQAData *vqa, VQAConfig *config) { VQACBNode *cb_this, *cb_next; VQAFrameNode *frame_this, *frame_next; long i; /*------------------------------------------------------------------------- * FREE THE FRAME INFORMATION TABLE. *-----------------------------------------------------------------------*/ if (vqa->Foff) { free(vqa->Foff); } /*------------------------------------------------------------------------- * FREE THE AUDIO BUFFERS. *-----------------------------------------------------------------------*/ #if(VQAAUDIO_ON) if ((config->AudioBuf == NULL) && (vqa->Audio.Buffer)) { free(vqa->Audio.Buffer); } /* Free the audio segments loaded flag array. */ if (vqa->Audio.IsLoaded) { free(vqa->Audio.IsLoaded); } /* Free the temporary audio buffer. */ if (vqa->Audio.TempBuf) { free(vqa->Audio.TempBuf); } #endif /* VQAAUDIO_ON */ /*------------------------------------------------------------------------- * FREE THE IMAGE BUFFER ONLY IF WE ALLOCATED IT. *-----------------------------------------------------------------------*/ if ((config->ImageBuf == NULL) && vqa->Drawer.ImageBuf) { free(vqa->Drawer.ImageBuf); } /*------------------------------------------------------------------------- * FREE THE FRAME BUFFERS. *-----------------------------------------------------------------------*/ frame_this = vqa->FrameData; for (i = 0; i < config->NumFrameBufs; i++) { if (frame_this) { frame_next = frame_this->Next; free(frame_this); frame_this = frame_next; } else { break; } } /*------------------------------------------------------------------------- * FREE THE CODEBOOK BUFFERS. *-----------------------------------------------------------------------*/ cb_this = vqa->CBData; for (i = 0; i < config->NumCBBufs; i++) { if (cb_this) { cb_next = cb_this->Next; free(cb_this); cb_this = cb_next; } else { break; } } /*------------------------------------------------------------------------- * FREE THE VQA DATA STRUCTURES. *-----------------------------------------------------------------------*/ free(vqa); } /**************************************************************************** * * NAME * Load_VQF - Loads a VQ Frame chunk. * * SYNOPSIS * Error = Load_VQF(VQAFile, VQAData, Iffsize) * * short Load_VQF(VQAFile *, VQAData *, unsigned long); * * FUNCTION * The VQ Frame Chunk contains a set of other chunks (codebooks, * palettes, pointers). This routine reads the frame's chunk size, * then loops until it's read that many bytes. * * INPUTS * VQAFile - Pointer to VQAFile structure. * VQAData - Pointer to VQAData structure. * Iffsize - Size of IFF chunk. * * RESULT * Error - 0 if successful or VQA_??? error code. * ****************************************************************************/ static long Load_VQF(VQAHandleP *vqap, unsigned long frame_iffsize) { VQAData *vqabuf; VQAFrameNode *curframe; ChunkHeader chunk; unsigned long iffsize; unsigned long framesize; unsigned long bytes_loaded = 0; /* Dereference commonly used data members for quicker access. */ vqabuf = vqap->VQABuf; curframe = vqabuf->Loader.CurFrame; framesize = PADSIZE(frame_iffsize); /*------------------------------------------------------------------------- * FRAME LOADING LOOP. *-----------------------------------------------------------------------*/ while (bytes_loaded < framesize) { /* Read chunk ID */ if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, &chunk, 8)) { return (VQAERR_EOF); } iffsize = REVERSE_LONG(chunk.size); bytes_loaded += 8; bytes_loaded += PADSIZE(iffsize); /* Handle each chunk type */ switch (chunk.id) { /* Full uncompressed codebook */ case ID_CBF0: if (Load_CBF0(vqap, iffsize)) { return (VQAERR_READ); } break; /* Full compressed codebook */ case ID_CBFZ: if (Load_CBFZ(vqap, iffsize)) { return (VQAERR_READ); } break; /* Partial uncompressed codebook */ case ID_CBP0: if (Load_CBP0(vqap, iffsize)) { return (VQAERR_READ); } break; /* Partial compressed codebook */ case ID_CBPZ: if (Load_CBPZ(vqap, iffsize)) { return (VQAERR_READ); } break; /* Uncompressed palette */ case ID_CPL0: if (Load_CPL0(vqap, iffsize)) { return (VQAERR_READ); } /* Flag this frame as having a palette. */ curframe->Flags |= VQAFRMF_PALETTE; break; /* Compressed palette */ case ID_CPLZ: if (Load_CPLZ(vqap, iffsize)) { return (VQAERR_READ); } /* Flag this frame as having a palette. */ curframe->Flags |= VQAFRMF_PALETTE; break; /* Uncompressed pointer data */ case ID_VPT0: if (Load_VPT0(vqap, iffsize)) { return (VQAERR_READ); } break; /* Compressed pointer data */ case ID_VPTZ: case ID_VPTD: if (Load_VPTZ(vqap, iffsize)) { return (VQAERR_READ); } break; /* Compressed pointer data */ case ID_VPTK: if (Load_VPTZ(vqap, iffsize)) { return (VQAERR_READ); } /* Flag this frame as being key. */ curframe->Flags |= VQAFRMF_KEY; break; /* An unknown chunk in the video frame is an error. */ default: return (VQAERR_READ); } } return (0); } /**************************************************************************** * * NAME * Load_FINF - Load Frame Info chunk. * * SYNOPSIS * Error = Load_FINF(VQAFile, VQAData, Iffsize) * * short Load_FINF(VQAFile *, VQAData *, Iffsize); * * FUNCTION * Load FINF chunk if buffer available, otherwise skip it. * * INPUTS * VQAFile - Pointer to VQAFile structure * VQAData - Pointer to VQAData structure * Iffsize - Size of IFF chunk. * * RESULT * Error - 0 if successful or VQA_??? error code. * ****************************************************************************/ static long Load_FINF(VQAHandleP *vqap, unsigned long iffsize) { VQAData *vqabuf; /* Dereference commonly used data members for quicker access. */ vqabuf = vqap->VQABuf; /* Load the frame information table if we need to, otherwise we will * skip it. */ if ((vqap->Config.OptionFlags & VQAOPTF_FINF) && (vqabuf->Foff != NULL)) { if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, vqabuf->Foff, PADSIZE(iffsize))) { return (VQAERR_READ); } } else { if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_SEEK, NULL, PADSIZE(iffsize))) { return (VQAERR_SEEK); } } return (0); } /**************************************************************************** * * NAME * Load_VQHD - Load VQA header chunk. * * SYNOPSIS * Error = Load_VQHD(VQAFile, VQAData, Iffsize) * * short Load_VQHD(VQAFile *, VQAData *, Iffsize); * * FUNCTION * * INPUTS * VQAFile - Pointer to VQAFile structure * VQAData - Pointer to VQAData structure * Iffsize - Size of IFF chunk. * * RESULT * Error - 0 if successful or VQA_??? error code. * ****************************************************************************/ static long Load_VQHD(VQAHandleP *vqap, unsigned long iffsize) { /* Read the header */ if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, &vqap->Header, PADSIZE(iffsize))) { return (VQAERR_READ); } /* Reconfigure the Drawer for the new settings */ VQA_Configure_Drawer(vqap); return (0); } /**************************************************************************** * * NAME * Load_CBF0 - Load full uncompressed codebook. * * SYNOPSIS * Error = Load_CBF0(VQAFile, VQAData, Iffsize) * * short Load_CBF0(VQAFile *, VQAData *, Iffsize); * * FUNCTION * * INPUTS * VQAFile - Pointer to VQAFile structure * VQAData - Pointer to VQAData structure * Iffsize - Size of IFF chunk. * * RESULT * Error - 0 if successful or VQA_??? error code. * ****************************************************************************/ static long Load_CBF0(VQAHandleP *vqap, unsigned long iffsize) { VQALoader *loader; VQACBNode *curcb; /* Dereference commonly used data members for quicker access. */ loader = &vqap->VQABuf->Loader; curcb = loader->CurCB; /* Read into the start of the buffer */ if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, curcb->Buffer, PADSIZE(iffsize))) { return (VQAERR_READ); } /* Reset the partial codebook counter. */ loader->NumPartialCB = 0; /* Flag this codebook as uncompressed. */ curcb->Flags &= (~VQACBF_CBCOMP); curcb->CBOffset = 0; /* Clock pointers to next CB Buffer. */ loader->FullCB = curcb; loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); loader->CurCB = curcb->Next; return (0); } /**************************************************************************** * * NAME * Load_CBFZ - Load full compressed codebook. * * SYNOPSIS * Error = Load_CBFZ(VQAFile, VQAData, Iffsize) * * short Load_CBFZ(VQAFile *, VQAData *, Iffsize); * * FUNCTION * * INPUTS * VQAFile - Pointer to VQAFile structure * VQAData - Pointer to VQAData structure * Iffsize - Size of IFF chunk. * * RESULT * Error - 0 if successful or VQA_??? error code. * ****************************************************************************/ static long Load_CBFZ(VQAHandleP *vqap, unsigned long iffsize) { VQALoader *loader; VQACBNode *curcb; void *buffer; unsigned long padsize; unsigned long lcwoffset; /* Dereference commonly used data members for quicker access. */ loader = &vqap->VQABuf->Loader; curcb = loader->CurCB; padsize = PADSIZE(iffsize); /* Load the codebook into the end of the buffer. */ lcwoffset = vqap->VQABuf->Max_CB_Size - (unsigned short)padsize; buffer = curcb->Buffer + lcwoffset; if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { return (VQAERR_READ); } /* Reset the partial codebook counter. */ loader->NumPartialCB = 0; /* Flag this codebook as compressed */ curcb->Flags |= VQACBF_CBCOMP; curcb->CBOffset = lcwoffset; /* Clock pointers to next CB Buffer */ loader->FullCB = curcb; loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); loader->CurCB = curcb->Next; return (0); } /**************************************************************************** * * NAME * Load_CBP0 - Load partial uncompressed codebook. * * SYNOPSIS * Error = Load_CBP0(VQAFile, VQAData, Iffsize) * * short Load_CBP0(VQAFile *, VQAData *, Iffsize); * * FUNCTION * * INPUTS * VQAFile - Pointer to VQAFile structure * VQAData - Pointer to VQAData structure * Iffsize - Size of IFF chunk. * * RESULT * Error - 0 if successful or VQA_??? error code. * ****************************************************************************/ static long Load_CBP0(VQAHandleP *vqap, unsigned long iffsize) { VQAData *vqabuf; VQALoader *loader; VQACBNode *curcb; void *buffer; /* Dereference commonly used data members for quicker access. */ vqabuf = vqap->VQABuf; loader = &vqabuf->Loader; curcb = loader->CurCB; /*------------------------------------------------------------------------- * ASSEMBLY PARTIAL CODEBOOKS. *-----------------------------------------------------------------------*/ /* Read the partial codebook into the next position in the buffer. */ buffer = curcb->Buffer + loader->PartialCBSize; if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, buffer, PADSIZE(iffsize))) { return (VQAERR_READ); } /* Accumulate the partial codebook values. */ loader->PartialCBSize += (unsigned short)iffsize; loader->NumPartialCB++; /*------------------------------------------------------------------------- * PROCESS FULL CODEBOOK. *-----------------------------------------------------------------------*/ if (loader->NumPartialCB == vqap->Header.Groupsize) { /* Reset the codebook accumulator values */ loader->NumPartialCB = 0; loader->PartialCBSize = 0; /* Flag this codebook as uncompressed */ curcb->Flags &= (~VQACBF_CBCOMP); curcb->CBOffset = 0; /* Go to the next codebook buffer */ loader->FullCB = curcb; loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); loader->CurCB = curcb->Next; } return (0); } /**************************************************************************** * * NAME * Load_CBPZ - Load partial compressed codebook. * * SYNOPSIS * Error = Load_CBPZ(VQAFile, VQAData, Iffsize) * * short Load_CBPZ(VQAFile *, VQAData *, Iffsize); * * FUNCTION * * INPUTS * VQAFile - Pointer to VQAFile structure * VQAData - Pointer to VQAData structure * Iffsize - Size of IFF chunk. * * RESULT * Error - 0 if successful or VQA_??? error code. * ****************************************************************************/ static long Load_CBPZ(VQAHandleP *vqap, unsigned long iffsize) { VQAData *vqabuf; VQALoader *loader; VQACBNode *curcb; void *buffer; unsigned long padsize; /* Dereference commonly used data members for quicker access */ vqabuf = vqap->VQABuf; loader = &vqabuf->Loader; curcb = loader->CurCB; padsize = PADSIZE(iffsize); /* Attempt to compute the LCW offset into the codebook buffer by * multiplying the size of this chunk by the # frames/group, and adding * a small fudge factor on, then subtracting that from the CB buffer size. */ if (loader->PartialCBSize == 0) { curcb->CBOffset = (vqabuf->Max_CB_Size - ((unsigned short)padsize * vqap->Header.Groupsize + 100)); } /*------------------------------------------------------------------------- * ASSEMBLE PARTIAL CODEBOOKS. *-----------------------------------------------------------------------*/ /* Read the partial codebook into the next position in the buffer. */ buffer = ((curcb->Buffer + curcb->CBOffset) + loader->PartialCBSize); if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { return (VQAERR_READ); } /* Accumulate partial codebook values */ loader->PartialCBSize += (unsigned short)iffsize; loader->NumPartialCB++; /*------------------------------------------------------------------------- * PROCESS FULL CODEBOOK. *-----------------------------------------------------------------------*/ if (loader->NumPartialCB == vqap->Header.Groupsize) { /* Reset the codebook accumulator values. */ loader->NumPartialCB = 0; loader->PartialCBSize = 0; /* Flag this codebook as compressed. */ curcb->Flags |= VQACBF_CBCOMP; /* Go to the next codebook buffer */ loader->FullCB = curcb; loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); loader->CurCB = curcb->Next; } return (0); } /**************************************************************************** * * NAME * Load_CPL0 - Load an uncompressed palette. * * SYNOPSIS * Error = Load_CPL0(VQAFile, VQAData, Iffsize) * * short Load_CPL0(VQAFile *, VQAData *, Iffsize); * * FUNCTION * * INPUTS * VQAFile - Pointer to VQAFile structure * VQAData - Pointer to VQAData structure * Iffsize - Size of IFF chunk. * * RESULT * Error - 0 if successful or VQA_??? error code. * ****************************************************************************/ static long Load_CPL0(VQAHandleP *vqap, unsigned long iffsize) { VQAFrameNode *curframe; /* Dereference commonly used data members for quicker access. */ curframe = vqap->VQABuf->Loader.CurFrame; /* Read the palette into the palette buffer */ if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, curframe->Palette, PADSIZE(iffsize))) { return (VQAERR_READ); } /* Flag the palette as uncompressed. */ curframe->Flags &= ~VQAFRMF_PALCOMP; curframe->PalOffset = 0; curframe->PaletteSize = (unsigned short)iffsize; return (0); } /**************************************************************************** * * NAME * Load_CPLZ - Load compressed palette. * * SYNOPSIS * Error = Load_CPLZ(VQAFile, VQAData, Iffsize) * * short Load_CPLZ(VQAFile *, VQAData *, Iffsize); * * FUNCTION * * INPUTS * VQAFile - Pointer to VQAFile structure * VQAData - Pointer to VQAData structure * Iffsize - Size of IFF chunk. * * RESULT * Error - 0 if successful or VQA_??? error code. * ****************************************************************************/ static long Load_CPLZ(VQAHandleP *vqap, unsigned long iffsize) { VQAFrameNode *curframe; void *buffer; unsigned long padsize; unsigned long lcwoffset; /* Dereference commonly used data members for quicker access. */ curframe = vqap->VQABuf->Loader.CurFrame; padsize = PADSIZE(iffsize); /* Read the palette into the end of the palette buffer. */ lcwoffset = vqap->VQABuf->Max_Pal_Size - (unsigned short)padsize; buffer = curframe->Palette + lcwoffset; if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { return (VQAERR_READ); } /* Flag this palette as compressed. */ curframe->Flags |= VQAFRMF_PALCOMP; curframe->PalOffset = lcwoffset; curframe->PaletteSize = (unsigned short)iffsize; return (0); } /**************************************************************************** * * NAME * Load_VPT0 - Load uncompressed pointers. * * SYNOPSIS * Error = Load_VPT0(VQAFile, VQAData, Iffsize) * * short Load_VPT0(VQAFile *, VQAData *, Iffsize); * * FUNCTION * * INPUTS * VQAFile - Pointer to VQAFile structure * VQAData - Pointer to VQAData structure * Iffsize - Size of IFF chunk. * * RESULT * Error - 0 if successful or VQA_??? error code. * ****************************************************************************/ static long Load_VPT0(VQAHandleP *vqap, unsigned long iffsize) { VQAFrameNode *curframe; /* Dereference commonly used data members for quicker access. */ curframe = vqap->VQABuf->Loader.CurFrame; /* Read the pointers into start of the pointer buffer. */ if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, curframe->Pointers, PADSIZE(iffsize))) { return (VQAERR_READ); } /* Flag this frame as uncompressed */ curframe->Flags &= ~VQAFRMF_PTRCOMP; curframe->PtrOffset = 0; return (0); } /**************************************************************************** * * NAME * Load_VPTZ - Load compressed pointers. * * SYNOPSIS * Error = Load_VPTZ(VQAFile, VQAData, Iffsize) * * short Load_VPTZ(VQAFile *, VQAData *, Iffsize); * * FUNCTION * * INPUTS * VQAFile - Pointer to VQAFile structure * VQAData - Pointer to VQAData structure * Iffsize - Size of IFF chunk. * * RESULT * Error - 0 if successful or VQA_??? error code. * ****************************************************************************/ static long Load_VPTZ(VQAHandleP *vqap, unsigned long iffsize) { VQAFrameNode *curframe; void *buffer; unsigned long padsize; unsigned long lcwoffset; /* Dereference commonly used data members for quicker access. */ curframe = vqap->VQABuf->Loader.CurFrame; padsize = PADSIZE(iffsize); lcwoffset = vqap->VQABuf->Max_Ptr_Size - (unsigned short)padsize; /* Read the pointers into end of the pointer buffer. */ buffer = curframe->Pointers + lcwoffset; if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { return (VQAERR_READ); } /* Flag this frame as compressed. */ curframe->Flags |= VQAFRMF_PTRCOMP; curframe->PtrOffset = lcwoffset; return (0); } #if(VQAAUDIO_ON) /**************************************************************************** * * NAME * Load_SND0 - Load uncompressed sound chunk. * * SYNOPSIS * Error = Load_SND0(VQAFile, VQAData, Iffsize) * * short Load_SND0(VQAFile *, VQAData *, Iffsize); * * FUNCTION * This routine normally loads the chunk into the TempBuf, unless the * chunk is larger than the temp buffer size, in which case it puts it * directly into the audio buffer itself. This assumes that the only * such chunk will be the first audio chunk! * * INPUTS * VQAFile - Pointer to VQAFile structure * VQAData - Pointer to VQAData structure * Iffsize - Size of IFF chunk. * * RESULT * Error - 0 if successful or VQA_??? error code. * ****************************************************************************/ static long Load_SND0(VQAHandleP *vqap, unsigned long iffsize) { VQAData *vqabuf; VQALoader *loader; VQAAudio *audio; VQAConfig *config; unsigned long padsize; long i; /* Dereference commonly used data members for quicker access. */ vqabuf = vqap->VQABuf; loader = &vqabuf->Loader; audio = &vqabuf->Audio; config = &vqap->Config; padsize = PADSIZE((unsigned short)iffsize); /* If sound is disabled, or if we're playing from a VOC file, or if * there's no Audio Buffer, just skip the chunk. */ #if(VQAVOC_ON && VQAAUDIO_ON) if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) || (audio->Buffer == NULL)) { #else /* VQAVOC_ON */ if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (audio->Buffer == NULL)) { #endif /* VQAVOC_ON */ if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_SEEK, NULL, padsize)) { return (VQAERR_SEEK); } else { return (0); } } /* Read large startup chunk directly into AudioBuf */ if ((padsize > VQA_AUD_TEMPSIZE) && (loader->AudBufPos == 0)) { if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, audio->Buffer, padsize)) { return (VQAERR_READ); } loader->AudBufPos += (unsigned short)iffsize; /* Flag the audio frame flags as loaded for the initial audio frame. */ for (i = 0; i < (iffsize / config->HMIBufSize); i++) { audio->IsLoaded[i] = 1; } return (0); } else { /* Read data into TempBuf */ if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, audio->TempBuf, padsize)) { return (VQAERR_READ); } } /* Set the TempBufLen */ audio->TempBufLen = (unsigned short)iffsize; return (0); } /**************************************************************************** * * NAME * Load_SND1 - Load compressed sound chunk. * * SYNOPSIS * Error = Load_SND1(VQAFile, VQAData, Iffsize) * * short Load_SND1(VQAFile *, VQAData *, Iffsize); * * FUNCTION * This routine normally loads the chunk into the TempBuf, unless the * chunk is larger than the temp buffer size, in which case it puts it * directly into the audio buffer itself. This assumes that the only * such chunk will be the first audio chunk! * * INPUTS * VQAFile - Pointer to VQAFile structure * VQAData - Pointer to VQAData structure * Iffsize - Size of IFF chunk. * * RESULT * Error - 0 if successful or VQA_??? error code. * ****************************************************************************/ static long Load_SND1(VQAHandleP *vqap, unsigned long iffsize) { VQAData *vqabuf; VQALoader *loader; VQAAudio *audio; VQAConfig *config; unsigned char *loadbuf; unsigned long padsize; unsigned long uncomp_size; unsigned long comp_size; long i; /* Dereference commonly used data members for quicker access. */ vqabuf = vqap->VQABuf; loader = &vqabuf->Loader; audio = &vqabuf->Audio; config = &vqap->Config; padsize = PADSIZE((unsigned short)iffsize); /* If sound is disabled, or if we're playing from a VOC file, or if * there's no Audio Buffer, just skip the chunk */ #if(VQAVOC_ON && VQAAUDIO_ON) if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) || (audio->Buffer == NULL)) { #else /* VQAVOC_ON */ if (((config->OptionFlags & VQAOPTF_AUDIO)==0) || (audio->Buffer==NULL)) { #endif /* VQAVOC_ON */ if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_SEEK, NULL, padsize)) { return (VQAERR_SEEK); } else { return (0); } } /* Read the uncompressed data size */ if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, &uncomp_size, 2)) { return (VQAERR_READ); } /* Read the compressed data size */ if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, &comp_size, 2)) { return (VQAERR_READ); } /* Adjust chunk size */ padsize -= 4; /* Read large startup chunk directly into AudioBuf */ if ((uncomp_size > VQA_AUD_TEMPSIZE) && (loader->AudBufPos == 0)) { /* Load uncompressed data */ if (uncomp_size == comp_size) { if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, audio->Buffer, padsize)) { return (VQAERR_READ); } } else { /* Load compressed data into the end of the buffer. */ loadbuf = (audio->Buffer + config->AudioBufSize) - padsize; if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, loadbuf, padsize)) { return (VQAERR_READ); } /* Uncompress the audio frame. */ AudioUnzap(loadbuf, audio->Buffer, uncomp_size); } /* Set buffer positions & flags */ loader->AudBufPos += uncomp_size; for (i = 0; i < (uncomp_size / config->HMIBufSize); i++) { audio->IsLoaded[i] = 1; } return (0); } /* Load an audio frame. */ if (uncomp_size == comp_size) { /* If the frame is uncompressed the load it in directly. */ if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, audio->TempBuf, padsize)) { return (VQAERR_READ); } } else { /* Load the audio frame into the end of the buffer. */ loadbuf = ((audio->TempBuf + VQA_AUD_TEMPSIZE) - padsize); if (vqap->StreamHandler((VQAHandle *)vqap,VQACMD_READ,loadbuf,padsize)) { return (VQAERR_READ); } /* Uncompress the audio frame. */ AudioUnzap(loadbuf, audio->TempBuf, uncomp_size); } /* Set the TempBufLen */ audio->TempBufLen = uncomp_size; return (0); } /**************************************************************************** * * NAME * Load_SND2 - Load compressed sound chunk. * * SYNOPSIS * Error = Load_SND2(VQAFile, VQAData, Iffsize) * * short Load_SND2(VQAFile *, VQAData *, Iffsize); * * FUNCTION * This routine normally loads the chunk into the TempBuf, unless the * chunk is larger than the temp buffer size, in which case it puts it * directly into the audio buffer itself. This assumes that the only * such chunk will be the first audio chunk! * * INPUTS * VQAFile - Pointer to VQAFile structure * VQAData - Pointer to VQAData structure * Iffsize - Size of IFF chunk. * * RESULT * Error - 0 if successful or VQA_??? error code. * ****************************************************************************/ static long Load_SND2(VQAHandleP *vqap, unsigned long iffsize) { VQAData *vqabuf; VQALoader *loader; VQAAudio *audio; VQAConfig *config; unsigned char *loadbuf; unsigned long padsize; unsigned long uncomp_size; long i; /* Dereference commonly used data members for quicker access. */ vqabuf = vqap->VQABuf; loader = &vqabuf->Loader; audio = &vqabuf->Audio; config = &vqap->Config; padsize = PADSIZE(iffsize); /* If sound is disabled, or if we're playing from a VOC file, or if * there's no Audio Buffer, just skip the chunk */ #if(VQAVOC_ON && VQAAUDIO_ON) if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) || (audio->Buffer == NULL)) { #else /* VQAVOC_ON */ if (((config->OptionFlags & VQAOPTF_AUDIO)==0) || (audio->Buffer==NULL)) { #endif /* VQAVOC_ON */ if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_SEEK, NULL, padsize)) { return (VQAERR_SEEK); } else { return (0); } } uncomp_size = iffsize * (8 / 4); /* Read large startup chunk directly into AudioBuf */ if ((uncomp_size > VQA_AUD_TEMPSIZE) && (loader->AudBufPos == 0)) { /* Load compressed data into the end of the buffer. */ loadbuf = (audio->Buffer + config->AudioBufSize) - padsize; if (vqap->StreamHandler((VQAHandle *)vqap,VQACMD_READ,loadbuf,padsize)) { return (VQAERR_READ); } /* Uncompress the audio frame. */ audio->sSOSInfo.lpSource = (char *)loadbuf; audio->sSOSInfo.lpDest = (char *)audio->Buffer; sosCODECDecompressData(&audio->sSOSInfo, uncomp_size); /* Set buffer positions & flags */ loader->AudBufPos += uncomp_size; for (i = 0; i < (uncomp_size / config->HMIBufSize); i++) { audio->IsLoaded[i] = 1; } return (0); } /* Load an audio frame. */ loadbuf = ((audio->TempBuf + VQA_AUD_TEMPSIZE) - padsize); if (vqap->StreamHandler((VQAHandle *)vqap,VQACMD_READ,loadbuf,padsize)) { return (VQAERR_READ); } /* Uncompress the audio frame. */ audio->sSOSInfo.lpSource = (char *)loadbuf; audio->sSOSInfo.lpDest = (char *)audio->TempBuf; sosCODECDecompressData(&audio->sSOSInfo, uncomp_size); /* Set the TempBufLen */ audio->TempBufLen = uncomp_size; return (0); } /**************************************************************************** * * NAME * Copy_SND - Copy data from Audio Temp buffer into Audio play buffer. * * SYNOPSIS * Error = Copy_SND(VQAFile, VQAData) * * short Load_SND0(VQAFile *, VQAData *); * * FUNCTION * This routine just copies the data in the TempBuf into the correct * spots in the audio play buffer. If there is no room available in the * audio play buffer, the routine returns VQAERR_SLEEPING, which will put * the whole Loader to "sleep" while it waits for a free buffer. * * If there's no data in the TempBuf to copy, the routine just returns 0. * * INPUTS * VQAFile - Pointer to VQAFile structure * VQAData - Pointer to VQAData structure * * RESULT * Error - 0 if successful or VQA_??? error code. * ****************************************************************************/ static long Copy_SND(VQAHandleP *vqap) { VQAData *vqabuf; VQALoader *loader; VQAAudio *audio; VQAConfig *config; long startblock; long endblock; long len1,len2; long i; /* Dereference commonly used data members for quicker access. */ vqabuf = vqap->VQABuf; loader = &vqabuf->Loader; audio = &vqabuf->Audio; config = &vqap->Config; /* If audio is disabled, or if we're playing from a VOC file, or if * there's no Audio Buffer, or if there's no data to copy, just return 0 */ #if(VQAVOC_ON && VQAAUDIO_ON) if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) || (audio->Buffer == NULL) || (audio->TempBufLen == 0)) { #else /* VQAVOC_ON */ if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (audio->Buffer == NULL) || (audio->TempBufLen == 0)) { #endif /* VQAVOC_ON */ return (0); } /* Compute start & end blocks to copy into */ startblock = (loader->AudBufPos / config->HMIBufSize); endblock = (loader->AudBufPos + audio->TempBufLen) / config->HMIBufSize; if (endblock >= audio->NumAudBlocks) { endblock -= audio->NumAudBlocks; } /* If 'endblock' hasn't played yet, return VQAERR_SLEEPING */ if (audio->IsLoaded[endblock] == 1) { return (VQAERR_SLEEPING); } /* Copy the data: * * - If 'startblock' < 'endblock', copy the entire buffer * - Otherwise, fill to the end of the buffer with part of the data, then * copy the rest to the beginning of the buffer */ if (startblock <= endblock) { /* Copy data */ memcpy((audio->Buffer + loader->AudBufPos), audio->TempBuf, audio->TempBufLen); /* Adjust current load position */ loader->AudBufPos += audio->TempBufLen; /* Mark buffer as empty */ audio->TempBufLen = 0; /* Set all blocks to loaded */ for (i = startblock; i < endblock; i++) { audio->IsLoaded[i] = 1; } return (0); } else { /* Compute length of each piece */ len1 = config->AudioBufSize - loader->AudBufPos; len2 = audio->TempBufLen - len1; /* Copy 1st piece into end of Audio Buffer */ memcpy((audio->Buffer + loader->AudBufPos), audio->TempBuf, len1); /* Copy 2nd piece into start of Audio Buffer */ memcpy(audio->Buffer, audio->TempBuf + len1, len2); /* Adjust load position */ loader->AudBufPos = len2; /* Mark buffer as empty */ audio->TempBufLen = 0; /* Set blocks to loaded */ for (i = startblock; i < audio->NumAudBlocks; i++) { audio->IsLoaded[i] = 1; } for (i = 0; i < endblock; i++) { audio->IsLoaded[i] = 1; } return (0); } } #if(VQAVOC_ON && VQAAUDIO_ON) /**************************************************************************** * * NAME * Load_VOC_Block - Loads blocks from seperate VOC file. * * SYNOPSIS * Load_VOC_Block(VQAFile, VQAData) * * void Load_VOC_Block(VQAFile *, VQAData *); * * FUNCTION * * INPUTS * VQAFile - Pointer to VQAFile structure * VQAData - Pointer to VQAData structure * * RESULT * NONE * ****************************************************************************/ static void Load_VOC_Block(VQAHandleP *vqap) { VQAData *vqabuf; VQALoader *loader; VQAAudio *audio; VQAConfig *config; static long lastplayblock = -1; static long myblock = 0; static long firsttime = 1; long numblocks; long i; /* Dereference commonly used data members for quicker access. */ vqabuf = vqap->VQABuf; loader = &vqabuf->Loader; audio = &vqabuf->Audio; config = &vqap->Config; /* Do nothing if no buffer */ if (audio->Buffer == NULL) { return; } /* If this is the first time we're called, pre-load the 1st 'n' audio * blocks, where 'n' is half the total audio buffer size; this way, we'll * always stay ahead of HMI. */ if (firsttime) { numblocks = (audio->NumAudBlocks / 2); read(vqap->vocfh, audio->Buffer, config->HMIBufSize * numblocks); loader->AudBufPos += config->HMIBufSize * numblocks; if (loader->AudBufPos >= config->AudioBufSize) { loader->AudBufPos = 0; } for (i = 0; i < numblocks; i++) { audio->IsLoaded[i] = 1; } myblock += numblocks; if (myblock >= audio->NumAudBlocks) { myblock = 0; } firsttime = 0; } /* If HMI's block has changed, load the next block & mark it as loaded */ if (audio->PlayPosition / config->HMIBufSize != lastplayblock) { /* update HMI's last known block position */ lastplayblock = audio->PlayPosition / config->HMIBufSize; /* read the VOC data */ read(vqap->vocfh, (audio->Buffer + myblock * config->HMIBufSize), config->HMIBufSize); loader->AudBufPos += config->HMIBufSize; if (loader->AudBufPos >= config->AudioBufSize) { loader->AudBufPos = 0; } /* set the IsLoaded flags */ audio->IsLoaded[myblock] = 1; /* increment my block counter */ myblock++; if (myblock >= audio->NumAudBlocks) { myblock = 0; } } } #endif /* VQAVOC_ON */ #endif /* VQAAUDIO_ON */