ue4下PS5自定义存储
前言
最近要将xbox游戏移植到ps5,在数据存储这块踩了不少坑,最初打算用ps平台自带的saveGame,但是里面的功能满足不了现有项目。所以只能边研究官方文档边看ps自带的sample。(只贴代码,不作过多讲解了,方便自己闲暇时回顾~~)
自己新建了一个存储插件
PlatformsGp5Save.h
/************************************************************************************
* PS5 SaveData Class
*/
#pragma once
#include "CoreMinimal.h"
#include <save_data.h>
#include "UObject/NoExportTypes.h"
#include "WorldSubsystem.h"
#include "PlatformsGp5Save.generated.h"
#define SAVE_DATA_PARAM_TITLE "Sample param title"
#define SAVE_DATA_PARAM_SUB_TITLE "Sample param sub title"
#define SAVE_DATA_PARAM_DETAIL "Sample param detail"
#define SAVE_DATA_PARAM_USERPARAM (0)
#define SAVE_DATA_ID_INVALID (-1)
#define SAVE_DATA_WAIT_BACKUP_INTERVAL_USEC (50 * 1000)
#define MOUNT_ROOT_PATH "gamedata"
#define LIST_FILE_PATH "pathes.sav"
UCLASS()
class PLATFORMS_API UPlatformsGp5Save : public UWorldSubsystem {
GENERATED_BODY()
public:
static UPlatformsGp5Save* GetInstance(const UObject* WorldContextObject);
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
void waitBackup(const char* dirName);
void SavePathList(FString content);
void SplitFileName(const FString& InPath, TArray<FString>& DirNames, FString& FileName);
void FindSavedGames(TArray<FString>& DirNames);
bool SaveStringToFile(FString JsonData, const TCHAR* Filename);
bool CreateDirectoryTree(TArray<FString> DirectoryParts, SceSaveDataMountPoint& mountPoint);
bool Mount(const FString dirName, SceSaveDataMountPoint& MountPoint);
int32_t writeFile(const char* mountPoint, const char* fileName, FString JsonData);
int32_t LoadFileToString(FString& fileName, FString& fileStr);
int32_t readFile(const char* mountPoint, const char* fileName, FString& fileStr);
int32_t getParam(const SceSaveDataMountPoint* mountPoint);
// Get User Id
bool GetUserId();
bool InitSaveData();
bool CreateTransactionResource();
// setup mount object
void setupSceSaveDataMount3(const SceUserServiceUserId userId, const SceSaveDataMountMode mode,
const SceSaveDataDirName* dirName, const SceSaveDataTransactionResourceId transactionResourceId,
SceSaveDataMount3* mount);
// prepare
int32_t prepare(const SceSaveDataMountPoint* mountPoint,
const SceSaveDataTransactionResourceId transactionResourceId,
const SceSaveDataPrepareMode prepareMode);
//E Set a parameter
int32_t setParam(const SceSaveDataMountPoint* mountPoint,
const char* title = SAVE_DATA_PARAM_TITLE,
const char* subTitle = SAVE_DATA_PARAM_SUB_TITLE,
const char* detail = SAVE_DATA_PARAM_DETAIL,
const uint32_t userParam = SAVE_DATA_PARAM_USERPARAM);
//E Commit update of save data
int32_t commit(const SceSaveDataTransactionResourceId transactionResourceId);
private:
// sec save data
int32_t m_saveDataInitId;
SceUserServiceUserId m_userId;
SceSaveDataDirName m_dirName;
SceSaveDataTransactionResourceId m_transactionResourceId;
SceSaveDataDirName SavedGameDirs[SCE_SAVE_DATA_DIRNAME_MAX_COUNT];
};
PlatformsGp5Save.cpp
/************************************************************************************
* PS5 SaveData Class
*/
#include "PlatformsGp5Save.h"
#include <user_service.h>
#include <scebase_common.h>
#include <string.h>
#include <_fs.h>
#include "UnrealMemory.h"
void UPlatformsGp5Save::Initialize(FSubsystemCollectionBase& Collection)
{
// initialize
InitSaveData();
// get user id
GetUserId();
// create transaction id
CreateTransactionResource();
}
void UPlatformsGp5Save::Deinitialize()
{
int32_t ret = sceSaveDataTerminate();
if (ret < SCE_OK)
{
printf("sceSaveDataTerminate : 0x%08x\n", ret);
// through
}
}
UPlatformsGp5Save* UPlatformsGp5Save::GetInstance(const UObject* WorldContextObject)
{
if (WorldContextObject != nullptr)
{
return UWorld::GetSubsystem<UPlatformsGp5Save>(WorldContextObject->GetWorld());
}
return nullptr;
}
void UPlatformsGp5Save::SplitFileName(const FString& Path, TArray<FString>& DirNames, FString& FileName)
{
FString ResultPath(Path);
ResultPath.ParseIntoArray(DirNames, TEXT("/"), true);
FileName = DirNames[DirNames.Num() - 1];
DirNames.RemoveAt(DirNames.Num() - 1);
}
void UPlatformsGp5Save::SavePathList(FString content)
{
// read native files
FString Path = LIST_FILE_PATH;
FString fileStr;
int32_t ret = LoadFileToString(Path, fileStr);
if (ret < SCE_OK)
{
printf("readFile : 0x%08x\n", ret);
}
// init data
TArray<FString> DirNames;
fileStr.ParseIntoArray(DirNames, TEXT("/n"), true);
TMap<FString, bool> SaveListMap;
for (int32 i = 0; i < DirNames.Num(); i++)
{
SaveListMap.Add(DirNames[i], true);
}
// add
if (!SaveListMap.Contains(content)) {
SaveListMap.Add(content, true);
mount
SceSaveDataMountPoint mountPoint;
if (!Mount(MOUNT_ROOT_PATH, mountPoint)) {
return;
}
// write
FString pathes = "";
for (TPair<FString, bool> Elem : SaveListMap) {
pathes = pathes + Elem.Key + TEXT("/n");
//pathes.Append(FString::Printf(TEXT("&%s%s/n"), *pathes, *Elem.Key));
}
ret = writeFile(mountPoint.data, TCHAR_TO_ANSI(*Path), pathes);
if (ret < SCE_OK)
{
printf("writeFile : 0x%08x\n", ret);
}
// commit
ret = commit(m_transactionResourceId);
if (ret < SCE_OK) {
printf("sceSaveDataUmount2 : 0x%08x\n", ret);
}
//E Unmount
//J アンマウント
SceSaveDataMountPoint* mPoint = &mountPoint;
int32_t ret2 = sceSaveDataUmount2(SCE_SAVE_DATA_UMOUNT_MODE_DEFAULT, mPoint);
if (ret2 < SCE_OK)
{
printf("sceSaveDataUmount2 : 0x%08x\n", ret2);
}
}
}
bool UPlatformsGp5Save::SaveStringToFile(FString JsonData, const TCHAR* Filename)
{
// split dirs
FString Path(Filename);
TArray<FString> DirectoryParts;
FString FileName;
SplitFileName(Path, DirectoryParts, FileName);
// mount
SceSaveDataMountPoint mountPoint;
if (!Mount(MOUNT_ROOT_PATH, mountPoint))
return false;
// create dirtree
if (!CreateDirectoryTree(DirectoryParts, mountPoint))
return false;
// write content
int32 ret = writeFile(mountPoint.data, TCHAR_TO_ANSI(*Path), JsonData);
if (ret < SCE_OK)
{
printf("writeFile : 0x%08x\n", ret);
commit(m_transactionResourceId);
return false;
}
// commit
ret = commit(m_transactionResourceId);
if (ret < SCE_OK) {
return false;
}
//E Unmount
//J アンマウント
int32_t ret2 = sceSaveDataUmount2(SCE_SAVE_DATA_UMOUNT_MODE_DEFAULT, &mountPoint);
if (ret2 < SCE_OK)
{
printf("sceSaveDataUmount2 : 0x%08x\n", ret2);
}
// save path
SavePathList(Path);
return true;
}
//E Read the saved data
//J セーブデータ読み込み
int32_t UPlatformsGp5Save::LoadFileToString(FString& fileName, FString& fileStr)
{
//TRACE;
int32_t ret = SCE_OK;
//E Wait for finish to create backup data
//J バックアップデータ作成の完了を待つ
waitBackup(m_dirName.data);
//E Mount
//J マウント
FString MountDirName(MOUNT_ROOT_PATH);
FMemory::Memzero(&m_dirName, sizeof(m_dirName));
strlcpy(m_dirName.data, TCHAR_TO_ANSI(*MountDirName), sizeof(m_dirName.data));
SceSaveDataMount3 mount3;
setupSceSaveDataMount3(m_userId,
SCE_SAVE_DATA_MOUNT_MODE_RDONLY,
&m_dirName,
SCE_SAVE_DATA_TRANSACTION_RESOURCE_ID_INVALID,
&mount3);
SceSaveDataMountResult mountResult;
FMemory::Memzero(&mountResult, sizeof(mountResult));
ret = sceSaveDataMount3(&mount3, &mountResult);
if (ret < SCE_OK && ret != SCE_SAVE_DATA_ERROR_BUSY)
{
//EPRINT("sceSaveDataMount3 : 0x%08x(%s)\n", ret, m_dirName.data);
return ret;
}
SceSaveDataMountPoint* mountPoint = &mountResult.mountPoint;
ret = readFile(mountPoint->data, TCHAR_TO_ANSI(*fileName), fileStr);
if (ret < SCE_OK)
{
printf("readFile : 0x%08x\n", ret);
goto End;
}
ret = SCE_OK;
End:
//E Unmount
//J アンマウント
int32_t ret2 = sceSaveDataUmount2(SCE_SAVE_DATA_UMOUNT_MODE_DEFAULT, mountPoint);
if (ret2 < SCE_OK)
{
printf("sceSaveDataUmount2 : 0x%08x\n", ret2);
}
return ret;
}
bool UPlatformsGp5Save::CreateDirectoryTree(TArray<FString> DirectoryParts, SceSaveDataMountPoint& mountPoint)
{
FString mPath = mountPoint.data;
for (int32 i = 0; i < DirectoryParts.Num(); ++i)
{
mPath /= DirectoryParts[i];
int32 Ret = sceKernelMkdir(TCHAR_TO_ANSI(*mPath), SCE_KERNEL_S_IRWU);
if (Ret != SCE_OK && Ret != SCE_KERNEL_ERROR_EEXIST)
{
return false;
}
}
return true;
}
void UPlatformsGp5Save::setupSceSaveDataMount3(const SceUserServiceUserId userId, const SceSaveDataMountMode mode,
const SceSaveDataDirName* dirName, const SceSaveDataTransactionResourceId transactionResourceId,
SceSaveDataMount3* mount)
{
FMemory::Memzero(mount, sizeof(SceSaveDataMount3));
mount->userId = userId;
mount->dirName = dirName;
mount->blocks = SCE_SAVE_DATA_BLOCKS_MAX2;
#ifdef SAVE_DATA_CONFIG_ENABLE_ROLLBACK
mount->systemBlocks = SCE_SAVE_DATA_SYSTEM_BLOCKS_EQUAL_TO_BLOCKS;
#else
mount->systemBlocks = 0;
#endif
mount->mountMode = mode;
mount->resource = transactionResourceId;
return;
}
bool UPlatformsGp5Save::InitSaveData()
{
int32_t ret = sceSaveDataInitialize3(NULL);
if (ret < SCE_OK)
{
UE_LOG(LogTemp, Warning, TEXT("sceUserServiceInitialize() failed in Mount(). Error code: 0x%08x"), ret);
return false;
}
m_saveDataInitId = ret;
return true;
}
bool UPlatformsGp5Save::GetUserId()
{
m_userId = SAVE_DATA_ID_INVALID;
int32_t ret = sceUserServiceGetInitialUser(&m_userId);
if (ret < SCE_OK)
{
UE_LOG(LogTemp, Warning, TEXT("sceUserServiceGetInitialUser() failed in Mount(). Error code: 0x%08x"), ret);
return false;
}
return true;
}
bool UPlatformsGp5Save::CreateTransactionResource()
{
int32_t ret = sceSaveDataCreateTransactionResource(0);
if (ret < SCE_OK)
{
UE_LOG(LogTemp, Warning, TEXT("sceSaveDataCreateTransactionResource() failed in Mount(). Error code: 0x%08x"), ret);
return false;
}
m_transactionResourceId = ret;
return true;
}
bool UPlatformsGp5Save::Mount(const FString dirName, SceSaveDataMountPoint& MountPoint)
{
FString MountDirName(dirName);
// mount dirName
//memset(mount, 0x00, sizeof(SceSaveDataMount3));
FMemory::Memzero(&m_dirName, sizeof(m_dirName));
strlcpy(m_dirName.data, TCHAR_TO_ANSI(*MountDirName), sizeof(m_dirName.data));
// mount3 init
SceSaveDataMount3 mount3;
setupSceSaveDataMount3(m_userId,
SCE_SAVE_DATA_MOUNT_MODE_CREATE2,
&m_dirName,
m_transactionResourceId,
&mount3);
SceSaveDataMountResult mountResult;
FMemory::Memzero(&mountResult, sizeof(mountResult));
int32_t ret = sceSaveDataMount3(&mount3, &mountResult);
if (ret < SCE_OK && ret != SCE_SAVE_DATA_ERROR_BUSY)
{
UE_LOG(LogTemp, Warning, TEXT("sceSaveDataMount3() failed in Mount(). Error code: 0x%08x"), ret);
return false;
}
SceSaveDataMountPoint* TmountPoint = &mountResult.mountPoint;
MountPoint = mountResult.mountPoint;
//E Prepare update of save data
SceSaveDataPrepareMode prepareMode = SCE_SAVE_DATA_PREPARE_MODE_DEFAULT;
#ifdef SAVE_DATA_CONFIG_ENABLE_ROLLBACK
prepareMode |= SCE_SAVE_DATA_PREPARE_MODE_ENABLE_CANCEL;
#endif
ret = prepare(TmountPoint, m_transactionResourceId, prepareMode);
if (ret < SCE_OK)
{
UE_LOG(LogTemp, Warning, TEXT("prepare() failed in Mount(). Error code: 0x%08x"), ret);
goto End;
}
//E Set parameters
ret = setParam(TmountPoint);
if (ret < SCE_OK)
{
UE_LOG(LogTemp, Warning, TEXT("setParam() failed in Mount(). Error code: 0x%08x"), ret);
commit(m_transactionResourceId);
goto End;
}
End:
return true;
}
//E Prepare update of save data
int32_t UPlatformsGp5Save::prepare(const SceSaveDataMountPoint* mountPoint,
const SceSaveDataTransactionResourceId transactionResourceId,
const SceSaveDataPrepareMode prepareMode)
{
int32_t ret = SCE_OK;
SceSaveDataPrepareParam prepareParam;
FMemory::Memzero(&prepareParam, sizeof(prepareParam));
prepareParam.resource = transactionResourceId;
prepareParam.prepareMode = prepareMode;
ret = sceSaveDataPrepare(mountPoint, &prepareParam);
if (ret < SCE_OK)
{
UE_LOG(LogTemp, Warning, TEXT("sceSaveDataPrepare() failed in Mount(). Error code: 0x%08x"), ret);
return ret;
}
return SCE_OK;
}
//E Set a parameter
int32_t UPlatformsGp5Save::setParam(const SceSaveDataMountPoint* mountPoint,
const char* title/*=SAVE_DATA_PARAM_TITLE*/,
const char* subTitle/*=SAVE_DATA_PARAM_SUB_TITLE*/,
const char* detail/*=SAVE_DATA_PARAM_DETAIL*/,
const uint32_t userParam/*=SAVE_DATA_PARAM_USERPARAM*/)
{
int32_t ret = SCE_OK;
SceSaveDataParam param;
FMemory::Memzero(¶m, sizeof(param));
strlcpy(param.title, title, sizeof(param.title));
strlcpy(param.subTitle, subTitle, sizeof(param.subTitle));
strlcpy(param.detail, detail, sizeof(param.detail));
param.userParam = SAVE_DATA_PARAM_USERPARAM;
ret = sceSaveDataSetParam(mountPoint,
SCE_SAVE_DATA_PARAM_TYPE_ALL,
¶m, sizeof(param));
if (ret < SCE_OK)
{
UE_LOG(LogTemp, Warning, TEXT("sceSaveDataSetParam() failed in Mount(). Error code: 0x%08x"), ret);
return ret;
}
return SCE_OK;
}
//E Commit update of save data
int32_t UPlatformsGp5Save::commit(const SceSaveDataTransactionResourceId transactionResourceId)
{
int32_t ret = SCE_OK;
SceSaveDataCommitParam commitParam;
FMemory::Memzero(&commitParam, sizeof(commitParam));
commitParam.resource = transactionResourceId;
ret = sceSaveDataCommit(&commitParam);
if (ret < SCE_OK)
{
UE_LOG(LogTemp, Warning, TEXT("sceSaveDataCommit() failed in Mount(). Error code: 0x%08x"), ret);
return ret;
}
return SCE_OK;
}
//E Write the save data
//E Create the file
//J ファイル作成
int32_t UPlatformsGp5Save::writeFile(const char* mountPoint, const char* fileName, FString JsonData)
{
int32_t ret = SCE_OK;
//E Write a file
char path[64];
snprintf(path, sizeof(path), "%s/%s", mountPoint, fileName);
int32_t fd = sceKernelOpen(path, SCE_KERNEL_O_RDWR | SCE_KERNEL_O_TRUNC | SCE_KERNEL_O_CREAT, SCE_KERNEL_S_IRWU);
if (fd < SCE_OK)
{
printf("sceKernelOpen : 0x%08x(%s)\n", fd, path);
return fd;
}
//FString data_buff = TEXT("{\n\t\"starttime\": \"apr 6, 2023, 10:07:02 am\",\n\t\"select_count\": \"maingame1\"\n}");
const size_t dummyBufSize = JsonData.Len();
// data init
unsigned char* dummyBuf = new unsigned char[dummyBufSize];
if (!dummyBuf)
{
return -1;
}
char* temp = TCHAR_TO_ANSI(*JsonData);
for (int i = 0; i < dummyBufSize; i++) {
dummyBuf[i] = temp[i];
}
//memset(dummyBuf, 0x00, sizeof(*dummyBuf));
//dummyBuf = TCHAR_TO_ANSI(*JsonData);
ret = static_cast<int32_t>(sceKernelWrite(fd, dummyBuf, dummyBufSize));
if (ret < SCE_OK)
{
printf("sceKernelWrite : 0x%08x\n", ret);
sceKernelClose(fd);
return ret;
}
sceKernelClose(fd);
return SCE_OK;
}
//E Read a file (+ dump)
//E In the case where the file system of the save data is corrupted, the error processing during reading is required
//E because the corruption might be detected during file reading.
//J ファイル読み込み(+ダンプ)
//J セーブデータのファイルシステムが破損しているケースは、ファイル読み込み時に破損検出される場合もあるので
//J 読み込み時にエラー処理は必須です。
int32_t UPlatformsGp5Save::readFile(const char* mountPoint, const char* fileName, FString& fileStr)
{
int32_t ret = SCE_OK;
char path[64];
snprintf(path, sizeof(path), "%s/%s", mountPoint, fileName);
SceKernelStat st;
ret = sceKernelStat(path, &st);
if (ret < SCE_OK)
{
printf("sceKernelStat : 0x%08x\n", ret);
return ret;
}
char* data = new char[st.st_size];
if (!data)
{
return -1;
}
//E Read a file
//E If the file system is corrupted, an error might occur during file opening/reading.
//J ファイル読み込み
//J ファイルシステムが壊れている場合は、ファイルオープン/リード時にエラーになる可能性がある
int fd = sceKernelOpen(path, SCE_KERNEL_O_RDONLY, SCE_KERNEL_S_INONE);
if (fd < SCE_OK)
{
printf("sceKernelOpen : 0x%08x\n", fd);
delete[] data;
return fd;
}
ret = static_cast<int32_t>(sceKernelRead(fd, data, static_cast<size_t>(st.st_size)));
if (ret < SCE_OK)
{
//EPRINT("sceKernelRead : 0x%08x(%s)\n", ret, path);
//goto End;
}
fileStr = ANSI_TO_TCHAR(data);
fileStr = fileStr.Left(st.st_size);
ret = SCE_OK;
//End:
sceKernelClose(fd);
delete[] data;
return ret;
}
//E Search
void UPlatformsGp5Save::FindSavedGames(TArray<FString>& DirNames)
{
// read native files
FString Path = LIST_FILE_PATH;
FString fileStr;
int32_t ret = LoadFileToString(Path, fileStr);
if (ret < SCE_OK)
{
printf("readFile : 0x%08x\n", ret);
}
// init data
fileStr.ParseIntoArray(DirNames, TEXT("/n"), true);
}
//E Get paramters
//J パラメータ取得
int32_t UPlatformsGp5Save::getParam(const SceSaveDataMountPoint* mountPoint)
{
int32_t ret = SCE_OK;
SceSaveDataParam param;
FMemory::Memzero(¶m, sizeof(param));
size_t gotSize = 0;
ret = sceSaveDataGetParam(mountPoint,
SCE_SAVE_DATA_PARAM_TYPE_ALL,
¶m, sizeof(param), &gotSize);
if (ret < SCE_OK)
{
return ret;
}
/*PRINT("get param\n");
PRINT("%20s : %s\n", "title", param.title);
PRINT("%20s : %s\n", "subTitle", param.subTitle);
PRINT("%20s : %s\n", "detail", param.detail);
PRINT("%20s : %u\n", "userParam", param.userParam);*/
char str[64];
struct tm tmData;
gmtime_s(¶m.mtime, &tmData);
strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S", &tmData);
return SCE_OK;
}
void UPlatformsGp5Save::waitBackup(const char* dirName)
{
#if defined(SAVE_DATA_CONFIG_CREATE_BACKUP_DATA) || !defined(SAVE_DATA_CONFIG_ENABLE_ROLLBACK)
SceSaveDataEvent event;
while (1)
{
FMemory::Memzero(&event, sizeof(event));
int32_t ret = sceSaveDataGetEventResult(NULL, &event);
if (ret == SCE_SAVE_DATA_ERROR_EVENT_BUSY)
{
sceKernelUsleep(SAVE_DATA_WAIT_BACKUP_INTERVAL_USEC);
continue;
}
else if (ret == SCE_SAVE_DATA_ERROR_NOT_FOUND)
{
break;
}
else if (ret < SCE_OK)
{
break;
}
if (strncmp(event.dirName.data, dirName, sizeof(event.dirName.data)) == 0)
{
break;
}
}
#endif
return;
}
PlatformsManager.h
管理工具
#include "CoreMinimal.h"
#include "EngineMinimal.h"
namespace PlatformsManager
{
void Write(const UObject* object, FString JsonData, const FString Filename);
void Read(const UObject* object, const FString Path, FString& fileStr);
void GetSaveGames(const UObject* object, FString SaveRootDir, TArray<FString>& SavFiles);
FString StripResultPath(const FString& Path);
bool IsGp5Dir(const FString& Path);
}
PlatformsManager.cpp
#include "PlatformsManager.h"
#include "PlatformsGp5Save.h"
void PlatformsManager::Write(const UObject* object, FString JsonData, const FString Path)
{
FString FinalSavedFilePath(Path);
FString platform_name = UGameplayStatics::GetPlatformName();
if (platform_name == "PS5" && IsGp5Dir(FinalSavedFilePath)) {
FinalSavedFilePath = PlatformsManager::StripResultPath(FinalSavedFilePath);
UPlatformsGp5Save::GetInstance(object)->SaveStringToFile(JsonData, *FinalSavedFilePath);
}
else
{
FFileHelper::SaveStringToFile(JsonData, *FinalSavedFilePath);
}
}
void PlatformsManager::Read(const UObject* object, const FString Path, FString& fileStr)
{
FString SingleFilePath(Path);
FString platform_name = UGameplayStatics::GetPlatformName();
if (platform_name == "PS5" && IsGp5Dir(SingleFilePath)) {
SingleFilePath = StripResultPath(SingleFilePath);
int32_t ret = UPlatformsGp5Save::GetInstance(object)->LoadFileToString(SingleFilePath, fileStr);
if (ret < SCE_OK)
{
//printf("readFile : 0x%08x\n", ret);
//continue;
}
}
else
{
FFileHelper::LoadFileToString(fileStr, *SingleFilePath);
}
}
void PlatformsManager::GetSaveGames(const UObject* object, FString SaveRootDir,TArray<FString>& SavFiles)
{
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
if (!PlatformFile.DirectoryExists(*SaveRootDir))
{
PlatformFile.CreateDirectory(*SaveRootDir);
}
FString platform_name = UGameplayStatics::GetPlatformName();
if (platform_name == "PS5") {
// read native files
UPlatformsGp5Save::GetInstance(object)->FindSavedGames(SavFiles);
}
else
{
FString Suffix = FString(".sav");
PlatformFile.FindFiles(SavFiles, *SaveRootDir, *Suffix);
}
}
FString PlatformsManager::StripResultPath(const FString& Path)
{
FString ResultPath(Path);
int32 DotDotSlashIdx = ResultPath.Find(TEXT("../"), ESearchCase::CaseSensitive);
while (DotDotSlashIdx != INDEX_NONE)
{
ResultPath = ResultPath.Right(ResultPath.Len() - (DotDotSlashIdx + 3));
DotDotSlashIdx = ResultPath.Find(TEXT("../"), ESearchCase::CaseSensitive);
}
return ResultPath;
}
bool PlatformsManager::IsGp5Dir(const FString& Path)
{
FString ResultPath = StripResultPath(Path);
if (ResultPath.StartsWith(TEXT("/devlog/app/"), ESearchCase::CaseSensitive) || ResultPath.StartsWith(TEXT("devlog/app/"), ESearchCase::CaseSensitive))
{
return false;
}
return true;
}