Merge pull request #90 from gizmo98/patch-2
[libretro-ppsspp.git] / UI / MiscScreens.cpp
blob22ab4e9541ecf5066acae1e17c76cbd6f13c154f
1 // Copyright (c) 2013- PPSSPP Project.
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.
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.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
18 #include <algorithm>
20 #include "base/functional.h"
21 #include "base/colorutil.h"
22 #include "base/timeutil.h"
23 #include "gfx_es2/draw_buffer.h"
24 #include "math/curves.h"
25 #include "i18n/i18n.h"
26 #include "ui/ui_context.h"
27 #include "ui/view.h"
28 #include "ui/viewgroup.h"
29 #include "ui/ui.h"
30 #include "file/vfs.h"
31 #include "UI/MiscScreens.h"
32 #include "UI/EmuScreen.h"
33 #include "UI/MainScreen.h"
34 #include "UI/GameInfoCache.h"
35 #include "Core/Config.h"
36 #include "Core/Host.h"
37 #include "Core/System.h"
38 #include "Core/MIPS/JitCommon/JitCommon.h"
39 #include "Core/MIPS/JitCommon/NativeJit.h"
40 #include "Core/HLE/sceUtility.h"
41 #include "Common/CPUDetect.h"
42 #include "Common/FileUtil.h"
43 #include "GPU/GPUState.h"
45 #include "ui_atlas.h"
47 #ifdef _MSC_VER
48 #pragma execution_character_set("utf-8")
49 #endif
51 #include "base/timeutil.h"
52 #include "base/colorutil.h"
53 #include "gfx_es2/draw_buffer.h"
54 #include "gfx_es2/gl_state.h"
55 #include "util/random/rng.h"
57 #include "UI/ui_atlas.h"
59 static const int symbols[4] = {
60 I_CROSS,
61 I_CIRCLE,
62 I_SQUARE,
63 I_TRIANGLE
66 static const uint32_t colors[4] = {
67 0xC0FFFFFF,
68 0xC0FFFFFF,
69 0xC0FFFFFF,
70 0xC0FFFFFF,
73 void DrawBackground(UIContext &dc, float alpha = 1.0f) {
74 static float xbase[100] = {0};
75 static float ybase[100] = {0};
76 float xres = dc.GetBounds().w;
77 float yres = dc.GetBounds().h;
78 static int last_xres = 0;
79 static int last_yres = 0;
81 if (xbase[0] == 0.0f || last_xres != xres || last_yres != yres) {
82 GMRng rng;
83 for (int i = 0; i < 100; i++) {
84 xbase[i] = rng.F() * xres;
85 ybase[i] = rng.F() * yres;
87 last_xres = xres;
88 last_yres = yres;
91 int img = I_BG;
92 #ifdef GOLD
93 img = I_BG_GOLD;
94 #endif
95 ui_draw2d.DrawImageStretch(img, dc.GetBounds());
96 float t = time_now();
97 for (int i = 0; i < 100; i++) {
98 float x = xbase[i] + dc.GetBounds().x;
99 float y = ybase[i] + dc.GetBounds().y + 40 * cosf(i * 7.2f + t * 1.3f);
100 float angle = sinf(i + t);
101 int n = i & 3;
102 ui_draw2d.DrawImageRotated(symbols[n], x, y, 1.0f, angle, colorAlpha(colors[n], alpha * 0.1f));
106 void DrawGameBackground(UIContext &dc, const std::string &gamePath) {
107 GameInfo *ginfo = g_gameInfoCache.GetInfo(dc.GetThin3DContext(), gamePath, GAMEINFO_WANTBG);
108 dc.Flush();
110 if (ginfo) {
111 bool hasPic = false;
112 double loadTime;
113 if (ginfo->pic1Texture) {
114 dc.GetThin3DContext()->SetTexture(0, ginfo->pic1Texture);
115 loadTime = ginfo->timePic1WasLoaded;
116 hasPic = true;
117 } else if (ginfo->pic0Texture) {
118 dc.GetThin3DContext()->SetTexture(0, ginfo->pic0Texture);
119 loadTime = ginfo->timePic0WasLoaded;
120 hasPic = true;
122 if (hasPic) {
123 uint32_t color = whiteAlpha(ease((time_now_d() - loadTime) * 3)) & 0xFFc0c0c0;
124 dc.Draw()->DrawTexRect(dc.GetBounds(), 0,0,1,1, color);
125 dc.Flush();
126 dc.RebindTexture();
127 } else {
128 ::DrawBackground(dc, 1.0f);
129 dc.RebindTexture();
130 dc.Flush();
135 void HandleCommonMessages(const char *message, const char *value, ScreenManager *manager) {
136 if (!strcmp(message, "clear jit")) {
137 if (MIPSComp::jit && PSP_IsInited()) {
138 MIPSComp::jit->ClearCache();
140 if (PSP_IsInited()) {
141 currentMIPS->UpdateCore(g_Config.bJit ? CPU_JIT : CPU_INTERPRETER);
146 void UIScreenWithBackground::DrawBackground(UIContext &dc) {
147 ::DrawBackground(dc, 1.0f);
148 dc.Flush();
151 void UIScreenWithGameBackground::DrawBackground(UIContext &dc) {
152 if (!gamePath_.empty()) {
153 DrawGameBackground(dc, gamePath_);
154 } else {
155 ::DrawBackground(dc, 1.0f);
156 dc.Flush();
160 void UIDialogScreenWithGameBackground::DrawBackground(UIContext &dc) {
161 DrawGameBackground(dc, gamePath_);
164 void UIScreenWithBackground::sendMessage(const char *message, const char *value) {
165 HandleCommonMessages(message, value, screenManager());
166 I18NCategory *dev = GetI18NCategory("Developer");
167 if (!strcmp(message, "language screen")) {
168 auto langScreen = new NewLanguageScreen(dev->T("Language"));
169 langScreen->OnChoice.Handle(this, &UIScreenWithBackground::OnLanguageChange);
170 screenManager()->push(langScreen);
174 UI::EventReturn UIScreenWithBackground::OnLanguageChange(UI::EventParams &e) {
175 screenManager()->RecreateAllViews();
176 if (host) {
177 host->UpdateUI();
180 return UI::EVENT_DONE;
183 UI::EventReturn UIDialogScreenWithBackground::OnLanguageChange(UI::EventParams &e) {
184 screenManager()->RecreateAllViews();
185 if (host) {
186 host->UpdateUI();
189 return UI::EVENT_DONE;
192 void UIDialogScreenWithBackground::DrawBackground(UIContext &dc) {
193 ::DrawBackground(dc);
194 dc.Flush();
197 void UIDialogScreenWithBackground::AddStandardBack(UI::ViewGroup *parent) {
198 using namespace UI;
199 I18NCategory *di = GetI18NCategory("Dialog");
200 parent->Add(new Choice(di->T("Back"), "", false, new AnchorLayoutParams(150, 64, 10, NONE, NONE, 10)))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
203 void UIDialogScreenWithBackground::sendMessage(const char *message, const char *value) {
204 HandleCommonMessages(message, value, screenManager());
205 I18NCategory *dev = GetI18NCategory("Developer");
206 if (!strcmp(message, "language screen")) {
207 auto langScreen = new NewLanguageScreen(dev->T("Language"));
208 langScreen->OnChoice.Handle(this, &UIDialogScreenWithBackground::OnLanguageChange);
209 screenManager()->push(langScreen);
210 } else if (!strcmp(message, "window minimized")) {
211 if (!strcmp(value, "true")) {
212 gstate_c.skipDrawReason |= SKIPDRAW_WINDOW_MINIMIZED;
213 } else {
214 gstate_c.skipDrawReason &= ~SKIPDRAW_WINDOW_MINIMIZED;
219 PromptScreen::PromptScreen(std::string message, std::string yesButtonText, std::string noButtonText, std::function<void(bool)> callback)
220 : message_(message), callback_(callback) {
221 I18NCategory *di = GetI18NCategory("Dialog");
222 yesButtonText_ = di->T(yesButtonText.c_str());
223 noButtonText_ = di->T(noButtonText.c_str());
226 void PromptScreen::CreateViews() {
227 // Information in the top left.
228 // Back button to the bottom left.
229 // Scrolling action menu to the right.
230 using namespace UI;
232 Margins actionMenuMargins(0, 100, 15, 0);
234 root_ = new LinearLayout(ORIENT_HORIZONTAL);
236 ViewGroup *leftColumn = new AnchorLayout(new LinearLayoutParams(1.0f));
237 root_->Add(leftColumn);
239 leftColumn->Add(new TextView(message_, ALIGN_LEFT, false, new AnchorLayoutParams(10, 10, NONE, NONE)));
241 ViewGroup *rightColumnItems = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins));
242 root_->Add(rightColumnItems);
243 Choice *yesButton = rightColumnItems->Add(new Choice(yesButtonText_));
244 yesButton->OnClick.Handle(this, &PromptScreen::OnYes);
245 root_->SetDefaultFocusView(yesButton);
246 if (noButtonText_ != "")
247 rightColumnItems->Add(new Choice(noButtonText_))->OnClick.Handle(this, &PromptScreen::OnNo);
250 UI::EventReturn PromptScreen::OnYes(UI::EventParams &e) {
251 callback_(true);
252 screenManager()->finishDialog(this, DR_OK);
253 return UI::EVENT_DONE;
256 UI::EventReturn PromptScreen::OnNo(UI::EventParams &e) {
257 callback_(false);
258 screenManager()->finishDialog(this, DR_CANCEL);
259 return UI::EVENT_DONE;
262 PostProcScreen::PostProcScreen(const std::string &title) : ListPopupScreen(title) {
263 I18NCategory *ps = GetI18NCategory("PostShaders");
264 shaders_ = GetAllPostShaderInfo();
265 std::vector<std::string> items;
266 int selected = -1;
267 for (int i = 0; i < (int)shaders_.size(); i++) {
268 if (shaders_[i].section == g_Config.sPostShaderName)
269 selected = i;
270 items.push_back(ps->T(shaders_[i].section.c_str()));
272 adaptor_ = UI::StringVectorListAdaptor(items, selected);
275 void PostProcScreen::OnCompleted(DialogResult result) {
276 if (result != DR_OK)
277 return;
278 g_Config.sPostShaderName = shaders_[listView_->GetSelected()].section;
281 NewLanguageScreen::NewLanguageScreen(const std::string &title) : ListPopupScreen(title) {
282 // Disable annoying encoding warning
283 #ifdef _MSC_VER
284 #pragma warning(disable:4566)
285 #endif
286 langValuesMapping = GetLangValuesMapping();
288 std::vector<FileInfo> tempLangs;
289 VFSGetFileListing("lang", &tempLangs, "ini");
290 std::vector<std::string> listing;
291 int selected = -1;
292 int counter = 0;
293 for (size_t i = 0; i < tempLangs.size(); i++) {
294 // Skip README
295 if (tempLangs[i].name.find("README") != std::string::npos) {
296 continue;
299 #ifndef _WIN32
300 // ar_AE only works on Windows.
301 if (tempLangs[i].name.find("ar_AE") != std::string::npos) {
302 continue;
304 // Farsi also only works on Windows.
305 if (tempLangs[i].name.find("fa_IR") != std::string::npos) {
306 continue;
308 #endif
309 FileInfo lang = tempLangs[i];
310 langs_.push_back(lang);
312 std::string code;
313 size_t dot = lang.name.find('.');
314 if (dot != std::string::npos)
315 code = lang.name.substr(0, dot);
317 std::string buttonTitle = lang.name;
319 if (!code.empty()) {
320 if (langValuesMapping.find(code) == langValuesMapping.end()) {
321 // No title found, show locale code
322 buttonTitle = code;
323 } else {
324 buttonTitle = langValuesMapping[code].first;
327 if (g_Config.sLanguageIni == code)
328 selected = counter;
329 listing.push_back(buttonTitle);
330 counter++;
333 adaptor_ = UI::StringVectorListAdaptor(listing, selected);
336 void NewLanguageScreen::OnCompleted(DialogResult result) {
337 if (result != DR_OK)
338 return;
339 std::string oldLang = g_Config.sLanguageIni;
340 std::string iniFile = langs_[listView_->GetSelected()].name;
342 size_t dot = iniFile.find('.');
343 std::string code;
344 if (dot != std::string::npos)
345 code = iniFile.substr(0, dot);
347 if (code.empty())
348 return;
350 g_Config.sLanguageIni = code;
352 bool iniLoadedSuccessfully = false;
353 // Allow the lang directory to be overridden for testing purposes (e.g. Android, where it's hard to
354 // test new languages without recompiling the entire app, which is a hassle).
355 const std::string langOverridePath = g_Config.memStickDirectory + "PSP/SYSTEM/lang/";
357 // If we run into the unlikely case that "lang" is actually a file, just use the built-in translations.
358 if (!File::Exists(langOverridePath) || !File::IsDirectory(langOverridePath))
359 iniLoadedSuccessfully = i18nrepo.LoadIni(g_Config.sLanguageIni);
360 else
361 iniLoadedSuccessfully = i18nrepo.LoadIni(g_Config.sLanguageIni, langOverridePath);
363 if (iniLoadedSuccessfully) {
364 // Dunno what else to do here.
365 if (langValuesMapping.find(code) == langValuesMapping.end()) {
366 // Fallback to English
367 g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;
368 } else {
369 g_Config.iLanguage = langValuesMapping[code].second;
371 RecreateViews();
372 } else {
373 g_Config.sLanguageIni = oldLang;
377 void LogoScreen::Next() {
378 if (!switched_) {
379 switched_ = true;
380 if (boot_filename.size()) {
381 screenManager()->switchScreen(new EmuScreen(boot_filename));
382 } else {
383 screenManager()->switchScreen(new MainScreen());
388 void LogoScreen::update(InputState &input_state) {
389 UIScreen::update(input_state);
390 frames_++;
391 if (frames_ > 180 || input_state.pointer_down[0]) {
392 Next();
396 void LogoScreen::sendMessage(const char *message, const char *value) {
397 if (!strcmp(message, "boot")) {
398 screenManager()->switchScreen(new EmuScreen(value));
402 bool LogoScreen::key(const KeyInput &key) {
403 if (key.deviceId != DEVICE_ID_MOUSE) {
404 Next();
405 return true;
407 return false;
410 void LogoScreen::render() {
411 UIScreen::render();
412 UIContext &dc = *screenManager()->getUIContext();
414 const Bounds &bounds = dc.GetBounds();
416 float xres = dc.GetBounds().w;
417 float yres = dc.GetBounds().h;
419 dc.Begin();
420 float t = (float)frames_ / 60.0f;
422 float alpha = t;
423 if (t > 1.0f)
424 alpha = 1.0f;
425 float alphaText = alpha;
426 if (t > 2.0f)
427 alphaText = 3.0f - t;
429 ::DrawBackground(dc, alpha);
431 I18NCategory *cr = GetI18NCategory("PSPCredits");
432 char temp[256];
433 // Manually formatting utf-8 is fun. \xXX doesn't work everywhere.
434 snprintf(temp, sizeof(temp), "%s Henrik Rydg%c%crd", cr->T("created", "Created by"), 0xC3, 0xA5);
435 #ifdef GOLD
436 dc.Draw()->DrawImage(I_ICONGOLD, bounds.centerX() - 120, bounds.centerY() - 30, 1.2f, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER);
437 #else
438 dc.Draw()->DrawImage(I_ICON, bounds.centerX() - 120, bounds.centerY() - 30, 1.2f, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER);
439 #endif
440 dc.Draw()->DrawImage(I_LOGO, bounds.centerX() + 40, bounds.centerY() - 30, 1.5f, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER);
441 //dc.Draw()->DrawTextShadow(UBUNTU48, "PPSSPP", xres / 2, yres / 2 - 30, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER);
442 dc.SetFontScale(1.0f, 1.0f);
443 dc.SetFontStyle(dc.theme->uiFont);
444 dc.DrawText(temp, bounds.centerX(), bounds.centerY() + 40, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER);
445 dc.DrawText(cr->T("license", "Free Software under GPL 2.0"), bounds.centerX(), bounds.centerY() + 70, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER);
446 dc.DrawText("www.ppsspp.org", bounds.centerX(), yres / 2 + 130, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER);
447 if (boot_filename.size()) {
448 dc.DrawTextShadow(boot_filename.c_str(), bounds.centerX(), bounds.centerY() + 180, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER);
451 #ifdef _WIN32
452 dc.DrawText(screenManager()->getThin3DContext()->GetInfoString(T3DInfo::APINAME), bounds.centerX(), bounds.y2() - 100, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER);
453 #endif
455 dc.End();
456 dc.Flush();
459 void CreditsScreen::CreateViews() {
460 using namespace UI;
461 I18NCategory *di = GetI18NCategory("Dialog");
462 I18NCategory *cr = GetI18NCategory("PSPCredits");
464 root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));
465 Button *back = root_->Add(new Button(di->T("Back"), new AnchorLayoutParams(260, 64, NONE, NONE, 10, 10, false)));
466 back->OnClick.Handle(this, &CreditsScreen::OnOK);
467 root_->SetDefaultFocusView(back);
468 #ifndef GOLD
469 root_->Add(new Button(cr->T("Buy Gold"), new AnchorLayoutParams(260, 64, 10, NONE, NONE, 10, false)))->OnClick.Handle(this, &CreditsScreen::OnSupport);
470 #endif
471 root_->Add(new Button(cr->T("PPSSPP Forums"), new AnchorLayoutParams(260, 64, 10, NONE, NONE, 84, false)))->OnClick.Handle(this, &CreditsScreen::OnForums);
472 root_->Add(new Button("www.ppsspp.org", new AnchorLayoutParams(260, 64, 10, NONE, NONE, 158, false)))->OnClick.Handle(this, &CreditsScreen::OnPPSSPPOrg);
473 #ifdef ANDROID
474 root_->Add(new Button(cr->T("Share PPSSPP"), new AnchorLayoutParams(260, 64, NONE, NONE, 10, 84, false)))->OnClick.Handle(this, &CreditsScreen::OnShare);
475 root_->Add(new Button(cr->T("Twitter @PPSSPP_emu"), new AnchorLayoutParams(260, 64, NONE, NONE, 10, 154, false)))->OnClick.Handle(this, &CreditsScreen::OnTwitter);
476 #endif
477 #ifdef GOLD
478 root_->Add(new ImageView(I_ICONGOLD, IS_DEFAULT, new AnchorLayoutParams(100, 64, 10, 10, NONE, NONE, false)));
479 #else
480 root_->Add(new ImageView(I_ICON, IS_DEFAULT, new AnchorLayoutParams(100, 64, 10, 10, NONE, NONE, false)));
481 #endif
484 UI::EventReturn CreditsScreen::OnSupport(UI::EventParams &e) {
485 #ifdef ANDROID
486 LaunchBrowser("market://details?id=org.ppsspp.ppssppgold");
487 #else
488 LaunchBrowser("http://central.ppsspp.org/buygold");
489 #endif
490 return UI::EVENT_DONE;
493 UI::EventReturn CreditsScreen::OnTwitter(UI::EventParams &e) {
494 #ifdef ANDROID
495 System_SendMessage("showTwitter", "PPSSPP_emu");
496 #else
497 LaunchBrowser("https://twitter.com/#!/PPSSPP_emu");
498 #endif
499 return UI::EVENT_DONE;
502 UI::EventReturn CreditsScreen::OnPPSSPPOrg(UI::EventParams &e) {
503 LaunchBrowser("http://www.ppsspp.org");
504 return UI::EVENT_DONE;
507 UI::EventReturn CreditsScreen::OnForums(UI::EventParams &e) {
508 LaunchBrowser("http://forums.ppsspp.org");
509 return UI::EVENT_DONE;
512 UI::EventReturn CreditsScreen::OnShare(UI::EventParams &e) {
513 I18NCategory *cr = GetI18NCategory("PSPCredits");
514 System_SendMessage("sharetext", cr->T("CheckOutPPSSPP", "Check out PPSSPP, the awesome PSP emulator: http://www.ppsspp.org/"));
515 return UI::EVENT_DONE;
518 UI::EventReturn CreditsScreen::OnOK(UI::EventParams &e) {
519 screenManager()->finishDialog(this, DR_OK);
520 return UI::EVENT_DONE;
523 void CreditsScreen::update(InputState &input_state) {
524 UIScreen::update(input_state);
525 UpdateUIState(UISTATE_MENU);
526 if (input_state.pad_buttons_down & PAD_BUTTON_BACK) {
527 screenManager()->finishDialog(this, DR_OK);
529 frames_++;
532 void CreditsScreen::render() {
533 UIScreen::render();
535 I18NCategory *cr = GetI18NCategory("PSPCredits");
537 const char * credits[] = {
538 "PPSSPP",
540 cr->T("title", "A fast and portable PSP emulator"),
543 cr->T("created", "Created by"),
544 "Henrik Rydg\xc3\xa5rd",
547 cr->T("contributors", "Contributors:"),
548 "unknownbrackets",
549 "oioitff",
550 "xsacha",
551 "raven02",
552 "tpunix",
553 "orphis",
554 "sum2012",
555 "mikusp",
556 "aquanull",
557 "The Dax",
558 "bollu",
559 "tmaul",
560 "artart78",
561 "ced2911",
562 "soywiz",
563 "kovensky",
564 "xele",
565 "chaserhjk",
566 "evilcorn",
567 "daniel dressler",
568 "makotech222",
569 "CPkmn",
570 "mgaver",
571 "jeid3",
572 "cinaera/BeaR",
573 "jtraynham",
574 "Kingcom",
575 "arnastia",
576 "lioncash",
577 "JulianoAmaralChaves",
578 "vnctdj",
579 "kaienfr",
580 "shenweip",
581 "Danyal Zia",
582 "Igor Calabria",
583 "Coldbird",
584 "Kyhel",
587 cr->T("specialthanks", "Special thanks to:"),
588 "Maxim for his amazing Atrac3+ decoder work",
589 "Keith Galocy at nVidia (hw, advice)",
590 "Orphis (build server)",
591 "angelxwind (iOS builds)",
592 "W.MS (iOS builds)",
593 "solarmystic (testing)",
594 "all the forum mods",
596 cr->T("this translation by", ""), // Empty string as this is the original :)
597 cr->T("translators1", ""),
598 cr->T("translators2", ""),
599 cr->T("translators3", ""),
600 cr->T("translators4", ""),
601 cr->T("translators5", ""),
602 cr->T("translators6", ""),
604 cr->T("written", "Written in C++ for speed and portability"),
607 cr->T("tools", "Free tools used:"),
608 #ifdef ANDROID
609 "Android SDK + NDK",
610 #elif defined(BLACKBERRY)
611 "Blackberry NDK",
612 #endif
613 #if defined(USING_QT_UI)
614 "Qt",
615 #elif !defined(USING_WIN_UI)
616 "SDL",
617 #endif
618 "CMake",
619 "freetype2",
620 "zlib",
621 "PSP SDK",
624 cr->T("website", "Check out the website:"),
625 "www.ppsspp.org",
626 cr->T("list", "compatibility lists, forums, and development info"),
629 cr->T("check", "Also check out Dolphin, the best Wii/GC emu around:"),
630 "http://www.dolphin-emu.org",
633 cr->T("info1", "PPSSPP is only intended to play games you own."),
634 cr->T("info2", "Please make sure that you own the rights to any games"),
635 cr->T("info3", "you play by owning the UMD or by buying the digital"),
636 cr->T("info4", "download from the PSN store on your real PSP."),
639 cr->T("info5", "PSP is a trademark by Sony, Inc."),
643 // TODO: This is kinda ugly, done on every frame...
644 char temp[256];
645 snprintf(temp, sizeof(temp), "PPSSPP %s", PPSSPP_GIT_VERSION);
646 credits[0] = (const char *)temp;
648 UIContext &dc = *screenManager()->getUIContext();
649 dc.Begin();
650 const Bounds &bounds = dc.GetBounds();
652 const int numItems = ARRAY_SIZE(credits);
653 int itemHeight = 36;
654 int totalHeight = numItems * itemHeight + bounds.h + 200;
655 int y = bounds.y2() - (frames_ % totalHeight);
656 for (int i = 0; i < numItems; i++) {
657 float alpha = linearInOut(y+32, 64, bounds.y2() - 192, 64);
658 if (alpha > 0.0f) {
659 dc.SetFontScale(ease(alpha), ease(alpha));
660 dc.DrawText(credits[i], dc.GetBounds().centerX(), y, whiteAlpha(alpha), ALIGN_HCENTER);
661 dc.SetFontScale(1.0f, 1.0f);
663 y += itemHeight;
666 dc.End();
667 dc.Flush();