403 lines
9.8 KiB
C++
403 lines
9.8 KiB
C++
![]() |
/*
|
|||
|
** 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
|
|||
|
* caption.c
|
|||
|
*
|
|||
|
* DESCRIPTION
|
|||
|
* Text caption process/display manager.
|
|||
|
*
|
|||
|
* PROGRAMMER
|
|||
|
* Denzil E. Long, Jr.
|
|||
|
*
|
|||
|
* DATE
|
|||
|
* July 26, 1995
|
|||
|
*
|
|||
|
****************************************************************************/
|
|||
|
|
|||
|
#include <mem.h>
|
|||
|
#include <malloc.h>
|
|||
|
#include <vqm32\font.h>
|
|||
|
#include <vqm32\text.h>
|
|||
|
#include <vqm32\graphics.h>
|
|||
|
#include <vqm32\captoken.h>
|
|||
|
#include "caption.h"
|
|||
|
|
|||
|
#if( VQACAPTIONS_ON )
|
|||
|
|
|||
|
|
|||
|
/*---------------------------------------------------------------------------
|
|||
|
* PRIVATE DECLARATIONS
|
|||
|
*-------------------------------------------------------------------------*/
|
|||
|
|
|||
|
#define NUM_NODES 3
|
|||
|
|
|||
|
/* Function prototypes. */
|
|||
|
static CaptionNode *AddCaptionNode(CaptionList *list, CaptionText *captext);
|
|||
|
static void RemCaptionNode(CaptionList *list, CaptionNode *node);
|
|||
|
|
|||
|
|
|||
|
/****************************************************************************
|
|||
|
*
|
|||
|
* NAME
|
|||
|
* OpenCaptions - Initialize the caption system.
|
|||
|
*
|
|||
|
* SYNOPSIS
|
|||
|
* OpenCaptions(Captions, Font)
|
|||
|
*
|
|||
|
* CaptionInfo *OpenCaptions(void *, void *);
|
|||
|
*
|
|||
|
* FUNCTION
|
|||
|
* Allocate and initialize resources used by the caption system.
|
|||
|
*
|
|||
|
* INPUTS
|
|||
|
* Captions - Captions to process.
|
|||
|
* Font - Font to use to display captions.
|
|||
|
*
|
|||
|
* RESULT
|
|||
|
* CaptionInfo - Caption information structure.
|
|||
|
*
|
|||
|
* SEE ALSO
|
|||
|
* CloseCaption
|
|||
|
*
|
|||
|
****************************************************************************/
|
|||
|
|
|||
|
CaptionInfo *OpenCaptions(void *captions, void *font)
|
|||
|
{
|
|||
|
CaptionInfo *cap = NULL;
|
|||
|
CaptionNode *node;
|
|||
|
FontInfo *fi;
|
|||
|
long i;
|
|||
|
|
|||
|
/* Allocate memory for the captioning system. */
|
|||
|
cap = (CaptionInfo *)malloc(sizeof(CaptionInfo) + (sizeof(CaptionNode)
|
|||
|
* NUM_NODES));
|
|||
|
|
|||
|
if (cap != NULL) {
|
|||
|
memset(cap,0,(sizeof(CaptionInfo) + (sizeof(CaptionNode) * NUM_NODES)));
|
|||
|
cap->Buffer = captions;
|
|||
|
cap->Next = (CaptionText *)captions;
|
|||
|
|
|||
|
/* Initialize font */
|
|||
|
fi = (FontInfo *)((char *)font + ((Font *)font)->InfoBlk);
|
|||
|
cap->Font = font;
|
|||
|
cap->FontHeight = fi->MaxHeight;
|
|||
|
cap->FontWidth = fi->MaxWidth;
|
|||
|
|
|||
|
/* Initialize list header. */
|
|||
|
cap->List.Head = (CaptionNode *)&cap->List.Tail;
|
|||
|
cap->List.Tail = NULL;
|
|||
|
cap->List.TailPred = (CaptionNode *)&cap->List.Head;
|
|||
|
|
|||
|
/* Link nodes. */
|
|||
|
node = (CaptionNode *)((char *)cap + sizeof(CaptionInfo));
|
|||
|
|
|||
|
for (i = 0; i < NUM_NODES; i++) {
|
|||
|
node->Succ = cap->List.Head;
|
|||
|
cap->List.Head = node;
|
|||
|
node->Pred = (CaptionNode *)&cap->List.Head;
|
|||
|
node->Succ->Pred = node;
|
|||
|
|
|||
|
/* Next node. */
|
|||
|
node = (CaptionNode *)((char *)node + sizeof(CaptionNode));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return (cap);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/****************************************************************************
|
|||
|
*
|
|||
|
* NAME
|
|||
|
* CloseCaptions - Shutdown the caption system.
|
|||
|
*
|
|||
|
* SYNOPSIS
|
|||
|
* CloseCaptions(CaptionInfo)
|
|||
|
*
|
|||
|
* void CloseCaptions(CaptionInfo *);
|
|||
|
*
|
|||
|
* FUNCTION
|
|||
|
* Free the resources allocated by the caption system.
|
|||
|
*
|
|||
|
* INPUTS
|
|||
|
* CaptionInfo - Caption information structure.
|
|||
|
*
|
|||
|
* RESULT
|
|||
|
* NONE
|
|||
|
*
|
|||
|
* SEE ALSO
|
|||
|
* OpenCaptions
|
|||
|
*
|
|||
|
****************************************************************************/
|
|||
|
|
|||
|
void CloseCaptions(CaptionInfo *cap)
|
|||
|
{
|
|||
|
free(cap);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/****************************************************************************
|
|||
|
*
|
|||
|
* NAME
|
|||
|
* DoCaption - Process and display caption text.
|
|||
|
*
|
|||
|
* SYNOPSIS
|
|||
|
* DoCaption(Captions, Frame)
|
|||
|
*
|
|||
|
* void DoCaption(CaptionInfo *, unsigned long);
|
|||
|
*
|
|||
|
* FUNCTION
|
|||
|
* Process the caption events.
|
|||
|
*
|
|||
|
* INPUTS
|
|||
|
* Captions - Pointer to CaptionInfo structure.
|
|||
|
* Frame - Current frame number being processed.
|
|||
|
*
|
|||
|
* RESULT
|
|||
|
* NONE
|
|||
|
*
|
|||
|
****************************************************************************/
|
|||
|
|
|||
|
void DoCaptions(CaptionInfo *cap, unsigned long frame)
|
|||
|
{
|
|||
|
CaptionText *captext;
|
|||
|
CaptionNode *node;
|
|||
|
void const *oldfont;
|
|||
|
long width;
|
|||
|
long i;
|
|||
|
|
|||
|
/* Initialize variables. */
|
|||
|
oldfont = Set_Font((char *)cap->Font);
|
|||
|
|
|||
|
/*-------------------------------------------------------------------------
|
|||
|
* Process the captions that are on the active queue.
|
|||
|
*-----------------------------------------------------------------------*/
|
|||
|
node = cap->List.Head;
|
|||
|
|
|||
|
while ((node->Succ != NULL) && (node->Flags & CNF_USED)) {
|
|||
|
captext = node->Captext;
|
|||
|
|
|||
|
/* Clear the any previous captions that have expired. */
|
|||
|
if (captext->OffFrame <= frame) {
|
|||
|
Fill_Rect(captext->Xpos, captext->Ypos,
|
|||
|
(captext->Xpos + node->BoundW - 1),
|
|||
|
(captext->Ypos + node->BoundH - 1), 0);
|
|||
|
|
|||
|
/* Remove the caption from the active list. */
|
|||
|
node = node->Pred;
|
|||
|
RemCaptionNode(&cap->List, node->Succ);
|
|||
|
} else {
|
|||
|
if (captext->CPF != 0) {
|
|||
|
|
|||
|
/* If a NULL terminator is not found then display the next set of
|
|||
|
* characters, otherwise remove the node.
|
|||
|
*/
|
|||
|
if (*node->Char != 0) {
|
|||
|
Set_Font_Palette_Range(&captext->BgPen, 0, 1);
|
|||
|
|
|||
|
for (i = 0; i < captext->CPF; i++) {
|
|||
|
|
|||
|
/* Check for terminator. */
|
|||
|
if (*node->Char == 0) {
|
|||
|
captext->CPF = 0;
|
|||
|
break;
|
|||
|
}
|
|||
|
/* Check for newline. */
|
|||
|
else if (*node->Char == 0x0D) {
|
|||
|
node->Char++;
|
|||
|
node->CurX = captext->Xpos;
|
|||
|
node->CurY += cap->FontHeight;
|
|||
|
node->BoundH += cap->FontHeight;
|
|||
|
}
|
|||
|
|
|||
|
Draw_Char(*node->Char, node->CurX, node->CurY);
|
|||
|
node->CurX += Char_Pixel_Width(*node->Char);
|
|||
|
node->Char++;
|
|||
|
}
|
|||
|
}
|
|||
|
} else if (captext->Flags & CTF_FLASH) {
|
|||
|
if (frame & 4) {
|
|||
|
Fill_Rect(captext->Xpos, captext->Ypos,
|
|||
|
(captext->Xpos + node->BoundW - 1),
|
|||
|
(captext->Ypos + node->BoundH - 1), 0);
|
|||
|
} else {
|
|||
|
Text_Print(captext->Text, captext->Xpos, captext->Ypos,
|
|||
|
captext->FgPen, captext->BgPen);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Next node. */
|
|||
|
node = node->Succ;
|
|||
|
}
|
|||
|
|
|||
|
/*-------------------------------------------------------------------------
|
|||
|
* Process any captions that are waiting to be activated.
|
|||
|
*-----------------------------------------------------------------------*/
|
|||
|
captext = cap->Next;
|
|||
|
|
|||
|
while (captext->OnFrame <= frame) {
|
|||
|
|
|||
|
width = String_Pixel_Width(captext->Text);
|
|||
|
|
|||
|
switch (captext->Flags & CTF_JUSTIFY) {
|
|||
|
case CTF_RIGHT:
|
|||
|
captext->Xpos = (319 - width);
|
|||
|
break;
|
|||
|
|
|||
|
case CTF_LEFT:
|
|||
|
captext->Xpos = 0;
|
|||
|
break;
|
|||
|
|
|||
|
case CTF_CENTER:
|
|||
|
captext->Xpos = (160 - (width / 2));
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
/* Display the text and record its bounding box. */
|
|||
|
if (captext->CPF == 0) {
|
|||
|
i = Text_Print(captext->Text, captext->Xpos, captext->Ypos,
|
|||
|
captext->FgPen, captext->BgPen);
|
|||
|
|
|||
|
node = AddCaptionNode(&cap->List, captext);
|
|||
|
node->BoundW = width;
|
|||
|
node->BoundH = (cap->FontHeight * i);
|
|||
|
} else {
|
|||
|
node = AddCaptionNode(&cap->List, captext);
|
|||
|
node->BoundW = width;
|
|||
|
node->BoundH = cap->FontHeight;
|
|||
|
}
|
|||
|
|
|||
|
/* Next */
|
|||
|
cap->Next = (CaptionText *)(((char *)captext) + captext->Size);
|
|||
|
captext = cap->Next;
|
|||
|
}
|
|||
|
|
|||
|
Set_Font(oldfont);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/****************************************************************************
|
|||
|
*
|
|||
|
* NAME
|
|||
|
* AddCaptionNode - Add a caption to the processing list.
|
|||
|
*
|
|||
|
* SYNOPSIS
|
|||
|
* Node = AddCaptionNode(List, Caption)
|
|||
|
*
|
|||
|
* CaptionNode *AddCaptionNode(CaptionList *, CaptionText *);
|
|||
|
*
|
|||
|
* FUNCTION
|
|||
|
* Add a caption the caption processing list.
|
|||
|
*
|
|||
|
* INPUTS
|
|||
|
* List - Caption processing list.
|
|||
|
* Caption - Caption to add to the list.
|
|||
|
*
|
|||
|
* RESULT
|
|||
|
* Node - Pointer to node, otherwise NULL if error.
|
|||
|
*
|
|||
|
****************************************************************************/
|
|||
|
|
|||
|
static CaptionNode *AddCaptionNode(CaptionList *list, CaptionText *captext)
|
|||
|
{
|
|||
|
CaptionNode *node = NULL;
|
|||
|
|
|||
|
/* If this list is not full. */
|
|||
|
node = list->TailPred;
|
|||
|
|
|||
|
if (!(node->Flags & CNF_USED)) {
|
|||
|
|
|||
|
/* Remove the node from the tail. */
|
|||
|
node->Pred->Succ = node->Succ;
|
|||
|
list->TailPred = node->Pred;
|
|||
|
|
|||
|
node->Flags |= CNF_USED;
|
|||
|
node->Captext = captext;
|
|||
|
node->Char = captext->Text;
|
|||
|
node->CurX = captext->Xpos;
|
|||
|
node->CurY = captext->Ypos;
|
|||
|
|
|||
|
/* Add the node to the head. */
|
|||
|
node->Succ = list->Head;
|
|||
|
list->Head = node;
|
|||
|
node->Pred = (CaptionNode *)&list->Head;
|
|||
|
node->Succ->Pred = node;
|
|||
|
}
|
|||
|
|
|||
|
return (node);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/****************************************************************************
|
|||
|
*
|
|||
|
* NAME
|
|||
|
* RemCaptionNode - Remove a caption from the processing list.
|
|||
|
*
|
|||
|
* SYNOPSIS
|
|||
|
* RemCaptionNode(List, Node)
|
|||
|
*
|
|||
|
* void RemCaptionNode(CaptionList *, CaptionNode *);
|
|||
|
*
|
|||
|
* FUNCTION
|
|||
|
* Remove the caption from the processing list. Mark the node as unused
|
|||
|
* and put it at the tail of the list.
|
|||
|
*
|
|||
|
* INPUTS
|
|||
|
* List - Caption processing list.
|
|||
|
* Node - Caption node to remove.
|
|||
|
*
|
|||
|
* RESULT
|
|||
|
* NONE
|
|||
|
*
|
|||
|
****************************************************************************/
|
|||
|
|
|||
|
static void RemCaptionNode(CaptionList *list, CaptionNode *node)
|
|||
|
{
|
|||
|
/* If the nodes successor is null then we are at the tail. */
|
|||
|
if (node->Succ != NULL) {
|
|||
|
|
|||
|
/* Mark the node as unused. */
|
|||
|
node->Flags = 0;
|
|||
|
|
|||
|
/* Relink the node to the tail. */
|
|||
|
node->Succ->Pred = node->Pred;
|
|||
|
node->Pred->Succ = node->Succ;
|
|||
|
node->Succ = (CaptionNode *)&list->Tail;
|
|||
|
node->Pred = list->TailPred;
|
|||
|
list->TailPred->Succ = node;
|
|||
|
list->TailPred = node;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#endif // VQACAPTIONS_ON
|
|||
|
|