1 // Copyright (C) 2012 PPSSPP Project
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
19 #include <sys/param.h>
22 #include "file/file_util.h"
24 #include "Common/StringUtils.h"
26 #include "Core/ELF/ElfReader.h"
27 #include "Core/ELF/ParamSFO.h"
29 #include "FileSystems/BlockDevices.h"
30 #include "FileSystems/DirectoryFileSystem.h"
31 #include "FileSystems/ISOFileSystem.h"
32 #include "FileSystems/MetaFileSystem.h"
33 #include "FileSystems/VirtualDiscFileSystem.h"
35 #include "Core/Loaders.h"
36 #include "Core/MemMap.h"
37 #include "Core/HDRemaster.h"
39 #include "Core/MIPS/MIPS.h"
40 #include "Core/MIPS/MIPSAnalyst.h"
41 #include "Core/MIPS/MIPSCodeUtils.h"
45 #include "Core/Config.h"
46 #include "Core/System.h"
47 #include "Core/PSPLoaders.h"
48 #include "Core/HLE/HLE.h"
49 #include "Core/HLE/sceKernel.h"
50 #include "Core/HLE/sceKernelThread.h"
51 #include "Core/HLE/sceKernelModule.h"
52 #include "Core/HLE/sceKernelMemory.h"
54 // We gather the game info before actually loading/booting the ISO
55 // to determine if the emulator should enable extra memory and
56 // double-sized texture coordinates.
57 void InitMemoryForGameISO(FileLoader
*fileLoader
) {
60 if (!fileLoader
->Exists()) {
64 bool actualIso
= false;
65 if (fileLoader
->IsDirectory())
67 umd2
= new VirtualDiscFileSystem(&pspFileSystem
, fileLoader
->Path());
71 auto bd
= constructBlockDevice(fileLoader
);
72 // Can't init anything without a block device...
77 if (g_Config
.bCacheFullIsoInRam
) {
78 // The constructor destroys the original block device object after reading it.
79 bd
= new RAMBlockDevice(bd
);
83 umd2
= new ISOFileSystem(&pspFileSystem
, bd
);
89 //pspFileSystem.Mount("host0:",umd2);
91 IFileSystem
*entireIso
= 0;
93 entireIso
= new ISOBlockSystem(static_cast<ISOFileSystem
*>(umd2
));
98 pspFileSystem
.Mount("umd0:", entireIso
);
99 pspFileSystem
.Mount("umd1:", entireIso
);
100 pspFileSystem
.Mount("disc0:", umd2
);
101 pspFileSystem
.Mount("umd:", entireIso
);
105 std::string
sfoPath("disc0:/PSP_GAME/PARAM.SFO");
106 PSPFileInfo fileInfo
= pspFileSystem
.GetFileInfo(sfoPath
.c_str());
110 std::vector
<u8
> paramsfo
;
111 pspFileSystem
.ReadEntireFile(sfoPath
, paramsfo
);
112 if (g_paramSFO
.ReadSFO(paramsfo
))
114 gameID
= g_paramSFO
.GetValueString("DISC_ID");
116 for (size_t i
= 0; i
< ARRAY_SIZE(g_HDRemasters
); i
++) {
117 if(g_HDRemasters
[i
].gameID
== gameID
) {
118 g_RemasterMode
= true;
119 Memory::g_MemorySize
= g_HDRemasters
[i
].MemorySize
;
120 if(g_HDRemasters
[i
].DoubleTextureCoordinates
)
121 g_DoubleTextureCoordinates
= true;
125 DEBUG_LOG(LOADER
, "HDRemaster mode is %s", g_RemasterMode
? "true": "false");
131 // Chinese translators like to rename EBOOT.BIN and replace it with some kind of stub
132 // that probably loads a plugin and then launches the actual game. These stubs don't work in PPSSPP.
133 // No idea why they are doing this, but it works to just bypass it. They could stop
134 // inventing new filenames though...
135 static const char *altBootNames
[] = {
136 "disc0:/PSP_GAME/SYSDIR/EBOOT.OLD",
137 "disc0:/PSP_GAME/SYSDIR/EBOOT.DAT",
138 "disc0:/PSP_GAME/SYSDIR/EBOOT.BI",
139 "disc0:/PSP_GAME/SYSDIR/EBOOT.LLD",
140 //"disc0:/PSP_GAME/SYSDIR/OLD_EBOOT.BIN", //Utawareru Mono Chinese version
141 "disc0:/PSP_GAME/SYSDIR/EBOOT.123",
142 "disc0:/PSP_GAME/SYSDIR/EBOOT_LRC_CH.BIN",
143 "disc0:/PSP_GAME/SYSDIR/BOOT0.OLD",
144 "disc0:/PSP_GAME/SYSDIR/BOOT1.OLD",
145 "disc0:/PSP_GAME/SYSDIR/BINOT.BIN",
146 "disc0:/PSP_GAME/SYSDIR/EBOOT.FRY",
147 "disc0:/PSP_GAME/SYSDIR/EBOOT.Z.Y",
148 "disc0:/PSP_GAME/SYSDIR/EBOOT.LEI",
149 "disc0:/PSP_GAME/SYSDIR/EBOOT.DNR",
150 "disc0:/PSP_GAME/SYSDIR/DBZ2.BIN",
151 "disc0:/PSP_GAME/SYSDIR/ss.RAW",
154 bool Load_PSP_ISO(FileLoader
*fileLoader
, std::string
*error_string
)
156 // Mounting stuff relocated to InitMemoryForGameISO due to HD Remaster restructuring of code.
158 std::string
sfoPath("disc0:/PSP_GAME/PARAM.SFO");
159 PSPFileInfo fileInfo
= pspFileSystem
.GetFileInfo(sfoPath
.c_str());
162 std::vector
<u8
> paramsfo
;
163 pspFileSystem
.ReadEntireFile(sfoPath
, paramsfo
);
164 if (g_paramSFO
.ReadSFO(paramsfo
))
167 sprintf(title
, "%s : %s", g_paramSFO
.GetValueString("DISC_ID").c_str(), g_paramSFO
.GetValueString("TITLE").c_str());
168 INFO_LOG(LOADER
, "%s", title
);
169 host
->SetWindowTitle(title
);
174 std::string
bootpath("disc0:/PSP_GAME/SYSDIR/EBOOT.BIN");
176 // Bypass Chinese translation patches, see comment above.
177 for (size_t i
= 0; i
< ARRAY_SIZE(altBootNames
); i
++) {
178 if (pspFileSystem
.GetFileInfo(altBootNames
[i
]).exists
) {
179 bootpath
= altBootNames
[i
];
183 // Bypass another more dangerous one where the file is in USRDIR - this could collide with files in some game.
184 std::string id
= g_paramSFO
.GetValueString("DISC_ID");
185 if (id
== "NPJH50624" && pspFileSystem
.GetFileInfo("disc0:/PSP_GAME/USRDIR/PAKFILE2.BIN").exists
) {
186 bootpath
= "disc0:/PSP_GAME/USRDIR/PAKFILE2.BIN";
188 if (id
== "NPJH00100" && pspFileSystem
.GetFileInfo("disc0:/PSP_GAME/USRDIR/DATA/GIM/GBL").exists
) {
189 bootpath
= "disc0:/PSP_GAME/USRDIR/DATA/GIM/GBL";
192 bool hasEncrypted
= false;
194 if ((fd
= pspFileSystem
.OpenFile(bootpath
, FILEACCESS_READ
)) != 0)
197 pspFileSystem
.ReadFile(fd
, head
, 4);
198 if (memcmp(head
, "~PSP", 4) == 0 || memcmp(head
, "\x7F""ELF", 4) == 0) {
201 pspFileSystem
.CloseFile(fd
);
205 // try unencrypted BOOT.BIN
206 bootpath
= "disc0:/PSP_GAME/SYSDIR/BOOT.BIN";
208 //in case we didn't go through EmuScreen::boot
209 g_Config
.loadGameConfig(id
);
210 INFO_LOG(LOADER
,"Loading %s...", bootpath
.c_str());
211 return __KernelLoadExec(bootpath
.c_str(), 0, error_string
);
214 #if defined(_WIN32) && defined(__MINGW32__)
215 #include "realpath.c"
218 static std::string
NormalizePath(const std::string
&path
)
220 #if defined(_WIN32) && !defined(__MINGW32__)
222 if (GetFullPathNameA(path
.c_str(), sizeof(buf
) - 1, buf
, NULL
) == 0)
225 char buf
[PATH_MAX
+ 1];
226 if (realpath(path
.c_str(), buf
) == NULL
)
232 bool Load_PSP_ELF_PBP(FileLoader
*fileLoader
, std::string
*error_string
)
234 // This is really just for headless, might need tweaking later.
235 if (PSP_CoreParameter().mountIsoLoader
!= nullptr)
237 auto bd
= constructBlockDevice(PSP_CoreParameter().mountIsoLoader
);
239 ISOFileSystem
*umd2
= new ISOFileSystem(&pspFileSystem
, bd
);
241 pspFileSystem
.Mount("umd1:", umd2
);
242 pspFileSystem
.Mount("disc0:", umd2
);
243 pspFileSystem
.Mount("umd:", umd2
);
247 std::string full_path
= fileLoader
->Path();
248 std::string path
, file
, extension
;
249 SplitPath(ReplaceAll(full_path
, "\\", "/"), &path
, &file
, &extension
);
251 path
= ReplaceAll(path
, "/", "\\");
254 if (!PSP_CoreParameter().mountRoot
.empty())
256 // We don't want to worry about .. and cwd and such.
257 const std::string rootNorm
= NormalizePath(PSP_CoreParameter().mountRoot
+ "/");
258 const std::string pathNorm
= NormalizePath(path
+ "/");
260 // If root is not a subpath of path, we can't boot the game.
261 if (!startsWith(pathNorm
, rootNorm
))
263 *error_string
= "Cannot boot ELF located outside mountRoot.";
267 const std::string filepath
= ReplaceAll(pathNorm
.substr(rootNorm
.size()), "\\", "/");
268 file
= filepath
+ "/" + file
;
269 path
= rootNorm
+ "/";
270 pspFileSystem
.SetStartingDirectory(filepath
);
273 DirectoryFileSystem
*fs
= new DirectoryFileSystem(&pspFileSystem
, path
);
274 pspFileSystem
.Mount("umd0:", fs
);
276 std::string finalName
= "umd0:/" + file
+ extension
;
277 return __KernelLoadExec(finalName
.c_str(), 0, error_string
);