animatedsoundmgr.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** 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 ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : ww3d2 *
  23. * *
  24. * $Archive:: /Commando/Code/ww3d2/animatedsoundmgr.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 12/13/01 6:05p $*
  29. * *
  30. * $Revision:: 2 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. //
  36. // MBL Update for CNC3 INCURSION - 10.23.2002 - Expanded param handling, Added STOP command
  37. //
  38. #include <string.h> // stricmp()
  39. #include "animatedsoundmgr.h"
  40. #include "ini.h"
  41. #include "inisup.h"
  42. #include "ffactory.h"
  43. #include "wwfile.h"
  44. #include <stdio.h>
  45. #include "definition.h"
  46. #include "definitionmgr.h"
  47. #include "definitionclassids.h"
  48. #include "wwaudio.h"
  49. #include "audiblesound.h"
  50. #include "htree.h"
  51. #include "hanim.h"
  52. #include "soundlibrarybridge.h"
  53. #include "WWDebug.h"
  54. //////////////////////////////////////////////////////////////////////
  55. // Static member initialization
  56. //////////////////////////////////////////////////////////////////////
  57. HashTemplateClass<StringClass, AnimatedSoundMgrClass::ANIM_SOUND_LIST *> AnimatedSoundMgrClass::AnimationNameHash;
  58. DynamicVectorClass<AnimatedSoundMgrClass::ANIM_SOUND_LIST *> AnimatedSoundMgrClass::AnimSoundLists;
  59. SoundLibraryBridgeClass* AnimatedSoundMgrClass::SoundLibrary = NULL;
  60. //////////////////////////////////////////////////////////////////////
  61. // Local inlines
  62. //////////////////////////////////////////////////////////////////////
  63. static WWINLINE INIClass *
  64. Get_INI (const char *filename)
  65. {
  66. INIClass *ini = NULL;
  67. //
  68. // Get the file from our filefactory
  69. //
  70. FileClass *file = _TheFileFactory->Get_File (filename);
  71. if (file) {
  72. //
  73. // Create the INI object
  74. //
  75. if (file->Is_Available ()) {
  76. ini = new INIClass (*file);
  77. }
  78. //
  79. // Close the file
  80. //
  81. _TheFileFactory->Return_File (file);
  82. }
  83. return ini;
  84. }
  85. static int
  86. Build_List_From_String
  87. (
  88. const char * buffer,
  89. const char * delimiter,
  90. StringClass ** string_list
  91. )
  92. {
  93. int count = 0;
  94. WWASSERT (buffer != NULL);
  95. WWASSERT (delimiter != NULL);
  96. WWASSERT (string_list != NULL);
  97. if ((buffer != NULL) &&
  98. (delimiter != NULL) &&
  99. (string_list != NULL))
  100. {
  101. int delim_len = ::strlen (delimiter);
  102. //
  103. // Determine how many entries there will be in the list
  104. //
  105. for (const char *entry = buffer;
  106. (entry != NULL) && (entry[1] != 0);
  107. entry = ::strstr (entry, delimiter))
  108. {
  109. //
  110. // Move past the current delimiter (if necessary)
  111. //
  112. if ((::strnicmp (entry, delimiter, delim_len) == 0) && (count > 0)) {
  113. entry += delim_len;
  114. }
  115. // Increment the count of entries
  116. count ++;
  117. }
  118. if (count > 0) {
  119. //
  120. // Allocate enough StringClass objects to hold all the strings in the list
  121. //
  122. (*string_list) = new StringClass[count];
  123. //
  124. // Parse the string and pull out its entries.
  125. //
  126. count = 0;
  127. for (entry = buffer;
  128. (entry != NULL) && (entry[1] != 0);
  129. entry = ::strstr (entry, delimiter))
  130. {
  131. //
  132. // Move past the current delimiter (if necessary)
  133. //
  134. if ((::strnicmp (entry, delimiter, delim_len) == 0) && (count > 0)) {
  135. entry += delim_len;
  136. }
  137. //
  138. // Copy this entry into its own string
  139. //
  140. StringClass entry_string = entry;
  141. char *delim_start = ::strstr (entry_string, delimiter);
  142. if (delim_start != NULL) {
  143. delim_start[0] = 0;
  144. }
  145. //
  146. // Add this entry to our list
  147. //
  148. if ((entry_string.Get_Length () > 0) || (count == 0)) {
  149. (*string_list)[count++] = entry_string;
  150. }
  151. }
  152. } else if (delim_len > 0) {
  153. count = 1;
  154. (*string_list) = new StringClass[count];
  155. (*string_list)[0] = buffer;
  156. }
  157. }
  158. //
  159. // Return the number of entries in our list
  160. //
  161. return count;
  162. }
  163. static bool
  164. Is_In_Param_List
  165. (
  166. StringClass *param_list,
  167. int param_count,
  168. const char *param_to_check
  169. )
  170. {
  171. //
  172. // Check incoming parameters
  173. //
  174. WWASSERT( param_list != NULL );
  175. if ( param_list == NULL )
  176. {
  177. return( false );
  178. }
  179. WWASSERT( param_count >= 2 );
  180. if ( param_count < 2 )
  181. {
  182. return( false );
  183. }
  184. WWASSERT( param_to_check != NULL );
  185. if ( param_to_check == NULL )
  186. {
  187. return( false );
  188. }
  189. //
  190. // Note: params 0 & 1 are fixed to frame and name...
  191. //
  192. for ( int param_index = 2; param_index < param_count; param_index ++ )
  193. {
  194. {
  195. StringClass string = param_list[ param_index ];
  196. // OutputDebugString( "MBL: Comparing " );
  197. // OutputDebugString( string.Peek_Buffer() );
  198. // OutputDebugString( " with " );
  199. // OutputDebugString( param_to_check );
  200. // OutputDebugString( "\n" );
  201. // if ( stricmp( string.Peek_Buffer(), param_to_check ) == 0 ) // Breaks with whitespaces
  202. if ( strstr( string.Peek_Buffer(), param_to_check ) != 0 )
  203. {
  204. return( true );
  205. }
  206. }
  207. }
  208. return( false );
  209. }
  210. //////////////////////////////////////////////////////////////////////
  211. //
  212. // Initialize
  213. //
  214. //////////////////////////////////////////////////////////////////////
  215. void
  216. AnimatedSoundMgrClass::Initialize (const char *ini_filename)
  217. {
  218. //
  219. // Don't re-initialize...
  220. //
  221. if (AnimSoundLists.Count () > 0) {
  222. return ;
  223. }
  224. const char *DEFAULT_INI_FILENAME = "w3danimsound.ini";
  225. //
  226. // Determine which filename to use
  227. //
  228. const char *filename_to_use = ini_filename;
  229. if (filename_to_use == NULL) {
  230. filename_to_use = DEFAULT_INI_FILENAME;
  231. }
  232. //
  233. // Get the INI file which contains the data for this viewer
  234. //
  235. INIClass *ini_file = ::Get_INI (filename_to_use);
  236. if (ini_file != NULL) {
  237. //
  238. // Loop over all the sections in the INI
  239. //
  240. List<INISection *> &section_list = ini_file->Get_Section_List ();
  241. for ( INISection *section = section_list.First ();
  242. section != NULL && section->Is_Valid ();
  243. section = section->Next_Valid ())
  244. {
  245. //
  246. // Get the animation name from the section name
  247. //
  248. StringClass animation_name = section->Section;
  249. ::strupr (animation_name.Peek_Buffer ());
  250. // OutputDebugString( "MBL Section / animation: " );
  251. // OutputDebugString( animation_name.Peek_Buffer() );
  252. // OutputDebugString( "\n" );
  253. //
  254. // Allocate a sound list
  255. //
  256. ANIM_SOUND_LIST *sound_list = new ANIM_SOUND_LIST;
  257. //
  258. // Loop over all the entries in this section
  259. //
  260. int entry_count = ini_file->Entry_Count (section->Section);
  261. for (int entry_index = 0; entry_index < entry_count; entry_index ++) {
  262. StringClass value;
  263. //
  264. // Get the data associated with this entry
  265. //
  266. const char *entry_name = ini_file->Get_Entry (section->Section, entry_index);
  267. // OutputDebugString( " MBL Entry name: " );
  268. // OutputDebugString( entry_name );
  269. // OutputDebugString( "\n" );
  270. if (strcmp(entry_name, "BoneName") == 0) {
  271. ini_file->Get_String (value, section->Section, entry_name);
  272. sound_list->BoneName = value;
  273. // OutputDebugString( " MBL (BoneName) entry line value: " );
  274. // OutputDebugString( value.Peek_Buffer() );
  275. // OutputDebugString( "\n" );
  276. } else {
  277. ini_file->Get_String (value, section->Section, entry_name);
  278. // OutputDebugString( " MBL (not BoneName) entry line value: " );
  279. // OutputDebugString( value.Peek_Buffer() );
  280. // OutputDebugString( "\n" );
  281. //
  282. // Extract the parameters from the section
  283. //
  284. int len = value.Get_Length ();
  285. StringClass definition_name (len + 1, true);
  286. int action_frame = 0;
  287. //
  288. // Separate the parameters into an easy-to-handle data structure
  289. //
  290. StringClass *param_list = NULL;
  291. int param_count = ::Build_List_From_String (value, ",", &param_list);
  292. // if ((param_count >= 2) && (param_count <= 3))
  293. {
  294. action_frame = ::atoi (param_list[0]);
  295. definition_name = param_list[1];
  296. definition_name.Trim ();
  297. //
  298. // Tie the relevant information together and store it
  299. // in the list of sounds for this animation
  300. //
  301. ANIM_SOUND_INFO* sound_info = new ANIM_SOUND_INFO;
  302. sound_info->Frame = action_frame;
  303. sound_info->SoundName = definition_name;
  304. //
  305. // "2D" check
  306. //
  307. // if ((param_count == 3) && (atoi(param_list[2]) == 2)) {
  308. // sound_info->Is2D = true;
  309. // }
  310. //
  311. sound_info->Is2D = false;
  312. if ( Is_In_Param_List( param_list, param_count, "2D" ) )
  313. {
  314. sound_info->Is2D = true;
  315. }
  316. //
  317. // "STOP" check
  318. //
  319. sound_info->IsStop = false;
  320. if ( Is_In_Param_List( param_list, param_count, "STOP" ) )
  321. {
  322. sound_info->IsStop = true;
  323. }
  324. sound_list->Add_Sound_Info (sound_info);
  325. delete [] param_list;
  326. }
  327. }
  328. }
  329. if (sound_list->List.Count () != 0) {
  330. //
  331. // Add this sound list to our hash-table and vector-array
  332. //
  333. AnimationNameHash.Insert (animation_name, sound_list);
  334. AnimSoundLists.Add (sound_list);
  335. } else {
  336. //WWDEBUG_SAY (("AnimatedSoundMgrClass::Initialize -- No sounds added for %d!\n", animation_name.Peek_Buffer ()));
  337. delete sound_list;
  338. }
  339. }
  340. delete ini_file;
  341. }
  342. return ;
  343. }
  344. //////////////////////////////////////////////////////////////////////
  345. //
  346. // Shutdown
  347. //
  348. //////////////////////////////////////////////////////////////////////
  349. void
  350. AnimatedSoundMgrClass::Shutdown (void)
  351. {
  352. //
  353. // Reset the animation name hash
  354. //
  355. AnimationNameHash.Remove_All ();
  356. //
  357. // Free each of the sound objects
  358. //
  359. for (int index = 0; index < AnimSoundLists.Count (); index ++) {
  360. /*
  361. ANIM_SOUND_LIST* list = AnimSoundLists[index];
  362. for (int i = 0; i < list->Count(); i++) {
  363. delete (*list)[i];
  364. }
  365. */
  366. delete AnimSoundLists[index];
  367. }
  368. AnimSoundLists.Delete_All ();
  369. return ;
  370. }
  371. //////////////////////////////////////////////////////////////////////
  372. //
  373. // Does_Animation_Have_Embedded_Sounds
  374. //
  375. //////////////////////////////////////////////////////////////////////
  376. const char*
  377. AnimatedSoundMgrClass::Get_Embedded_Sound_Name (HAnimClass *anim)
  378. {
  379. if (anim == NULL) {
  380. return NULL;
  381. }
  382. ANIM_SOUND_LIST* list = Find_Sound_List (anim);
  383. if (list == NULL) {
  384. return NULL;
  385. }
  386. return list->BoneName.Peek_Buffer();
  387. }
  388. //////////////////////////////////////////////////////////////////////
  389. //
  390. // Find_Sound_List
  391. //
  392. //////////////////////////////////////////////////////////////////////
  393. AnimatedSoundMgrClass::ANIM_SOUND_LIST *
  394. AnimatedSoundMgrClass::Find_Sound_List (HAnimClass *anim)
  395. {
  396. //
  397. // Build the full name of the animation
  398. //
  399. StringClass full_name (0, true);
  400. full_name = anim->Get_Name ();
  401. //
  402. // Make the name uppercase
  403. //
  404. ::strupr (full_name.Peek_Buffer ());
  405. //
  406. // Lookup the sound list for this animation
  407. //
  408. ANIM_SOUND_LIST *retval = AnimationNameHash.Get (full_name);
  409. return retval;
  410. }
  411. //////////////////////////////////////////////////////////////////////
  412. //
  413. // Trigger_Sound
  414. //
  415. //////////////////////////////////////////////////////////////////////
  416. float
  417. AnimatedSoundMgrClass::Trigger_Sound
  418. (
  419. HAnimClass * anim,
  420. float old_frame,
  421. float new_frame,
  422. const Matrix3D & tm
  423. )
  424. {
  425. if ((SoundLibrary == NULL) || (anim == NULL)) {
  426. return old_frame;
  427. }
  428. float retval = old_frame;
  429. #ifndef W3D_MAX4
  430. //
  431. // Lookup the sound list for this animation
  432. //
  433. ANIM_SOUND_LIST *sound_list = Find_Sound_List (anim);
  434. if (sound_list != NULL) {
  435. for (int index = 0; index < sound_list->List.Count (); index ++) {
  436. int frame = sound_list->List[index]->Frame;
  437. //
  438. // Is the animation passing the frame we need?
  439. //
  440. if ((old_frame < frame) && (new_frame >= frame)) {
  441. //
  442. // Don't trigger the sound if its skipped too far past...
  443. //
  444. //if (WWMath::Fabs (new_frame - old_frame) < 3.0F) {
  445. //
  446. // Stop the audio?
  447. //
  448. if (sound_list->List[index]->IsStop == true)
  449. {
  450. //
  451. // Stop the audio
  452. //
  453. SoundLibrary->Stop_Playing_Audio( sound_list->List[index]->SoundName.Peek_Buffer() );
  454. }
  455. else
  456. {
  457. //
  458. // Play the audio
  459. //
  460. if (sound_list->List[index]->Is2D == true)
  461. {
  462. SoundLibrary->Play_2D_Audio(sound_list->List[index]->SoundName.Peek_Buffer());
  463. }
  464. else
  465. {
  466. SoundLibrary->Play_3D_Audio(sound_list->List[index]->SoundName.Peek_Buffer(), tm);
  467. }
  468. }
  469. //WWDEBUG_SAY (("Triggering Sound %d %s\n", GetTickCount (), sound_list->List[index]->SoundName));
  470. retval = frame;
  471. //}
  472. }
  473. }
  474. //retval = true;
  475. }
  476. #endif
  477. return retval;
  478. }
  479. void AnimatedSoundMgrClass::Set_Sound_Library(SoundLibraryBridgeClass* library)
  480. {
  481. SoundLibrary = library;
  482. }