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.

404 lines
9.7 KiB

  1. #include "tier1/timeutils.h"
  2. #include "tier0/dbg.h"
  3. #include "tier1/utlbuffer.h"
  4. #include "tier1/utlbufferutil.h"
  5. #include "mathlib/mathlib.h"
  6. #include <ctype.h>
  7. #include <math.h>
  8. // NOTE: This has to be the last file included!
  9. #include "tier0/memdbgon.h"
  10. ////////////////////////////////////////////////////////////////////////////////////////
  11. //
  12. // DmeFramerate_t
  13. //
  14. // exact (rational) representation of common framerates - any integral or ntsc framerate
  15. //
  16. ////////////////////////////////////////////////////////////////////////////////////////
  17. DmeFramerate_t::DmeFramerate_t( float fps )
  18. {
  19. SetFramerate( fps );
  20. }
  21. DmeFramerate_t::DmeFramerate_t( int fps /*= 0*/ ) :
  22. m_num( fps ), m_den( 10000 )
  23. {
  24. }
  25. DmeFramerate_t::DmeFramerate_t( int nNumerator, int nDenominator ) :
  26. m_num( nNumerator ), m_den( nDenominator * 10000 )
  27. {
  28. }
  29. void DmeFramerate_t::SetFramerate( float flFrameRate )
  30. {
  31. if ( IsIntegralValue( flFrameRate ) )
  32. {
  33. SetFramerate( RoundFloatToInt( flFrameRate ) );
  34. }
  35. else if ( IsIntegralValue( flFrameRate * 1001.0f / 1000.0f, 0.01f ) ) // 1001 is the ntsc divisor (30*1000/1001 = 29.97, etc)
  36. {
  37. SetFramerateNTSC( RoundFloatToInt( flFrameRate * 1001.0f / 1000.0f ) );
  38. }
  39. else
  40. {
  41. Assert( 0 );
  42. SetFramerate( RoundFloatToInt( flFrameRate ) );
  43. }
  44. }
  45. void DmeFramerate_t::SetFramerate( int fps )
  46. {
  47. m_num = fps;
  48. m_den = 10000;
  49. }
  50. // other (uncommon) options besides 30(29.97 - ntsc video) are 24 (23.976 - ntsc film) and 60 (59.94 - ntsc progressive)
  51. void DmeFramerate_t::SetFramerateNTSC( int multiplier /*= 30*/ )
  52. {
  53. // ntsc = 30 fps * 1000 / 1001
  54. // = ( 30 / 10000 fptms ) * 1000 / 1001
  55. // = 30 / 10010
  56. m_num = multiplier;
  57. m_den = 10010;
  58. }
  59. float DmeFramerate_t::GetFramesPerSecond() const
  60. {
  61. return 10000.0f * m_num / float( m_den );
  62. }
  63. DmeTime_t DmeFramerate_t::GetTimePerFrame() const
  64. {
  65. return DmeTime_t( m_den / m_num );
  66. }
  67. ////////////////////////////////////////////////////////////////////////////////////////
  68. //
  69. // DmeTime_t
  70. //
  71. // representing time as integral tenths of a millisecond (tms)
  72. //
  73. ////////////////////////////////////////////////////////////////////////////////////////
  74. DmeTime_t::DmeTime_t( int frame, DmeFramerate_t framerate )
  75. {
  76. int64 num = int64( framerate.m_num );
  77. int64 prod = frame * int64( framerate.m_den );
  78. // add signed offset to force integer truncation (towards 0) to give us truncation towards -inf
  79. if ( frame < 0 )
  80. {
  81. prod -= num - 1;
  82. }
  83. m_tms = int( prod / num ); // round tms towards 0
  84. }
  85. // float operators - comment these out to find potentially incorrect uses of DmeTime_t
  86. DmeTime_t DmeTime_t::operator*=( float f )
  87. {
  88. m_tms = int( floor( m_tms * f + 0.5f ) );
  89. return *this;
  90. }
  91. DmeTime_t DmeTime_t::operator/=( float f )
  92. {
  93. m_tms = int( floor( m_tms / f + 0.5f ) );
  94. return *this;
  95. }
  96. // helper methods
  97. void DmeTime_t::Clamp( DmeTime_t lo, DmeTime_t hi )
  98. {
  99. m_tms = clamp( m_tms, lo.m_tms, hi.m_tms );
  100. }
  101. bool DmeTime_t::IsInRange( DmeTime_t lo, DmeTime_t hi ) const
  102. {
  103. return m_tms >= lo.m_tms && m_tms < hi.m_tms;
  104. }
  105. // helper functions
  106. float GetFractionOfTimeBetween( DmeTime_t t, DmeTime_t start, DmeTime_t end, bool bClamp /*= false*/ )
  107. {
  108. return GetFractionOfTime( t - start, end - start, bClamp );
  109. }
  110. float GetFractionOfTime( DmeTime_t t, DmeTime_t duration, bool bClamp /*= false*/ )
  111. {
  112. if ( duration == DMETIME_ZERO )
  113. return 0.0f;
  114. if ( bClamp )
  115. {
  116. t.Clamp( DMETIME_ZERO, duration );
  117. }
  118. return t.m_tms / float( duration.m_tms );
  119. }
  120. int FrameForTime( DmeTime_t t, DmeFramerate_t framerate )
  121. {
  122. return t.CurrentFrame( framerate );
  123. }
  124. // framerate-dependent conversions to/from frames
  125. int DmeTime_t::CurrentFrame( DmeFramerate_t framerate, RoundStyle_t roundStyle /*=ROUND_DOWN*/ ) const
  126. {
  127. int64 den = int64( framerate.m_den );
  128. int64 num = int64( framerate.m_num );
  129. int64 prod = int64( m_tms ) * num;
  130. // times within this range are considered on a frame: (frame*den/num - 1, frame*den/num]
  131. // this follows from the truncation towards -inf behavior of the frame,framerate constructor above
  132. // the following logic is there to ensure the above rule,
  133. // while working around the truncation towards 0 behavior of integer divide
  134. if ( m_tms < 0 )
  135. {
  136. if ( roundStyle == ROUND_NEAREST )
  137. return int( ( prod - den/2 + num ) / den );
  138. if ( roundStyle == ROUND_DOWN )
  139. return int( ( prod - den + num ) / den );
  140. }
  141. else
  142. {
  143. if ( roundStyle == ROUND_NEAREST )
  144. return int( ( prod + den/2 ) / den ); // this is intentionally not symmetric with the negative case, s.t. nearest always rounds up at half-frames (rather than always towards 0)
  145. if ( roundStyle == ROUND_UP )
  146. return int( ( prod + den - num ) / den );
  147. if ( roundStyle == ROUND_DOWN )
  148. return int( ( prod + num ) / den );
  149. }
  150. return int( prod / den );
  151. }
  152. DmeTime_t DmeTime_t::TimeAtCurrentFrame( DmeFramerate_t framerate, RoundStyle_t roundStyle /*=ROUND_DOWN*/ ) const
  153. {
  154. int frame = CurrentFrame( framerate, roundStyle );
  155. return DmeTime_t( frame, framerate );
  156. }
  157. DmeTime_t DmeTime_t::TimeAtNextFrame( DmeFramerate_t framerate ) const
  158. {
  159. // since we always round towards -inf, go to next frame whether we're on a frame or not
  160. int frame = CurrentFrame( framerate, ROUND_DOWN );
  161. return DmeTime_t( frame + 1, framerate );
  162. }
  163. DmeTime_t DmeTime_t::TimeAtPrevFrame( DmeFramerate_t framerate ) const
  164. {
  165. int frame = CurrentFrame( framerate, ROUND_UP );
  166. return DmeTime_t( frame - 1, framerate ); // we're exactly on a frame
  167. }
  168. int DmeTime_t::RoundSecondsToTMS( float sec )
  169. {
  170. return (int)floor( 10000.0f * sec + 0.5f ); // round at half-tms boundary
  171. }
  172. int DmeTime_t::RoundSecondsToTMS( double sec )
  173. {
  174. return (int)floor( 10000.0 * sec + 0.5 ); // round at half-tms boundary
  175. }
  176. bool Serialize( CUtlBuffer &buf, const DmeTime_t &src )
  177. {
  178. int tms = src.GetTenthsOfMS();
  179. if ( buf.IsText() )
  180. {
  181. #if 1
  182. double tms = src.GetTenthsOfMS();
  183. buf.Printf( "%.04f", tms / 10000 );
  184. #else
  185. int tms = src.GetTenthsOfMS();
  186. uint postms = tms; // can't just negate tms, since -INT_MIN == INT_MIN
  187. if ( tms < 0 )
  188. {
  189. buf.PutChar( '-' );
  190. postms = -tms;
  191. }
  192. int seconds = postms / 10000;
  193. int remainder = postms % 10000;
  194. buf.Printf( "%d.%04d", seconds, remainder );
  195. #endif
  196. }
  197. else
  198. {
  199. buf.PutInt( tms );
  200. }
  201. return buf.IsValid();
  202. }
  203. bool Unserialize( CUtlBuffer &buf, DmeTime_t &dest )
  204. {
  205. if ( buf.IsText() )
  206. {
  207. buf.EatWhiteSpace();
  208. #if 1
  209. double tms = buf.GetDouble() * 10000;
  210. if ( !buf.IsValid() )
  211. return false;
  212. if ( tms < INT_MIN || tms > INT_MAX )
  213. return false;
  214. dest.SetTenthsOfMS( ( int )floor( tms + 0.5 ) );
  215. #else
  216. char str[ 16 ];
  217. buf.GetString( str, sizeof( str ) );
  218. if ( !buf.IsValid() )
  219. return false;
  220. char *p = str;
  221. bool bNegative = *p == '-';
  222. if ( bNegative )
  223. {
  224. ++p;
  225. }
  226. bool bSeenDigit = false;
  227. bool bOverflow = false;
  228. int seconds = 0;
  229. while ( isdigit( *p ) )
  230. {
  231. seconds = seconds * 10 + *p++ - '0';
  232. bSeenDigit = true;
  233. bOverflow = seconds > INT_MAX / 10000; // once this goes invalid, it stays that way, so no extra check is needed
  234. }
  235. int remainder = 0;
  236. if ( *p == '.' )
  237. {
  238. ++p;
  239. int multiplier = 1000;
  240. while ( isdigit( *p ) )
  241. {
  242. remainder += multiplier * ( *p++ - '0' );
  243. multiplier /= 10;
  244. bSeenDigit = true;
  245. }
  246. }
  247. uint tms = seconds * 10000 + remainder;
  248. if ( bOverflow || !bSeenDigit || ( tms > ( bNegative ? ( uint )-INT_MIN : INT_MAX ) ) )
  249. return false;
  250. dest.SetTenthsOfMS( bNegative ? -tms : tms );
  251. #endif
  252. return true;
  253. }
  254. int tms = buf.GetInt();
  255. if ( !buf.IsValid() )
  256. return false;
  257. dest.SetTenthsOfMS( tms );
  258. return true;
  259. }
  260. ////////////////////////////////////////////////////////////////////////////////
  261. // DmeTime_t serialization/unserialization tests
  262. ////////////////////////////////////////////////////////////////////////////////
  263. #if 0 // not as necessary now that CUtlBuffer::GetDouble() works
  264. class CTestTimeSerialization
  265. {
  266. public:
  267. CTestTimeSerialization()
  268. {
  269. TestSerialization( DMETIME_INVALID );
  270. TestSerialization( DMETIME_MINTIME );
  271. TestSerialization( DMETIME_MAXTIME );
  272. TestSerialization( DMETIME_MINDELTA );
  273. TestSerialization( DMETIME_ZERO );
  274. TestSerialization( "214748.3647" );
  275. TestSerialization( "214748.3648", NULL, false );
  276. TestSerialization( "500000.0000", NULL, false );
  277. TestSerialization( "-214748.3648" );
  278. TestSerialization( "-214748.3649", NULL, false );
  279. TestSerialization( "-500000.0000", NULL, false );
  280. TestSerialization( "1.2", "1.2000" );
  281. TestSerialization( "1", "1.0000" );
  282. TestSerialization( "1.", "1.0000" );
  283. TestSerialization( ".2", "0.2000" );
  284. TestSerialization( "-1.2", "-1.2000" );
  285. TestSerialization( "-1.", "-1.0000" );
  286. TestSerialization( "-1", "-1.0000" );
  287. TestSerialization( "-.2", "-0.2000" );
  288. TestSerialization( "1.23456", "1.2345" );
  289. TestSerialization( "-1.23456", "-1.2345" );
  290. }
  291. void TestSerialization( DmeTime_t time )
  292. {
  293. CUtlBuffer buf( 0, 20, CUtlBuffer::TEXT_BUFFER );
  294. Serialize( buf, time );
  295. DmeTime_t test;
  296. if ( !Unserialize( buf, test ) )
  297. {
  298. Msg( "TestUnserialize: %d failed\n", time.GetTenthsOfMS() );
  299. return;
  300. }
  301. if ( time != test )
  302. {
  303. Msg( "TestUnserialize: %d != %d\n", test.GetTenthsOfMS(), time.GetTenthsOfMS() );
  304. }
  305. }
  306. void TestSerialization( const char *pStr, const char *pExpectedStr = NULL, bool bExpectedSuccess = true )
  307. {
  308. CUtlBuffer buf( pStr, V_strlen( pStr ) + 1, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
  309. DmeTime_t time;
  310. if ( !Unserialize( buf, time ) )
  311. {
  312. if ( bExpectedSuccess )
  313. {
  314. Msg( "TestUnserialize: %s failed\n", pStr );
  315. }
  316. return;
  317. }
  318. CUtlBuffer testbuf( 0, 20, CUtlBuffer::TEXT_BUFFER );
  319. Serialize( testbuf, time );
  320. char pTestStr[ 20 ];
  321. testbuf.GetString( pTestStr, sizeof( pTestStr ) );
  322. if ( !pExpectedStr )
  323. {
  324. pExpectedStr = pStr;
  325. }
  326. if ( V_strcmp( pTestStr, pExpectedStr ) )
  327. {
  328. Msg( "TestUnserialize: %s != %s\n", pTestStr, pExpectedStr );
  329. }
  330. }
  331. };
  332. CTestTimeSerialization g_testTimeSerialization;
  333. #endif