BVB Source Codes

CRYENGINE Show XMLCPB_Reader.cpp Source code

Return Download CRYENGINE: download XMLCPB_Reader.cpp Source code - Download CRYENGINE Source code - Type:.cpp
  1. // Copyright 2001-2016 Crytek GmbH / Crytek Group. All rights reserved.
  2.  
  3. /*************************************************************************
  4. *************************************************************************/
  5.  
  6. #include "StdAfx.h"
  7. #include "XMLCPB_Reader.h"
  8. #include "CryActionCVars.h"
  9. #include <CrySystem/ZLib/IZLibCompressor.h>
  10.  
  11. using namespace XMLCPB;
  12.  
  13. int CReader::MAX_NUM_LIVE_NODES = 64;
  14.  
  15. //////////////////////////////////////////////////////////////////////////
  16. _smart_ptr<IGeneralMemoryHeap> CReader::CreateHeap()
  17. {
  18.         return CryGetIMemoryManager()->CreateGeneralExpandingMemoryHeap(8 * 1024 * 1024, 0, "Load heap");
  19. }
  20.  
  21. //////////////////////////////////////////////////////////////////////////
  22.  
  23. CReader::CReader(IGeneralMemoryHeap* pHeap)
  24.         : m_pHeap(pHeap)
  25.         , m_liveNodes(NAlloc::GeneralHeapAlloc(pHeap))
  26.         , m_buffer(pHeap)
  27.         , m_tableTags(pHeap)
  28.         , m_tableAttrNames(pHeap)
  29.         , m_tableStrData(pHeap)
  30.         , m_tableAttrSets(pHeap)
  31.         , m_firstFreeLiveNode(0)
  32.         , m_maxNumActiveNodes(0)
  33.         , m_numActiveNodes(0)
  34.         , m_errorReading(false)
  35.         , m_nodesAddrTable(NAlloc::GeneralHeapAlloc(pHeap))
  36.         , m_totalSize(0)
  37.         , m_nodesDataSize(0)
  38.         , m_pZLibBuffer(NULL)
  39.         , m_pZLibCompressedBuffer(NULL)
  40.         , m_ZLibBufferSizeWithData(0)
  41.         , m_ZLibBufferSizeAlreadyRead(0)
  42.         , m_numNodes(0)
  43. {
  44.         InitializeDataTypeInfo();
  45.         m_liveNodes.resize(MAX_NUM_LIVE_NODES, CNodeLiveReader(*this));
  46. }
  47.  
  48. //////////////////////////////////////////////////////////////////////////
  49.  
  50. CReader::~CReader()
  51. {
  52.         SAFE_DELETE_ARRAY(m_pZLibBuffer);
  53.         SAFE_DELETE_ARRAY(m_pZLibCompressedBuffer);
  54.         //      CryLog(" Binary SaveGame Reader: max live nodes active in the reader: %d", m_maxNumActiveNodes );
  55. }
  56.  
  57. //////////////////////////////////////////////////////////////////////////
  58.  
  59. CNodeLiveReaderRef CReader::GetRoot()
  60. {
  61.         CNodeLiveReaderRef nodeRef(*this, XMLCPB_ROOTNODE_ID);    // root node always have same live ID and is always valid
  62.  
  63.         assert(m_liveNodes[0].IsValid());
  64.         return nodeRef;
  65. }
  66.  
  67. //////////////////////////////////////////////////////////////////////////
  68.  
  69. CNodeLiveReaderRef CReader::CreateNodeRef()
  70. {
  71.         return CNodeLiveReaderRef(*this);
  72. }
  73.  
  74. //////////////////////////////////////////////////////////////////////////
  75.  
  76. const CNodeLiveReader& CReader::ActivateLiveNodeFromCompact(NodeGlobalID nodeId)
  77. {
  78.         NodeLiveID liveId = m_firstFreeLiveNode;
  79.         CNodeLiveReader& node = m_liveNodes[liveId];
  80.         node.ActivateFromCompact(liveId, nodeId);
  81.  
  82.         // find the now first free live node
  83.         bool found = false;
  84.         uint32 size = m_liveNodes.size();
  85.         for (int i = m_firstFreeLiveNode + 1; i < size; ++i)
  86.         {
  87.                 const CNodeLiveReader& nodeIter = m_liveNodes[i];
  88.                 if (!nodeIter.IsValid())
  89.                 {
  90.                         found = true;
  91.                         m_firstFreeLiveNode = i;
  92.                         break;
  93.                 }
  94.         }
  95.  
  96.         assert(found);
  97.         m_numActiveNodes++;
  98.         if (m_numActiveNodes > m_maxNumActiveNodes)
  99.                 m_maxNumActiveNodes = m_numActiveNodes;
  100.  
  101.         return node;
  102. }
  103.  
  104. //////////////////////////////////////////////////////////////////////////
  105.  
  106. CNodeLiveReader* CReader::GetNodeLive(NodeLiveID nodeId)
  107. {
  108.         CNodeLiveReader* pNode = NULL;
  109.         assert(nodeId < m_liveNodes.size());
  110.  
  111.         if (nodeId < m_liveNodes.size())
  112.                 pNode = &(m_liveNodes[nodeId]);
  113.  
  114.         return pNode;
  115. }
  116.  
  117. //////////////////////////////////////////////////////////////////////////
  118.  
  119. void CReader::FreeNodeLive(NodeLiveID nodeId)
  120. {
  121.         if (nodeId < m_firstFreeLiveNode)
  122.                 m_firstFreeLiveNode = nodeId;
  123.  
  124.         assert(nodeId < m_liveNodes.size());
  125.  
  126.         m_liveNodes[nodeId].Reset();
  127.         m_numActiveNodes--;
  128. }
  129.  
  130. //////////////////////////////////////////////////////////////////////////
  131.  
  132. void CReader::CheckErrorFlag(IPlatformOS::EFileOperationCode code)
  133. {
  134.         if (!m_errorReading)
  135.                 m_errorReading = (code != IPlatformOS::eFOC_Success);
  136. }
  137.  
  138. //////////////////////////////////////////////////////////////////////////
  139.  
  140. FlatAddr CReader::ReadFromBuffer(FlatAddr addr, uint8*& rdata, uint32 len)
  141. {
  142.         m_buffer.CopyTo(rdata, addr, len);
  143.  
  144.         return addr + len;
  145. }
  146.  
  147. //////////////////////////////////////////////////////////////////////////
  148. // high level function for reading data from the file. it will use (or not) zlib compression depending on the cvar
  149. // TODO: remove all those pOSSaveReader parameter chains and make it a member, passing it along is not needed anymore
  150. // TODO: extract all zlib code into a separate object
  151. void CReader::ReadDataFromFile(IPlatformOS::ISaveReaderPtr& pOSSaveReader, void* pDst, uint32 numBytes)
  152. {
  153.         if (!CCryActionCVars::Get().g_XMLCPBUseExtraZLibCompression)
  154.         {
  155.                 ReadDataFromFileInternal(pOSSaveReader, pDst, numBytes);
  156.                 return;
  157.         }
  158.  
  159.         uint32 numBytesLeft = numBytes;
  160.         uint8* pNextDst = (uint8*)pDst;
  161.         do
  162.         {
  163.                 ReadDataFromZLibBuffer(pOSSaveReader, pNextDst, numBytesLeft);
  164.         }
  165.         while (numBytesLeft > 0 && !m_errorReading);
  166. }
  167.  
  168. //////////////////////////////////////////////////////////////////////////
  169. // reads uncompressed data from the buffer, and decompress the next block if needed
  170. // TODO: extract all zlib code into a separate object
  171. void CReader::ReadDataFromZLibBuffer(IPlatformOS::ISaveReaderPtr& pOSSaveReader, uint8*& pDst, uint32& numBytesToRead)
  172. {
  173.         // decompress next block
  174.         if (m_ZLibBufferSizeAlreadyRead == m_ZLibBufferSizeWithData)
  175.         {
  176.                 SZLibBlockHeader blockHeader;
  177.                 ReadDataFromFileInternal(pOSSaveReader, &blockHeader, sizeof(blockHeader));
  178.                 if (!m_errorReading)
  179.                 {
  180.                         bool isCompressedData = blockHeader.m_compressedSize != SZLibBlockHeader::NO_ZLIB_USED;
  181.  
  182.                         if (isCompressedData)
  183.                         {
  184.                                 assert(blockHeader.m_compressedSize < XMLCPB_ZLIB_BUFFER_SIZE);
  185.                                 ReadDataFromFileInternal(pOSSaveReader, m_pZLibCompressedBuffer, blockHeader.m_compressedSize);
  186.                                 if (!m_errorReading)
  187.                                 {
  188.                                         size_t uncompressedLength = XMLCPB_ZLIB_BUFFER_SIZE;
  189.                                         bool ok = gEnv->pSystem->DecompressDataBlock(m_pZLibCompressedBuffer, blockHeader.m_compressedSize, m_pZLibBuffer, uncompressedLength);
  190.                                         if (!ok)
  191.                                                 m_errorReading = true;
  192.                                         m_ZLibBufferSizeWithData = uncompressedLength;
  193.                                 }
  194.                         }
  195.                         else // when is not compressed data, reads directly into the uncompressed buffer
  196.                         {
  197.                                 ReadDataFromFileInternal(pOSSaveReader, m_pZLibBuffer, blockHeader.m_uncompressedSize);
  198.                                 m_ZLibBufferSizeWithData = blockHeader.m_uncompressedSize;
  199.                         }
  200.                         m_ZLibBufferSizeAlreadyRead = 0;
  201.                 }
  202.         }
  203.  
  204.         // read from the decompressed data
  205.         uint32 bytesAvailableInBuffer = m_ZLibBufferSizeWithData - m_ZLibBufferSizeAlreadyRead;
  206.         uint32 bytesToCopy = min(bytesAvailableInBuffer, numBytesToRead);
  207.         memcpy(pDst, m_pZLibBuffer + m_ZLibBufferSizeAlreadyRead, bytesToCopy);
  208.  
  209.         m_ZLibBufferSizeAlreadyRead += bytesToCopy;
  210.         pDst += bytesToCopy;
  211.         numBytesToRead -= bytesToCopy;
  212. }
  213.  
  214. //////////////////////////////////////////////////////////////////////////
  215. // low level function, reads directly from the file
  216. // TODO: remove all those pOSSaveReader parameter chains and make it a member, passing it along is not needed anymore
  217. void CReader::ReadDataFromFileInternal(IPlatformOS::ISaveReaderPtr& pOSSaveReader, void* pDst, uint32 numBytes)
  218. {
  219.         assert(pOSSaveReader.get());
  220.  
  221.         if (!m_errorReading)
  222.         {
  223.                 IPlatformOS::EFileOperationCode code = pOSSaveReader->ReadBytes(pDst, numBytes);
  224.                 CheckErrorFlag(code);
  225.         }
  226. }
  227.  
  228. //////////////////////////////////////////////////////////////////////////
  229. // file structure: see CWriter::WriteBinaryFile
  230.  
  231. bool CReader::ReadBinaryFile(const char* pFileName)
  232. {
  233.         IPlatformOS::ISaveReaderPtr pOSSaveReader = gEnv->pSystem->GetPlatformOS()->SaveGetReader(pFileName);
  234.         if (!m_pZLibBuffer)
  235.                 m_pZLibBuffer = new uint8[XMLCPB_ZLIB_BUFFER_SIZE];
  236.         if (!m_pZLibCompressedBuffer)
  237.                 m_pZLibCompressedBuffer = new uint8[XMLCPB_ZLIB_BUFFER_SIZE];
  238.         m_errorReading = pOSSaveReader.get() == NULL;
  239.  
  240.         if (!m_errorReading)
  241.         {
  242.                 size_t totalNumBytesInFile = 0;
  243.                 IPlatformOS::EFileOperationCode code = pOSSaveReader->GetNumBytes(totalNumBytesInFile);
  244.                 m_totalSize = totalNumBytesInFile;
  245.                 CheckErrorFlag(code);
  246.  
  247.                 if (!m_errorReading)
  248.                 {
  249.                         SFileHeader fileHeader;
  250.                         pOSSaveReader->Seek(-int(sizeof(fileHeader)), IPlatformOS::ISaveReader::ESM_END);
  251.                         ReadDataFromFileInternal(pOSSaveReader, &fileHeader, sizeof(fileHeader));
  252. #ifdef XMLCPB_CHECK_FILE_INTEGRITY
  253.                         m_errorReading = CheckFileCorruption(pOSSaveReader, fileHeader, m_totalSize);
  254. #endif
  255.  
  256.                         pOSSaveReader->Seek(0, IPlatformOS::ISaveReader::ESM_BEGIN);
  257.  
  258.                         if (fileHeader.m_fileTypeCheck != fileHeader.FILETYPECHECK)
  259.                         {
  260.                                 CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "XMLCPB ERROR: file type signature not correct. Savegame File corrupted!");
  261.                                 m_errorReading = true;
  262.                         }
  263.  
  264.                         if (!m_errorReading)
  265.                         {
  266.                                 m_nodesDataSize = fileHeader.m_sizeNodes;
  267.                                 m_numNodes = fileHeader.m_numNodes;
  268.                                 m_buffer.ReadFromFile(*this, pOSSaveReader, fileHeader.m_sizeNodes);
  269.                                 m_tableTags.ReadFromFile(*this, pOSSaveReader, fileHeader.m_tags);
  270.                                 m_tableAttrNames.ReadFromFile(*this, pOSSaveReader, fileHeader.m_attrNames);
  271.                                 m_tableStrData.ReadFromFile(*this, pOSSaveReader, fileHeader.m_strData);
  272.                                 m_tableAttrSets.ReadFromFile(*this, pOSSaveReader, fileHeader);
  273.                                 CreateNodeAddressTables();
  274.                         }
  275.  
  276.                         if (!m_errorReading)
  277.                         {
  278.                                 const CNodeLiveReader& root = ActivateLiveNodeFromCompact(m_numNodes - 1);   // the last node is always the root
  279.                                 assert(root.GetLiveId() == XMLCPB_ROOTNODE_ID);
  280.  
  281.                                 pOSSaveReader->TouchFile();
  282.                         }
  283.                 }
  284.  
  285.                 pOSSaveReader->Close();
  286.         }
  287.  
  288.         CryLog("[LOAD GAME] --Binary saveload: reading done--");
  289.  
  290.         if (m_errorReading)
  291.                 CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "XMLCPB ERROR: while reading the file: '%s'", pFileName);
  292.  
  293.         return !m_errorReading;
  294. }
  295.  
  296. //////////////////////////////////////////////////////////////////////////
  297. // The current implementation is not console friendly: it causes the savegame file to be read 2 times, and it uses an extra memory block the size of the full savegame.
  298. // This is not a problem because right now this integrity check is used only on the PC version.
  299. // But if at any point we need it for consoles too, we probably will need to make it more efficient.
  300. //   Using an md5 check on each block instead of the current full file check would probably be the right way. That could be done on the fly, without any extra reading or memory reservation.
  301. #ifdef XMLCPB_CHECK_FILE_INTEGRITY
  302.  
  303. bool CReader::CheckFileCorruption(IPlatformOS::ISaveReaderPtr& pOSSaveReader, const SFileHeader& fileHeader, uint32 totalSize)
  304. {
  305.         bool error = false;
  306.  
  307.         if (totalSize <= sizeof(fileHeader))
  308.         {
  309.                 CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "XMLCPB ERROR: size check failed. savegame File corrupted! (probably not fully saved)");
  310.                 error = true;
  311.                 return error;
  312.         }
  313.  
  314.         uint32 sizeToCheck = totalSize - sizeof(fileHeader);
  315.  
  316.         char* pBuf = new char[sizeToCheck];
  317.         pOSSaveReader->Seek(0, IPlatformOS::ISaveReader::ESM_BEGIN);
  318.         ReadDataFromFileInternal(pOSSaveReader, pBuf, sizeToCheck);
  319.  
  320.         IZLibCompressor* pZLib = GetISystem()->GetIZLibCompressor();
  321.  
  322.         if (!pZLib)
  323.         {
  324.                 SAFE_DELETE_ARRAY(pBuf);
  325.                 error = true;
  326.                 return error;
  327.         }
  328.  
  329.         SMD5Context context;
  330.         char MD5signature[SFileHeader::MD5_SIGNATURE_SIZE];
  331.         pZLib->MD5Init(&context);
  332.         pZLib->MD5Update(&context, pBuf, sizeToCheck);
  333.  
  334.         SFileHeader tempFileHeader = fileHeader;
  335.         for (uint32 i = 0; i < SFileHeader::MD5_SIGNATURE_SIZE; ++i)
  336.                 tempFileHeader.m_MD5Signature[i] = 0;                                                        // the original signature is always calculated with this zeroed.
  337.         pZLib->MD5Update(&context, (const char*)(&tempFileHeader), sizeof(tempFileHeader));
  338.         pZLib->MD5Final(&context, MD5signature);
  339.  
  340.         SAFE_DELETE_ARRAY(pBuf);
  341.  
  342.         for (uint32 i = 0; i < SFileHeader::MD5_SIGNATURE_SIZE; ++i)
  343.         {
  344.                 if (fileHeader.m_MD5Signature[i] != MD5signature[i])
  345.                 {
  346.                         CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "XMLCPB ERROR: md5 check failed. savegame File corrupted!");
  347.                         error = true;
  348.                         break;
  349.                 }
  350.         }
  351.  
  352.         return error;
  353. }
  354.  
  355. #endif
  356.  
  357. //////////////////////////////////////////////////////////////////////////
  358.  
  359. void CReader::ReadDataFromMemory(const uint8* pData, uint32 dataSize, void* pSrc, uint32 numBytes, uint32& outReadLoc)
  360. {
  361.         assert(pData);
  362.         assert(pSrc);
  363.  
  364.         if (!m_errorReading && pData && pSrc && numBytes > 0)
  365.         {
  366.                 const int dataRemaining = (dataSize - numBytes + outReadLoc);
  367.                 m_errorReading = (dataRemaining < 0);
  368.  
  369.                 if (!m_errorReading)
  370.                 {
  371.                         memcpy(pSrc, &pData[outReadLoc], numBytes);
  372.                         outReadLoc += numBytes;
  373.                 }
  374.         }
  375. }
  376.  
  377. //////////////////////////////////////////////////////////////////////////
  378.  
  379. bool CReader::ReadBinaryMemory(const uint8* pData, uint32 uSize)
  380. {
  381.         m_errorReading = !(pData && uSize > 0);
  382.  
  383.         if (!m_errorReading)
  384.         {
  385.                 uint32 uReadLoc = 0;
  386.                 m_totalSize = uSize;
  387.  
  388.                 SFileHeader fileHeader;
  389.                 ReadDataFromMemory(pData, uSize, &fileHeader, sizeof(fileHeader), uReadLoc);
  390.  
  391.                 if (fileHeader.m_fileTypeCheck != fileHeader.FILETYPECHECK)
  392.                         m_errorReading = true;
  393.  
  394.                 if (!m_errorReading)
  395.                 {
  396.                         m_nodesDataSize = fileHeader.m_sizeNodes;
  397.                         m_numNodes = fileHeader.m_numNodes;
  398.                         m_buffer.ReadFromMemory(*this, pData, uSize, fileHeader.m_sizeNodes, uReadLoc);
  399.                         m_tableTags.ReadFromMemory(*this, pData, uSize, fileHeader.m_tags, uReadLoc);
  400.                         m_tableAttrNames.ReadFromMemory(*this, pData, uSize, fileHeader.m_attrNames, uReadLoc);
  401.                         m_tableStrData.ReadFromMemory(*this, pData, uSize, fileHeader.m_strData, uReadLoc);
  402.                         m_tableAttrSets.ReadFromMemory(*this, pData, uSize, fileHeader, uReadLoc);
  403.                         CreateNodeAddressTables();
  404.                 }
  405.  
  406.                 if (!m_errorReading)
  407.                 {
  408.                         const CNodeLiveReader& root = ActivateLiveNodeFromCompact(m_numNodes - 1);   // the last node is always the root
  409.                         assert(root.GetLiveId() == XMLCPB_ROOTNODE_ID);
  410.                 }
  411.         }
  412.  
  413.         if (m_errorReading)
  414.                 CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "XMLCPB ERROR: while reading from memory location");
  415.  
  416.         return !m_errorReading;
  417. }
  418.  
  419. //////////////////////////////////////////////////////////////////////////
  420. // for debug purposes
  421. #ifndef _RELEASE
  422. void CReader::SaveTestFiles()
  423. {
  424.         m_tableTags.WriteStringsIntoTextFile("tableTags.txt");
  425.         m_tableAttrNames.WriteStringsIntoTextFile("tableAttrNames.txt");
  426.         m_tableStrData.WriteStringsIntoTextFile("tableStrData.txt");
  427. }
  428. #endif
  429.  
  430. //////////////////////////////////////////////////////////////////////////
  431.  
  432. void CReader::CreateNodeAddressTables()
  433. {
  434.         m_nodesAddrTable.resize(m_numNodes);
  435.  
  436.         FlatAddr addr = 0;
  437.  
  438.         // TODO (improve): first, because the partially initialized table is used in the node calls,
  439.         //   and also because those nodes are temporary and manually activated just to calculate the address of the next node
  440.         for (uint32 n = 0; n < m_numNodes; ++n)
  441.         {
  442.                 m_nodesAddrTable[n] = addr;
  443.                 CNodeLiveReader node(*this);
  444.                 node.ActivateFromCompact(0, n);
  445.                 addr = node.GetAddrNextNode();
  446.                 assert(addr > m_nodesAddrTable[n]);
  447.         }
  448.  
  449.         assert(addr == m_nodesDataSize);
  450. }
  451.  
downloadXMLCPB_Reader.cpp Source code - Download CRYENGINE Source code
Related Source Codes/Software:
postal - 2017-06-11
reactide - Reactide is the first dedicated IDE for React web ... 2017-06-11
rkt - rkt is a pod-native container engine for Linux. It... 2017-06-11
uWebSockets - Tiny WebSockets https://for... 2017-06-11
realworld - TodoMVC for the RealWorld - Exemplary fullstack Me... 2017-06-11
CRYENGINE - CRYENGINE is a powerful real-time game development... 2017-06-11
goreplay - GoReplay is an open-source tool for capturing and ... 2017-06-10
pyenv - Simple Python version management 2017-06-10
redux-saga - An alternative side effect model for Redux apps ... 2017-06-10
angular-starter - 2017-06-10

 Back to top