XferSave.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: XferSave.cpp /////////////////////////////////////////////////////////////////////////////
  24. // Author: Colin Day, February 2002
  25. // Desc: Xfer disk write implementation
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/XferSave.h"
  30. #include "Common/Snapshot.h"
  31. #include "Common/GameMemory.h"
  32. // PRIVATE TYPES //////////////////////////////////////////////////////////////////////////////////
  33. class XferBlockData : public MemoryPoolObject
  34. {
  35. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(XferBlockData, "XferBlockData")
  36. public:
  37. XferFilePos filePos; ///< the file position of this block
  38. XferBlockData *next; ///< next block on the stack
  39. };
  40. EMPTY_DTOR(XferBlockData)
  41. ///////////////////////////////////////////////////////////////////////////////////////////////////
  42. // PUBLIC METHDOS /////////////////////////////////////////////////////////////////////////////////
  43. ///////////////////////////////////////////////////////////////////////////////////////////////////
  44. //-------------------------------------------------------------------------------------------------
  45. //-------------------------------------------------------------------------------------------------
  46. XferSave::XferSave( void )
  47. {
  48. m_xferMode = XFER_SAVE;
  49. m_fileFP = NULL;
  50. m_blockStack = NULL;
  51. } // end XferSave
  52. //-------------------------------------------------------------------------------------------------
  53. //-------------------------------------------------------------------------------------------------
  54. XferSave::~XferSave( void )
  55. {
  56. // warn the user if a file was left open
  57. if( m_fileFP != NULL )
  58. {
  59. DEBUG_CRASH(( "Warning: Xfer file '%s' was left open\n", m_identifier.str() ));
  60. close();
  61. } // end if
  62. //
  63. // the block stack should be empty, if it's not that means we started blocks but never
  64. // called enough matching end blocks
  65. //
  66. if( m_blockStack != NULL )
  67. {
  68. // tell the user there is an error
  69. DEBUG_CRASH(( "Warning: XferSave::~XferSave - m_blockStack was not NULL!\n" ));
  70. // delete the block stack
  71. XferBlockData *next;
  72. while( m_blockStack )
  73. {
  74. next = m_blockStack->next;
  75. m_blockStack->deleteInstance();
  76. m_blockStack = next;
  77. } // end while
  78. } // end if
  79. } // end ~XferSave
  80. //-------------------------------------------------------------------------------------------------
  81. /** Open file 'identifier' for writing */
  82. //-------------------------------------------------------------------------------------------------
  83. void XferSave::open( AsciiString identifier )
  84. {
  85. // sanity, check to see if we're already open
  86. if( m_fileFP != NULL )
  87. {
  88. DEBUG_CRASH(( "Cannot open file '%s' cause we've already got '%s' open\n",
  89. identifier.str(), m_identifier.str() ));
  90. throw XFER_FILE_ALREADY_OPEN;
  91. } // end if
  92. // call base class
  93. Xfer::open( identifier );
  94. // open the file
  95. m_fileFP = fopen( identifier.str(), "w+b" );
  96. if( m_fileFP == NULL )
  97. {
  98. DEBUG_CRASH(( "File '%s' not found\n", identifier.str() ));
  99. throw XFER_FILE_NOT_FOUND;
  100. } // end if
  101. } // end open
  102. //-------------------------------------------------------------------------------------------------
  103. /** Close our current file */
  104. //-------------------------------------------------------------------------------------------------
  105. void XferSave::close( void )
  106. {
  107. // sanity, if we don't have an open file we can do nothing
  108. if( m_fileFP == NULL )
  109. {
  110. DEBUG_CRASH(( "Xfer close called, but no file was open\n" ));
  111. throw XFER_FILE_NOT_OPEN;
  112. } // end if
  113. // close the file
  114. fclose( m_fileFP );
  115. m_fileFP = NULL;
  116. // erase the filename
  117. m_identifier.clear();
  118. } // end close
  119. //-------------------------------------------------------------------------------------------------
  120. /** Write a placeholder at the current location in the file and store this location
  121. * internally. The next endBlock that is called will back up to the most recently stored
  122. * beginBlock and write the difference in file bytes from the endBlock call to the
  123. * location of this beginBlock. The current file position will then return to the location
  124. * at which endBlock was called */
  125. //-------------------------------------------------------------------------------------------------
  126. Int XferSave::beginBlock( void )
  127. {
  128. // sanity
  129. DEBUG_ASSERTCRASH( m_fileFP != NULL, ("Xfer begin block - file pointer for '%s' is NULL\n",
  130. m_identifier.str()) );
  131. // get the current file position so we can back up here for the next end block call
  132. XferFilePos filePos = ftell( m_fileFP );
  133. // write a placeholder
  134. XferBlockSize blockSize = 0;
  135. if( fwrite( &blockSize, sizeof( XferBlockSize ), 1, m_fileFP ) != 1 )
  136. {
  137. DEBUG_CRASH(( "XferSave::beginBlock - Error writing block size in '%s'\n",
  138. m_identifier.str() ));
  139. return XFER_WRITE_ERROR;
  140. } // end if
  141. // save this block position on the top of the "stack"
  142. XferBlockData *top = newInstance(XferBlockData);
  143. // impossible -- exception will be thrown (srj)
  144. // if( top == NULL )
  145. // {
  146. //
  147. // DEBUG_CRASH(( "XferSave - Begin block, out of memory - can't save block stack data\n" ));
  148. // return XFER_OUT_OF_MEMORY;
  149. //
  150. // } // end if
  151. top->filePos = filePos;
  152. top->next = m_blockStack;
  153. m_blockStack = top;
  154. return XFER_OK;
  155. } // end beginBlock
  156. //-------------------------------------------------------------------------------------------------
  157. /** Do the tail end as described in beginBlock above. Back up to the last begin block,
  158. * write the file difference from current position to the last begin position, and put
  159. * current file position back to where it was */
  160. //-------------------------------------------------------------------------------------------------
  161. void XferSave::endBlock( void )
  162. {
  163. // sanity
  164. DEBUG_ASSERTCRASH( m_fileFP != NULL, ("Xfer end block - file pointer for '%s' is NULL\n",
  165. m_identifier.str()) );
  166. // sanity, make sure we have a block started
  167. if( m_blockStack == NULL )
  168. {
  169. DEBUG_CRASH(( "Xfer end block called, but no matching begin block was found\n" ));
  170. throw XFER_BEGIN_END_MISMATCH;
  171. } // end if
  172. // save our current file position
  173. XferFilePos currentFilePos = ftell( m_fileFP );
  174. // pop the block descriptor off the top of the block stack
  175. XferBlockData *top = m_blockStack;
  176. m_blockStack = m_blockStack->next;
  177. // rewind the file to the block position
  178. fseek( m_fileFP, top->filePos, SEEK_SET );
  179. // write the size in bytes between the block position and what is our current file position
  180. XferBlockSize blockSize = currentFilePos - top->filePos - sizeof( XferBlockSize );
  181. if( fwrite( &blockSize, sizeof( XferBlockSize ), 1, m_fileFP ) != 1 )
  182. {
  183. DEBUG_CRASH(( "Error writing block size to file '%s'\n", m_identifier.str() ));
  184. throw XFER_WRITE_ERROR;
  185. } // end if
  186. // place the file pointer back to the current position
  187. fseek( m_fileFP, currentFilePos, SEEK_SET );
  188. // delete the block data as it's all used up now
  189. top->deleteInstance();
  190. } // end endBlock
  191. //-------------------------------------------------------------------------------------------------
  192. /** Skip forward 'dataSize' bytes in the file */
  193. //-------------------------------------------------------------------------------------------------
  194. void XferSave::skip( Int dataSize )
  195. {
  196. // sanity
  197. DEBUG_ASSERTCRASH( m_fileFP != NULL, ("XferSave - file pointer for '%s' is NULL\n",
  198. m_identifier.str()) );
  199. // skip forward dataSize bytes
  200. fseek( m_fileFP, dataSize, SEEK_CUR );
  201. } // end skip
  202. // ------------------------------------------------------------------------------------------------
  203. /** Entry point for xfering a snapshot */
  204. // ------------------------------------------------------------------------------------------------
  205. void XferSave::xferSnapshot( Snapshot *snapshot )
  206. {
  207. if( snapshot == NULL )
  208. {
  209. DEBUG_CRASH(( "XferSave::xferSnapshot - Invalid parameters\n" ));
  210. throw XFER_INVALID_PARAMETERS;
  211. } // end if
  212. // run the xfer function of the snapshot
  213. snapshot->xfer( this );
  214. } // end xferSnapshot
  215. // ------------------------------------------------------------------------------------------------
  216. /** Save ascii string */
  217. // ------------------------------------------------------------------------------------------------
  218. void XferSave::xferAsciiString( AsciiString *asciiStringData )
  219. {
  220. // sanity
  221. if( asciiStringData->getLength() > 255 )
  222. {
  223. DEBUG_CRASH(( "XferSave cannot save this unicode string because it's too long. Change the size of the length header (but be sure to preserve save file compatability\n" ));
  224. throw XFER_STRING_ERROR;
  225. } // end if
  226. // save length of string to follow
  227. UnsignedByte len = asciiStringData->getLength();
  228. xferUnsignedByte( &len );
  229. // save string data
  230. if( len > 0 )
  231. xferUser( (void *)asciiStringData->str(), sizeof( Byte ) * len );
  232. } // end xferAsciiString
  233. // ------------------------------------------------------------------------------------------------
  234. /** Save unicodee string */
  235. // ------------------------------------------------------------------------------------------------
  236. void XferSave::xferUnicodeString( UnicodeString *unicodeStringData )
  237. {
  238. // sanity
  239. if( unicodeStringData->getLength() > 255 )
  240. {
  241. DEBUG_CRASH(( "XferSave cannot save this unicode string because it's too long. Change the size of the length header (but be sure to preserve save file compatability\n" ));
  242. throw XFER_STRING_ERROR;
  243. } // end if
  244. // save length of string to follow
  245. UnsignedByte len = unicodeStringData->getLength();
  246. xferUnsignedByte( &len );
  247. // save string data
  248. if( len > 0 )
  249. xferUser( (void *)unicodeStringData->str(), sizeof( WideChar ) * len );
  250. } // end xferUnicodeString
  251. //-------------------------------------------------------------------------------------------------
  252. /** Perform the write operation */
  253. //-------------------------------------------------------------------------------------------------
  254. void XferSave::xferImplementation( void *data, Int dataSize )
  255. {
  256. // sanity
  257. DEBUG_ASSERTCRASH( m_fileFP != NULL, ("XferSave - file pointer for '%s' is NULL\n",
  258. m_identifier.str()) );
  259. // write data to file
  260. if( fwrite( data, dataSize, 1, m_fileFP ) != 1 )
  261. {
  262. DEBUG_CRASH(( "XferSave - Error writing to file '%s'\n", m_identifier.str() ));
  263. throw XFER_WRITE_ERROR;
  264. } // end if
  265. } // end xferImplementation