1 // Copyright (c) 2012- 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/.
21 #include "base/display.h"
22 #include "base/logging.h"
23 #include "base/colorutil.h"
24 #include "base/timeutil.h"
25 #include "base/NativeApp.h"
26 #include "gfx_es2/glsl_program.h"
27 #include "input/input_state.h"
28 #include "math/curves.h"
31 #include "util/random/rng.h"
32 #include "util/text/utf8.h"
35 #include "../../GPU/ge_constants.h"
36 #include "../../GPU/GPUState.h"
37 #include "../../GPU/GPUInterface.h"
38 #include "../../Core/Config.h"
39 #include "../../Core/CoreParameter.h"
40 #include "../../Core/SaveState.h"
42 #include "MenuScreens.h"
43 #include "EmuScreen.h"
46 #include <QFileDialog>
52 // Ugly communication with NativeApp
53 extern std::string game_title
;
56 static const int symbols
[4] = {
63 static const uint32_t colors
[4] = {
76 static void DrawBackground(float alpha
) {
77 static float xbase
[100] = {0};
78 static float ybase
[100] = {0};
79 if (xbase
[0] == 0.0f
) {
81 for (int i
= 0; i
< 100; i
++) {
82 xbase
[i
] = rng
.F() * dp_xres
;
83 ybase
[i
] = rng
.F() * dp_yres
;
86 glClearColor(0.1f
,0.2f
,0.43f
,1.0f
);
87 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
| GL_STENCIL_BUFFER_BIT
);
88 ui_draw2d
.DrawImageStretch(I_BG
, 0, 0, dp_xres
, dp_yres
);
90 for (int i
= 0; i
< 100; i
++) {
92 float y
= ybase
[i
] + 40*cos(i
* 7.2 + t
* 1.3);
93 float angle
= sin(i
+ t
);
95 ui_draw2d
.DrawImageRotated(symbols
[n
], x
, y
, 1.0f
, angle
, colorAlpha(colors
[n
], alpha
* 0.1f
));
99 // For private alphas, etc.
100 void DrawWatermark() {
101 // ui_draw2d.DrawTextShadow(UBUNTU24, "PRIVATE BUILD", dp_xres / 2, 10, 0xFF0000FF, ALIGN_HCENTER);
104 void LogoScreen::update(InputState
&input_state
) {
106 if (frames_
> 180 || input_state
.pointer_down
[0]) {
107 if (bootFilename_
.size()) {
108 screenManager()->switchScreen(new EmuScreen(bootFilename_
));
110 screenManager()->switchScreen(new MenuScreen());
115 void LogoScreen::render() {
116 float t
= (float)frames_
/ 60.0f
;
119 if (t
> 1.0f
) alpha
= 1.0f
;
120 float alphaText
= alpha
;
121 if (t
> 2.0f
) alphaText
= 3.0f
- t
;
125 DrawBackground(alpha
);
127 ui_draw2d
.SetFontScale(1.5f
, 1.5f
);
128 ui_draw2d
.DrawText(UBUNTU48
, "PPSSPP", dp_xres
/ 2, dp_yres
/ 2 - 30, colorAlpha(0xFFFFFFFF, alphaText
), ALIGN_CENTER
);
129 ui_draw2d
.SetFontScale(1.0f
, 1.0f
);
130 ui_draw2d
.DrawText(UBUNTU24
, "Created by Henrik Rydgard", dp_xres
/ 2, dp_yres
/ 2 + 40, colorAlpha(0xFFFFFFFF, alphaText
), ALIGN_CENTER
);
131 ui_draw2d
.DrawText(UBUNTU24
, "Free Software under GPL 2.0", dp_xres
/ 2, dp_yres
/ 2 + 70, colorAlpha(0xFFFFFFFF, alphaText
), ALIGN_CENTER
);
132 ui_draw2d
.DrawText(UBUNTU24
, "www.ppsspp.org", dp_xres
/ 2, dp_yres
/ 2 + 130, colorAlpha(0xFFFFFFFF, alphaText
), ALIGN_CENTER
);
133 if (bootFilename_
.size()) {
134 ui_draw2d
.DrawText(UBUNTU24
, bootFilename_
.c_str(), dp_xres
/ 2, dp_yres
/ 2 + 180, colorAlpha(0xFFFFFFFF, alphaText
), ALIGN_CENTER
);
140 glsl_bind(UIShader_Get());
141 ui_draw2d
.Flush(UIShader_Get());
145 // ==================
147 // ==================
149 void MenuScreen::update(InputState
&input_state
) {
153 void MenuScreen::render() {
156 DrawBackground(1.0f
);
158 double xoff
= 150 - frames_
* frames_
* 0.4f
;
161 if (frames_
> 200) // seems the above goes nuts after a while...
164 int w
= LARGE_BUTTON_WIDTH
+ 40;
166 ui_draw2d
.DrawTextShadow(UBUNTU48
, "PPSSPP", dp_xres
+ xoff
- w
/2, 80, 0xFFFFFFFF, ALIGN_HCENTER
| ALIGN_BOTTOM
);
167 ui_draw2d
.SetFontScale(0.7f
, 0.7f
);
168 ui_draw2d
.DrawTextShadow(UBUNTU24
, PPSSPP_VERSION_STR
, dp_xres
+ xoff
, 80, 0xFFFFFFFF, ALIGN_RIGHT
| ALIGN_BOTTOM
);
169 ui_draw2d
.SetFontScale(1.0f
, 1.0f
);
170 VLinear
vlinear(dp_xres
+ xoff
, 95, 20);
172 if (UIButton(GEN_ID
, vlinear
, w
, "Load...", ALIGN_RIGHT
)) {
173 #if defined(USING_QT_UI) && defined(__SYMBIAN32__)
174 QString fileName
= QFileDialog::getOpenFileName(NULL
, "Load ROM", g_Config
.currentDirectory
.c_str(), "PSP ROMs (*.iso *.cso *.pbp *.elf)");
175 if (QFile::exists(fileName
)) {
177 g_Config
.currentDirectory
= newPath
.filePath(fileName
).toStdString();
179 screenManager()->switchScreen(new EmuScreen(fileName
.toStdString()));
182 FileSelectScreenOptions options
;
183 options
.allowChooseDirectory
= true;
184 options
.filter
= "iso:cso:pbp:elf:prx:";
185 options
.folderIcon
= I_ICON_FOLDER
;
186 options
.iconMapping
["iso"] = I_ICON_UMD
;
187 options
.iconMapping
["cso"] = I_ICON_UMD
;
188 options
.iconMapping
["pbp"] = I_ICON_EXE
;
189 options
.iconMapping
["elf"] = I_ICON_EXE
;
190 screenManager()->switchScreen(new FileSelectScreen(options
));
195 if (UIButton(GEN_ID
, vlinear
, w
, "Settings", ALIGN_RIGHT
)) {
196 screenManager()->switchScreen(new SettingsScreen());
200 if (UIButton(GEN_ID
, vlinear
, w
, "Credits", ALIGN_RIGHT
)) {
201 screenManager()->switchScreen(new CreditsScreen());
205 if (UIButton(GEN_ID
, vlinear
, w
, "Exit", ALIGN_RIGHT
)) {
206 // TODO: Save when setting changes, rather than when we quit
208 // TODO: Need a more elegant way to quit
212 if (UIButton(GEN_ID
, vlinear
, w
, "www.ppsspp.org", ALIGN_RIGHT
)) {
213 LaunchBrowser("http://www.ppsspp.org/");
220 glsl_bind(UIShader_Get());
221 ui_draw2d
.Flush(UIShader_Get());
225 void InGameMenuScreen::update(InputState
&input
) {
226 if (input
.pad_buttons_down
& PAD_BUTTON_BACK
) {
227 screenManager()->finishDialog(this, DR_CANCEL
);
231 void InGameMenuScreen::render() {
234 DrawBackground(1.0f
);
237 if (UTF8StringHasNonASCII(game_title
.c_str())) {
238 title
= "(can't display japanese title)";
240 title
= game_title
.c_str();
243 ui_draw2d
.DrawText(UBUNTU48
, title
, dp_xres
/ 2, 30, 0xFFFFFFFF, ALIGN_HCENTER
);
247 UICheckBox(GEN_ID
, x
, y
+= 50, "Show Debug Statistics", ALIGN_TOPLEFT
, &g_Config
.bShowDebugStats
);
248 UICheckBox(GEN_ID
, x
, y
+= 50, "Hardware Transform", ALIGN_TOPLEFT
, &g_Config
.bHardwareTransform
);
250 // TODO: Add UI for more than one slot.
251 VLinear
vlinear1(x
, y
+ 80, 20);
252 UIText(UBUNTU24
, vlinear1
, "Save states are experimental (and large)", 0xFFFFFFFF);
253 if (UIButton(GEN_ID
, vlinear1
, LARGE_BUTTON_WIDTH
, "Save State", ALIGN_LEFT
)) {
254 SaveState::SaveSlot(0, 0, 0);
255 screenManager()->finishDialog(this, DR_CANCEL
);
257 if (UIButton(GEN_ID
, vlinear1
, LARGE_BUTTON_WIDTH
, "Load State", ALIGN_LEFT
)) {
258 SaveState::LoadSlot(0, 0, 0);
259 screenManager()->finishDialog(this, DR_CANCEL
);
262 VLinear
vlinear(dp_xres
- 10, 160, 20);
263 if (UIButton(GEN_ID
, vlinear
, LARGE_BUTTON_WIDTH
, "Continue", ALIGN_RIGHT
)) {
264 screenManager()->finishDialog(this, DR_CANCEL
);
266 if (UIButton(GEN_ID
, vlinear
, LARGE_BUTTON_WIDTH
, "Return to Menu", ALIGN_RIGHT
)) {
267 screenManager()->finishDialog(this, DR_OK
);
270 if (UIButton(GEN_ID
, Pos(dp_xres
- 10, dp_yres
- 10), LARGE_BUTTON_WIDTH
*2, "Debug: Dump Next Frame", ALIGN_BOTTOMRIGHT
)) {
271 gpu
->DumpNextFrame();
277 glsl_bind(UIShader_Get());
278 ui_draw2d
.Flush(UIShader_Get());
281 void SettingsScreen::update(InputState
&input
) {
282 if (input
.pad_buttons_down
& PAD_BUTTON_BACK
) {
284 screenManager()->switchScreen(new MenuScreen());
288 void SettingsScreen::render() {
291 DrawBackground(1.0f
);
293 ui_draw2d
.DrawText(UBUNTU48
, "Settings", dp_xres
/ 2, 20, 0xFFFFFFFF, ALIGN_HCENTER
);
295 // TODO: Need to add tabs soon...
296 // VLinear vlinear(10, 80, 10);
301 UICheckBox(GEN_ID
, x
, y
+= stride
, "Sound Emulation", ALIGN_TOPLEFT
, &g_Config
.bEnableSound
);
302 UICheckBox(GEN_ID
, x
, y
+= stride
, "Buffered Rendering", ALIGN_TOPLEFT
, &g_Config
.bBufferedRendering
);
303 if (g_Config
.bBufferedRendering
) {
304 bool doubleRes
= g_Config
.iWindowZoom
== 2;
305 UICheckBox(GEN_ID
, x
+ 50, y
+= stride
, "2x Render Resolution", ALIGN_TOPLEFT
, &doubleRes
);
306 g_Config
.iWindowZoom
= doubleRes
? 2 : 1;
308 UICheckBox(GEN_ID
, x
, y
+= stride
, "Hardware Transform", ALIGN_TOPLEFT
, &g_Config
.bHardwareTransform
);
309 UICheckBox(GEN_ID
, x
, y
+= stride
, "Draw using Stream VBO", ALIGN_TOPLEFT
, &g_Config
.bUseVBO
);
310 UICheckBox(GEN_ID
, x
, y
+= stride
, "Vertex Cache", ALIGN_TOPLEFT
, &g_Config
.bVertexCache
);
312 bool useJit
= g_Config
.iCpuCore
== CPU_JIT
;
313 UICheckBox(GEN_ID
, x
, y
+= stride
, "JIT (Dynarec)", ALIGN_TOPLEFT
, &useJit
);
314 if (g_Config
.iCpuCore
== CPU_JIT
)
315 UICheckBox(GEN_ID
, x
+ 450, y
, "Fastmem (may crash)", ALIGN_TOPLEFT
, &g_Config
.bFastMemory
);
316 g_Config
.iCpuCore
= useJit
? CPU_JIT
: CPU_INTERPRETER
;
317 // ui_draw2d.DrawText(UBUNTU48, "much faster JIT coming later", x, y+=50, 0xcFFFFFFF, ALIGN_LEFT);
318 UICheckBox(GEN_ID
, x
, y
+= stride
, "On-screen Touch Controls", ALIGN_TOPLEFT
, &g_Config
.bShowTouchControls
);
319 if (g_Config
.bShowTouchControls
) {
320 UICheckBox(GEN_ID
, x
+ 450, y
, "Large Controls", ALIGN_TOPLEFT
, &g_Config
.bLargeControls
);
321 UICheckBox(GEN_ID
, x
+ 50, y
+= stride
, "Show Analog Stick", ALIGN_TOPLEFT
, &g_Config
.bShowAnalogStick
);
323 // UICheckBox(GEN_ID, x, y += stride, "Draw raw framebuffer (for some homebrew)", ALIGN_TOPLEFT, &g_Config.bDisplayFramebuffer);
325 if (UIButton(GEN_ID
, Pos(dp_xres
- 10, dp_yres
-10), LARGE_BUTTON_WIDTH
, "Back", ALIGN_RIGHT
| ALIGN_BOTTOM
)) {
326 screenManager()->switchScreen(new MenuScreen());
331 glsl_bind(UIShader_Get());
332 ui_draw2d
.Flush(UIShader_Get());
336 class FileListAdapter
: public UIListAdapter
{
338 FileListAdapter(const FileSelectScreenOptions
&options
, const std::vector
<FileInfo
> *items
) : options_(options
), items_(items
) {}
339 virtual size_t getCount() const { return items_
->size(); }
340 virtual void drawItem(int item
, int x
, int y
, int w
, int h
, bool active
) const;
343 const FileSelectScreenOptions
&options_
;
344 const std::vector
<FileInfo
> *items_
;
347 void FileListAdapter::drawItem(int item
, int x
, int y
, int w
, int h
, bool selected
) const
350 if ((*items_
)[item
].isDirectory
) {
351 icon
= options_
.folderIcon
;
353 std::string extension
= getFileExtension((*items_
)[item
].name
);
354 auto iter
= options_
.iconMapping
.find(extension
);
355 if (iter
!= options_
.iconMapping
.end())
358 int iconSpace
= this->itemHeight(item
);
359 ui_draw2d
.DrawImage2GridH(selected
? I_BUTTON_SELECTED
: I_BUTTON
, x
, y
, x
+ w
);
360 ui_draw2d
.DrawTextShadow(UBUNTU24
, (*items_
)[item
].name
.c_str(), x
+ UI_SPACE
+ iconSpace
, y
+ 25, 0xFFFFFFFF, ALIGN_LEFT
| ALIGN_VCENTER
);
362 ui_draw2d
.DrawImage(icon
, x
+ UI_SPACE
, y
+ 25, 1.0f
, 0xFFFFFFFF, ALIGN_VCENTER
| ALIGN_LEFT
);
366 FileSelectScreen::FileSelectScreen(const FileSelectScreenOptions
&options
) : options_(options
) {
367 currentDirectory_
= g_Config
.currentDirectory
;
371 void FileSelectScreen::updateListing() {
373 getFilesInDir(currentDirectory_
.c_str(), &listing_
, options_
.filter
);
374 g_Config
.currentDirectory
= currentDirectory_
;
375 list_
.contentChanged();
378 void FileSelectScreen::update(InputState
&input_state
) {
379 if (input_state
.pad_buttons_down
& PAD_BUTTON_BACK
) {
381 screenManager()->switchScreen(new MenuScreen());
385 void FileSelectScreen::render() {
386 FileListAdapter
adapter(options_
, &listing_
);
390 DrawBackground(1.0f
);
392 if (list_
.Do(GEN_ID
, 10, BUTTON_HEIGHT
+ 20, dp_xres
-20, dp_yres
- BUTTON_HEIGHT
- 30, &adapter
)) {
393 if (listing_
[list_
.selected
].isDirectory
) {
394 currentDirectory_
= listing_
[list_
.selected
].fullName
;
395 ILOG("%s", currentDirectory_
.c_str());
399 std::string boot_filename
= listing_
[list_
.selected
].fullName
;
400 ILOG("Selected: %i : %s", list_
.selected
, boot_filename
.c_str());
404 screenManager()->switchScreen(new EmuScreen(boot_filename
));
408 ui_draw2d
.DrawImageStretch(I_BUTTON
, 0, 0, dp_xres
, 70);
410 if (UIButton(GEN_ID
, Pos(10,10), SMALL_BUTTON_WIDTH
, "Up", ALIGN_TOPLEFT
)) {
411 currentDirectory_
= getDir(currentDirectory_
);
414 ui_draw2d
.DrawTextShadow(UBUNTU24
, currentDirectory_
.c_str(), 20 + SMALL_BUTTON_WIDTH
, 10 + 25, 0xFFFFFFFF, ALIGN_LEFT
| ALIGN_VCENTER
);
417 if (UIButton(GEN_ID, Pos(dp_xres - 10, 10), SMALL_BUTTON_WIDTH, "Back", ALIGN_RIGHT)) {
419 screenManager()->switchScreen(new MenuScreen());
423 glsl_bind(UIShader_Get());
424 ui_draw2d
.Flush(UIShader_Get());
427 void CreditsScreen::update(InputState
&input_state
) {
428 if (input_state
.pad_buttons_down
& PAD_BUTTON_BACK
) {
429 screenManager()->switchScreen(new MenuScreen());
434 static const char *credits
[] =
436 "PPSSPP " PPSSPP_VERSION_STR
,
439 "A fast and portable PSP emulator",
440 "(well, an early prototype of that)",
442 "Created by Henrik Rydgard",
455 "Written in C++ for speed and portability",
461 #elif defined(BLACKBERRY)
463 #elif defined(USING_QT_UI)
474 "Check out the website:",
476 "compatibility lists, forums, and development info",
479 "Also check out Dolphin, the best Wii/GC emu around:",
480 "http://www.dolphin-emu.org",
483 "PPSSPP is intended for educational purposes only.",
485 "Please make sure that you own the rights to any games",
486 "you play by owning the UMD or buying the digital",
487 "download from the PSN store on your real PSP.",
490 "PSP is a trademark by Sony, Inc.",
493 void CreditsScreen::render() {
496 DrawBackground(1.0f
);
498 const int numItems
= ARRAY_SIZE(credits
);
500 int totalHeight
= numItems
* itemHeight
+ dp_yres
+ 200;
501 int y
= dp_yres
- (frames_
% totalHeight
);
502 for (int i
= 0; i
< numItems
; i
++) {
503 float alpha
= linearInOut(y
+32, 64, dp_yres
- 192, 64);
505 UIText(dp_xres
/2, y
, credits
[i
], whiteAlpha(alpha
), ease(alpha
), ALIGN_HCENTER
);
510 if (UIButton(GEN_ID
, Pos(dp_xres
- 10, dp_yres
- 10), 200, "Back", ALIGN_BOTTOMRIGHT
)) {
511 screenManager()->switchScreen(new MenuScreen());
516 glsl_bind(UIShader_Get());
517 ui_draw2d
.Flush(UIShader_Get());
520 void ErrorScreen::update(InputState
&input_state
) {
521 if (input_state
.pad_buttons_down
& PAD_BUTTON_BACK
) {
522 screenManager()->finishDialog(this, DR_OK
);
526 void ErrorScreen::render()
530 DrawBackground(1.0f
);
532 ui_draw2d
.DrawText(UBUNTU48
, errorTitle_
.c_str(), dp_xres
/ 2, 30, 0xFFFFFFFF, ALIGN_HCENTER
);
533 ui_draw2d
.DrawText(UBUNTU24
, errorMessage_
.c_str(), 40, 120, 0xFFFFFFFF, ALIGN_LEFT
);
535 if (UIButton(GEN_ID
, Pos(dp_xres
- 10, dp_yres
- 10), 200, "Back", ALIGN_BOTTOMRIGHT
)) {
536 screenManager()->finishDialog(this, DR_OK
);
541 glsl_bind(UIShader_Get());
542 ui_draw2d
.Flush(UIShader_Get());