//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //===========================================================================// #include "cbase.h" #include #include #include #include "filesystem.h" #include "mxtk/mx.h" #include "mxStatusWindow.h" #include "filesystem.h" #include "StudioModel.h" #include "ControlPanel.h" #include "MDLViewer.h" #include "mxExpressionTray.H" #include "viewersettings.h" #include "tier1/strtools.h" #include "faceposer_models.h" #include "expressions.h" #include "choreoview.h" #include "choreoscene.h" #include "vstdlib/random.h" #include "SoundEmitterSystem/isoundemittersystembase.h" #include "soundchars.h" #include "sentence.h" #include "PhonemeEditor.h" #include #include "filesystem_init.h" #include "tier2/p4helpers.h" extern vgui::ILocalize *g_pLocalize; StudioModel *FindAssociatedModel( CChoreoScene *scene, CChoreoActor *a ); //----------------------------------------------------------------------------- // Purpose: Takes a full path and determines if the file exists on the disk // Input : *filename - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool FPFullpathFileExists( const char *filename ) { // Should be a full path Assert( strchr( filename, ':' ) ); struct _stat buf; int result = _stat( filename, &buf ); if ( result != -1 ) return true; return false; } // Utility functions mostly char *FacePoser_MakeWindowsSlashes( char *pname ) { static char returnString[ 4096 ]; strcpy( returnString, pname ); pname = returnString; while ( *pname ) { if ( *pname == '/' ) { *pname = '\\'; } pname++; } return returnString; } //----------------------------------------------------------------------------- // Purpose: // Output : int //----------------------------------------------------------------------------- int GetCloseCaptionLanguageId() { return g_viewerSettings.cclanguageid; } //----------------------------------------------------------------------------- // Purpose: // Input : id - //----------------------------------------------------------------------------- void SetCloseCaptionLanguageId( int id, bool force /* = false */ ) { Assert( id >= 0 && id < CC_NUM_LANGUAGES ); bool changed = g_viewerSettings.cclanguageid != id; g_viewerSettings.cclanguageid = id; if ( changed || force ) { // Switch languages char const *suffix = CSentence::NameForLanguage( id ); if ( Q_stricmp( suffix, "unknown_language" ) ) { char fn[ MAX_PATH ]; Q_snprintf( fn, sizeof( fn ), "resource/closecaption_%s.txt", suffix ); g_pLocalize->RemoveAll(); if ( Q_stricmp( suffix, "english" )&& filesystem->FileExists( "resource/closecaption_english.txt" ) ) { g_pLocalize->AddFile( "resource/closecaption_english.txt", "GAME", true ); } if ( filesystem->FileExists( fn ) ) { g_pLocalize->AddFile( fn, "GAME", true ); } else { Con_ErrorPrintf( "PhonemeEditor::SetCloseCaptionLanguageId Warning, can't find localization file %s\n", fn ); } // Need to redraw the choreoview at least if ( g_pChoreoView ) { g_pChoreoView->InvalidateLayout(); } } } if ( g_MDLViewer ) { g_MDLViewer->UpdateLanguageMenu( id ); } } char *va( const char *fmt, ... ) { va_list args; static char output[32][1024]; static int outbuffer = 0; outbuffer++; va_start( args, fmt ); vprintf( fmt, args ); vsprintf( output[ outbuffer & 31 ], fmt, args ); return output[ outbuffer & 31 ]; } void Con_Printf( const char *fmt, ... ) { va_list args; static char output[1024]; va_start( args, fmt ); vprintf( fmt, args ); vsprintf( output, fmt, args ); if ( !g_pStatusWindow ) { return; } g_pStatusWindow->StatusPrint( CONSOLE_COLOR, false, output ); } void Con_ColorPrintf( COLORREF rgb, const char *fmt, ... ) { va_list args; static char output[1024]; va_start( args, fmt ); vprintf( fmt, args ); vsprintf( output, fmt, args ); if ( !g_pStatusWindow ) { return; } g_pStatusWindow->StatusPrint( rgb, false, output ); } void Con_ErrorPrintf( const char *fmt, ... ) { va_list args; static char output[1024]; va_start( args, fmt ); vprintf( fmt, args ); vsprintf( output, fmt, args ); if ( !g_pStatusWindow ) { return; } g_pStatusWindow->StatusPrint( ERROR_COLOR, false, output ); } //----------------------------------------------------------------------------- // Purpose: // Input : *filename - //----------------------------------------------------------------------------- void MakeFileWriteable( const char *filename ) { Assert( filesystem ); char pFullPathBuf[ 512 ]; char *pFullPath; if ( !Q_IsAbsolutePath( filename ) ) { pFullPath = (char*)filesystem->RelativePathToFullPath( filename, NULL, pFullPathBuf, sizeof(pFullPathBuf) ); } else { Q_strncpy( pFullPathBuf, filename, sizeof(pFullPathBuf) ); pFullPath = pFullPathBuf; } if ( pFullPath ) { Q_FixSlashes( pFullPath ); SetFileAttributes( pFullPath, FILE_ATTRIBUTE_NORMAL ); } } //----------------------------------------------------------------------------- // Purpose: // Input : *filename - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool IsFileWriteable( const char *filename ) { Assert( filesystem ); char pFullPathBuf[ 512 ]; char *pFullPath; if ( !Q_IsAbsolutePath( filename ) ) { pFullPath = (char*)filesystem->RelativePathToFullPath( filename, NULL, pFullPathBuf, sizeof(pFullPathBuf) ); } else { Q_strncpy( pFullPathBuf, filename, sizeof(pFullPathBuf) ); pFullPath = pFullPathBuf; } if ( pFullPath ) { Q_FixSlashes( pFullPath ); DWORD attrib = GetFileAttributes( pFullPath ); return ( ( attrib & FILE_ATTRIBUTE_READONLY ) == 0 ); } // Doesn't seem to exist, so yeah, it's writable return true; } bool MakeFileWriteablePrompt( const char *filename, char const *promptTitle ) { if ( !IsFileWriteable( filename ) ) { int retval = mxMessageBox( NULL, va( "File '%s' is Read-Only, make writable?", filename ), promptTitle, MX_MB_WARNING | MX_MB_YESNO ); // Didn't pick yes, bail if ( retval != 0 ) return false; MakeFileWriteable( filename ); } return true; } void FPCopyFile( const char *source, const char *dest, bool bCheckOut ) { Assert( filesystem ); char fullpaths[ MAX_PATH ]; char fullpathd[ MAX_PATH ]; if ( !Q_IsAbsolutePath( source ) ) { filesystem->RelativePathToFullPath( source, NULL, fullpaths, sizeof(fullpaths) ); } else { Q_strncpy( fullpaths, source, sizeof(fullpaths) ); } Q_strncpy( fullpathd, fullpaths, MAX_PATH ); char *pSubdir = Q_stristr( fullpathd, source ); if ( pSubdir ) { *pSubdir = 0; } Q_AppendSlash( fullpathd, MAX_PATH ); Q_strncat( fullpathd, dest, MAX_PATH, MAX_PATH ); Q_FixSlashes( fullpaths ); Q_FixSlashes( fullpathd ); if ( bCheckOut ) { CP4AutoEditAddFile checkout( fullpathd ); CopyFile( fullpaths, fullpathd, FALSE ); } else { CopyFile( fullpaths, fullpathd, FALSE ); } } bool FacePoser_HasWindowStyle( mxWindow *w, int bits ) { HWND wnd = (HWND)w->getHandle(); DWORD style = GetWindowLong( wnd, GWL_STYLE ); return ( style & bits ) ? true : false; } bool FacePoser_HasWindowExStyle( mxWindow *w, int bits ) { HWND wnd = (HWND)w->getHandle(); DWORD style = GetWindowLong( wnd, GWL_EXSTYLE ); return ( style & bits ) ? true : false; } void FacePoser_AddWindowStyle( mxWindow *w, int addbits ) { HWND wnd = (HWND)w->getHandle(); DWORD style = GetWindowLong( wnd, GWL_STYLE ); style |= addbits; SetWindowLong( wnd, GWL_STYLE, style ); } void FacePoser_AddWindowExStyle( mxWindow *w, int addbits ) { HWND wnd = (HWND)w->getHandle(); DWORD style = GetWindowLong( wnd, GWL_EXSTYLE ); style |= addbits; SetWindowLong( wnd, GWL_EXSTYLE, style ); } void FacePoser_RemoveWindowStyle( mxWindow *w, int removebits ) { HWND wnd = (HWND)w->getHandle(); DWORD style = GetWindowLong( wnd, GWL_STYLE ); style &= ~removebits; SetWindowLong( wnd, GWL_STYLE, style ); } void FacePoser_RemoveWindowExStyle( mxWindow *w, int removebits ) { HWND wnd = (HWND)w->getHandle(); DWORD style = GetWindowLong( wnd, GWL_EXSTYLE ); style &= ~removebits; SetWindowLong( wnd, GWL_EXSTYLE, style ); } //----------------------------------------------------------------------------- // Purpose: // Input : *w - //----------------------------------------------------------------------------- void FacePoser_MakeToolWindow( mxWindow *w, bool smallcaption ) { FacePoser_AddWindowStyle( w, WS_VISIBLE | WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS ); if ( smallcaption ) { FacePoser_AddWindowExStyle( w, WS_EX_OVERLAPPEDWINDOW ); FacePoser_AddWindowExStyle( w, WS_EX_TOOLWINDOW ); } } bool LoadViewerSettingsInt( char const *keyname, int *value ); bool SaveViewerSettingsInt ( const char *keyname, int value ); void FacePoser_LoadWindowPositions( char const *name, bool& visible, int& x, int& y, int& w, int& h, bool& locked, bool& zoomed ) { char subkey[ 512 ]; int v; Q_snprintf( subkey, sizeof( subkey ), "%s - visible", name ); LoadViewerSettingsInt( subkey, &v ); visible = v ? true : false; Q_snprintf( subkey, sizeof( subkey ), "%s - locked", name ); LoadViewerSettingsInt( subkey, &v ); locked = v ? true : false; Q_snprintf( subkey, sizeof( subkey ), "%s - zoomed", name ); LoadViewerSettingsInt( subkey, &v ); zoomed = v ? true : false; Q_snprintf( subkey, sizeof( subkey ), "%s - x", name ); LoadViewerSettingsInt( subkey, &x ); Q_snprintf( subkey, sizeof( subkey ), "%s - y", name ); LoadViewerSettingsInt( subkey, &y ); Q_snprintf( subkey, sizeof( subkey ), "%s - width", name ); LoadViewerSettingsInt( subkey, &w ); Q_snprintf( subkey, sizeof( subkey ), "%s - height", name ); LoadViewerSettingsInt( subkey, &h ); } void FacePoser_SaveWindowPositions( char const *name, bool visible, int x, int y, int w, int h, bool locked, bool zoomed ) { char subkey[ 512 ]; Q_snprintf( subkey, sizeof( subkey ), "%s - visible", name ); SaveViewerSettingsInt( subkey, visible ); Q_snprintf( subkey, sizeof( subkey ), "%s - locked", name ); SaveViewerSettingsInt( subkey, locked ); Q_snprintf( subkey, sizeof( subkey ), "%s - x", name ); SaveViewerSettingsInt( subkey, x ); Q_snprintf( subkey, sizeof( subkey ), "%s - y", name ); SaveViewerSettingsInt( subkey, y ); Q_snprintf( subkey, sizeof( subkey ), "%s - width", name ); SaveViewerSettingsInt( subkey, w ); Q_snprintf( subkey, sizeof( subkey ), "%s - height", name ); SaveViewerSettingsInt( subkey, h ); Q_snprintf( subkey, sizeof( subkey ), "%s - zoomed", name ); SaveViewerSettingsInt( subkey, zoomed ); } static char g_PhonemeRoot[ MAX_PATH ] = { 0 }; void FacePoser_SetPhonemeRootDir( char const *pchRootDir ) { Q_strncpy( g_PhonemeRoot, pchRootDir, sizeof( g_PhonemeRoot ) ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void FacePoser_EnsurePhonemesLoaded( void ) { // Don't bother unless a model is loaded, at least... CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr(); if ( !hdr ) { return; } char const *ext[] = { "", "_strong", "_weak", }; for ( int i = 0 ; i < ARRAYSIZE( ext ); ++i ) { char clname[ 256 ]; Q_snprintf( clname, sizeof( clname ), "%sphonemes%s", g_PhonemeRoot, ext[ i ] ); Q_FixSlashes( clname ); Q_strlower( clname ); if ( !expressions->FindClass( clname, false ) ) { char clfile[ MAX_PATH ]; Q_snprintf( clfile, sizeof( clfile ), "expressions/%sphonemes%s.txt", g_PhonemeRoot, ext[ i ] ); Q_FixSlashes( clfile ); Q_strlower( clfile ); if ( g_pFileSystem->FileExists( clfile ) ) { expressions->LoadClass( clfile ); CExpClass *cl = expressions->FindClass( clname, false ); if ( !cl ) { Con_Printf( "FacePoser_EnsurePhonemesLoaded: %s missing!!!\n", clfile ); } } } } } bool FacePoser_ShowFileNameDialog( bool openFile, char *relative, size_t bufsize, char const *subdir, char const *wildcard ) { Assert( relative ); relative[ 0 ] = 0 ; Assert( subdir ); Assert( wildcard ); char workingdir[ 256 ]; Q_getwd( workingdir, sizeof( workingdir ) ); strlwr( workingdir ); Q_FixSlashes( workingdir, '/' ); // Show file io bool inWorkingDirectoryAlready = false; if ( Q_stristr_slash( workingdir, va( "%s%s", GetGameDirectory(), subdir ) ) ) { inWorkingDirectoryAlready = true; } // Show file io const char *fullpath = NULL; if ( openFile ) { fullpath = mxGetOpenFileName( 0, inWorkingDirectoryAlready ? "." : FacePoser_MakeWindowsSlashes( va( "%s%s/", GetGameDirectory(), subdir ) ), wildcard ); } else { fullpath = mxGetSaveFileName( 0, inWorkingDirectoryAlready ? "." : FacePoser_MakeWindowsSlashes( va( "%s%s/", GetGameDirectory(), subdir ) ), wildcard ); } if ( !fullpath || !fullpath[ 0 ] ) return false; Q_strncpy( relative, fullpath, bufsize ); return true; } bool FacePoser_ShowOpenFileNameDialog( char *relative, size_t bufsize, char const *subdir, char const *wildcard ) { return FacePoser_ShowFileNameDialog( true, relative, bufsize, subdir, wildcard ); } bool FacePoser_ShowSaveFileNameDialog( char *relative, size_t bufsize, char const *subdir, char const *wildcard ) { return FacePoser_ShowFileNameDialog( false, relative, bufsize, subdir, wildcard ); } //----------------------------------------------------------------------------- // Purpose: converts an english string to unicode //----------------------------------------------------------------------------- int ConvertANSIToUnicode(const char *ansi, wchar_t *unicode, int unicodeBufferSize) { return ::MultiByteToWideChar(CP_ACP, 0, ansi, -1, unicode, unicodeBufferSize); } //----------------------------------------------------------------------------- // Purpose: converts an unicode string to an english string //----------------------------------------------------------------------------- int ConvertUnicodeToANSI(const wchar_t *unicode, char *ansi, int ansiBufferSize) { return ::WideCharToMultiByte(CP_ACP, 0, unicode, -1, ansi, ansiBufferSize, NULL, NULL); } //----------------------------------------------------------------------------- // Purpose: If FPS is set and "using grid", snap to proper fractional time value // Input : t - // Output : float //----------------------------------------------------------------------------- float FacePoser_SnapTime( float t ) { if ( !g_pChoreoView ) return t; CChoreoScene *scene = g_pChoreoView->GetScene(); if ( !scene ) return t; return scene->SnapTime( t ); } //----------------------------------------------------------------------------- // Purpose: // Input : t - // Output : char const //----------------------------------------------------------------------------- char const *FacePoser_DescribeSnappedTime( float t ) { static char desc[ 128 ]; Q_snprintf( desc, sizeof( desc ), "%.3f", t ); if ( !g_pChoreoView ) return desc; CChoreoScene *scene = g_pChoreoView->GetScene(); if ( !scene ) return desc; t = scene->SnapTime( t ); int fps = scene->GetSceneFPS(); int ipart = (int)t; int fracpart = (int)( ( t - (float)ipart ) * (float)fps + 0.5f ); int frame = ipart * fps + fracpart; if ( fracpart == 0 ) { Q_snprintf( desc, sizeof( desc ), "frame %i (time %i s.)", frame, ipart ); } else { Q_snprintf( desc, sizeof( desc ), "frame %i (time %i + %i/%i s.)", frame, ipart,fracpart, fps ); } return desc; } //----------------------------------------------------------------------------- // Purpose: // Output : int //----------------------------------------------------------------------------- int FacePoser_GetSceneFPS( void ) { if ( !g_pChoreoView ) return 1000; CChoreoScene *scene = g_pChoreoView->GetScene(); if ( !scene ) return 1000; return scene->GetSceneFPS(); } //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool FacePoser_IsSnapping( void ) { if ( !g_pChoreoView ) return false; CChoreoScene *scene = g_pChoreoView->GetScene(); if ( !scene ) return false; return scene->IsUsingFrameSnap(); } char const *FacePoser_TranslateSoundNameGender( char const *soundname, gender_t gender ) { if ( Q_stristr( soundname, ".wav" ) ) return PSkipSoundChars( soundname ); return PSkipSoundChars( soundemitter->GetWavFileForSound( soundname, gender ) ); } char const *FacePoser_TranslateSoundName( char const *soundname, StudioModel *model /*= NULL*/ ) { if ( Q_stristr( soundname, ".wav" ) ) return PSkipSoundChars( soundname ); static char temp[ 256 ]; if ( model ) { Q_strncpy( temp, PSkipSoundChars( soundemitter->GetWavFileForSound( soundname, model->GetFileName() ) ), sizeof( temp ) ); } else { Q_strncpy( temp, PSkipSoundChars( soundemitter->GetWavFileForSound( soundname, NULL ) ), sizeof( temp ) ); } return temp; } char const *FacePoser_TranslateSoundName( CChoreoEvent *event ) { char const *soundname = event->GetParameters(); if ( Q_stristr( soundname, ".wav" ) ) return PSkipSoundChars( soundname ); // See if we can figure out the .mdl associated to this event's actor static char temp[ 256 ]; temp[ 0 ] = 0; StudioModel *model = NULL; CChoreoActor *a = event->GetActor(); CChoreoScene *s = event->GetScene(); if ( a != NULL && s != NULL ) { model = FindAssociatedModel( s, a ); } Q_strncpy( temp, PSkipSoundChars( soundemitter->GetWavFileForSound( soundname, model ? model->GetFileName() : NULL ) ), sizeof( temp ) ); return temp; } #if defined( _WIN32 ) || defined( WIN32 ) #define PATHSEPARATOR(c) ((c) == '\\' || (c) == '/') #else //_WIN32 #define PATHSEPARATOR(c) ((c) == '/') #endif //_WIN32 static bool charsmatch( char c1, char c2 ) { if ( tolower( c1 ) == tolower( c2 ) ) return true; if ( PATHSEPARATOR( c1 ) && PATHSEPARATOR( c2 ) ) return true; return false; } char *Q_stristr_slash( char const *pStr, char const *pSearch ) { AssertValidStringPtr(pStr); AssertValidStringPtr(pSearch); if (!pStr || !pSearch) return 0; char const* pLetter = pStr; // Check the entire string while (*pLetter != 0) { // Skip over non-matches if ( charsmatch( *pLetter, *pSearch ) ) { // Check for match char const* pMatch = pLetter + 1; char const* pTest = pSearch + 1; while (*pTest != 0) { // We've run off the end; don't bother. if (*pMatch == 0) return 0; if ( !charsmatch( *pMatch, *pTest ) ) break; ++pMatch; ++pTest; } // Found a match! if (*pTest == 0) return (char *)pLetter; } ++pLetter; } return 0; } static CUniformRandomStream g_Random; IUniformRandomStream *random = &g_Random;