Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/UI/GameScreen.cpp
3185 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 <algorithm>
19
20
#include "ppsspp_config.h"
21
22
#include "Common/Render/DrawBuffer.h"
23
#include "Common/UI/Context.h"
24
#include "Common/UI/View.h"
25
#include "Common/UI/ViewGroup.h"
26
27
#include "Common/Data/Text/I18n.h"
28
#include "Common/Data/Text/Parsers.h"
29
#include "Common/Data/Encoding/Utf8.h"
30
#include "Common/File/FileUtil.h"
31
#include "Common/StringUtils.h"
32
#include "Common/System/System.h"
33
#include "Common/System/OSD.h"
34
#include "Common/System/Request.h"
35
#include "Common/System/NativeApp.h"
36
#include "Core/Config.h"
37
#include "Core/Reporting.h"
38
#include "Core/System.h"
39
#include "Core/Loaders.h"
40
#include "Core/Util/GameDB.h"
41
#include "Core/HLE/Plugins.h"
42
#include "Core/Util/RecentFiles.h"
43
#include "UI/OnScreenDisplay.h"
44
#include "UI/CwCheatScreen.h"
45
#include "UI/EmuScreen.h"
46
#include "UI/GameScreen.h"
47
#include "UI/GameSettingsScreen.h"
48
#include "UI/GameInfoCache.h"
49
#include "UI/MiscScreens.h"
50
#include "UI/MainScreen.h"
51
#include "UI/BackgroundAudio.h"
52
#include "UI/SavedataScreen.h"
53
#include "Core/Reporting.h"
54
55
GameScreen::GameScreen(const Path &gamePath, bool inGame) : UIDialogScreenWithGameBackground(gamePath), inGame_(inGame) {
56
g_BackgroundAudio.SetGame(gamePath);
57
System_PostUIMessage(UIMessage::GAME_SELECTED, gamePath.ToString());
58
}
59
60
GameScreen::~GameScreen() {
61
if (CRC32string == "...") {
62
Reporting::CancelCRC();
63
}
64
System_PostUIMessage(UIMessage::GAME_SELECTED, "");
65
}
66
67
template <typename I> std::string int2hexstr(I w, size_t hex_len = sizeof(I) << 1) {
68
static const char* digits = "0123456789ABCDEF";
69
std::string rc(hex_len, '0');
70
for (size_t i = 0, j = (hex_len - 1) * 4; i < hex_len; ++i, j -= 4)
71
rc[i] = digits[(w >> j) & 0x0f];
72
return rc;
73
}
74
75
void GameScreen::update() {
76
UIScreen::update();
77
78
// Has the user requested a CRC32?
79
if (CRC32string == "...") {
80
// Wait until the CRC32 is ready. It might take time on some devices.
81
if (Reporting::HasCRC(gamePath_)) {
82
uint32_t crcvalue = Reporting::RetrieveCRC(gamePath_);
83
CRC32string = int2hexstr(crcvalue);
84
if (tvCRC_) {
85
tvCRC_->SetVisibility(UI::V_VISIBLE);
86
tvCRC_->SetText(CRC32string);
87
}
88
if (tvCRCCopy_) {
89
tvCRCCopy_->SetVisibility(UI::V_VISIBLE);
90
}
91
if (btnCalcCRC_) {
92
btnCalcCRC_->SetVisibility(UI::V_GONE);
93
}
94
}
95
}
96
}
97
98
void GameScreen::CreateViews() {
99
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO | GameInfoFlags::ICON | GameInfoFlags::PIC0 | GameInfoFlags::PIC1);
100
101
auto di = GetI18NCategory(I18NCat::DIALOG);
102
auto ga = GetI18NCategory(I18NCat::GAME);
103
104
// Information in the top left.
105
// Back button to the bottom left.
106
// Scrolling action menu to the right.
107
using namespace UI;
108
109
Margins actionMenuMargins(0, 15, 15, 0);
110
111
root_ = new LinearLayout(ORIENT_HORIZONTAL);
112
113
ViewGroup *leftColumn = new AnchorLayout(new LinearLayoutParams(1.0f));
114
root_->Add(leftColumn);
115
116
bool fileTypeSupportCRC = false;
117
if (info) {
118
switch (info->fileType) {
119
case IdentifiedFileType::PSP_PBP:
120
case IdentifiedFileType::PSP_PBP_DIRECTORY:
121
case IdentifiedFileType::PSP_ISO_NP:
122
case IdentifiedFileType::PSP_ISO:
123
fileTypeSupportCRC = true;
124
break;
125
126
default:
127
break;
128
}
129
}
130
131
leftColumn->Add(new Choice(di->T("Back"), "", false, new AnchorLayoutParams(150, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle(this, &GameScreen::OnSwitchBack);
132
if (info->Ready(GameInfoFlags::PARAM_SFO)) {
133
ViewGroup *badgeHolder = new LinearLayout(ORIENT_HORIZONTAL, new AnchorLayoutParams(10, 10, 110, NONE));
134
LinearLayout *mainGameInfo = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f));
135
mainGameInfo->SetSpacing(3.0f);
136
137
// Need an explicit size here because homebrew uses screenshots as icons.
138
badgeHolder->Add(new GameIconView(gamePath_, 2.0f, new LinearLayoutParams(144 * 2, 80 * 2, UI::Margins(0))));
139
badgeHolder->Add(mainGameInfo);
140
141
leftColumn->Add(badgeHolder);
142
143
LinearLayout *infoLayout = new LinearLayout(ORIENT_VERTICAL, new AnchorLayoutParams(10, 200, NONE, NONE));
144
leftColumn->Add(infoLayout);
145
146
// TODO: Add non-translated title here if available in gameDB.
147
tvTitle_ = mainGameInfo->Add(new TextView(info->GetTitle(), ALIGN_LEFT | FLAG_WRAP_TEXT, false, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
148
tvTitle_->SetShadow(true);
149
tvID_ = mainGameInfo->Add(new TextView("", ALIGN_LEFT | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
150
tvID_->SetShadow(true);
151
tvRegion_ = mainGameInfo->Add(new TextView("", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
152
tvRegion_->SetShadow(true);
153
tvGameSize_ = mainGameInfo->Add(new TextView("...", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
154
tvGameSize_->SetShadow(true);
155
156
// This one doesn't need to be updated.
157
infoLayout->Add(new TextView(gamePath_.ToVisualString(), ALIGN_LEFT | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetShadow(true);
158
tvSaveDataSize_ = infoLayout->Add(new TextView("...", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
159
tvSaveDataSize_->SetShadow(true);
160
tvInstallDataSize_ = infoLayout->Add(new TextView("", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
161
tvInstallDataSize_->SetShadow(true);
162
tvInstallDataSize_->SetVisibility(V_GONE);
163
tvPlayTime_ = infoLayout->Add(new TextView("", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
164
tvPlayTime_->SetShadow(true);
165
tvPlayTime_->SetVisibility(V_GONE);
166
167
LinearLayout *crcHoriz = infoLayout->Add(new LinearLayout(ORIENT_HORIZONTAL));
168
169
if (fileTypeSupportCRC) {
170
// CRC button makes sense.
171
tvCRC_ = crcHoriz->Add(new TextView("", ALIGN_LEFT, true, new LinearLayoutParams(0.0, G_VCENTER)));
172
tvCRC_->SetShadow(true);
173
Visibility crcVisibility = Reporting::HasCRC(gamePath_) ? V_VISIBLE : V_GONE;
174
tvCRC_->SetVisibility(crcVisibility);
175
if (System_GetPropertyBool(SYSPROP_HAS_TEXT_CLIPBOARD)) {
176
tvCRCCopy_ = crcHoriz->Add(new Button(di->T("Copy to clipboard"), new LinearLayoutParams(0.0, G_VCENTER)));
177
tvCRCCopy_->OnClick.Add([this](UI::EventParams &) {
178
u32 crc = Reporting::RetrieveCRC(gamePath_);
179
char buffer[16];
180
snprintf(buffer, sizeof(buffer), "%08X", crc);
181
System_CopyStringToClipboard(buffer);
182
// Success indication. Not worth a translatable string.
183
g_OSD.Show(OSDType::MESSAGE_SUCCESS, buffer, 1.0f);
184
return UI::EVENT_DONE;
185
});
186
tvCRCCopy_->SetVisibility(crcVisibility);
187
tvCRCCopy_->SetScale(0.82f);
188
} else {
189
tvCRCCopy_ = nullptr;
190
}
191
}
192
193
tvVerified_ = infoLayout->Add(new NoticeView(NoticeLevel::INFO, ga->T("Click \"Calculate CRC\" to verify ISO"), "", new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
194
tvVerified_->SetVisibility(UI::V_GONE);
195
tvVerified_->SetSquishy(true);
196
197
// Show plugin info, if any. Later might add checkboxes.
198
auto plugins = HLEPlugins::FindPlugins(info->id, g_Config.sLanguageIni);
199
if (!plugins.empty()) {
200
auto sy = GetI18NCategory(I18NCat::SYSTEM);
201
infoLayout->Add(new TextView(sy->T("Plugins"), ALIGN_LEFT, true));
202
for (const auto &plugin : plugins) {
203
infoLayout->Add(new TextView(ApplySafeSubstitutions("* %1", plugin.name), ALIGN_LEFT, true));
204
}
205
}
206
} else {
207
tvTitle_ = nullptr;
208
tvID_ = nullptr;
209
tvGameSize_ = nullptr;
210
tvSaveDataSize_ = nullptr;
211
tvInstallDataSize_ = nullptr;
212
tvRegion_ = nullptr;
213
tvPlayTime_ = nullptr;
214
tvCRC_ = nullptr;
215
tvCRCCopy_ = nullptr;
216
tvVerified_ = nullptr;
217
}
218
219
ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins));
220
root_->Add(rightColumn);
221
222
LinearLayout *rightColumnItems = new LinearLayout(ORIENT_VERTICAL);
223
rightColumnItems->SetSpacing(0.0f);
224
rightColumn->Add(rightColumnItems);
225
226
if (!inGame_) {
227
rightColumnItems->Add(new Choice(ga->T("Play")))->OnClick.Handle(this, &GameScreen::OnPlay);
228
}
229
230
btnGameSettings_ = rightColumnItems->Add(new Choice(ga->T("Game Settings")));
231
btnGameSettings_->OnClick.Handle(this, &GameScreen::OnGameSettings);
232
if (inGame_)
233
btnGameSettings_->SetEnabled(false);
234
235
btnDeleteGameConfig_ = rightColumnItems->Add(new Choice(ga->T("Delete Game Config")));
236
btnDeleteGameConfig_->OnClick.Handle(this, &GameScreen::OnDeleteConfig);
237
if (inGame_)
238
btnDeleteGameConfig_->SetEnabled(false);
239
240
btnCreateGameConfig_ = rightColumnItems->Add(new Choice(ga->T("Create Game Config")));
241
btnCreateGameConfig_->OnClick.Handle(this, &GameScreen::OnCreateConfig);
242
if (inGame_)
243
btnCreateGameConfig_->SetEnabled(false);
244
245
btnGameSettings_->SetVisibility(V_GONE);
246
btnDeleteGameConfig_->SetVisibility(V_GONE);
247
btnCreateGameConfig_->SetVisibility(V_GONE);
248
249
btnDeleteSaveData_ = new Choice(ga->T("Delete Save Data"));
250
rightColumnItems->Add(btnDeleteSaveData_)->OnClick.Handle(this, &GameScreen::OnDeleteSaveData);
251
btnDeleteSaveData_->SetVisibility(V_GONE);
252
253
otherChoices_.clear();
254
255
// Don't want to be able to delete the game while it's running.
256
Choice *deleteChoice = rightColumnItems->Add(AddOtherChoice(new Choice(ga->T("Delete Game"))));
257
deleteChoice->OnClick.Handle(this, &GameScreen::OnDeleteGame);
258
if (inGame_) {
259
deleteChoice->SetEnabled(false);
260
}
261
if (System_GetPropertyBool(SYSPROP_CAN_CREATE_SHORTCUT)) {
262
rightColumnItems->Add(AddOtherChoice(new Choice(ga->T("Create Shortcut"))))->OnClick.Add([=](UI::EventParams &e) {
263
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO);
264
if (info->Ready(GameInfoFlags::PARAM_SFO)) {
265
// TODO: Should we block on Ready?
266
System_CreateGameShortcut(gamePath_, info->GetTitle());
267
}
268
return UI::EVENT_DONE;
269
});
270
}
271
272
// TODO: This is synchronous, bad!
273
if (g_recentFiles.ContainsFile(gamePath_.ToString())) {
274
Choice *removeButton = rightColumnItems->Add(AddOtherChoice(new Choice(ga->T("Remove From Recent"))));
275
removeButton->OnClick.Handle(this, &GameScreen::OnRemoveFromRecent);
276
if (inGame_) {
277
removeButton->SetEnabled(false);
278
}
279
}
280
281
if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) {
282
rightColumnItems->Add(AddOtherChoice(new Choice(di->T("Show in folder"))))->OnClick.Add([this](UI::EventParams &e) {
283
System_ShowFileInFolder(gamePath_);
284
return UI::EVENT_DONE;
285
});
286
}
287
288
if (g_Config.bEnableCheats) {
289
auto pa = GetI18NCategory(I18NCat::PAUSE);
290
rightColumnItems->Add(AddOtherChoice(new Choice(pa->T("Cheats"))))->OnClick.Handle(this, &GameScreen::OnCwCheat);
291
}
292
293
btnSetBackground_ = rightColumnItems->Add(new Choice(ga->T("Use UI background")));
294
btnSetBackground_->OnClick.Handle(this, &GameScreen::OnSetBackground);
295
btnSetBackground_->SetVisibility(V_GONE);
296
297
isHomebrew_ = info && info->region == GameRegion::HOMEBREW;
298
if (fileTypeSupportCRC && !isHomebrew_ && !Reporting::HasCRC(gamePath_) ) {
299
btnCalcCRC_ = rightColumnItems->Add(new ChoiceWithValueDisplay(&CRC32string, ga->T("Calculate CRC"), I18NCat::NONE));
300
btnCalcCRC_->OnClick.Handle(this, &GameScreen::OnDoCRC32);
301
} else {
302
btnCalcCRC_ = nullptr;
303
}
304
}
305
306
UI::Choice *GameScreen::AddOtherChoice(UI::Choice *choice) {
307
otherChoices_.push_back(choice);
308
// While loading.
309
choice->SetVisibility(UI::V_GONE);
310
return choice;
311
}
312
313
UI::EventReturn GameScreen::OnCreateConfig(UI::EventParams &e) {
314
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO);
315
if (!info->Ready(GameInfoFlags::PARAM_SFO)) {
316
return UI::EVENT_SKIPPED;
317
}
318
g_Config.createGameConfig(info->id);
319
g_Config.saveGameConfig(info->id, info->GetTitle());
320
info->hasConfig = true;
321
322
screenManager()->topScreen()->RecreateViews();
323
return UI::EVENT_DONE;
324
}
325
326
UI::EventReturn GameScreen::OnDeleteConfig(UI::EventParams &e) {
327
auto di = GetI18NCategory(I18NCat::DIALOG);
328
const bool trashAvailable = System_GetPropertyBool(SYSPROP_HAS_TRASH_BIN);
329
screenManager()->push(
330
new PromptScreen(gamePath_, di->T("DeleteConfirmGameConfig", "Do you really want to delete the settings for this game?"), trashAvailable ? di->T("Move to trash") : di->T("Delete"), di->T("Cancel"),
331
[this](bool result) {
332
if (result) {
333
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO);
334
if (!info->Ready(GameInfoFlags::PARAM_SFO)) {
335
return;
336
}
337
g_Config.deleteGameConfig(info->id);
338
info->hasConfig = false;
339
screenManager()->RecreateAllViews();
340
}
341
}));
342
return UI::EVENT_DONE;
343
}
344
345
ScreenRenderFlags GameScreen::render(ScreenRenderMode mode) {
346
ScreenRenderFlags flags = UIScreen::render(mode);
347
348
auto ga = GetI18NCategory(I18NCat::GAME);
349
350
UIContext &dc = *screenManager()->getUIContext();
351
Draw::DrawContext *draw = dc.GetDrawContext();
352
353
float maxY = 0;
354
// NOTE: This won't be correct the first frame.
355
auto updateMaxY = [&](UI::View *v) {
356
maxY = std::max(maxY, v->GetBounds().y2());
357
};
358
359
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(draw, gamePath_, GameInfoFlags::PIC1 | GameInfoFlags::SIZE | GameInfoFlags::UNCOMPRESSED_SIZE);
360
361
if (tvTitle_) {
362
tvTitle_->SetText(info->GetTitle());
363
updateMaxY(tvTitle_);
364
}
365
366
if (info->Ready(GameInfoFlags::SIZE | GameInfoFlags::UNCOMPRESSED_SIZE)) {
367
char temp[256];
368
if (tvGameSize_) {
369
snprintf(temp, sizeof(temp), "%s: %s", ga->T_cstr("Game"), NiceSizeFormat(info->gameSizeOnDisk).c_str());
370
if (info->gameSizeUncompressed != info->gameSizeOnDisk) {
371
size_t len = strlen(temp);
372
snprintf(temp + len, sizeof(temp) - len, " (%s: %s)", ga->T_cstr("Uncompressed"), NiceSizeFormat(info->gameSizeUncompressed).c_str());
373
}
374
tvGameSize_->SetText(temp);
375
updateMaxY(tvGameSize_);
376
}
377
if (tvSaveDataSize_) {
378
if (info->saveDataSize > 0) {
379
snprintf(temp, sizeof(temp), "%s: %s", ga->T_cstr("SaveData"), NiceSizeFormat(info->saveDataSize).c_str());
380
tvSaveDataSize_->SetText(temp);
381
} else {
382
tvSaveDataSize_->SetVisibility(UI::V_GONE);
383
}
384
updateMaxY(tvSaveDataSize_);
385
}
386
if (info->installDataSize > 0 && tvInstallDataSize_) {
387
snprintf(temp, sizeof(temp), "%s: %1.2f %s", ga->T_cstr("InstallData"), (float) (info->installDataSize) / 1024.f / 1024.f, ga->T_cstr("MB"));
388
tvInstallDataSize_->SetText(temp);
389
tvInstallDataSize_->SetVisibility(UI::V_VISIBLE);
390
}
391
updateMaxY(tvInstallDataSize_);
392
}
393
394
if (tvRegion_) {
395
if (info->region == GameRegion::OTHER) {
396
tvRegion_->SetText(ga->T("Homebrew"));
397
} else {
398
tvRegion_->SetText(ga->T(GameRegionToString(info->region)));
399
}
400
updateMaxY(tvRegion_);
401
}
402
403
if (tvPlayTime_) {
404
std::string str;
405
if (g_Config.TimeTracker().GetPlayedTimeString(info->id, &str)) {
406
tvPlayTime_->SetText(str);
407
tvPlayTime_->SetVisibility(UI::V_VISIBLE);
408
}
409
updateMaxY(tvPlayTime_);
410
}
411
412
if (tvCRC_ && Reporting::HasCRC(gamePath_)) {
413
auto rp = GetI18NCategory(I18NCat::REPORTING);
414
uint32_t crcVal = Reporting::RetrieveCRC(gamePath_);
415
std::string crc = StringFromFormat("%08X", crcVal);
416
tvCRC_->SetText(ReplaceAll(rp->T("FeedbackCRCValue", "Disc CRC: %1"), "%1", crc));
417
tvCRC_->SetVisibility(UI::V_VISIBLE);
418
if (tvCRCCopy_) {
419
tvCRCCopy_->SetVisibility(UI::V_VISIBLE);
420
}
421
422
// Let's check the CRC in the game database, looking up the ID and also matching the crc.
423
std::vector<GameDBInfo> dbInfos;
424
if (tvVerified_ && info->Ready(GameInfoFlags::PARAM_SFO) && g_gameDB.GetGameInfos(info->id_version, &dbInfos)) {
425
bool found = false;
426
for (auto &dbInfo : dbInfos) {
427
if (dbInfo.crc == crcVal) {
428
found = true;
429
}
430
}
431
if (found) {
432
tvVerified_->SetText(ga->T("ISO OK according to the ReDump project"));
433
tvVerified_->SetLevel(NoticeLevel::SUCCESS);
434
tvVerified_->SetVisibility(UI::V_VISIBLE);
435
} else {
436
// Like the other messages below, disabled until we have a database we have confidence in.
437
// tvVerified_->SetText(ga->T("CRC checksum does not match, bad or modified ISO"));
438
// tvVerified_->SetLevel(NoticeLevel::ERROR);
439
tvVerified_->SetVisibility(UI::V_GONE);
440
}
441
} else if (tvVerified_) {
442
// tvVerified_->SetText(ga->T("Game ID unknown - not in the ReDump database"));
443
// tvVerified_->SetVisibility(UI::V_VISIBLE);
444
// tvVerified_->SetLevel(NoticeLevel::WARN);
445
tvVerified_->SetVisibility(UI::V_GONE);
446
}
447
448
updateMaxY(tvCRC_);
449
updateMaxY(tvVerified_);
450
} else if (!isHomebrew_) {
451
GameDBInfo dbInfo;
452
if (tvVerified_) {
453
std::vector<GameDBInfo> dbInfos;
454
if (info->Ready(GameInfoFlags::PARAM_SFO) && !g_gameDB.GetGameInfos(info->id_version, &dbInfos)) {
455
// tvVerified_->SetText(ga->T("Game ID unknown - not in the ReDump database"));
456
// tvVerified_->SetVisibility(UI::V_VISIBLE);
457
// tvVerified_->SetLevel(NoticeLevel::WARN);
458
} else if (info->Ready(GameInfoFlags::UNCOMPRESSED_SIZE) && info->gameSizeUncompressed != 0) { // don't do this check if info still pending
459
bool found = false;
460
for (auto &dbInfo : dbInfos) {
461
// TODO: Doesn't take CSO/CHD into account.
462
if (info->gameSizeUncompressed == dbInfo.size) {
463
found = true;
464
}
465
}
466
if (!found) {
467
// tvVerified_->SetText(ga->T("File size incorrect, bad or modified ISO"));
468
// tvVerified_->SetVisibility(UI::V_VISIBLE);
469
// tvVerified_->SetLevel(NoticeLevel::ERROR);
470
// INFO_LOG(Log::Loader, "File size %d not matching game DB", (int)info->gameSizeUncompressed);
471
} else {
472
tvVerified_->SetText(ga->T("Click \"Calculate CRC\" to verify ISO"));
473
tvVerified_->SetVisibility(UI::V_VISIBLE);
474
tvVerified_->SetLevel(NoticeLevel::INFO);
475
}
476
}
477
updateMaxY(tvVerified_);
478
}
479
}
480
481
if (tvID_) {
482
tvID_->SetText(ReplaceAll(info->id_version, "_", " v"));
483
updateMaxY(tvID_);
484
}
485
486
if (!info->id.empty()) {
487
btnGameSettings_->SetVisibility(info->hasConfig ? UI::V_VISIBLE : UI::V_GONE);
488
btnDeleteGameConfig_->SetVisibility(info->hasConfig ? UI::V_VISIBLE : UI::V_GONE);
489
btnCreateGameConfig_->SetVisibility(info->hasConfig ? UI::V_GONE : UI::V_VISIBLE);
490
491
if (info->saveDataSize) {
492
btnDeleteSaveData_->SetVisibility(UI::V_VISIBLE);
493
}
494
if (info->pic1.texture) {
495
btnSetBackground_->SetVisibility(UI::V_VISIBLE);
496
}
497
}
498
499
if (info->Ready(GameInfoFlags::PARAM_SFO)) {
500
// At this point, the above buttons won't become visible. We can show these now.
501
for (UI::Choice *choice : otherChoices_) {
502
choice->SetVisibility(UI::V_VISIBLE);
503
}
504
}
505
506
if (info->Ready(GameInfoFlags::PIC0) && info->pic0.texture) {
507
// Draw PIC0 as an overlay.
508
509
bool draw = true;
510
511
const float w = dc.GetBounds().w - 500;
512
const float h = w * (info->pic0.texture->Height() / (float)info->pic0.texture->Width());
513
514
// Bottom align the image.
515
Bounds bounds(180, dc.GetBounds().h - h - 10, w, h);
516
517
maxY += 20;
518
519
if (bounds.y < maxY) {
520
// Recalculate.
521
bounds.h = dc.GetBounds().h - 10 - maxY;
522
if (bounds.h < 0) {
523
// let's not draw it.
524
draw = false;
525
}
526
bounds.w = bounds.h * (info->pic0.texture->Width() / (float)info->pic0.texture->Height());
527
bounds.y = dc.GetBounds().h - 10 - bounds.h;
528
}
529
530
if (draw) {
531
dc.Flush();
532
533
dc.GetDrawContext()->BindTexture(0, info->pic0.texture);
534
uint32_t color = 0xFFFFFFFF;
535
dc.Draw()->DrawTexRect(bounds, 0, 0, 1, 1, color);
536
dc.Flush();
537
dc.Begin();
538
}
539
}
540
return flags;
541
}
542
543
UI::EventReturn GameScreen::OnCwCheat(UI::EventParams &e) {
544
screenManager()->push(new CwCheatScreen(gamePath_));
545
return UI::EVENT_DONE;
546
}
547
548
UI::EventReturn GameScreen::OnDoCRC32(UI::EventParams& e) {
549
CRC32string = "...";
550
Reporting::QueueCRC(gamePath_);
551
if (btnCalcCRC_) {
552
btnCalcCRC_->SetEnabled(false);
553
}
554
return UI::EVENT_DONE;
555
}
556
557
558
UI::EventReturn GameScreen::OnSwitchBack(UI::EventParams &e) {
559
TriggerFinish(DR_OK);
560
return UI::EVENT_DONE;
561
}
562
563
UI::EventReturn GameScreen::OnPlay(UI::EventParams &e) {
564
screenManager()->switchScreen(new EmuScreen(gamePath_));
565
return UI::EVENT_DONE;
566
}
567
568
UI::EventReturn GameScreen::OnGameSettings(UI::EventParams &e) {
569
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO);
570
if (info && info->Ready(GameInfoFlags::PARAM_SFO)) {
571
std::string discID = info->GetParamSFO().GetValueString("DISC_ID");
572
if ((discID.empty() || !info->disc_total) && gamePath_.FilePathContainsNoCase("PSP/GAME/"))
573
discID = g_paramSFO.GenerateFakeID(gamePath_);
574
screenManager()->push(new GameSettingsScreen(gamePath_, discID, true));
575
}
576
return UI::EVENT_DONE;
577
}
578
579
UI::EventReturn GameScreen::OnDeleteSaveData(UI::EventParams &e) {
580
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO | GameInfoFlags::SIZE);
581
if (info) {
582
// Check that there's any savedata to delete
583
if (info->saveDataSize) {
584
const bool trashAvailable = System_GetPropertyBool(SYSPROP_HAS_TRASH_BIN);
585
auto di = GetI18NCategory(I18NCat::DIALOG);
586
Path gamePath = gamePath_;
587
screenManager()->push(
588
new PromptScreen(gamePath_, di->T("DeleteConfirmAll", "Do you really want to delete all\nyour save data for this game?"), trashAvailable ? di->T("Move to trash") : di->T("Delete"), di->T("Cancel"),
589
[gamePath](bool yes) {
590
if (yes) {
591
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath, GameInfoFlags::PARAM_SFO);
592
info->DeleteAllSaveData();
593
info->saveDataSize = 0;
594
info->installDataSize = 0;
595
}
596
}));
597
}
598
}
599
RecreateViews();
600
return UI::EVENT_DONE;
601
}
602
603
UI::EventReturn GameScreen::OnDeleteGame(UI::EventParams &e) {
604
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO);
605
if (info->Ready(GameInfoFlags::PARAM_SFO)) {
606
auto di = GetI18NCategory(I18NCat::DIALOG);
607
std::string prompt;
608
prompt = di->T("DeleteConfirmGame", "Do you really want to delete this game\nfrom your device? You can't undo this.");
609
prompt += "\n\n" + gamePath_.ToVisualString(g_Config.memStickDirectory.c_str());
610
const bool trashAvailable = System_GetPropertyBool(SYSPROP_HAS_TRASH_BIN);
611
Path gamePath = gamePath_;
612
ScreenManager *sm = screenManager();
613
screenManager()->push(
614
new PromptScreen(gamePath_, prompt, trashAvailable ? di->T("Move to trash") : di->T("Delete"), di->T("Cancel"),
615
[sm, gamePath](bool yes) {
616
if (yes) {
617
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath, GameInfoFlags::PARAM_SFO);
618
info->Delete();
619
g_gameInfoCache->Clear();
620
g_recentFiles.Remove(gamePath.c_str());
621
sm->switchScreen(new MainScreen());
622
}
623
}));
624
}
625
return UI::EVENT_DONE;
626
}
627
628
UI::EventReturn GameScreen::OnRemoveFromRecent(UI::EventParams &e) {
629
g_recentFiles.Remove(gamePath_.ToString());
630
screenManager()->switchScreen(new MainScreen());
631
return UI::EVENT_DONE;
632
}
633
634
class SetBackgroundPopupScreen : public PopupScreen {
635
public:
636
SetBackgroundPopupScreen(std::string_view title, const Path &gamePath)
637
: PopupScreen(title), gamePath_(gamePath) {
638
timeStart_ = time_now_d();
639
}
640
const char *tag() const override { return "SetBackgroundPopup"; }
641
642
protected:
643
bool FillVertical() const override { return false; }
644
bool ShowButtons() const override { return false; }
645
void CreatePopupContents(UI::ViewGroup *parent) override;
646
void update() override;
647
648
private:
649
Path gamePath_;
650
double timeStart_;
651
double timeDone_ = 0.0;
652
653
enum class Status {
654
PENDING,
655
DELAY,
656
DONE,
657
};
658
Status status_ = Status::PENDING;
659
};
660
661
void SetBackgroundPopupScreen::CreatePopupContents(UI::ViewGroup *parent) {
662
auto ga = GetI18NCategory(I18NCat::GAME);
663
parent->Add(new UI::TextView(ga->T("One moment please..."), ALIGN_LEFT | ALIGN_VCENTER, false, new UI::LinearLayoutParams(UI::Margins(10, 0, 10, 10))));
664
}
665
666
void SetBackgroundPopupScreen::update() {
667
PopupScreen::update();
668
669
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PIC1);
670
if (status_ == Status::PENDING && info->Ready(GameInfoFlags::PIC1)) {
671
GameInfoTex *pic = nullptr;
672
if (info->pic1.dataLoaded && info->pic1.data.size()) {
673
pic = &info->pic1;
674
}
675
676
if (pic) {
677
const Path bgPng = GetSysDirectory(DIRECTORY_SYSTEM) / "background.png";
678
File::WriteStringToFile(false, pic->data, bgPng);
679
}
680
681
UIBackgroundShutdown();
682
683
// It's worse if it flickers, stay open for at least 1s.
684
timeDone_ = timeStart_ + 1.0;
685
status_ = Status::DELAY;
686
}
687
688
if (status_ == Status::DELAY && timeDone_ <= time_now_d()) {
689
TriggerFinish(DR_OK);
690
status_ = Status::DONE;
691
}
692
}
693
694
UI::EventReturn GameScreen::OnSetBackground(UI::EventParams &e) {
695
auto ga = GetI18NCategory(I18NCat::GAME);
696
// This popup is used to prevent any race condition:
697
// g_gameInfoCache may take time to load the data, and a crash could happen if they exit before then.
698
SetBackgroundPopupScreen *pop = new SetBackgroundPopupScreen(ga->T("Setting Background"), gamePath_);
699
if (e.v)
700
pop->SetPopupOrigin(e.v);
701
screenManager()->push(pop);
702
return UI::EVENT_DONE;
703
}
704
705