| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583 |
- /*
- ** Command & Conquer Renegade(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/>.
- */
- /****************************************************************************
- *
- * FILE
- * $Archive: /Commando/Code/BinkMovie/subtitleparser.cpp $
- *
- * DESCRIPTION
- * Subtitling control file parser
- *
- * PROGRAMMER
- * Denzil E. Long, Jr.
- *
- * VERSION INFO
- * $Author: Denzil_l $
- * $Modtime: 1/12/02 9:27p $
- * $Revision: 2 $
- *
- ****************************************************************************/
- #include "subtitleparser.h"
- #include "subtitle.h"
- #include "straw.h"
- #include "readline.h"
- #include "trim.h"
- #include <wchar.h>
- #include <stdlib.h>
- // Subtitle control file parsing tokens
- #define BEGINMOVIE_TOKEN L"BeginMovie"
- #define ENDMOVIE_TOKEN L"EndMovie"
- #define TIMEBIAS_TOKEN L"TimeBias"
- #define TIME_TOKEN L"Time"
- #define DURATION_TOKEN L"Duration"
- #define POSITION_TOKEN L"Position"
- #define COLOR_TOKEN L"Color"
- #define TEXT_TOKEN L"Text"
- unsigned long DecodeTimeString(wchar_t* string);
- void Parse_Time(wchar_t* string, SubTitleClass* subTitle);
- void Parse_Duration(wchar_t* string, SubTitleClass* subTitle);
- void Parse_Position(wchar_t* string, SubTitleClass* subTitle);
- void Parse_Color(wchar_t* string, SubTitleClass* subTitle);
- void Parse_Text(wchar_t* string, SubTitleClass* subTitle);
- SubTitleParserClass::TokenHook SubTitleParserClass::mTokenHooks[] =
- {
- {TIME_TOKEN, Parse_Time},
- {DURATION_TOKEN, Parse_Duration},
- {POSITION_TOKEN, Parse_Position},
- {COLOR_TOKEN, Parse_Color},
- {TEXT_TOKEN, Parse_Text},
- {NULL, NULL}
- };
- /******************************************************************************
- *
- * NAME
- * SubTitleParserClass::SubTitleParserClass
- *
- * DESCRIPTION
- *
- * INPUTS
- * Input - Control file input stream.
- *
- * RESULTS
- * NONE
- *
- ******************************************************************************/
- SubTitleParserClass::SubTitleParserClass(Straw& input)
- :
- mInput(input),
- mLineNumber(0)
- {
- // Check for Unicode byte-order mark.
- // All Unicode plaintext files are prefixed with the byte-order mark U+FEFF
- // or its mirror U+FFFE. This mark is used to indicate the byte order of a
- // text stream.
- wchar_t byteOrderMark = 0;
- mInput.Get(&byteOrderMark, sizeof(wchar_t));
- WWASSERT(byteOrderMark == 0xFEFF);
- if (byteOrderMark != 0xFEFF) {
- WWDEBUG_SAY(("Error: Subtitle control file is not unicode!\n"));
- }
- }
- /******************************************************************************
- *
- * NAME
- * SubTitleParserClass::~SubTitleParserClass
- *
- * DESCRIPTION
- *
- * INPUTS
- * NONE
- *
- * RESULTS
- * NONE
- *
- ******************************************************************************/
- SubTitleParserClass::~SubTitleParserClass()
- {
- }
- /******************************************************************************
- *
- * NAME
- * SubTitleParserClass::GetSubTitles
- *
- * DESCRIPTION
- *
- * INPUTS
- * NONE
- *
- * RESULTS
- *
- ******************************************************************************/
- DynamicVectorClass<SubTitleClass*>* SubTitleParserClass::Get_Sub_Titles(const char* moviename)
- {
- DynamicVectorClass<SubTitleClass*>* subTitleCollection = NULL;
-
- // Find the movie marker
- if (Find_Movie_Entry(moviename) == true) {
- // Allocate container to hold subtitles
- subTitleCollection = new DynamicVectorClass<SubTitleClass*>;
- WWASSERT(subTitleCollection != NULL);
- if (subTitleCollection != NULL) {
- for (;;) {
- // Retrieve a line from the control file
- wchar_t* string = Get_Next_Line();
- if ((string != NULL) && (wcslen(string) > 0)) {
- // Check for subtitle entry markers
- if ((string[0] == L'<') && (string[wcslen(string) - 1] == L'>')) {
- // Trim off markers
- string++;
- string[wcslen(string) - 1] = 0;
- wcstrim(string);
- // Ignore empty caption
- if (wcslen(string) == 0) {
- continue;
- }
- // Create a new SubTitleClass
- SubTitleClass* subTitle = new SubTitleClass();
- WWASSERT(subTitle != NULL);
- if (subTitle == NULL) {
- WWDEBUG_SAY(("***** Failed to create SubTitleClass!\n"));
- break;
- }
- if (Parse_Sub_Title(string, subTitle) == true) {
- subTitleCollection->Add(subTitle);
- }
- else {
- delete subTitle;
- }
- continue;
- }
- // Terminate if end movie token encountered.
- if (wcsnicmp(string, ENDMOVIE_TOKEN, wcslen(ENDMOVIE_TOKEN)) == 0) {
- break;
- }
- }
- }
- if (subTitleCollection->Count() == 0) {
- delete subTitleCollection;
- subTitleCollection = NULL;
- }
- }
- }
- return subTitleCollection;
- }
- /******************************************************************************
- *
- * NAME
- * SubTitleParserClass::FindMovieEntry
- *
- * DESCRIPTION
- * No description provided,
- *
- * INPUTS
- * Moviename - Pointer to name of movie to find subtitles for.
- *
- * RESULTS
- * Success - True if movie entry found; False if unable to find movie entry.
- *
- ******************************************************************************/
- bool SubTitleParserClass::Find_Movie_Entry(const char* moviename)
- {
- // Convert the moviename into Unicode
- WWASSERT(moviename != NULL);
- wchar_t wideName[32];
- mbstowcs(wideName, moviename, 32);
- do {
- // Retrieve line of text
- wchar_t* string = Get_Next_Line();
- // Terminate if no string read.
- if (string == NULL) {
- break;
- }
- // Look for begin movie token
- if (wcsnicmp(string, BEGINMOVIE_TOKEN, wcslen(BEGINMOVIE_TOKEN)) == 0) {
- // Get moviename following the token
- wchar_t* ptr = wcschr(string, L' ');
- // Check for matching moviename
- if (ptr != NULL) {
- wcstrim(ptr);
- if (wcsicmp(ptr, wideName) == 0) {
- WWDEBUG_SAY(("Found movie entry %s\n", moviename));
- return true;
- }
- }
- }
- } while (true);
- return false;
- }
- /******************************************************************************
- *
- * NAME
- * SubTitleParserClass::ParseSubTitle
- *
- * DESCRIPTION
- *
- * INPUTS
- * wchar_t* string
- * SubTitleClass* subTitle
- *
- * RESULTS
- * bool
- *
- ******************************************************************************/
- bool SubTitleParserClass::Parse_Sub_Title(wchar_t* string, SubTitleClass* subTitle)
- {
- // Parameter check
- WWASSERT(string != NULL);
- WWASSERT(subTitle != NULL);
- for (;;) {
- // Find token separator
- wchar_t* separator = wcschr(string, L'=');
- if (separator == NULL) {
- WWDEBUG_SAY(("Error on line %d: syntax error\n", Get_Line_Number()));
- return false;
- }
- // NULL terminate token part
- *separator++ = 0;
- // Tokens are to the left of the separator
- wchar_t* token = string;
- wcstrim(token);
- // Parameters are to the right of the separator
- wchar_t* param = separator;
- wcstrim(param);
- // Quoted parameters are treated as literals (ignore contents)
- if (param[0] == L'"') {
- // Skip leading quote
- param++;
- // Use next quote to mark end of parameter
- separator = wcschr(param, L'"');
- if (separator == NULL) {
- WWDEBUG_SAY(("Error on line %d: mismatched quotes\n", Get_Line_Number()));
- return false;
- }
- // NULL terminate parameter
- *separator++ = 0;
- // Skip any comma following a literal string since we used the trailing
- // quote to terminate the tokens parameters
- wcstrim(separator);
-
- if (*separator == L',') {
- separator++;
- }
- // Advance string past quoted parameter
- string = separator;
- }
- else {
- // Look for separator to next token
- separator = wcspbrk(param, L", ");
- if (separator != NULL) {
- *separator++ = 0;
- string = separator;
- }
- else {
- string = L"";
- }
- }
- // Error on empty tokens
- if (wcslen(token) == 0) {
- WWDEBUG_SAY(("Error on line %d: missing token\n", Get_Line_Number()));
- return false;
- }
- // Parse current token
- Parse_Token(token, param, subTitle);
- // Prepare for next token
- wcstrim(string);
- if (wcslen(string) == 0) {
- break;
- }
- }
- return true;
- }
- /******************************************************************************
- *
- * NAME
- * SubTitleParserClass::ParseToken
- *
- * DESCRIPTION
- *
- * INPUTS
- * wchar_t* token
- * wchar_t* param
- * SubTitleClass* subTitle
- *
- * RESULTS
- * NONE
- *
- ******************************************************************************/
- void SubTitleParserClass::Parse_Token(wchar_t* token, wchar_t* param, SubTitleClass* subTitle)
- {
- // Parameter check
- WWASSERT(token != NULL);
- WWASSERT(subTitle != NULL);
- if (token != NULL) {
- int index = 0;
- while (mTokenHooks[index].Token != NULL) {
- TokenHook& hook = mTokenHooks[index];
- if (wcsicmp(hook.Token, token) == 0) {
- WWASSERT(subTitle != NULL);
- hook.Handler(param, subTitle);
- return;
- }
- index++;
- }
- }
- }
- /******************************************************************************
- *
- * NAME
- * SubTitleParserClass::GetNextLine
- *
- * DESCRIPTION
- * Retrieve the next line of text from the control file.
- *
- * INPUTS
- * NONE
- *
- * RESULTS
- * String - Pointer to next line of text. NULL if error or EOF.
- *
- ******************************************************************************/
- wchar_t* SubTitleParserClass::Get_Next_Line(void)
- {
- bool eof = false;
- while (eof == false) {
- // Read in a line of text
- Read_Line(mInput, mBuffer, LINE_MAX, eof);
- mLineNumber++;
- // Remove whitespace
- wchar_t* string = wcstrim(mBuffer);
- // Skip comments and blank lines
- if ((wcslen(string) > 0) && (string[0] != L';')) {
- return string;
- }
- }
- return NULL;
- }
- // Convert a time string in the format hh:mm:ss:tt into 1/60 second ticks.
- unsigned long Decode_Time_String(wchar_t* string)
- {
- #define TICKS_PER_SECOND 60
- #define TICKS_PER_MINUTE (60 * TICKS_PER_SECOND)
- #define TICKS_PER_HOUR (60 * TICKS_PER_MINUTE)
- WWASSERT(string != NULL);
- wchar_t buffer[12];
- wcsncpy(buffer, string, 12);
- buffer[11] = 0;
- wchar_t* ptr = &buffer[0];
- // Isolate hours part
- wchar_t* separator = wcschr(ptr, L':');
- WWASSERT(separator != NULL);
- *separator++ = 0;
- unsigned long hours = wcstoul(ptr, NULL, 10);
- // Isolate minutes part
- ptr = separator;
- separator = wcschr(ptr, L':');
- WWASSERT(separator != NULL);
- *separator++ = 0;
- unsigned long minutes = wcstoul(ptr, NULL, 10);
- // Isolate seconds part
- ptr = separator;
- separator = wcschr(ptr, L':');
- WWASSERT(separator != NULL);
- *separator++ = 0;
- unsigned long seconds = wcstoul(ptr, NULL, 10);
- // Isolate hundredth part (1/100th of a second)
- ptr = separator;
- unsigned long hundredth = wcstoul(ptr, NULL, 10);
- unsigned long time = (hours * TICKS_PER_HOUR);
- time += (minutes * TICKS_PER_MINUTE);
- time += (seconds * TICKS_PER_SECOND);
- time += ((hundredth * TICKS_PER_SECOND) / 100);
- return time;
- }
- void Parse_Time(wchar_t* param, SubTitleClass* subTitle)
- {
- WWASSERT(param != NULL);
- WWASSERT(subTitle != NULL);
- unsigned long time = Decode_Time_String(param);
- subTitle->Set_Display_Time(time);
- }
- void Parse_Duration(wchar_t* param, SubTitleClass* subTitle)
- {
- WWASSERT(param != NULL);
- WWASSERT(subTitle != NULL);
- unsigned long time = Decode_Time_String(param);
- if (time > 0) {
- subTitle->Set_Display_Duration(time);
- }
- }
- void Parse_Position(wchar_t* param, SubTitleClass* subTitle)
- {
- static struct
- {
- const wchar_t* Name;
- SubTitleClass::Alignment Align;
- } _alignLookup[] = {
- {L"Left", SubTitleClass::Left},
- {L"Right", SubTitleClass::Right},
- {L"Center", SubTitleClass::Center},
- {NULL, SubTitleClass::Center}
- };
- WWASSERT(subTitle != NULL);
- WWASSERT(param != NULL);
- wchar_t* ptr = param;
- // Line position
- wchar_t* separator = wcschr(ptr, L':');
- if (separator != NULL) {
- *separator++ = 0;
- int linePos = wcstol(ptr, NULL, 0);
- subTitle->Set_Line_Position(linePos);
- ptr = separator;
- }
- // Justification
- SubTitleClass::Alignment align = SubTitleClass::Center;
- int index = 0;
- while (_alignLookup[index].Name != NULL) {
- if (wcsicmp(ptr, _alignLookup[index].Name) == 0) {
- align = _alignLookup[index].Align;
- break;
- }
- index++;
- }
- subTitle->Set_Alignment(align);
- }
- void Parse_Color(wchar_t* param, SubTitleClass* subTitle)
- {
- WWASSERT(param != NULL);
- WWASSERT(subTitle != NULL);
- wchar_t* ptr = param;
- wchar_t* separator = wcschr(ptr, L':');
- *separator++ = 0;
- unsigned char red = (unsigned char)wcstoul(ptr, NULL, 10);
-
- ptr = separator;
- separator = wcschr(ptr, L':');
- *separator++ = 0;
- unsigned char green = (unsigned char)wcstoul(ptr, NULL, 10);
- ptr = separator;
- unsigned char blue = (unsigned char)wcstoul(ptr, NULL, 10);
- subTitle->Set_RGB_Color(red, green, blue);
- }
- void Parse_Text(wchar_t* param, SubTitleClass* subTitle)
- {
- WWASSERT(param != NULL);
- WWASSERT(subTitle != NULL);
- subTitle->Set_Caption(param);
- }
|