Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/UI/MiscScreens.cpp
5670 views
1
// Copyright (c) 2013- 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 "ppsspp_config.h"
19
20
#include <algorithm>
21
#include <cmath>
22
#include <functional>
23
24
#include "Common/Render/DrawBuffer.h"
25
#include "Common/UI/Context.h"
26
#include "Common/UI/View.h"
27
#include "Common/UI/ViewGroup.h"
28
#include "Common/UI/UI.h"
29
30
#include "Common/System/Display.h"
31
#include "Common/System/NativeApp.h"
32
#include "Common/System/System.h"
33
#include "Common/System/Request.h"
34
#include "Common/Math/curves.h"
35
#include "Common/File/VFS/VFS.h"
36
37
#include "Common/Data/Color/RGBAUtil.h"
38
#include "Common/Data/Encoding/Utf8.h"
39
#include "Common/Data/Text/I18n.h"
40
#include "Common/TimeUtil.h"
41
#include "Common/File/FileUtil.h"
42
#include "Common/Render/ManagedTexture.h"
43
#include "Common/StringUtils.h"
44
45
#include "Core/Config.h"
46
#include "Core/System.h"
47
#include "Core/MIPS/JitCommon/JitCommon.h"
48
#include "Core/HLE/sceUtility.h"
49
#include "Core/Util/RecentFiles.h"
50
#include "GPU/GPUState.h"
51
#include "GPU/Common/PostShader.h"
52
53
#include "UI/ControlMappingScreen.h"
54
#include "UI/Background.h"
55
#include "UI/DisplayLayoutScreen.h"
56
#include "UI/EmuScreen.h"
57
#include "UI/GameSettingsScreen.h"
58
#include "UI/MainScreen.h"
59
#include "UI/MiscScreens.h"
60
#include "UI/MemStickScreen.h"
61
#include "UI/MiscViews.h"
62
63
void HandleCommonMessages(UIMessage message, const char *value, ScreenManager *manager, const Screen *activeScreen) {
64
bool isActiveScreen = manager->topScreen() == activeScreen;
65
66
if (message == UIMessage::REQUEST_CLEAR_JIT && PSP_IsInited()) {
67
// TODO: This seems to clearly be the wrong place to handle this.
68
if (MIPSComp::jit) {
69
std::lock_guard<std::recursive_mutex> guard(MIPSComp::jitLock);
70
if (MIPSComp::jit)
71
MIPSComp::jit->ClearCache();
72
}
73
currentMIPS->UpdateCore((CPUCore)g_Config.iCpuCore);
74
} else if (message == UIMessage::SHOW_CONTROL_MAPPING && isActiveScreen && std::string(activeScreen->tag()) != "ControlMapping") {
75
UpdateUIState(UISTATE_MENU);
76
manager->push(new ControlMappingScreen(Path()));
77
} else if (message == UIMessage::SHOW_DISPLAY_LAYOUT_EDITOR && isActiveScreen && std::string(activeScreen->tag()) != "DisplayLayout") {
78
UpdateUIState(UISTATE_MENU);
79
manager->push(new DisplayLayoutScreen(Path()));
80
} else if (message == UIMessage::SHOW_SETTINGS && isActiveScreen && std::string(activeScreen->tag()) != "GameSettings") {
81
UpdateUIState(UISTATE_MENU);
82
manager->push(new GameSettingsScreen(Path()));
83
} else if (message == UIMessage::SHOW_LANGUAGE_SCREEN && isActiveScreen) {
84
auto sy = GetI18NCategory(I18NCat::SYSTEM);
85
auto langScreen = new NewLanguageScreen(sy->T("Language"));
86
langScreen->OnChoice.Add([](UI::EventParams &) {
87
System_PostUIMessage(UIMessage::RECREATE_VIEWS);
88
System_Notify(SystemNotification::UI);
89
});
90
manager->push(langScreen);
91
} else if (message == UIMessage::WINDOW_MINIMIZED) {
92
if (!strcmp(value, "true")) {
93
gstate_c.skipDrawReason |= SKIPDRAW_WINDOW_MINIMIZED;
94
} else {
95
gstate_c.skipDrawReason &= ~SKIPDRAW_WINDOW_MINIMIZED;
96
}
97
}
98
}
99
100
ScreenRenderFlags BackgroundScreen::render(ScreenRenderMode mode) {
101
if (mode & ScreenRenderMode::FIRST) {
102
SetupViewport();
103
} else {
104
_dbg_assert_(false);
105
}
106
107
UIContext *uiContext = screenManager()->getUIContext();
108
109
uiContext->PushTransform({ translation_, scale_, alpha_ });
110
111
uiContext->Begin();
112
Lin::Vec3 focus;
113
screenManager()->getFocusPosition(focus.x, focus.y, focus.z);
114
115
if (!gamePath_.empty()) {
116
::DrawGameBackground(*uiContext, gamePath_, focus, 1.0f);
117
} else {
118
::DrawBackground(*uiContext, 1.0f, focus);
119
}
120
121
uiContext->Flush();
122
123
uiContext->PopTransform();
124
125
return ScreenRenderFlags::NONE;
126
}
127
128
void BackgroundScreen::sendMessage(UIMessage message, const char *value) {
129
switch (message) {
130
case UIMessage::GAME_SELECTED:
131
if (value && strlen(value)) {
132
gamePath_ = Path(value);
133
} else {
134
gamePath_.clear();
135
}
136
break;
137
default:
138
break;
139
}
140
}
141
142
void UIBaseDialogScreen::sendMessage(UIMessage message, const char *value) {
143
if (message == UIMessage::SHOW_SETTINGS && screenManager()->topScreen() == this) {
144
screenManager()->push(new GameSettingsScreen(gamePath_));
145
} else {
146
HandleCommonMessages(message, value, screenManager(), this);
147
}
148
}
149
150
void UIBaseScreen::sendMessage(UIMessage message, const char *value) {
151
HandleCommonMessages(message, value, screenManager(), this);
152
}
153
154
void UIBaseDialogScreen::AddStandardBack(UI::ViewGroup *parent) {
155
using namespace UI;
156
auto di = GetI18NCategory(I18NCat::DIALOG);
157
parent->Add(new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK"), new AnchorLayoutParams(190, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
158
}
159
160
PromptScreen::PromptScreen(const Path &gamePath, std::string_view message, std::string_view yesButtonText, std::string_view noButtonText, std::function<void(bool)> callback)
161
: UIBaseDialogScreen(gamePath), message_(message), callback_(callback) {
162
yesButtonText_ = yesButtonText;
163
noButtonText_ = noButtonText;
164
}
165
166
void PromptScreen::CreateViews() {
167
// Information in the top left.
168
// Back button to the bottom left.
169
// Scrolling action menu to the right.
170
using namespace UI;
171
172
const bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait;
173
174
root_ = new AnchorLayout();
175
ViewGroup *rightColumnItems;
176
177
if (!portrait) {
178
// Horizontal layout.
179
root_->Add(new TextView(message_, ALIGN_LEFT | FLAG_WRAP_TEXT, false, new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, 15, 105, 330, 10)))->SetClip(false);
180
rightColumnItems = new LinearLayout(ORIENT_VERTICAL, new AnchorLayoutParams(300, WRAP_CONTENT, NONE, 105, 15, NONE));
181
root_->Add(rightColumnItems);
182
} else {
183
// Vertical layout
184
root_->Add(new TextView(message_, ALIGN_LEFT | FLAG_WRAP_TEXT, false, new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, 15, 15, 55, NONE)))->SetClip(false);
185
// Leave space for the version at the bottom.
186
rightColumnItems = new LinearLayout(ORIENT_HORIZONTAL, new AnchorLayoutParams(FILL_PARENT, WRAP_CONTENT, 15, NONE, 15, 65));
187
root_->Add(rightColumnItems);
188
}
189
190
Choice *yesButton = new Choice(yesButtonText_, portrait ? new LinearLayoutParams(1.0f) : nullptr);
191
yesButton->SetCentered(portrait);
192
yesButton->OnClick.Add([this](UI::EventParams &e) {
193
TriggerFinish(DR_OK);
194
});
195
Choice *noButton = nullptr;
196
if (!noButtonText_.empty()) {
197
noButton = new Choice(noButtonText_, portrait ? new LinearLayoutParams(1.0f) : nullptr);
198
noButton->SetCentered(portrait);
199
noButton->OnClick.Add([this](UI::EventParams &e) {
200
TriggerFinish(DR_CANCEL);
201
});
202
}
203
204
// The order of the button depends on the platform, if vertical layout is used.
205
// Following UI standards here.
206
if (portrait) {
207
#if PPSSPP_PLATFORM(WINDOWS)
208
// On Windows, we put the yes button on the left.
209
rightColumnItems->Add(yesButton);
210
if (noButton) {
211
rightColumnItems->Add(noButton);
212
}
213
#else
214
// On other platforms, we put the yes button on the right.
215
if (noButton) {
216
rightColumnItems->Add(noButton);
217
}
218
rightColumnItems->Add(yesButton);
219
#endif
220
} else {
221
// In horizontal layout, the buttons are placed vertically, we always put the yes button on top.
222
rightColumnItems->Add(yesButton);
223
if (noButton) {
224
rightColumnItems->Add(noButton);
225
}
226
}
227
228
if (!noButton) {
229
// This is an information screen, not a question.
230
// Sneak in the version of PPSSPP in the bottom left corner, for debug-reporting user screenshots.
231
std::string version = System_GetProperty(SYSPROP_BUILD_VERSION);
232
root_->Add(new TextView(version, 0, true, new AnchorLayoutParams(10.0f, NONE, NONE, 10.0f)));
233
}
234
root_->SetDefaultFocusView(noButton ? noButton : yesButton);
235
}
236
237
void PromptScreen::TriggerFinish(DialogResult result) {
238
if (callback_) {
239
callback_(result == DR_OK || result == DR_YES);
240
}
241
UIBaseDialogScreen::TriggerFinish(result);
242
}
243
244
TextureShaderScreen::TextureShaderScreen(std::string_view title) : ListPopupScreen(title) {}
245
246
void TextureShaderScreen::CreateViews() {
247
auto ps = GetI18NCategory(I18NCat::TEXTURESHADERS);
248
ReloadAllPostShaderInfo(screenManager()->getDrawContext());
249
shaders_ = GetAllTextureShaderInfo();
250
std::vector<std::string> items;
251
int selected = -1;
252
for (int i = 0; i < (int)shaders_.size(); i++) {
253
if (shaders_[i].section == g_Config.sTextureShaderName)
254
selected = i;
255
items.emplace_back(ps->T(shaders_[i].section, shaders_[i].name));
256
}
257
adaptor_ = UI::StringVectorListAdaptor(items, selected);
258
259
ListPopupScreen::CreateViews();
260
}
261
262
void TextureShaderScreen::OnCompleted(DialogResult result) {
263
if (result != DR_OK)
264
return;
265
g_Config.sTextureShaderName = shaders_[listView_->GetSelected()].section;
266
}
267
268
NewLanguageScreen::NewLanguageScreen(std::string_view title) : ListPopupScreen(title) {
269
// Disable annoying encoding warning
270
#ifdef _MSC_VER
271
#pragma warning(disable:4566)
272
#endif
273
auto &langValuesMapping = GetLangValuesMapping();
274
275
std::vector<File::FileInfo> tempLangs;
276
g_VFS.GetFileListing("lang", &tempLangs, "ini");
277
std::vector<std::string> listing;
278
int selected = -1;
279
int counter = 0;
280
for (size_t i = 0; i < tempLangs.size(); i++) {
281
// Skip README
282
if (tempLangs[i].name.find("README") != std::string::npos) {
283
continue;
284
}
285
286
// We only support Arabic on platforms where we have support for the native text rendering
287
// APIs, as proper Arabic support is way too difficult to implement ourselves.
288
#if !(defined(USING_QT_UI) || PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(ANDROID))
289
if (tempLangs[i].name.find("ar_AE") != std::string::npos) {
290
continue;
291
}
292
293
if (tempLangs[i].name.find("fa_IR") != std::string::npos) {
294
continue;
295
}
296
#endif
297
298
const File::FileInfo &lang = tempLangs[i];
299
langs_.push_back(lang);
300
301
std::string code;
302
size_t dot = lang.name.find('.');
303
if (dot != std::string::npos)
304
code = lang.name.substr(0, dot);
305
306
std::string buttonTitle = lang.name;
307
308
if (!code.empty()) {
309
auto iter = langValuesMapping.find(code);
310
if (iter == langValuesMapping.end()) {
311
// No title found, show locale code
312
buttonTitle = code;
313
} else {
314
buttonTitle = iter->second.first;
315
}
316
}
317
if (g_Config.sLanguageIni == code)
318
selected = counter;
319
listing.push_back(buttonTitle);
320
counter++;
321
}
322
323
adaptor_ = UI::StringVectorListAdaptor(listing, selected);
324
}
325
326
void NewLanguageScreen::OnCompleted(DialogResult result) {
327
if (result != DR_OK)
328
return;
329
std::string oldLang = g_Config.sLanguageIni;
330
std::string iniFile = langs_[listView_->GetSelected()].name;
331
332
std::string_view code, part2;
333
if (!SplitStringOnce(iniFile, &code, &part2, '.')) {
334
return;
335
}
336
337
g_Config.sLanguageIni = code;
338
339
bool iniLoadedSuccessfully = false;
340
// Allow the lang directory to be overridden for testing purposes (e.g. Android, where it's hard to
341
// test new languages without recompiling the entire app, which is a hassle).
342
const Path langOverridePath = GetSysDirectory(DIRECTORY_SYSTEM) / "lang";
343
344
// If we run into the unlikely case that "lang" is actually a file, just use the built-in translations.
345
if (!File::Exists(langOverridePath) || !File::IsDirectory(langOverridePath))
346
iniLoadedSuccessfully = g_i18nrepo.LoadIni(g_Config.sLanguageIni);
347
else
348
iniLoadedSuccessfully = g_i18nrepo.LoadIni(g_Config.sLanguageIni, langOverridePath);
349
350
if (iniLoadedSuccessfully) {
351
RecreateViews();
352
System_Notify(SystemNotification::UI);
353
} else {
354
// Failed to load the language ini. Shouldn't really happen, but let's just switch back to the old language.
355
g_Config.sLanguageIni = oldLang;
356
}
357
}
358
359
void LogoScreen::Next() {
360
if (!switched_) {
361
switched_ = true;
362
Path gamePath = boot_filename;
363
364
switch (afterLogoScreen_) {
365
case AfterLogoScreen::TO_GAME_SETTINGS:
366
if (!gamePath.empty()) {
367
screenManager()->switchScreen(new EmuScreen(gamePath));
368
} else {
369
screenManager()->switchScreen(new MainScreen());
370
}
371
screenManager()->push(new GameSettingsScreen(gamePath));
372
break;
373
case AfterLogoScreen::MEMSTICK_SCREEN_INITIAL_SETUP:
374
screenManager()->switchScreen(new MemStickScreen(true));
375
break;
376
case AfterLogoScreen::DEFAULT:
377
default:
378
if (boot_filename.size()) {
379
screenManager()->switchScreen(new EmuScreen(gamePath));
380
} else {
381
screenManager()->switchScreen(new MainScreen());
382
}
383
break;
384
}
385
}
386
}
387
388
const float logoScreenSeconds = 2.5f;
389
390
LogoScreen::LogoScreen(AfterLogoScreen afterLogoScreen)
391
: afterLogoScreen_(afterLogoScreen) {
392
}
393
394
void LogoScreen::update() {
395
UIScreen::update();
396
double rate = std::max(30.0, (double)System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE));
397
398
if ((double)frames_ / rate > logoScreenSeconds) {
399
Next();
400
}
401
frames_++;
402
sinceStart_ = (double)frames_ / rate;
403
}
404
405
void LogoScreen::sendMessage(UIMessage message, const char *value) {
406
if (message == UIMessage::REQUEST_GAME_BOOT && screenManager()->topScreen() == this) {
407
screenManager()->switchScreen(new EmuScreen(Path(value)));
408
}
409
}
410
411
bool LogoScreen::key(const KeyInput &key) {
412
if (key.deviceId != DEVICE_ID_MOUSE && (key.flags & KeyInputFlags::DOWN)) {
413
Next();
414
return true;
415
}
416
return false;
417
}
418
419
void LogoScreen::touch(const TouchInput &touch) {
420
if (touch.flags & TouchInputFlags::DOWN) {
421
Next();
422
}
423
}
424
425
void LogoScreen::DrawForeground(UIContext &dc) {
426
using namespace Draw;
427
428
const Bounds &bounds = dc.GetLayoutBounds();
429
430
dc.Begin();
431
432
float t = (float)sinceStart_ / (logoScreenSeconds / 3.0f);
433
434
float alpha = t;
435
if (t > 1.0f)
436
alpha = 1.0f;
437
float alphaText = alpha;
438
if (t > 2.0f)
439
alphaText = 3.0f - t;
440
uint32_t textColor = colorAlpha(dc.GetTheme().infoStyle.fgColor, alphaText);
441
442
auto cr = GetI18NCategory(I18NCat::PSPCREDITS);
443
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
444
char temp[256];
445
446
const float startY = bounds.centerY() - 70;
447
448
// Manually formatting UTF-8 is fun. \xXX doesn't work everywhere.
449
snprintf(temp, sizeof(temp), "%s Henrik Rydg%c%crd", cr->T_cstr("created", "Created by"), 0xC3, 0xA5);
450
if (System_GetPropertyBool(SYSPROP_APP_GOLD)) {
451
UI::DrawIconShine(dc, Bounds::FromCenter(bounds.centerX() - 125, startY, 60.0f), 0.7f, true);
452
dc.Draw()->DrawImage(ImageID("I_ICON_GOLD"), bounds.centerX() - 125, startY, 1.2f, 0xFFFFFFFF, ALIGN_CENTER);
453
} else {
454
dc.Draw()->DrawImage(ImageID("I_ICON"), bounds.centerX() - 125, startY, 1.2f, 0xFFFFFFFF, ALIGN_CENTER);
455
}
456
dc.Draw()->DrawImage(ImageID("I_LOGO"), bounds.centerX() + 45, startY, 1.5f, 0xFFFFFFFF, ALIGN_CENTER);
457
//dc.Draw()->DrawTextShadow(UBUNTU48, "PPSSPP", bounds.w / 2, bounds.h / 2 - 30, textColor, ALIGN_CENTER);
458
dc.SetFontScale(1.0f, 1.0f);
459
dc.SetFontStyle(dc.GetTheme().uiFont);
460
dc.DrawText(temp, bounds.centerX(), startY + 70, textColor, ALIGN_CENTER);
461
dc.DrawText(cr->T_cstr("license", "Free Software under GPL 2.0+"), bounds.centerX(), startY + 110, textColor, ALIGN_CENTER);
462
463
dc.DrawText("www.ppsspp.org", bounds.centerX(), startY + 160, textColor, ALIGN_CENTER);
464
465
#if !PPSSPP_PLATFORM(UWP) || defined(_DEBUG)
466
// Draw the graphics API, except on UWP where it's always D3D11
467
std::string apiName(gr->T(screenManager()->getDrawContext()->GetInfoString(InfoField::APINAME)));
468
#ifdef _DEBUG
469
apiName += ", debug build ";
470
// Add some emoji for testing.
471
apiName += CodepointToUTF8(0x1F41B) + CodepointToUTF8(0x1F41C) + CodepointToUTF8(0x1F914);
472
#endif
473
dc.DrawText(apiName, bounds.centerX(), startY + 200, textColor, ALIGN_CENTER);
474
#endif
475
476
dc.Flush();
477
}
478
479
class CreditsScroller : public UI::View {
480
public:
481
CreditsScroller(UI::LayoutParams *layoutParams) : UI::View(layoutParams) {}
482
bool Touch(const TouchInput &touch) override {
483
if (touch.id != 0)
484
return false;
485
if (touch.flags & TouchInputFlags::DOWN) {
486
dragYStart_ = touch.y;
487
dragYOffsetStart_ = dragOffset_;
488
}
489
if (touch.flags & TouchInputFlags::UP) {
490
dragYStart_ = -1.0f;
491
}
492
if (touch.flags & TouchInputFlags::MOVE) {
493
if (dragYStart_ >= 0.0f) {
494
dragOffset_ = dragYOffsetStart_ + (touch.y - dragYStart_);
495
}
496
}
497
return true;
498
}
499
void Draw(UIContext &dc) override;
500
private:
501
Instant startTime_ = Instant::Now();
502
double dragYStart_ = -1.0;
503
double dragOffset_ = 0.0;
504
double dragYOffsetStart_ = 0.0;
505
};
506
507
std::string_view CreditsScreen::GetTitle() const {
508
auto mm = GetI18NCategory(I18NCat::MAINMENU);
509
return mm->T("About PPSSPP");
510
}
511
512
void CreditsScreen::CreateDialogViews(UI::ViewGroup *parent) {
513
using namespace UI;
514
515
ignoreBottomInset_ = false;
516
517
auto di = GetI18NCategory(I18NCat::DIALOG);
518
auto cr = GetI18NCategory(I18NCat::PSPCREDITS);
519
auto mm = GetI18NCategory(I18NCat::MAINMENU);
520
521
const bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait;
522
523
const bool gold = System_GetPropertyBool(SYSPROP_APP_GOLD);
524
525
/*
526
if (System_GetPropertyBool(SYSPROP_APP_GOLD)) {
527
root_->Add(new ShinyIcon(ImageID("I_ICON_GOLD"), new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, 10, 10, NONE, NONE, false)))->SetScale(1.5f);
528
} else {
529
root_->Add(new ImageView(ImageID("I_ICON"), "", IS_DEFAULT, new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, 10, 10, NONE, NONE, false)))->SetScale(1.5f);
530
}*/
531
532
constexpr float columnWidth = 265.0f;
533
534
LinearLayout *left;
535
LinearLayout *right;
536
if (portrait) {
537
parent->Add(new CreditsScroller(new LinearLayoutParams(1.0f)));
538
539
LinearLayout *columns = parent->Add(new LinearLayout(ORIENT_HORIZONTAL));
540
541
left = columns->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(columnWidth, WRAP_CONTENT, Margins(10))));
542
columns->Add(new Spacer(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)));
543
right = columns->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(columnWidth, WRAP_CONTENT, Margins(10))));
544
} else {
545
LinearLayout *columns = parent->Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(1.0f)));
546
547
left = columns->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(columnWidth, FILL_PARENT, Margins(10))));
548
left->Add(new Spacer(0.0f, new LinearLayoutParams(1.0f)));
549
columns->Add(new CreditsScroller(new LinearLayoutParams(WRAP_CONTENT, FILL_PARENT, 1.0f)));
550
right = columns->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(columnWidth, FILL_PARENT, Margins(10))));
551
right->Add(new Spacer(0.0f, new LinearLayoutParams(1.0f)));
552
}
553
554
int rightYOffset = 0;
555
if (!System_GetPropertyBool(SYSPROP_APP_GOLD)) {
556
ScreenManager *sm = screenManager();
557
Choice *gold = new Choice(mm->T("Buy PPSSPP Gold"));
558
gold->SetIconRight(ImageID("I_ICON_GOLD"), 0.5f);
559
gold->SetImageScale(0.6f); // for the left-icon in case of vertical.
560
gold->SetShine(true);
561
562
left->Add(gold)->OnClick.Add([sm](UI::EventParams) {
563
LaunchBuyGold(sm);
564
});
565
rightYOffset = 74;
566
}
567
left->Add(new Choice(cr->T("PPSSPP Forums"), ImageID("I_LINK_OUT")))->OnClick.Add([](UI::EventParams &e) {
568
System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://forums.ppsspp.org");
569
});
570
left->Add(new Choice(cr->T("Discord"), ImageID("I_LOGO_DISCORD")))->OnClick.Add([](UI::EventParams &e) {
571
System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://discord.gg/5NJB6dD");
572
});
573
left->Add(new Choice("www.ppsspp.org", ImageID("I_LINK_OUT")))->OnClick.Add([](UI::EventParams &e) {
574
System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://www.ppsspp.org");
575
});
576
right->Add(new Choice(cr->T("Privacy Policy"), ImageID("I_LINK_OUT")))->OnClick.Add([](UI::EventParams &e) {
577
System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://www.ppsspp.org/privacy");
578
});
579
right->Add(new Choice(cr->T("@PPSSPP_emu"), ImageID("I_LOGO_X")))->OnClick.Add([](UI::EventParams &e) {
580
System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://x.com/PPSSPP_emu");
581
});
582
583
if (System_GetPropertyBool(SYSPROP_SUPPORTS_SHARE_TEXT)) {
584
right->Add(new Choice(cr->T("Share PPSSPP"), ImageID("I_SHARE")))->OnClick.Add([](UI::EventParams &e) {
585
auto cr = GetI18NCategory(I18NCat::PSPCREDITS);
586
System_ShareText(cr->T("CheckOutPPSSPP", "Check out PPSSPP, the awesome PSP emulator: https://www.ppsspp.org/"));
587
});
588
}
589
}
590
591
void CreditsScreen::update() {
592
UIScreen::update();
593
UpdateUIState(UISTATE_MENU);
594
}
595
596
void CreditsScroller::Draw(UIContext &dc) {
597
auto cr = GetI18NCategory(I18NCat::PSPCREDITS);
598
599
std::string specialthanksMaxim = "Maxim ";
600
specialthanksMaxim += cr->T("specialthanksMaxim", "for his amazing Atrac3+ decoder work");
601
602
std::string specialthanksKeithGalocy = "Keith Galocy ";
603
specialthanksKeithGalocy += cr->T("specialthanksKeithGalocy", "at NVIDIA (hardware, advice)");
604
605
std::string specialthanksOrphis = "Orphis (";
606
specialthanksOrphis += cr->T("build server");
607
specialthanksOrphis += ')';
608
609
std::string specialthanksangelxwind = "angelxwind (";
610
specialthanksangelxwind += cr->T("iOS builds");
611
specialthanksangelxwind += ')';
612
613
std::string specialthanksW_MS = "W.MS (";
614
specialthanksW_MS += cr->T("iOS builds");
615
specialthanksW_MS += ')';
616
617
std::string specialthankssolarmystic = "solarmystic (";
618
specialthankssolarmystic += cr->T("testing");
619
specialthankssolarmystic += ')';
620
621
std::string_view credits[] = {
622
System_GetPropertyBool(SYSPROP_APP_GOLD) ? "PPSSPP Gold" : "PPSSPP",
623
"",
624
cr->T("title", "A fast and portable PSP emulator"),
625
"",
626
"",
627
cr->T("created", "Created by"),
628
"Henrik Rydg\xc3\xa5rd",
629
"",
630
"",
631
cr->T("contributors", "Contributors:"),
632
"unknownbrackets",
633
"oioitff",
634
"xsacha",
635
"raven02",
636
"tpunix",
637
"orphis",
638
"sum2012",
639
"mikusp",
640
"aquanull",
641
"The Dax",
642
"bollu",
643
"tmaul",
644
"artart78",
645
"ced2911",
646
"soywiz",
647
"kovensky",
648
"xele",
649
"chaserhjk",
650
"evilcorn",
651
"daniel dressler",
652
"makotech222",
653
"CPkmn",
654
"mgaver",
655
"jeid3",
656
"cinaera/BeaR",
657
"jtraynham",
658
"Kingcom",
659
"arnastia",
660
"lioncash",
661
"JulianoAmaralChaves",
662
"vnctdj",
663
"kaienfr",
664
"shenweip",
665
"Danyal Zia",
666
"Igor Calabria",
667
"Coldbird",
668
"Kyhel",
669
"xebra",
670
"LunaMoo",
671
"zminhquanz",
672
"ANR2ME",
673
"adenovan",
674
"iota97",
675
"Lubos",
676
"stenzek", // For retroachievements integration
677
"fp64",
678
"",
679
cr->T("specialthanks", "Special thanks to:"),
680
specialthanksMaxim,
681
specialthanksKeithGalocy,
682
specialthanksOrphis,
683
specialthanksangelxwind,
684
specialthanksW_MS,
685
specialthankssolarmystic,
686
cr->T("all the forum mods"),
687
"",
688
cr->T("this translation by", ""), // Empty string as this is the original :)
689
cr->T("translators1", ""),
690
cr->T("translators2", ""),
691
cr->T("translators3", ""),
692
cr->T("translators4", ""),
693
cr->T("translators5", ""),
694
cr->T("translators6", ""),
695
"",
696
cr->T("written", "Written in C++ for speed and portability"),
697
"",
698
"",
699
cr->T("tools", "Free tools used:"),
700
#if PPSSPP_PLATFORM(ANDROID)
701
"Android SDK + NDK",
702
#endif
703
#if defined(USING_QT_UI)
704
"Qt",
705
#endif
706
#if defined(SDL)
707
"SDL",
708
#endif
709
"CMake",
710
"freetype2",
711
"zlib",
712
"rcheevos",
713
"SPIRV-Cross",
714
"armips",
715
"Basis Universal",
716
"cityhash",
717
"zstd",
718
"glew",
719
"libchdr",
720
"minimp3",
721
"xxhash",
722
"naett-http",
723
"PSP SDK",
724
"",
725
"",
726
cr->T("website", "Check out the website:"),
727
"www.ppsspp.org",
728
cr->T("list", "compatibility lists, forums, and development info"),
729
"",
730
"",
731
cr->T("check", "Also check out Dolphin, the best Wii/GC emu around:"),
732
"https://www.dolphin-emu.org",
733
"",
734
"",
735
cr->T("info1", "PPSSPP is only intended to play games you own."),
736
cr->T("info2", "Please make sure that you own the rights to any games"),
737
cr->T("info3", "you play by owning the UMD or by buying the digital"),
738
cr->T("info4", "download from the PSN store on your real PSP."),
739
"",
740
"",
741
cr->T("info5", "PSP is a trademark by Sony, Inc."),
742
};
743
744
// TODO: This is kinda ugly, done on every frame...
745
char temp[256];
746
if (System_GetPropertyBool(SYSPROP_APP_GOLD)) {
747
snprintf(temp, sizeof(temp), "PPSSPP Gold %s", PPSSPP_GIT_VERSION);
748
} else {
749
snprintf(temp, sizeof(temp), "PPSSPP %s", PPSSPP_GIT_VERSION);
750
}
751
credits[0] = (const char *)temp;
752
753
dc.Begin();
754
755
const Bounds &bounds = bounds_;
756
bounds.Inset(10.f, 10.f);
757
const int numItems = ARRAY_SIZE(credits);
758
int itemHeight = 36;
759
int contentsHeight = numItems * itemHeight + bounds.h + 200;
760
761
const float t = (float)(startTime_.ElapsedSeconds() * 60.0);
762
763
const float yOffset = fmodf(t - dragOffset_, (float)contentsHeight);
764
765
float y = bounds.h - yOffset;
766
for (int i = 0; i < numItems; i++, y += itemHeight) {
767
if (y + itemHeight < 0.0f)
768
continue;
769
if (y > bounds.h)
770
continue;
771
float fadeLength = 64.0f;
772
float alpha = linearInOut(y, 64, bounds.h - fadeLength * 2.0f, 64);
773
uint32_t textColor = colorAlpha(dc.GetTheme().infoStyle.fgColor, alpha);
774
775
if (alpha > 0.0f) {
776
dc.SetFontScale(ease(alpha), ease(alpha));
777
dc.DrawText(credits[i], bounds.centerX(), y + bounds.y, textColor, ALIGN_HCENTER);
778
dc.SetFontScale(1.0f, 1.0f);
779
}
780
}
781
782
dc.Flush();
783
}
784
785