/* ** 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 . */ /**************************************************************************** * * 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 #include #include #include #include #include #include "caption.h" /*--------------------------------------------------------------------------- * 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; } }