THEME.CPP (33553B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 16 /* $Header: F:\projects\c&c\vcs\code\theme.cpv 2.18 16 Oct 1995 16:51:10 JOE_BOSTIC $ */ 17 /*********************************************************************************************** 18 *** 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 *** 19 *********************************************************************************************** 20 * * 21 * Project Name : Command & Conquer * 22 * * 23 * File Name : THEME.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : August 14, 1994 * 28 * * 29 * Last Update : May 29, 1995 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * ThemeClass::Scan -- Scans all scores for availability. * 34 * ThemeClass::AI -- Process the theme engine and restart songs. * 35 * ThemeClass::Base_Name -- Fetches the base filename for the theme specified. * 36 * ThemeClass::From_Name -- Determines theme number from specified name. * 37 * ThemeClass::Full_Name -- Retrieves the full score name. * 38 * ThemeClass::Is_Allowed -- Checks to see if the specified theme is legal. * 39 * ThemeClass::Next_Song -- Calculates the next song number to play. * 40 * ThemeClass::Play_Song -- Starts the specified song play NOW. * 41 * ThemeClass::Queue_Song -- Queues the song to the play queue. * 42 * ThemeClass::Still_Playing -- Determines if music is still playing. * 43 * ThemeClass::Stop -- Stops the current theme from playing. * 44 * ThemeClass::ThemeClass -- Default constructor for the theme manager class. * 45 * ThemeClass::Theme_File_Name -- Constructs a filename for the specified theme. * 46 * ThemeClass::Track_Length -- Caclulates the length of the song (in seconds). * 47 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 48 49 #include "function.h" 50 51 #include "theme.h" 52 53 54 /* 55 ** These are the actual filename list for the theme sample files. 56 */ 57 ThemeClass::ThemeControl ThemeClass::_themes[THEME_COUNT] = { 58 {"AIRSTRIK", TXT_THEME_AIRSTRIKE, 0, 200, false, false,false,true}, 59 {"80MX226M", TXT_THEME_80MX, 0, 248, false, false,false,true}, 60 {"CHRG226M", TXT_THEME_CHRG, 0, 256, true, false,false,true}, 61 {"CREP226M", TXT_THEME_CREP, 0, 222, true, false,false,true}, 62 {"DRIL226M", TXT_THEME_DRIL, 0, 272, true, false,false,true}, 63 {"DRON226M", TXT_THEME_DRON, 0, 275, true, false,false,true}, 64 {"FIST226M", TXT_THEME_FIST, 0, 212, true, false,false,true}, 65 {"RECN226M", TXT_THEME_RECON, 0, 261, true, false,false,true}, 66 {"VOIC226M", TXT_THEME_VOICE, 0, 306, true, false,false,true}, 67 {"HEAVYG", TXT_THEME_HEAVYG, 0, 180, true, false,false,true}, 68 {"J1", TXT_THEME_J1, 4, 187, true, false,false,true}, 69 // {"J1", TXT_THEME_J1, 4, 187, false, false,false,true}, 70 {"JDI_V2", TXT_THEME_JDI_V2, 5, 183, true, false,false,true}, 71 {"RADIO", TXT_THEME_RADIO, 6, 183, true, false,false,true}, 72 {"RAIN", TXT_THEME_RAIN, 7, 156, true, false,false,true}, 73 {"AOI", TXT_THEME_AOI, 0, 168, true, true, false,true}, 74 {"CCTHANG", TXT_THEME_CCTHANG, 12, 193, true, false,false,true}, 75 {"DIE", TXT_THEME_DIE, 11, 162, false, false,false,true}, 76 {"FWP", TXT_THEME_FWP, 10, 53, true, false,false,true}, 77 {"IND", TXT_THEME_IND, 1, 175, true, false,false,true}, 78 {"IND2", TXT_THEME_IND2, 1, 38, true, false,false,true}, 79 {"JUSTDOIT", TXT_THEME_JUSTDOIT, 9, 142, true, false,false,true}, 80 {"LINEFIRE", TXT_THEME_LINEFIRE, 8, 125, true, false,false,true}, 81 {"MARCH", TXT_THEME_MARCH, 7, 157, true, false,false,true}, 82 {"TARGET", TXT_THEME_TARGET, 0, 173, true, false,false,true}, 83 {"NOMERCY", TXT_THEME_NOMERCY, 2, 204, true, false,false,true}, 84 {"OTP", TXT_THEME_OTP, 3, 182, true, false,false,true}, 85 {"PRP", TXT_THEME_PRP, 4, 211, true, false,false,true}, 86 {"ROUT", TXT_THEME_ROUT, 12, 121, false, true, false,true}, 87 {"HEART", TXT_THEME_HEART, 5, 206, false, true, false,true}, 88 {"STOPTHEM", TXT_THEME_STOPTHEM, 0, 190, true, false,false,true}, 89 {"TROUBLE", TXT_THEME_TROUBLE, 6, 191, true, true, false,true}, 90 {"WARFARE", TXT_THEME_WARFARE, 0, 182, true, false,false,true}, 91 {"BEFEARED", TXT_THEME_BEFEARED, 13, 164, false, true, false,true}, 92 {"I_AM", TXT_THEME_IAM, 6, 161, false, false,false,true}, 93 {"WIN1", TXT_THEME_WIN1, 0, 41, false, true, true,true}, 94 {"MAP1", TXT_THEME_WIN1, 0, 61, false, false,true,true}, 95 {"VALKYRIE", TXT_THEME_VALK, 0, 306, false, false,true,true}, 96 }; 97 98 99 /*********************************************************************************************** 100 * ThemeClass::Base_Name -- Fetches the base filename for the theme specified. * 101 * * 102 * This routine is used to retrieve a pointer to the base filename for the theme * 103 * specified. * 104 * * 105 * INPUT: theme -- The theme number to convert into a base filename. * 106 * * 107 * OUTPUT: Returns with a pointer to the base filename for the theme specified. If the * 108 * theme number is invalid, then a pointer to "No Theme" is returned instead. * 109 * * 110 * WARNINGS: none * 111 * * 112 * HISTORY: * 113 * 05/29/1995 JLB : Created. * 114 *=============================================================================================*/ 115 char const * ThemeClass::Base_Name(ThemeType theme) const 116 { 117 if (theme != THEME_NONE) { 118 return(_themes[theme].Name); 119 } 120 return("No theme"); 121 } 122 123 124 /*********************************************************************************************** 125 * ThemeClass::ThemeClass -- Default constructor for the theme manager class. * 126 * * 127 * This is the default constructor for the theme class object. * 128 * * 129 * INPUT: none * 130 * * 131 * OUTPUT: none * 132 * * 133 * WARNINGS: none * 134 * * 135 * HISTORY: * 136 * 01/16/1995 JLB : Created. * 137 *=============================================================================================*/ 138 ThemeClass::ThemeClass(void) 139 { 140 Current = -1; 141 Score = THEME_NONE; 142 Pending = THEME_NONE; 143 } 144 145 146 /*********************************************************************************************** 147 * ThemeClass::Full_Name -- Retrieves the full score name. * 148 * * 149 * This routine will fetch and return with a pointer to the full name of the theme * 150 * specified. * 151 * * 152 * INPUT: theme -- The theme to fetch the full name for. * 153 * * 154 * OUTPUT: Returns with a pointer to the full name for this score. This pointer may point to * 155 * EMS memory. * 156 * * 157 * WARNINGS: none * 158 * * 159 * HISTORY: * 160 * 01/16/1995 JLB : Created. * 161 *=============================================================================================*/ 162 char const * ThemeClass::Full_Name(ThemeType theme) const 163 { 164 if (theme != THEME_NONE) { 165 return(Text_String(_themes[theme].Fullname)); 166 } 167 return(NULL); 168 } 169 170 171 /*********************************************************************************************** 172 * ThemeClass::AI -- Process the theme engine and restart songs. * 173 * * 174 * This is a maintenance function that will restart an appropriate theme if the current one * 175 * has finished. This routine should be called frequently. * 176 * * 177 * INPUT: none * 178 * * 179 * OUTPUT: none * 180 * * 181 * WARNINGS: none * 182 * * 183 * HISTORY: * 184 * 09/08/1994 JLB : Created. * 185 * 01/23/1995 JLB : Picks new song just as it is about to play it. * 186 *=============================================================================================*/ 187 void ThemeClass::AI(void) 188 { 189 if (SampleType && !Debug_Quiet) { 190 if (ScoresPresent && Options.ScoreVolume && !Still_Playing() && Pending != THEME_NONE) { 191 192 /* 193 ** If the pending song needs to be picked, then pick it now. 194 */ 195 if (Pending == THEME_PICK_ANOTHER) { 196 Pending = Next_Song(Score); 197 } 198 199 /* 200 ** Start the song playing and then flag it so that a new song will 201 ** be picked when this one ends. 202 */ 203 Play_Song(Pending); 204 Pending = THEME_PICK_ANOTHER; 205 } 206 Sound_Callback(); 207 } 208 } 209 210 211 /*********************************************************************************************** 212 * ThemeClass::Next_Song -- Calculates the next song number to play. * 213 * * 214 * use this routine to figure out what song number to play. It examines the option settings * 215 * for repeat and shuffle so that it can return the correct value. * 216 * * 217 * INPUT: theme -- The origin (last) index. The new value is related to this for all but * 218 * the shuffling method of play. * 219 * * 220 * OUTPUT: Returns with the song number for the next song to play. * 221 * * 222 * WARNINGS: none * 223 * * 224 * HISTORY: * 225 * 01/16/1995 JLB : Created. * 226 * 01/19/1995 JLB : Will not play the same song twice when in shuffle mode. * 227 *=============================================================================================*/ 228 ThemeType ThemeClass::Next_Song(ThemeType theme) 229 { 230 if (theme == THEME_NONE) { 231 theme = Next_Song(THEME_PICK_ANOTHER); 232 } else { 233 if (theme == THEME_PICK_ANOTHER || (!_themes[theme].Repeat && !Options.IsScoreRepeat)) { 234 if (Options.IsScoreShuffle) { 235 236 /* 237 ** Shuffle the theme, but never pick the same theme that was just 238 ** playing. 239 */ 240 ThemeType newtheme; 241 do { 242 newtheme = Sim_Random_Pick(THEME_FIRST, THEME_LAST); 243 } while(newtheme == theme || !Is_Allowed(newtheme)); 244 theme = newtheme; 245 246 } else { 247 248 /* 249 ** Sequential score playing. 250 */ 251 do { 252 theme++; 253 if (theme > THEME_LAST) { 254 theme = THEME_FIRST; 255 } 256 } while(!Is_Allowed(theme)); 257 } 258 } 259 } 260 return(theme); 261 } 262 263 264 /*********************************************************************************************** 265 * ThemeClass::Queue_Song -- Queues the song to the play queue. * 266 * * 267 * This routine will cause the current song to fade and the specified song to start. This * 268 * is the normal and friendly method of changing the current song. * 269 * * 270 * INPUT: theme -- The song to start playing. If -1 is pssed in, then just the current song * 271 * is faded. * 272 * * 273 * OUTPUT: none * 274 * * 275 * WARNINGS: none * 276 * * 277 * HISTORY: * 278 * 01/16/1995 JLB : Created. * 279 *=============================================================================================*/ 280 void ThemeClass::Queue_Song(ThemeType theme) 281 { 282 if (ScoresPresent && SampleType && !Debug_Quiet && (Pending == THEME_NONE || Pending == THEME_PICK_ANOTHER)) { 283 if (!Options.ScoreVolume && theme != THEME_NONE) return; 284 285 Pending = theme; 286 Fade_Sample(Current, THEME_DELAY); 287 } 288 } 289 290 291 /*********************************************************************************************** 292 * ThemeClass::Play_Song -- Starts the specified song play NOW. * 293 * * 294 * This routine is used to start the specified theme playing right now. If there is already * 295 * a theme playing, it is cut short so that this one may start. * 296 * * 297 * INPUT: theme -- The theme number to start playing. * 298 * * 299 * OUTPUT: Returns with the sample play handle. * 300 * * 301 * WARNINGS: This cuts off any current song in a abrubt manner. Only use this routine when * 302 * necessary. * 303 * * 304 * HISTORY: * 305 * 01/16/1995 JLB : Created. * 306 *=============================================================================================*/ 307 int ThemeClass::Play_Song(ThemeType theme) 308 { 309 if (ScoresPresent && SampleType && !Debug_Quiet && Options.ScoreVolume) { 310 Stop(); 311 Score = theme; 312 if (theme >= THEME_FIRST) { 313 314 #ifdef DEMO 315 if (_themes[theme].Scenario != 99) { 316 CCFileClass file(Theme_File_Name(theme)); 317 if (file.Is_Available()) { 318 Current = File_Stream_Sample_Vol(Theme_File_Name(theme), 0xFF, true); 319 } else { 320 Current = -1; 321 } 322 } else { 323 Current = -1; 324 } 325 #else 326 Current = File_Stream_Sample_Vol(Theme_File_Name(theme), 0xFF, true); 327 #endif 328 } 329 } 330 return(Current); 331 } 332 333 334 /*********************************************************************************************** 335 * ThemeClass::Theme_File_Name -- Constructs a filename for the specified theme. * 336 * * 337 * This routine will construct (into a static buffer) a filename that matches the theme * 338 * number specified. This constructed filename is returned as a pointer. The filename will * 339 * remain valid until the next call to this routine. * 340 * * 341 * INPUT: theme -- The theme number to convert to a filename. * 342 * * 343 * OUTPUT: Returns with a pointer to the constructed filename for the specified theme number. * 344 * * 345 * WARNINGS: none * 346 * * 347 * HISTORY: * 348 * 01/16/1995 JLB : Created. * 349 * 05/09/1995 JLB : Theme variation support. * 350 *=============================================================================================*/ 351 char const * ThemeClass::Theme_File_Name(ThemeType theme) 352 { 353 static char name[_MAX_FNAME+_MAX_EXT]; 354 355 if (_themes[theme].Variation && Special.IsVariation) { 356 _makepath(name, NULL, NULL, _themes[theme].Name, ".VAR"); 357 CCFileClass file(name); 358 if (file.Is_Available()) { 359 return((char const *)(&name[0])); 360 } 361 } 362 _makepath(name, NULL, NULL, _themes[theme].Name, ".AUD"); 363 return((char const *)(&name[0])); 364 } 365 366 367 /*********************************************************************************************** 368 * ThemeClass::Track_Length -- Caclulates the length of the song (in seconds). * 369 * * 370 * Use this routine to calculate the length of the song. The length is determined by * 371 * reading the header of the song and dividing the sample rate into the sample length. * 372 * * 373 * INPUT: theme -- The song number to examine to find its length. * 374 * * 375 * OUTPUT: Returns with the length of the specified theme. This length is in the form of * 376 * seconds. * 377 * * 378 * WARNINGS: This routine goes to disk to fetch this information. Don't call frivolously. * 379 * * 380 * HISTORY: * 381 * 01/16/1995 JLB : Created. * 382 *=============================================================================================*/ 383 int ThemeClass::Track_Length(ThemeType theme) 384 { 385 if ((unsigned)theme < THEME_COUNT) { 386 return(_themes[theme].Duration); 387 } 388 return(0); 389 } 390 391 392 /*********************************************************************************************** 393 * ThemeClass::Stop -- Stops the current theme from playing. * 394 * * 395 * Use this routine to stop the current theme. After this routine is called, no more music * 396 * will play until the Start() function is called. * 397 * * 398 * INPUT: none * 399 * * 400 * OUTPUT: none * 401 * * 402 * WARNINGS: none * 403 * * 404 * HISTORY: * 405 * 09/08/1994 JLB : Created. * 406 *=============================================================================================*/ 407 void ThemeClass::Stop(void) 408 { 409 if (ScoresPresent && SampleType && !Debug_Quiet) { 410 if (Current != -1) { 411 Stop_Sample(Current); 412 Current = -1; 413 Score = THEME_NONE; 414 Pending = THEME_NONE; 415 } 416 } 417 } 418 419 420 /*********************************************************************************************** 421 * ThemeClass::Still_Playing -- Determines if music is still playing. * 422 * * 423 * Use this routine to determine if music is still playing. * 424 * * 425 * INPUT: none * 426 * * 427 * OUTPUT: bool; Is the music still audible? * 428 * * 429 * WARNINGS: none * 430 * * 431 * HISTORY: * 432 * 12/20/1994 JLB : Created. * 433 *=============================================================================================*/ 434 int ThemeClass::Still_Playing(void) 435 { 436 if (ScoresPresent && SampleType && Current != -1 && !Debug_Quiet) { 437 return(Sample_Status(Current)); 438 } 439 return(false); 440 } 441 442 443 /*********************************************************************************************** 444 * ThemeClass::Is_Allowed -- Checks to see if the specified theme is legal. * 445 * * 446 * Use this routine to determine if a theme is allowed to be played. A theme is not allowed * 447 * if the scenario is too early for that score, or the score only is allowed in special * 448 * cases. * 449 * * 450 * INPUT: index -- The score the check to see if it is allowed to play. * 451 * * 452 * OUTPUT: Is the specified score allowed to play in the normal score playlist? * 453 * * 454 * WARNINGS: none * 455 * * 456 * HISTORY: * 457 * 05/09/1995 JLB : Created. * 458 *=============================================================================================*/ 459 bool ThemeClass::Is_Allowed(ThemeType index) const 460 { 461 #ifdef DEMO 462 char buffer[128]; 463 464 sprintf(buffer, "%s.AUD", Base_Name(index)); 465 CCFileClass file(buffer); 466 if (_themes[index].Scenario == 99 || !file.Is_Available()) { 467 _themes[index].Scenario = 99; 468 return(false); 469 } 470 #endif 471 472 return( 473 _themes[index].Available && 474 (_themes[index].Normal || 475 // (index == THEME_MAP1 && ScenarioInit) || 476 ((Special.IsVariation && _themes[index].Variation && index != THEME_WIN1) && 477 #ifndef DEMO 478 (GameToPlay != GAME_NORMAL || _themes[index].Scenario <= (int)Scenario) && 479 #endif 480 (index != THEME_J1 || Special.IsJurassic)))); 481 } 482 483 484 /*********************************************************************************************** 485 * ThemeClass::From_Name -- Determines theme number from specified name. * 486 * * 487 * Use this routine to convert a name (either the base filename of the theme, or a partial * 488 * substring of the full name) into the matching ThemeType value. Typical use of this is * 489 * when parsing the INI file for theme control values. * 490 * * 491 * INPUT: name -- Pointer to base filename of theme or a partial substring of the full * 492 * theme name. * 493 * * 494 * OUTPUT: Returns with the matching theme number. If no match could be found, then * 495 * THEME_NONE is returned. * 496 * * 497 * WARNINGS: If a filename is specified the comparison is case insensitive. When scanning * 498 * the full theme name, the comparison is case sensitive. * 499 * * 500 * HISTORY: * 501 * 05/29/1995 JLB : Created. * 502 *=============================================================================================*/ 503 ThemeType ThemeClass::From_Name(char const * name) 504 { 505 if (name && strlen(name) > 0) { 506 /* 507 ** First search for an exact name match with the filename 508 ** of the theme. This is guaranteed to be unique. 509 */ 510 ThemeType theme; 511 for (theme = THEME_FIRST; theme < THEME_COUNT; theme++) { 512 if (stricmp(_themes[theme].Name, name) == 0) { 513 return(theme); 514 } 515 } 516 517 /* 518 ** If the filename scan failed to find a match, then scan for 519 ** a substring within the full name of the score. This might 520 ** yeild a match, but is not guaranteed to be unique. 521 */ 522 for (theme = THEME_FIRST; theme < THEME_COUNT; theme++) { 523 if (strstr(Text_String(_themes[theme].Fullname), name) != NULL) { 524 return(theme); 525 } 526 } 527 } 528 529 return(THEME_NONE); 530 } 531 532 533 /*********************************************************************************************** 534 * ThemeClass::Scan -- Scans all scores for availability. * 535 * * 536 * This routine should be called whenever a score mixfile is registered. It will scan * 537 * to see if any score is unavailable. If this is the case, then the score will be so * 538 * flagged in order not to appear on the play list. This condition is likely to occur * 539 * when expansion mission disks contain a different score mix than the release version. * 540 * * 541 * INPUT: none * 542 * * 543 * OUTPUT: none * 544 * * 545 * WARNINGS: none * 546 * * 547 * HISTORY: * 548 * 01/04/1996 JLB : Created. * 549 *=============================================================================================*/ 550 void ThemeClass::Scan(void) 551 { 552 for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { 553 // if (theme == THEME_J1 && !Special.IsJurassic) { 554 // _themes[theme].Available = false; 555 // } else { 556 _themes[theme].Available = CCFileClass(Theme_File_Name(theme)).Is_Available(); 557 // } 558 } 559 } 560