void RageFileManager::MountInitialFilesystems() { /* Add file search paths, higher priority first. */ #if defined(XBOX) RageFileManager::Mount( "dir", "D:\\", "" ); #elif defined(LINUX) /* Absolute paths. This is rarely used, eg. by Alsa9Buf::GetSoundCardDebugInfo(). * All paths that start with a slash (eg. "/proc") should use this, so put it * first. */ RageFileManager::Mount( "dir", "/", "/" ); /* We can almost do this, to have machine profiles be system-global to eg. share * scores. It would need to handle permissions properly. */ /* RageFileManager::Mount( "dir", "/var/lib/games/stepmania", "Data/Profiles" ); */ // CString Home = getenv( "HOME" ) + "/" + PRODUCT_NAME; /* * Next: path to write general mutable user data. If the above path fails (eg. * wrong permissions, doesn't exist), machine memcard data will also go in here. * XXX: It seems silly to have two ~ directories. If we're going to create a * directory on our own, it seems like it should be a dot directory, but it * seems wrong to put lots of data (eg. music) in one. Hmm. */ /* XXX: create */ /* RageFileManager::Mount( "dir", Home + "." PRODUCT_NAME, "Data" ); */ /* Next, search ~/StepMania. This is where users can put music, themes, etc. */ /* RageFileManager::Mount( "dir", Home + PRODUCT_NAME, "" ); */ /* Search for a directory with "Songs" in it. Be careful: the CWD is likely to * be ~, and it's possible that some users will have a ~/Songs/ directory that * has nothing to do with us, so check the initial directory last. */ CString Root = ""; struct stat st; if( Root == "" && !stat( DirOfExecutable + "/Songs", &st ) && st.st_mode&S_IFDIR ) Root = DirOfExecutable; if( Root == "" && !stat( InitialWorkingDirectory + "/Songs", &st ) && st.st_mode&S_IFDIR ) Root = InitialWorkingDirectory; if( Root == "" ) RageException::Throw( "Couldn't find \"Songs\"" ); RageFileManager::Mount( "dir", Root, "" ); #elif defined(_WINDOWS) /* All Windows data goes in the directory one level above the executable. */ CHECKPOINT_M( ssprintf( "DOE \"%s\"", DirOfExecutable.c_str()) ); CStringArray parts; split( DirOfExecutable, "/", parts ); CHECKPOINT_M( ssprintf( "... %i parts", parts.size()) ); ASSERT_M( parts.size() > 1, ssprintf("Strange DirOfExecutable: %s", DirOfExecutable.c_str()) ); CString Dir = join( "/", parts.begin(), parts.end()-1 ); RageFileManager::Mount( "dir", Dir, "" ); #else /* Paths relative to the CWD: */ RageFileManager::Mount( "dir", ".", "" ); #endif }
static RString GetMountDir( const RString &sDirOfExecutable ) { /* All Windows data goes in the directory one level above the executable. */ CHECKPOINT_M( ssprintf( "DOE \"%s\"", sDirOfExecutable.c_str()) ); vector<RString> asParts; split( sDirOfExecutable, "/", asParts ); CHECKPOINT_M( ssprintf( "... %i asParts", asParts.size()) ); ASSERT_M( asParts.size() > 1, ssprintf("Strange sDirOfExecutable: %s", sDirOfExecutable.c_str()) ); RString sDir = join( "/", asParts.begin(), asParts.end()-1 ); return sDir; }
bool IniFile::ReadFile( const CString &sPath ) { m_sPath = sPath; CHECKPOINT_M( ssprintf("Reading '%s'",m_sPath.c_str()) ); RageFile f; if( !f.Open( m_sPath ) ) { LOG->Trace( "Reading '%s' failed: %s", m_sPath.c_str(), f.GetError().c_str() ); m_sError = f.GetError(); return 0; } CString keyname; while( 1 ) { CString line; int ret = f.GetLine(line); if( ret == 0 ) /* eof */ return true; if( ret < 0 ) { m_sError = f.GetError(); return false; } if( line.size() >= 3 && line[0] == '\xef' && line[1] == '\xbb' && line[2] == '\xbf' ) { /* Obnoxious NT marker for UTF-8. Remove it. */ line.erase(0, 3); } if( line == "" ) continue; if( line.substr(0, 2) == "//" || line.substr(0) == "#" ) continue; /* comment */ if( line[0] == '[' && line[line.GetLength()-1] == ']' ) { /* New section. */ keyname = line.substr(1, line.size()-2); } else //if a value { int iEqualIndex = line.Find("="); if( iEqualIndex != -1 ) { CString valuename = line.Left(iEqualIndex); CString value = line.Right(line.GetLength()-valuename.GetLength()-1); SetValue(keyname,valuename,value); } } } }
void OptionRow::PrepareItemText( CString &s ) const { if( s == "" ) return; bool bTheme = false; // HACK: Always theme the NEXT_ROW and EXIT items, even if metrics says not to theme. if( s == NEXT_ROW_NAME ) bTheme = true; if( s == EXIT_NAME ) bTheme = true; if( THEME_ITEMS && m_RowDef.m_bAllowThemeItems ) bTheme = true; if( bTheme ) { if (m_RowDef.name != "Speed") s = THEME_OPTION_ITEM( s, false ); else // very hacky: we assume an ITG-related theme, so we assume a uniform way of determining // the display of speed mods. Very non-standard, but allows for flexibility of changing the options // --infamouspat { CHECKPOINT_M(s); s = THEME_OPTION_ITEM( s, true ); } } if( CAPITALIZE_ALL_OPTION_NAMES ) s.MakeUpper(); }
/* We write the cache even if we won't use it, so we don't have to recache everything * if the memory or settings change. */ void BannerCache::CacheBanner( CString BannerPath ) { if( PREFSMAN->m_BannerCache != PrefsManager::BNCACHE_LOW_RES ) return; CHECKPOINT_M( BannerPath ); if( !DoesFileExist(BannerPath) ) return; const CString CachePath = GetBannerCachePath(BannerPath); /* Check the full file hash. If it's the loaded and identical, don't recache. */ if( DoesFileExist(CachePath) ) { unsigned CurFullHash; const unsigned FullHash = GetHashForFile( BannerPath ); if( BannerData.GetValue( BannerPath, "FullHash", CurFullHash ) && CurFullHash == FullHash ) { /* It's identical. Just load it. */ LoadBanner( BannerPath ); return; } } CacheBannerInternal( BannerPath ); }
/* seemingly good start of an automagical way to mount a user pack based on the folder structure */ CString UserPackManager::GetPackMountPoint( const CString &sPack ) { enum UserPackMountType { UPACK_MOUNT_ROOT, UPACK_MOUNT_SONGS }; RageFileDriverZip *pZip = new RageFileDriverZip; CHECKPOINT_M( sPack ); // it should already be a valid zip by now... ASSERT( pZip->Load( sPack ) ); UserPackMountType upmt = UPACK_MOUNT_SONGS; CStringArray asRootEntries; pZip->GetDirListing( "/", asRootEntries, true, false ); SAFE_DELETE( pZip ); // if we find a StepMania root folder, mount it as one for( unsigned i = 0; i < asRootEntries.size(); ++i ) { for( unsigned j = 0; j < ARRAYLEN(asRootDirs); j++ ) { if ( asRootEntries[i].CompareNoCase( asRootDirs[j] ) == 0 ) return "/"; } } /* for now, assume a Songs-only pack if the root dirs aren't there */ return "/Songs"; }
std::string MovieTexture_Generic::Init() { std::string sError = m_pDecoder->Open( GetID().filename ); if( sError != "" ) return sError; CreateTexture(); CreateFrameRects(); /* Decode one frame, to guarantee that the texture is drawn when this function returns. */ int ret = m_pDecoder->DecodeFrame( -1 ); if( ret == -1 ) return fmt::sprintf( "%s: error getting first frame", GetID().filename.c_str() ); if( ret == 0 ) { /* There's nothing there. */ return fmt::sprintf( "%s: EOF getting first frame", GetID().filename.c_str() ); } m_ImageWaiting = FRAME_DECODED; LOG->Trace( "Resolution: %ix%i (%ix%i, %ix%i)", m_iSourceWidth, m_iSourceHeight, m_iImageWidth, m_iImageHeight, m_iTextureWidth, m_iTextureHeight ); UpdateFrame(); CHECKPOINT_M("Generic initialization completed. No errors found."); return std::string(); }
void StageStats::AssertValid( PlayerNumber pn ) const { if( vpSongs[0] ) CHECKPOINT_M( vpSongs[0]->GetFullTranslitTitle() ); ASSERT( vpSteps[pn][0] ); ASSERT_M( playMode < NUM_PLAY_MODES, ssprintf("playmode %i", playMode) ); ASSERT( pStyle != NULL ); ASSERT_M( vpSteps[pn][0]->GetDifficulty() < NUM_DIFFICULTIES, ssprintf("difficulty %i", vpSteps[pn][0]->GetDifficulty()) ); ASSERT( vpSongs.size() == vpSteps[pn].size() ); }
void ScreenOptionsMaster::ExportOptions( int r, const vector<PlayerNumber> &vpns ) { CHECKPOINT_M( ssprintf("%i/%i", r, int(m_pRows.size())) ); OptionRow &row = *m_pRows[r]; bool bRowHasFocus[NUM_PLAYERS]; ZERO( bRowHasFocus ); FOREACH_CONST( PlayerNumber, vpns, p ) { int iCurRow = m_iCurrentRow[*p]; bRowHasFocus[*p] = iCurRow == r; }
void DirectFilenameDB::CacheFile( const RString &sPath ) { CHECKPOINT_M( root+sPath ); RString sDir = Dirname( sPath ); FileSet *pFileSet = GetFileSet( sDir, false ); if( pFileSet == NULL ) { // This directory isn't cached so do nothing. m_Mutex.Unlock(); // Locked by GetFileSet() return; } while( !pFileSet->m_bFilled ) m_Mutex.Wait(); #if defined(WIN32) // There is almost surely a better way to do this WIN32_FIND_DATA fd; HANDLE hFind = DoFindFirstFile( root+sPath, &fd ); if( hFind == INVALID_HANDLE_VALUE ) { m_Mutex.Unlock(); // Locked by GetFileSet() return; } File f( fd.cFileName ); f.dir = !!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); f.size = fd.nFileSizeLow; f.hash = fd.ftLastWriteTime.dwLowDateTime; pFileSet->files.insert( f ); FindClose( hFind ); #else File f( Basename(sPath) ); struct stat st; if( DoStat(root+sPath, &st) == -1 ) { int iError = errno; /* If it's a broken symlink, ignore it. Otherwise, warn. */ /* Huh? */ WARN( ssprintf("File '%s' is gone! (%s)", sPath.c_str(), strerror(iError)) ); } else { f.dir = (st.st_mode & S_IFDIR); f.size = (int)st.st_size; f.hash = st.st_mtime; } pFileSet->files.insert(f); #endif m_Mutex.Unlock(); // Locked by GetFileSet() }
void StageStats::AssertValid( MultiPlayer pn ) const { ASSERT( m_vpPlayedSongs.size() != 0 ); ASSERT( m_vpPossibleSongs.size() != 0 ); if( m_vpPlayedSongs[0] ) CHECKPOINT_M( m_vpPlayedSongs[0]->GetTranslitFullTitle() ); ASSERT( m_multiPlayer[pn].m_vpPossibleSteps.size() != 0 ); ASSERT( m_multiPlayer[pn].m_vpPossibleSteps[0] != NULL ); ASSERT_M( m_playMode < NUM_PlayMode, ssprintf("playmode %i", m_playMode) ); ASSERT_M( m_player[pn].m_vpPossibleSteps[0]->GetDifficulty() < NUM_Difficulty, ssprintf("difficulty %i", m_player[pn].m_vpPossibleSteps[0]->GetDifficulty()) ); ASSERT( (int) m_vpPlayedSongs.size() == m_player[pn].m_iStepsPlayed ); ASSERT( m_vpPossibleSongs.size() == m_player[pn].m_vpPossibleSteps.size() ); }
void StageStats::AssertValid( PlayerNumber pn ) const { ASSERT( m_vpPlayedSongs.size() != 0 ); ASSERT( m_vpPossibleSongs.size() != 0 ); if( m_vpPlayedSongs[0] ) CHECKPOINT_M( m_vpPlayedSongs[0]->GetTranslitFullTitle() ); ASSERT( m_player[pn].m_iStepsPlayed > 0 ); ASSERT( m_player[pn].m_vpPossibleSteps.size() != 0 ); ASSERT( m_player[pn].m_vpPossibleSteps[0] != NULL ); ASSERT_M( m_playMode < NUM_PlayMode, ssprintf("playmode %i", m_playMode) ); ASSERT_M( m_player[pn].m_vpPossibleSteps[0]->GetDifficulty() < NUM_Difficulty, ssprintf("Invalid Difficulty %i", m_player[pn].m_vpPossibleSteps[0]->GetDifficulty()) ); ASSERT_M( (int) m_vpPlayedSongs.size() == m_player[pn].m_iStepsPlayed, ssprintf("%i Songs Played != %i Steps Played for player %i", (int)m_vpPlayedSongs.size(), (int)m_player[pn].m_iStepsPlayed, pn) ); ASSERT_M( m_vpPossibleSongs.size() == m_player[pn].m_vpPossibleSteps.size(), ssprintf("%i Possible Songs != %i Possible Steps for player %i", (int)m_vpPossibleSongs.size(), (int)m_player[pn].m_vpPossibleSteps.size(), pn) ); }
bool IniFile::ReadFile( const RString &sPath ) { m_sPath = sPath; CHECKPOINT_M( ssprintf("Reading '%s'",m_sPath.c_str()) ); RageFile f; if( !f.Open( m_sPath ) ) { LOG->Trace( "Reading '%s' failed: %s", m_sPath.c_str(), f.GetError().c_str() ); m_sError = f.GetError(); return 0; } return ReadFile( f ); }
bool BitmapText::LoadFromTextureAndChars( const CString& sTexturePath, const CString& sChars ) { CHECKPOINT_M( ssprintf("BitmapText::LoadFromTextureAndChars(\"%s\",\"%s\")", sTexturePath.c_str(), sChars.c_str()) ); if( m_pFont ) { FONT->UnloadFont( m_pFont ); m_pFont = NULL; } m_pFont = FONT->LoadFont( sTexturePath, sChars ); BuildChars(); return true; }
RageFileManager::RageFileManager( CString argv0 ) { CHECKPOINT_M( argv0 ); ChangeToDirOfExecutable( argv0 ); g_Mutex = new RageMutex("RageFileManager"); g_Mountpoints = new RageFileDriverMountpoints; LoadedDriver ld; ld.driver = g_Mountpoints; ld.MountPoint = ""; g_Drivers.push_back( ld ); /* The mount path is unused, but must be nonempty. */ RageFileManager::Mount( "mem", "(cache)", "@mem" ); }
bool BitmapText::LoadFromFont( const CString& sFontFilePath ) { CHECKPOINT_M( ssprintf("BitmapText::LoadFromFont(%s)", sFontFilePath.c_str()) ); if( m_pFont ) { FONT->UnloadFont( m_pFont ); m_pFont = NULL; } m_pFont = FONT->LoadFont( sFontFilePath ); BuildChars(); return true; }
bool BitmapText::LoadFromFont( const RString& sFontFilePath ) { CHECKPOINT_M( ssprintf("BitmapText::LoadFromFont(%s)", sFontFilePath.c_str()) ); if( m_pFont ) { FONT->UnloadFont( m_pFont ); m_pFont = NULL; } m_pFont = FONT->LoadFont( sFontFilePath ); this->SetStrokeColor( m_pFont->GetDefaultStrokeColor() ); BuildChars(); return true; }
void MovieTexture_Generic::UpdateFrame() { /* Just in case we were invalidated: */ CreateTexture(); if( m_pTextureLock != nullptr ) { int iHandle = m_pTextureIntermediate != nullptr? m_pTextureIntermediate->GetTexHandle(): this->GetTexHandle(); m_pTextureLock->Lock( iHandle, m_pSurface ); } m_pDecoder->GetFrame( m_pSurface ); if( m_pTextureLock != nullptr ) m_pTextureLock->Unlock( m_pSurface, true ); if( m_pRenderTarget != nullptr ) { CHECKPOINT_M( "About to upload the texture."); /* If we have no m_pTextureLock, we still have to upload the texture. */ if( m_pTextureLock == nullptr ) { DISPLAY->UpdateTexture( m_pTextureIntermediate->GetTexHandle(), m_pSurface, 0, 0, m_pSurface->w, m_pSurface->h ); } m_pRenderTarget->BeginRenderingTo( false ); m_pSprite->Draw(); m_pRenderTarget->FinishRenderingTo(); } else { if( m_pTextureLock == nullptr ) { DISPLAY->UpdateTexture( m_uTexHandle, m_pSurface, 0, 0, m_iImageWidth, m_iImageHeight ); } } }
void RageFileManager::Mount( CString Type, CString Root, CString MountPoint ) { LockMut( *g_Mutex ); FixSlashesInPlace( Root ); FixSlashesInPlace( MountPoint ); if( MountPoint.size() && MountPoint.Right(1) != "/" ) MountPoint += '/'; ASSERT( Root != "" ); CHECKPOINT_M( ssprintf("\"%s\", \"%s\", \"%s\"", Type.c_str(), Root.c_str(), MountPoint.c_str() ) ); // Unmount anything that was previously mounted here. Unmount( Type, Root, MountPoint ); CHECKPOINT; RageFileDriver *driver = MakeFileDriver( Type, Root ); if( !driver ) { CHECKPOINT; if( LOG ) LOG->Warn("Can't mount unknown VFS type \"%s\", root \"%s\"", Type.c_str(), Root.c_str() ); else fprintf( stderr, "Can't mount unknown VFS type \"%s\", root \"%s\"\n", Type.c_str(), Root.c_str() ); return; } CHECKPOINT; LoadedDriver ld; ld.driver = driver; ld.Type = Type; ld.Root = Root; ld.MountPoint = MountPoint; g_Drivers.push_back( ld ); CHECKPOINT; g_Mountpoints->LoadFromDrivers( g_Drivers ); CHECKPOINT; }
void RageSoundDriver::DecodeThread() { SetupDecodingThread(); while( !m_bShutdownDecodeThread ) { /* Fill each playing sound, round-robin. */ { int iSampleRate = GetSampleRate(); ASSERT_M( iSampleRate > 0, ssprintf("%i", iSampleRate) ); int iUsecs = 1000000*chunksize() / iSampleRate; usleep( iUsecs ); } LockMut( m_Mutex ); // LOG->Trace("begin mix"); for( unsigned i = 0; i < ARRAYLEN(m_Sounds); ++i ) { if( m_Sounds[i].m_State != Sound::PLAYING ) continue; Sound *pSound = &m_Sounds[i]; CHECKPOINT_M("Processing the sound while buffers are available."); while( pSound->m_Buffer.num_writable() ) { int iWrote = GetDataForSound( *pSound ); if( iWrote == RageSoundReader::WOULD_BLOCK ) break; if( iWrote < 0 ) { /* This sound is finishing. */ pSound->m_State = Sound::STOPPING; break; // LOG->Trace("mixer: (#%i) eof (%p)", i, pSound->m_pSound ); } } } // LOG->Trace("end mix"); } }
bool GetThreadBacktraceContext( uint64_t ThreadID, BacktraceContext *ctx ) { /* Can't GetThreadBacktraceContext the current thread. */ ASSERT( ThreadID != GetCurrentThreadId() ); /* Attach to the thread. This may fail with EPERM. This can happen for at least * two common reasons: the process might be in a debugger already, or *we* might * already have attached to it via SuspendThread. * * If it's in a debugger, we won't be able to ptrace(PTRACE_GETREGS). If * it's us that attached, we will. */ if( PtraceAttach( int(ThreadID) ) == -1 ) { if( errno != EPERM ) { CHECKPOINT_M( ssprintf( "%s (pid %i tid %i locking tid %i)", strerror(errno), getpid(), (int)GetCurrentThreadId(), int(ThreadID) ) ); return false; } } user_regs_struct regs; if( ptrace( PTRACE_GETREGS, pid_t(ThreadID), NULL, ®s ) == -1 ) return false; ctx->pid = pid_t(ThreadID); #if defined(CPU_X86_64) ctx->ip = (void *) regs.rip; ctx->bp = (void *) regs.rbp; ctx->sp = (void *) regs.rsp; #elif defined(CPU_X86) ctx->ip = (void *) regs.eip; ctx->bp = (void *) regs.ebp; ctx->sp = (void *) regs.esp; #else #error GetThreadBacktraceContext: which arch? #endif return true; }
void FontManager::UnloadFont( Font *fp ) { CHECKPOINT_M( ssprintf("FontManager::UnloadFont(%s).", fp->path.c_str()) ); for( std::map<FontName, Font*>::iterator i = g_mapPathToFont.begin(); i != g_mapPathToFont.end(); ++i) { if(i->second != fp) continue; i->second->m_iRefCount--; if( fp->m_iRefCount == 0 ) { delete i->second; // free the texture g_mapPathToFont.erase( i ); // and remove the key in the map } return; } RageException::Throw( "Unloaded an unknown font (%p)", fp ); }
Font* FontManager::LoadFont( const CString &sFontOrTextureFilePath, CString sChars ) { // Convert the path to lowercase so that we don't load duplicates. // Really, this does not solve the duplicate problem. We could have two copies // of the same bitmap if there are equivalent but different paths // (e.g. "graphics\blah.png" and "..\stepmania\graphics\blah.png" ). CHECKPOINT_M( ssprintf("FontManager::LoadFont(%s).", sFontOrTextureFilePath.c_str()) ); const FontName NewName( sFontOrTextureFilePath, sChars ); map<FontName, Font*>::iterator p = g_mapPathToFont.find( NewName ); if( p != g_mapPathToFont.end() ) { Font *pFont=p->second; pFont->m_iRefCount++; return pFont; } Font *f = new Font; f->Load(sFontOrTextureFilePath, sChars); g_mapPathToFont[NewName] = f; return f; }
void BannerCache::LoadBanner( CString BannerPath ) { if( PREFSMAN->m_BannerCache != PrefsManager::BNCACHE_LOW_RES || BannerPath == "" ) return; /* Load it. */ const CString CachePath = GetBannerCachePath(BannerPath); for( int tries = 0; tries < 2; ++tries ) { if( g_BannerPathToImage.find(BannerPath) != g_BannerPathToImage.end() ) return; /* already loaded */ CHECKPOINT_M( ssprintf( "BannerCache::LoadBanner: %s", CachePath.c_str() ) ); RageSurface *img = RageSurfaceUtils::LoadSurface( CachePath ); if( img == NULL ) { if(tries == 0) { /* The file doesn't exist. It's possible that the banner cache file is * missing, so try to create it. Don't do this first, for efficiency. */ LOG->Trace( "Cached banner load of '%s' ('%s') failed, trying to cache ...", BannerPath.c_str(), CachePath.c_str() ); /* Skip the up-to-date check; it failed to load, so it can't be up * to date. */ CacheBannerInternal( BannerPath ); continue; } else { LOG->Trace( "Cached banner load of '%s' ('%s') failed", BannerPath.c_str(), CachePath.c_str() ); return; } } g_BannerPathToImage[BannerPath] = img; } }
void FontManager::UnloadFont( Font *fp ) { CHECKPOINT_M( ssprintf("FontManager::UnloadFont(%s).", fp->path.c_str()) ); for( std::map<FontName, Font*>::iterator i = g_mapPathToFont.begin(); i != g_mapPathToFont.end(); ++i) { if(i->second != fp) continue; ASSERT_M(fp->m_iRefCount > 0,"Attempting to unload a font with zero ref count!"); i->second->m_iRefCount--; if( fp->m_iRefCount == 0 ) { delete i->second; // free the texture g_mapPathToFont.erase( i ); // and remove the key in the map } return; } FAIL_M( ssprintf("Unloaded an unknown font (%p)", fp) ); }
int RageSoundReader_Vorbisfile::Read( float *buf, int iFrames ) { int frames_read = 0; while( iFrames && !eof ) { const int bytes_per_frame = sizeof(float)*channels; int iFramesRead = 0; { int curofs = (int) ov_pcm_tell(vf); if( curofs < read_offset ) { /* The timestamps moved backwards. Ignore it. This file probably * won't sync correctly. */ LOG->Trace( "p ahead %p %i < %i, we're ahead by %i", this, curofs, read_offset, read_offset-curofs ); read_offset = curofs; } else if( curofs > read_offset ) { /* Our offset doesn't match. We have a hole in the data, or corruption. * If we're reading with accurate syncing, insert silence to line it up. * That way, corruptions in the file won't casue desyncs. */ /* In bytes: */ int iSilentFrames = curofs - read_offset; iSilentFrames = min( iSilentFrames, (int) iFrames ); int silence = iSilentFrames * bytes_per_frame; CHECKPOINT_M( ssprintf("p %i,%i: %i frames of silence needed", curofs, read_offset, silence) ); memset( buf, 0, silence ); iFramesRead = iSilentFrames; } } if( iFramesRead == 0 ) { int bstream; #if defined(INTEGER_VORBIS) int ret = ov_read( vf, (char *) buf, iFrames * channels * sizeof(int16_t), &bstream ); #else // float vorbis decoder float **pcm; int ret = ov_read_float( vf, &pcm, iFrames, &bstream ); #endif { vorbis_info *vi = ov_info( vf, -1 ); ASSERT( vi != NULL ); if( (unsigned) vi->channels != channels ) RageException::Throw( "File \"%s\" changes channel count from %i to %i; not supported.", filename.c_str(), channels, (int)vi->channels ); } if( ret == OV_HOLE ) continue; if( ret == OV_EBADLINK ) { SetError( ssprintf("Read: OV_EBADLINK") ); return ERROR; } if( ret == 0 ) { eof = true; continue; } #if defined(INTEGER_VORBIS) if( ret > 0 ) { int iSamplesRead = ret / sizeof(int16_t); iFramesRead = iSamplesRead / channels; /* Convert in reverse, so we can do it in-place. */ const int16_t *pIn = (int16_t *) buf; float *pOut = (float *) buf; for( int i = iSamplesRead-1; i >= 0; --i ) pOut[i] = pIn[i] / 32768.0f; } #else if( ret > 0 ) { iFramesRead = ret; int iNumChannels = channels; for( int iChannel = 0; iChannel < iNumChannels; ++iChannel ) { const float *pChannelIn = pcm[iChannel]; float *pChannelOut = &buf[iChannel]; for( int i = 0; i < iFramesRead; ++i ) { *pChannelOut = *pChannelIn; ++pChannelIn; pChannelOut += iNumChannels; } } } #endif } read_offset += iFramesRead; buf += iFramesRead * channels; frames_read += iFramesRead; iFrames -= iFramesRead; } if( !frames_read ) return END_OF_FILE; return frames_read; }
static LRESULT CALLBACK GraphicsWindow_WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { CHECKPOINT_M( ssprintf("%p, %u, %08x, %08x", hWnd, msg, wParam, lParam) ); // Suppress autorun. if( msg == g_iQueryCancelAutoPlayMessage ) return true; switch( msg ) { case WM_ACTIVATE: { const bool bInactive = (LOWORD(wParam) == WA_INACTIVE); const bool bMinimized = (HIWORD(wParam) != 0); const bool bHadFocus = g_bHasFocus; g_bHasFocus = !bInactive && !bMinimized; LOG->Trace( "WM_ACTIVATE (%i, %i): %s", bInactive, bMinimized, g_bHasFocus? "has focus":"doesn't have focus" ); if( !g_bHasFocus ) { RString sName = GetNewWindow(); static set<RString> sLostFocusTo; sLostFocusTo.insert( sName ); RString sStr; for( set<RString>::const_iterator it = sLostFocusTo.begin(); it != sLostFocusTo.end(); ++it ) sStr += (sStr.size()?", ":"") + *it; LOG->MapLog( "LOST_FOCUS", "Lost focus to: %s", sStr.c_str() ); } if( !g_bD3D && !g_CurrentParams.windowed && !g_bRecreatingVideoMode ) { /* In OpenGL (not D3D), it's our job to unset and reset the * full-screen video mode when we focus changes, and to hide * and show the window. Hiding is done in WM_KILLFOCUS, * because that's where most other apps seem to do it. */ if( g_bHasFocus && !bHadFocus ) { ChangeDisplaySettings( &g_FullScreenDevMode, CDS_FULLSCREEN ); ShowWindow( g_hWndMain, SW_SHOWNORMAL ); } else if( !g_bHasFocus && bHadFocus ) { ChangeDisplaySettings( NULL, 0 ); } } return 0; } case WM_KILLFOCUS: if( !g_bD3D && !g_CurrentParams.windowed && !g_bRecreatingVideoMode ) ShowWindow( g_hWndMain, SW_SHOWMINNOACTIVE ); break; /* Is there any reason we should care what size the user resizes * the window to? (who? -aj) * Short answer: yes. -aj */ // case WM_GETMINMAXINFO: case WM_SETCURSOR: if( !g_CurrentParams.windowed ) { SetCursor( NULL ); return 1; } break; case WM_SYSCOMMAND: switch( wParam&0xFFF0 ) { case SC_MONITORPOWER: case SC_SCREENSAVE: return 0; } break; case WM_PAINT: { PAINTSTRUCT ps; BeginPaint( hWnd, &ps ); EndPaint( hWnd, &ps ); break; } case WM_KEYDOWN: case WM_KEYUP: case WM_SYSKEYDOWN: case WM_SYSKEYUP: case WM_MOUSEWHEEL: // might want to use this for GET_WHEEL_DELTA_WPARAM(wParam) -aj // We handle all input ourself, via DirectInput. return 0; case WM_CLOSE: LOG->Trace("WM_CLOSE: shutting down"); ArchHooks::SetUserQuit(); return 0; case WM_WINDOWPOSCHANGED: { /* If we're fullscreen and don't have focus, our window is hidden, * so GetClientRect isn't meaningful. */ if( !g_CurrentParams.windowed && !g_bHasFocus ) break; RECT rect; GetClientRect( hWnd, &rect ); int iWidth = rect.right - rect.left; int iHeight = rect.bottom - rect.top; if( g_CurrentParams.width != iWidth || g_CurrentParams.height != iHeight ) { g_CurrentParams.width = iWidth; g_CurrentParams.height = iHeight; g_bResolutionChanged = true; } break; } case WM_COPYDATA: { PCOPYDATASTRUCT pMyCDS = (PCOPYDATASTRUCT) lParam; RString sCommandLine( (char*)pMyCDS->lpData, pMyCDS->cbData ); CommandLineActions::CommandLineArgs args; split( sCommandLine, "|", args.argv, false ); CommandLineActions::ToProcess.push_back( args ); break; } } CHECKPOINT_M( ssprintf("%p, %u, %08x, %08x", hWnd, msg, wParam, lParam) ); if( m_bWideWindowClass ) return DefWindowProcW( hWnd, msg, wParam, lParam ); else return DefWindowProcA( hWnd, msg, wParam, lParam ); }
static void StartMusic( MusicToPlay &ToPlay ) { CHECKPOINT; LockMutex L( *g_Mutex ); CHECKPOINT; if( g_Playing->m_Music->IsPlaying() && !g_Playing->m_Music->GetLoadedFilePath().CompareNoCase(ToPlay.file) ) return; CHECKPOINT; if( ToPlay.file.empty() ) { /* StopPlaying() can take a while, so don't hold the lock while we stop the sound. * Be sure to leave the rest of g_Playing in place. */ RageSound *pOldSound = g_Playing->m_Music; g_Playing->m_Music = new RageSound; L.Unlock(); /* We're not allowed to delete the sound in a separate thread, because * RageSoundManager::FlushPosMapQueue might be running. Stop the sound, * and give it to RageSoundManager to delete. */ SOUNDMAN->DeleteSound( pOldSound ); return; } CHECKPOINT; /* Unlock, load the sound here, and relock. Loading may take a while if we're * reading from CD and we have to seek far, which can throw off the timing below. */ MusicPlaying *NewMusic; { g_Mutex->Unlock(); RageSound *pSound = new RageSound; pSound->Load( ToPlay.file, false ); g_Mutex->Lock(); NewMusic = new MusicPlaying( pSound ); } NewMusic->m_Timing = g_Playing->m_Timing; CHECKPOINT; /* See if we can find timing data, if it's not already loaded. */ if( !ToPlay.HasTiming && IsAFile(ToPlay.timing_file) ) { LOG->Trace("Found '%s'", ToPlay.timing_file.c_str()); if( SMLoader::LoadTimingFromFile( ToPlay.timing_file, ToPlay.timing_data ) ) ToPlay.HasTiming = true; } CHECKPOINT; if( ToPlay.HasTiming ) NewMusic->m_NewTiming = ToPlay.timing_data; if( ToPlay.align_beat && ToPlay.HasTiming && ToPlay.force_loop && ToPlay.length_sec != -1 ) { /* Extend the loop period so it always starts and ends on the same fractional * beat. That is, if it starts on beat 1.5, and ends on beat 10.2, extend it * to end on beat 10.5. This way, effects always loop cleanly. */ float fStartBeat = NewMusic->m_NewTiming.GetBeatFromElapsedTime( ToPlay.start_sec ); float fEndSec = ToPlay.start_sec + ToPlay.length_sec; float fEndBeat = NewMusic->m_NewTiming.GetBeatFromElapsedTime( fEndSec ); const float fStartBeatFraction = fmodfp( fStartBeat, 1 ); const float fEndBeatFraction = fmodfp( fEndBeat, 1 ); float fBeatDifference = fStartBeatFraction - fEndBeatFraction; if( fBeatDifference < 0 ) fBeatDifference += 1.0f; /* unwrap */ fEndBeat += fBeatDifference; const float fRealEndSec = NewMusic->m_NewTiming.GetElapsedTimeFromBeat( fEndBeat ); const float fNewLengthSec = fRealEndSec - ToPlay.start_sec; /* Extend the fade_len, so the added time is faded out. */ ToPlay.fade_len += fNewLengthSec - ToPlay.length_sec; ToPlay.length_sec = fNewLengthSec; } CHECKPOINT; bool StartImmediately = false; if( !ToPlay.HasTiming ) { /* This song has no real timing data. The offset is arbitrary. Change it so * the beat will line up to where we are now, so we don't have to delay. */ float fDestBeat = fmodfp( GAMESTATE->m_fSongBeat, 1 ); CHECKPOINT_M(ssprintf("%f",GAMESTATE->m_fSongBeat)); CHECKPOINT_M(ssprintf("%p",NewMusic)); float fTime = NewMusic->m_NewTiming.GetElapsedTimeFromBeat( fDestBeat ); NewMusic->m_NewTiming.m_fBeat0OffsetInSeconds = fTime; StartImmediately = true; } CHECKPOINT; /* If we have an active timer, try to start on the next update. Otherwise, * start now. */ if( !g_Playing->m_HasTiming && !g_UpdatingTimer ) StartImmediately = true; if( !ToPlay.align_beat ) StartImmediately = true; CHECKPOINT; RageTimer when; /* zero */ if( !StartImmediately ) { /* GetPlayLatency returns the minimum time until a sound starts. That's * common when starting a precached sound, but our sound isn't, so it'll * probably take a little longer. Nudge the latency up. */ const float PresumedLatency = SOUND->GetPlayLatency() + 0.040f; const float fCurSecond = GAMESTATE->m_fMusicSeconds + PresumedLatency; const float fCurBeat = g_Playing->m_Timing.GetBeatFromElapsedTime( fCurSecond ); const float fCurBeatFraction = fmodfp( fCurBeat,1 ); /* The beat that the new sound will start on. */ const float fStartBeat = NewMusic->m_NewTiming.GetBeatFromElapsedTime( ToPlay.start_sec ); float fStartBeatFraction = fmodfp( fStartBeat, 1 ); if( fStartBeatFraction < fCurBeatFraction ) fStartBeatFraction += 1.0f; /* unwrap */ const float fCurBeatToStartOn = truncf(fCurBeat) + fStartBeatFraction; const float fSecondToStartOn = g_Playing->m_Timing.GetElapsedTimeFromBeat( fCurBeatToStartOn ); const float fMaximumDistance = 2; const float fDistance = min( fSecondToStartOn - fCurSecond, fMaximumDistance ); when = GAMESTATE->m_LastBeatUpdate + PresumedLatency + fDistance; } CHECKPOINT; /* Important: don't hold the mutex while we load and seek the actual sound. */ L.Unlock(); { NewMusic->m_HasTiming = ToPlay.HasTiming; if( ToPlay.HasTiming ) NewMusic->m_NewTiming = ToPlay.timing_data; NewMusic->m_TimingDelayed = true; // NewMusic->m_Music->Load( ToPlay.file, false ); RageSoundParams p; p.m_StartSecond = ToPlay.start_sec; p.m_LengthSeconds = ToPlay.length_sec; p.m_FadeLength = ToPlay.fade_len; p.StartTime = when; if( ToPlay.force_loop ) p.StopMode = RageSoundParams::M_LOOP; NewMusic->m_Music->SetParams( p ); NewMusic->m_Music->SetPositionSeconds( p.m_StartSecond ); NewMusic->m_Music->StartPlaying(); } CHECKPOINT; LockMut( *g_Mutex ); delete g_Playing; g_Playing = NewMusic; CHECKPOINT; }
void GameSoundManager::Update( float fDeltaTime ) { LockMut( *g_Mutex ); if( !g_UpdatingTimer ) return; const float fAdjust = GetFrameTimingAdjustment( fDeltaTime ); if( !g_Playing->m_Music->IsPlaying() ) { /* There's no song playing. Fake it. */ CHECKPOINT_M( ssprintf("%f, delta %f", GAMESTATE->m_fMusicSeconds, fDeltaTime) ); GAMESTATE->UpdateSongPosition( GAMESTATE->m_fMusicSeconds + fDeltaTime, g_Playing->m_Timing ); return; } /* There's a delay between us calling Play() and the sound actually playing. * During this time, approximate will be true. Keep using the previous timing * data until we get a non-approximate time, indicating that the sound has actually * started playing. */ bool approximate; RageTimer tm; const float fSeconds = g_Playing->m_Music->GetPositionSeconds( &approximate, &tm ); if( g_Playing->m_TimingDelayed ) { if( approximate ) { /* We're still waiting for the new sound to start playing, so keep using the * old timing data and fake the time. */ CHECKPOINT_M( ssprintf("%f, delta %f", GAMESTATE->m_fMusicSeconds, fDeltaTime) ); GAMESTATE->UpdateSongPosition( GAMESTATE->m_fMusicSeconds + fDeltaTime, g_Playing->m_Timing ); return; } else { /* We've passed the start position of the new sound, so we should be OK. * Load up the new timing data. */ g_Playing->m_Timing = g_Playing->m_NewTiming; g_Playing->m_TimingDelayed = false; } } else if( PREFSMAN->m_bLogSkips ) { const float fExpectedTimePassed = (tm - GAMESTATE->m_LastBeatUpdate) * g_Playing->m_Music->GetPlaybackRate(); const float fSoundTimePassed = fSeconds - GAMESTATE->m_fMusicSeconds; const float fDiff = fExpectedTimePassed - fSoundTimePassed; static CString sLastFile = ""; const CString ThisFile = g_Playing->m_Music->GetLoadedFilePath(); /* If fSoundTimePassed < 0, the sound has probably looped. */ if( sLastFile == ThisFile && fSoundTimePassed >= 0 && fabsf(fDiff) > 0.003f ) LOG->Trace("Song position skip in %s: expected %.3f, got %.3f (cur %f, prev %f) (%.3f difference)", Basename(ThisFile).c_str(), fExpectedTimePassed, fSoundTimePassed, fSeconds, GAMESTATE->m_fMusicSeconds, fDiff ); sLastFile = ThisFile; } CHECKPOINT_M( ssprintf("%f", fSeconds) ); GAMESTATE->UpdateSongPosition( fSeconds+fAdjust, g_Playing->m_Timing, tm+fAdjust ); }
void DirectFilenameDB::PopulateFileSet( FileSet &fs, const RString &path ) { RString sPath = path; #if defined(XBOX) /* Xbox doesn't handle path names which end with ".", which are used when using an * alternative song directory */ if( sPath.size() > 0 && sPath.Right(1) == "." ) sPath.erase( sPath.size() - 1 ); #endif /* Resolve path cases (path/Path -> PATH/path). */ ResolvePath( sPath ); fs.age.GetDeltaTime(); /* reset */ fs.files.clear(); #if defined(WIN32) WIN32_FIND_DATA fd; if ( sPath.size() > 0 && sPath.Right(1) == "/" ) sPath.erase( sPath.size() - 1 ); HANDLE hFind = DoFindFirstFile( root+sPath+"/*", &fd ); CHECKPOINT_M( root+sPath+"/*" ); if( hFind == INVALID_HANDLE_VALUE ) return; do { if( !strcmp(fd.cFileName, ".") || !strcmp(fd.cFileName, "..") ) continue; File f; f.SetName( fd.cFileName ); f.dir = !!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); f.size = fd.nFileSizeLow; f.hash = fd.ftLastWriteTime.dwLowDateTime; fs.files.insert( f ); } while( FindNextFile( hFind, &fd ) ); FindClose( hFind ); #else /* Ugly: POSIX threads are not guaranteed to have per-thread cwds, and only * a few systems have openat() or equivalent; one or the other is needed * to do efficient, thread-safe directory traversal. Instead, we have to * use absolute paths, which forces the system to re-parse the directory * for each file. This isn't a major issue, since most large directory * scans are I/O-bound. */ DIR *pDir = opendir(root+sPath); if( pDir == NULL ) return; while( struct dirent *pEnt = readdir(pDir) ) { if( !strcmp(pEnt->d_name, ".") ) continue; if( !strcmp(pEnt->d_name, "..") ) continue; File f( pEnt->d_name ); struct stat st; if( DoStat(root+sPath + "/" + pEnt->d_name, &st) == -1 ) { int iError = errno; /* If it's a broken symlink, ignore it. Otherwise, warn. */ if( lstat(root+sPath + "/" + pEnt->d_name, &st) == 0 ) continue; /* Huh? */ WARN( ssprintf("Got file '%s' in '%s' from list, but can't stat? (%s)", pEnt->d_name, sPath.c_str(), strerror(iError)) ); continue; } else { f.dir = (st.st_mode & S_IFDIR); f.size = (int)st.st_size; f.hash = st.st_mtime; } fs.files.insert(f); } closedir( pDir ); #endif /* * Check for any ".ignore" markers. If a marker exists, hide the marker and its * corresponding file. * For example, if "file.xml.ignore" exists, hide both it and "file.xml" by * removing them from the file set. * Ignore markers are used for convenience during build staging and are not used in * performance-critical situations. To avoid incurring some of the overheard * due to ignore markers, delete the file instead instead of using an ignore marker. */ static const RString IGNORE_MARKER_BEGINNING = "ignore-"; vector<RString> vsFilesToRemove; for( set<File>::iterator iter = fs.files.lower_bound(IGNORE_MARKER_BEGINNING); iter != fs.files.end(); ++iter ) { if( !BeginsWith( iter->lname, IGNORE_MARKER_BEGINNING ) ) break; RString sFileLNameToIgnore = iter->lname.Right( iter->lname.length() - IGNORE_MARKER_BEGINNING.length() ); vsFilesToRemove.push_back( iter->name ); vsFilesToRemove.push_back( sFileLNameToIgnore ); } FOREACH_CONST( RString, vsFilesToRemove, iter ) { // Erase the file corresponding to the ignore marker File fileToDelete; fileToDelete.SetName( *iter ); set<File>::iterator iter2 = fs.files.find( fileToDelete ); if( iter2 != fs.files.end() ) fs.files.erase( iter2 ); }