1 #include <pspsdk.h>
2 #include <pspkernel.h>
3 #include <pspdebug.h>
4 #include <pspmoduleinfo.h>
5 #include <pspctrl.h>
6 #include <pspchnnlsv.h>
7 #include <psputility.h>
8 #include "kernelcall/kernelcall.h"
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include "encrypt.h"
13 #include "decrypt.h"
14 #include "psf.h"
15 
16 #define printf pspDebugScreenPrintf
17 
18 /* Define the module info section */
19 PSP_MODULE_INFO("ppssppsavetool", 0, 1, 0);
20 PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER);
21 PSP_HEAP_SIZE_KB(-64);
22 
23 #define ENCRYPT_FILE_VERSION 1
24 
25 
26 int currentMenu = 0;
27 int selectedOption = 0;
28 int basePath = 0;
29 int workDir = 0;
30 
31 char *menuList0[] = {"Encrypt","Decrypt", "Exit", NULL};
32 char *menuList1[] = {"ms0:/PSP/SAVEDATAPPSSPP/","host0:/","host1:/", "host2:/", "Back", NULL};
33 char *menuList2[] = {"Back", NULL};
34 
GetSDKMainVersion(int sdkVersion)35 int GetSDKMainVersion(int sdkVersion)
36 {
37 	if(sdkVersion > 0x307FFFF)
38 		return 6;
39 	if(sdkVersion > 0x300FFFF)
40 		return 5;
41 	if(sdkVersion > 0x206FFFF)
42 		return 4;
43 	if(sdkVersion > 0x205FFFF)
44 		return 3;
45 	if(sdkVersion >= 0x2000000)
46 		return 2;
47 	if(sdkVersion >= 0x1000000)
48 		return 1;
49 	return 0;
50 };
51 
ProcessInput(int maxOption,int * selectedOption)52 int ProcessInput(int maxOption, int *selectedOption)
53 {
54 	SceCtrlData pad, oldpad;
55 	sceCtrlReadBufferPositive(&oldpad, 1);
56 	while(1)
57 	{
58 		sceCtrlReadBufferPositive(&pad, 1);
59 
60 		if (pad.Buttons != 0)
61 		{
62 			if (!(oldpad.Buttons & PSP_CTRL_CROSS) &&  pad.Buttons & PSP_CTRL_CROSS)
63 			{
64 				return *selectedOption;
65 			}
66 			else if (!(oldpad.Buttons & PSP_CTRL_UP) &&  pad.Buttons & PSP_CTRL_UP && *selectedOption > 0)
67 			{
68 				*selectedOption = *selectedOption-1;
69 				return -1;
70 			}
71 			else if (!(oldpad.Buttons & PSP_CTRL_DOWN) &&  pad.Buttons & PSP_CTRL_DOWN && *selectedOption < maxOption-1)
72 			{
73 				*selectedOption = *selectedOption + 1;
74 				return -1;
75 			}
76 		}
77 		oldpad = pad;
78 	}
79 }
80 
81 typedef struct
82 {
83 	char name[30];
84 	char saveFile[30];
85 	int errorId;
86 } DirInfo;
87 
88 typedef struct
89 {
90 	int fileVersion;
91 	u8 key[16];
92 	int sdkVersion;
93 } EncryptFileInfo;
94 
95 DirInfo dirList[128];
96 int numDirList;
97 DirInfo invalidDirList[128];
98 int numInvalidDirList;
99 
FileExist(char * basePath,char * dirPath,char * fileName)100 int FileExist(char* basePath, char* dirPath, char* fileName)
101 {
102 	SceIoStat fileStat;
103 	char path[1024];
104 	sprintf(path,"%s%s/%s",basePath, dirPath, fileName);
105 	if(sceIoGetstat(path, &fileStat) < 0) // no file
106 		return 0;
107 	return 1;
108 }
109 
FileRead(char * basePath,char * dirPath,char * fileName,u8 * dataout,int size)110 int FileRead(char* basePath, char* dirPath, char* fileName, u8* dataout, int size)
111 {
112 	char path[1024];
113 	sprintf(path,"%s%s/%s",basePath, dirPath, fileName);
114 	SceUID fileId = sceIoOpen(path, PSP_O_RDONLY, 0777);
115 	if(fileId < 0)
116 		return -1;
117 	sceIoRead(fileId, dataout, size);
118 	sceIoClose(fileId);
119 	return 0;
120 }
121 
AddErrorDir(char * dirName,int error)122 void AddErrorDir(char* dirName, int error)
123 {
124 	if(numInvalidDirList >= 128)
125 		return;
126 	DirInfo *inf = &invalidDirList[numInvalidDirList];
127 	strcpy(inf->name,dirName);
128 	inf->errorId = error;
129 	numInvalidDirList++;
130 }
131 
UpdateValidDir(int isEncrypt)132 int UpdateValidDir(int isEncrypt)
133 {
134 	numDirList = 0;
135 	numInvalidDirList = 0;
136 
137 	const char* pspPath = "ms0:/PSP/SAVEDATA/";
138 
139 	char* pathSrc;
140 	char* pathDst;
141 	if(isEncrypt)
142 	{
143 		pathSrc = menuList1[basePath];
144 		pathDst = pspPath;
145 	}
146 	else
147 	{
148 		pathSrc = pspPath;
149 		pathDst = menuList1[basePath];
150 	}
151 
152 	int dfd;
153 	dfd = sceIoDopen(menuList1[basePath]);
154 	if(dfd >= 0)
155 	{
156 		SceIoDirent data;
157 		while(sceIoDread(dfd, &data) > 0 && numDirList < 128)
158 		{
159 			if(!(data.d_stat.st_attr & 0x10)) // is not a directory
160 			{
161 				continue;
162 			}
163 
164 			if(data.d_name[0] == '.') // ignore "." and ".."
165 				continue;
166 
167 			if(FileExist(menuList1[basePath], data.d_name, "ENCRYPT_INFO.BIN") < 0)
168 			{
169 				AddErrorDir(data.d_name,1);
170 				continue;
171 			}
172 
173 			EncryptFileInfo encryptInfo;
174 			if(FileRead(menuList1[basePath], data.d_name, "ENCRYPT_INFO.BIN",(u8*)&encryptInfo,sizeof(encryptInfo)) < 0)
175 			{
176 				AddErrorDir(data.d_name,2);
177 				continue;
178 			}
179 
180 			if(encryptInfo.fileVersion != ENCRYPT_FILE_VERSION) // Not good version
181 			{
182 				AddErrorDir(data.d_name,3);
183 				continue;
184 			}
185 
186 			if(FileExist(pathSrc, data.d_name, "PARAM.SFO") < 0)
187 			{
188 				AddErrorDir(data.d_name,4);
189 				continue;
190 			}
191 
192 			u8 paramsfo[0x1330];
193 			if(FileRead(pathSrc, data.d_name, "PARAM.SFO",(u8*)&paramsfo,0x1330) < 0)
194 			{
195 				AddErrorDir(data.d_name,5);
196 				continue;
197 			}
198 
199 			u8 *datafile;
200 			int listLen;
201 			if (find_psf_section("SAVEDATA_FILE_LIST", paramsfo, 0x1330,
202 							&datafile, &listLen) < 0)
203 			{
204 				AddErrorDir(data.d_name,6);
205 				continue;
206 			}
207 			if(datafile[0] == 0)
208 			{
209 				AddErrorDir(data.d_name,7);
210 				continue;
211 			}
212 
213 			char filename[32];
214 			strcpy(filename, (char*)datafile);
215 
216 			if(FileExist(pathSrc, data.d_name, filename) < 0)
217 			{
218 				AddErrorDir(data.d_name,8);
219 				continue;
220 			}
221 
222 			DirInfo *inf = &dirList[numDirList];
223 			inf->errorId = 0;
224 			strcpy(inf->name, data.d_name);
225 			strcpy(inf->saveFile, filename);
226 
227 			numDirList++;
228 
229 		}
230 		sceIoDclose(dfd);
231 		if(numDirList == 0)
232 		{
233 			return -1;
234 		}
235 	}
236 	else
237 	{
238 		return -2;
239 	}
240 	return 0;
241 }
242 
FileCopy(char * srcPath,char * destPath,char * fileName)243 int FileCopy(char* srcPath, char* destPath, char* fileName)
244 {
245 	SceIoStat fileStat;
246 	char path[258];
247 	sprintf(path,"%s/%s",srcPath, fileName);
248 
249 	if(sceIoGetstat(path, &fileStat) < 0)
250 		return -1;
251 	u8* data = malloc(fileStat.st_size);
252 
253 	SceUID fileId = sceIoOpen(path, PSP_O_RDONLY, 0777);
254 	if(fileId < 0)
255 	{
256 		printf("Fail opening %s\n",path);
257 		free(data);
258 		return -1;
259 	}
260 	sceIoRead(fileId, data, fileStat.st_size);
261 	sceIoClose(fileId);
262 
263 	sprintf(path,"%s/%s",destPath, fileName);
264 
265 	fileId = sceIoOpen(path, PSP_O_WRONLY | PSP_O_CREAT, 0777);
266 	if(fileId < 0)
267 	{
268 		printf("Fail opening %s\n",path);
269 		return -1;
270 	}
271 	sceIoWrite(fileId, data, fileStat.st_size);
272 	sceIoClose(fileId);
273 
274 	free(data);
275 	return 0;
276 }
277 
main(int argc,char * argv[])278 int main(int argc, char *argv[])
279 {
280 	int i;
281 	pspDebugScreenInit();
282 
283 	SceUID mod = pspSdkLoadStartModule ("flash0:/kd/chnnlsv.prx",PSP_MEMORY_PARTITION_KERNEL);
284 	if (mod < 0) {
285 		printf("Error 0x%08X loading/starting chnnlsv.prx.\n", mod);
286 	}
287 
288 	mod = pspSdkLoadStartModule ("kernelcall.prx",PSP_MEMORY_PARTITION_KERNEL);
289 	if (mod < 0) {
290 		printf("Error 0x%08X loading/starting kernelcall.prx.\n", mod);
291 	}
292 
293 	sceCtrlSetSamplingCycle(0);
294 	sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);
295 	for(;;)
296 	{
297 		printf("====================================================================");
298 		printf("PPSSPP Save Tool\n");
299 		printf("====================================================================\n\n\n");
300 
301 		switch(currentMenu)
302 		{
303 
304 		case 0:
305 			{
306 				int maxOption = 0;
307 				for(i = 0; menuList0[i]; i++)
308 				{
309 					if(i == selectedOption)
310 						printf("   > %s\n",menuList0[i]);
311 					else
312 						printf("     %s\n",menuList0[i]);
313 					maxOption++;
314 				}
315 
316 				int input = ProcessInput(maxOption, &selectedOption);
317 				if(input == 0)
318 				{
319 					currentMenu = 1;
320 					selectedOption = 0;
321 				}
322 				else if(input == 1)
323 				{
324 					currentMenu = 4;
325 					selectedOption = 0;
326 				}
327 				else if(input == 2)
328 				{
329 					sceKernelExitGame();
330 				}
331 			}
332 			break;
333 		case 4:
334 		case 1:
335 			{
336 				int maxOption = 0;
337 				printf("PPSSPP Decrypted Save Directory : \n");
338 				for(i = 0; menuList1[i]; i++)
339 				{
340 					if(i == selectedOption)
341 						printf("   > %s\n",menuList1[i]);
342 					else
343 						printf("     %s\n",menuList1[i]);
344 					maxOption++;
345 				}
346 
347 				int input = ProcessInput(maxOption, &selectedOption);
348 				if(input == maxOption-1)
349 				{
350 					if(currentMenu == 1)
351 						selectedOption = 0;
352 					else
353 						selectedOption = 1;
354 					currentMenu = 0;
355 				}
356 				else if(input >= 0)
357 				{
358 					basePath = selectedOption;
359 					if(currentMenu == 1)
360 					{
361 						currentMenu = 2;
362 						UpdateValidDir(1);
363 					}
364 					else
365 					{
366 						currentMenu = 5;
367 						UpdateValidDir(0);
368 					}
369 					selectedOption = 0;
370 				}
371 			}
372 			break;
373 		case 5:
374 		case 2:
375 			{
376 				int maxOption = 0;
377 				if(currentMenu == 2)
378 					printf("Save to encrypt : \n");
379 				else
380 					printf("Save to decrypt : \n");
381 
382 				if(numDirList == 0)
383 				{
384 					printf("No compatible data, see README for help on use\n");
385 				}
386 				for(i = 0; i < numDirList; i++)
387 				{
388 					if(i == selectedOption)
389 						printf("   > %s\n",dirList[i].name);
390 					else
391 						printf("     %s\n",dirList[i].name);
392 					maxOption++;
393 				}
394 
395 				for(i = 0; menuList2[i]; i++)
396 				{
397 					if((i+numDirList) == selectedOption)
398 						printf("   > %s\n",menuList2[i]);
399 					else
400 						printf("     %s\n",menuList2[i]);
401 					maxOption++;
402 				}
403 
404 				printf("\n Invalid path : \n");
405 				for(i = 0; i < numInvalidDirList && i < (22-numDirList); i++)
406 				{
407 					switch(invalidDirList[i].errorId)
408 					{
409 						case 1:
410 							printf("     %s : ENCRYPT_INFO.BIN not found\n",invalidDirList[i].name);
411 						break;
412 						case 2:
413 							printf("     %s : ENCRYPT_INFO.BIN read error\n",invalidDirList[i].name);
414 						break;
415 						case 3:
416 							printf("     %s : ENCRYPT_INFO.BIN wrong version\n",invalidDirList[i].name);
417 						break;
418 						case 4:
419 							printf("     %s : PARAM.SFO not found\n",invalidDirList[i].name);
420 						break;
421 						case 5:
422 							printf("     %s : PARAM.SFO read error\n",invalidDirList[i].name);
423 						break;
424 						case 6:
425 							printf("     %s : SAVEDATA_FILE_LIST not found in PARAM.SFO\n",invalidDirList[i].name);
426 						break;
427 						case 7:
428 							printf("     %s : no save name in SAVEDATA_FILE_LIST\n",invalidDirList[i].name);
429 						break;
430 						case 8:
431 							printf("     %s : no save found\n",invalidDirList[i].name);
432 						break;
433 						default:
434 						break;
435 					}
436 				}
437 
438 				int input = ProcessInput(maxOption, &selectedOption);
439 				if(input == numDirList)
440 				{
441 					if(currentMenu == 2)
442 						currentMenu = 1;
443 					else
444 						currentMenu = 4;
445 					selectedOption = basePath;
446 				}
447 				else if(input >= 0)
448 				{
449 					if(currentMenu == 2)
450 						currentMenu = 3;
451 					else
452 						currentMenu = 6;
453 					workDir = input;
454 					selectedOption = 0;
455 				}
456 			}
457 			break;
458 		case 6:
459 		case 3:
460 		{
461 
462 			EncryptFileInfo encryptInfo;
463 			if(FileRead(menuList1[basePath], dirList[workDir].name, "ENCRYPT_INFO.BIN",(u8*)&encryptInfo,sizeof(encryptInfo)) < 0)
464 			{
465 				printf("Can't read encrypt file\n");
466 			}
467 			else
468 			{
469 				printf("Key : ");
470 				for(i = 0; i < 16; i++)
471 					printf(" %02x",(u8)encryptInfo.key[i]);
472 				printf("\n");
473 				printf("SDK Version : 0x%x\n",encryptInfo.sdkVersion);
474 
475 				char srcPath[128];
476 				char dstPath[128];
477 				if(currentMenu == 3)
478 				{
479 					sprintf(srcPath,"%s%s",menuList1[basePath], dirList[workDir].name);
480 					sprintf(dstPath,"ms0:/PSP/SAVEDATA/%s",dirList[workDir].name);
481 					sceIoMkdir(dstPath,0777);
482 				}
483 				else
484 				{
485 					sprintf(srcPath,"ms0:/PSP/SAVEDATA/%s",dirList[workDir].name);
486 					sprintf(dstPath,"%s%s",menuList1[basePath], dirList[workDir].name);
487 				}
488 
489 				int dfd;
490 				dfd = sceIoDopen(srcPath);
491 				if(dfd >= 0)
492 				{
493 					SceIoDirent dirinfo;
494 					while(sceIoDread(dfd, &dirinfo) > 0)
495 					{
496 
497 						if(!(dirinfo.d_stat.st_mode & 0x2000)) // is not a file
498 							continue;
499 
500 						if(strcmp(dirinfo.d_name,"ENCRYPT_INFO.BIN") == 0) // don't copy encrypt info
501 							continue;
502 
503 						FileCopy(srcPath, dstPath, dirinfo.d_name);
504 
505 					}
506 					sceIoDclose(dfd);
507 				}
508 
509 				if(currentMenu == 3)
510 				{
511 
512 					char decryptedFile[258], encryptedFile[258], srcSFO[258], dstSFO[258];
513 					sprintf(decryptedFile,"%s/%s",srcPath ,dirList[workDir].saveFile);
514 					sprintf(srcSFO,"%s/PARAM.SFO",srcPath);
515 
516 					sprintf(encryptedFile,"%s/%s",dstPath ,dirList[workDir].saveFile);
517 					sprintf(dstSFO,"%s/PARAM.SFO",dstPath);
518 
519 					printf("Encoding %s into %s\n",decryptedFile, encryptedFile);
520 
521 					int ret = encrypt_file(decryptedFile,
522 											encryptedFile,
523 											dirList[workDir].saveFile,
524 											srcSFO,
525 											dstSFO,
526 											encryptInfo.key[0] != 0 ? encryptInfo.key : NULL,
527 											GetSDKMainVersion(encryptInfo.sdkVersion)
528 											);
529 
530 					if(ret < 0) {
531 						printf("Error: encrypt_file() returned %d\n\n", ret);
532 					} else {
533 						printf("Successfully wrote %d bytes to\n", ret);
534 						printf("  %s\n", encryptedFile);
535 						printf("and updated hashes in\n");
536 						printf("  %s\n\n", dstSFO);
537 					}
538 				}
539 				else
540 				{
541 					char decryptedFile[258], encryptedFile[258];
542 					sprintf(encryptedFile,"%s/%s",srcPath ,dirList[workDir].saveFile);
543 					sprintf(decryptedFile,"%s/%s",dstPath ,dirList[workDir].saveFile);
544 
545 					printf("Decoding %s into %s\n",encryptedFile, decryptedFile);
546 
547 					int ret = decrypt_file(decryptedFile, encryptedFile, encryptInfo.key[0] != 0 ? encryptInfo.key : NULL, GetSDKMainVersion(encryptInfo.sdkVersion));
548 
549 					if(ret < 0) {
550 						printf("Error: decrypt_file() returned %d\n\n", ret);
551 					} else {
552 						printf("Successfully wrote %d bytes to\n", ret);
553 						printf("  %s\n", decryptedFile);
554 					}
555 				}
556 				printf("   > Back\n");
557 
558 				int input = ProcessInput(1, &selectedOption);
559 				if(input >= 0)
560 				{
561 					if(currentMenu == 3)
562 						currentMenu = 2;
563 					else
564 						currentMenu = 5;
565 					selectedOption = 0;
566 				}
567 			}
568 		}
569 		break;
570 		default:
571 			sceKernelExitGame();
572 			break;
573 		}
574 
575 		pspDebugScreenClear();
576 		sceDisplayWaitVblankStart();
577 		sceGuSwapBuffers();
578 	}
579 	return 0;
580 }
581