Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/HLE/Plugins.cpp
5654 views
1
// Copyright (c) 2020- PPSSPP Project.
2
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.
6
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.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include <set>
19
#include <mutex>
20
21
#include "Common/Data/Format/IniFile.h"
22
#include "Common/File/FileUtil.h"
23
#include "Common/File/DirListing.h"
24
#include "Common/Serialize/SerializeFuncs.h"
25
#include "Common/System/OSD.h"
26
#include "Common/Data/Text/I18n.h"
27
#include "Common/StringUtils.h"
28
#include "Core/Config.h"
29
#include "Core/MemMap.h"
30
#include "Core/System.h"
31
#include "Core/ELF/ParamSFO.h"
32
#include "Core/HLE/Plugins.h"
33
#include "Core/HLE/sceKernelModule.h"
34
35
namespace HLEPlugins {
36
37
std::mutex g_inputMutex;
38
float PluginDataAxis[JOYSTICK_AXIS_MAX];
39
std::map<int, uint8_t> PluginDataKeys;
40
41
static bool anyEnabled = false;
42
static std::vector<std::string> prxPlugins;
43
44
static PluginInfo ReadPluginIni(const std::string &subdir, IniFile &ini) {
45
PluginInfo info;
46
47
auto options = ini.GetOrCreateSection("options");
48
std::string value;
49
50
if (options->Get("type", &value)) {
51
if (value == "prx") {
52
info.type = PluginType::PRX;
53
}
54
}
55
56
value.clear();
57
if (options->Get("filename", &value)) {
58
info.name = value;
59
info.filename = "ms0:/PSP/PLUGINS/" + subdir + "/" + value;
60
} else {
61
info.type = PluginType::INVALID;
62
}
63
64
value.clear();
65
if (options->Get("name", &value)) {
66
info.name = value;
67
}
68
69
info.version = 0;
70
info.memory = 0;
71
options->Get("version", &info.version);
72
options->Get("memory", &info.memory);
73
if (info.memory > 93) {
74
ERROR_LOG(Log::System, "Plugin memory too high, using 93 MB");
75
info.memory = 93;
76
}
77
78
if (info.version == 0) {
79
ERROR_LOG(Log::System, "Plugin without version ignored: %s", subdir.c_str());
80
info.type = PluginType::INVALID;
81
info.memory = 0;
82
} else if (info.type == PluginType::INVALID && !info.filename.empty()) {
83
ERROR_LOG(Log::System, "Plugin without valid type: %s", subdir.c_str());
84
}
85
86
return info;
87
}
88
89
std::vector<PluginInfo> FindPlugins(const std::string &gameID, const std::string &lang) {
90
std::vector<File::FileInfo> pluginDirs;
91
GetFilesInDir(GetSysDirectory(DIRECTORY_PLUGINS), &pluginDirs);
92
93
std::vector<PluginInfo> found;
94
for (const auto &subdir : pluginDirs) {
95
const Path &subdirFullName = subdir.fullName;
96
if (!subdir.isDirectory || !File::Exists(subdirFullName / "plugin.ini"))
97
continue;
98
99
IniFile ini;
100
if (!ini.Load(subdirFullName / "plugin.ini")) {
101
ERROR_LOG(Log::System, "Failed to load plugin ini: %s/plugin.ini", subdirFullName.c_str());
102
continue;
103
}
104
105
std::set<std::string> matches;
106
107
std::string gameIni;
108
109
// TODO: Should just use getsection and fail the ini if not found, I guess.
110
const Section *games = ini.GetSection("games");
111
if (games) {
112
gameIni.clear();
113
if (games->Get(gameID.c_str(), &gameIni)) {
114
if (!strcasecmp(gameIni.c_str(), "true")) {
115
matches.insert("plugin.ini");
116
} else if (!strcasecmp(gameIni.c_str(), "false")) {
117
continue;
118
} else if (!gameIni.empty()) {
119
matches.insert(gameIni);
120
}
121
}
122
gameIni.clear();
123
if (games->Get("ALL", &gameIni)) {
124
if (!strcasecmp(gameIni.c_str(), "true")) {
125
matches.insert("plugin.ini");
126
} else if (!gameIni.empty()) {
127
matches.insert(gameIni);
128
}
129
}
130
}
131
132
std::set<std::string> langMatches;
133
for (const std::string &subini : matches) {
134
if (!ini.Load(subdirFullName / subini)) {
135
ERROR_LOG(Log::System, "Failed to load plugin ini: %s/%s", subdirFullName.c_str(), subini.c_str());
136
continue;
137
}
138
139
found.push_back(ReadPluginIni(subdir.name, ini));
140
gameIni.clear();
141
if (ini.GetOrCreateSection("lang")->Get(lang.c_str(), &gameIni)) {
142
if (!gameIni.empty() && matches.find(gameIni) == matches.end()) {
143
langMatches.insert(gameIni);
144
}
145
}
146
}
147
148
for (const std::string &subini : langMatches) {
149
if (!ini.Load(subdirFullName / subini)) {
150
ERROR_LOG(Log::System, "Failed to load plugin ini: %s/%s", subdirFullName.c_str(), subini.c_str());
151
continue;
152
}
153
154
found.push_back(ReadPluginIni(subdir.name, ini));
155
}
156
}
157
158
return found;
159
}
160
161
void Init() {
162
if (!g_Config.bLoadPlugins) {
163
return;
164
}
165
166
std::vector<PluginInfo> plugins = FindPlugins(g_paramSFO.GetDiscID(), g_Config.sLanguageIni);
167
for (auto &plugin : plugins) {
168
if (plugin.memory << 20 > Memory::g_MemorySize) {
169
Memory::g_MemorySize = plugin.memory << 20;
170
anyEnabled = true;
171
}
172
173
if (plugin.type == PluginType::PRX) {
174
prxPlugins.push_back(plugin.filename);
175
anyEnabled = true;
176
}
177
}
178
}
179
180
bool Load(PSPModule *pluginWaitingModule, SceUID threadID) {
181
bool started = false;
182
183
auto sy = GetI18NCategory(I18NCat::SYSTEM);
184
185
for (const std::string &filename : prxPlugins) {
186
if (!g_Config.bEnablePlugins) {
187
WARN_LOG(Log::System, "Plugins are disabled, ignoring enabled plugin %s", filename.c_str());
188
continue;
189
}
190
191
std::string error_string = "";
192
SceUID module = KernelLoadModule(filename, &error_string);
193
if (!error_string.empty() || module < 0) {
194
ERROR_LOG(Log::System, "Unable to load plugin %s (module %d): '%s'", filename.c_str(), module, error_string.c_str());
195
continue;
196
}
197
198
int ret = __KernelStartModule(module, 0, 0, 0, nullptr, nullptr);
199
if (ret < 0) {
200
ERROR_LOG(Log::System, "Unable to start plugin %s: %08x", filename.c_str(), ret);
201
} else {
202
std::string shortName = Path(filename).GetFilename();
203
g_OSD.Show(OSDType::MESSAGE_SUCCESS, ApplySafeSubstitutions(sy->T("Loaded plugin: %1"), shortName), 6.0f);
204
started = true;
205
pluginWaitingModule->startingPlugins.push_back(module);
206
u32 error;
207
PSPModule *plugin_module = kernelObjects.Get<PSPModule>(module, error);
208
plugin_module->pluginWaitingThread = threadID;
209
}
210
211
INFO_LOG(Log::System, "Loaded plugin: %s", filename.c_str());
212
}
213
214
std::lock_guard<std::mutex> guard(g_inputMutex);
215
PluginDataKeys.clear();
216
return started;
217
}
218
219
void Unload() {
220
// Nothing to do here, for now.
221
}
222
223
void Shutdown() {
224
prxPlugins.clear();
225
anyEnabled = false;
226
std::lock_guard<std::mutex> guard(g_inputMutex);
227
PluginDataKeys.clear();
228
}
229
230
void DoState(PointerWrap &p) {
231
auto s = p.Section("Plugins", 0, 1);
232
if (!s)
233
return;
234
235
// Remember if any were enabled.
236
Do(p, anyEnabled);
237
}
238
239
bool HasEnabled() {
240
return anyEnabled;
241
}
242
243
void SetKey(int key, uint8_t value) {
244
if (anyEnabled) {
245
std::lock_guard<std::mutex> guard(g_inputMutex);
246
PluginDataKeys[key] = value;
247
}
248
}
249
250
uint8_t GetKey(int key) {
251
std::lock_guard<std::mutex> guard(g_inputMutex);
252
return PluginDataKeys[key];
253
}
254
255
} // namespace
256
257