Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for loading game-specific plugins #13335

Merged
merged 4 commits into from
Sep 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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