2012-11-01 16:19:01 +01:00
|
|
|
// Copyright (C) 2012 PPSSPP Project
|
|
|
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
2012-11-04 23:01:49 +01:00
|
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
2012-11-01 16:19:01 +01:00
|
|
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
|
|
|
|
// Official git repository and contact information can be found at
|
|
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
|
2018-01-01 19:52:24 -08:00
|
|
|
#include <thread>
|
2018-03-13 11:25:00 +01:00
|
|
|
|
2020-10-01 13:05:04 +02:00
|
|
|
#include "Common/Data/Encoding/Utf8.h"
|
2020-10-01 09:27:25 +02:00
|
|
|
#include "Common/Thread/ThreadUtil.h"
|
2013-12-29 23:28:31 +01:00
|
|
|
|
2020-10-04 20:48:47 +02:00
|
|
|
#include "Common/File/FileUtil.h"
|
2013-12-29 23:28:31 +01:00
|
|
|
#include "Common/StringUtils.h"
|
2017-02-27 21:57:46 +01:00
|
|
|
#ifdef _WIN32
|
|
|
|
#include "Common/CommonWindows.h"
|
|
|
|
#endif
|
2013-12-29 23:28:31 +01:00
|
|
|
|
|
|
|
#include "Core/ELF/ElfReader.h"
|
|
|
|
#include "Core/ELF/ParamSFO.h"
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2017-06-03 12:47:38 -07:00
|
|
|
#include "Core/FileSystems/BlockDevices.h"
|
|
|
|
#include "Core/FileSystems/BlobFileSystem.h"
|
|
|
|
#include "Core/FileSystems/DirectoryFileSystem.h"
|
|
|
|
#include "Core/FileSystems/ISOFileSystem.h"
|
|
|
|
#include "Core/FileSystems/MetaFileSystem.h"
|
|
|
|
#include "Core/FileSystems/VirtualDiscFileSystem.h"
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2014-11-23 13:25:32 -08:00
|
|
|
#include "Core/Loaders.h"
|
2013-12-29 23:28:31 +01:00
|
|
|
#include "Core/MemMap.h"
|
2014-03-15 10:38:46 -07:00
|
|
|
#include "Core/HDRemaster.h"
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2013-12-29 23:28:31 +01:00
|
|
|
#include "Core/MIPS/MIPS.h"
|
|
|
|
#include "Core/MIPS/MIPSAnalyst.h"
|
|
|
|
#include "Core/MIPS/MIPSCodeUtils.h"
|
2012-11-01 16:19:01 +01:00
|
|
|
|
|
|
|
#include "Host.h"
|
|
|
|
|
2013-11-28 14:37:10 -05:00
|
|
|
#include "Core/Config.h"
|
2018-06-16 18:42:31 -07:00
|
|
|
#include "Core/ConfigValues.h"
|
2013-12-29 23:28:31 +01:00
|
|
|
#include "Core/System.h"
|
|
|
|
#include "Core/PSPLoaders.h"
|
|
|
|
#include "Core/HLE/HLE.h"
|
|
|
|
#include "Core/HLE/sceKernel.h"
|
|
|
|
#include "Core/HLE/sceKernelThread.h"
|
|
|
|
#include "Core/HLE/sceKernelModule.h"
|
|
|
|
#include "Core/HLE/sceKernelMemory.h"
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2019-09-28 11:07:57 -07:00
|
|
|
static std::thread loadingThread;
|
|
|
|
|
2015-12-24 10:04:08 -08:00
|
|
|
static void UseLargeMem(int memsize) {
|
|
|
|
if (memsize != 1) {
|
|
|
|
// Nothing requested.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Memory::g_PSPModel != PSP_MODEL_FAT) {
|
|
|
|
INFO_LOG(LOADER, "Game requested full PSP-2000 memory access");
|
|
|
|
Memory::g_MemorySize = Memory::RAM_DOUBLE_SIZE;
|
|
|
|
} else {
|
|
|
|
WARN_LOG(LOADER, "Game requested full PSP-2000 memory access, ignoring in PSP-1000 mode");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-22 04:05:28 -04:00
|
|
|
// We gather the game info before actually loading/booting the ISO
|
|
|
|
// to determine if the emulator should enable extra memory and
|
|
|
|
// double-sized texture coordinates.
|
2014-11-23 13:25:32 -08:00
|
|
|
void InitMemoryForGameISO(FileLoader *fileLoader) {
|
|
|
|
if (!fileLoader->Exists()) {
|
|
|
|
return;
|
|
|
|
}
|
2013-07-23 17:24:33 +02:00
|
|
|
|
2015-12-24 10:04:08 -08:00
|
|
|
IFileSystem *fileSystem = nullptr;
|
|
|
|
IFileSystem *blockSystem = nullptr;
|
2020-01-04 09:11:15 +08:00
|
|
|
|
2015-12-24 10:04:08 -08:00
|
|
|
if (fileLoader->IsDirectory()) {
|
2021-05-09 17:10:14 -07:00
|
|
|
fileSystem = new VirtualDiscFileSystem(&pspFileSystem, fileLoader->GetPath());
|
2015-12-24 10:04:08 -08:00
|
|
|
blockSystem = fileSystem;
|
|
|
|
} else {
|
2014-11-23 13:59:56 -08:00
|
|
|
auto bd = constructBlockDevice(fileLoader);
|
2013-07-23 17:24:33 +02:00
|
|
|
// Can't init anything without a block device...
|
|
|
|
if (!bd)
|
|
|
|
return;
|
2014-10-31 00:14:00 +01:00
|
|
|
|
2015-12-24 10:04:08 -08:00
|
|
|
ISOFileSystem *iso = new ISOFileSystem(&pspFileSystem, bd);
|
|
|
|
fileSystem = iso;
|
|
|
|
blockSystem = new ISOBlockSystem(iso);
|
2014-07-27 23:42:46 +02:00
|
|
|
}
|
|
|
|
|
2015-12-24 10:04:08 -08:00
|
|
|
pspFileSystem.Mount("umd0:", blockSystem);
|
|
|
|
pspFileSystem.Mount("umd1:", blockSystem);
|
|
|
|
pspFileSystem.Mount("disc0:", fileSystem);
|
|
|
|
pspFileSystem.Mount("umd:", blockSystem);
|
|
|
|
// TODO: Should we do this?
|
|
|
|
//pspFileSystem.Mount("host0:", fileSystem);
|
2013-11-24 11:02:36 +08:00
|
|
|
|
2013-06-22 04:05:28 -04:00
|
|
|
std::string gameID;
|
2015-12-27 00:31:33 -08:00
|
|
|
std::string umdData;
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2012-11-30 21:49:59 +01:00
|
|
|
std::string sfoPath("disc0:/PSP_GAME/PARAM.SFO");
|
|
|
|
PSPFileInfo fileInfo = pspFileSystem.GetFileInfo(sfoPath.c_str());
|
2013-06-22 04:05:28 -04:00
|
|
|
|
2015-12-24 10:04:08 -08:00
|
|
|
if (fileInfo.exists) {
|
2013-12-08 12:02:37 -08:00
|
|
|
std::vector<u8> paramsfo;
|
|
|
|
pspFileSystem.ReadEntireFile(sfoPath, paramsfo);
|
2015-12-23 15:10:08 -08:00
|
|
|
if (g_paramSFO.ReadSFO(paramsfo)) {
|
2015-12-24 10:04:08 -08:00
|
|
|
UseLargeMem(g_paramSFO.GetValueInt("MEMSIZE"));
|
2013-06-22 04:05:28 -04:00
|
|
|
gameID = g_paramSFO.GetValueString("DISC_ID");
|
2015-12-24 10:04:08 -08:00
|
|
|
}
|
2015-12-27 00:31:33 -08:00
|
|
|
|
|
|
|
std::vector<u8> umdDataBin;
|
|
|
|
if (pspFileSystem.ReadEntireFile("disc0:/UMD_DATA.BIN", umdDataBin) >= 0) {
|
|
|
|
umdData = std::string((const char *)&umdDataBin[0], umdDataBin.size());
|
|
|
|
}
|
2015-12-24 10:04:08 -08:00
|
|
|
}
|
2013-06-22 04:05:28 -04:00
|
|
|
|
2015-12-27 00:31:33 -08:00
|
|
|
for (size_t i = 0; i < g_HDRemastersCount; i++) {
|
|
|
|
const auto &entry = g_HDRemasters[i];
|
|
|
|
if (entry.gameID != gameID) {
|
|
|
|
continue;
|
2015-12-23 15:10:08 -08:00
|
|
|
}
|
2015-12-27 09:06:26 -08:00
|
|
|
if (entry.umdDataValue && umdData.find(entry.umdDataValue) == umdData.npos) {
|
2015-12-27 00:31:33 -08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_RemasterMode = true;
|
|
|
|
Memory::g_MemorySize = entry.memorySize;
|
|
|
|
g_DoubleTextureCoordinates = entry.doubleTextureCoordinates;
|
|
|
|
break;
|
2015-12-23 15:10:08 -08:00
|
|
|
}
|
2015-12-24 10:04:08 -08:00
|
|
|
if (g_RemasterMode) {
|
|
|
|
INFO_LOG(LOADER, "HDRemaster found, using increased memory");
|
|
|
|
}
|
2015-12-23 15:10:08 -08:00
|
|
|
}
|
|
|
|
|
2020-01-02 14:56:24 +08:00
|
|
|
bool ReInitMemoryForGameISO(FileLoader *fileLoader) {
|
|
|
|
if (!fileLoader->Exists()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFileSystem *fileSystem = nullptr;
|
|
|
|
IFileSystem *blockSystem = nullptr;
|
|
|
|
|
|
|
|
if (fileLoader->IsDirectory()) {
|
2021-05-09 17:10:14 -07:00
|
|
|
fileSystem = new VirtualDiscFileSystem(&pspFileSystem, fileLoader->GetPath());
|
2020-01-02 14:56:24 +08:00
|
|
|
blockSystem = fileSystem;
|
2020-01-04 09:11:15 +08:00
|
|
|
} else {
|
2020-01-02 14:56:24 +08:00
|
|
|
auto bd = constructBlockDevice(fileLoader);
|
|
|
|
if (!bd)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ISOFileSystem *iso = new ISOFileSystem(&pspFileSystem, bd);
|
|
|
|
fileSystem = iso;
|
|
|
|
blockSystem = new ISOBlockSystem(iso);
|
|
|
|
}
|
|
|
|
|
2020-01-04 11:14:32 +08:00
|
|
|
pspFileSystem.Remount("umd0:", blockSystem);
|
|
|
|
pspFileSystem.Remount("umd1:", blockSystem);
|
|
|
|
pspFileSystem.Remount("umd:", blockSystem);
|
|
|
|
pspFileSystem.Remount("disc0:", fileSystem);
|
2020-01-04 09:11:15 +08:00
|
|
|
|
2020-01-02 14:56:24 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-12-23 15:10:08 -08:00
|
|
|
void InitMemoryForGamePBP(FileLoader *fileLoader) {
|
2015-12-24 10:04:08 -08:00
|
|
|
if (!fileLoader->Exists()) {
|
|
|
|
return;
|
|
|
|
}
|
2015-12-23 15:10:08 -08:00
|
|
|
|
2015-12-24 10:40:25 -08:00
|
|
|
PBPReader pbp(fileLoader);
|
2015-12-23 15:10:08 -08:00
|
|
|
if (pbp.IsValid() && !pbp.IsELF()) {
|
2015-12-24 10:20:27 -08:00
|
|
|
std::vector<u8> sfoData;
|
|
|
|
if (pbp.GetSubFile(PBP_PARAM_SFO, &sfoData)) {
|
2015-12-23 15:10:08 -08:00
|
|
|
ParamSFOData paramSFO;
|
2015-12-24 10:20:27 -08:00
|
|
|
if (paramSFO.ReadSFO(sfoData)) {
|
2015-12-24 10:04:08 -08:00
|
|
|
// This is the parameter CFW uses to determine homebrew wants the full 64MB.
|
|
|
|
UseLargeMem(paramSFO.GetValueInt("MEMSIZE"));
|
2021-02-28 10:41:53 -08:00
|
|
|
|
|
|
|
// Take this moment to bring over the title, if set.
|
|
|
|
bool updateTitle = false;
|
|
|
|
std::string title = paramSFO.GetValueString("TITLE");
|
|
|
|
if (g_paramSFO.GetValueString("TITLE").empty() && !title.empty()) {
|
|
|
|
g_paramSFO.SetValue("TITLE", title, (int)title.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string discID = paramSFO.GetValueString("DISC_ID");
|
2021-04-17 14:28:53 -07:00
|
|
|
std::string systemVer = paramSFO.GetValueString("PSP_SYSTEM_VER");
|
2021-02-28 10:41:53 -08:00
|
|
|
// Homebrew typically always leave this zero.
|
|
|
|
bool discTotalCheck = paramSFO.GetValueInt("DISC_TOTAL") != 0;
|
|
|
|
// A lot of homebrew reuse real game disc IDs - avoid.
|
|
|
|
bool formatCheck = discID.substr(0, 2) != "NP" && discID.substr(0, 2) != "UL" && discID.substr(0, 2) != "UC";
|
|
|
|
char region = discID.size() > 3 ? discID[2] : '\0';
|
|
|
|
bool regionCheck = region != 'A' && region != 'E' && region != 'H' && region != 'I' && region != 'J' && region != 'K' && region != 'U' && region != 'X';
|
2021-04-17 14:28:53 -07:00
|
|
|
bool systemVerCheck = !systemVer.empty() && systemVer[0] >= '5';
|
|
|
|
if ((formatCheck || regionCheck || discTotalCheck || systemVerCheck) && !discID.empty()) {
|
2021-02-28 10:41:53 -08:00
|
|
|
g_paramSFO.SetValue("DISC_ID", discID, (int)discID.size());
|
|
|
|
std::string ver = paramSFO.GetValueString("DISC_VERSION");
|
|
|
|
if (ver.empty())
|
|
|
|
ver = "1.00";
|
|
|
|
g_paramSFO.SetValue("DISC_VERSION", ver, (int)ver.size());
|
|
|
|
}
|
2015-12-24 10:04:08 -08:00
|
|
|
}
|
2015-12-23 15:10:08 -08:00
|
|
|
}
|
|
|
|
}
|
2013-06-22 04:05:28 -04:00
|
|
|
}
|
|
|
|
|
2014-02-15 14:30:40 +01:00
|
|
|
|
|
|
|
// Chinese translators like to rename EBOOT.BIN and replace it with some kind of stub
|
|
|
|
// that probably loads a plugin and then launches the actual game. These stubs don't work in PPSSPP.
|
|
|
|
// No idea why they are doing this, but it works to just bypass it. They could stop
|
|
|
|
// inventing new filenames though...
|
|
|
|
static const char *altBootNames[] = {
|
|
|
|
"disc0:/PSP_GAME/SYSDIR/EBOOT.OLD",
|
|
|
|
"disc0:/PSP_GAME/SYSDIR/EBOOT.DAT",
|
|
|
|
"disc0:/PSP_GAME/SYSDIR/EBOOT.BI",
|
|
|
|
"disc0:/PSP_GAME/SYSDIR/EBOOT.LLD",
|
2015-04-23 20:16:45 +08:00
|
|
|
//"disc0:/PSP_GAME/SYSDIR/OLD_EBOOT.BIN", //Utawareru Mono Chinese version
|
2014-02-15 14:30:40 +01:00
|
|
|
"disc0:/PSP_GAME/SYSDIR/EBOOT.123",
|
2020-06-07 18:15:49 +08:00
|
|
|
//"disc0:/PSP_GAME/SYSDIR/EBOOT_LRC_CH.BIN", // Hatsune Miku Project Diva Extend chinese version
|
2014-02-15 14:30:40 +01:00
|
|
|
"disc0:/PSP_GAME/SYSDIR/BOOT0.OLD",
|
|
|
|
"disc0:/PSP_GAME/SYSDIR/BOOT1.OLD",
|
|
|
|
"disc0:/PSP_GAME/SYSDIR/BINOT.BIN",
|
|
|
|
"disc0:/PSP_GAME/SYSDIR/EBOOT.FRY",
|
|
|
|
"disc0:/PSP_GAME/SYSDIR/EBOOT.Z.Y",
|
|
|
|
"disc0:/PSP_GAME/SYSDIR/EBOOT.LEI",
|
|
|
|
"disc0:/PSP_GAME/SYSDIR/EBOOT.DNR",
|
|
|
|
"disc0:/PSP_GAME/SYSDIR/DBZ2.BIN",
|
2020-06-14 09:43:08 +08:00
|
|
|
//"disc0:/PSP_GAME/SYSDIR/ss.RAW",//Code Geass: Lost Colors chinese version
|
2014-02-15 14:30:40 +01:00
|
|
|
};
|
|
|
|
|
2017-02-19 10:26:16 +01:00
|
|
|
bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) {
|
2013-06-23 01:37:24 -04:00
|
|
|
// Mounting stuff relocated to InitMemoryForGameISO due to HD Remaster restructuring of code.
|
2013-06-22 04:05:28 -04:00
|
|
|
|
|
|
|
std::string sfoPath("disc0:/PSP_GAME/PARAM.SFO");
|
|
|
|
PSPFileInfo fileInfo = pspFileSystem.GetFileInfo(sfoPath.c_str());
|
2017-02-19 10:26:16 +01:00
|
|
|
if (fileInfo.exists) {
|
2013-12-08 12:02:37 -08:00
|
|
|
std::vector<u8> paramsfo;
|
|
|
|
pspFileSystem.ReadEntireFile(sfoPath, paramsfo);
|
2017-02-19 10:26:16 +01:00
|
|
|
if (g_paramSFO.ReadSFO(paramsfo)) {
|
2017-05-30 13:52:29 +02:00
|
|
|
std::string title = StringFromFormat("%s : %s", g_paramSFO.GetValueString("DISC_ID").c_str(), g_paramSFO.GetValueString("TITLE").c_str());
|
|
|
|
INFO_LOG(LOADER, "%s", title.c_str());
|
|
|
|
host->SetWindowTitle(title.c_str());
|
2012-11-30 21:49:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-05 14:54:28 +01:00
|
|
|
std::string bootpath("disc0:/PSP_GAME/SYSDIR/EBOOT.BIN");
|
2014-02-15 14:30:40 +01:00
|
|
|
|
|
|
|
// Bypass Chinese translation patches, see comment above.
|
2014-03-03 00:08:32 -08:00
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(altBootNames); i++) {
|
2014-02-15 14:30:40 +01:00
|
|
|
if (pspFileSystem.GetFileInfo(altBootNames[i]).exists) {
|
2014-02-16 10:51:45 +08:00
|
|
|
bootpath = altBootNames[i];
|
2014-02-15 14:30:40 +01:00
|
|
|
}
|
2014-02-14 20:39:08 +08:00
|
|
|
}
|
2013-03-02 00:48:20 +08:00
|
|
|
|
2014-02-15 15:40:39 +01:00
|
|
|
// Bypass another more dangerous one where the file is in USRDIR - this could collide with files in some game.
|
2014-02-15 15:44:32 +01:00
|
|
|
std::string id = g_paramSFO.GetValueString("DISC_ID");
|
|
|
|
if (id == "NPJH50624" && pspFileSystem.GetFileInfo("disc0:/PSP_GAME/USRDIR/PAKFILE2.BIN").exists) {
|
2014-02-16 10:51:45 +08:00
|
|
|
bootpath = "disc0:/PSP_GAME/USRDIR/PAKFILE2.BIN";
|
2014-02-15 15:40:39 +01:00
|
|
|
}
|
2014-05-30 12:28:27 +08:00
|
|
|
if (id == "NPJH00100" && pspFileSystem.GetFileInfo("disc0:/PSP_GAME/USRDIR/DATA/GIM/GBL").exists) {
|
|
|
|
bootpath = "disc0:/PSP_GAME/USRDIR/DATA/GIM/GBL";
|
|
|
|
}
|
2014-02-15 15:40:39 +01:00
|
|
|
|
2012-11-05 14:54:28 +01:00
|
|
|
bool hasEncrypted = false;
|
2020-03-08 21:22:21 -07:00
|
|
|
int fd;
|
2019-10-20 11:03:37 -07:00
|
|
|
if ((fd = pspFileSystem.OpenFile(bootpath, FILEACCESS_READ)) >= 0)
|
2012-11-05 14:54:28 +01:00
|
|
|
{
|
|
|
|
u8 head[4];
|
|
|
|
pspFileSystem.ReadFile(fd, head, 4);
|
2012-11-06 16:20:13 +01:00
|
|
|
if (memcmp(head, "~PSP", 4) == 0 || memcmp(head, "\x7F""ELF", 4) == 0) {
|
2012-11-05 14:54:28 +01:00
|
|
|
hasEncrypted = true;
|
|
|
|
}
|
2012-11-30 21:49:59 +01:00
|
|
|
pspFileSystem.CloseFile(fd);
|
2012-11-05 14:54:28 +01:00
|
|
|
}
|
2016-03-06 14:16:40 +01:00
|
|
|
if (!hasEncrypted) {
|
2012-11-05 14:54:28 +01:00
|
|
|
// try unencrypted BOOT.BIN
|
|
|
|
bootpath = "disc0:/PSP_GAME/SYSDIR/BOOT.BIN";
|
|
|
|
}
|
2016-06-04 22:10:47 -07:00
|
|
|
|
|
|
|
// Fail early with a clearer message for some types of ISOs.
|
|
|
|
if (!pspFileSystem.GetFileInfo(bootpath).exists) {
|
|
|
|
// Can't tell for sure if it's PS1 or PS2, but doesn't much matter.
|
|
|
|
if (pspFileSystem.GetFileInfo("disc0:/SYSTEM.CNF;1").exists || pspFileSystem.GetFileInfo("disc0:/PSX.EXE;1").exists) {
|
2020-11-30 23:58:43 +01:00
|
|
|
*error_string = "PPSSPP plays PSP games, not PlayStation 1 or 2 games.";
|
2016-06-04 22:10:47 -07:00
|
|
|
} else if (pspFileSystem.GetFileInfo("disc0:/UMD_VIDEO/PLAYLIST.UMD").exists) {
|
|
|
|
*error_string = "PPSSPP doesn't support UMD Video.";
|
|
|
|
} else if (pspFileSystem.GetFileInfo("disc0:/UMD_AUDIO/PLAYLIST.UMD").exists) {
|
|
|
|
*error_string = "PPSSPP doesn't support UMD Music.";
|
|
|
|
} else if (pspFileSystem.GetDirListing("disc0:/").empty()) {
|
|
|
|
*error_string = "Not a valid disc image.";
|
|
|
|
} else {
|
|
|
|
*error_string = "A PSP game couldn't be found on the disc.";
|
|
|
|
}
|
2020-07-04 20:19:56 +02:00
|
|
|
coreState = CORE_BOOT_ERROR;
|
2016-06-04 22:10:47 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-12-14 20:33:20 +01:00
|
|
|
//in case we didn't go through EmuScreen::boot
|
2019-07-17 21:28:21 -07:00
|
|
|
g_Config.loadGameConfig(id, g_paramSFO.GetValueString("TITLE"));
|
2017-12-09 14:11:39 -08:00
|
|
|
host->SendUIMessage("config_loaded", "");
|
2012-11-01 16:19:01 +01:00
|
|
|
INFO_LOG(LOADER,"Loading %s...", bootpath.c_str());
|
2018-01-01 19:52:24 -08:00
|
|
|
|
2019-09-28 11:07:57 -07:00
|
|
|
PSPLoaders_Shutdown();
|
|
|
|
// Note: this thread reads the game binary, loads caches, and links HLE while UI spins.
|
|
|
|
// To do something deterministically when the game starts, disabling this thread won't be enough.
|
|
|
|
// Instead: Use Core_ListenLifecycle() or watch coreState.
|
|
|
|
loadingThread = std::thread([bootpath] {
|
2020-12-01 00:46:26 +01:00
|
|
|
SetCurrentThreadName("ExecLoader");
|
2018-11-01 21:27:01 -07:00
|
|
|
PSP_LoadingLock guard;
|
|
|
|
if (coreState != CORE_POWERUP)
|
|
|
|
return;
|
|
|
|
|
2018-03-13 11:25:00 +01:00
|
|
|
PSP_SetLoading("Loading executable...");
|
2018-01-01 19:52:24 -08:00
|
|
|
// TODO: We can't use the initial error_string pointer.
|
|
|
|
bool success = __KernelLoadExec(bootpath.c_str(), 0, &PSP_CoreParameter().errorString);
|
|
|
|
if (success && coreState == CORE_POWERUP) {
|
2018-06-23 10:14:36 -07:00
|
|
|
coreState = PSP_CoreParameter().startBreak ? CORE_STEPPING : CORE_RUNNING;
|
2018-01-01 19:52:24 -08:00
|
|
|
} else {
|
2020-07-04 20:19:56 +02:00
|
|
|
coreState = CORE_BOOT_ERROR;
|
2018-01-01 19:52:24 -08:00
|
|
|
// TODO: This is a crummy way to communicate the error...
|
2021-05-09 17:38:39 -07:00
|
|
|
PSP_CoreParameter().fileToStart.clear();
|
2018-01-01 19:52:24 -08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
return true;
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
|
|
|
|
2017-02-19 10:26:16 +01:00
|
|
|
static std::string NormalizePath(const std::string &path) {
|
2014-04-19 12:52:43 -07:00
|
|
|
#ifdef _WIN32
|
2017-02-24 18:59:41 +01:00
|
|
|
std::wstring wpath = ConvertUTF8ToWString(path);
|
2020-01-04 09:02:10 -08:00
|
|
|
std::wstring buf;
|
|
|
|
buf.resize(512);
|
|
|
|
size_t sz = GetFullPathName(wpath.c_str(), (DWORD)buf.size(), &buf[0], nullptr);
|
|
|
|
if (sz != 0 && sz < buf.size()) {
|
|
|
|
buf.resize(sz);
|
|
|
|
} else if (sz > buf.size()) {
|
|
|
|
buf.resize(sz);
|
|
|
|
sz = GetFullPathName(wpath.c_str(), (DWORD)buf.size(), &buf[0], nullptr);
|
|
|
|
// This should truncate off the null terminator.
|
|
|
|
buf.resize(sz);
|
|
|
|
}
|
2017-02-24 18:59:41 +01:00
|
|
|
return ConvertWStringToUTF8(buf);
|
2014-04-19 12:52:43 -07:00
|
|
|
#else
|
|
|
|
char buf[PATH_MAX + 1];
|
|
|
|
if (realpath(path.c_str(), buf) == NULL)
|
|
|
|
return "";
|
|
|
|
return buf;
|
2017-02-24 18:59:41 +01:00
|
|
|
#endif
|
2014-04-19 12:52:43 -07:00
|
|
|
}
|
|
|
|
|
2017-02-19 10:26:16 +01:00
|
|
|
bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) {
|
2013-05-08 23:36:57 +08:00
|
|
|
// This is really just for headless, might need tweaking later.
|
2017-02-19 10:26:16 +01:00
|
|
|
if (PSP_CoreParameter().mountIsoLoader != nullptr) {
|
2014-11-23 13:59:56 -08:00
|
|
|
auto bd = constructBlockDevice(PSP_CoreParameter().mountIsoLoader);
|
2013-07-01 00:33:19 -07:00
|
|
|
if (bd != NULL) {
|
|
|
|
ISOFileSystem *umd2 = new ISOFileSystem(&pspFileSystem, bd);
|
2020-05-21 18:01:53 -07:00
|
|
|
ISOBlockSystem *blockSystem = new ISOBlockSystem(umd2);
|
2013-01-09 00:46:47 -08:00
|
|
|
|
2020-05-21 18:01:53 -07:00
|
|
|
pspFileSystem.Mount("umd1:", blockSystem);
|
2013-07-01 00:33:19 -07:00
|
|
|
pspFileSystem.Mount("disc0:", umd2);
|
2020-05-21 18:01:53 -07:00
|
|
|
pspFileSystem.Mount("umd:", blockSystem);
|
2013-07-01 00:33:19 -07:00
|
|
|
}
|
2013-05-08 23:36:57 +08:00
|
|
|
}
|
2013-01-09 00:46:47 -08:00
|
|
|
|
2021-05-09 17:10:14 -07:00
|
|
|
std::string full_path = fileLoader->GetPath();
|
2013-05-08 23:36:57 +08:00
|
|
|
std::string path, file, extension;
|
|
|
|
SplitPath(ReplaceAll(full_path, "\\", "/"), &path, &file, &extension);
|
2021-05-09 17:50:59 -07:00
|
|
|
if (!path.empty() && path.back() == '/')
|
|
|
|
path.resize(path.size() - 1);
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (!path.empty() && path.back() == '\\')
|
|
|
|
path.resize(path.size() - 1);
|
|
|
|
#endif
|
2017-02-19 10:26:16 +01:00
|
|
|
|
2021-05-09 17:50:59 -07:00
|
|
|
size_t pos = path.find("PSP/GAME/");
|
2017-02-19 10:26:16 +01:00
|
|
|
std::string ms_path;
|
|
|
|
if (pos != std::string::npos) {
|
2021-05-09 17:50:59 -07:00
|
|
|
ms_path = "ms0:/" + path.substr(pos) + "/";
|
2017-02-19 10:26:16 +01:00
|
|
|
} else {
|
2017-06-07 18:52:46 -07:00
|
|
|
// This is wrong, but it's better than not having a working directory at all.
|
|
|
|
// Note that umd0:/ is actually the writable containing directory, in this case.
|
|
|
|
ms_path = "umd0:/";
|
2017-02-19 10:26:16 +01:00
|
|
|
}
|
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
#ifdef _WIN32
|
2017-02-19 10:26:16 +01:00
|
|
|
// Turn the slashes back to the Windows way.
|
2013-05-08 23:36:57 +08:00
|
|
|
path = ReplaceAll(path, "/", "\\");
|
2012-11-01 16:19:01 +01:00
|
|
|
#endif
|
|
|
|
|
2015-12-27 19:25:28 -08:00
|
|
|
if (!PSP_CoreParameter().mountRoot.empty()) {
|
2014-04-19 12:52:43 -07:00
|
|
|
// We don't want to worry about .. and cwd and such.
|
|
|
|
const std::string rootNorm = NormalizePath(PSP_CoreParameter().mountRoot + "/");
|
|
|
|
const std::string pathNorm = NormalizePath(path + "/");
|
|
|
|
|
|
|
|
// If root is not a subpath of path, we can't boot the game.
|
2015-12-27 19:25:28 -08:00
|
|
|
if (!startsWith(pathNorm, rootNorm)) {
|
2014-04-19 12:52:43 -07:00
|
|
|
*error_string = "Cannot boot ELF located outside mountRoot.";
|
2020-07-04 20:19:56 +02:00
|
|
|
coreState = CORE_BOOT_ERROR;
|
2014-04-19 12:52:43 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string filepath = ReplaceAll(pathNorm.substr(rootNorm.size()), "\\", "/");
|
|
|
|
file = filepath + "/" + file;
|
2014-08-30 22:38:19 -07:00
|
|
|
path = rootNorm + "/";
|
2014-04-19 12:52:43 -07:00
|
|
|
pspFileSystem.SetStartingDirectory(filepath);
|
2015-12-27 19:25:28 -08:00
|
|
|
} else {
|
2017-06-07 18:52:46 -07:00
|
|
|
pspFileSystem.SetStartingDirectory(ms_path);
|
2014-04-19 12:52:43 -07:00
|
|
|
}
|
|
|
|
|
2020-05-21 16:10:08 -07:00
|
|
|
DirectoryFileSystem *fs = new DirectoryFileSystem(&pspFileSystem, path, FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD);
|
2013-05-08 23:36:57 +08:00
|
|
|
pspFileSystem.Mount("umd0:", fs);
|
2013-04-27 23:16:51 +02:00
|
|
|
|
2017-02-19 10:26:16 +01:00
|
|
|
std::string finalName = ms_path + file + extension;
|
2017-05-26 23:15:04 +02:00
|
|
|
|
|
|
|
std::string homebrewName = PSP_CoreParameter().fileToStart;
|
|
|
|
std::size_t lslash = homebrewName.find_last_of("/");
|
2021-02-28 10:41:53 -08:00
|
|
|
if (lslash != homebrewName.npos)
|
|
|
|
homebrewName = homebrewName.substr(lslash + 1);
|
|
|
|
std::string homebrewTitle = g_paramSFO.GetValueString("TITLE");
|
|
|
|
if (homebrewTitle.empty())
|
|
|
|
homebrewTitle = homebrewName;
|
|
|
|
std::string discID = g_paramSFO.GetDiscID();
|
|
|
|
std::string discVersion = g_paramSFO.GetValueString("DISC_VERSION");
|
2017-05-26 23:15:04 +02:00
|
|
|
std::string madeUpID = g_paramSFO.GenerateFakeID();
|
|
|
|
|
2021-02-28 10:41:53 -08:00
|
|
|
std::string title = StringFromFormat("%s : %s", discID.c_str(), homebrewTitle.c_str());
|
2017-05-30 13:52:29 +02:00
|
|
|
INFO_LOG(LOADER, "%s", title.c_str());
|
|
|
|
host->SetWindowTitle(title.c_str());
|
2017-05-26 23:15:04 +02:00
|
|
|
|
2021-02-28 10:41:53 -08:00
|
|
|
// Migrate old save states from old versions of fake game IDs.
|
|
|
|
const std::string savestateDir = GetSysDirectory(DIRECTORY_SAVESTATE);
|
|
|
|
for (int i = 0; i < 5; ++i) {
|
|
|
|
std::string newPrefix = StringFromFormat("%s%s_%s_%d", savestateDir.c_str(), discID.c_str(), discVersion.c_str(), i);
|
|
|
|
std::string oldNamePrefix = StringFromFormat("%s%s_%d", savestateDir.c_str(), homebrewName.c_str(), i);
|
|
|
|
std::string oldIDPrefix = StringFromFormat("%s%s_1.00_%d", savestateDir.c_str(), madeUpID.c_str(), i);
|
|
|
|
|
|
|
|
if (oldIDPrefix != newPrefix && File::Exists(oldIDPrefix + ".ppst"))
|
|
|
|
File::Rename(oldIDPrefix + ".ppst", newPrefix + ".ppst");
|
|
|
|
else if (File::Exists(oldNamePrefix + ".ppst"))
|
|
|
|
File::Rename(oldNamePrefix + ".ppst", newPrefix + ".ppst");
|
|
|
|
if (oldIDPrefix != newPrefix && File::Exists(oldIDPrefix + ".jpg"))
|
|
|
|
File::Rename(oldIDPrefix + ".jpg", newPrefix + ".jpg");
|
|
|
|
else if (File::Exists(oldNamePrefix + ".jpg"))
|
|
|
|
File::Rename(oldNamePrefix + ".jpg", newPrefix + ".jpg");
|
2017-05-29 03:42:45 +02:00
|
|
|
}
|
|
|
|
|
2019-09-28 11:07:57 -07:00
|
|
|
PSPLoaders_Shutdown();
|
|
|
|
// Note: See Load_PSP_ISO for notes about this thread.
|
|
|
|
loadingThread = std::thread([finalName] {
|
2020-12-01 00:46:26 +01:00
|
|
|
SetCurrentThreadName("ExecLoader");
|
2018-11-01 21:27:01 -07:00
|
|
|
PSP_LoadingLock guard;
|
|
|
|
if (coreState != CORE_POWERUP)
|
|
|
|
return;
|
|
|
|
|
2018-01-01 19:52:24 -08:00
|
|
|
bool success = __KernelLoadExec(finalName.c_str(), 0, &PSP_CoreParameter().errorString);
|
|
|
|
if (success && coreState == CORE_POWERUP) {
|
2018-06-23 10:14:36 -07:00
|
|
|
coreState = PSP_CoreParameter().startBreak ? CORE_STEPPING : CORE_RUNNING;
|
2018-01-01 19:52:24 -08:00
|
|
|
} else {
|
2020-07-04 20:19:56 +02:00
|
|
|
coreState = CORE_BOOT_ERROR;
|
2018-01-01 19:52:24 -08:00
|
|
|
// TODO: This is a crummy way to communicate the error...
|
2021-05-09 17:38:39 -07:00
|
|
|
PSP_CoreParameter().fileToStart.clear();
|
2018-01-01 19:52:24 -08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
return true;
|
2012-11-01 16:19:01 +01:00
|
|
|
}
|
2017-06-03 12:47:38 -07:00
|
|
|
|
|
|
|
bool Load_PSP_GE_Dump(FileLoader *fileLoader, std::string *error_string) {
|
|
|
|
BlobFileSystem *umd = new BlobFileSystem(&pspFileSystem, fileLoader, "data.ppdmp");
|
|
|
|
pspFileSystem.Mount("disc0:", umd);
|
|
|
|
|
2019-09-28 11:07:57 -07:00
|
|
|
PSPLoaders_Shutdown();
|
|
|
|
// Note: See Load_PSP_ISO for notes about this thread.
|
|
|
|
loadingThread = std::thread([] {
|
2020-12-01 00:46:26 +01:00
|
|
|
SetCurrentThreadName("ExecLoader");
|
2018-11-01 21:27:01 -07:00
|
|
|
PSP_LoadingLock guard;
|
|
|
|
if (coreState != CORE_POWERUP)
|
|
|
|
return;
|
|
|
|
|
2018-01-01 19:52:24 -08:00
|
|
|
bool success = __KernelLoadGEDump("disc0:/data.ppdmp", &PSP_CoreParameter().errorString);
|
|
|
|
if (success && coreState == CORE_POWERUP) {
|
2018-06-23 10:14:36 -07:00
|
|
|
coreState = PSP_CoreParameter().startBreak ? CORE_STEPPING : CORE_RUNNING;
|
2018-01-01 19:52:24 -08:00
|
|
|
} else {
|
2020-07-04 20:19:56 +02:00
|
|
|
coreState = CORE_BOOT_ERROR;
|
2018-01-01 19:52:24 -08:00
|
|
|
// TODO: This is a crummy way to communicate the error...
|
2021-05-09 17:38:39 -07:00
|
|
|
PSP_CoreParameter().fileToStart.clear();
|
2018-01-01 19:52:24 -08:00
|
|
|
}
|
|
|
|
});
|
2017-06-03 12:47:38 -07:00
|
|
|
return true;
|
|
|
|
}
|
2019-09-28 11:07:57 -07:00
|
|
|
|
|
|
|
void PSPLoaders_Shutdown() {
|
|
|
|
if (loadingThread.joinable())
|
|
|
|
loadingThread.join();
|
|
|
|
}
|