BVB Source Codes

CRYENGINE Show PlayerProfileImplRSFHelper.cpp Source code

Return Download CRYENGINE: download PlayerProfileImplRSFHelper.cpp Source code - Download CRYENGINE Source code - Type:.cpp
  1. // Copyright 2001-2016 Crytek GmbH / Crytek Group. All rights reserved.
  2.  
  3. #include "StdAfx.h"
  4. #include <CryCore/Platform/platform.h>
  5. #include <CrySystem/XML/IXml.h>
  6. #include <CryString/StringUtils.h>
  7. #include "PlayerProfileImplRSFHelper.h"
  8. #include "PlayerProfile.h"
  9. #include "Serialization/XmlSaveGame.h"
  10. #include "Serialization/XmlLoadGame.h"
  11. #include "BMPHelper.h"
  12. #include "RichSaveGameTypes.h"
  13. #include "CryAction.h"
  14. #include <CryCore/Platform/IPlatformOS.h>
  15. #include <CryString/StringUtils.h>
  16.  
  17. #define RSF_USE_COMPRESSION // write compressed XML data
  18. // #undef RSF_USE_COMPRESSSION
  19.  
  20. #ifndef MAKEFOURCC
  21.         #if defined(NEED_ENDIAN_SWAP) // big endian
  22.                 #define MAKEFOURCC(ch0, ch1, ch2, ch3)              \
  23.                   ((DWORD)(BYTE)(ch3) | ((DWORD)(BYTE)(ch2) << 8) | \
  24.                    ((DWORD)(BYTE)(ch1) << 16) | ((DWORD)(BYTE)(ch0) << 24))
  25.         #else // little endian
  26.                 #define MAKEFOURCC(ch0, ch1, ch2, ch3)              \
  27.                   ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
  28.                    ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
  29.         #endif
  30. #endif //defined(MAKEFOURCC)
  31.  
  32. #ifdef min
  33.         #undef min
  34. #endif
  35.  
  36. #ifdef max
  37.         #undef max
  38. #endif
  39.  
  40. #define TEST_THUMBNAIL_AUTOCAPTURE // auto-screen capture
  41. #undef TEST_THUMBNAIL_AUTOCAPTURE
  42.  
  43. #define TEST_THUMBNAIL_WRITE // write capture into separate file
  44. #undef TEST_THUMBNAIL_WRITE
  45.  
  46. #define TEST_THUMBNAIL_REWRITE // write capture into separate file
  47. #undef TEST_THUMBNAIL_REWRITE
  48.  
  49. // Layout of our RichSaveGames
  50. // RICH_GAME_MEDIA_HEADER (as provided)
  51. // optional thumbnail (see RICH_GAME_MEDIA_HEADER)
  52. // uint32 RM_META_DATA_TAG
  53. // uint32 metaDataLength;
  54. // char   metaData[metaDataLength]                   or
  55. // uint32 RM_SAVEDATA_TAG                            uint32 RM_COMPRESSED_SAVEDATA_TAG
  56. // uint32 saveGameDataLength;                        uint32 saveGameDataLength
  57. // char   saveGameData[saveGameDataLength]           uint32 uncompressedDataLength
  58. //                                                   char   [saveGameDataLength-sizeof(uint32)]
  59. // thumbnail is a BMP (can be any size)
  60. // metaData and saveGameData is a non-zero-terminated XMLString
  61. // [saveGameData actually contains meta-data also]
  62. // first metaDataBlock is needed to get metainformation without accessing
  63. // full savegamedata
  64. namespace RichSaveGames
  65. {
  66. // only used if TEST_THUMBNAIL_AUTOCAPTURE
  67. //      static const int THUMBNAIL_DEFAULT_WIDTH  = 256;   // 16:9
  68. //      static const int THUMBNAIL_DEFAULT_HEIGHT = 144;   //
  69. //      static const int THUMBNAIL_DEFAULT_DEPTH = 4;   // write out with alpha
  70. //      static const bool THUMBNAIL_KEEP_ASPECT_RATIO = true; // keep renderes aspect ratio and surround with black borders
  71. // ~only used if TEST_THUMBNAIL_AUTOCAPTURE
  72.  
  73. // our tags in the binary file
  74. static const uint32 RM_METADATA_TAG = MAKEFOURCC('M', 'E', 'T', 'A');            // META
  75. static const uint32 RM_SAVEDATA_TAG = MAKEFOURCC('D', 'A', 'T', 'A');            // DATA
  76. static const uint32 RM_COMPRESSED_SAVEDATA_TAG = MAKEFOURCC('D', 'A', 'T', 'C'); // DATC compressed data
  77. static const uint32 RM_MAGICNUMBER = MAKEFOURCC('R', 'G', 'M', 'H');
  78. //static const char* gGameGUID = // "{8236D2E9-2528-4C5C-ABA3-E0B8B657A297}";
  79. //                                                                                                                                      "{CDC82B4A-7540-45A5-B92E-9A7C7033DBF2}";
  80. }; // ~namespace RichSaveGames
  81.  
  82. //------------------------------------------------------------------------
  83. // COMMON RichSaveGameHelper used by both CPlayerProfileImplFSDir and CPlayerProfileImplFS
  84. //------------------------------------------------------------------------
  85.  
  86. namespace
  87. {
  88. //-----------------------------------------------------------------------------
  89. // Converts a string to a GUID
  90. //-----------------------------------------------------------------------------
  91. const char* ConvertGUIDToString(const RichSaveGames::GUID* pGuid)
  92. {
  93.         static char guidString[64];
  94.         const RichSaveGames::GUID& guid = *pGuid;
  95.         cry_sprintf(guidString, "{%.8X-%.4X-%.4X-%.2X%.2X-%.2X%.2X%.2X%.2X%.2X%.2X}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1],
  96.                     guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
  97.         return guidString;
  98. }
  99.  
  100. //-----------------------------------------------------------------------------
  101. // Converts a string to a GUID
  102. //-----------------------------------------------------------------------------
  103. bool ConvertStringToGUID(const char* strIn, RichSaveGames::GUID* pGuidOut)
  104. {
  105.         unsigned int aiTmp[10];
  106.  
  107.         if (sscanf(strIn, "{%8X-%4X-%4X-%2X%2X-%2X%2X%2X%2X%2X%2X}",
  108.                    &pGuidOut->Data1,
  109.                    &aiTmp[0], &aiTmp[1],
  110.                    &aiTmp[2], &aiTmp[3],
  111.                    &aiTmp[4], &aiTmp[5],
  112.                    &aiTmp[6], &aiTmp[7],
  113.                    &aiTmp[8], &aiTmp[9]) != 11)
  114.         {
  115.                 memset(pGuidOut, 0, sizeof(RichSaveGames::GUID));
  116.                 return false;
  117.         }
  118.         else
  119.         {
  120.                 pGuidOut->Data2 = (uint16) aiTmp[0];
  121.                 pGuidOut->Data3 = (uint16) aiTmp[1];
  122.                 pGuidOut->Data4[0] = (unsigned char) aiTmp[2];
  123.                 pGuidOut->Data4[1] = (unsigned char) aiTmp[3];
  124.                 pGuidOut->Data4[2] = (unsigned char) aiTmp[4];
  125.                 pGuidOut->Data4[3] = (unsigned char) aiTmp[5];
  126.                 pGuidOut->Data4[4] = (unsigned char) aiTmp[6];
  127.                 pGuidOut->Data4[5] = (unsigned char) aiTmp[7];
  128.                 pGuidOut->Data4[6] = (unsigned char) aiTmp[8];
  129.                 pGuidOut->Data4[7] = (unsigned char) aiTmp[9];
  130.                 return true;
  131.         }
  132. }
  133.  
  134. template<class T> void CopyToWideString(T& t, const string& str)
  135. {
  136.         size_t maxCount = std::min(CRY_ARRAY_COUNT(t) - 1, str.length());
  137. #if CRY_PLATFORM_ANDROID
  138.         /*UTF32* wt = reinterpret_cast<UTF32*>(d);
  139.            UTF32** wt_start = &wt;
  140.            const UTF8* out =  reinterpret_cast<const UTF8*>(s);
  141.            const UTF8** out_start = &out;
  142.  
  143.            unsigned int bytes_used;
  144.            if ( ConvertUTF8toUTF32(out_start, out + str.length(),wt_start,wt + maxCount, strictConversion) == conversionOK)
  145.            {
  146.            d[maxCount] = L'\0';
  147.            }
  148.            else
  149.            {
  150.            CryLogAlways("Failed to convert single byte chart to multibyte char");
  151.            d[0] = L'\0';
  152.            }*/
  153.         wstring wstr = CryStringUtils::UTF8ToWStr(str);
  154.         memcpy(t, wstr.c_str(), sizeof(wchar_t) * maxCount);
  155.         t[maxCount] = L'\0';
  156. #else
  157.         wchar_t* d = &t[0];
  158.         const char* s = str.c_str();
  159.         while (maxCount-- > 0)
  160.                 mbtowc(d++, s++, 1);
  161.         *d = L'\0';
  162. #endif
  163. }
  164.  
  165. /*
  166.    // some helpers
  167.    bool SaveXMLFile(const string& filename, const XmlNodeRef& rootNode)
  168.    {
  169.     if (rootNode == 0)
  170.       return true;
  171.  
  172.     const bool ok = rootNode->saveToFile(filename.c_str(), 1024*1024);
  173.     if (!ok)
  174.       GameWarning("[PlayerProfiles] CRichSaveGames: Cannot save XML file '%s'", filename.c_str());
  175.     return ok;
  176.    }
  177.  
  178.    XmlNodeRef LoadXMLFile(const string& filename)
  179.    {
  180.     XmlNodeRef rootNode = GetISystem()->LoadXmlFromFile(filename.c_str());
  181.     if (rootNode == 0)
  182.     {
  183.       GameWarning("[PlayerProfiles] CRichSaveGames: Cannot load XML file '%s'", filename.c_str());
  184.     }
  185.     return rootNode;
  186.    }
  187.  */
  188. };
  189.  
  190. bool ExtractMetaDataFromXML(XmlNodeRef& root, CPlayerProfileManager::SSaveGameMetaData& metaData)
  191. {
  192.         // TODO: use CXmlLoadGame for this
  193.         XmlNodeRef metaDataNode = root;
  194.         if (metaDataNode->isTag("Metadata") == false)
  195.                 metaDataNode = root->findChild("Metadata");
  196.         if (metaDataNode == 0)
  197.                 return false;
  198.         bool ok = true;
  199.         ok &= GetAttr(metaDataNode, "level", metaData.levelName);
  200.         ok &= GetAttr(metaDataNode, "gameRules", metaData.gameRules);
  201.         ok &= GetAttr(metaDataNode, "version", metaData.fileVersion);
  202.         ok &= GetAttr(metaDataNode, "build", metaData.buildVersion);
  203.         ok &= GetTimeAttr(metaDataNode, "saveTime", metaData.saveTime);
  204.         metaData.loadTime = metaData.saveTime;
  205.         metaData.xmlMetaDataNode = metaDataNode;
  206.         return ok;
  207. }
  208.  
  209. string tagToString(uint32 tag)
  210. {
  211.         char tagString[5];
  212.         tagString[0] = (char) (tag & 0xFF);
  213.         tagString[1] = (char) ((tag >> 8) & 0xFF);
  214.         tagString[2] = (char) ((tag >> 16) & 0xFF);
  215.         tagString[3] = (char) ((tag >> 24) & 0xFF);
  216.         tagString[4] = 0;
  217.         return string(tagString);
  218. }
  219.  
  220. // writes out the Tag ID, length, and data (if length > 0), compresses if wanted
  221. bool WriteXMLNode(const uint32 tag, const XmlNodeRef& node, FILE* pFile, bool bCompress, const char* debugFilename = "")
  222. {
  223.         ICryPak* pCryPak = gEnv->pCryPak;
  224.         if (node != 0)
  225.         {
  226.                 _smart_ptr<IXmlStringData> pXmlStrData = node->getXMLData(16000000);
  227.                 size_t xmlDataLength = pXmlStrData->GetStringLength();
  228.  
  229.                 if (debugFilename && *debugFilename)
  230.                 {
  231.                         FILE* pDebugFile = pCryPak->FOpen(debugFilename, "wb");
  232.                         if (pDebugFile)
  233.                         {
  234.                                 pCryPak->FWrite((void*)pXmlStrData->GetString(), pXmlStrData->GetStringLength(), 1, pDebugFile);
  235.                                 pCryPak->FClose(pDebugFile);
  236.                         }
  237.                 }
  238.  
  239.                 if (bCompress == false)
  240.                 {
  241.                         // write the tag
  242.                         pCryPak->FWrite(&tag, 1, pFile);
  243.                         // assert (xmlDataLength <= 0xFFFFFFFF)
  244.                         uint32 dataLength = xmlDataLength;
  245.                         pCryPak->FWrite(&dataLength, 1, pFile);
  246.                         if (xmlDataLength > 0)
  247.                         {
  248.                                 pCryPak->FWrite((const void*)pXmlStrData->GetString(), xmlDataLength, 1, pFile);
  249.                         }
  250.                 }
  251.                 else
  252.                 {
  253.                         char* compressedBuf = static_cast<char*>(pCryPak->PoolMalloc(xmlDataLength));
  254.                         size_t compressedLength = xmlDataLength;
  255.                         bool bOK = gEnv->pSystem->CompressDataBlock((const void*)pXmlStrData->GetString(), xmlDataLength, (void*) compressedBuf, compressedLength);
  256.                         if (!bOK)
  257.                         {
  258.                                 string tagString = tagToString(tag);
  259.                                 GameWarning("CRichSaveGameHelper:WriteXMLNode: Cannot compress data block while writing tag '%s'", tagString.c_str());
  260.                                 pCryPak->PoolFree(compressedBuf);
  261.                                 return false;
  262.                         }
  263.                         // write the tag
  264.                         pCryPak->FWrite(&tag, 1, pFile);
  265.                         uint32 dataLength = (uint32) compressedLength;
  266.                         dataLength += sizeof(uint32); // because we store the uncompressed size as well
  267.                         // write size of the complete tag
  268.                         pCryPak->FWrite(&dataLength, 1, pFile);
  269.                         // write size of uncompressed buffer for decompression later
  270.                         uint32 uncompressedSize = (uint32) xmlDataLength;
  271.                         pCryPak->FWrite(&uncompressedSize, 1, pFile);
  272.                         pCryPak->FWrite((void*) compressedBuf, compressedLength, 1, pFile);
  273.                         pCryPak->PoolFree(compressedBuf);
  274.                 }
  275.         }
  276.         else
  277.         {
  278.                 // write the tag
  279.                 pCryPak->FWrite(&tag, 1, pFile);
  280.                 uint32 dataLength = 0;
  281.                 pCryPak->FWrite(&dataLength, 1, pFile);
  282.         }
  283.         return true;
  284. }
  285.  
  286. bool ReadTag(FILE* pFile, uint32& outTag, bool bRestorePos)
  287. {
  288.         uint32 tmp;
  289.         ICryPak* pCryPak = gEnv->pCryPak;
  290.         long curOffset = pCryPak->FTell(pFile);
  291.         if (pCryPak->FRead(&tmp, 1, pFile) != 1)
  292.                 return false;
  293.         outTag = tmp;
  294.         if (bRestorePos)
  295.                 pCryPak->FSeek(pFile, curOffset, SEEK_SET);
  296.         return true;
  297. }
  298.  
  299. bool SkipXMLTagData(const uint32 tag, FILE* pFile)
  300. {
  301.         uint32 tmp = 0;
  302.         ICryPak* pCryPak = gEnv->pCryPak;
  303.         size_t fileSize = pCryPak->FGetSize(pFile);
  304.         long curOffset = pCryPak->FTell(pFile);
  305.         if (pCryPak->FRead(&tmp, 1, pFile) != 1)
  306.                 return false;
  307.         if (tag != tmp)
  308.         {
  309.                 GameWarning("CRichSaveGameHelper:SkipXMLTagData: Expected tag '%s' not found (read '%s')", tagToString(tag).c_str(), tagToString(tmp).c_str());
  310.                 pCryPak->FSeek(pFile, curOffset, SEEK_SET);
  311.                 return false;
  312.         }
  313.         uint32 len = 0;
  314.         if (pCryPak->FRead(&len, 1, pFile) != 1)
  315.         {
  316.                 GameWarning("CRichSaveGameHelper:SkipXMLTagData: tag='%s': Error while reading stored length", tagToString(tag).c_str());
  317.                 pCryPak->FSeek(pFile, curOffset, SEEK_SET);
  318.                 return false;
  319.         }
  320.  
  321.         // verify that length is somehow reasonable (less than filesize for now)
  322.         if (len > fileSize)
  323.         {
  324.                 GameWarning("CRichSaveGameHelper:SkipXMLTagData: tag='%s': Read size is invalid (read=%d filesize=%d)", tagToString(tag).c_str(), len, (uint32)fileSize);
  325.                 return 0;
  326.         }
  327.  
  328.         pCryPak->FSeek(pFile, len, SEEK_CUR);
  329.         return true;
  330. }
  331.  
  332. char* ReadXMLTagData(const uint32 tag, FILE* pFile, bool bIsCompressed)
  333. {
  334.         uint32 tmp = 0;
  335.         ICryPak* pCryPak = gEnv->pCryPak;
  336.         if (pCryPak->FRead(&tmp, 1, pFile) != 1)
  337.                 return 0;
  338.         if (tag != tmp)
  339.         {
  340.                 GameWarning("CRichSaveGameHelper:ReadXMLTagData: Expected tag '%s' not found (read '%s')", tagToString(tag).c_str(), tagToString(tmp).c_str());
  341.                 return 0;
  342.         }
  343.         uint32 len = 0;
  344.         if (pCryPak->FRead(&len, 1, pFile) != 1)
  345.         {
  346.                 GameWarning("CRichSaveGameHelper:ReadXMLTagData: tag='%s': Error while reading stored length", tagToString(tag).c_str());
  347.                 return 0;
  348.         }
  349.  
  350.         // verify that length is somehow reasonable (less than filesize for now)
  351.         size_t fileSize = pCryPak->FGetSize(pFile);
  352.         if (len > fileSize)
  353.         {
  354.                 GameWarning("CRichSaveGameHelper:ReadXMLTagData: tag='%s': Read size is invalid (read=%d filesize=%d)", tagToString(tag).c_str(), len, (uint32)fileSize);
  355.                 return 0;
  356.         }
  357.  
  358.         uint32 uncompressedSize = 0;
  359.         if (bIsCompressed)
  360.         {
  361.                 if (pCryPak->FRead(&uncompressedSize, 1, pFile) != 1)
  362.                 {
  363.                         GameWarning("CRichSaveGameHelper:ReadXMLTagData: tag='%s': Error while reading uncompresssed length", tagToString(tag).c_str());
  364.                         return 0;
  365.                 }
  366.                 len -= sizeof(uint32);
  367.         }
  368.  
  369.         char* buf = new char[len + 1];
  370.         const size_t readBytes = pCryPak->FReadRaw(buf, 1, len, pFile);
  371.         if (readBytes != len)
  372.         {
  373.                 GameWarning("CRichSaveGameHelper:ReadXMLTagData: tag='%s': Error while reading (read=%" PRISIZE_T " expected=%u)", tagToString(tag).c_str(), readBytes, len);
  374.                 delete[] buf;
  375.                 return 0;
  376.         }
  377.         buf[len] = '\0';
  378.  
  379.         if (bIsCompressed && uncompressedSize > 0)
  380.         {
  381.                 char* uncompressedData = new char[uncompressedSize + 1];
  382.                 if (uncompressedData == 0)
  383.                 {
  384.                         GameWarning("CRichSaveGameHelper:ReadXMLTagData: tag='%s': Error while allocating decompression buffer. (compressedSize=%d, uncompressedSize=%d)", tagToString(tag).c_str(), len, uncompressedSize);
  385.                         delete[] buf;
  386.                         return 0;
  387.                 }
  388.                 size_t longUncompressedSize = uncompressedSize;
  389.                 const bool bOK = gEnv->pSystem->DecompressDataBlock((void*) buf, len, uncompressedData, longUncompressedSize);
  390.                 if (bOK == false)
  391.                 {
  392.                         GameWarning("CRichSaveGameHelper:ReadXMLTagData: tag='%s': Error while decompressing. (compressedSize=%d, uncompressedSize=%d)", tagToString(tag).c_str(), len, uncompressedSize);
  393.                         delete[] uncompressedData;
  394.                         delete[] buf;
  395.                         return 0;
  396.                 }
  397.                 // delete the compressed buffer
  398.                 delete[] buf;
  399.                 // and assign the uncompressed buffer for return value
  400.                 buf = uncompressedData;
  401.                 buf[uncompressedSize] = 0;
  402.         }
  403.         return buf;
  404. }
  405.  
  406. bool ReadRichGameMediaHeader(const char* filename, FILE* pFile, RichSaveGames::RICH_GAME_MEDIA_HEADER& header)
  407. {
  408.         ICryPak* pCryPak = gEnv->pCryPak;
  409.         memset(&header, 0, sizeof(RichSaveGames::RICH_GAME_MEDIA_HEADER));
  410.         size_t len = pCryPak->FRead(&header, 1, pFile);
  411.         if (len != 1 || header.dwMagicNumber != RichSaveGames::RM_MAGICNUMBER)
  412.         {
  413.                 GameWarning("CXMLRichLoadGame:GetSaveGameThumbnail: File '%s' is not a RichSaveGame", filename);
  414.                 pCryPak->FClose(pFile);
  415.                 return false;
  416.         }
  417.  
  418.         const char* guid = ConvertGUIDToString(&header.guidGameId);
  419.         if (strcmp(guid, CCryAction::GetCryAction()->GetGameGUID()) != 0)
  420.         {
  421.                 GameWarning("CXMLRichLoadGame:GetSaveGameThumbnail: GUID '%s' in File '%s' does not match this game's '%s'", guid, filename, CCryAction::GetCryAction()->GetGameGUID());
  422.                 // pCryPak->FClose(pFile);
  423.                 // return false;
  424.         }
  425.         return true;
  426. }
  427.  
  428. bool ReadRichGameMetaData(const string& filename, CPlayerProfileManager::SSaveGameMetaData& metaData)
  429. {
  430.         ICryPak* pCryPak = gEnv->pCryPak;
  431.         FILE* pFile = pCryPak->FOpen(filename, "rbx"); // x=don't chache full file
  432.         if (!pFile)
  433.                 return false;
  434.  
  435.         RichSaveGames::RICH_GAME_MEDIA_HEADER savedHeader;
  436.         if (ReadRichGameMediaHeader(filename.c_str(), pFile, savedHeader) == false)
  437.         {
  438.                 GameWarning("CXMLRichLoadGame:ReadRichGameMetaData: Can't read rich game media header from file '%s'", filename.c_str());
  439.                 pCryPak->FClose(pFile);
  440.                 return false;
  441.         }
  442.  
  443.         // for now, skip thumbnails
  444.         int64 thumbNailOffset = savedHeader.liThumbnailOffset;
  445.         DWORD thumbNailSize = savedHeader.dwThumbnailSize;
  446.         if (thumbNailOffset > 0)
  447.         {
  448.                 pCryPak->FSeek(pFile, (int)thumbNailOffset, SEEK_CUR);
  449.         }
  450.         if (thumbNailSize > 0)
  451.         {
  452.                 pCryPak->FSeek(pFile, thumbNailSize, SEEK_CUR);
  453.         }
  454.  
  455.         const char* const metaDataBuf = ReadXMLTagData(RichSaveGames::RM_METADATA_TAG, pFile, false);
  456.         if (metaDataBuf == 0)
  457.         {
  458.                 GameWarning("CXMLRichLoadGame:ReadRichGameMetaData: Can't read meta data from file '%s'", filename.c_str());
  459.                 pCryPak->FClose(pFile);
  460.                 return false;
  461.         }
  462.  
  463.         XmlNodeRef xmlMetaDataNode = gEnv->pSystem->LoadXmlFromBuffer(metaDataBuf, strlen(metaDataBuf));
  464.         if (xmlMetaDataNode == 0)
  465.         {
  466.                 GameWarning("CXMLRichLoadGame:ReadRichGameMetaData: Can't parse XML meta data from file '%s'", filename.c_str());
  467.                 pCryPak->FClose(pFile);
  468.                 delete[] metaDataBuf;
  469.                 return false;
  470.         }
  471.  
  472.         bool bOK = ExtractMetaDataFromXML(xmlMetaDataNode, metaData);
  473.         if (!bOK)
  474.         {
  475.                 GameWarning("CXMLRichLoadGame:ReadRichGameMetaData: Can't extract XML meta data from file '%s'", filename.c_str());
  476.         }
  477.  
  478.         delete[] metaDataBuf;
  479.         pCryPak->FClose(pFile);
  480.         return bOK;
  481. }
  482.  
  483. bool CRichSaveGameHelper::FetchMetaData(XmlNodeRef& root, CPlayerProfileManager::SSaveGameMetaData& metaData)
  484. {
  485.         return ExtractMetaDataFromXML(root, metaData);
  486. }
  487.  
  488. bool CRichSaveGameHelper::GetSaveGames(CPlayerProfileManager::SUserEntry* pEntry, CPlayerProfileManager::TSaveGameInfoVec& outVec, const char* altProfileName)
  489. {
  490.         // Scan savegames directory for XML files
  491.         // we scan only for save game meta information
  492.         string path;
  493.         string profileName = (altProfileName && *altProfileName) ? altProfileName : pEntry->pCurrentProfile->GetName();
  494.         m_pImpl->InternalMakeFSSaveGamePath(pEntry, profileName, path, true);
  495.  
  496.         const bool bNeedProfilePrefix = m_pImpl->GetManager()->IsSaveGameFolderShared();
  497.         string profilePrefix = profileName;
  498.         profilePrefix += '_';
  499.         size_t profilePrefixLen = profilePrefix.length();
  500.  
  501.         ICryPak* pCryPak = gEnv->pCryPak;
  502.         _finddata_t fd;
  503.  
  504.         path.TrimRight("/\\");
  505.         string search;
  506.         search.Format("%s/*%s", path.c_str(), CRY_SAVEGAME_FILE_EXT);
  507.  
  508.         IPlatformOS* os = GetISystem()->GetPlatformOS();
  509.         unsigned int userIndex = os->UserGetPlayerIndex(profileName);
  510.         IPlatformOS::IFileFinderPtr fileFinder = os->GetFileFinder(userIndex);
  511.         intptr_t handle = fileFinder->FindFirst(search.c_str(), &fd);
  512.  
  513.         if (handle != -1)
  514.         {
  515.                 CPlayerProfileManager::SSaveGameInfo sgInfo;
  516.                 do
  517.                 {
  518.                         if (strcmp(fd.name, ".") == 0 || strcmp(fd.name, "..") == 0)
  519.                                 continue;
  520.  
  521.                         if (bNeedProfilePrefix)
  522.                         {
  523.                                 if (strnicmp(profilePrefix, fd.name, profilePrefixLen) != 0)
  524.                                         continue;
  525.                         }
  526.  
  527.                         sgInfo.name = fd.name;
  528.                         if (bNeedProfilePrefix) // skip profile_ prefix (we made sure this is valid by comparism above)
  529.                                 sgInfo.humanName = fd.name + profilePrefixLen;
  530.                         else
  531.                                 sgInfo.humanName = fd.name;
  532.  
  533.                         PathUtil::RemoveExtension(sgInfo.humanName);
  534.                         sgInfo.description = "no description";
  535.  
  536.                         string filename = path;
  537.                         filename.append("/");
  538.                         filename.append(fd.name);
  539.  
  540.                         bool ok = ReadRichGameMetaData(filename, sgInfo.metaData);
  541.  
  542.                         if (ok)
  543.                         {
  544.                                 outVec.push_back(sgInfo);
  545.                         }
  546.                         else
  547.                         {
  548.                                 GameWarning("CRichSaveGameHelper::GetSaveGames: SaveGame '%s' of user '%s' is invalid", fd.name, pEntry->userId.c_str());
  549.                         }
  550.                 }
  551.                 while (fileFinder->FindNext(handle, &fd) >= 0);
  552.  
  553.                 fileFinder->FindClose(handle);
  554.         }
  555.  
  556.         // temp debug. write out again the bmp files
  557. #ifdef TEST_THUMBNAIL_REWRITE
  558.         for (int i = 0; i < outVec.size(); ++i)
  559.         {
  560.                 CPlayerProfileManager::SSaveGameInfo& sgInfo = outVec[i];
  561.                 CPlayerProfileManager::SThumbnail img;
  562.                 if (GetSaveGameThumbnail(pEntry, sgInfo.name, img))
  563.                 {
  564.                         string newName = path;
  565.                         newName.append("/");
  566.                         newName.append(sgInfo.name);
  567.                         newName.append("_new.bmp");
  568.                         BMPHelper::SaveBMP(newName, (uint8*) img.data.begin(), img.width, img.height, img.depth, true);
  569.                 }
  570.         }
  571.  
  572. #endif
  573.  
  574.         return true;
  575. }
  576.  
  577. class CXMLRichSaveGame : public CXmlSaveGame
  578. {
  579. public:
  580.         CXMLRichSaveGame(ICommonProfileImpl* pImpl, CPlayerProfileImplFSDir::SUserEntry* pEntry)
  581.         {
  582.                 m_pProfileImpl = pImpl;
  583.                 m_pEntry = pEntry;
  584.                 m_thumbnailWidth = 0;
  585.                 m_thumbnailHeight = 0;
  586.                 m_thumbnailDepth = 0;
  587.                 assert(m_pProfileImpl != 0);
  588.                 assert(m_pEntry != 0);
  589.         }
  590.  
  591.         // ILoadGame
  592.         virtual bool Init(const char* name)
  593.         {
  594.                 assert(m_pEntry->pCurrentProfile != 0);
  595.                 if (m_pEntry->pCurrentProfile == 0)
  596.                 {
  597.                         GameWarning("CXMLRichSaveGame: Entry for user '%s' has no current profile", m_pEntry->userId.c_str());
  598.                         return false;
  599.                 }
  600.  
  601. #ifdef TEST_THUMBNAIL_AUTOCAPTURE
  602.                 // the image file we write out is always in 16:9 format, e.g. 256x144
  603.                 // or scaled depending on renderer height
  604.                 const int h = gEnv->pRenderer->GetHeight();
  605.                 const int imageDepth = RichSaveGames::THUMBNAIL_DEFAULT_DEPTH;
  606.                 int imageHeight = std::min(RichSaveGames::THUMBNAIL_DEFAULT_HEIGHT, h);
  607.                 int imageWidth = imageHeight * 16 / 9;
  608.                 SetThumbnail(0, imageWidth, imageHeight, imageDepth);
  609. #endif
  610.  
  611.                 string path;
  612.                 m_pProfileImpl->InternalMakeFSSaveGamePath(m_pEntry, m_pEntry->pCurrentProfile->GetName(), path, false);
  613.                 // make directory or use the SaveXMLFile helper function
  614.                 // CryCreateDirectory(...)
  615.                 string strippedName = PathUtil::GetFile(name);
  616.                 path.append(strippedName);
  617.                 return CXmlSaveGame::Init(path.c_str());
  618.         }
  619.  
  620.         // BGR or BGRA
  621.         virtual uint8* SetThumbnail(const uint8* imageData, int width, int height, int depth)
  622.         {
  623.                 m_thumbnailWidth = width;
  624.                 m_thumbnailHeight = height;
  625.                 m_thumbnailDepth = depth;
  626.  
  627.                 size_t size = width * height * depth;
  628.                 m_thumbnailData.resize(size);
  629.                 if (imageData)
  630.                         memcpy(m_thumbnailData.begin(), imageData, size);
  631.                 else
  632.                 {
  633.                         if (m_thumbnailDepth == 3)
  634.                         {
  635.                                 uint8* p = (uint8*) m_thumbnailData.begin();
  636.                                 size_t n = size;
  637.                                 while (n)
  638.                                 {
  639.                                         *p++ = 0x00; // B
  640.                                         *p++ = 0x00; // G
  641.                                         *p++ = 0x00; // R
  642.                                         n -= 3;
  643.                                 }
  644.                         }
  645.                         else if (m_thumbnailDepth == 4)
  646.                         {
  647.                                 const uint32 col = RGBA8(0x00, 0x00, 0x00, 0x00); // alpha see through
  648.                                 uint32* p = (uint32*) m_thumbnailData.begin();
  649.                                 size_t n = size >> 2;
  650.                                 while (n--)
  651.                                         *p++ = col;
  652.                         }
  653.                         else
  654.                         {
  655.                                 memset(m_thumbnailData.begin(), 0, size);
  656.                         }
  657.                 }
  658.                 return m_thumbnailData.begin();
  659.         }
  660.  
  661.         virtual bool SetThumbnailFromBMP(const char* filename)
  662.         {
  663.                 int width = 0;
  664.                 int height = 0;
  665.                 int depth = 0;
  666.                 bool bSuccess = BMPHelper::LoadBMP(filename, 0, width, height, depth, true);
  667.                 if (bSuccess)
  668.                 {
  669.                         CPlayerProfileManager::SThumbnail thumbnail;
  670.                         thumbnail.data.resize(width * height * depth);
  671.                         bSuccess = BMPHelper::LoadBMP(filename, thumbnail.data.begin(), width, height, depth, true);
  672.                         if (bSuccess)
  673.                         {
  674.                                 SetThumbnail(thumbnail.data.begin(), width, height, depth);
  675.                         }
  676.                 }
  677.                 return bSuccess;
  678.         }
  679.  
  680.         size_t CalcThumbnailSize()
  681.         {
  682.                 if (m_thumbnailWidth * m_thumbnailHeight * m_thumbnailDepth == 0)
  683.                         return 0;
  684.                 const size_t size = BMPHelper::CalcBMPSize(m_thumbnailWidth, m_thumbnailHeight, m_thumbnailDepth);
  685.                 return size;
  686.         }
  687.  
  688.         virtual bool Write(const char* filename, XmlNodeRef data)
  689.         {
  690.                 ICryPak* pCryPak = gEnv->pCryPak;
  691.                 FILE* pFile = pCryPak->FOpen(filename, "wb");
  692.                 if (!pFile)
  693.                         return false;
  694.  
  695.                 DWORD thumbnailSize = CalcThumbnailSize();
  696.  
  697.                 const string fname(filename);
  698.                 // fill in RSF
  699.  
  700.                 RichSaveGames::RICH_GAME_MEDIA_HEADER savedHeader;
  701.                 memset(&savedHeader, 0, sizeof(RichSaveGames::RICH_GAME_MEDIA_HEADER));
  702.                 savedHeader.dwMagicNumber = RichSaveGames::RM_MAGICNUMBER;
  703.                 savedHeader.dwHeaderVersion = 1;
  704.                 savedHeader.dwHeaderSize = sizeof(RichSaveGames::RICH_GAME_MEDIA_HEADER);
  705.                 // Change this string to the gameID GUID found in the game's GDF file
  706.                 ConvertStringToGUID(CCryAction::GetCryAction()->GetGameGUID(), &savedHeader.guidGameId);
  707.                 // Point to the embedded thumbnail (optional)
  708.                 // The offset it relative to the end of the RICH_GAME_MEDIA_HEADER structure.
  709.                 savedHeader.liThumbnailOffset = 0; // put it right RSF header
  710.                 savedHeader.dwThumbnailSize = thumbnailSize;
  711.  
  712.                 CopyToWideString(savedHeader.szSaveName, fname);
  713.                 savedHeader.szComments[0] = L'\0';
  714.  
  715.                 CPlayerProfileManager::SSaveGameMetaData metaData;
  716.                 if (ExtractMetaDataFromXML(data, metaData))
  717.                 {
  718.                         CopyToWideString(savedHeader.szGameName, metaData.gameRules);
  719.                         CopyToWideString(savedHeader.szLevelName, metaData.levelName);
  720.                 }
  721.                 else
  722.                 {
  723.                         savedHeader.szGameName[0] = L'\0';
  724.                         savedHeader.szLevelName[0] = L'\0';
  725.                 }
  726.  
  727.                 // write out header
  728.                 pCryPak->FWrite(&savedHeader, 1, pFile);
  729.  
  730.                 const bool bFlipImage = false;
  731. #ifdef TEST_THUMBNAIL_AUTOCAPTURE
  732.                 // debug: get screen shot here
  733.                 if (thumbnailSize > 0)
  734.                 {
  735.                         int w = gEnv->pRenderer->GetWidth();
  736.                         int h = gEnv->pRenderer->GetHeight();
  737.  
  738.                         // initialize to stretch thumbnail
  739.                         int captureDestWidth = m_thumbnailWidth;
  740.                         int captureDestHeight = m_thumbnailHeight;
  741.                         int captureDestOffX = 0;
  742.                         int captureDestOffY = 0;
  743.  
  744.                         const bool bKeepAspectRatio = RichSaveGames::THUMBNAIL_KEEP_ASPECT_RATIO;
  745.  
  746.                         // should we keep the aspect ratio of the renderer?
  747.                         if (bKeepAspectRatio)
  748.                         {
  749.                                 captureDestHeight = m_thumbnailHeight;
  750.                                 captureDestWidth = captureDestHeight * w / h;
  751.  
  752.                                 // adjust for SCOPE formats, like 2.35:1
  753.                                 if (captureDestWidth > RichSaveGames::THUMBNAIL_DEFAULT_WIDTH)
  754.                                 {
  755.                                         captureDestHeight = captureDestHeight * RichSaveGames::THUMBNAIL_DEFAULT_WIDTH / captureDestWidth;
  756.                                         captureDestWidth = RichSaveGames::THUMBNAIL_DEFAULT_WIDTH;
  757.                                 }
  758.  
  759.                                 captureDestOffX = (m_thumbnailWidth - captureDestWidth) * 0.5f;
  760.                                 captureDestOffY = (m_thumbnailHeight - captureDestHeight) * 0.5f;
  761.  
  762.                                 // CryLogAlways("CXMLRichSaveGame: TEST_THUMBNAIL_AUTOCAPTURE: capWidth=%d capHeight=%d (off=%d,%d) thmbw=%d thmbh=%d rw=%d rh=%d",
  763.                                 //      captureDestWidth, captureDestHeight, captureDestOffX, captureDestOffY, m_thumbnailWidth, m_thumbnailHeight, w,h);
  764.  
  765.                                 if (captureDestWidth > m_thumbnailWidth || captureDestHeight > m_thumbnailHeight)
  766.                                 {
  767.                                         assert(false);
  768.                                         GameWarning("CXMLRichSaveGame: TEST_THUMBNAIL_AUTOCAPTURE: capWidth=%d capHeight=%d", captureDestWidth, captureDestHeight);
  769.                                         captureDestHeight = m_thumbnailHeight;
  770.                                         captureDestWidth = m_thumbnailWidth;
  771.                                         captureDestOffX = captureDestOffY = 0;
  772.                                 }
  773.                         }
  774.  
  775.                         const bool bAlpha = m_thumbnailDepth == 4;
  776.                         const int bpl = m_thumbnailWidth * m_thumbnailDepth;
  777.                         uint8* pBuf = m_thumbnailData.begin() + captureDestOffY * bpl + captureDestOffX * m_thumbnailDepth;
  778.                         gEnv->pRenderer->ReadFrameBufferFast(pBuf, m_thumbnailWidth, w, h, eRB_BackBuffer, bAlpha, captureDestWidth, captureDestHeight); // no inverse needed
  779.                         // gEnv->pRenderer->ReadFrameBufferFast((uint32*)pBuf, m_thumbnailWidth, m_thumbnailHeight); // needs inverse
  780.                         // bFlipImage = true;
  781.                 }
  782. #endif
  783.  
  784. #ifdef TEST_THUMBNAIL_WRITE
  785.                 // write thumbnail also separately for debugging
  786.                 // write out thumbnail
  787.                 if (thumbnailSize > 0)
  788.                 {
  789.                         string imgName = PathUtil::ReplaceExtension(fname, ".bmp");
  790.                         BMPHelper::SaveBMP(imgName, m_thumbnailData.begin(), m_thumbnailWidth, m_thumbnailHeight, m_thumbnailDepth, bFlipImage);
  791.                 }
  792. #endif
  793.  
  794.                 // write out thumbnail into savegame
  795.                 if (thumbnailSize > 0)
  796.                 {
  797.                         BMPHelper::SaveBMP(pFile, m_thumbnailData.begin(), m_thumbnailWidth, m_thumbnailHeight, m_thumbnailDepth, bFlipImage);
  798.                 }
  799.  
  800.                 WriteXMLNode(RichSaveGames::RM_METADATA_TAG, data->findChild("Metadata"), pFile, false);
  801.  
  802.                 string debugFilename;
  803.                 if (CPlayerProfileManager::sRSFDebugWrite != 0)
  804.                         debugFilename = PathUtil::ReplaceExtension(fname, ".xml");
  805.  
  806. #if defined RSF_USE_COMPRESSION
  807.                 // try to write compressed
  808.                 const bool bOK = WriteXMLNode(RichSaveGames::RM_COMPRESSED_SAVEDATA_TAG, data, pFile, true, debugFilename);
  809.                 if (bOK == false) // try to write uncompressed
  810.                 {
  811.                         WriteXMLNode(RichSaveGames::RM_SAVEDATA_TAG, data, pFile, false, debugFilename);
  812.                 }
  813. #else
  814.                 WriteXMLNode(RichSaveGames::RM_SAVEDATA_TAG, data, pFile, false, debugFilename);
  815. #endif
  816.  
  817.                 pCryPak->FClose(pFile);
  818.  
  819.                 return true;
  820.         }
  821.  
  822.         ICommonProfileImpl*                  m_pProfileImpl;
  823.         CPlayerProfileImplFSDir::SUserEntry* m_pEntry;
  824.         DynArray<uint8>                      m_thumbnailData;
  825.         int m_thumbnailWidth;
  826.         int m_thumbnailHeight;
  827.         int m_thumbnailDepth;
  828.  
  829. };
  830.  
  831. ISaveGame* CRichSaveGameHelper::CreateSaveGame(CPlayerProfileManager::SUserEntry* pEntry)
  832. {
  833.         return new CXMLRichSaveGame(m_pImpl, pEntry);
  834. }
  835.  
  836. class CXMLRichLoadGame : public CXmlLoadGame
  837. {
  838. public:
  839.         CXMLRichLoadGame(ICommonProfileImpl* pImpl, CPlayerProfileImplFSDir::SUserEntry* pEntry)
  840.         {
  841.                 m_pImpl = pImpl;
  842.                 m_pEntry = pEntry;
  843.                 assert(m_pImpl != 0);
  844.                 assert(m_pEntry != 0);
  845.         }
  846.  
  847.         // ILoadGame
  848.         virtual bool Init(const char* name)
  849.         {
  850.                 assert(m_pEntry->pCurrentProfile != 0);
  851.                 if (m_pEntry->pCurrentProfile == 0)
  852.                 {
  853.                         GameWarning("CXMLRichLoadGame: Entry for user '%s' has no current profile", m_pEntry->userId.c_str());
  854.                         return false;
  855.                 }
  856.  
  857.                 string filename;
  858.                 // figure out, if 'name' is an absolute path or a profile-relative path
  859.                 if (gEnv->pCryPak->IsAbsPath(name) == false)
  860.                 {
  861.                         // no full path, assume 'name' is local to profile directory
  862.                         bool bNeedFolder = true;
  863.                         if (m_pImpl->GetManager()->IsSaveGameFolderShared())
  864.                         {
  865.                                 // if the savegame's name doesn't start with a profile_ prefix
  866.                                 // add one (for quickload)
  867.                                 string profilePrefix = m_pEntry->pCurrentProfile->GetName();
  868.                                 profilePrefix.append("_");
  869.                                 size_t profilePrefixLen = profilePrefix.length();
  870.                                 if (strnicmp(name, profilePrefix, profilePrefixLen) != 0)
  871.                                         bNeedFolder = false;
  872.                         }
  873.                         m_pImpl->InternalMakeFSSaveGamePath(m_pEntry, m_pEntry->pCurrentProfile->GetName(), filename, bNeedFolder);
  874.                         string strippedName = PathUtil::GetFile(name);
  875.                         filename.append(strippedName);
  876.                 }
  877.                 else
  878.                 {
  879.                         // it's an abs path, assign it
  880.                         filename.assign(name);
  881.                 }
  882.  
  883.                 ICryPak* pCryPak = gEnv->pCryPak;
  884.                 FILE* pFile = pCryPak->FOpen(filename, "rbx"); // x=don't chache full file
  885.                 if (!pFile)
  886.                         return false;
  887.  
  888.                 RichSaveGames::RICH_GAME_MEDIA_HEADER savedHeader;
  889.                 if (ReadRichGameMediaHeader(filename.c_str(), pFile, savedHeader) == false)
  890.                 {
  891.                         GameWarning("CXMLRichLoadGame:GetSaveGameThumbnail: Can't read rich game media header from file '%s'", filename.c_str());
  892.                         return false;
  893.                 }
  894.  
  895.                 // for now, skip thumbnails
  896.                 int64 thumbNailOffset = savedHeader.liThumbnailOffset;
  897.                 DWORD thumbNailSize = savedHeader.dwThumbnailSize;
  898.                 if (thumbNailOffset > 0)
  899.                 {
  900.                         pCryPak->FSeek(pFile, (int)thumbNailOffset, SEEK_CUR);
  901.                 }
  902.                 if (thumbNailSize > 0)
  903.                 {
  904.                         pCryPak->FSeek(pFile, thumbNailSize, SEEK_CUR);
  905.                 }
  906.  
  907.                 bool bSuccess = SkipXMLTagData(RichSaveGames::RM_METADATA_TAG, pFile);
  908.                 if (bSuccess == false)
  909.                 {
  910.                         GameWarning("CXMLRichLoadGame: Cannot read metadata for file '%s'.", filename.c_str());
  911.                         pCryPak->FClose(pFile);
  912.                         return false;
  913.                 }
  914.  
  915.                 uint32 dataTag = 0;
  916.                 const bool bOK = ReadTag(pFile, dataTag, true);
  917.                 if (!bOK)
  918.                 {
  919.                         GameWarning("CXMLRichLoadGame: Cannot read data tag for file '%s'.", filename.c_str());
  920.                         pCryPak->FClose(pFile);
  921.                         return false;
  922.                 }
  923.  
  924.                 char* dataBuf = 0;
  925.                 if (dataTag == RichSaveGames::RM_SAVEDATA_TAG)
  926.                 {
  927.                         dataBuf = ReadXMLTagData(RichSaveGames::RM_SAVEDATA_TAG, pFile, false);
  928.                 }
  929.                 else if (dataTag == RichSaveGames::RM_COMPRESSED_SAVEDATA_TAG)
  930.                 {
  931.                         dataBuf = ReadXMLTagData(RichSaveGames::RM_COMPRESSED_SAVEDATA_TAG, pFile, true);
  932.                 }
  933.                 else
  934.                 {
  935.                         const string tagString = tagToString(dataTag);
  936.                         GameWarning("CXMLRichLoadGame: Unknown data tag'%s'.", tagString.c_str());
  937.                 }
  938.  
  939.                 if (dataBuf == 0)
  940.                 {
  941.                         GameWarning("CXMLRichLoadGame: Cannot read data for file '%s'.", filename.c_str());
  942.                         pCryPak->FClose(pFile);
  943.                         return false;
  944.                 }
  945.  
  946.                 // write out
  947.                 if (CPlayerProfileManager::sRSFDebugWriteOnLoad)
  948.                 {
  949.                         string outUncompress = filename;
  950.                         PathUtil::RemoveExtension(outUncompress);
  951.                         outUncompress.append("_uncompressed.xml");
  952.                         FILE* const pUnFile = pCryPak->FOpen(outUncompress.c_str(), "wb");
  953.                         if (pUnFile)
  954.                         {
  955.                                 pCryPak->FWrite(dataBuf, strlen(dataBuf), pUnFile);
  956.                                 pCryPak->FClose(pUnFile);
  957.                         }
  958.                 }
  959.  
  960.                 // parse the file
  961.                 XmlNodeRef xmlRootNode = gEnv->pSystem->LoadXmlFromBuffer(dataBuf, strlen(dataBuf));
  962.                 if (xmlRootNode == 0)
  963.                 {
  964.                         GameWarning("CXMLRichLoadGame: Cannot parse XML Data in '%s'", filename.c_str());
  965.                         delete[] dataBuf;
  966.                         pCryPak->FClose(pFile);
  967.                         return false;
  968.                 }
  969.                 delete[] dataBuf;
  970.                 pCryPak->FClose(pFile);
  971.                 return CXmlLoadGame::Init(xmlRootNode, name);
  972.         }
  973.  
  974.         ICommonProfileImpl*                  m_pImpl;
  975.         CPlayerProfileImplFSDir::SUserEntry* m_pEntry;
  976.  
  977. };
  978.  
  979. ILoadGame* CRichSaveGameHelper::CreateLoadGame(CPlayerProfileManager::SUserEntry* pEntry)
  980. {
  981.         return new CXMLRichLoadGame(m_pImpl, pEntry);
  982. }
  983.  
  984. bool CRichSaveGameHelper::DeleteSaveGame(CPlayerProfileManager::SUserEntry* pEntry, const char* name)
  985. {
  986.         string filename;
  987.         m_pImpl->InternalMakeFSSaveGamePath(pEntry, pEntry->pCurrentProfile->GetName(), filename, true);
  988.         string strippedName = PathUtil::GetFile(name);
  989.         filename.append(strippedName);
  990.         bool bOK = gEnv->pCryPak->RemoveFile(filename.c_str());
  991.         if (bOK && CPlayerProfileManager::sRSFDebugWrite != 0)
  992.         {
  993.                 // remove the debug .xml as well
  994.                 filename = PathUtil::ReplaceExtension(filename, ".xml");
  995.                 gEnv->pCryPak->RemoveFile(filename.c_str());
  996.         }
  997.         return bOK;
  998. }
  999.  
  1000. bool CRichSaveGameHelper::MoveSaveGames(CPlayerProfileManager::SUserEntry* pEntry, const char* oldProfileName, const char* newProfileName)
  1001. {
  1002.         // move savegames or, if savegame folder is shared, rename them
  1003.         string oldSaveGamesPath;
  1004.         string newSaveGamesPath;
  1005.         m_pImpl->InternalMakeFSSaveGamePath(pEntry, oldProfileName, oldSaveGamesPath, true);
  1006.         m_pImpl->InternalMakeFSSaveGamePath(pEntry, newProfileName, newSaveGamesPath, true);
  1007.  
  1008.         CPlayerProfileManager* pMgr = m_pImpl->GetManager();
  1009.         if (pMgr->IsSaveGameFolderShared() == false)
  1010.         {
  1011.                 // move complete folder
  1012.                 pMgr->MoveFileHelper(oldSaveGamesPath, newSaveGamesPath);
  1013.         }
  1014.         else
  1015.         {
  1016.                 // save game folder is shared, move file by file
  1017.                 CPlayerProfileManager::TSaveGameInfoVec saveGameInfoVec;
  1018.                 if (GetSaveGames(pEntry, saveGameInfoVec, oldProfileName))
  1019.                 {
  1020.                         CPlayerProfileManager::TSaveGameInfoVec::iterator iter = saveGameInfoVec.begin();
  1021.                         CPlayerProfileManager::TSaveGameInfoVec::iterator iterEnd = saveGameInfoVec.end();
  1022.                         string oldPrefix = oldProfileName;
  1023.                         oldPrefix += "_";
  1024.                         size_t oldPrefixLen = oldPrefix.length();
  1025.                         string newPrefix = newProfileName;
  1026.                         newPrefix += "_";
  1027.                         while (iter != iterEnd)
  1028.                         {
  1029.                                 const string& oldSGName = iter->name;
  1030.                                 // begins with old profile's prefix?
  1031.                                 if (strnicmp(oldSGName, oldPrefix, oldPrefixLen) == 0)
  1032.                                 {
  1033.                                         string newSGName = newPrefix;
  1034.                                         newSGName.append(oldSGName, oldPrefixLen, oldSGName.length() - oldPrefixLen);
  1035.                                         string oldPath = oldSaveGamesPath + oldSGName;
  1036.                                         string newPath = newSaveGamesPath + newSGName;
  1037.                                         pMgr->MoveFileHelper(oldPath, newPath); // savegame
  1038.  
  1039.                                         if (CPlayerProfileManager::sRSFDebugWrite != 0)
  1040.                                         {
  1041.                                                 // in case we wrote some debug savegames, remove it as well
  1042.                                                 oldPath = PathUtil::ReplaceExtension(oldPath, ".xml");
  1043.                                                 newPath = PathUtil::ReplaceExtension(newPath, ".xml");
  1044.                                                 pMgr->MoveFileHelper(oldPath, newPath); // debug xml file
  1045.                                         }
  1046.                                 }
  1047.                                 ++iter;
  1048.                         }
  1049.                 }
  1050.         }
  1051.         return true;
  1052. }
  1053.  
  1054. bool CRichSaveGameHelper::GetSaveGameThumbnail(CPlayerProfileManager::SUserEntry* pEntry, const char* saveGameName, CPlayerProfileManager::SThumbnail& thumbnail)
  1055. {
  1056.         assert(pEntry->pCurrentProfile != 0);
  1057.         if (pEntry->pCurrentProfile == 0)
  1058.         {
  1059.                 GameWarning("CXMLRichLoadGame:GetSaveGameThumbnail: Entry for user '%s' has no current profile", pEntry->userId.c_str());
  1060.                 return false;
  1061.         }
  1062.         const char* name = saveGameName;
  1063.         string filename;
  1064.         m_pImpl->InternalMakeFSSaveGamePath(pEntry, pEntry->pCurrentProfile->GetName(), filename, true);
  1065.         string strippedName = PathUtil::GetFile(name);
  1066.         filename.append(strippedName);
  1067.  
  1068.         ICryPak* pCryPak = gEnv->pCryPak;
  1069.         FILE* pFile = pCryPak->FOpen(filename, "rbx"); // x=don't chache full file
  1070.         if (!pFile)
  1071.                 return false;
  1072.  
  1073.         RichSaveGames::RICH_GAME_MEDIA_HEADER savedHeader;
  1074.         if (ReadRichGameMediaHeader(filename.c_str(), pFile, savedHeader) == false)
  1075.         {
  1076.                 GameWarning("CXMLRichLoadGame:GetSaveGameThumbnail: Can't read rich game media header from file '%s'", filename.c_str());
  1077.                 return false;
  1078.         }
  1079.  
  1080.         bool bSuccess = false;
  1081.         int64 thumbNailOffset = savedHeader.liThumbnailOffset;
  1082.         DWORD thumbNailSize = savedHeader.dwThumbnailSize;
  1083.         if (thumbNailOffset > 0)
  1084.         {
  1085.                 pCryPak->FSeek(pFile, (int)thumbNailOffset, SEEK_CUR);
  1086.         }
  1087.         if (thumbNailSize > 0)
  1088.         {
  1089.                 int width = 0;
  1090.                 int height = 0;
  1091.                 int depth = 0;
  1092.                 bSuccess = BMPHelper::LoadBMP(pFile, 0, width, height, depth);
  1093.                 if (bSuccess)
  1094.                 {
  1095.                         thumbnail.data.resize(width * height * depth);
  1096.                         bSuccess = BMPHelper::LoadBMP(pFile, thumbnail.data.begin(), width, height, depth);
  1097.                         if (bSuccess)
  1098.                         {
  1099.                                 thumbnail.height = height;
  1100.                                 thumbnail.width = width;
  1101.                                 thumbnail.depth = depth;
  1102.                         }
  1103.                 }
  1104.         }
  1105.         pCryPak->FClose(pFile);
  1106.         if (!bSuccess)
  1107.                 thumbnail.ReleaseData();
  1108.         return bSuccess;
  1109. }
  1110.  
downloadPlayerProfileImplRSFHelper.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