CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/Dialog/PSPGamedataInstallDialog.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <algorithm>18#include "Common/CommonTypes.h"19#include "Common/Serialize/Serializer.h"20#include "Common/Serialize/SerializeFuncs.h"21#include "Common/System/OSD.h"22#include "Core/ELF/ParamSFO.h"23#include "Core/MemMapHelpers.h"24#include "Core/Reporting.h"25#include "Core/System.h"26#include "Core/FileSystems/MetaFileSystem.h"27#include "Core/Dialog/PSPGamedataInstallDialog.h"28#include "Common/Data/Text/I18n.h"29#include "UI/OnScreenDisplay.h"3031std::string saveBasePath = "ms0:/PSP/SAVEDATA/";3233// Guesses.34const static int GAMEDATA_INIT_DELAY_US = 200000;35const static int GAMEDATA_SHUTDOWN_DELAY_US = 2000;36const static u32 GAMEDATA_BYTES_PER_READ = 32768;37// TODO: Could adjust based on real-time into frame? Or eat cycles?38// If this is too high, some games (e.g. Senjou no Valkyria 3) will lag.39const static u32 GAMEDATA_READS_PER_UPDATE = 20;4041const u32 PSP_UTILITY_GAMEDATA_MODE_SHOW_PROGRESS = 1;4243const u32 ERROR_UTILITY_GAMEDATA_MEMSTRICK_REMOVED = 0x80111901;44const u32 ERROR_UTILITY_GAMEDATA_MEMSTRICK_WRITE_PROTECTED = 0x80111903;45const u32 ERROR_UTILITY_GAMEDATA_INVALID_MODE = 0x80111908;4647static const std::string SFO_FILENAME = "PARAM.SFO";4849namespace50{51std::vector<std::string> GetPSPFileList (const std::string &dirpath) {52std::vector<std::string> FileList;53auto Fileinfos = pspFileSystem.GetDirListing(dirpath);54FileList.reserve(Fileinfos.size());5556for (auto it = Fileinfos.begin(); it != Fileinfos.end(); ++it) {57std::string info = (*it).name;58FileList.push_back(info);59}60return FileList;61}62}6364PSPGamedataInstallDialog::PSPGamedataInstallDialog(UtilityDialogType type) : PSPDialog(type) {65}6667PSPGamedataInstallDialog::~PSPGamedataInstallDialog() {68}6970int PSPGamedataInstallDialog::Init(u32 paramAddr) {71if (GetStatus() != SCE_UTILITY_STATUS_NONE) {72ERROR_LOG_REPORT(Log::sceUtility, "A game install request is already running, not starting a new one");73return SCE_ERROR_UTILITY_INVALID_STATUS;74}7576param.ptr = paramAddr;77inFileNames = GetPSPFileList("disc0:/PSP_GAME/INSDIR");78numFiles = (int)inFileNames.size();79readFiles = 0;80progressValue = 0;81allFilesSize = 0;82allReadSize = 0;83currentInputFile = 0;84currentOutputFile = 0;8586for (std::string filename : inFileNames) {87allFilesSize += pspFileSystem.GetFileInfo("disc0:/PSP_GAME/INSDIR/" + filename).size;88}8990if (allFilesSize == 0) {91ERROR_LOG_REPORT(Log::sceUtility, "Game install with no files / data");92// TODO: What happens here?93return -1;94}9596int size = Memory::Read_U32(paramAddr);97if (size != 1424 && size != 1432) {98ERROR_LOG_REPORT(Log::sceUtility, "sceGamedataInstallInitStart: invalid param size %d", size);99return SCE_ERROR_UTILITY_INVALID_PARAM_SIZE;100}101102memset(&request, 0, sizeof(request));103// Only copy the right size to support different request format104Memory::Memcpy(&request, paramAddr, size, "sceGamedataInstallInitStart");105InitCommon();106107ChangeStatusInit(GAMEDATA_INIT_DELAY_US);108return 0;109}110111int PSPGamedataInstallDialog::Update(int animSpeed) {112if (GetStatus() != SCE_UTILITY_STATUS_RUNNING)113return SCE_ERROR_UTILITY_INVALID_STATUS;114115if (param->mode >= 2) {116param->common.result = ERROR_UTILITY_GAMEDATA_INVALID_MODE;117param.NotifyWrite("DialogResult");118ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);119WARN_LOG_REPORT(Log::sceUtility, "sceUtilityGamedataInstallUpdate: invalid mode %d", param->mode);120return 0;121}122123UpdateCommon();124125// TODO: param->mode == 1 should show a prompt to confirm, then a progress bar.126// Any other mode (i.e. 0 or negative) should proceed and show no UI.127128// TODO: This should return error codes in some cases, like write failure.129// request.common.result must be updated for errors as well.130131if (readFiles < numFiles) {132if (currentInputFile != 0 && currentOutputFile != 0) {133// Continue copying, this will close once done automatically.134CopyCurrentFileData();135} else {136OpenNextFile();137}138139UpdateProgress();140} else {141WriteSfoFile();142143// TODO: What is this? Should one of these update per file or anything?144param->unknownResult1 = readFiles;145param->unknownResult2 = readFiles;146param.NotifyWrite("DialogResult");147148ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);149}150return 0;151}152153void PSPGamedataInstallDialog::OpenNextFile() {154std::string inputFileName = "disc0:/PSP_GAME/INSDIR/" + inFileNames[readFiles];155std::string outputFileName = GetGameDataInstallFileName(&request, inFileNames[readFiles]);156157currentInputFile = pspFileSystem.OpenFile(inputFileName, FILEACCESS_READ);158if (currentInputFile < 0) {159// TODO: Generate an error code?160ERROR_LOG_REPORT(Log::sceUtility, "Unable to read from install file: %s", inFileNames[readFiles].c_str());161++readFiles;162currentInputFile = 0;163return;164}165currentOutputFile = pspFileSystem.OpenFile(outputFileName, (FileAccess)(FILEACCESS_WRITE | FILEACCESS_CREATE | FILEACCESS_TRUNCATE));166if (currentOutputFile < 0) {167// TODO: Generate an error code?168ERROR_LOG(Log::sceUtility, "Unable to write to install file: %s", inFileNames[readFiles].c_str());169pspFileSystem.CloseFile(currentInputFile);170currentInputFile = 0;171currentOutputFile = 0;172++readFiles;173return;174}175176currentInputBytesLeft = (u32)pspFileSystem.GetFileInfo(inputFileName).size;177}178179void PSPGamedataInstallDialog::CopyCurrentFileData() {180u8 buffer[GAMEDATA_BYTES_PER_READ];181for (u32 i = 0; i < GAMEDATA_READS_PER_UPDATE; ++i) {182if (currentInputBytesLeft <= 0) {183break;184}185186const u32 bytesToRead = std::min(GAMEDATA_BYTES_PER_READ, currentInputBytesLeft);187size_t readSize = pspFileSystem.ReadFile(currentInputFile, buffer, bytesToRead);188if (readSize > 0) {189pspFileSystem.WriteFile(currentOutputFile, buffer, readSize);190currentInputBytesLeft -= (u32)readSize;191allReadSize += readSize;192} else {193break;194}195}196197if (currentInputBytesLeft <= 0) {198CloseCurrentFile();199}200}201202void PSPGamedataInstallDialog::CloseCurrentFile() {203if (currentOutputFile >= 0)204pspFileSystem.CloseFile(currentOutputFile);205currentOutputFile = 0;206207if (currentInputFile >= 0)208pspFileSystem.CloseFile(currentInputFile);209currentInputFile = 0;210211++readFiles;212}213214void PSPGamedataInstallDialog::WriteSfoFile() {215ParamSFOData sfoFile;216std::string sfopath = GetGameDataInstallFileName(&request, SFO_FILENAME);217std::vector<u8> sfoFileData;218if (pspFileSystem.ReadEntireFile(sfopath, sfoFileData) >= 0) {219sfoFile.ReadSFO(sfoFileData);220}221222// Update based on the just-saved data.223sfoFile.SetValue("TITLE", param->sfoParam.title, 128);224sfoFile.SetValue("SAVEDATA_TITLE", param->sfoParam.savedataTitle, 128);225sfoFile.SetValue("SAVEDATA_DETAIL", param->sfoParam.detail, 1024);226sfoFile.SetValue("PARENTAL_LEVEL", param->sfoParam.parentalLevel, 4);227// TODO: Verify category.228sfoFile.SetValue("CATEGORY", "MS", 4);229sfoFile.SetValue("SAVEDATA_DIRECTORY", std::string(param->gameName) + param->dataName, 64);230231// TODO: Maybe there should be other things in the SFO file? Needs testing.232233u8 *sfoData;234size_t sfoSize;235sfoFile.WriteSFO(&sfoData,&sfoSize);236237int handle = pspFileSystem.OpenFile(sfopath, (FileAccess)(FILEACCESS_WRITE | FILEACCESS_CREATE | FILEACCESS_TRUNCATE));238if (handle >= 0) {239pspFileSystem.WriteFile(handle, sfoData, sfoSize);240pspFileSystem.CloseFile(handle);241}242243delete[] sfoData;244}245246int PSPGamedataInstallDialog::Abort() {247param->common.result = 1;248param.NotifyWrite("DialogResult");249250// TODO: Delete the files or anything?251return PSPDialog::Shutdown();252}253254int PSPGamedataInstallDialog::Shutdown(bool force) {255if (GetStatus() != SCE_UTILITY_STATUS_FINISHED && !force)256return SCE_ERROR_UTILITY_INVALID_STATUS;257258return PSPDialog::Shutdown(force);259}260261std::string PSPGamedataInstallDialog::GetGameDataInstallFileName(const SceUtilityGamedataInstallParam *param, const std::string &filename) {262if (!param)263return "";264std::string GameDataInstallPath = saveBasePath + param->gameName + param->dataName + "/";265if (!pspFileSystem.GetFileInfo(GameDataInstallPath).exists)266pspFileSystem.MkDir(GameDataInstallPath);267268return GameDataInstallPath + filename;269}270271void PSPGamedataInstallDialog::UpdateProgress() {272// Update progress bar(if there is).273// We only should update progress[0] here as the max progress value is 100.274if (allFilesSize != 0)275progressValue = (int)((allReadSize * 100) / allFilesSize);276else277progressValue = 100;278279if (param->mode == PSP_UTILITY_GAMEDATA_MODE_SHOW_PROGRESS) {280RenderProgress(progressValue);281}282283param->progress = progressValue;284param.NotifyWrite("DialogResult");285}286287void PSPGamedataInstallDialog::RenderProgress(int percentage) {288StartDraw();289290float barWidth = 380;291float barX = (480 - barWidth) / 2;292float barWidthDone = barWidth * percentage / 100;293float barH = 10.0;294float barY = 272 / 2 - barH / 2;295296PPGeDrawRect(barX - 3, barY - 3, barX + barWidth + 3, barY + barH + 3, 0x30000000);297PPGeDrawRect(barX, barY, barX + barWidth, barY + barH, 0xFF707070);298PPGeDrawRect(barX, barY, barX + barWidthDone, barY + barH, 0xFFE0E0E0);299300auto di = GetI18NCategory(I18NCat::DIALOG);301302fadeValue = 255;303PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_HCENTER, 0.6f);304305PPGeDrawText(di->T("Installing..."), 480 / 2, barY + barH + 10, textStyle);306307EndDraw();308}309310void PSPGamedataInstallDialog::DoState(PointerWrap &p) {311auto s = p.Section("PSPGamedataInstallDialog", 0, 4);312if (!s)313return;314315// This was included in version 1 and higher.316PSPDialog::DoState(p);317Do(p, request);318319// This was included in version 2 and higher, but for BC reasons we use 3+.320if (s >= 3) {321Do(p, param.ptr);322Do(p, inFileNames);323Do(p, numFiles);324Do(p, readFiles);325Do(p, allFilesSize);326Do(p, allReadSize);327Do(p, progressValue);328} else {329param.ptr = 0;330}331332if (s >= 4) {333Do(p, currentInputFile);334Do(p, currentInputBytesLeft);335Do(p, currentOutputFile);336} else {337currentInputFile = 0;338currentInputBytesLeft = 0;339currentOutputFile = 0;340}341}342343344