diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ac71497648d..e83e590f44b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1565,6 +1565,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/HLE/KernelWaitHelpers.h Core/HLE/KUBridge.h Core/HLE/KUBridge.cpp + Core/HLE/Plugins.h + Core/HLE/Plugins.cpp Core/HLE/ThreadQueueList.h Core/HLE/__sceAudio.cpp Core/HLE/__sceAudio.h diff --git a/Core/Config.cpp b/Core/Config.cpp index 3164c9af9b5e..48e8750fd695 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -501,6 +501,7 @@ static ConfigSetting generalSettings[] = { ConfigSetting("FullscreenOnDoubleclick", &g_Config.bFullscreenOnDoubleclick, true, false, false), ReportedConfigSetting("MemStickInserted", &g_Config.bMemStickInserted, true, true, true), + ConfigSetting("LoadPlugins", &g_Config.bLoadPlugins, false, true, true), ConfigSetting(false), }; diff --git a/Core/Config.h b/Core/Config.h index 3072efdbc333..b8eb5399dd59 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -123,6 +123,7 @@ struct Config { std::string sRemoteISOSubdir; bool bRemoteDebuggerOnStartup; bool bMemStickInserted; + bool bLoadPlugins; int iScreenRotation; // The rotation angle of the PPSSPP UI. Only supported on Android and possibly other mobile platforms. int iInternalScreenRotation; // The internal screen rotation angle. Useful for vertical SHMUPs and similar. diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index d690042fb949..d7e7d3343e5b 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -384,6 +384,7 @@ + @@ -918,6 +919,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 1a63c8b4a267..53b8252fe349 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -758,6 +758,9 @@ HW + + HLE + @@ -1406,6 +1409,9 @@ Core + + HLE + diff --git a/Core/HLE/Plugins.cpp b/Core/HLE/Plugins.cpp new file mode 100644 index 000000000000..5f22e9fc37a3 --- /dev/null +++ b/Core/HLE/Plugins.cpp @@ -0,0 +1,213 @@ +// Copyright (c) 2020- 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 +// the Free Software Foundation, version 2.0 or later versions. + +// 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/. + +#include +#include "file/file_util.h" +#include "file/ini_file.h" +#include "Common/FileUtil.h" +#include "Common/Serialize/SerializeFuncs.h" +#include "Core/Config.h" +#include "Core/MemMap.h" +#include "Core/System.h" +#include "Core/ELF/ParamSFO.h" +#include "Core/HLE/Plugins.h" +#include "Core/HLE/sceKernelModule.h" + +namespace HLEPlugins { + +static bool anyEnabled = false; +static std::vector prxPlugins; + +enum class PluginType { + INVALID = 0, + PRX, +}; + +struct PluginInfo { + PluginType type; + std::string filename; + int version; + uint32_t memory; +}; + +static PluginInfo ReadPluginIni(const std::string &subdir, IniFile &ini) { + PluginInfo info; + + auto options = ini.GetOrCreateSection("options"); + std::string value; + + if (options->Get("type", &value, "")) { + if (value == "prx") { + info.type = PluginType::PRX; + } + } + + if (options->Get("filename", &value, "")) { + info.filename = "ms0:/PSP/PLUGINS/" + subdir + "/" + value; + } else { + info.type = PluginType::INVALID; + } + + options->Get("version", &info.version, 0); + options->Get("memory", &info.memory, 0); + if (info.memory > 93) { + ERROR_LOG(SYSTEM, "Plugin memory too high, using 93 MB"); + info.memory = 93; + } + + if (info.version == 0) { + ERROR_LOG(SYSTEM, "Plugin without version ignored: %s", subdir.c_str()); + info.type = PluginType::INVALID; + info.memory = 0; + } else if (info.type == PluginType::INVALID && !info.filename.empty()) { + ERROR_LOG(SYSTEM, "Plugin without valid type: %s", subdir.c_str()); + } + + return info; +} + +static std::vector FindPlugins(const std::string &gameID, const std::string &lang) { + std::vector pluginDirs; + getFilesInDir(GetSysDirectory(DIRECTORY_PLUGINS).c_str(), &pluginDirs); + + std::vector found; + for (auto subdir : pluginDirs) { + if (!subdir.isDirectory || !File::Exists(subdir.fullName + "/plugin.ini")) + continue; + + IniFile ini; + if (!ini.Load(subdir.fullName + "/plugin.ini")) { + ERROR_LOG(SYSTEM, "Failed to load plugin ini: %s/plugin.ini", subdir.fullName.c_str()); + continue; + } + + std::set matches; + + std::string gameIni; + if (ini.GetOrCreateSection("games")->Get("ALL", &gameIni, "")) { + if (!strcasecmp(gameIni.c_str(), "true")) { + matches.insert("plugin.ini"); + } else if (!gameIni.empty()) { + matches.insert(gameIni); + } + } + + if (ini.GetOrCreateSection("games")->Get(gameID.c_str(), &gameIni, "")) { + if (!strcasecmp(gameIni.c_str(), "true")) { + matches.insert("plugin.ini"); + } else if (!gameIni.empty()) { + matches.insert(gameIni); + } + } + + std::set langMatches; + for (const std::string &subini : matches) { + if (!ini.Load(subdir.fullName + "/" + subini)) { + ERROR_LOG(SYSTEM, "Failed to load plugin ini: %s/%s", subdir.fullName.c_str(), subini.c_str()); + continue; + } + + found.push_back(ReadPluginIni(subdir.name, ini)); + + if (ini.GetOrCreateSection("lang")->Get(lang.c_str(), &gameIni, "")) { + if (!gameIni.empty() && matches.find(gameIni) == matches.end()) { + langMatches.insert(gameIni); + } + } + } + + for (const std::string &subini : langMatches) { + if (!ini.Load(subdir.fullName + "/" + subini)) { + ERROR_LOG(SYSTEM, "Failed to load plugin ini: %s/%s", subdir.fullName.c_str(), subini.c_str()); + continue; + } + + found.push_back(ReadPluginIni(subdir.name, ini)); + } + } + + return found; +} + +void Init() { + if (!g_Config.bLoadPlugins) { + return; + } + + std::vector plugins = FindPlugins(g_paramSFO.GetDiscID(), g_Config.sLanguageIni); + for (auto &plugin : plugins) { + if (plugin.memory << 20 > Memory::g_MemorySize) { + Memory::g_MemorySize = plugin.memory << 20; + anyEnabled = true; + } + + if (plugin.type == PluginType::PRX) { + prxPlugins.push_back(plugin.filename); + anyEnabled = true; + } + } +} + +bool Load() { + bool started = false; + for (const std::string &filename : prxPlugins) { + std::string error_string = ""; + SceUID module = KernelLoadModule(filename, &error_string); + if (!error_string.empty()) { + ERROR_LOG(SYSTEM, "Unable to load plugin %s: %s", filename.c_str(), error_string.c_str()); + continue; + } + if (module < 0) { + ERROR_LOG(SYSTEM, "Unable to load plugin %s: %08x", filename.c_str(), module); + continue; + } + + int ret = KernelStartModule(module, 0, 0, 0, nullptr, nullptr); + if (ret < 0) { + ERROR_LOG(SYSTEM, "Unable to start plugin %s: %08x", filename.c_str(), ret); + } + + INFO_LOG(SYSTEM, "Loaded plugin: %s", filename.c_str()); + started = true; + } + + return started; +} + +void Unload() { + // Nothing to do here, for now. +} + +void Shutdown() { + prxPlugins.clear(); + anyEnabled = false; +} + +void DoState(PointerWrap &p) { + auto s = p.Section("Plugins", 0, 1); + if (!s) + return; + + // Remember if any were enabled. + Do(p, anyEnabled); +} + +bool HasEnabled() { + return anyEnabled; +} + +}; diff --git a/Core/HLE/Plugins.h b/Core/HLE/Plugins.h new file mode 100644 index 000000000000..6db2966d9867 --- /dev/null +++ b/Core/HLE/Plugins.h @@ -0,0 +1,34 @@ +// Copyright (c) 2020- 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 +// the Free Software Foundation, version 2.0 or later versions. + +// 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/. + +#pragma once + +class PointerWrap; + +namespace HLEPlugins { + +void Init(); +void Shutdown(); + +bool Load(); +void Unload(); + +void DoState(PointerWrap &p); + +bool HasEnabled(); + +}; diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 0b2b55ae74d1..e31e0f93e329 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -32,6 +32,7 @@ #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" #include "Core/HLE/HLETables.h" +#include "Core/HLE/Plugins.h" #include "Core/HLE/ReplaceTables.h" #include "Core/Reporting.h" #include "Core/Host.h" @@ -331,6 +332,8 @@ class PSPModule : public KernelObject { } } + HLEPlugins::DoState(p); + RebuildImpExpModuleNames(); } @@ -529,6 +532,7 @@ void __KernelModuleShutdown() { loadedModules.clear(); MIPSAnalyst::Reset(); + HLEPlugins::Unload(); } // Sometimes there are multiple LO16's or HI16's per pair, even though the ABI says nothing of this. @@ -1541,6 +1545,27 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load return module; } +SceUID KernelLoadModule(const std::string &filename, std::string *error_string) { + PSPFileInfo info = pspFileSystem.GetFileInfo(filename); + if (!info.exists) + return SCE_KERNEL_ERROR_NOFILE; + + std::vector buffer; + buffer.resize((size_t)info.size); + + u32 handle = pspFileSystem.OpenFile(filename, FILEACCESS_READ); + pspFileSystem.ReadFile(handle, &buffer[0], info.size); + pspFileSystem.CloseFile(handle); + + u32 error = SCE_KERNEL_ERROR_ILLEGAL_OBJECT; + u32 magic; + PSPModule *module = __KernelLoadELFFromPtr(&buffer[0], buffer.size(), 0, false, error_string, &magic, error); + + if (module == nullptr) + return error; + return module->GetUID(); +} + static PSPModule *__KernelLoadModule(u8 *fileptr, size_t fileSize, SceKernelLMOption *options, std::string *error_string) { PSPModule *module = nullptr; // Check for PBP @@ -1600,6 +1625,11 @@ static void __KernelStartModule(PSPModule *m, int args, const char *argp, SceKer SceUID threadID = __KernelSetupRootThread(m->GetUID(), args, argp, options->priority, options->stacksize, options->attribute); __KernelSetThreadRA(threadID, NID_MODULERETURN); + + if (HLEPlugins::Load()) { + KernelRotateThreadReadyQueue(0); + __KernelReSchedule("Started plugins"); + } } @@ -1960,12 +1990,71 @@ static u32 sceKernelLoadModuleNpDrm(const char *name, u32 flags, u32 optionAddr) return sceKernelLoadModule(name, flags, optionAddr); } +int KernelStartModule(SceUID moduleId, u32 argsize, u32 argAddr, u32 returnValueAddr, SceKernelSMOption *smoption, bool *needsWait) { + if (needsWait) { + *needsWait = false; + } + + u32 error; + PSPModule *module = kernelObjects.Get(moduleId, error); + if (!module) { + return error; + } + + u32 priority = 0x20; + u32 stacksize = 0x40000; + int attribute = module->nm.attribute; + u32 entryAddr = module->nm.entry_addr; + + if (module->nm.module_start_func != 0 && module->nm.module_start_func != (u32)-1) { + entryAddr = module->nm.module_start_func; + if (module->nm.module_start_thread_attr != 0) + attribute = module->nm.module_start_thread_attr; + } else if (entryAddr == (u32)-1 || entryAddr == module->memoryBlockAddr - 1) { + if (smoption) { + // TODO: Does sceKernelStartModule() really give an error when no entry only if you pass options? + attribute = smoption->attribute; + } else { + // TODO: Why are we just returning the module ID in this case? + WARN_LOG(SCEMODULE, "sceKernelStartModule(): module has no start or entry func"); + module->nm.status = MODULE_STATUS_STARTED; + return moduleId; + } + } + + if (Memory::IsValidAddress(entryAddr)) { + if (smoption && smoption->priority > 0) { + priority = smoption->priority; + } else if (module->nm.module_start_thread_priority > 0) { + priority = module->nm.module_start_thread_priority; + } + + if (smoption && smoption->stacksize > 0) { + stacksize = smoption->stacksize; + } else if (module->nm.module_start_thread_stacksize > 0) { + stacksize = module->nm.module_start_thread_stacksize; + } + + SceUID threadID = __KernelCreateThread(module->nm.name, moduleId, entryAddr, priority, stacksize, attribute, 0, (module->nm.attribute & 0x1000) != 0); + __KernelStartThreadValidate(threadID, argsize, argAddr); + __KernelSetThreadRA(threadID, NID_MODULERETURN); + + if (needsWait) { + *needsWait = true; + } + } else if (entryAddr == 0) { + INFO_LOG(SCEMODULE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x): no entry address", moduleId, argsize, argAddr, returnValueAddr); + module->nm.status = MODULE_STATUS_STARTED; + } else { + ERROR_LOG(SCEMODULE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x): invalid entry address", moduleId, argsize, argAddr, returnValueAddr); + return -1; + } + + return moduleId; +} + static void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValueAddr, u32 optionAddr) { - u32 priority = 0x20; - u32 stacksize = 0x40000; - u32 attr = 0; - int stackPartition = 0; SceKernelSMOption smoption = {0}; if (optionAddr) { Memory::ReadStruct(optionAddr, &smoption); @@ -1994,71 +2083,19 @@ static void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 ret INFO_LOG(SCEMODULE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x)", moduleId,argsize,argAddr,returnValueAddr,optionAddr); - int attribute = module->nm.attribute; - u32 entryAddr = module->nm.entry_addr; - - if (module->nm.module_start_func != 0 && module->nm.module_start_func != (u32)-1) - { - entryAddr = module->nm.module_start_func; - if (module->nm.module_start_thread_attr != 0) - attribute = module->nm.module_start_thread_attr; - } - else if ((entryAddr == (u32)-1) || entryAddr == module->memoryBlockAddr - 1) - { - if (optionAddr) - { - // TODO: Does sceKernelStartModule() really give an error when no entry only if you pass options? - attribute = smoption.attribute; - } - else - { - // TODO: Why are we just returning the module ID in this case? - WARN_LOG(SCEMODULE, "sceKernelStartModule(): module has no start or entry func"); - module->nm.status = MODULE_STATUS_STARTED; - RETURN(moduleId); - return; - } - } - - if (Memory::IsValidAddress(entryAddr)) - { - if ((optionAddr) && smoption.priority > 0) { - priority = smoption.priority; - } else if (module->nm.module_start_thread_priority > 0) { - priority = module->nm.module_start_thread_priority; - } - - if ((optionAddr) && (smoption.stacksize > 0)) { - stacksize = smoption.stacksize; - } else if (module->nm.module_start_thread_stacksize > 0) { - stacksize = module->nm.module_start_thread_stacksize; - } + bool needsWait; + int ret = KernelStartModule(moduleId, argsize, argAddr, returnValueAddr, optionAddr ? &smoption : nullptr, &needsWait); - SceUID threadID = __KernelCreateThread(module->nm.name, moduleId, entryAddr, priority, stacksize, attribute, 0, (module->nm.attribute & 0x1000) != 0); - __KernelStartThreadValidate(threadID, argsize, argAddr); - __KernelSetThreadRA(threadID, NID_MODULERETURN); + if (needsWait) { __KernelWaitCurThread(WAITTYPE_MODULE, moduleId, 1, 0, false, "started module"); const ModuleWaitingThread mwt = {__KernelGetCurThread(), returnValueAddr}; module->nm.status = MODULE_STATUS_STARTING; module->waitingThreads.push_back(mwt); } - else if (entryAddr == 0) - { - INFO_LOG(SCEMODULE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x): no entry address", - moduleId,argsize,argAddr,returnValueAddr,optionAddr); - module->nm.status = MODULE_STATUS_STARTED; - } - else - { - ERROR_LOG(SCEMODULE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x): invalid entry address", - moduleId,argsize,argAddr,returnValueAddr,optionAddr); - RETURN(-1); - return; - } - } - RETURN(moduleId); + RETURN(ret); + } } static u32 sceKernelStopModule(u32 moduleId, u32 argSize, u32 argAddr, u32 returnValueAddr, u32 optionAddr) diff --git a/Core/HLE/sceKernelModule.h b/Core/HLE/sceKernelModule.h index 99e5473b056d..a22715347776 100644 --- a/Core/HLE/sceKernelModule.h +++ b/Core/HLE/sceKernelModule.h @@ -33,6 +33,7 @@ struct PspModuleInfo { }; class PointerWrap; +struct SceKernelSMOption; KernelObject *__KernelModuleObject(); void __KernelModuleDoState(PointerWrap &p); @@ -44,6 +45,8 @@ bool __KernelLoadGEDump(const std::string &base_filename, std::string *error_str bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_string); void __KernelGPUReplay(); void __KernelReturnFromModuleFunc(); +SceUID KernelLoadModule(const std::string &filename, std::string *error_string); +int KernelStartModule(SceUID moduleId, u32 argsize, u32 argAddr, u32 returnValueAddr, SceKernelSMOption *smoption, bool *needsWait); u32 hleKernelStopUnloadSelfModuleWithOrWithoutStatus(u32 exitCode, u32 argSize, u32 argp, u32 statusAddr, u32 optionAddr, bool WithStatus); u32 sceKernelFindModuleByUID(u32 uid); diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index cade794e0808..6bd3d2d43414 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -2246,10 +2246,7 @@ bool __KernelIsDispatchEnabled() return dispatchEnabled && __InterruptsEnabled(); } -int sceKernelRotateThreadReadyQueue(int priority) -{ - VERBOSE_LOG(SCEKERNEL, "sceKernelRotateThreadReadyQueue(%x)", priority); - +int KernelRotateThreadReadyQueue(int priority) { PSPThread *cur = __GetCurrentThread(); // 0 is special, it means "my current priority." @@ -2259,11 +2256,9 @@ int sceKernelRotateThreadReadyQueue(int priority) if (priority <= 0x07 || priority > 0x77) return SCE_KERNEL_ERROR_ILLEGAL_PRIORITY; - if (!threadReadyQueue.empty(priority)) - { + if (!threadReadyQueue.empty(priority)) { // In other words, yield to everyone else. - if (cur->nt.currentPriority == priority) - { + if (cur->nt.currentPriority == priority) { threadReadyQueue.push_back(priority, currentThread); cur->nt.status = (cur->nt.status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; } @@ -2272,11 +2267,18 @@ int sceKernelRotateThreadReadyQueue(int priority) threadReadyQueue.rotate(priority); } - hleReSchedule("rotatethreadreadyqueue"); - hleEatCycles(250); return 0; } +int sceKernelRotateThreadReadyQueue(int priority) { + int result = KernelRotateThreadReadyQueue(priority); + if (result == 0) { + hleReSchedule("rotatethreadreadyqueue"); + hleEatCycles(250); + } + return hleLogSuccessVerboseI(SCEKERNEL, result); +} + int sceKernelDeleteThread(int threadID) { if (threadID == 0 || threadID == currentThread) { ERROR_LOG(SCEKERNEL, "sceKernelDeleteThread(%i): cannot delete current thread", threadID); diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index bb08bb3b6687..65dce5ff54f4 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -57,6 +57,7 @@ u32 sceKernelReferThreadRunStatus(u32 uid, u32 statusPtr); int sceKernelReleaseWaitThread(SceUID threadID); int sceKernelChangeCurrentThreadAttr(u32 clearAttr, u32 setAttr); int sceKernelRotateThreadReadyQueue(int priority); +int KernelRotateThreadReadyQueue(int priority); int sceKernelCheckThreadStack(); int sceKernelSuspendThread(SceUID threadID); int sceKernelResumeThread(SceUID threadID); diff --git a/Core/Reporting.cpp b/Core/Reporting.cpp index 28c9609b01b7..1829fd8a9725 100644 --- a/Core/Reporting.cpp +++ b/Core/Reporting.cpp @@ -34,6 +34,7 @@ #include "Core/System.h" #include "Core/FileSystems/BlockDevices.h" #include "Core/FileSystems/MetaFileSystem.h" +#include "Core/HLE/Plugins.h" #include "Core/HLE/sceDisplay.h" #include "Core/HLE/sceKernelMemory.h" #include "Core/ELF/ParamSFO.h" @@ -497,7 +498,7 @@ namespace Reporting bool IsSupported() { // Disabled when using certain hacks, because they make for poor reports. - if (CheatsInEffect()) + if (CheatsInEffect() || HLEPlugins::HasEnabled()) return false; if (g_Config.iLockedCPUSpeed != 0) return false; diff --git a/Core/System.cpp b/Core/System.cpp index b3b1e30756d3..ebf3eaaaa215 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -48,6 +48,7 @@ #include "Core/Host.h" #include "Core/System.h" #include "Core/HLE/HLE.h" +#include "Core/HLE/Plugins.h" #include "Core/HLE/ReplaceTables.h" #include "Core/HLE/sceKernel.h" #include "Core/HLE/sceKernelMemory.h" @@ -286,6 +287,7 @@ bool CPU_Init() { // likely to collide with any commercial ones. coreParameter.compat.Load(discID); + HLEPlugins::Init(); if (!Memory::Init()) { // We're screwed. return false; @@ -351,6 +353,7 @@ void CPU_Shutdown() { pspFileSystem.Shutdown(); mipsr4k.Shutdown(); Memory::Shutdown(); + HLEPlugins::Shutdown(); delete loadedFile; loadedFile = nullptr; @@ -592,6 +595,8 @@ std::string GetSysDirectory(PSPDirectories directoryType) { return g_Config.memStickDirectory + "PSP/SYSTEM/CACHE/"; case DIRECTORY_TEXTURES: return g_Config.memStickDirectory + "PSP/TEXTURES/"; + case DIRECTORY_PLUGINS: + return g_Config.memStickDirectory + "PSP/PLUGINS/"; case DIRECTORY_APP_CACHE: if (!g_Config.appCacheDirectory.empty()) { return g_Config.appCacheDirectory; diff --git a/Core/System.h b/Core/System.h index 0d21b0d1bb05..fac6b1676712 100644 --- a/Core/System.h +++ b/Core/System.h @@ -46,6 +46,7 @@ enum PSPDirectories { DIRECTORY_SAVESTATE, DIRECTORY_CACHE, DIRECTORY_TEXTURES, + DIRECTORY_PLUGINS, DIRECTORY_APP_CACHE, // Use the OS app cache if available DIRECTORY_VIDEO, DIRECTORY_AUDIO diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 047111665019..ffd0051677d8 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -361,6 +361,7 @@ void CreateDirectoriesAndroid() { File::CreateFullPath(GetSysDirectory(DIRECTORY_GAME)); File::CreateFullPath(GetSysDirectory(DIRECTORY_SYSTEM)); File::CreateFullPath(GetSysDirectory(DIRECTORY_TEXTURES)); + File::CreateFullPath(GetSysDirectory(DIRECTORY_PLUGINS)); // Avoid media scanners in PPSSPP_STATE and SAVEDATA directories, // and in the root PSP directory as well. @@ -368,6 +369,7 @@ void CreateDirectoriesAndroid() { File::CreateEmptyFile(GetSysDirectory(DIRECTORY_SAVEDATA) + ".nomedia"); File::CreateEmptyFile(GetSysDirectory(DIRECTORY_SYSTEM) + ".nomedia"); File::CreateEmptyFile(GetSysDirectory(DIRECTORY_TEXTURES) + ".nomedia"); + File::CreateEmptyFile(GetSysDirectory(DIRECTORY_PLUGINS) + ".nomedia"); } static void CheckFailedGPUBackends() { diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index 31f2327547d7..7090fca69587 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -561,6 +561,7 @@ + @@ -804,6 +805,7 @@ + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index 6538bd1df148..1082a1416050 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -89,6 +89,7 @@ + @@ -738,6 +739,7 @@ + diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 36a29682f0c3..3e2c6367c0f6 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -346,6 +346,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/HLE/ReplaceTables.cpp \ $(SRC)/Core/HLE/HLE.cpp \ $(SRC)/Core/HLE/KUBridge.cpp \ + $(SRC)/Core/HLE/Plugins.cpp \ $(SRC)/Core/HLE/sceAdler.cpp \ $(SRC)/Core/HLE/sceAtrac.cpp \ $(SRC)/Core/HLE/__sceAudio.cpp.arm \ diff --git a/libretro/Makefile.common b/libretro/Makefile.common index d92fd5446828..bf327b35a318 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -368,6 +368,7 @@ SOURCES_CXX += $(NATIVEDIR)/math/dataconv.cpp \ $(COREDIR)/Font/PGF.cpp \ $(COREDIR)/HLE/HLE.cpp \ $(COREDIR)/HLE/KUBridge.cpp \ + $(COREDIR)/HLE/Plugins.cpp \ $(COREDIR)/HLE/sceSha256.cpp \ $(COREDIR)/HLE/sceG729.cpp \ $(COREDIR)/HLE/sceSfmt19937.cpp \