CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/HLE/Plugins.cpp
Views: 1401
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
if (options->Get("filename", &value, "")) {
57
info.name = value;
58
info.filename = "ms0:/PSP/PLUGINS/" + subdir + "/" + value;
59
} else {
60
info.type = PluginType::INVALID;
61
}
62
63
if (options->Get("name", &value, "")) {
64
info.name = value;
65
}
66
67
options->Get("version", &info.version, 0);
68
options->Get("memory", &info.memory, 0);
69
if (info.memory > 93) {
70
ERROR_LOG(Log::System, "Plugin memory too high, using 93 MB");
71
info.memory = 93;
72
}
73
74
if (info.version == 0) {
75
ERROR_LOG(Log::System, "Plugin without version ignored: %s", subdir.c_str());
76
info.type = PluginType::INVALID;
77
info.memory = 0;
78
} else if (info.type == PluginType::INVALID && !info.filename.empty()) {
79
ERROR_LOG(Log::System, "Plugin without valid type: %s", subdir.c_str());
80
}
81
82
return info;
83
}
84
85
std::vector<PluginInfo> FindPlugins(const std::string &gameID, const std::string &lang) {
86
std::vector<File::FileInfo> pluginDirs;
87
GetFilesInDir(GetSysDirectory(DIRECTORY_PLUGINS), &pluginDirs);
88
89
std::vector<PluginInfo> found;
90
for (const auto &subdir : pluginDirs) {
91
const Path &subdirFullName = subdir.fullName;
92
if (!subdir.isDirectory || !File::Exists(subdirFullName / "plugin.ini"))
93
continue;
94
95
IniFile ini;
96
if (!ini.Load(subdirFullName / "plugin.ini")) {
97
ERROR_LOG(Log::System, "Failed to load plugin ini: %s/plugin.ini", subdirFullName.c_str());
98
continue;
99
}
100
101
std::set<std::string> matches;
102
103
std::string gameIni;
104
105
// TODO: Should just use getsection and fail the ini if not found, I guess.
106
const Section *games = ini.GetSection("games");
107
if (games) {
108
if (games->Get(gameID.c_str(), &gameIni, "")) {
109
if (!strcasecmp(gameIni.c_str(), "true")) {
110
matches.insert("plugin.ini");
111
} else if (!strcasecmp(gameIni.c_str(), "false")) {
112
continue;
113
} else if (!gameIni.empty()) {
114
matches.insert(gameIni);
115
}
116
}
117
118
if (games->Get("ALL", &gameIni, "")) {
119
if (!strcasecmp(gameIni.c_str(), "true")) {
120
matches.insert("plugin.ini");
121
} else if (!gameIni.empty()) {
122
matches.insert(gameIni);
123
}
124
}
125
}
126
127
std::set<std::string> langMatches;
128
for (const std::string &subini : matches) {
129
if (!ini.Load(subdirFullName / subini)) {
130
ERROR_LOG(Log::System, "Failed to load plugin ini: %s/%s", subdirFullName.c_str(), subini.c_str());
131
continue;
132
}
133
134
found.push_back(ReadPluginIni(subdir.name, ini));
135
136
if (ini.GetOrCreateSection("lang")->Get(lang.c_str(), &gameIni, "")) {
137
if (!gameIni.empty() && matches.find(gameIni) == matches.end()) {
138
langMatches.insert(gameIni);
139
}
140
}
141
}
142
143
for (const std::string &subini : langMatches) {
144
if (!ini.Load(subdirFullName / subini)) {
145
ERROR_LOG(Log::System, "Failed to load plugin ini: %s/%s", subdirFullName.c_str(), subini.c_str());
146
continue;
147
}
148
149
found.push_back(ReadPluginIni(subdir.name, ini));
150
}
151
}
152
153
return found;
154
}
155
156
void Init() {
157
if (!g_Config.bLoadPlugins) {
158
return;
159
}
160
161
std::vector<PluginInfo> plugins = FindPlugins(g_paramSFO.GetDiscID(), g_Config.sLanguageIni);
162
for (auto &plugin : plugins) {
163
if (plugin.memory << 20 > Memory::g_MemorySize) {
164
Memory::g_MemorySize = plugin.memory << 20;
165
anyEnabled = true;
166
}
167
168
if (plugin.type == PluginType::PRX) {
169
prxPlugins.push_back(plugin.filename);
170
anyEnabled = true;
171
}
172
}
173
}
174
175
bool Load() {
176
bool started = false;
177
178
auto sy = GetI18NCategory(I18NCat::SYSTEM);
179
180
for (const std::string &filename : prxPlugins) {
181
if (!g_Config.bEnablePlugins) {
182
WARN_LOG(Log::System, "Plugins are disabled, ignoring enabled plugin %s", filename.c_str());
183
continue;
184
}
185
186
std::string error_string = "";
187
SceUID module = KernelLoadModule(filename, &error_string);
188
if (!error_string.empty() || module < 0) {
189
ERROR_LOG(Log::System, "Unable to load plugin %s (module %d): '%s'", filename.c_str(), module, error_string.c_str());
190
continue;
191
}
192
193
int ret = KernelStartModule(module, 0, 0, 0, nullptr, nullptr);
194
if (ret < 0) {
195
ERROR_LOG(Log::System, "Unable to start plugin %s: %08x", filename.c_str(), ret);
196
} else {
197
std::string shortName = Path(filename).GetFilename();
198
g_OSD.Show(OSDType::MESSAGE_SUCCESS, ApplySafeSubstitutions(sy->T("Loaded plugin: %1"), shortName), 6.0f);
199
started = true;
200
}
201
202
INFO_LOG(Log::System, "Loaded plugin: %s", filename.c_str());
203
}
204
205
std::lock_guard<std::mutex> guard(g_inputMutex);
206
PluginDataKeys.clear();
207
return started;
208
}
209
210
void Unload() {
211
// Nothing to do here, for now.
212
}
213
214
void Shutdown() {
215
prxPlugins.clear();
216
anyEnabled = false;
217
std::lock_guard<std::mutex> guard(g_inputMutex);
218
PluginDataKeys.clear();
219
}
220
221
void DoState(PointerWrap &p) {
222
auto s = p.Section("Plugins", 0, 1);
223
if (!s)
224
return;
225
226
// Remember if any were enabled.
227
Do(p, anyEnabled);
228
}
229
230
bool HasEnabled() {
231
return anyEnabled;
232
}
233
234
void SetKey(int key, uint8_t value) {
235
if (anyEnabled) {
236
std::lock_guard<std::mutex> guard(g_inputMutex);
237
PluginDataKeys[key] = value;
238
}
239
}
240
241
uint8_t GetKey(int key) {
242
std::lock_guard<std::mutex> guard(g_inputMutex);
243
return PluginDataKeys[key];
244
}
245
246
} // namespace
247
248