Skip to content

Commit

Permalink
Merge pull request #13335 from unknownbrackets/plugins2
Browse files Browse the repository at this point in the history
Support for loading game-specific plugins
  • Loading branch information
hrydgard authored Sep 8, 2020
2 parents 40272ff + ac7522b commit cf43f2d
Show file tree
Hide file tree
Showing 19 changed files with 389 additions and 72 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions Core/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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),
};
Expand Down
1 change: 1 addition & 0 deletions Core/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions Core/Core.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@
<ClCompile Include="Debugger\WebSocket\WebSocketUtils.cpp" />
<ClCompile Include="FileSystems\BlobFileSystem.cpp" />
<ClCompile Include="HLE\KUBridge.cpp" />
<ClCompile Include="HLE\Plugins.cpp" />
<ClCompile Include="HLE\sceKernelHeap.cpp" />
<ClCompile Include="HLE\sceUsbAcc.cpp" />
<ClCompile Include="HLE\sceUsbCam.cpp" />
Expand Down Expand Up @@ -918,6 +919,7 @@
<ClInclude Include="FileSystems\BlobFileSystem.h" />
<ClInclude Include="HLE\KernelThreadDebugInterface.h" />
<ClInclude Include="HLE\KUBridge.h" />
<ClInclude Include="HLE\Plugins.h" />
<ClInclude Include="HLE\sceKernelHeap.h" />
<ClInclude Include="HLE\sceUsbAcc.h" />
<ClInclude Include="HLE\sceUsbCam.h" />
Expand Down
6 changes: 6 additions & 0 deletions Core/Core.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,9 @@
<ClCompile Include="HW\BufferQueue.cpp">
<Filter>HW</Filter>
</ClCompile>
<ClCompile Include="HLE\Plugins.cpp">
<Filter>HLE</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELF\ElfReader.h">
Expand Down Expand Up @@ -1406,6 +1409,9 @@
<ClInclude Include="Instance.h">
<Filter>Core</Filter>
</ClInclude>
<ClInclude Include="HLE\Plugins.h">
<Filter>HLE</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />
Expand Down
213 changes: 213 additions & 0 deletions Core/HLE/Plugins.cpp
Original file line number Diff line number Diff line change
@@ -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 <set>
#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<std::string> 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<PluginInfo> FindPlugins(const std::string &gameID, const std::string &lang) {
std::vector<FileInfo> pluginDirs;
getFilesInDir(GetSysDirectory(DIRECTORY_PLUGINS).c_str(), &pluginDirs);

std::vector<PluginInfo> 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<std::string> 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<std::string> 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<PluginInfo> 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;
}

};
34 changes: 34 additions & 0 deletions Core/HLE/Plugins.h
Original file line number Diff line number Diff line change
@@ -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();

};
Loading

0 comments on commit cf43f2d

Please sign in to comment.