#include "cbase.h" #include "asw_vgui_music_importer.h" #include "filesystem.h" #include "strtools.h" #include "fmtstr.h" #include "c_asw_jukebox.h" #include "gamestringpool.h" const char *g_ID3MusicGenres[] = { "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", "Acapella", "Euro-House", "Dance Hall" }; unsigned int g_ID3SupportedVersions[] = { 2, 3, 4, }; int ReadID3FrameSize( int nVersion, byte **ppBuffer, unsigned int unBufferSize ) { int nBytesToRead = nVersion < 3 ? 3 : 4; int nBitsToShift = nVersion < 4 ? 8 : 7; // Read the ridiculous tag size format int nTagSize = 0; for( int i=0; i= 3 ) pCurr += 2; // Check for a text frame if( *szLabel == 'T' ) { const int nMaxStringLen = 64; // Only care about unicode (the first byte in a text frame, all versions) if( *pCurr == 0 ) { if( !Q_strncmp( szLabel, "TP1", 3 ) || !Q_strncmp( szLabel, "TOLY", 4 ) || !Q_strncmp( szLabel, "TOPE", 4 ) || !Q_strncmp( szLabel, "TPE1", 4 ) || !Q_strncmp( szLabel, "TPE2", 4 ) ) { // Read the artist char szTemp[ nMaxStringLen ]; Q_strncpy( szTemp, (char*)( pCurr + 1 ), ( nMaxStringLen > nFrameSize ) ? nFrameSize : nMaxStringLen ); szArtistName = (char*)AllocPooledString( szTemp ); } else if( !Q_strncmp( szLabel, "TT2", 3 ) || !Q_strncmp( szLabel, "TIT2", 4 ) ) { // Read the title char szTemp[ nMaxStringLen ]; Q_strncpy( szTemp, (char*)( pCurr + 1 ), ( nMaxStringLen > nFrameSize ) ? nFrameSize : nMaxStringLen ); szTrackName = (char*)AllocPooledString( szTemp ); } else if( !Q_strncmp( szLabel, "TAL", 3 ) ) { // Read the album char szTemp[ nMaxStringLen ]; Q_strncpy( szTemp, (char*)( pCurr + 1 ), ( nMaxStringLen > nFrameSize ) ? nFrameSize : nMaxStringLen ); szAlbumName = (char*)AllocPooledString( szTemp ); } else if( !Q_strncmp( szLabel, "TCO", 3 ) ) { // In newer versions, make sure it's a TCON frame if( unMajorVersion > 2 && !Q_strncmp( szLabel, "TCON", 4 ) || unMajorVersion <= 2 ) { // Get the genre if( *( pCurr + 1) == '(' ) { const char *szGenreIndex = (char*)( pCurr + 2 ); int nGenre = atoi( szGenreIndex ); if( nGenre < ARRAYSIZE(g_ID3MusicGenres) ) szGenre = (char*)g_ID3MusicGenres[ nGenre ]; else szGenre = "UNKNOWN"; } } } } } // Advance to the next frame pCurr += nFrameSize; assert( pCurr < pBuffer + unBufferSize ); } return true; } ID3Info_t::ID3Info_t( void ) : szAlbumName( null ) , szArtistName( null ) , szGenre( null ) , szTrackName( null ) {} MusicImporterDialog::MusicImporterDialog( Panel *parent, const char *title, vgui::FileOpenDialogType_t type, KeyValues *pContextKeyValues /*= 0 */ ) : FileOpenDialog(parent, title, type, pContextKeyValues ) { AddActionSignalTarget( this ); } MusicImporterDialog::~MusicImporterDialog() { } void MusicImporterDialog::OnMusicItemSelected( KeyValues *pInfo ) { if( !pInfo ) return; // Parse the keyvalues const char* szDirectory = pInfo->GetString("activedirectory"); KeyValues *pSelections = pInfo->FindKey("selectedfiles"); if( !pSelections || !szDirectory ) return; for( KeyValues *pSub = pSelections->GetFirstSubKey(); pSub; pSub = pSub->GetNextKey() ) { const char* szAttributes = pSub->GetString("attributes"); if( !szAttributes ) continue; const char* szFilename = CFmtStr( "%s%s", szDirectory, pSub->GetString("text")); //char szOutFilename[MAX_PATH]; // If the item selected was a directory, search it recursively for music if( strstr( szAttributes, "D" ) ) { ImportAllMusicInDirectory( szFilename ); continue; } ImportMusic( pSub->GetString("text"), szDirectory ); } PostActionSignal( new KeyValues( "MusicImportComplete" ) ); } void MusicImporterDialog::ImportMusic( const char* szSrcFilename, const char *szDirectory ) { char fn[ 512 ]; Q_snprintf( fn, sizeof( fn ), "%s%s", szDirectory, szSrcFilename ); // Get temp filename from crc CRC32_t crc; CRC32_Init( &crc ); CRC32_ProcessBuffer( &crc, fn, Q_strlen( fn ) ); CRC32_Final( &crc ); char hexname[ 16 ]; Q_binarytohex( (const byte *)&crc, sizeof( crc ), hexname, sizeof( hexname ) ); char hexfilename[ 512 ]; Q_snprintf( hexfilename, sizeof( hexfilename ), "sound/music/_mp3/%s.mp3", hexname ); Q_FixSlashes( hexfilename ); if ( g_pFullFileSystem->FileExists( fn ) ) { // Make a local copy char mp3_temp_path[ 512 ]; Q_snprintf( mp3_temp_path, sizeof( mp3_temp_path ), "sound/music/_mp3" ); g_pFullFileSystem->CreateDirHierarchy( mp3_temp_path, "MOD" ); char destpath[ 512 ]; Q_snprintf( destpath, sizeof( destpath ), "%s/%s", engine->GetGameDirectory(), hexfilename ); Q_FixSlashes( destpath ); FileHandle_t hFile = g_pFullFileSystem->Open( fn, "rb", "MOD" ); FileHandle_t hFileOut = g_pFullFileSystem->Open( destpath, "wb", "MOD" ); if ( FILESYSTEM_INVALID_HANDLE != hFile && FILESYSTEM_INVALID_HANDLE != hFileOut ) { unsigned int unSize = g_pFullFileSystem->Size( hFile ); byte *pBuffer = (byte*) malloc( unSize ); if ( g_pFullFileSystem->Read( pBuffer, unSize, hFile ) == (int) unSize ) { g_pFullFileSystem->Write( pBuffer, unSize, hFileOut ); } // Read ID3 header info ID3Info_t id3Header; id3Header.Deserialize( pBuffer, unSize ); g_ASWJukebox.AddMusicToPlaylist( szSrcFilename, hexname, id3Header.szAlbumName, id3Header.szArtistName, id3Header.szGenre ); free( pBuffer ); g_pFullFileSystem->Close( hFile ); g_pFullFileSystem->Close( hFileOut ); } } } void MusicImporterDialog::ImportAllMusicInDirectory( const char *szDirectory ) { // Check to make sure the specified directory exists if( !g_pFullFileSystem->IsDirectory( szDirectory ) ) return; // Search the directory structure. const char *musicwildcard = CFmtStr( "%s%s", szDirectory, "/*.mp3"); FileFindHandle_t findHandle; char const *findfn = filesystem->FindFirst(musicwildcard, &findHandle); while ( findfn ) { //const char*szFilename = CFmtStr( "%s/%s", szDirectory, findfn ); ImportMusic( szDirectory, findfn ); findfn = filesystem->FindNext( findHandle ); } filesystem->FindClose(findHandle); } vgui::DHANDLE g_hMusicImportDialog; void MusicImporterDialog::OpenImportDialog( Panel *pParent ) { if (g_hMusicImportDialog.Get() == NULL) { g_hMusicImportDialog = new MusicImporterDialog( NULL, "#asw_music_import_dialog", vgui::FOD_OPEN_MULTIPLE, NULL); g_hMusicImportDialog->AddFilter("*.mp3", "#asw_music_types", true); } if( pParent ) g_hMusicImportDialog->AddActionSignalTarget( pParent ); g_hMusicImportDialog->DoModal(false); g_hMusicImportDialog->Activate(); } void MusicImporterDialog::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); SetAlpha( 255 ); } void asw_open_music_dialog_f( void ) { MusicImporterDialog::OpenImportDialog( null ); } ConCommand asw_open_music_dialog( "asw_open_music_dialog", asw_open_music_dialog_f, "Shows a dialog for picking custom combat music", FCVAR_DEVELOPMENTONLY );