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
|
||
|