Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/Dialog/PSPSaveDialog.cpp
3186 views
1
// Copyright (c) 2012- 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
#include <ctime>
20
#include <thread>
21
22
#include "Common/Data/Encoding/Utf8.h"
23
#include "Common/Data/Text/I18n.h"
24
#include "Common/System/OSD.h"
25
#include "Common/File/FileUtil.h"
26
#include "Common/Serialize/Serializer.h"
27
#include "Common/Serialize/SerializeFuncs.h"
28
#include "Common/StringUtils.h"
29
#include "Common/Thread/ThreadUtil.h"
30
#include "Core/Dialog/PSPSaveDialog.h"
31
#include "Core/FileSystems/MetaFileSystem.h"
32
#include "Core/Util/PPGeDraw.h"
33
#include "Common/TimeUtil.h"
34
#include "Core/HLE/sceCtrl.h"
35
#include "Core/HLE/sceUtility.h"
36
#include "Core/HLE/ErrorCodes.h"
37
#include "Core/HW/MemoryStick.h"
38
#include "Core/MemMapHelpers.h"
39
#include "Core/Config.h"
40
#include "Core/Reporting.h"
41
#include "Core/SaveState.h"
42
43
static double g_lastSaveTime = -1.0;
44
45
// Actually this should be called on both saves and loads, since just after a load it's safe to exit.
46
void ResetSecondsSinceLastGameSave() {
47
g_lastSaveTime = time_now_d();
48
}
49
50
void ShowSaveLoadIndicator(bool save) {
51
g_OSD.Show(OSDType::STATUS_ICON, "", "", save ? "I_ROTATE_RIGHT" : "I_ROTATE_LEFT", 1.0f, "save_indicator");
52
g_OSD.SetFlags("save_indicator", (save ? OSDMessageFlags::SpinRight : OSDMessageFlags::SpinLeft) | OSDMessageFlags::Transparent);
53
}
54
55
double SecondsSinceLastGameSave() {
56
if (g_lastSaveTime < 0) {
57
return -1.0;
58
} else {
59
return time_now_d() - g_lastSaveTime;
60
}
61
}
62
63
const static float FONT_SCALE = 0.55f;
64
65
// These are rough, it seems to take at least 100ms or so to init, and shutdown depends on threads.
66
// Some games seem to required slightly longer delays to work, so we try 200ms as a compromise.
67
const static int SAVEDATA_INIT_DELAY_US = 200000;
68
const static int SAVEDATA_SHUTDOWN_DELAY_US = 2000;
69
70
// These are the only sizes which are allowed.
71
// TODO: We should test what the different behavior is for each.
72
const static int SAVEDATA_DIALOG_SIZE_V1 = 1480;
73
const static int SAVEDATA_DIALOG_SIZE_V2 = 1500;
74
const static int SAVEDATA_DIALOG_SIZE_V3 = 1536;
75
76
static bool IsNotVisibleAction(SceUtilitySavedataType type) {
77
switch (type) {
78
case SCE_UTILITY_SAVEDATA_TYPE_AUTOLOAD:
79
case SCE_UTILITY_SAVEDATA_TYPE_AUTOSAVE:
80
case SCE_UTILITY_SAVEDATA_TYPE_SIZES:
81
case SCE_UTILITY_SAVEDATA_TYPE_LIST:
82
case SCE_UTILITY_SAVEDATA_TYPE_FILES:
83
case SCE_UTILITY_SAVEDATA_TYPE_GETSIZE:
84
case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE:
85
case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATA:
86
case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE:
87
case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATA:
88
case SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE:
89
case SCE_UTILITY_SAVEDATA_TYPE_READDATA:
90
case SCE_UTILITY_SAVEDATA_TYPE_ERASESECURE:
91
case SCE_UTILITY_SAVEDATA_TYPE_ERASE:
92
case SCE_UTILITY_SAVEDATA_TYPE_DELETEDATA:
93
case SCE_UTILITY_SAVEDATA_TYPE_AUTODELETE:
94
return true;
95
96
default:
97
break;
98
}
99
return false;
100
}
101
102
PSPSaveDialog::PSPSaveDialog(UtilityDialogType type) : PSPDialog(type) {
103
param.SetPspParam(0);
104
}
105
106
PSPSaveDialog::~PSPSaveDialog() {
107
if (ioThread.joinable()) {
108
ioThread.join();
109
}
110
}
111
112
int PSPSaveDialog::Init(int paramAddr) {
113
// Ignore if already running
114
if (GetStatus() != SCE_UTILITY_STATUS_NONE) {
115
ERROR_LOG_REPORT(Log::sceUtility, "A save request is already running, not starting a new one");
116
return SCE_ERROR_UTILITY_INVALID_STATUS;
117
}
118
119
if (ioThread.joinable()) {
120
// Normally shouldn't be the case here.
121
ioThread.join();
122
}
123
124
ioThreadStatus = SAVEIO_NONE;
125
126
requestAddr = paramAddr;
127
int size = Memory::Read_U32(requestAddr);
128
memset(&request, 0, sizeof(request));
129
// Only copy the right size to support different save request format
130
if (size != SAVEDATA_DIALOG_SIZE_V1 && size != SAVEDATA_DIALOG_SIZE_V2 && size != SAVEDATA_DIALOG_SIZE_V3) {
131
ERROR_LOG_REPORT(Log::sceUtility, "sceUtilitySavedataInitStart: invalid size %d", size);
132
return SCE_ERROR_UTILITY_INVALID_PARAM_SIZE;
133
}
134
Memory::Memcpy(&request, requestAddr, size);
135
Memory::Memcpy(&originalRequest, requestAddr, size);
136
137
param.SetIgnoreTextures(IsNotVisibleAction((SceUtilitySavedataType)(u32)request.mode));
138
param.ClearSFOCache();
139
int retval = param.SetPspParam(&request);
140
141
const u32 mode = (u32)param.GetPspParam()->mode;
142
const char *modeName = mode < ARRAY_SIZE(utilitySavedataTypeNames) ? utilitySavedataTypeNames[mode] : "UNKNOWN";
143
INFO_LOG(Log::sceUtility,"sceUtilitySavedataInitStart(%08x) - %s (%d)", paramAddr, modeName, mode);
144
INFO_LOG(Log::sceUtility,"sceUtilitySavedataInitStart(%08x) : Game key (hex): %s", paramAddr, param.GetKey(param.GetPspParam()).c_str());
145
146
yesnoChoice = 1;
147
switch ((SceUtilitySavedataFocus)(u32)param.GetPspParam()->focus) {
148
case SCE_UTILITY_SAVEDATA_FOCUS_NAME:
149
currentSelectedSave = param.GetSaveNameIndex(param.GetPspParam());
150
break;
151
case SCE_UTILITY_SAVEDATA_FOCUS_FIRSTLIST:
152
currentSelectedSave = param.GetFirstListSave();
153
break;
154
case SCE_UTILITY_SAVEDATA_FOCUS_LASTLIST:
155
currentSelectedSave = param.GetLastListSave();
156
break;
157
case SCE_UTILITY_SAVEDATA_FOCUS_LATEST:
158
currentSelectedSave = param.GetLatestSave();
159
break;
160
case SCE_UTILITY_SAVEDATA_FOCUS_OLDEST:
161
currentSelectedSave = param.GetOldestSave();
162
break;
163
case SCE_UTILITY_SAVEDATA_FOCUS_FIRSTDATA:
164
currentSelectedSave = param.GetFirstDataSave();
165
break;
166
case SCE_UTILITY_SAVEDATA_FOCUS_LASTDATA:
167
currentSelectedSave = param.GetLastDataSave();
168
break;
169
case SCE_UTILITY_SAVEDATA_FOCUS_FIRSTEMPTY:
170
currentSelectedSave = param.GetFirstEmptySave();
171
break;
172
case SCE_UTILITY_SAVEDATA_FOCUS_LASTEMPTY:
173
currentSelectedSave = param.GetLastEmptySave();
174
break;
175
default:
176
WARN_LOG(Log::sceUtility, "Unknown save list focus option: %d", param.GetPspParam()->focus);
177
currentSelectedSave = 0;
178
break;
179
}
180
181
if (!param.WouldHaveMultiSaveName(param.GetPspParam()))
182
currentSelectedSave = 0;
183
184
switch ((SceUtilitySavedataType)(u32)param.GetPspParam()->mode)
185
{
186
case SCE_UTILITY_SAVEDATA_TYPE_LOAD:
187
DEBUG_LOG(Log::sceUtility, "Loading. Title: %s Save: %s File: %s", param.GetGameName(param.GetPspParam()).c_str(), param.GetSaveName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
188
if (param.GetFileInfo(0).size != 0) {
189
if (param.GetFileInfo(0).broken) {
190
param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_LOAD_DATA_BROKEN;
191
display = DS_LOAD_FAILED;
192
} else {
193
display = DS_LOAD_CONFIRM;
194
}
195
} else
196
display = DS_LOAD_NODATA;
197
break;
198
case SCE_UTILITY_SAVEDATA_TYPE_AUTOLOAD:
199
DEBUG_LOG(Log::sceUtility, "Loading. Title: %s Save: %s File: %s", param.GetGameName(param.GetPspParam()).c_str(), param.GetSaveName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
200
display = DS_NONE;
201
// Is this necessary?
202
// currentSelectedSave = param.GetSelectedSave();
203
break;
204
case SCE_UTILITY_SAVEDATA_TYPE_LISTLOAD:
205
DEBUG_LOG(Log::sceUtility, "Loading. Title: %s Save: %s File: %s", param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
206
if(param.GetFilenameCount() == 0)
207
display = DS_LOAD_NODATA;
208
else
209
display = DS_LOAD_LIST_CHOICE;
210
break;
211
case SCE_UTILITY_SAVEDATA_TYPE_SAVE:
212
DEBUG_LOG(Log::sceUtility, "Saving. Title: %s Save: %s File: %s", param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
213
if (param.GetFileInfo(0).size != 0) {
214
yesnoChoice = 0;
215
display = DS_SAVE_CONFIRM_OVERWRITE;
216
}
217
else
218
display = DS_SAVE_CONFIRM;
219
break;
220
case SCE_UTILITY_SAVEDATA_TYPE_AUTOSAVE:
221
DEBUG_LOG(Log::sceUtility, "Saving. Title: %s Save: %s File: %s", param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
222
display = DS_NONE;
223
// Is this necessary?
224
// currentSelectedSave = param.GetSelectedSave();
225
break;
226
case SCE_UTILITY_SAVEDATA_TYPE_LISTSAVE:
227
DEBUG_LOG(Log::sceUtility, "Saving. Title: %s Save: %s File: %s", param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
228
display = DS_SAVE_LIST_CHOICE;
229
break;
230
case SCE_UTILITY_SAVEDATA_TYPE_LISTALLDELETE:
231
DEBUG_LOG(Log::sceUtility, "Delete. Title: %s Save: %s File: %s", param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
232
if(param.GetFilenameCount() == 0)
233
display = DS_DELETE_NODATA;
234
else
235
display = DS_DELETE_LIST_CHOICE;
236
break;
237
case SCE_UTILITY_SAVEDATA_TYPE_SIZES:
238
case SCE_UTILITY_SAVEDATA_TYPE_LIST:
239
case SCE_UTILITY_SAVEDATA_TYPE_FILES:
240
case SCE_UTILITY_SAVEDATA_TYPE_GETSIZE:
241
case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE:
242
case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATA:
243
case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE:
244
case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATA:
245
case SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE:
246
case SCE_UTILITY_SAVEDATA_TYPE_READDATA:
247
case SCE_UTILITY_SAVEDATA_TYPE_ERASESECURE:
248
case SCE_UTILITY_SAVEDATA_TYPE_ERASE:
249
case SCE_UTILITY_SAVEDATA_TYPE_DELETEDATA:
250
display = DS_NONE;
251
break;
252
253
case SCE_UTILITY_SAVEDATA_TYPE_DELETE:
254
DEBUG_LOG(Log::sceUtility, "Delete. Title: %s Save: %s File: %s", param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
255
if (param.GetFileInfo(0).size != 0) {
256
yesnoChoice = 0;
257
display = DS_DELETE_CONFIRM;
258
} else
259
display = DS_DELETE_NODATA;
260
break;
261
262
case SCE_UTILITY_SAVEDATA_TYPE_AUTODELETE:
263
DEBUG_LOG(Log::sceUtility, "Delete. Title: %s Save: %s File: %s", param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
264
display = DS_NONE;
265
break;
266
267
case SCE_UTILITY_SAVEDATA_TYPE_LISTDELETE:
268
DEBUG_LOG(Log::sceUtility, "Delete. Title: %s Save: %s File: %s", param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
269
if (param.GetFilenameCount() == 0)
270
display = DS_DELETE_NODATA;
271
else
272
display = DS_DELETE_LIST_CHOICE;
273
break;
274
default:
275
{
276
ERROR_LOG_REPORT(Log::sceUtility, "Load/Save function %d not coded. Title: %s Save: %s File: %s", (SceUtilitySavedataType)(u32)param.GetPspParam()->mode, param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
277
param.GetPspParam()->common.result = 0;
278
ChangeStatusInit(SAVEDATA_INIT_DELAY_US);
279
display = DS_NONE;
280
return 0; // Return 0 should allow the game to continue, but missing function must be implemented and returning the right value or the game can block.
281
}
282
break;
283
}
284
285
if (retval < 0) {
286
ChangeStatusShutdown(SAVEDATA_SHUTDOWN_DELAY_US);
287
} else {
288
ChangeStatusInit(SAVEDATA_INIT_DELAY_US);
289
}
290
291
param.ClearSFOCache();
292
InitCommon();
293
UpdateButtons();
294
StartFade(true);
295
296
/*INFO_LOG(Log::sceUtility,"Dump Param :");
297
INFO_LOG(Log::sceUtility,"size : %d",param.GetPspParam()->common.size);
298
INFO_LOG(Log::sceUtility,"language : %d",param.GetPspParam()->common.language);
299
INFO_LOG(Log::sceUtility,"buttonSwap : %d",param.GetPspParam()->common.buttonSwap);
300
INFO_LOG(Log::sceUtility,"result : %d",param.GetPspParam()->common.result);
301
INFO_LOG(Log::sceUtility,"mode : %d",param.GetPspParam()->mode);
302
INFO_LOG(Log::sceUtility,"bind : %d",param.GetPspParam()->bind);
303
INFO_LOG(Log::sceUtility,"overwriteMode : %d",param.GetPspParam()->overwriteMode);
304
INFO_LOG(Log::sceUtility,"gameName : %s",param.GetGameName(param.GetPspParam()).c_str());
305
INFO_LOG(Log::sceUtility,"saveName : %s",param.GetPspParam()->saveName);
306
INFO_LOG(Log::sceUtility,"saveNameList : %08x",*((unsigned int*)&param.GetPspParam()->saveNameList));
307
INFO_LOG(Log::sceUtility,"fileName : %s",param.GetPspParam()->fileName);
308
INFO_LOG(Log::sceUtility,"dataBuf : %08x",*((unsigned int*)&param.GetPspParam()->dataBuf));
309
INFO_LOG(Log::sceUtility,"dataBufSize : %u",param.GetPspParam()->dataBufSize);
310
INFO_LOG(Log::sceUtility,"dataSize : %u",param.GetPspParam()->dataSize);
311
312
INFO_LOG(Log::sceUtility,"sfo title : %s",param.GetPspParam()->sfoParam.title);
313
INFO_LOG(Log::sceUtility,"sfo savedataTitle : %s",param.GetPspParam()->sfoParam.savedataTitle);
314
INFO_LOG(Log::sceUtility,"sfo detail : %s",param.GetPspParam()->sfoParam.detail);
315
316
INFO_LOG(Log::sceUtility,"icon0 data : %08x",*((unsigned int*)&param.GetPspParam()->icon0FileData.buf));
317
INFO_LOG(Log::sceUtility,"icon0 size : %u",param.GetPspParam()->icon0FileData.bufSize);
318
319
INFO_LOG(Log::sceUtility,"icon1 data : %08x",*((unsigned int*)&param.GetPspParam()->icon1FileData.buf));
320
INFO_LOG(Log::sceUtility,"icon1 size : %u",param.GetPspParam()->icon1FileData.bufSize);
321
322
INFO_LOG(Log::sceUtility,"pic1 data : %08x",*((unsigned int*)&param.GetPspParam()->pic1FileData.buf));
323
INFO_LOG(Log::sceUtility,"pic1 size : %u",param.GetPspParam()->pic1FileData.bufSize);
324
325
INFO_LOG(Log::sceUtility,"snd0 data : %08x",*((unsigned int*)&param.GetPspParam()->snd0FileData.buf));
326
INFO_LOG(Log::sceUtility,"snd0 size : %u",param.GetPspParam()->snd0FileData.bufSize);*/
327
INFO_LOG(Log::sceUtility, "Return value: %d", retval);
328
return retval;
329
}
330
331
std::string PSPSaveDialog::GetSelectedSaveDirName() const
332
{
333
switch ((SceUtilitySavedataType)(u32)param.GetPspParam()->mode)
334
{
335
case SCE_UTILITY_SAVEDATA_TYPE_LOAD:
336
case SCE_UTILITY_SAVEDATA_TYPE_AUTOLOAD:
337
case SCE_UTILITY_SAVEDATA_TYPE_SAVE:
338
case SCE_UTILITY_SAVEDATA_TYPE_AUTOSAVE:
339
case SCE_UTILITY_SAVEDATA_TYPE_AUTODELETE:
340
case SCE_UTILITY_SAVEDATA_TYPE_DELETE:
341
return param.GetSaveDirName(param.GetPspParam());
342
343
case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE:
344
case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATA:
345
case SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE:
346
case SCE_UTILITY_SAVEDATA_TYPE_READDATA:
347
case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE:
348
case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATA:
349
case SCE_UTILITY_SAVEDATA_TYPE_ERASESECURE:
350
case SCE_UTILITY_SAVEDATA_TYPE_ERASE:
351
case SCE_UTILITY_SAVEDATA_TYPE_DELETEDATA:
352
return param.GetSaveDirName(param.GetPspParam());
353
354
// SIZES ignores saveName it seems.
355
356
default:
357
return param.GetSaveDirName(param.GetPspParam(), currentSelectedSave);
358
break;
359
}
360
}
361
362
void PSPSaveDialog::DisplayBanner(int which)
363
{
364
auto di = GetI18NCategory(I18NCat::DIALOG);
365
PPGeDrawRect(0, 0, 480, 23, CalcFadedColor(0x65636358));
366
367
PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_VCENTER, 0.6f);
368
textStyle.hasShadow = false;
369
370
std::string_view title;
371
switch (which) {
372
case DB_SAVE:
373
title = di->T("Save");
374
break;
375
case DB_LOAD:
376
title = di->T("Load");
377
break;
378
case DB_DELETE:
379
title = di->T("Delete");
380
break;
381
default:
382
title = "";
383
break;
384
}
385
// TODO: Draw a hexagon icon
386
PPGeDrawImage(10, 6, 12.0f, 12.0f, 1, 10, 1, 10, 10, 10, FadedImageStyle());
387
PPGeDrawText(title, 30, 11, textStyle);
388
}
389
390
void PSPSaveDialog::DisplaySaveList(bool canMove) {
391
std::lock_guard<std::mutex> guard(paramLock);
392
static int upFramesHeld = 0;
393
static int downFramesHeld = 0;
394
395
for (int displayCount = 0; displayCount < param.GetFilenameCount(); displayCount++) {
396
PPGeImageStyle imageStyle = FadedImageStyle();
397
auto fileInfo = param.GetFileInfo(displayCount);
398
399
if (fileInfo.size == 0 && fileInfo.texture && fileInfo.texture->IsValid())
400
imageStyle.color = CalcFadedColor(0xFF777777);
401
402
// Calc save image position on screen
403
float w, h, x;
404
float y = 97;
405
if (displayCount != currentSelectedSave) {
406
w = 81;
407
h = 45;
408
x = 58.5f;
409
} else {
410
w = 144;
411
h = 80;
412
x = 27;
413
}
414
if (displayCount < currentSelectedSave)
415
y -= 13 + 45 * (currentSelectedSave - displayCount);
416
else if (displayCount > currentSelectedSave)
417
y += 48 + 45 * (displayCount - currentSelectedSave);
418
419
// Skip if it's well outside the screen.
420
if (y > 472.0f || y < -200.0f)
421
continue;
422
423
int pad = 0;
424
if (fileInfo.texture != nullptr && fileInfo.texture->IsValid()) {
425
fileInfo.texture->SetTexture();
426
int tw = fileInfo.texture->Width();
427
int th = fileInfo.texture->Height();
428
float scale = (float)h / (float)th;
429
int scaledW = (int)(tw * scale);
430
pad = (w - scaledW) / 2;
431
w = scaledW;
432
433
PPGeDrawImage(x + pad, y, w, h, 0, 0, 1, 1, tw, th, imageStyle);
434
} else {
435
PPGeDrawRect(x, y, x + w, y + h, 0x88666666);
436
}
437
if (displayCount == currentSelectedSave) {
438
float b = 1.2f;
439
uint32_t bc = CalcFadedColor(0xD0FFFFFF);
440
PPGeDrawRect(x + pad - b, y - b, x + pad + w + b, y, bc); // top border
441
PPGeDrawRect(x + pad - b, y, x + pad, y + h, bc); // left border
442
PPGeDrawRect(x + pad - b, y + h, x + pad + w + b, y + h + b, bc); //bottom border
443
PPGeDrawRect(x + pad + w, y, x + pad + w + b, y + h, bc); //right border
444
}
445
PPGeSetDefaultTexture();
446
}
447
448
if (canMove) {
449
if ( (IsButtonPressed(CTRL_UP) || IsButtonHeld(CTRL_UP, upFramesHeld)) && currentSelectedSave > 0)
450
currentSelectedSave--;
451
452
else if ( (IsButtonPressed(CTRL_DOWN) || IsButtonHeld(CTRL_DOWN, downFramesHeld)) && currentSelectedSave < (param.GetFilenameCount() - 1))
453
currentSelectedSave++;
454
}
455
}
456
457
void PSPSaveDialog::DisplaySaveIcon(bool checkExists)
458
{
459
std::lock_guard<std::mutex> guard(paramLock);
460
PPGeImageStyle imageStyle = FadedImageStyle();
461
auto curSave = param.GetFileInfo(currentSelectedSave);
462
463
if (curSave.size == 0 && checkExists)
464
imageStyle.color = CalcFadedColor(0xFF777777);
465
466
// Calc save image position on screen
467
float w = 144;
468
float h = 80;
469
float x = 27;
470
float y = 97;
471
472
int tw = 256;
473
int th = 256;
474
if (curSave.texture != nullptr && curSave.texture->IsValid()) {
475
curSave.texture->SetTexture();
476
tw = curSave.texture->Width();
477
th = curSave.texture->Height();
478
float scale = (float)h / (float)th;
479
int scaledW = (int)(tw * scale);
480
x += (w - scaledW) / 2;
481
w = scaledW;
482
} else {
483
PPGeDisableTexture();
484
}
485
PPGeDrawImage(x, y, w, h, 0, 0, 1, 1, tw, th, imageStyle);
486
PPGeSetDefaultTexture();
487
}
488
489
static void FormatSaveHourMin(char *hour_time, size_t sz, const tm &t) {
490
const char *am_pm = "AM";
491
int hour = t.tm_hour;
492
switch (g_Config.iTimeFormat) {
493
case 1:
494
if (hour == 12) {
495
am_pm = "PM";
496
} else if (hour > 12) {
497
am_pm = "PM";
498
hour -= 12;
499
} else if (hour == 0) {
500
hour = 12;
501
}
502
snprintf(hour_time, sz, "%02d:%02d %s", hour, t.tm_min, am_pm);
503
break;
504
case 0:
505
default:
506
snprintf(hour_time, sz, "%02d:%02d", hour, t.tm_min);
507
break;
508
}
509
}
510
511
static void FormatSaveDate(char *date, size_t sz, const tm &t) {
512
int year = t.tm_year + 1900;
513
int month = t.tm_mon + 1;
514
switch (g_Config.iDateFormat) {
515
case 1:
516
snprintf(date, sz, "%02d/%02d/%04d", month, t.tm_mday, year);
517
break;
518
case 2:
519
snprintf(date, sz, "%02d/%02d/%04d", t.tm_mday, month, year);
520
break;
521
case 0:
522
default:
523
snprintf(date, sz, "%04d/%02d/%02d", year, month, t.tm_mday);
524
break;
525
}
526
}
527
528
void PSPSaveDialog::DisplaySaveDataInfo1() {
529
std::lock_guard<std::mutex> guard(paramLock);
530
const SaveFileInfo &saveInfo = param.GetFileInfo(currentSelectedSave);
531
PPGeStyle saveTitleStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.55f);
532
533
if (saveInfo.broken) {
534
auto di = GetI18NCategory(I18NCat::DIALOG);
535
PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_VCENTER, 0.6f);
536
PPGeDrawText(di->T("Corrupted Data"), 180, 136, textStyle);
537
PPGeDrawText(saveInfo.title, 175, 159, saveTitleStyle);
538
} else if (saveInfo.size == 0) {
539
auto di = GetI18NCategory(I18NCat::DIALOG);
540
PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_VCENTER, 0.6f);
541
PPGeDrawText(di->T("NEW DATA"), 180, 136, textStyle);
542
} else {
543
char hour_time[32];
544
FormatSaveHourMin(hour_time, sizeof(hour_time), saveInfo.modif_time);
545
546
char date_year[32];
547
FormatSaveDate(date_year, sizeof(date_year), saveInfo.modif_time);
548
549
s64 sizeK = saveInfo.size / 1024;
550
551
PPGeDrawRect(180, 136, 480, 137, CalcFadedColor(0xFFFFFFFF));
552
std::string titleTxt = saveInfo.title;
553
std::string timeTxt = StringFromFormat("%s %s %lld KB", date_year, hour_time, sizeK);
554
std::string saveTitleTxt = saveInfo.saveTitle;
555
std::string saveDetailTxt = saveInfo.saveDetail;
556
557
PPGeStyle titleStyle = FadedStyle(PPGeAlign::BOX_BOTTOM, 0.6f);
558
titleStyle.color = CalcFadedColor(0xFFC0C0C0);
559
PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.5f);
560
561
PPGeDrawText(titleTxt, 180, 136, titleStyle);
562
PPGeDrawText(timeTxt, 180, 137, textStyle);
563
PPGeDrawText(saveTitleTxt, 175, 159, saveTitleStyle);
564
PPGeDrawTextWrapped(saveDetailTxt, 175, 181, 480 - 175, 250 - 181, textStyle);
565
}
566
}
567
568
void PSPSaveDialog::DisplaySaveDataInfo2(bool showNewData) {
569
std::lock_guard<std::mutex> guard(paramLock);
570
571
tm modif_time;
572
const char *save_title;
573
u32 data_size;
574
575
if (showNewData || param.GetFileInfo(currentSelectedSave).size == 0) {
576
time_t t;
577
time(&t);
578
localtime_r(&t, &modif_time);
579
save_title = param.GetPspParam()->sfoParam.savedataTitle;
580
// TODO: Account for icon, etc., etc.
581
data_size = param.GetPspParam()->dataSize;
582
} else {
583
modif_time = param.GetFileInfo(currentSelectedSave).modif_time;
584
save_title = param.GetFileInfo(currentSelectedSave).saveTitle;
585
data_size = param.GetFileInfo(currentSelectedSave).size;
586
}
587
588
char hour_time[32];
589
FormatSaveHourMin(hour_time, sizeof(hour_time), modif_time);
590
591
char date_year[32];
592
FormatSaveDate(date_year, sizeof(date_year), modif_time);
593
594
s64 sizeK = data_size / 1024;
595
596
PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.5f);
597
std::string title = SanitizeUTF8(std::string(save_title, strnlen(save_title, 128)));
598
std::string saveinfoTxt = StringFromFormat("%s\n%s %s\n%lld KB", title.c_str(), date_year, hour_time, sizeK);
599
PPGeDrawText(saveinfoTxt.c_str(), 8, 200, textStyle);
600
}
601
602
void PSPSaveDialog::DisplayMessage(std::string_view text, bool hasYesNo)
603
{
604
PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_CENTER, FONT_SCALE);
605
606
const float WRAP_WIDTH = 254.0f;
607
float y = 136.0f, h;
608
PPGeMeasureText(nullptr, &h, text, FONT_SCALE, PPGE_LINE_WRAP_WORD, WRAP_WIDTH);
609
float h2 = h / 2.0f;
610
if (hasYesNo)
611
{
612
auto di = GetI18NCategory(I18NCat::DIALOG);
613
std::string_view choiceText;
614
float x, w;
615
if (yesnoChoice == 1) {
616
choiceText = di->T("Yes");
617
x = 302.0f;
618
}
619
else {
620
choiceText = di->T("No");
621
x = 366.0f;
622
}
623
PPGeMeasureText(&w, &h, choiceText, FONT_SCALE);
624
w = w / 2.0f + 5.5f;
625
h /= 2.0f;
626
float y2 = y + h2 + 4.0f;
627
h2 += h + 4.0f;
628
y = 132.0f - h;
629
PPGeDrawRect(x - w, y2 - h, x + w, y2 + h, CalcFadedColor(0x40C0C0C0));
630
PPGeDrawText(di->T("Yes"), 302.0f, y2, textStyle);
631
PPGeDrawText(di->T("No"), 366.0f, y2, textStyle);
632
if (IsButtonPressed(CTRL_LEFT) && yesnoChoice == 0) {
633
yesnoChoice = 1;
634
}
635
else if (IsButtonPressed(CTRL_RIGHT) && yesnoChoice == 1) {
636
yesnoChoice = 0;
637
}
638
}
639
PPGeDrawTextWrapped(text, 334.0f, y, WRAP_WIDTH, 0, textStyle);
640
float sy = 122.0f - h2, ey = 150.0f + h2;
641
PPGeDrawRect(202.0f, sy, 466.0f, sy + 1.0f, CalcFadedColor(0xFFFFFFFF));
642
PPGeDrawRect(202.0f, ey, 466.0f, ey + 1.0f, CalcFadedColor(0xFFFFFFFF));
643
}
644
645
int PSPSaveDialog::Update(int animSpeed)
646
{
647
if (GetStatus() != SCE_UTILITY_STATUS_RUNNING)
648
return SCE_ERROR_UTILITY_INVALID_STATUS;
649
650
if (!param.GetPspParam()) {
651
ChangeStatusShutdown(SAVEDATA_SHUTDOWN_DELAY_US);
652
return 0;
653
}
654
655
if (pendingStatus != SCE_UTILITY_STATUS_RUNNING) {
656
// We're actually done, we're just waiting to tell the game that.
657
return 0;
658
}
659
660
// The struct may have been updated by the game. This happens in "Where Is My Heart?"
661
// Check if it has changed, reload it.
662
// TODO: Cut down on preloading? This rebuilds the list from scratch.
663
int size = std::min((u32)sizeof(originalRequest), Memory::Read_U32(requestAddr));
664
const u8 *updatedRequest = Memory::GetPointerRange(requestAddr, size);
665
if (updatedRequest && memcmp(updatedRequest, &originalRequest, size) != 0) {
666
memset(&request, 0, sizeof(request));
667
Memory::Memcpy(&request, requestAddr, size);
668
Memory::Memcpy(&originalRequest, requestAddr, size);
669
std::lock_guard<std::mutex> guard(paramLock);
670
param.SetPspParam(&request);
671
}
672
673
param.ClearSFOCache();
674
UpdateButtons();
675
UpdateFade(animSpeed);
676
677
UpdateCommon();
678
679
auto di = GetI18NCategory(I18NCat::DIALOG);
680
681
switch (display)
682
{
683
case DS_SAVE_LIST_CHOICE:
684
StartDraw();
685
686
DisplaySaveList();
687
DisplaySaveDataInfo1();
688
689
DisplayButtons(DS_BUTTON_OK | DS_BUTTON_CANCEL);
690
DisplayBanner(DB_SAVE);
691
692
if (IsButtonPressed(cancelButtonFlag)) {
693
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
694
StartFade(false);
695
} else if (IsButtonPressed(okButtonFlag)) {
696
// Save exist, ask user confirm
697
if (param.GetFileInfo(currentSelectedSave).size > 0) {
698
yesnoChoice = 0;
699
display = DS_SAVE_CONFIRM_OVERWRITE;
700
} else {
701
display = DS_SAVE_SAVING;
702
StartIOThread();
703
}
704
}
705
EndDraw();
706
break;
707
case DS_SAVE_CONFIRM:
708
StartDraw();
709
710
DisplaySaveIcon(false);
711
DisplaySaveDataInfo2(true);
712
713
DisplayMessage(di->T("Confirm Save", "Do you want to save this data?"), true);
714
715
DisplayButtons(DS_BUTTON_OK | DS_BUTTON_CANCEL);
716
DisplayBanner(DB_SAVE);
717
718
if (IsButtonPressed(cancelButtonFlag) || (IsButtonPressed(okButtonFlag) && yesnoChoice == 0)) {
719
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
720
StartFade(false);
721
} else if (IsButtonPressed(okButtonFlag)) {
722
display = DS_SAVE_SAVING;
723
StartIOThread();
724
}
725
726
EndDraw();
727
break;
728
case DS_SAVE_CONFIRM_OVERWRITE:
729
StartDraw();
730
731
DisplaySaveIcon(true);
732
DisplaySaveDataInfo2();
733
734
DisplayMessage(di->T("Confirm Overwrite","Do you want to overwrite the data?"), true);
735
736
DisplayButtons(DS_BUTTON_OK | DS_BUTTON_CANCEL);
737
DisplayBanner(DB_SAVE);
738
739
if (IsButtonPressed(cancelButtonFlag) || (IsButtonPressed(okButtonFlag) && yesnoChoice == 0)) {
740
if (param.GetPspParam()->mode != SCE_UTILITY_SAVEDATA_TYPE_SAVE)
741
display = DS_SAVE_LIST_CHOICE;
742
else {
743
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
744
StartFade(false);
745
}
746
} else if (IsButtonPressed(okButtonFlag)) {
747
display = DS_SAVE_SAVING;
748
StartIOThread();
749
}
750
751
EndDraw();
752
break;
753
case DS_SAVE_SAVING:
754
if (ioThreadStatus != SAVEIO_PENDING) {
755
if (ioThread.joinable()) {
756
ioThread.join();
757
}
758
}
759
760
StartDraw();
761
762
DisplaySaveIcon(true);
763
DisplaySaveDataInfo2(true);
764
765
DisplayMessage(di->T("Saving","Saving\nPlease Wait..."));
766
767
DisplayBanner(DB_SAVE);
768
769
EndDraw();
770
break;
771
case DS_SAVE_FAILED:
772
if (ioThread.joinable()) {
773
ioThread.join();
774
}
775
StartDraw();
776
777
DisplaySaveIcon(true);
778
DisplaySaveDataInfo2(true);
779
780
DisplayMessage(di->T("SavingFailed", "Unable to save data."));
781
782
DisplayButtons(DS_BUTTON_CANCEL);
783
DisplayBanner(DB_SAVE);
784
785
if (IsButtonPressed(cancelButtonFlag)) {
786
// Go back to the list so they can try again.
787
if (param.GetPspParam()->mode != SCE_UTILITY_SAVEDATA_TYPE_SAVE) {
788
display = DS_SAVE_LIST_CHOICE;
789
} else {
790
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
791
StartFade(false);
792
}
793
}
794
795
EndDraw();
796
break;
797
case DS_SAVE_DONE:
798
if (ioThread.joinable()) {
799
ioThread.join();
800
param.SetPspParam(param.GetPspParam());
801
}
802
StartDraw();
803
804
DisplaySaveIcon(true);
805
DisplaySaveDataInfo2();
806
807
DisplayMessage(di->T("Save completed"));
808
809
DisplayButtons(DS_BUTTON_CANCEL);
810
DisplayBanner(DB_SAVE);
811
812
if (IsButtonPressed(cancelButtonFlag)) {
813
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_SUCCESS;
814
// Set the save to use for autosave and autoload
815
param.SetSelectedSave(param.GetFileInfo(currentSelectedSave).idx);
816
StartFade(false);
817
}
818
819
EndDraw();
820
break;
821
822
case DS_LOAD_LIST_CHOICE:
823
StartDraw();
824
825
DisplaySaveList();
826
DisplaySaveDataInfo1();
827
828
DisplayButtons(DS_BUTTON_OK | DS_BUTTON_CANCEL);
829
DisplayBanner(DB_LOAD);
830
831
if (IsButtonPressed(cancelButtonFlag)) {
832
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
833
StartFade(false);
834
} else if (IsButtonPressed(okButtonFlag)) {
835
display = DS_LOAD_LOADING;
836
StartIOThread();
837
}
838
839
EndDraw();
840
break;
841
case DS_LOAD_CONFIRM:
842
StartDraw();
843
844
DisplaySaveIcon(true);
845
DisplaySaveDataInfo2();
846
847
DisplayMessage(di->T("ConfirmLoad", "Load this data?"), true);
848
849
DisplayButtons(DS_BUTTON_OK | DS_BUTTON_CANCEL);
850
DisplayBanner(DB_LOAD);
851
852
if (IsButtonPressed(cancelButtonFlag) || (IsButtonPressed(okButtonFlag) && yesnoChoice == 0)) {
853
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
854
StartFade(false);
855
} else if (IsButtonPressed(okButtonFlag)) {
856
display = DS_LOAD_LOADING;
857
StartIOThread();
858
}
859
860
EndDraw();
861
break;
862
case DS_LOAD_LOADING:
863
if (ioThreadStatus != SAVEIO_PENDING) {
864
if (ioThread.joinable()) {
865
ioThread.join();
866
}
867
}
868
869
StartDraw();
870
871
DisplaySaveIcon(true);
872
DisplaySaveDataInfo2();
873
874
DisplayMessage(di->T("Loading","Loading\nPlease Wait..."));
875
876
DisplayBanner(DB_LOAD);
877
878
EndDraw();
879
break;
880
case DS_LOAD_FAILED:
881
if (ioThread.joinable()) {
882
ioThread.join();
883
}
884
StartDraw();
885
886
DisplaySaveIcon(true);
887
DisplaySaveDataInfo2();
888
889
DisplayMessage(di->T("LoadingFailed", "Load failed\nThe data is corrupted."));
890
891
DisplayButtons(DS_BUTTON_CANCEL);
892
DisplayBanner(DB_LOAD);
893
894
if (IsButtonPressed(cancelButtonFlag)) {
895
// Go back to the list so they can try again.
896
if (param.GetPspParam()->mode != SCE_UTILITY_SAVEDATA_TYPE_LOAD) {
897
display = DS_LOAD_LIST_CHOICE;
898
} else {
899
StartFade(false);
900
}
901
}
902
903
EndDraw();
904
break;
905
case DS_LOAD_DONE:
906
if (ioThread.joinable()) {
907
ioThread.join();
908
}
909
StartDraw();
910
911
DisplaySaveIcon(true);
912
DisplaySaveDataInfo2();
913
914
DisplayMessage(di->T("Load completed"));
915
916
DisplayButtons(DS_BUTTON_CANCEL);
917
DisplayBanner(DB_LOAD);
918
919
// Allow OK to be pressed as well to confirm the save.
920
// The PSP only allows cancel, but that's generally not great UX.
921
// Allowing this here makes it quicker for most users to get into the actual game.
922
if (IsButtonPressed(cancelButtonFlag) || IsButtonPressed(okButtonFlag)) {
923
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_SUCCESS;
924
// Set the save to use for autosave and autoload
925
param.SetSelectedSave(param.GetFileInfo(currentSelectedSave).idx);
926
StartFade(false);
927
}
928
929
EndDraw();
930
break;
931
case DS_LOAD_NODATA:
932
StartDraw();
933
934
DisplayMessage(di->T("There is no data"));
935
936
DisplayButtons(DS_BUTTON_CANCEL);
937
DisplayBanner(DB_LOAD);
938
939
if (IsButtonPressed(cancelButtonFlag)) {
940
param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA;
941
StartFade(false);
942
}
943
944
EndDraw();
945
break;
946
947
case DS_DELETE_LIST_CHOICE:
948
StartDraw();
949
950
DisplaySaveList();
951
DisplaySaveDataInfo1();
952
953
DisplayButtons(DS_BUTTON_OK | DS_BUTTON_CANCEL);
954
DisplayBanner(DB_DELETE);
955
956
if (IsButtonPressed(cancelButtonFlag)) {
957
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
958
StartFade(false);
959
} else if (IsButtonPressed(okButtonFlag)) {
960
yesnoChoice = 0;
961
display = DS_DELETE_CONFIRM;
962
}
963
964
EndDraw();
965
break;
966
case DS_DELETE_CONFIRM:
967
StartDraw();
968
969
DisplaySaveIcon(true);
970
DisplaySaveDataInfo2();
971
972
DisplayMessage(di->T("DeleteConfirm",
973
"This save data will be deleted.\nAre you sure you want to continue?"),
974
true);
975
976
DisplayButtons(DS_BUTTON_OK | DS_BUTTON_CANCEL);
977
DisplayBanner(DB_DELETE);
978
979
if (IsButtonPressed(cancelButtonFlag) || (IsButtonPressed(okButtonFlag) && yesnoChoice == 0)) {
980
if (param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_LISTDELETE || param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_LISTALLDELETE)
981
display = DS_DELETE_LIST_CHOICE;
982
else {
983
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
984
StartFade(false);
985
}
986
} else if (IsButtonPressed(okButtonFlag)) {
987
display = DS_DELETE_DELETING;
988
StartIOThread();
989
}
990
991
EndDraw();
992
break;
993
case DS_DELETE_DELETING:
994
if (ioThreadStatus != SAVEIO_PENDING) {
995
if (ioThread.joinable()) {
996
ioThread.join();
997
}
998
}
999
1000
StartDraw();
1001
1002
DisplayMessage(di->T("Deleting","Deleting\nPlease Wait..."));
1003
1004
DisplayBanner(DB_DELETE);
1005
1006
EndDraw();
1007
break;
1008
case DS_DELETE_FAILED:
1009
if (ioThread.joinable()) {
1010
ioThread.join();
1011
}
1012
StartDraw();
1013
1014
DisplayMessage(di->T("DeleteFailed", "Unable to delete data."));
1015
1016
DisplayButtons(DS_BUTTON_CANCEL);
1017
DisplayBanner(DB_DELETE);
1018
1019
if (IsButtonPressed(cancelButtonFlag)) {
1020
if (param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_LISTDELETE || param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_LISTALLDELETE)
1021
display = DS_DELETE_LIST_CHOICE;
1022
else
1023
StartFade(false);
1024
}
1025
1026
EndDraw();
1027
break;
1028
case DS_DELETE_DONE:
1029
if (ioThread.joinable()) {
1030
ioThread.join();
1031
param.SetPspParam(param.GetPspParam());
1032
}
1033
StartDraw();
1034
1035
DisplayMessage(di->T("Delete completed"));
1036
1037
DisplayButtons(DS_BUTTON_CANCEL);
1038
DisplayBanner(DB_DELETE);
1039
1040
if (IsButtonPressed(cancelButtonFlag)) {
1041
if (param.GetFilenameCount() == 0)
1042
display = DS_DELETE_NODATA;
1043
else if (param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_LISTDELETE || param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_LISTALLDELETE) {
1044
if (currentSelectedSave > param.GetFilenameCount() - 1)
1045
currentSelectedSave = param.GetFilenameCount() - 1;
1046
display = DS_DELETE_LIST_CHOICE;
1047
} else {
1048
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_SUCCESS;
1049
StartFade(false);
1050
}
1051
}
1052
1053
EndDraw();
1054
break;
1055
case DS_DELETE_NODATA:
1056
StartDraw();
1057
1058
DisplayMessage(di->T("There is no data"));
1059
1060
DisplayButtons(DS_BUTTON_CANCEL);
1061
DisplayBanner(DB_DELETE);
1062
1063
if (IsButtonPressed(cancelButtonFlag)) {
1064
param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_DELETE_NO_DATA;
1065
StartFade(false);
1066
}
1067
1068
EndDraw();
1069
break;
1070
1071
case DS_NONE: // For action which display nothing
1072
switch (ioThreadStatus) {
1073
case SAVEIO_NONE:
1074
StartIOThread();
1075
break;
1076
case SAVEIO_PENDING:
1077
case SAVEIO_DONE:
1078
// To make sure there aren't any timing variations, we sync the next frame.
1079
if (g_Config.iIOTimingMethod == IOTIMING_HOST && ioThreadStatus == SAVEIO_PENDING) {
1080
// ... except in Host IO timing, where we wait as long as needed.
1081
break;
1082
}
1083
if (ioThread.joinable()) {
1084
ioThread.join();
1085
}
1086
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);
1087
break;
1088
}
1089
break;
1090
1091
default:
1092
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);
1093
break;
1094
}
1095
1096
if (ReadStatus() == SCE_UTILITY_STATUS_FINISHED || pendingStatus == SCE_UTILITY_STATUS_FINISHED)
1097
Memory::Memcpy(requestAddr, &request, request.common.size, "SaveDialogParam");
1098
param.ClearSFOCache();
1099
1100
return 0;
1101
}
1102
1103
// It's kinda ugly how this uses the "global" 'display'...
1104
void PSPSaveDialog::ExecuteIOAction() {
1105
param.ClearSFOCache();
1106
auto &result = param.GetPspParam()->common.result;
1107
std::lock_guard<std::mutex> guard(paramLock);
1108
switch (display) {
1109
case DS_LOAD_LOADING:
1110
result = param.Load(param.GetPspParam(), GetSelectedSaveDirName(), currentSelectedSave);
1111
if (result == 0) {
1112
display = DS_LOAD_DONE;
1113
g_lastSaveTime = time_now_d();
1114
} else {
1115
display = DS_LOAD_FAILED;
1116
}
1117
break;
1118
case DS_SAVE_SAVING:
1119
SaveState::NotifySaveData();
1120
if (param.Save(param.GetPspParam(), GetSelectedSaveDirName()) == 0) {
1121
display = DS_SAVE_DONE;
1122
g_lastSaveTime = time_now_d();
1123
} else {
1124
display = DS_SAVE_FAILED;
1125
}
1126
break;
1127
case DS_DELETE_DELETING:
1128
if (param.Delete(param.GetPspParam(), currentSelectedSave)) {
1129
result = 0;
1130
display = DS_DELETE_DONE;
1131
} else {
1132
//result = SCE_UTILITY_SAVEDATA_ERROR_DELETE_NO_DATA;// What the result should be?
1133
display = DS_DELETE_FAILED;
1134
}
1135
break;
1136
case DS_NONE:
1137
ExecuteNotVisibleIOAction();
1138
break;
1139
1140
default:
1141
// Nothing to do here.
1142
break;
1143
}
1144
1145
ioThreadStatus = SAVEIO_DONE;
1146
param.ClearSFOCache();
1147
}
1148
1149
void PSPSaveDialog::ExecuteNotVisibleIOAction() {
1150
param.ClearSFOCache();
1151
auto &result = param.GetPspParam()->common.result;
1152
1153
SceUtilitySavedataType utilityMode = (SceUtilitySavedataType)(u32)param.GetPspParam()->mode;
1154
switch (utilityMode) {
1155
case SCE_UTILITY_SAVEDATA_TYPE_LOAD: // Only load and exit
1156
case SCE_UTILITY_SAVEDATA_TYPE_AUTOLOAD:
1157
result = param.Load(param.GetPspParam(), GetSelectedSaveDirName(), currentSelectedSave);
1158
ResetSecondsSinceLastGameSave();
1159
ShowSaveLoadIndicator(false);
1160
break;
1161
case SCE_UTILITY_SAVEDATA_TYPE_SAVE: // Only save and exit
1162
case SCE_UTILITY_SAVEDATA_TYPE_AUTOSAVE:
1163
SaveState::NotifySaveData();
1164
result = param.Save(param.GetPspParam(), GetSelectedSaveDirName());
1165
ResetSecondsSinceLastGameSave();
1166
ShowSaveLoadIndicator(true);
1167
break;
1168
case SCE_UTILITY_SAVEDATA_TYPE_SIZES:
1169
result = param.GetSizes(param.GetPspParam());
1170
break;
1171
case SCE_UTILITY_SAVEDATA_TYPE_LIST:
1172
param.GetList(param.GetPspParam());
1173
result = 0;
1174
break;
1175
case SCE_UTILITY_SAVEDATA_TYPE_FILES:
1176
result = param.GetFilesList(param.GetPspParam(), requestAddr);
1177
break;
1178
case SCE_UTILITY_SAVEDATA_TYPE_GETSIZE:
1179
{
1180
bool sizeResult = param.GetSize(param.GetPspParam());
1181
// TODO: According to JPCSP, should test/verify this part but seems edge casey.
1182
if (MemoryStick_State() != PSP_MEMORYSTICK_STATE_INSERTED) {
1183
result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_MEMSTICK;
1184
} else if (sizeResult) {
1185
result = 0;
1186
} else {
1187
result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA;
1188
}
1189
}
1190
break;
1191
case SCE_UTILITY_SAVEDATA_TYPE_DELETEDATA:
1192
DEBUG_LOG(Log::sceUtility, "sceUtilitySavedata DELETEDATA: %s", param.GetPspParam()->saveName);
1193
if (param.Delete(param.GetPspParam(), param.GetSelectedSave())) {
1194
result = 0;
1195
} else {
1196
result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA;
1197
}
1198
break;
1199
case SCE_UTILITY_SAVEDATA_TYPE_AUTODELETE:
1200
case SCE_UTILITY_SAVEDATA_TYPE_DELETE:
1201
if (param.Delete(param.GetPspParam(), param.GetSelectedSave())) {
1202
result = 0;
1203
} else {
1204
result = SCE_UTILITY_SAVEDATA_ERROR_DELETE_NO_DATA;
1205
}
1206
break;
1207
// TODO: Should reset the directory's other files.
1208
case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATA:
1209
case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE:
1210
result = param.Save(param.GetPspParam(), GetSelectedSaveDirName(), param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE);
1211
if (result == SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_NOSPACE) {
1212
result = SCE_UTILITY_SAVEDATA_ERROR_RW_MEMSTICK_FULL;
1213
} else {
1214
SaveState::NotifySaveData();
1215
ResetSecondsSinceLastGameSave();
1216
ShowSaveLoadIndicator(true);
1217
}
1218
break;
1219
case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATA:
1220
case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE:
1221
SaveState::NotifySaveData();
1222
result = param.Save(param.GetPspParam(), GetSelectedSaveDirName(), param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE);
1223
ResetSecondsSinceLastGameSave();
1224
ShowSaveLoadIndicator(true);
1225
break;
1226
case SCE_UTILITY_SAVEDATA_TYPE_READDATA:
1227
case SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE:
1228
result = param.Load(param.GetPspParam(), GetSelectedSaveDirName(), currentSelectedSave, param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE);
1229
if (result == SCE_UTILITY_SAVEDATA_ERROR_LOAD_DATA_BROKEN)
1230
result = SCE_UTILITY_SAVEDATA_ERROR_RW_DATA_BROKEN;
1231
if (result == SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA)
1232
result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA;
1233
ResetSecondsSinceLastGameSave();
1234
ShowSaveLoadIndicator(false);
1235
break;
1236
case SCE_UTILITY_SAVEDATA_TYPE_ERASE:
1237
case SCE_UTILITY_SAVEDATA_TYPE_ERASESECURE:
1238
result = param.DeleteData(param.GetPspParam());
1239
break;
1240
default:
1241
break;
1242
}
1243
1244
param.ClearSFOCache();
1245
}
1246
1247
void PSPSaveDialog::StartIOThread() {
1248
if (ioThread.joinable()) {
1249
WARN_LOG_REPORT(Log::sceUtility, "Starting a save io thread when one already pending, uh oh.");
1250
ioThread.join();
1251
}
1252
1253
// Show save indicator. It's strange how "display" is just as much an action as what to display.
1254
if (display == DS_SAVE_SAVING || display == DS_LOAD_LOADING) {
1255
const bool save = display != DS_LOAD_LOADING;
1256
ResetSecondsSinceLastGameSave();
1257
ShowSaveLoadIndicator(save);
1258
}
1259
1260
ioThreadStatus = SAVEIO_PENDING;
1261
ioThread = std::thread([this]() {
1262
SetCurrentThreadName("SaveIO");
1263
1264
AndroidJNIThreadContext jniContext;
1265
this->ExecuteIOAction();
1266
});
1267
}
1268
1269
int PSPSaveDialog::Shutdown(bool force) {
1270
if (GetStatus() != SCE_UTILITY_STATUS_FINISHED && !force)
1271
return SCE_ERROR_UTILITY_INVALID_STATUS;
1272
1273
if (ioThread.joinable()) {
1274
ioThread.join();
1275
}
1276
ioThreadStatus = SAVEIO_NONE;
1277
1278
PSPDialog::Shutdown(force);
1279
if (!force) {
1280
ChangeStatusShutdown(SAVEDATA_SHUTDOWN_DELAY_US);
1281
}
1282
param.SetPspParam(0);
1283
param.ClearSFOCache();
1284
1285
return 0;
1286
}
1287
1288
void PSPSaveDialog::DoState(PointerWrap &p) {
1289
if (ioThread.joinable()) {
1290
ioThread.join();
1291
}
1292
PSPDialog::DoState(p);
1293
1294
auto s = p.Section("PSPSaveDialog", 1, 2);
1295
if (!s) {
1296
return;
1297
}
1298
1299
Do(p, display);
1300
param.DoState(p);
1301
Do(p, request);
1302
// Just reset it.
1303
bool hasParam = param.GetPspParam() != NULL;
1304
Do(p, hasParam);
1305
if (hasParam && p.mode == p.MODE_READ) {
1306
param.SetPspParam(&request);
1307
}
1308
Do(p, requestAddr);
1309
Do(p, currentSelectedSave);
1310
Do(p, yesnoChoice);
1311
if (s > 2) {
1312
Do(p, ioThreadStatus);
1313
} else {
1314
ioThreadStatus = SAVEIO_NONE;
1315
}
1316
}
1317
1318
pspUtilityDialogCommon *PSPSaveDialog::GetCommonParam() {
1319
return &param.GetPspParam()->common;
1320
}
1321
1322