Team Fortress 2 Source Code as on 22/4/2020
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.

386 lines
9.7 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. // Serialization/unserialization buffer
  8. //=============================================================================//
  9. #include "tier2/utlstreambuffer.h"
  10. #include "tier2/tier2.h"
  11. #include "filesystem.h"
  12. //-----------------------------------------------------------------------------
  13. // default stream chunk size
  14. //-----------------------------------------------------------------------------
  15. enum
  16. {
  17. DEFAULT_STREAM_CHUNK_SIZE = 16 * 1024
  18. };
  19. //-----------------------------------------------------------------------------
  20. // Constructor, destructor
  21. //-----------------------------------------------------------------------------
  22. CUtlStreamBuffer::CUtlStreamBuffer( ) : BaseClass( DEFAULT_STREAM_CHUNK_SIZE, DEFAULT_STREAM_CHUNK_SIZE, 0 )
  23. {
  24. SetUtlBufferOverflowFuncs( &CUtlStreamBuffer::StreamGetOverflow, &CUtlStreamBuffer::StreamPutOverflow );
  25. m_hFileHandle = FILESYSTEM_INVALID_HANDLE;
  26. m_pFileName = NULL;
  27. m_pPath = NULL;
  28. }
  29. CUtlStreamBuffer::CUtlStreamBuffer( const char *pFileName, const char *pPath, int nFlags, bool bDelayOpen ) :
  30. BaseClass( DEFAULT_STREAM_CHUNK_SIZE, DEFAULT_STREAM_CHUNK_SIZE, nFlags )
  31. {
  32. SetUtlBufferOverflowFuncs( &CUtlStreamBuffer::StreamGetOverflow, &CUtlStreamBuffer::StreamPutOverflow );
  33. if ( bDelayOpen )
  34. {
  35. m_pFileName = V_strdup( pFileName );
  36. if ( pPath )
  37. {
  38. int nPathLen = Q_strlen( pPath );
  39. m_pPath = new char[ nPathLen + 1 ];
  40. Q_strcpy( m_pPath, pPath );
  41. }
  42. else
  43. {
  44. m_pPath = new char[ 1 ];
  45. m_pPath[0] = 0;
  46. }
  47. m_hFileHandle = FILESYSTEM_INVALID_HANDLE;
  48. }
  49. else
  50. {
  51. m_pFileName = NULL;
  52. m_pPath = NULL;
  53. m_hFileHandle = OpenFile( pFileName, pPath );
  54. if ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
  55. {
  56. return;
  57. }
  58. }
  59. if ( IsReadOnly() )
  60. {
  61. // NOTE: MaxPut may not actually be this exact size for text files;
  62. // it could be slightly less owing to the /r/n -> /n conversion
  63. m_nMaxPut = g_pFullFileSystem->Size( m_hFileHandle );
  64. // Read in the first bytes of the file
  65. if ( Size() > 0 )
  66. {
  67. int nSizeToRead = min( Size(), m_nMaxPut );
  68. ReadBytesFromFile( nSizeToRead, 0 );
  69. }
  70. }
  71. }
  72. void CUtlStreamBuffer::Close()
  73. {
  74. if ( !IsReadOnly() )
  75. {
  76. // Write the final bytes
  77. int nBytesToWrite = TellPut() - m_nOffset;
  78. if ( nBytesToWrite > 0 )
  79. {
  80. if ( ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE ) && m_pFileName )
  81. {
  82. m_hFileHandle = OpenFile( m_pFileName, m_pPath );
  83. if( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
  84. {
  85. Error( "CUtlStreamBuffer::Close() Unable to open file %s!\n", m_pFileName );
  86. }
  87. }
  88. if ( m_hFileHandle != FILESYSTEM_INVALID_HANDLE )
  89. {
  90. if ( g_pFullFileSystem )
  91. {
  92. int nBytesWritten = g_pFullFileSystem->Write( Base(), nBytesToWrite, m_hFileHandle );
  93. if( nBytesWritten != nBytesToWrite )
  94. {
  95. Error( "CUtlStreamBuffer::Close() Write %s failed %d != %d.\n", m_pFileName, nBytesWritten, nBytesToWrite );
  96. }
  97. }
  98. }
  99. }
  100. }
  101. if ( m_hFileHandle != FILESYSTEM_INVALID_HANDLE )
  102. {
  103. if ( g_pFullFileSystem )
  104. g_pFullFileSystem->Close( m_hFileHandle );
  105. m_hFileHandle = FILESYSTEM_INVALID_HANDLE;
  106. }
  107. if ( m_pFileName )
  108. {
  109. delete[] m_pFileName;
  110. m_pFileName = NULL;
  111. }
  112. if ( m_pPath )
  113. {
  114. delete[] m_pPath;
  115. m_pPath = NULL;
  116. }
  117. m_Error = 0;
  118. }
  119. CUtlStreamBuffer::~CUtlStreamBuffer()
  120. {
  121. Close();
  122. }
  123. //-----------------------------------------------------------------------------
  124. // Open the file. normally done in constructor
  125. //-----------------------------------------------------------------------------
  126. void CUtlStreamBuffer::Open( const char *pFileName, const char *pPath, int nFlags )
  127. {
  128. if ( IsOpen() )
  129. {
  130. Close();
  131. }
  132. m_Get = 0;
  133. m_Put = 0;
  134. m_nTab = 0;
  135. m_nOffset = 0;
  136. m_Flags = nFlags;
  137. m_hFileHandle = OpenFile( pFileName, pPath );
  138. if ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
  139. return;
  140. if ( IsReadOnly() )
  141. {
  142. // NOTE: MaxPut may not actually be this exact size for text files;
  143. // it could be slightly less owing to the /r/n -> /n conversion
  144. m_nMaxPut = g_pFullFileSystem->Size( m_hFileHandle );
  145. // Read in the first bytes of the file
  146. if ( Size() > 0 )
  147. {
  148. int nSizeToRead = min( Size(), m_nMaxPut );
  149. ReadBytesFromFile( nSizeToRead, 0 );
  150. }
  151. }
  152. else
  153. {
  154. if ( m_Memory.NumAllocated() != 0 )
  155. {
  156. m_nMaxPut = -1;
  157. AddNullTermination();
  158. }
  159. else
  160. {
  161. m_nMaxPut = 0;
  162. }
  163. }
  164. }
  165. //-----------------------------------------------------------------------------
  166. // Is the file open?
  167. //-----------------------------------------------------------------------------
  168. bool CUtlStreamBuffer::IsOpen() const
  169. {
  170. if ( m_hFileHandle != FILESYSTEM_INVALID_HANDLE )
  171. return true;
  172. // Delayed open case
  173. return ( m_pFileName != 0 );
  174. }
  175. //-----------------------------------------------------------------------------
  176. // Grow allocation size to fit requested size
  177. //-----------------------------------------------------------------------------
  178. void CUtlStreamBuffer::GrowAllocatedSize( int nSize )
  179. {
  180. int nNewSize = Size();
  181. if ( nNewSize < nSize + 1 )
  182. {
  183. while ( nNewSize < nSize + 1 )
  184. {
  185. nNewSize += DEFAULT_STREAM_CHUNK_SIZE;
  186. }
  187. m_Memory.Grow( nNewSize - Size() );
  188. }
  189. }
  190. //-----------------------------------------------------------------------------
  191. // Load up more of the stream when we overflow
  192. //-----------------------------------------------------------------------------
  193. bool CUtlStreamBuffer::StreamPutOverflow( int nSize )
  194. {
  195. if ( !IsValid() || IsReadOnly() )
  196. return false;
  197. // Make sure the allocated size is at least as big as the requested size
  198. if ( nSize > 0 )
  199. {
  200. GrowAllocatedSize( nSize + 2 );
  201. }
  202. // Don't write the last byte (for NULL termination logic to work)
  203. int nBytesToWrite = TellPut() - m_nOffset - 1;
  204. if ( ( nBytesToWrite > 0 ) || ( nSize < 0 ) )
  205. {
  206. if ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
  207. {
  208. m_hFileHandle = OpenFile( m_pFileName, m_pPath );
  209. if( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
  210. return false;
  211. }
  212. }
  213. if ( nBytesToWrite > 0 )
  214. {
  215. int nBytesWritten = g_pFullFileSystem->Write( Base(), nBytesToWrite, m_hFileHandle );
  216. if ( nBytesWritten != nBytesToWrite )
  217. {
  218. m_Error |= FILE_WRITE_ERROR;
  219. return false;
  220. }
  221. // This is necessary to deal with auto-NULL terminiation
  222. m_Memory[0] = *(unsigned char*)PeekPut( -1 );
  223. if ( TellPut() < Size() )
  224. {
  225. m_Memory[1] = *(unsigned char*)PeekPut( );
  226. }
  227. m_nOffset = TellPut() - 1;
  228. }
  229. if ( nSize < 0 )
  230. {
  231. m_nOffset = -nSize-1;
  232. g_pFullFileSystem->Seek( m_hFileHandle, m_nOffset, FILESYSTEM_SEEK_HEAD );
  233. }
  234. return true;
  235. }
  236. //-----------------------------------------------------------------------------
  237. // Reads bytes from the file; fixes up maxput if necessary and null terminates
  238. //-----------------------------------------------------------------------------
  239. int CUtlStreamBuffer::ReadBytesFromFile( int nBytesToRead, int nReadOffset )
  240. {
  241. if ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
  242. {
  243. if ( !m_pFileName )
  244. {
  245. Warning( "File has not been opened!\n" );
  246. Assert(0);
  247. return 0;
  248. }
  249. m_hFileHandle = OpenFile( m_pFileName, m_pPath );
  250. if ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
  251. {
  252. Error( "Unable to read file %s!\n", m_pFileName );
  253. return 0;
  254. }
  255. if ( m_nOffset != 0 )
  256. {
  257. g_pFullFileSystem->Seek( m_hFileHandle, m_nOffset, FILESYSTEM_SEEK_HEAD );
  258. }
  259. }
  260. char *pReadPoint = (char*)Base() + nReadOffset;
  261. int nBytesRead = g_pFullFileSystem->Read( pReadPoint, nBytesToRead, m_hFileHandle );
  262. if ( nBytesRead != nBytesToRead )
  263. {
  264. // Since max put is a guess at the start,
  265. // we need to shrink it based on the actual # read
  266. if ( m_nMaxPut > TellGet() + nReadOffset + nBytesRead )
  267. {
  268. m_nMaxPut = TellGet() + nReadOffset + nBytesRead;
  269. }
  270. }
  271. if ( nReadOffset + nBytesRead < Size() )
  272. {
  273. // This is necessary to deal with auto-NULL terminiation
  274. pReadPoint[nBytesRead] = 0;
  275. }
  276. return nBytesRead;
  277. }
  278. //-----------------------------------------------------------------------------
  279. // Load up more of the stream when we overflow
  280. //-----------------------------------------------------------------------------
  281. bool CUtlStreamBuffer::StreamGetOverflow( int nSize )
  282. {
  283. if ( !IsValid() || !IsReadOnly() )
  284. return false;
  285. // Shift the unread bytes down
  286. // NOTE: Can't use the partial overlap path if we're seeking. We'll
  287. // get negative sizes passed in if we're seeking.
  288. int nUnreadBytes;
  289. bool bHasPartialOverlap = ( nSize >= 0 ) && ( TellGet() >= m_nOffset ) && ( TellGet() <= m_nOffset + Size() );
  290. if ( bHasPartialOverlap )
  291. {
  292. nUnreadBytes = Size() - ( TellGet() - m_nOffset );
  293. if ( ( TellGet() != m_nOffset ) && ( nUnreadBytes > 0 ) )
  294. {
  295. memmove( Base(), (const char*)Base() + TellGet() - m_nOffset, nUnreadBytes );
  296. }
  297. }
  298. else
  299. {
  300. m_nOffset = TellGet();
  301. g_pFullFileSystem->Seek( m_hFileHandle, m_nOffset, FILESYSTEM_SEEK_HEAD );
  302. nUnreadBytes = 0;
  303. }
  304. // Make sure the allocated size is at least as big as the requested size
  305. if ( nSize > 0 )
  306. {
  307. GrowAllocatedSize( nSize );
  308. }
  309. int nBytesToRead = Size() - nUnreadBytes;
  310. int nBytesRead = ReadBytesFromFile( nBytesToRead, nUnreadBytes );
  311. if ( nBytesRead == 0 )
  312. return false;
  313. m_nOffset = TellGet();
  314. return ( nBytesRead + nUnreadBytes >= nSize );
  315. }
  316. //-----------------------------------------------------------------------------
  317. // open file unless already failed to open
  318. //-----------------------------------------------------------------------------
  319. FileHandle_t CUtlStreamBuffer::OpenFile( const char *pFileName, const char *pPath )
  320. {
  321. if ( m_Error & FILE_OPEN_ERROR )
  322. return FILESYSTEM_INVALID_HANDLE;
  323. char openflags[ 3 ] = "xx";
  324. openflags[ 0 ] = IsReadOnly() ? 'r' : 'w';
  325. openflags[ 1 ] = IsText() && !ContainsCRLF() ? 't' : 'b';
  326. FileHandle_t fh = g_pFullFileSystem->Open( pFileName, openflags, pPath );
  327. if( fh == FILESYSTEM_INVALID_HANDLE )
  328. {
  329. m_Error |= FILE_OPEN_ERROR;
  330. }
  331. return fh;
  332. }