bool SavedataParam::GetFilesList(SceUtilitySavedataParam *param) { if (!param) { return false; } u32 dataAddr = param->fileListAddr; if (!Memory::IsValidAddress(dataAddr)) return false; // TODO : Need to be checked against more game u32 fileInfosAddr = Memory::Read_U32(dataAddr + 24); //for Valkyria2, dataAddr+0 and dataAddr+12 has "5" for 5 files int numFiles = Memory::Read_U32(dataAddr+12); int foundFiles = 0; for (int i = 0; i < numFiles; i++) { // for each file (80 bytes): // u32 mode, u32 ??, u64 size, u64 ctime, u64 ??, u64 atime, u64 ???, u64 mtime, u64 ??? // u8[16] filename (or 13 + padding?) u32 curFileInfoAddr = fileInfosAddr + i*80; char fileName[16]; strncpy(fileName, Memory::GetCharPointer(curFileInfoAddr + 64),16); std::string filePath = savePath + GetGameName(param) + GetSaveName(param) + "/" + fileName; PSPFileInfo info = pspFileSystem.GetFileInfo(filePath); if (info.exists) { bool isCrypted = IsSaveEncrypted(param,0); Memory::Write_U32(0x21FF, curFileInfoAddr+0); if(isCrypted) // Crypted save are 16 bytes bigger Memory::Write_U64(info.size - 0x10, curFileInfoAddr+8); else Memory::Write_U64(info.size, curFileInfoAddr+8); Memory::Write_U64(0,curFileInfoAddr + 16); // TODO ctime Memory::Write_U64(0,curFileInfoAddr + 24); // TODO unknow Memory::Write_U64(0,curFileInfoAddr + 32); // TODO atime Memory::Write_U64(0,curFileInfoAddr + 40); // TODO unknow Memory::Write_U64(0,curFileInfoAddr + 48); // TODO mtime Memory::Write_U64(0,curFileInfoAddr + 56); // TODO unknow foundFiles++; } } // TODO : verify if return true if at least 1 file found or only if all found return foundFiles > 0; }
bool SavedataParam::Load(SceUtilitySavedataParam *param, int saveId) { if (!param) { return false; } u8 *data_ = (u8*)Memory::GetPointer(param->dataBuf); std::string dirPath = GetSaveFilePath(param, saveId); if (saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it { if (saveDataList[saveId].size == 0) // don't read no existing file { return false; } } std::string filePath = dirPath+"/"+GetFileName(param); s64 readSize; INFO_LOG(HLE,"Loading file with size %u in %s",param->dataBufSize,filePath.c_str()); u8* saveData = 0; int saveSize = -1; if (!ReadPSPFile(filePath, &saveData, saveSize, &readSize)) { ERROR_LOG(HLE,"Error reading file %s",filePath.c_str()); return false; } saveSize = (int)readSize; // copy back save name in request strncpy(param->saveName,GetSaveDirName(param, saveId).c_str(),20); ParamSFOData sfoFile; std::string sfopath = dirPath+"/"+sfoName; PSPFileInfo sfoInfo = pspFileSystem.GetFileInfo(sfopath); if(sfoInfo.exists) // Read sfo { u8 *sfoData = new u8[(size_t)sfoInfo.size]; size_t sfoSize = (size_t)sfoInfo.size; if(ReadPSPFile(sfopath,&sfoData,sfoSize, NULL)) { sfoFile.ReadSFO(sfoData,sfoSize); // copy back info in request strncpy(param->sfoParam.title,sfoFile.GetValueString("TITLE").c_str(),128); strncpy(param->sfoParam.savedataTitle,sfoFile.GetValueString("SAVEDATA_TITLE").c_str(),128); strncpy(param->sfoParam.detail,sfoFile.GetValueString("SAVEDATA_DETAIL").c_str(),1024); param->sfoParam.parentalLevel = sfoFile.GetValueInt("PARENTAL_LEVEL"); } delete[] sfoData; } // Don't know what it is, but PSP always respond this and this unlock some game param->bind = 1021; bool isCrypted = IsSaveEncrypted(param,saveId); bool saveDone = false; if(isCrypted)// Try to decrypt { int align_len = align16(saveSize); u8* data_base = new u8[align_len]; u8* cryptKey = new u8[0x10]; memset(cryptKey,0,0x10); if(param->key[0] != 0) { memcpy(cryptKey, param->key, 0x10); } memset(data_base + saveSize, 0, align_len - saveSize); memcpy(data_base, saveData, saveSize); int decryptMode = 1; if(param->key[0] != 0) { decryptMode = (GetSDKMainVersion(sceKernelGetCompiledSdkVersion()) >= 4 ? 5 : 3); } if(DecryptSave(decryptMode, data_base, &saveSize, &align_len, ((param->key[0] != 0)?cryptKey:0)) == 0) { memcpy(data_, data_base, saveSize); saveDone = true; } delete[] data_base; delete[] cryptKey; } if(!saveDone) // not crypted or decrypt fail { memcpy(data_, saveData, saveSize); } param->dataSize = (SceSize)saveSize; delete[] saveData; return true; }
int SavedataParam::GetFilesList(SceUtilitySavedataParam *param) { if (!param) { return SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_STATUS; } if (!param->fileList.Valid()) { ERROR_LOG_REPORT(HLE, "SavedataParam::GetFilesList(): bad fileList address %08x", param->fileList.ptr); // Should crash. return -1; } auto &fileList = param->fileList; if (fileList->secureEntries.Valid() && fileList->maxSecureEntries > 99) { ERROR_LOG_REPORT(HLE, "SavedataParam::GetFilesList(): too many secure entries, %d", fileList->maxSecureEntries); return SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_PARAMS; } if (fileList->normalEntries.Valid() && fileList->maxNormalEntries > 8192) { ERROR_LOG_REPORT(HLE, "SavedataParam::GetFilesList(): too many normal entries, %d", fileList->maxNormalEntries); return SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_PARAMS; } if (fileList->systemEntries.Valid() && fileList->maxSystemEntries > 5) { ERROR_LOG_REPORT(HLE, "SavedataParam::GetFilesList(): too many system entries, %d", fileList->maxSystemEntries); return SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_PARAMS; } std::string dirPath = savePath + GetGameName(param) + GetSaveName(param); if (!pspFileSystem.GetFileInfo(dirPath).exists) { DEBUG_LOG(HLE, "SavedataParam::GetFilesList(): directory %s does not exist", dirPath.c_str()); return SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; } // Even if there are no files, initialize to 0. fileList->resultNumSecureEntries = 0; fileList->resultNumNormalEntries = 0; fileList->resultNumSystemEntries = 0; // We need PARAMS.SFO's SAVEDATA_FILE_LIST to determine which entries are secure. PSPFileInfo sfoFileInfo = pspFileSystem.GetFileInfo(dirPath + "/" + SFO_FILENAME); std::set<std::string> secureFilenames; // TODO: Error code if not? if (sfoFileInfo.exists) { ParamSFOData sfoFile; size_t sfoSize = (size_t)sfoFileInfo.size; u8 *sfoData = new u8[sfoSize]; if (ReadPSPFile(dirPath + "/" + SFO_FILENAME, &sfoData, sfoSize, NULL)){ sfoFile.ReadSFO(sfoData, sfoSize); } delete[] sfoData; u32 sfoFileListSize = 0; char *sfoFileList = (char *)sfoFile.GetValueData("SAVEDATA_FILE_LIST", &sfoFileListSize); const int FILE_LIST_ITEM_SIZE = 13 + 16 + 3; const int FILE_LIST_COUNT_MAX = 99; // Filenames are 13 bytes long at most. Add a NULL so there's no surprises. char temp[14]; temp[13] = '\0'; for (u32 i = 0; i < FILE_LIST_COUNT_MAX; ++i) { // Ends at a NULL filename. if (i * FILE_LIST_ITEM_SIZE >= sfoFileListSize || sfoFileList[i * FILE_LIST_ITEM_SIZE] == '\0') { break; } strncpy(temp, &sfoFileList[i * FILE_LIST_ITEM_SIZE], 13); secureFilenames.insert(temp); } } // Does not list directories, nor recurse into them, and ignores files not ALL UPPERCASE. auto files = pspFileSystem.GetDirListing(dirPath); for (auto file = files.begin(), end = files.end(); file != end; ++file) { if (file->type == FILETYPE_DIRECTORY) { continue; } // TODO: What are the exact rules? It definitely skips lowercase, and allows FILE or FILE.EXT. if (file->name.find_first_of("abcdefghijklmnopqrstuvwxyz") != file->name.npos) { DEBUG_LOG(HLE, "SavedataParam::GetFilesList(): skipping file %s with lowercase", file->name.c_str()); continue; } bool isSystemFile = file->name == ICON0_FILENAME || file->name == ICON1_FILENAME || file->name == PIC1_FILENAME; isSystemFile = isSystemFile || file->name == SND0_FILENAME || file->name == SFO_FILENAME; SceUtilitySavedataFileListEntry *entry = NULL; int sizeOffset = 0; if (isSystemFile) { if (fileList->systemEntries.Valid() && fileList->resultNumSystemEntries < fileList->maxSystemEntries) { entry = &fileList->systemEntries[fileList->resultNumSystemEntries++]; } } else if (secureFilenames.find(file->name) != secureFilenames.end()) { if (fileList->secureEntries.Valid() && fileList->resultNumSecureEntries < fileList->maxSecureEntries) { entry = &fileList->secureEntries[fileList->resultNumSecureEntries++]; } // Secure files are slightly bigger. bool isCrypted = IsSaveEncrypted(param, GetSaveDirName(param, 0)); if (isCrypted) { sizeOffset = -0x10; } } else { if (fileList->normalEntries.Valid() && fileList->resultNumNormalEntries < fileList->maxNormalEntries) { entry = &fileList->normalEntries[fileList->resultNumNormalEntries++]; } } // Out of space for this file in the list. if (entry == NULL) { continue; } entry->st_mode = 0x21FF; entry->st_size = file->size + sizeOffset; // TODO: ctime, atime, mtime // TODO: Probably actually 13 + 3 pad... strncpy(entry->name, file->name.c_str(), 16); entry->name[15] = '\0'; } return 0; }