Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

744 lines
19 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include "cbase.h"
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <sys/stat.h>
  12. #include "filesystem.h"
  13. #include "mxtk/mx.h"
  14. #include "mxStatusWindow.h"
  15. #include "filesystem.h"
  16. #include "StudioModel.h"
  17. #include "ControlPanel.h"
  18. #include "MDLViewer.h"
  19. #include "mxExpressionTray.H"
  20. #include "viewersettings.h"
  21. #include "tier1/strtools.h"
  22. #include "faceposer_models.h"
  23. #include "expressions.h"
  24. #include "choreoview.h"
  25. #include "choreoscene.h"
  26. #include "vstdlib/random.h"
  27. #include "SoundEmitterSystem/isoundemittersystembase.h"
  28. #include "soundchars.h"
  29. #include "sentence.h"
  30. #include "PhonemeEditor.h"
  31. #include <vgui/ILocalize.h>
  32. #include "filesystem_init.h"
  33. #include "tier2/p4helpers.h"
  34. extern vgui::ILocalize *g_pLocalize;
  35. StudioModel *FindAssociatedModel( CChoreoScene *scene, CChoreoActor *a );
  36. //-----------------------------------------------------------------------------
  37. // Purpose: Takes a full path and determines if the file exists on the disk
  38. // Input : *filename -
  39. // Output : Returns true on success, false on failure.
  40. //-----------------------------------------------------------------------------
  41. bool FPFullpathFileExists( const char *filename )
  42. {
  43. // Should be a full path
  44. Assert( strchr( filename, ':' ) );
  45. struct _stat buf;
  46. int result = _stat( filename, &buf );
  47. if ( result != -1 )
  48. return true;
  49. return false;
  50. }
  51. // Utility functions mostly
  52. char *FacePoser_MakeWindowsSlashes( char *pname )
  53. {
  54. static char returnString[ 4096 ];
  55. strcpy( returnString, pname );
  56. pname = returnString;
  57. while ( *pname )
  58. {
  59. if ( *pname == '/' )
  60. {
  61. *pname = '\\';
  62. }
  63. pname++;
  64. }
  65. return returnString;
  66. }
  67. //-----------------------------------------------------------------------------
  68. // Purpose:
  69. // Output : int
  70. //-----------------------------------------------------------------------------
  71. int GetCloseCaptionLanguageId()
  72. {
  73. return g_viewerSettings.cclanguageid;
  74. }
  75. //-----------------------------------------------------------------------------
  76. // Purpose:
  77. // Input : id -
  78. //-----------------------------------------------------------------------------
  79. void SetCloseCaptionLanguageId( int id, bool force /* = false */ )
  80. {
  81. Assert( id >= 0 && id < CC_NUM_LANGUAGES );
  82. bool changed = g_viewerSettings.cclanguageid != id;
  83. g_viewerSettings.cclanguageid = id;
  84. if ( changed || force )
  85. {
  86. // Switch languages
  87. char const *suffix = CSentence::NameForLanguage( id );
  88. if ( Q_stricmp( suffix, "unknown_language" ) )
  89. {
  90. char fn[ MAX_PATH ];
  91. Q_snprintf( fn, sizeof( fn ), "resource/closecaption_%s.txt", suffix );
  92. g_pLocalize->RemoveAll();
  93. if ( Q_stricmp( suffix, "english" )&&
  94. filesystem->FileExists( "resource/closecaption_english.txt" ) )
  95. {
  96. g_pLocalize->AddFile( "resource/closecaption_english.txt", "GAME", true );
  97. }
  98. if ( filesystem->FileExists( fn ) )
  99. {
  100. g_pLocalize->AddFile( fn, "GAME", true );
  101. }
  102. else
  103. {
  104. Con_ErrorPrintf( "PhonemeEditor::SetCloseCaptionLanguageId Warning, can't find localization file %s\n", fn );
  105. }
  106. // Need to redraw the choreoview at least
  107. if ( g_pChoreoView )
  108. {
  109. g_pChoreoView->InvalidateLayout();
  110. }
  111. }
  112. }
  113. if ( g_MDLViewer )
  114. {
  115. g_MDLViewer->UpdateLanguageMenu( id );
  116. }
  117. }
  118. char *va( const char *fmt, ... )
  119. {
  120. va_list args;
  121. static char output[32][1024];
  122. static int outbuffer = 0;
  123. outbuffer++;
  124. va_start( args, fmt );
  125. vprintf( fmt, args );
  126. vsprintf( output[ outbuffer & 31 ], fmt, args );
  127. return output[ outbuffer & 31 ];
  128. }
  129. void Con_Printf( const char *fmt, ... )
  130. {
  131. va_list args;
  132. static char output[1024];
  133. va_start( args, fmt );
  134. vprintf( fmt, args );
  135. vsprintf( output, fmt, args );
  136. if ( !g_pStatusWindow )
  137. {
  138. return;
  139. }
  140. g_pStatusWindow->StatusPrint( CONSOLE_COLOR, false, output );
  141. }
  142. void Con_ColorPrintf( COLORREF rgb, const char *fmt, ... )
  143. {
  144. va_list args;
  145. static char output[1024];
  146. va_start( args, fmt );
  147. vprintf( fmt, args );
  148. vsprintf( output, fmt, args );
  149. if ( !g_pStatusWindow )
  150. {
  151. return;
  152. }
  153. g_pStatusWindow->StatusPrint( rgb, false, output );
  154. }
  155. void Con_ErrorPrintf( const char *fmt, ... )
  156. {
  157. va_list args;
  158. static char output[1024];
  159. va_start( args, fmt );
  160. vprintf( fmt, args );
  161. vsprintf( output, fmt, args );
  162. if ( !g_pStatusWindow )
  163. {
  164. return;
  165. }
  166. g_pStatusWindow->StatusPrint( ERROR_COLOR, false, output );
  167. }
  168. //-----------------------------------------------------------------------------
  169. // Purpose:
  170. // Input : *filename -
  171. //-----------------------------------------------------------------------------
  172. void MakeFileWriteable( const char *filename )
  173. {
  174. Assert( filesystem );
  175. char pFullPathBuf[ 512 ];
  176. char *pFullPath;
  177. if ( !Q_IsAbsolutePath( filename ) )
  178. {
  179. pFullPath = (char*)filesystem->RelativePathToFullPath( filename, NULL, pFullPathBuf, sizeof(pFullPathBuf) );
  180. }
  181. else
  182. {
  183. Q_strncpy( pFullPathBuf, filename, sizeof(pFullPathBuf) );
  184. pFullPath = pFullPathBuf;
  185. }
  186. if ( pFullPath )
  187. {
  188. Q_FixSlashes( pFullPath );
  189. SetFileAttributes( pFullPath, FILE_ATTRIBUTE_NORMAL );
  190. }
  191. }
  192. //-----------------------------------------------------------------------------
  193. // Purpose:
  194. // Input : *filename -
  195. // Output : Returns true on success, false on failure.
  196. //-----------------------------------------------------------------------------
  197. bool IsFileWriteable( const char *filename )
  198. {
  199. Assert( filesystem );
  200. char pFullPathBuf[ 512 ];
  201. char *pFullPath;
  202. if ( !Q_IsAbsolutePath( filename ) )
  203. {
  204. pFullPath = (char*)filesystem->RelativePathToFullPath( filename, NULL, pFullPathBuf, sizeof(pFullPathBuf) );
  205. }
  206. else
  207. {
  208. Q_strncpy( pFullPathBuf, filename, sizeof(pFullPathBuf) );
  209. pFullPath = pFullPathBuf;
  210. }
  211. if ( pFullPath )
  212. {
  213. Q_FixSlashes( pFullPath );
  214. DWORD attrib = GetFileAttributes( pFullPath );
  215. return ( ( attrib & FILE_ATTRIBUTE_READONLY ) == 0 );
  216. }
  217. // Doesn't seem to exist, so yeah, it's writable
  218. return true;
  219. }
  220. bool MakeFileWriteablePrompt( const char *filename, char const *promptTitle )
  221. {
  222. if ( !IsFileWriteable( filename ) )
  223. {
  224. int retval = mxMessageBox( NULL, va( "File '%s' is Read-Only, make writable?", filename ),
  225. promptTitle, MX_MB_WARNING | MX_MB_YESNO );
  226. // Didn't pick yes, bail
  227. if ( retval != 0 )
  228. return false;
  229. MakeFileWriteable( filename );
  230. }
  231. return true;
  232. }
  233. void FPCopyFile( const char *source, const char *dest, bool bCheckOut )
  234. {
  235. Assert( filesystem );
  236. char fullpaths[ MAX_PATH ];
  237. char fullpathd[ MAX_PATH ];
  238. if ( !Q_IsAbsolutePath( source ) )
  239. {
  240. filesystem->RelativePathToFullPath( source, NULL, fullpaths, sizeof(fullpaths) );
  241. }
  242. else
  243. {
  244. Q_strncpy( fullpaths, source, sizeof(fullpaths) );
  245. }
  246. Q_strncpy( fullpathd, fullpaths, MAX_PATH );
  247. char *pSubdir = Q_stristr( fullpathd, source );
  248. if ( pSubdir )
  249. {
  250. *pSubdir = 0;
  251. }
  252. Q_AppendSlash( fullpathd, MAX_PATH );
  253. Q_strncat( fullpathd, dest, MAX_PATH, MAX_PATH );
  254. Q_FixSlashes( fullpaths );
  255. Q_FixSlashes( fullpathd );
  256. if ( bCheckOut )
  257. {
  258. CP4AutoEditAddFile checkout( fullpathd );
  259. CopyFile( fullpaths, fullpathd, FALSE );
  260. }
  261. else
  262. {
  263. CopyFile( fullpaths, fullpathd, FALSE );
  264. }
  265. }
  266. bool FacePoser_HasWindowStyle( mxWindow *w, int bits )
  267. {
  268. HWND wnd = (HWND)w->getHandle();
  269. DWORD style = GetWindowLong( wnd, GWL_STYLE );
  270. return ( style & bits ) ? true : false;
  271. }
  272. bool FacePoser_HasWindowExStyle( mxWindow *w, int bits )
  273. {
  274. HWND wnd = (HWND)w->getHandle();
  275. DWORD style = GetWindowLong( wnd, GWL_EXSTYLE );
  276. return ( style & bits ) ? true : false;
  277. }
  278. void FacePoser_AddWindowStyle( mxWindow *w, int addbits )
  279. {
  280. HWND wnd = (HWND)w->getHandle();
  281. DWORD style = GetWindowLong( wnd, GWL_STYLE );
  282. style |= addbits;
  283. SetWindowLong( wnd, GWL_STYLE, style );
  284. }
  285. void FacePoser_AddWindowExStyle( mxWindow *w, int addbits )
  286. {
  287. HWND wnd = (HWND)w->getHandle();
  288. DWORD style = GetWindowLong( wnd, GWL_EXSTYLE );
  289. style |= addbits;
  290. SetWindowLong( wnd, GWL_EXSTYLE, style );
  291. }
  292. void FacePoser_RemoveWindowStyle( mxWindow *w, int removebits )
  293. {
  294. HWND wnd = (HWND)w->getHandle();
  295. DWORD style = GetWindowLong( wnd, GWL_STYLE );
  296. style &= ~removebits;
  297. SetWindowLong( wnd, GWL_STYLE, style );
  298. }
  299. void FacePoser_RemoveWindowExStyle( mxWindow *w, int removebits )
  300. {
  301. HWND wnd = (HWND)w->getHandle();
  302. DWORD style = GetWindowLong( wnd, GWL_EXSTYLE );
  303. style &= ~removebits;
  304. SetWindowLong( wnd, GWL_EXSTYLE, style );
  305. }
  306. //-----------------------------------------------------------------------------
  307. // Purpose:
  308. // Input : *w -
  309. //-----------------------------------------------------------------------------
  310. void FacePoser_MakeToolWindow( mxWindow *w, bool smallcaption )
  311. {
  312. FacePoser_AddWindowStyle( w, WS_VISIBLE | WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS );
  313. if ( smallcaption )
  314. {
  315. FacePoser_AddWindowExStyle( w, WS_EX_OVERLAPPEDWINDOW );
  316. FacePoser_AddWindowExStyle( w, WS_EX_TOOLWINDOW );
  317. }
  318. }
  319. bool LoadViewerSettingsInt( char const *keyname, int *value );
  320. bool SaveViewerSettingsInt ( const char *keyname, int value );
  321. void FacePoser_LoadWindowPositions( char const *name, bool& visible, int& x, int& y, int& w, int& h, bool& locked, bool& zoomed )
  322. {
  323. char subkey[ 512 ];
  324. int v;
  325. Q_snprintf( subkey, sizeof( subkey ), "%s - visible", name );
  326. LoadViewerSettingsInt( subkey, &v );
  327. visible = v ? true : false;
  328. Q_snprintf( subkey, sizeof( subkey ), "%s - locked", name );
  329. LoadViewerSettingsInt( subkey, &v );
  330. locked = v ? true : false;
  331. Q_snprintf( subkey, sizeof( subkey ), "%s - zoomed", name );
  332. LoadViewerSettingsInt( subkey, &v );
  333. zoomed = v ? true : false;
  334. Q_snprintf( subkey, sizeof( subkey ), "%s - x", name );
  335. LoadViewerSettingsInt( subkey, &x );
  336. Q_snprintf( subkey, sizeof( subkey ), "%s - y", name );
  337. LoadViewerSettingsInt( subkey, &y );
  338. Q_snprintf( subkey, sizeof( subkey ), "%s - width", name );
  339. LoadViewerSettingsInt( subkey, &w );
  340. Q_snprintf( subkey, sizeof( subkey ), "%s - height", name );
  341. LoadViewerSettingsInt( subkey, &h );
  342. }
  343. void FacePoser_SaveWindowPositions( char const *name, bool visible, int x, int y, int w, int h, bool locked, bool zoomed )
  344. {
  345. char subkey[ 512 ];
  346. Q_snprintf( subkey, sizeof( subkey ), "%s - visible", name );
  347. SaveViewerSettingsInt( subkey, visible );
  348. Q_snprintf( subkey, sizeof( subkey ), "%s - locked", name );
  349. SaveViewerSettingsInt( subkey, locked );
  350. Q_snprintf( subkey, sizeof( subkey ), "%s - x", name );
  351. SaveViewerSettingsInt( subkey, x );
  352. Q_snprintf( subkey, sizeof( subkey ), "%s - y", name );
  353. SaveViewerSettingsInt( subkey, y );
  354. Q_snprintf( subkey, sizeof( subkey ), "%s - width", name );
  355. SaveViewerSettingsInt( subkey, w );
  356. Q_snprintf( subkey, sizeof( subkey ), "%s - height", name );
  357. SaveViewerSettingsInt( subkey, h );
  358. Q_snprintf( subkey, sizeof( subkey ), "%s - zoomed", name );
  359. SaveViewerSettingsInt( subkey, zoomed );
  360. }
  361. static char g_PhonemeRoot[ MAX_PATH ] = { 0 };
  362. void FacePoser_SetPhonemeRootDir( char const *pchRootDir )
  363. {
  364. Q_strncpy( g_PhonemeRoot, pchRootDir, sizeof( g_PhonemeRoot ) );
  365. }
  366. //-----------------------------------------------------------------------------
  367. // Purpose:
  368. //-----------------------------------------------------------------------------
  369. void FacePoser_EnsurePhonemesLoaded( void )
  370. {
  371. // Don't bother unless a model is loaded, at least...
  372. CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
  373. if ( !hdr )
  374. {
  375. return;
  376. }
  377. char const *ext[] =
  378. {
  379. "",
  380. "_strong",
  381. "_weak",
  382. };
  383. for ( int i = 0 ; i < ARRAYSIZE( ext ); ++i )
  384. {
  385. char clname[ 256 ];
  386. Q_snprintf( clname, sizeof( clname ), "%sphonemes%s", g_PhonemeRoot, ext[ i ] );
  387. Q_FixSlashes( clname );
  388. Q_strlower( clname );
  389. if ( !expressions->FindClass( clname, false ) )
  390. {
  391. char clfile[ MAX_PATH ];
  392. Q_snprintf( clfile, sizeof( clfile ), "expressions/%sphonemes%s.txt", g_PhonemeRoot, ext[ i ] );
  393. Q_FixSlashes( clfile );
  394. Q_strlower( clfile );
  395. if ( g_pFileSystem->FileExists( clfile ) )
  396. {
  397. expressions->LoadClass( clfile );
  398. CExpClass *cl = expressions->FindClass( clname, false );
  399. if ( !cl )
  400. {
  401. Con_Printf( "FacePoser_EnsurePhonemesLoaded: %s missing!!!\n", clfile );
  402. }
  403. }
  404. }
  405. }
  406. }
  407. bool FacePoser_ShowFileNameDialog( bool openFile, char *relative, size_t bufsize, char const *subdir, char const *wildcard )
  408. {
  409. Assert( relative );
  410. relative[ 0 ] = 0 ;
  411. Assert( subdir );
  412. Assert( wildcard );
  413. char workingdir[ 256 ];
  414. Q_getwd( workingdir, sizeof( workingdir ) );
  415. strlwr( workingdir );
  416. Q_FixSlashes( workingdir, '/' );
  417. // Show file io
  418. bool inWorkingDirectoryAlready = false;
  419. if ( Q_stristr_slash( workingdir, va( "%s%s", GetGameDirectory(), subdir ) ) )
  420. {
  421. inWorkingDirectoryAlready = true;
  422. }
  423. // Show file io
  424. const char *fullpath = NULL;
  425. if ( openFile )
  426. {
  427. fullpath = mxGetOpenFileName(
  428. 0,
  429. inWorkingDirectoryAlready ? "." : FacePoser_MakeWindowsSlashes( va( "%s%s/", GetGameDirectory(), subdir ) ),
  430. wildcard );
  431. }
  432. else
  433. {
  434. fullpath = mxGetSaveFileName(
  435. 0,
  436. inWorkingDirectoryAlready ? "." : FacePoser_MakeWindowsSlashes( va( "%s%s/", GetGameDirectory(), subdir ) ),
  437. wildcard );
  438. }
  439. if ( !fullpath || !fullpath[ 0 ] )
  440. return false;
  441. Q_strncpy( relative, fullpath, bufsize );
  442. return true;
  443. }
  444. bool FacePoser_ShowOpenFileNameDialog( char *relative, size_t bufsize, char const *subdir, char const *wildcard )
  445. {
  446. return FacePoser_ShowFileNameDialog( true, relative, bufsize, subdir, wildcard );
  447. }
  448. bool FacePoser_ShowSaveFileNameDialog( char *relative, size_t bufsize, char const *subdir, char const *wildcard )
  449. {
  450. return FacePoser_ShowFileNameDialog( false, relative, bufsize, subdir, wildcard );
  451. }
  452. //-----------------------------------------------------------------------------
  453. // Purpose: converts an english string to unicode
  454. //-----------------------------------------------------------------------------
  455. int ConvertANSIToUnicode(const char *ansi, wchar_t *unicode, int unicodeBufferSize)
  456. {
  457. return ::MultiByteToWideChar(CP_ACP, 0, ansi, -1, unicode, unicodeBufferSize);
  458. }
  459. //-----------------------------------------------------------------------------
  460. // Purpose: converts an unicode string to an english string
  461. //-----------------------------------------------------------------------------
  462. int ConvertUnicodeToANSI(const wchar_t *unicode, char *ansi, int ansiBufferSize)
  463. {
  464. return ::WideCharToMultiByte(CP_ACP, 0, unicode, -1, ansi, ansiBufferSize, NULL, NULL);
  465. }
  466. //-----------------------------------------------------------------------------
  467. // Purpose: If FPS is set and "using grid", snap to proper fractional time value
  468. // Input : t -
  469. // Output : float
  470. //-----------------------------------------------------------------------------
  471. float FacePoser_SnapTime( float t )
  472. {
  473. if ( !g_pChoreoView )
  474. return t;
  475. CChoreoScene *scene = g_pChoreoView->GetScene();
  476. if ( !scene )
  477. return t;
  478. return scene->SnapTime( t );
  479. }
  480. //-----------------------------------------------------------------------------
  481. // Purpose:
  482. // Input : t -
  483. // Output : char const
  484. //-----------------------------------------------------------------------------
  485. char const *FacePoser_DescribeSnappedTime( float t )
  486. {
  487. static char desc[ 128 ];
  488. Q_snprintf( desc, sizeof( desc ), "%.3f", t );
  489. if ( !g_pChoreoView )
  490. return desc;
  491. CChoreoScene *scene = g_pChoreoView->GetScene();
  492. if ( !scene )
  493. return desc;
  494. t = scene->SnapTime( t );
  495. int fps = scene->GetSceneFPS();
  496. int ipart = (int)t;
  497. int fracpart = (int)( ( t - (float)ipart ) * (float)fps + 0.5f );
  498. int frame = ipart * fps + fracpart;
  499. if ( fracpart == 0 )
  500. {
  501. Q_snprintf( desc, sizeof( desc ), "frame %i (time %i s.)", frame, ipart );
  502. }
  503. else
  504. {
  505. Q_snprintf( desc, sizeof( desc ), "frame %i (time %i + %i/%i s.)",
  506. frame, ipart,fracpart, fps );
  507. }
  508. return desc;
  509. }
  510. //-----------------------------------------------------------------------------
  511. // Purpose:
  512. // Output : int
  513. //-----------------------------------------------------------------------------
  514. int FacePoser_GetSceneFPS( void )
  515. {
  516. if ( !g_pChoreoView )
  517. return 1000;
  518. CChoreoScene *scene = g_pChoreoView->GetScene();
  519. if ( !scene )
  520. return 1000;
  521. return scene->GetSceneFPS();
  522. }
  523. //-----------------------------------------------------------------------------
  524. // Purpose:
  525. // Output : Returns true on success, false on failure.
  526. //-----------------------------------------------------------------------------
  527. bool FacePoser_IsSnapping( void )
  528. {
  529. if ( !g_pChoreoView )
  530. return false;
  531. CChoreoScene *scene = g_pChoreoView->GetScene();
  532. if ( !scene )
  533. return false;
  534. return scene->IsUsingFrameSnap();
  535. }
  536. char const *FacePoser_TranslateSoundNameGender( char const *soundname, gender_t gender )
  537. {
  538. if ( Q_stristr( soundname, ".wav" ) )
  539. return PSkipSoundChars( soundname );
  540. return PSkipSoundChars( soundemitter->GetWavFileForSound( soundname, gender ) );
  541. }
  542. char const *FacePoser_TranslateSoundName( char const *soundname, StudioModel *model /*= NULL*/ )
  543. {
  544. if ( Q_stristr( soundname, ".wav" ) )
  545. return PSkipSoundChars( soundname );
  546. static char temp[ 256 ];
  547. if ( model )
  548. {
  549. Q_strncpy( temp, PSkipSoundChars( soundemitter->GetWavFileForSound( soundname, model->GetFileName() ) ), sizeof( temp ) );
  550. }
  551. else
  552. {
  553. Q_strncpy( temp, PSkipSoundChars( soundemitter->GetWavFileForSound( soundname, NULL ) ), sizeof( temp ) );
  554. }
  555. return temp;
  556. }
  557. char const *FacePoser_TranslateSoundName( CChoreoEvent *event )
  558. {
  559. char const *soundname = event->GetParameters();
  560. if ( Q_stristr( soundname, ".wav" ) )
  561. return PSkipSoundChars( soundname );
  562. // See if we can figure out the .mdl associated to this event's actor
  563. static char temp[ 256 ];
  564. temp[ 0 ] = 0;
  565. StudioModel *model = NULL;
  566. CChoreoActor *a = event->GetActor();
  567. CChoreoScene *s = event->GetScene();
  568. if ( a != NULL &&
  569. s != NULL )
  570. {
  571. model = FindAssociatedModel( s, a );
  572. }
  573. Q_strncpy( temp, PSkipSoundChars( soundemitter->GetWavFileForSound( soundname, model ? model->GetFileName() : NULL ) ), sizeof( temp ) );
  574. return temp;
  575. }
  576. #if defined( _WIN32 ) || defined( WIN32 )
  577. #define PATHSEPARATOR(c) ((c) == '\\' || (c) == '/')
  578. #else //_WIN32
  579. #define PATHSEPARATOR(c) ((c) == '/')
  580. #endif //_WIN32
  581. static bool charsmatch( char c1, char c2 )
  582. {
  583. if ( tolower( c1 ) == tolower( c2 ) )
  584. return true;
  585. if ( PATHSEPARATOR( c1 ) && PATHSEPARATOR( c2 ) )
  586. return true;
  587. return false;
  588. }
  589. char *Q_stristr_slash( char const *pStr, char const *pSearch )
  590. {
  591. AssertValidStringPtr(pStr);
  592. AssertValidStringPtr(pSearch);
  593. if (!pStr || !pSearch)
  594. return 0;
  595. char const* pLetter = pStr;
  596. // Check the entire string
  597. while (*pLetter != 0)
  598. {
  599. // Skip over non-matches
  600. if ( charsmatch( *pLetter, *pSearch ) )
  601. {
  602. // Check for match
  603. char const* pMatch = pLetter + 1;
  604. char const* pTest = pSearch + 1;
  605. while (*pTest != 0)
  606. {
  607. // We've run off the end; don't bother.
  608. if (*pMatch == 0)
  609. return 0;
  610. if ( !charsmatch( *pMatch, *pTest ) )
  611. break;
  612. ++pMatch;
  613. ++pTest;
  614. }
  615. // Found a match!
  616. if (*pTest == 0)
  617. return (char *)pLetter;
  618. }
  619. ++pLetter;
  620. }
  621. return 0;
  622. }
  623. static CUniformRandomStream g_Random;
  624. IUniformRandomStream *random = &g_Random;