3
0

SaveData_SystemComponent_Windows.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <SaveDataSystemComponent.h>
  9. #include <AzCore/IO/SystemFile.h>
  10. #include <AzCore/std/string/conversions.h>
  11. #include <AzCore/Utils/Utils.h>
  12. #include <AzCore/PlatformIncl.h>
  13. #include <shlobj.h>
  14. ////////////////////////////////////////////////////////////////////////////////////////////////////
  15. namespace SaveData
  16. {
  17. ////////////////////////////////////////////////////////////////////////////////////////////////
  18. //! Platform specific implementation for the save data system component on Windows
  19. class SaveDataSystemComponentWindows : public SaveDataSystemComponent::Implementation
  20. {
  21. public:
  22. ////////////////////////////////////////////////////////////////////////////////////////////
  23. static constexpr const char* DefaultSaveDataDirectoryName = "SaveData";
  24. ////////////////////////////////////////////////////////////////////////////////////////////
  25. // Allocator
  26. AZ_CLASS_ALLOCATOR(SaveDataSystemComponentWindows, AZ::SystemAllocator);
  27. ////////////////////////////////////////////////////////////////////////////////////////////
  28. //! Constructor
  29. //! \param[in] saveDataSystemComponent Reference to the parent being implemented
  30. SaveDataSystemComponentWindows(SaveDataSystemComponent& saveDataSystemComponent);
  31. ////////////////////////////////////////////////////////////////////////////////////////////
  32. //! Destructor
  33. ~SaveDataSystemComponentWindows() override;
  34. protected:
  35. ////////////////////////////////////////////////////////////////////////////////////////////
  36. //! \ref SaveData::SaveDataSystemComponent::Implementation::SaveDataBuffer
  37. void SaveDataBuffer(const SaveDataRequests::SaveDataBufferParams& saveDataBufferParams) override;
  38. ////////////////////////////////////////////////////////////////////////////////////////////
  39. //! \ref SaveData::SaveDataSystemComponent::Implementation::LoadDataBuffer
  40. void LoadDataBuffer(const SaveDataRequests::LoadDataBufferParams& loadDataBufferParams) override;
  41. ////////////////////////////////////////////////////////////////////////////////////////////
  42. //! \ref SaveData::SaveDataSystemComponent::Implementation::SetSaveDataDirectoryPath
  43. void SetSaveDataDirectoryPath(const char* saveDataDirectoryPath) override;
  44. private:
  45. ////////////////////////////////////////////////////////////////////////////////////////////
  46. //! Convenience function to construct the full save data file path.
  47. //! \param[in] dataBufferName The name of the save data buffer.
  48. //! \param[in] localUserId The local user id the save data buffer is associated with.
  49. AZStd::string GetSaveDataFilePath(const AZStd::string& dataBufferName,
  50. AzFramework::LocalUserId localUserId);
  51. ////////////////////////////////////////////////////////////////////////////////////////////
  52. //! The absolute path to the application's save data dircetory.
  53. AZStd::string m_saveDataDircetoryPathAbsolute;
  54. };
  55. ////////////////////////////////////////////////////////////////////////////////////////////////
  56. AZStd::string GetDefaultWindowsUserSaveDataPath()
  57. {
  58. // Unfortunately, there is no universally accepted default "Save Data" directory on Windows,
  59. // so we are forced to choose between the following commonly used user save data locations:
  60. //
  61. // C:\Users\{username}\AppData\Local (FOLDERID_LocalAppData)
  62. // C:\Users\{username}\AppData\Roaming (FOLDERID_RoamingAppData)
  63. // C:\Users\{username}\Documents (FOLDERID_Documents)
  64. // C:\Users\{username}\Saved Games (FOLDERID_SavedGames)
  65. //
  66. // which are all best retrieved using the Windows SHGetKnownFolderPath function:
  67. // https://docs.microsoft.com/en-us/windows/desktop/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath
  68. // Get the 'known folder path'
  69. wchar_t* knownFolderPathUTF16 = nullptr;
  70. [[maybe_unused]] long result = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &knownFolderPathUTF16);
  71. AZ_Assert(SUCCEEDED(result), "SHGetKnownFolderPath could not retrieve LocalAppData folder");
  72. // Convert it from UTF-16 to UTF-8
  73. AZStd::string defaultWindowsUserSaveDataPathUTF8;
  74. AZStd::to_string(defaultWindowsUserSaveDataPathUTF8, AZStd::wstring(knownFolderPathUTF16));
  75. // Free the memory allocated by SHGetKnownFolderPath
  76. CoTaskMemFree(knownFolderPathUTF16);
  77. // Append the trailing path separator and return
  78. defaultWindowsUserSaveDataPathUTF8 += '\\';
  79. return defaultWindowsUserSaveDataPathUTF8;
  80. }
  81. ////////////////////////////////////////////////////////////////////////////////////////////////
  82. AZStd::string GetExecutableName()
  83. {
  84. char moduleFileName[AZ_MAX_PATH_LEN];
  85. AZ::Utils::GetExecutablePath(moduleFileName, AZ_MAX_PATH_LEN);
  86. const AZStd::string moduleFileNameString(moduleFileName);
  87. const size_t executableNameStart = moduleFileNameString.find_last_of('\\') + 1;
  88. const size_t executableNameEnd = moduleFileNameString.find_last_of('.');
  89. const size_t executableNameLength = executableNameEnd - executableNameStart;
  90. AZ_Assert(executableNameLength > 0, "Could not extract executable name from: %s", moduleFileName);
  91. return moduleFileNameString.substr(executableNameStart, executableNameLength);
  92. }
  93. ////////////////////////////////////////////////////////////////////////////////////////////////
  94. bool IsAbsolutePath(const char* path)
  95. {
  96. char drive[16];
  97. _splitpath_s(path, drive, 16, nullptr, 0, nullptr, 0, nullptr, 0);
  98. return strlen(drive) > 0;
  99. }
  100. ////////////////////////////////////////////////////////////////////////////////////////////////
  101. SaveDataSystemComponent::Implementation* SaveDataSystemComponent::Implementation::Create(SaveDataSystemComponent& saveDataSystemComponent)
  102. {
  103. return aznew SaveDataSystemComponentWindows(saveDataSystemComponent);
  104. }
  105. ////////////////////////////////////////////////////////////////////////////////////////////////
  106. SaveDataSystemComponentWindows::SaveDataSystemComponentWindows(SaveDataSystemComponent& saveDataSystemComponent)
  107. : SaveDataSystemComponent::Implementation(saveDataSystemComponent)
  108. , m_saveDataDircetoryPathAbsolute(GetDefaultWindowsUserSaveDataPath() +
  109. GetExecutableName() + "\\" +
  110. DefaultSaveDataDirectoryName + "\\")
  111. {
  112. }
  113. ////////////////////////////////////////////////////////////////////////////////////////////////
  114. SaveDataSystemComponentWindows::~SaveDataSystemComponentWindows()
  115. {
  116. }
  117. ////////////////////////////////////////////////////////////////////////////////////////////////
  118. void SaveDataSystemComponentWindows::SaveDataBuffer(const SaveDataRequests::SaveDataBufferParams& saveDataBufferParams)
  119. {
  120. const AZStd::string& absoluteFilePath = GetSaveDataFilePath(saveDataBufferParams.dataBufferName,
  121. saveDataBufferParams.localUserId);
  122. SaveDataBufferToFileSystem(saveDataBufferParams, absoluteFilePath);
  123. }
  124. ////////////////////////////////////////////////////////////////////////////////////////////////
  125. void SaveDataSystemComponentWindows::LoadDataBuffer(const SaveDataRequests::LoadDataBufferParams& loadDataBufferParams)
  126. {
  127. const AZStd::string& absoluteFilePath = GetSaveDataFilePath(loadDataBufferParams.dataBufferName,
  128. loadDataBufferParams.localUserId);
  129. LoadDataBufferFromFileSystem(loadDataBufferParams, absoluteFilePath);
  130. }
  131. ////////////////////////////////////////////////////////////////////////////////////////////////
  132. void SaveDataSystemComponentWindows::SetSaveDataDirectoryPath(const char* saveDataDirectoryPath)
  133. {
  134. if (IsAbsolutePath(saveDataDirectoryPath))
  135. {
  136. m_saveDataDircetoryPathAbsolute = saveDataDirectoryPath;
  137. }
  138. else
  139. {
  140. m_saveDataDircetoryPathAbsolute = GetDefaultWindowsUserSaveDataPath() + saveDataDirectoryPath;
  141. }
  142. AZ_Assert(!m_saveDataDircetoryPathAbsolute.empty(), "Cannot set an empty save data directory path.");
  143. // Append the trailing path separator if needed
  144. if (m_saveDataDircetoryPathAbsolute.back() != '/' ||
  145. m_saveDataDircetoryPathAbsolute.back() != '\\')
  146. {
  147. m_saveDataDircetoryPathAbsolute += '\\';
  148. }
  149. }
  150. ////////////////////////////////////////////////////////////////////////////////////////////////
  151. AZStd::string SaveDataSystemComponentWindows::GetSaveDataFilePath(const AZStd::string& dataBufferName,
  152. AzFramework::LocalUserId localUserId)
  153. {
  154. AZStd::string saveDataFilePath = m_saveDataDircetoryPathAbsolute;
  155. if (localUserId != AzFramework::LocalUserIdNone)
  156. {
  157. saveDataFilePath += AZStd::string::format("User_%u\\", localUserId);
  158. }
  159. saveDataFilePath += dataBufferName;
  160. return saveDataFilePath;
  161. }
  162. } // namespace SaveData