Counter Strike : Global Offensive Source Code
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.

409 lines
11 KiB

  1. //====== Copyright 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "winlite.h"
  7. #include "mpafile.h"
  8. #include "soundchars.h"
  9. #include "tier1/utlrbtree.h"
  10. // NOTE: This has to be the last file included!
  11. #include "tier0/memdbgon.h"
  12. // exception class
  13. CMPAException::CMPAException(ErrorIDs ErrorID, const char *szFile, const char *szFunction, bool bGetLastError ) :
  14. m_ErrorID( ErrorID ), m_bGetLastError( bGetLastError )
  15. {
  16. m_szFile = strdup(szFile);
  17. m_szFunction = strdup(szFunction);
  18. }
  19. // copy constructor (necessary for exception throwing without pointers)
  20. CMPAException::CMPAException(const CMPAException& Source)
  21. {
  22. m_ErrorID = Source.m_ErrorID;
  23. m_bGetLastError = Source.m_bGetLastError;
  24. m_szFile = strdup(Source.m_szFile);
  25. m_szFunction = strdup(Source.m_szFunction);
  26. }
  27. // destructor
  28. CMPAException::~CMPAException()
  29. {
  30. if( m_szFile )
  31. free( (void*)m_szFile );
  32. if( m_szFunction )
  33. free( (void*)m_szFunction );
  34. }
  35. // should be in resource file for multi language applications
  36. const char *m_szErrors[] =
  37. {
  38. "Can't open the file.",
  39. "Can't set file position.",
  40. "Can't read from file.",
  41. "Reached end of buffer.",
  42. "No VBR Header found.",
  43. "Incomplete VBR Header.",
  44. "No subsequent frame found within tolerance range.",
  45. "No frame found."
  46. };
  47. #define MAX_ERR_LENGTH 256
  48. void CMPAException::ShowError()
  49. {
  50. char szErrorMsg[MAX_ERR_LENGTH] = {0};
  51. char szHelp[MAX_ERR_LENGTH];
  52. // this is not buffer-overflow-proof!
  53. if( m_szFunction )
  54. {
  55. sprintf( szHelp, _T("%s: "), m_szFunction );
  56. strcat( szErrorMsg, szHelp );
  57. }
  58. if( m_szFile )
  59. {
  60. sprintf( szHelp, _T("'%s'\n"), m_szFile );
  61. strcat( szErrorMsg, szHelp );
  62. }
  63. strcat( szErrorMsg, m_szErrors[m_ErrorID] );
  64. #if defined(WIN32) && !defined(_X360)
  65. if( m_bGetLastError )
  66. {
  67. // get error message of last system error id
  68. LPVOID pMsgBuf;
  69. if ( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  70. NULL,
  71. GetLastError(),
  72. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  73. (LPTSTR) &pMsgBuf,
  74. 0,
  75. NULL ))
  76. {
  77. strcat( szErrorMsg, "\n" );
  78. strcat( szErrorMsg, (const char *)pMsgBuf );
  79. LocalFree( pMsgBuf );
  80. }
  81. }
  82. #endif
  83. // show error message
  84. Warning( "%s\n", szErrorMsg );
  85. }
  86. // 1KB is inital buffersize, each time the buffer needs to be increased it is doubled
  87. const uint32 CMPAFile::m_dwInitBufferSize = 1024;
  88. CMPAFile::CMPAFile( const char * szFile, uint32 dwFileOffset, FileHandle_t hFile ) :
  89. m_pBuffer(NULL), m_dwBufferSize(0), m_dwBegin( dwFileOffset ), m_dwEnd(0),
  90. m_dwNumTimesRead(0), m_bVBRFile( false ), m_pVBRHeader(NULL), m_bMustReleaseFile( false ),
  91. m_pMPAHeader(NULL), m_hFile( hFile ), m_szFile(NULL), m_dwFrameNo(1)
  92. {
  93. // open file, if not already done
  94. if( m_hFile == FILESYSTEM_INVALID_HANDLE )
  95. {
  96. Open( szFile );
  97. m_bMustReleaseFile = true;
  98. }
  99. // save filename
  100. m_szFile = strdup( szFile );
  101. // set end of MPEG data (assume file end)
  102. if( m_dwEnd <= 0 )
  103. {
  104. // get file size
  105. m_dwEnd = g_pFullFileSystem->Size( m_hFile );
  106. }
  107. // find first valid MPEG frame
  108. m_pMPAHeader = new CMPAHeader( this );
  109. // is VBR header available?
  110. CVBRHeader::VBRHeaderType HeaderType = CVBRHeader::NoHeader;
  111. uint32 dwOffset = m_pMPAHeader->m_dwSyncOffset;
  112. if( CVBRHeader::IsVBRHeaderAvailable( this, HeaderType, dwOffset ) )
  113. {
  114. try
  115. {
  116. // read out VBR header
  117. m_pVBRHeader = new CVBRHeader( this, HeaderType, dwOffset );
  118. m_bVBRFile = true;
  119. m_dwBytesPerSec = m_pVBRHeader->m_dwBytesPerSec;
  120. if( m_pVBRHeader->m_dwBytes > 0 )
  121. m_dwEnd = m_dwBegin + m_pVBRHeader->m_dwBytes;
  122. }
  123. catch(CMPAException& Exc)
  124. {
  125. Exc.ShowError();
  126. }
  127. }
  128. if( !m_pVBRHeader )
  129. {
  130. // always skip empty (32kBit) frames
  131. m_bVBRFile = m_pMPAHeader->SkipEmptyFrames();
  132. m_dwBytesPerSec = m_pMPAHeader->GetBytesPerSecond();
  133. }
  134. }
  135. bool CMPAFile::GetNextFrame()
  136. {
  137. uint32 dwOffset = m_pMPAHeader->m_dwSyncOffset + m_pMPAHeader->m_dwRealFrameSize;
  138. try
  139. {
  140. CMPAHeader* pFrame = new CMPAHeader( this, dwOffset, false );
  141. delete m_pMPAHeader;
  142. m_pMPAHeader = pFrame;
  143. if( m_dwFrameNo > 0 )
  144. m_dwFrameNo++;
  145. }
  146. catch(...)
  147. {
  148. return false;
  149. }
  150. return true;
  151. }
  152. bool CMPAFile::GetPrevFrame()
  153. {
  154. uint32 dwOffset = m_pMPAHeader->m_dwSyncOffset-MPA_HEADER_SIZE;
  155. try
  156. {
  157. // look backward from dwOffset on
  158. CMPAHeader* pFrame = new CMPAHeader( this, dwOffset, false, true );
  159. delete m_pMPAHeader;
  160. m_pMPAHeader = pFrame;
  161. if( m_dwFrameNo > 0 )
  162. m_dwFrameNo --;
  163. }
  164. catch(...)
  165. {
  166. return false;
  167. }
  168. return true;
  169. }
  170. bool CMPAFile::GetFirstFrame()
  171. {
  172. uint32 dwOffset = 0;
  173. try
  174. {
  175. CMPAHeader* pFrame = new CMPAHeader( this, dwOffset, false );
  176. delete m_pMPAHeader;
  177. m_pMPAHeader = pFrame;
  178. m_dwFrameNo = 1;
  179. }
  180. catch(...)
  181. {
  182. return false;
  183. }
  184. return true;
  185. }
  186. bool CMPAFile::GetLastFrame()
  187. {
  188. uint32 dwOffset = m_dwEnd - m_dwBegin - MPA_HEADER_SIZE;
  189. try
  190. {
  191. // look backward from dwOffset on
  192. CMPAHeader* pFrame = new CMPAHeader( this, dwOffset, false, true );
  193. delete m_pMPAHeader;
  194. m_pMPAHeader = pFrame;
  195. m_dwFrameNo = 0;
  196. }
  197. catch(...)
  198. {
  199. return false;
  200. }
  201. return true;
  202. }
  203. // destructor
  204. CMPAFile::~CMPAFile(void)
  205. {
  206. delete m_pMPAHeader;
  207. if( m_pVBRHeader )
  208. delete m_pVBRHeader;
  209. if( m_pBuffer )
  210. delete[] m_pBuffer;
  211. // close file
  212. if( m_bMustReleaseFile )
  213. g_pFullFileSystem->Close( m_hFile );
  214. if( m_szFile )
  215. free( (void*)m_szFile );
  216. }
  217. // open file
  218. void CMPAFile::Open( const char * szFilename )
  219. {
  220. // open with CreateFile (no limitation of 128byte filename length, like in mmioOpen)
  221. m_hFile = g_pFullFileSystem->Open( szFilename, "rb", "GAME" );//::CreateFile( szFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
  222. if( m_hFile == FILESYSTEM_INVALID_HANDLE )
  223. {
  224. // throw error
  225. throw CMPAException( CMPAException::ErrOpenFile, szFilename, _T("CreateFile"), true );
  226. }
  227. }
  228. // set file position
  229. void CMPAFile::SetPosition( int offset )
  230. {
  231. /*
  232. LARGE_INTEGER liOff;
  233. liOff.QuadPart = lOffset;
  234. liOff.LowPart = ::SetFilePointer(m_hFile, liOff.LowPart, &liOff.HighPart, dwMoveMethod );
  235. if (liOff.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR )
  236. {
  237. // throw error
  238. throw CMPAException( CMPAException::ErrSetPosition, m_szFile, _T("SetFilePointer"), true );
  239. }
  240. */
  241. g_pFullFileSystem->Seek( m_hFile, offset, FILESYSTEM_SEEK_HEAD );
  242. }
  243. // read from file, return number of bytes read
  244. uint32 CMPAFile::Read( void *pData, uint32 dwSize, uint32 dwOffset )
  245. {
  246. uint32 dwBytesRead = 0;
  247. // set position first
  248. SetPosition( m_dwBegin+dwOffset );
  249. //if( !::ReadFile( m_hFile, pData, dwSize, &dwBytesRead, NULL ) )
  250. // throw CMPAException( CMPAException::ErrReadFile, m_szFile, _T("ReadFile"), true );
  251. dwBytesRead = g_pFullFileSystem->Read( pData, dwSize, m_hFile );
  252. return dwBytesRead;
  253. }
  254. // convert from big endian to native format (Intel=little endian) and return as uint32 (32bit)
  255. uint32 CMPAFile::ExtractBytes( uint32& dwOffset, uint32 dwNumBytes, bool bMoveOffset )
  256. {
  257. Assert( dwNumBytes > 0 );
  258. Assert( dwNumBytes <= 4 ); // max 4 byte
  259. // enough bytes in buffer, otherwise read from file
  260. if( !m_pBuffer || ( ((int)(m_dwBufferSize - dwOffset)) < (int)dwNumBytes) )
  261. FillBuffer( dwOffset + dwNumBytes );
  262. uint32 dwResult = 0;
  263. // big endian extract (most significant byte first) (will work on little and big-endian computers)
  264. uint32 dwNumByteShifts = dwNumBytes - 1;
  265. for( uint32 n=dwOffset; n < dwOffset+dwNumBytes; n++ )
  266. {
  267. dwResult |= m_pBuffer[n] << 8*dwNumByteShifts--; // the bit shift will do the correct byte order for you
  268. }
  269. if( bMoveOffset )
  270. dwOffset += dwNumBytes;
  271. return dwResult;
  272. }
  273. // throws exception if not possible
  274. void CMPAFile::FillBuffer( uint32 dwOffsetToRead )
  275. {
  276. uint32 dwNewBufferSize;
  277. // calc new buffer size
  278. if( m_dwBufferSize == 0 )
  279. dwNewBufferSize = m_dwInitBufferSize;
  280. else
  281. dwNewBufferSize = m_dwBufferSize*2;
  282. // is it big enough?
  283. if( dwNewBufferSize < dwOffsetToRead )
  284. dwNewBufferSize = dwOffsetToRead;
  285. // reserve new buffer
  286. BYTE* pNewBuffer = new BYTE[dwNewBufferSize];
  287. // take over data from old buffer
  288. if( m_pBuffer )
  289. {
  290. memcpy( pNewBuffer, m_pBuffer, m_dwBufferSize );
  291. // release old buffer
  292. delete[] m_pBuffer;
  293. }
  294. m_pBuffer = (char*)pNewBuffer;
  295. // read <dwNewBufferSize-m_dwBufferSize> bytes from offset <m_dwBufferSize>
  296. uint32 dwBytesRead = Read( m_pBuffer+m_dwBufferSize, dwNewBufferSize-m_dwBufferSize, m_dwBufferSize );
  297. // no more bytes in buffer than read out from file
  298. m_dwBufferSize += dwBytesRead;
  299. }
  300. // Uses mp3 code from: http://www.codeproject.com/audio/MPEGAudioInfo.asp
  301. struct MP3Duration_t
  302. {
  303. FileNameHandle_t h;
  304. float duration;
  305. static bool LessFunc( const MP3Duration_t& lhs, const MP3Duration_t& rhs )
  306. {
  307. return lhs.h < rhs.h;
  308. }
  309. };
  310. CUtlRBTree< MP3Duration_t, int > g_MP3Durations( 0, 0, MP3Duration_t::LessFunc );
  311. float GetMP3Duration_Helper( char const *filename )
  312. {
  313. float duration = 60.0f;
  314. // See if it's in the RB tree already...
  315. char fn[ 512 ];
  316. V_snprintf( fn, sizeof( fn ), "sound/%s", PSkipSoundChars( filename ) );
  317. FileNameHandle_t h = g_pFullFileSystem->FindOrAddFileName( fn );
  318. MP3Duration_t search;
  319. search.h = h;
  320. int idx = g_MP3Durations.Find( search );
  321. if ( idx != g_MP3Durations.InvalidIndex() )
  322. {
  323. return g_MP3Durations[ idx ].duration;
  324. }
  325. try
  326. {
  327. CMPAFile MPAFile( fn, 0 );
  328. if ( MPAFile.m_dwBytesPerSec != 0 )
  329. {
  330. duration = (float)(MPAFile.m_dwEnd - MPAFile.m_dwBegin) / (float)MPAFile.m_dwBytesPerSec;
  331. }
  332. }
  333. catch ( ... )
  334. {
  335. }
  336. search.duration = duration;
  337. g_MP3Durations.Insert( search );
  338. return duration;
  339. }