mirror of
https://github.com/libretro/ppsspp.git
synced 2025-01-31 13:52:21 +00:00
* Add PSP Homebrew for encode/decode save between PSP and PPSSPP.
This commit is contained in:
parent
020e4e7f13
commit
f965fb7623
44
Tools/SaveTool/Makefile
Normal file
44
Tools/SaveTool/Makefile
Normal file
@ -0,0 +1,44 @@
|
||||
SUBDIRS = kernelcall
|
||||
release:
|
||||
@for i in $(SUBDIRS); do echo "make all in $$i..."; (cd $$i; $(MAKE) release; cp $$i.prx ..); done
|
||||
make all
|
||||
|
||||
allclean:
|
||||
make clean
|
||||
@for i in $(SUBDIRS); do echo "Clearing in $$i..."; (cd $$i; $(MAKE) clean; rm -rf $$i.prx); done
|
||||
|
||||
TARGET = ppssppsavetool
|
||||
OBJS = main.o decrypt.o encrypt.o hash.o psf.o $(KERNELCALL_OBJS)
|
||||
|
||||
BUILD_PRX=1
|
||||
|
||||
|
||||
EXTRA_TARGETS = EBOOT.PBP
|
||||
PSP_EBOOT_TITLE = PPSSPP Save Tool
|
||||
|
||||
PSP_FW_VERSION=350
|
||||
|
||||
INCDIR =
|
||||
CFLAGS = -O0 -G0 -Wall -g
|
||||
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
|
||||
ASFLAGS = $(CFLAGS)
|
||||
|
||||
KERNELCALL_OBJS = kernelcall_0000.o \
|
||||
kernelcall_0001.o \
|
||||
kernelcall_0002.o \
|
||||
kernelcall_0003.o \
|
||||
kernelcall_0004.o \
|
||||
kernelcall_0005.o \
|
||||
kernelcall_0006.o \
|
||||
kernelcall_0007.o
|
||||
|
||||
$(KERNELCALL_OBJS): kernelcall/kernelcall.S
|
||||
psp-gcc $(CFLAGS) -DF_$* $< -c -o $@
|
||||
|
||||
LIBDIR =
|
||||
LDFLAGS =
|
||||
|
||||
LIBS = -lpsputility -lpspgum -lpspgu -lm
|
||||
|
||||
PSPSDK=$(shell psp-config --pspsdk-path)
|
||||
include $(PSPSDK)/lib/build.mak
|
77
Tools/SaveTool/README
Normal file
77
Tools/SaveTool/README
Normal file
@ -0,0 +1,77 @@
|
||||
|
||||
PSP Tool for encoding save data from PPSSPP to read on PSP and decoding
|
||||
PSP save to read on PPSSPP.
|
||||
|
||||
Alpha version. Was only tested on Project Dive Extend.
|
||||
|
||||
|
||||
Build
|
||||
=====
|
||||
|
||||
Require PSP Toolchain with PSP SDK
|
||||
|
||||
|
||||
make release
|
||||
This will build ppssppsavetool.prx and kernelcall.prx.
|
||||
|
||||
clean : make allclean
|
||||
|
||||
|
||||
How to use
|
||||
==========
|
||||
|
||||
Require PSP Toolchain with PRX Link and usb link, pspsh to work. Seems
|
||||
there is limitation for directories listing when running directly the
|
||||
tool from the PSP.
|
||||
|
||||
A) PREPARING DATA
|
||||
=================
|
||||
|
||||
1) Play the game on PPSSPP until a game save is done. This will create
|
||||
the file "ENCRYPT_INFO.BIN" in the save directory. It contain the
|
||||
encoding key and sdk version of the game. This file is the same for all
|
||||
save of the game, so you can use it on different save folder without
|
||||
the need to make them all on PPSSPP.
|
||||
|
||||
2) Run usbhostfs_pc. Then mount .ppsspp/PSP/SAVEDATA directory :
|
||||
mount 1 <path to .ppsspp/PSP/SAVEDATA>
|
||||
If you enter "drives", you should see host1: mapped to the directory.
|
||||
|
||||
3) Run PRX Link on the psp, and pspsh on the PC.
|
||||
|
||||
B) ENCODING A SAVE FROM PPSSPP TO PSP
|
||||
=====================================
|
||||
|
||||
1) From pspsh, run ppssppsavetool.prx.
|
||||
|
||||
2) In the menu, select "Encrypt", "host1:/". This will list the
|
||||
directories in ppsspp save directory which can be encoded.
|
||||
For a directory to appear, it must contain the ENCRYPT_INFO.BIN file,
|
||||
and the files of a ppsspp save.
|
||||
|
||||
3) After selecting the directory to encode, it will be copied into
|
||||
the PSP/SAVEGAME directory and encoded. Now you can play the save on
|
||||
the PSP.
|
||||
|
||||
C) DECODING A SAVE FROM PSP TO PPSSPP
|
||||
=====================================
|
||||
|
||||
1) You should have a directory in the PPSSPP save directory with the
|
||||
same name that the PSP save you want to convert. And in it, the
|
||||
"ENCRYPT_INFO.BIN" file.
|
||||
|
||||
Ex : You want to decrypt the save in ms0:/PSP/SAVEDATA/XXXXYYY/
|
||||
You create .ppsspp/PSP/SAVEDATA/XXXXYYY/ if not existing, and put the
|
||||
"ENCRYPT_INFO.BIN" generated before in it.
|
||||
|
||||
2) From pspsh, run ppssppsavetool.prx.
|
||||
|
||||
3) In the menu, select "decrypt", "host1:/". You should see your
|
||||
directory in the list.
|
||||
|
||||
4) After selecting it, the game decode the save and save it into the
|
||||
PPSSPP save directory. You can now launch your game in PPSSPP and load
|
||||
the save.
|
||||
|
||||
|
||||
|
164
Tools/SaveTool/decrypt.c
Normal file
164
Tools/SaveTool/decrypt.c
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* PSP Software Development Kit - http://www.pspdev.org
|
||||
* -----------------------------------------------------------------------
|
||||
* Licensed under the BSD license, see LICENSE in PSPSDK root for details.
|
||||
*
|
||||
* decrypt.c - Decryption routines using sceChnnlsv
|
||||
*
|
||||
* Copyright (c) 2005 Jim Paris <jim@jtan.com>
|
||||
* Coypright (c) 2005 psp123
|
||||
*
|
||||
* $Id: decrypt.c 1562 2005-12-10 20:52:45Z jim $
|
||||
*/
|
||||
|
||||
#include "decrypt.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <pspchnnlsv.h>
|
||||
#include "kernelcall/kernelcall.h"
|
||||
|
||||
unsigned int align16(unsigned int v)
|
||||
{
|
||||
return ((v + 0xF) >> 4) << 4;
|
||||
}
|
||||
|
||||
/* Read, decrypt, and write a savedata file. See main.c for example usage. */
|
||||
int decrypt_file(const char *decrypted_filename,
|
||||
const char *encrypted_filename,
|
||||
const unsigned char *gamekey,
|
||||
const int mainSdkVersion)
|
||||
{
|
||||
FILE *in, *out;
|
||||
int len, aligned_len;
|
||||
unsigned char *data, *cryptkey;
|
||||
int retval;
|
||||
|
||||
/* Open file and get size */
|
||||
|
||||
if ((in = fopen(encrypted_filename, "r")) == NULL) {
|
||||
retval = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fseek(in, 0, SEEK_END);
|
||||
len = ftell(in);
|
||||
fseek(in, 0, SEEK_SET);
|
||||
|
||||
if (len <= 0) {
|
||||
retval = -2;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
/* Allocate buffers */
|
||||
|
||||
aligned_len = align16(len);
|
||||
|
||||
if ((data = (unsigned char *) memalign(0x10, aligned_len)) == NULL) {
|
||||
retval = -3;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
if ((cryptkey = (unsigned char *) memalign(0x10, 0x10)) == NULL) {
|
||||
retval = -4;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
/* Fill buffers */
|
||||
|
||||
if (gamekey != NULL)
|
||||
memcpy(cryptkey, gamekey, 0x10);
|
||||
|
||||
memset(data + len, 0, aligned_len - len);
|
||||
if (fread(data, 1, len, in) != len) {
|
||||
retval = -5;
|
||||
goto out3;
|
||||
}
|
||||
|
||||
/* Do the decryption */
|
||||
|
||||
if ((retval = decrypt_data( gamekey ? (mainSdkVersion >= 4 ? 5 : 3) : 1, // 5 for sdk >= 4, else 3
|
||||
data, &len, &aligned_len,
|
||||
gamekey ? cryptkey : NULL)) < 0) {
|
||||
retval -= 100;
|
||||
goto out3;
|
||||
}
|
||||
|
||||
/* Write the data out. decrypt_data has set len correctly. */
|
||||
|
||||
if ((out = fopen(decrypted_filename, "w")) == NULL) {
|
||||
retval = -6;
|
||||
goto out3;
|
||||
}
|
||||
|
||||
if (fwrite(data, 1, len, out) != len) {
|
||||
retval = -7;
|
||||
goto out4;
|
||||
}
|
||||
|
||||
/* All done. Return file length. */
|
||||
retval = len;
|
||||
out4:
|
||||
fclose(out);
|
||||
out3:
|
||||
free(cryptkey);
|
||||
out2:
|
||||
free(data);
|
||||
out1:
|
||||
fclose(in);
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Do the actual hardware decryption.
|
||||
mode is 3 for saves with a cryptkey, or 1 otherwise
|
||||
data, dataLen, and cryptkey must be multiples of 0x10.
|
||||
cryptkey is NULL if mode == 1.
|
||||
*/
|
||||
int decrypt_data(unsigned int mode,
|
||||
unsigned char *data,
|
||||
int *dataLen,
|
||||
int *alignedLen,
|
||||
unsigned char *cryptkey)
|
||||
{
|
||||
pspChnnlsvContext1 ctx1;
|
||||
pspChnnlsvContext2 ctx2;
|
||||
|
||||
/* Need a 16-byte IV plus some data */
|
||||
if (*alignedLen <= 0x10)
|
||||
return -1;
|
||||
*dataLen -= 0x10;
|
||||
*alignedLen -= 0x10;
|
||||
|
||||
/* Set up buffers */
|
||||
memset(&ctx1, 0, sizeof(pspChnnlsvContext1));
|
||||
memset(&ctx2, 0, sizeof(pspChnnlsvContext2));
|
||||
|
||||
/* Perform the magic */
|
||||
if (sceChnnlsv_E7833020_(&ctx1, mode) < 0)
|
||||
return -2;
|
||||
if (sceChnnlsv_ABFDFC8B_(&ctx2, mode, 2, data, cryptkey) < 0)
|
||||
return -3;
|
||||
if (sceChnnlsv_F21A1FCA_(&ctx1, data, 0x10) < 0)
|
||||
return -4;
|
||||
if (sceChnnlsv_F21A1FCA_(&ctx1, data + 0x10, *alignedLen) < 0)
|
||||
return -5;
|
||||
if (sceChnnlsv_850A7FA1_(&ctx2, data + 0x10, *alignedLen) < 0)
|
||||
return -6;
|
||||
|
||||
/* Verify that it decrypted correctly */
|
||||
if (sceChnnlsv_21BE78B4_(&ctx2) < 0)
|
||||
return -7;
|
||||
|
||||
/* If desired, a new file hash from this PSP can be computed now:
|
||||
if (sceChnnlsv_C4C494F8(ctx1, newhash, cryptkey) < 0)
|
||||
return -8;
|
||||
*/
|
||||
|
||||
/* The decrypted data starts at data + 0x10, so shift it back. */
|
||||
memmove(data, data + 0x10, *dataLen);
|
||||
|
||||
/* All done */
|
||||
return 0;
|
||||
}
|
31
Tools/SaveTool/decrypt.h
Normal file
31
Tools/SaveTool/decrypt.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* PSP Software Development Kit - http://www.pspdev.org
|
||||
* -----------------------------------------------------------------------
|
||||
* Licensed under the BSD license, see LICENSE in PSPSDK root for details.
|
||||
*
|
||||
* decrypt.h - Declarations for functions in decrypt.c
|
||||
*
|
||||
* Copyright (c) 2005 Jim Paris <jim@jtan.com>
|
||||
* Coypright (c) 2005 psp123
|
||||
*
|
||||
* $Id: decrypt.h 1562 2005-12-10 20:52:45Z jim $
|
||||
*/
|
||||
|
||||
#include <pspchnnlsv.h>
|
||||
|
||||
/* Detect the samegame format and decrypt it. See main.c for an example. */
|
||||
int decrypt_file(const char *decrypted_filename,
|
||||
const char *encrypted_filename,
|
||||
const unsigned char *gamekey,
|
||||
const int mainSdkVersion);
|
||||
|
||||
/* Do the actual hardware decryption.
|
||||
mode is 3 for saves with a cryptkey, or 1 otherwise.
|
||||
data, alignedLen, and cryptkey must be multiples of 0x10.
|
||||
cryptkey is NULL if mode == 1.
|
||||
*/
|
||||
int decrypt_data(unsigned int mode,
|
||||
unsigned char *data,
|
||||
int *dataLen,
|
||||
int *alignedLen,
|
||||
unsigned char *cryptkey);
|
233
Tools/SaveTool/encrypt.c
Normal file
233
Tools/SaveTool/encrypt.c
Normal file
@ -0,0 +1,233 @@
|
||||
/*
|
||||
* PSP Software Development Kit - http://www.pspdev.org
|
||||
* -----------------------------------------------------------------------
|
||||
* Licensed under the BSD license, see LICENSE in PSPSDK root for details.
|
||||
*
|
||||
* encrypt.c - Encryption routines using sceChnnlsv
|
||||
*
|
||||
* Copyright (c) 2005 Jim Paris <jim@jtan.com>
|
||||
* Coypright (c) 2005 psp123
|
||||
*
|
||||
* $Id: encrypt.c 1560 2005-12-10 01:16:32Z jim $
|
||||
*/
|
||||
|
||||
#include "encrypt.h"
|
||||
#include "hash.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <pspchnnlsv.h>
|
||||
#include "kernelcall/kernelcall.h"
|
||||
|
||||
static inline int align16(unsigned int v)
|
||||
{
|
||||
return ((v + 0xF) >> 4) << 4;
|
||||
}
|
||||
|
||||
int fopen_getsize(const char *filename, FILE **fd, int *size)
|
||||
{
|
||||
if ((*fd = fopen(filename, "r")) == NULL)
|
||||
return -1;
|
||||
|
||||
fseek(*fd, 0, SEEK_END);
|
||||
*size = ftell(*fd);
|
||||
fseek(*fd, 0, SEEK_SET);
|
||||
|
||||
if (*size <= 0) {
|
||||
fclose(*fd);
|
||||
return -2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Encrypt the given plaintext file, and update the message
|
||||
authentication hashes in the param.sfo. The data_filename is
|
||||
usually the final component of encrypted_filename, e.g. "DATA.BIN".
|
||||
See main.c for an example of usage. */
|
||||
int encrypt_file(const char *plaintext_filename,
|
||||
const char *encrypted_filename,
|
||||
const char *data_filename,
|
||||
const char *paramsfo_filename,
|
||||
const char *paramsfo_filename_out,
|
||||
const unsigned char *gamekey,
|
||||
const int mainSdkVersion)
|
||||
{
|
||||
FILE *in = NULL, *out = NULL, *sfo = NULL;
|
||||
unsigned char *data = NULL, *cryptkey = NULL, *hash = NULL;
|
||||
unsigned char paramsfo[0x1330];
|
||||
int len, aligned_len, tmp;
|
||||
int retval;
|
||||
|
||||
/* Open plaintext and param.sfo files and get size */
|
||||
|
||||
if (fopen_getsize(plaintext_filename, &in, &len) < 0) {
|
||||
retval = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fopen_getsize(paramsfo_filename, &sfo, &tmp) < 0) {
|
||||
retval = -2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Verify size of param.sfo; all known saves use this size */
|
||||
|
||||
if (tmp != 0x1330) {
|
||||
retval = -3;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Allocate buffers. data has 0x10 bytes extra for the IV. */
|
||||
|
||||
aligned_len = align16(len);
|
||||
|
||||
if ((data =
|
||||
(unsigned char *) memalign(0x10, aligned_len + 0x10)) == NULL) {
|
||||
retval = -4;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((cryptkey = (unsigned char *) memalign(0x10, 0x10)) == NULL) {
|
||||
retval = -5;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((hash = (unsigned char *) memalign(0x10, 0x10)) == NULL) {
|
||||
retval = -6;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Fill buffers. */
|
||||
|
||||
memset(data + len, 0, aligned_len - len);
|
||||
if (fread(data, 1, len, in) != len) {
|
||||
retval = -7;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fread(paramsfo, 1, 0x1330, sfo) != 0x1330) {
|
||||
retval = -8;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (gamekey != NULL)
|
||||
memcpy(cryptkey, gamekey, 0x10);
|
||||
|
||||
/* Do the encryption */
|
||||
|
||||
if ((retval = encrypt_data( gamekey ? (mainSdkVersion >= 4 ? 5 : 3) : 1, // 5 for sdk >= 4, 3 otherwise
|
||||
data,
|
||||
&len, &aligned_len,
|
||||
hash,
|
||||
gamekey ? cryptkey : NULL)) < 0) {
|
||||
retval -= 1000;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Update the param.sfo hashes */
|
||||
|
||||
if ((retval = update_hashes(paramsfo, 0x1330,
|
||||
data_filename, hash,
|
||||
gamekey ? 3 : 1)) < 0) {
|
||||
retval -= 2000;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Write the data to the file. encrypt_data has already set len. */
|
||||
|
||||
if ((out = fopen(encrypted_filename, "w")) == NULL) {
|
||||
retval = -9;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fwrite(data, 1, len, out) != len) {
|
||||
retval = -10;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Reopen param.sfo, and write the updated copy out. */
|
||||
|
||||
fclose(sfo);
|
||||
if ((sfo = fopen(paramsfo_filename_out, "w")) == NULL) {
|
||||
retval = -11;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fwrite(paramsfo, 1, 0x1330, sfo) != 0x1330) {
|
||||
retval = -12;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* All done. Return file length. */
|
||||
|
||||
retval = len;
|
||||
|
||||
out:
|
||||
if(out) fclose(out);
|
||||
if(hash) free(hash);
|
||||
if(cryptkey) free(cryptkey);
|
||||
if(data) free(data);
|
||||
if(sfo) fclose(sfo);
|
||||
if(in) fclose(in);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Do the actual hardware encryption.
|
||||
mode is 3 for saves with a cryptkey, or 1 otherwise
|
||||
data, dataLen, and cryptkey must be multiples of 0x10.
|
||||
cryptkey is NULL if mode == 1.
|
||||
*/
|
||||
int encrypt_data(unsigned int mode,
|
||||
unsigned char *data,
|
||||
int *dataLen,
|
||||
int *alignedLen,
|
||||
unsigned char *hash,
|
||||
unsigned char *cryptkey)
|
||||
{
|
||||
pspChnnlsvContext1 ctx1;
|
||||
pspChnnlsvContext2 ctx2;
|
||||
|
||||
/* Make room for the IV in front of the data. */
|
||||
memmove(data + 0x10, data, *alignedLen);
|
||||
|
||||
/* Set up buffers */
|
||||
memset(&ctx1, 0, sizeof(pspChnnlsvContext1));
|
||||
memset(&ctx2, 0, sizeof(pspChnnlsvContext2));
|
||||
memset(hash, 0, 0x10);
|
||||
memset(data, 0, 0x10);
|
||||
|
||||
/* Build the 0x10-byte IV and setup encryption */
|
||||
if (sceChnnlsv_ABFDFC8B_(&ctx2, mode, 1, data, cryptkey) < 0)
|
||||
return -1;
|
||||
if (sceChnnlsv_E7833020_(&ctx1, mode) < 0)
|
||||
return -2;
|
||||
if (sceChnnlsv_F21A1FCA_(&ctx1, data, 0x10) < 0)
|
||||
return -3;
|
||||
if (sceChnnlsv_850A7FA1_(&ctx2, data + 0x10, *alignedLen) < 0)
|
||||
return -4;
|
||||
|
||||
/* Clear any extra bytes left from the previous steps */
|
||||
memset(data + 0x10 + *dataLen, 0, *alignedLen - *dataLen);
|
||||
|
||||
/* Encrypt the data */
|
||||
if (sceChnnlsv_F21A1FCA_(&ctx1, data + 0x10, *alignedLen) < 0)
|
||||
return -5;
|
||||
|
||||
/* Verify encryption */
|
||||
if (sceChnnlsv_21BE78B4_(&ctx2) < 0)
|
||||
return -6;
|
||||
|
||||
/* Build the file hash from this PSP */
|
||||
if (sceChnnlsv_C4C494F8_(&ctx1, hash, cryptkey) < 0)
|
||||
return -7;
|
||||
|
||||
/* Adjust sizes to account for IV */
|
||||
*alignedLen += 0x10;
|
||||
*dataLen += 0x10;
|
||||
|
||||
/* All done */
|
||||
return 0;
|
||||
}
|
38
Tools/SaveTool/encrypt.h
Normal file
38
Tools/SaveTool/encrypt.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* PSP Software Development Kit - http://www.pspdev.org
|
||||
* -----------------------------------------------------------------------
|
||||
* Licensed under the BSD license, see LICENSE in PSPSDK root for details.
|
||||
*
|
||||
* encrypt.h - Declarations for functions in encrypt.c
|
||||
*
|
||||
* Copyright (c) 2005 Jim Paris <jim@jtan.com>
|
||||
* Coypright (c) 2005 psp123
|
||||
*
|
||||
* $Id: encrypt.h 1559 2005-12-10 01:10:11Z jim $
|
||||
*/
|
||||
|
||||
#include <pspchnnlsv.h>
|
||||
|
||||
/* Encrypt the given plaintext file, and update the message
|
||||
authentication hashes in the param.sfo. The data_filename is
|
||||
usually the final component of encrypted_filename, e.g. "DATA.BIN".
|
||||
See main.c for an example of usage. */
|
||||
int encrypt_file(const char *plaintext_filename,
|
||||
const char *encrypted_filename,
|
||||
const char *data_filename,
|
||||
const char *paramsfo_filename,
|
||||
const char *paramsfo_filename_out,
|
||||
const unsigned char *gamekey,
|
||||
const int mainSdkVersion);
|
||||
|
||||
/* Do the actual hardware encryption.
|
||||
mode is 3 for saves with a cryptkey, or 1 otherwise.
|
||||
data, alignedLen, cryptkey, and hash must be multiples of 0x10.
|
||||
cryptkey is NULL if mode == 1.
|
||||
*/
|
||||
int encrypt_data(unsigned int mode,
|
||||
unsigned char *data,
|
||||
int *dataLen,
|
||||
int *alignedLen,
|
||||
unsigned char *hash,
|
||||
unsigned char *cryptkey);
|
129
Tools/SaveTool/hash.c
Normal file
129
Tools/SaveTool/hash.c
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* PSP Software Development Kit - http://www.pspdev.org
|
||||
* -----------------------------------------------------------------------
|
||||
* Licensed under the BSD license, see LICENSE in PSPSDK root for details.
|
||||
*
|
||||
* hash.c - Hashing routines using sceChnnlsv
|
||||
*
|
||||
* Copyright (c) 2005 Jim Paris <jim@jtan.com>
|
||||
* Coypright (c) 2005 psp123
|
||||
*
|
||||
* $Id: hash.c 1560 2005-12-10 01:16:32Z jim $
|
||||
*/
|
||||
|
||||
#include "hash.h"
|
||||
#include "psf.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <pspchnnlsv.h>
|
||||
#include "kernelcall/kernelcall.h"
|
||||
|
||||
static inline int align16(unsigned int v)
|
||||
{
|
||||
return ((v + 0xF) >> 4) << 4;
|
||||
}
|
||||
|
||||
/* Update the hashes in the param.sfo data, using
|
||||
the given file hash, and by computing the param.sfo hashes.
|
||||
filehash must be a multiple of 16 bytes, and is reused to
|
||||
store other hashes. The filename is e.g. "DATA.BIN". */
|
||||
int update_hashes(unsigned char *data,
|
||||
int len,
|
||||
const char *filename,
|
||||
unsigned char *filehash,
|
||||
int encryptmode)
|
||||
{
|
||||
int alignedLen = align16(len);
|
||||
unsigned char *datafile, *savedata_params;
|
||||
int listLen, paramsLen;
|
||||
int ret;
|
||||
|
||||
/* Locate SAVEDATA_PARAM section in the param.sfo. */
|
||||
if ((ret = find_psf_section("SAVEDATA_PARAMS", data, 0x1330,
|
||||
&savedata_params, ¶msLen)) < 0) {
|
||||
return ret - 100;
|
||||
}
|
||||
|
||||
/* Locate the pointer for this DATA.BIN equivalent */
|
||||
if ((ret = find_psf_section("SAVEDATA_FILE_LIST", data, 0x1330,
|
||||
&datafile, &listLen)) < 0) {
|
||||
return ret - 200;
|
||||
}
|
||||
|
||||
if ((ret = find_psf_datafile(filename, datafile,
|
||||
listLen, &datafile)) < 0) {
|
||||
return ret - 300;
|
||||
}
|
||||
|
||||
/* Check minimum sizes based on where we want to write */
|
||||
if ((listLen < 0x20) || (paramsLen < 0x80)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Clear params and insert file hash */
|
||||
memset(savedata_params, 0, paramsLen);
|
||||
memcpy(datafile + 0x0D, filehash, 0x10);
|
||||
|
||||
/* Compute 11D0 hash over entire file */
|
||||
if ((ret = build_hash(filehash, data, len, alignedLen,
|
||||
(encryptmode & 2) ? 4 : 2, NULL)) < 0) { // Not sure about "2"
|
||||
return ret - 400;
|
||||
}
|
||||
|
||||
/* Copy 11D0 hash to param.sfo and set flag indicating it's there */
|
||||
memcpy(savedata_params + 0x20, filehash, 0x10);
|
||||
*savedata_params |= 0x01;
|
||||
|
||||
/* If new encryption mode, compute and insert the 1220 hash. */
|
||||
if (encryptmode & 2) {
|
||||
|
||||
/* Enable the hash bit first */
|
||||
*savedata_params |= 0x20;
|
||||
|
||||
if ((ret = build_hash(filehash, data, len, alignedLen,
|
||||
3, 0)) < 0) {
|
||||
return ret - 500;
|
||||
}
|
||||
memcpy(savedata_params + 0x70, filehash, 0x10);
|
||||
}
|
||||
|
||||
/* Compute and insert the 11C0 hash. */
|
||||
if ((ret = build_hash(filehash, data, len, alignedLen, 1, 0)) < 0) {
|
||||
return ret - 600;
|
||||
}
|
||||
memcpy(savedata_params + 0x10, filehash, 0x10);
|
||||
|
||||
/* All done. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Build a single hash using the given data and mode.
|
||||
data and alignedLen must be multiples of 0x10.
|
||||
cryptkey is NULL for savedata. */
|
||||
int build_hash(unsigned char *output,
|
||||
unsigned char *data,
|
||||
unsigned int len,
|
||||
unsigned int alignedLen,
|
||||
int mode,
|
||||
unsigned char *cryptkey)
|
||||
{
|
||||
pspChnnlsvContext1 ctx1;
|
||||
|
||||
/* Set up buffers */
|
||||
memset(&ctx1, 0, sizeof(pspChnnlsvContext1));
|
||||
memset(output, 0, 0x10);
|
||||
memset(data + len, 0, alignedLen - len);
|
||||
|
||||
/* Perform the magic */
|
||||
if (sceChnnlsv_E7833020_(&ctx1, mode & 0xFF) < 0)
|
||||
return -1;
|
||||
if (sceChnnlsv_F21A1FCA_(&ctx1, data, alignedLen) < 0)
|
||||
return -2;
|
||||
if (sceChnnlsv_C4C494F8_(&ctx1, output, cryptkey) < 0)
|
||||
return -3;
|
||||
|
||||
/* All done. */
|
||||
return 0;
|
||||
}
|
34
Tools/SaveTool/hash.h
Normal file
34
Tools/SaveTool/hash.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* PSP Software Development Kit - http://www.pspdev.org
|
||||
* -----------------------------------------------------------------------
|
||||
* Licensed under the BSD license, see LICENSE in PSPSDK root for details.
|
||||
*
|
||||
* hash.h - Declarations for functions in hash.c
|
||||
*
|
||||
* Copyright (c) 2005 Jim Paris <jim@jtan.com>
|
||||
* Coypright (c) 2005 psp123
|
||||
*
|
||||
* $Id: hash.h 1559 2005-12-10 01:10:11Z jim $
|
||||
*/
|
||||
|
||||
#include <pspchnnlsv.h>
|
||||
|
||||
/* Update the hashes in the param.sfo data, using
|
||||
the given file hash, and by computing the param.sfo hashes.
|
||||
filehash must be a multiple of 16 bytes, and is reused to
|
||||
store other hashes. The filename is e.g. "DATA.BIN". */
|
||||
int update_hashes(unsigned char *data,
|
||||
int len,
|
||||
const char *filename,
|
||||
unsigned char *filehash,
|
||||
int encryptmode);
|
||||
|
||||
/* Build a single hash using the given data and mode.
|
||||
data, and alignedLen must be multiples of 0x10.
|
||||
cryptkey is NULL for savedata.*/
|
||||
int build_hash(unsigned char *output,
|
||||
unsigned char *data,
|
||||
unsigned int len,
|
||||
unsigned int alignedLen,
|
||||
int mode,
|
||||
unsigned char *cryptkey);
|
BIN
Tools/SaveTool/kernelcall.prx
Normal file
BIN
Tools/SaveTool/kernelcall.prx
Normal file
Binary file not shown.
24
Tools/SaveTool/kernelcall/Makefile
Normal file
24
Tools/SaveTool/kernelcall/Makefile
Normal file
@ -0,0 +1,24 @@
|
||||
release: all
|
||||
psp-build-exports -k exports.exp
|
||||
psp-build-exports -s -k -v exports.exp
|
||||
|
||||
TARGET = kernelcall
|
||||
OBJS = kernelcall.o
|
||||
|
||||
BUILD_PRX=1
|
||||
PRX_EXPORTS=exports.exp
|
||||
USE_KERNEL_LIBS = 1
|
||||
USE_KERNEL_LIBC = 1
|
||||
|
||||
INCDIR =
|
||||
CFLAGS = -O0 -G0 -Wall -g
|
||||
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
|
||||
ASFLAGS = $(CFLAGS)
|
||||
|
||||
LIBDIR =
|
||||
LDFLAGS = -mno-crt0 -nostartfiles
|
||||
|
||||
LIBS = -lpspchnnlsv
|
||||
|
||||
PSPSDK=$(shell psp-config --pspsdk-path)
|
||||
include $(PSPSDK)/lib/build.mak
|
84
Tools/SaveTool/kernelcall/kernelcall.c
Normal file
84
Tools/SaveTool/kernelcall/kernelcall.c
Normal file
@ -0,0 +1,84 @@
|
||||
#include <pspsdk.h>
|
||||
#include <pspkernel.h>
|
||||
#include <pspidstorage.h>
|
||||
#include <pspsysreg.h>
|
||||
#include <string.h>
|
||||
#include <pspchnnlsv.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
||||
PSP_MODULE_INFO("kernelcall", 0x1006, 2, 0);
|
||||
|
||||
|
||||
u64 sceSysreg_driver_4F46EEDE();
|
||||
|
||||
|
||||
u64 GetFuseId()
|
||||
{
|
||||
u64 fuseId = sceSysreg_driver_4F46EEDE();
|
||||
return fuseId;
|
||||
}
|
||||
|
||||
int sceChnnlsv_E7833020_(pspChnnlsvContext1 *ctx, int mode)
|
||||
{
|
||||
int k1 = pspSdkSetK1(0);
|
||||
int res = sceChnnlsv_E7833020(ctx,mode);
|
||||
|
||||
pspSdkSetK1(k1);
|
||||
return res;
|
||||
}
|
||||
|
||||
int sceChnnlsv_F21A1FCA_(pspChnnlsvContext1 *ctx, unsigned char *data, int len)
|
||||
{
|
||||
int k1 = pspSdkSetK1(0);
|
||||
int res = sceChnnlsv_F21A1FCA(ctx,data,len);
|
||||
pspSdkSetK1(k1);
|
||||
return res;
|
||||
}
|
||||
|
||||
int sceChnnlsv_C4C494F8_(pspChnnlsvContext1 *ctx,
|
||||
unsigned char *hash, unsigned char *cryptkey)
|
||||
{
|
||||
int k1 = pspSdkSetK1(0);
|
||||
int res = sceChnnlsv_C4C494F8(ctx,hash,cryptkey);
|
||||
pspSdkSetK1(k1);
|
||||
return res;
|
||||
}
|
||||
|
||||
int sceChnnlsv_ABFDFC8B_(pspChnnlsvContext2 *ctx, int mode1, int mode2,
|
||||
unsigned char *hashkey, unsigned char *cipherkey)
|
||||
{
|
||||
int k1 = pspSdkSetK1(0);
|
||||
int res = sceChnnlsv_ABFDFC8B(ctx,mode1,mode2,hashkey,cipherkey);
|
||||
pspSdkSetK1(k1);
|
||||
return res;
|
||||
}
|
||||
|
||||
int sceChnnlsv_850A7FA1_(pspChnnlsvContext2 *ctx, unsigned char *data, int len)
|
||||
{
|
||||
int k1 = pspSdkSetK1(0);
|
||||
int res = sceChnnlsv_850A7FA1(ctx,data,len);
|
||||
pspSdkSetK1(k1);
|
||||
return res;
|
||||
}
|
||||
|
||||
int sceChnnlsv_21BE78B4_(pspChnnlsvContext2 *ctx)
|
||||
{
|
||||
int k1 = pspSdkSetK1(0);
|
||||
int res = sceChnnlsv_21BE78B4(ctx);
|
||||
pspSdkSetK1(k1);
|
||||
return res;
|
||||
}
|
||||
|
||||
int module_start(SceSize args, void *argp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int module_stop()
|
||||
{
|
||||
return 0;
|
||||
}
|
9
Tools/SaveTool/kernelcall/kernelcall.h
Normal file
9
Tools/SaveTool/kernelcall/kernelcall.h
Normal file
@ -0,0 +1,9 @@
|
||||
u64 GetFuseId();
|
||||
int sceChnnlsv_E7833020_(pspChnnlsvContext1 *ctx, int mode);
|
||||
int sceChnnlsv_F21A1FCA_(pspChnnlsvContext1 *ctx, unsigned char *data, int len);
|
||||
int sceChnnlsv_C4C494F8_(pspChnnlsvContext1 *ctx,
|
||||
unsigned char *hash, unsigned char *cryptkey);
|
||||
int sceChnnlsv_ABFDFC8B_(pspChnnlsvContext2 *ctx, int mode1, int mode2,
|
||||
unsigned char *hashkey, unsigned char *cipherkey);
|
||||
int sceChnnlsv_850A7FA1_(pspChnnlsvContext2 *ctx, unsigned char *data, int len);
|
||||
int sceChnnlsv_21BE78B4_(pspChnnlsvContext2 *ctx);
|
579
Tools/SaveTool/main.c
Normal file
579
Tools/SaveTool/main.c
Normal file
@ -0,0 +1,579 @@
|
||||
#include <pspsdk.h>
|
||||
#include <pspkernel.h>
|
||||
#include <pspdebug.h>
|
||||
#include <pspmoduleinfo.h>
|
||||
#include <pspctrl.h>
|
||||
#include <pspchnnlsv.h>
|
||||
#include <psputility.h>
|
||||
#include "kernelcall/kernelcall.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "encrypt.h"
|
||||
#include "decrypt.h"
|
||||
#include "psf.h"
|
||||
|
||||
#define printf pspDebugScreenPrintf
|
||||
|
||||
/* Define the module info section */
|
||||
PSP_MODULE_INFO("ppssppsavetool", 0, 1, 0);
|
||||
PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER);
|
||||
PSP_HEAP_SIZE_KB(-64);
|
||||
|
||||
#define ENCRYPT_FILE_VERSION 1
|
||||
|
||||
|
||||
int currentMenu = 0;
|
||||
int selectedOption = 0;
|
||||
int basePath = 0;
|
||||
int workDir = 0;
|
||||
|
||||
char *menuList0[] = {"Encrypt","Decrypt", "Exit", NULL};
|
||||
char *menuList1[] = {"ms0:/PSP/SAVEDATAPPSSPP/","host0:/","host1:/", "host2:/", "Back", NULL};
|
||||
char *menuList2[] = {"Back", NULL};
|
||||
|
||||
int GetSDKMainVersion(int sdkVersion)
|
||||
{
|
||||
if(sdkVersion > 0x307FFFF)
|
||||
return 6;
|
||||
if(sdkVersion > 0x300FFFF)
|
||||
return 5;
|
||||
if(sdkVersion > 0x206FFFF)
|
||||
return 4;
|
||||
if(sdkVersion > 0x205FFFF)
|
||||
return 3;
|
||||
if(sdkVersion >= 0x2000000)
|
||||
return 2;
|
||||
if(sdkVersion >= 0x1000000)
|
||||
return 1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
int ProcessInput(int maxOption, int *selectedOption)
|
||||
{
|
||||
SceCtrlData pad, oldpad;
|
||||
sceCtrlReadBufferPositive(&oldpad, 1);
|
||||
while(1)
|
||||
{
|
||||
sceCtrlReadBufferPositive(&pad, 1);
|
||||
|
||||
if (pad.Buttons != 0)
|
||||
{
|
||||
if (!(oldpad.Buttons & PSP_CTRL_CROSS) && pad.Buttons & PSP_CTRL_CROSS)
|
||||
{
|
||||
return *selectedOption;
|
||||
}
|
||||
else if (!(oldpad.Buttons & PSP_CTRL_UP) && pad.Buttons & PSP_CTRL_UP && *selectedOption > 0)
|
||||
{
|
||||
*selectedOption = *selectedOption-1;
|
||||
return -1;
|
||||
}
|
||||
else if (!(oldpad.Buttons & PSP_CTRL_DOWN) && pad.Buttons & PSP_CTRL_DOWN && *selectedOption < maxOption-1)
|
||||
{
|
||||
*selectedOption = *selectedOption + 1;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
oldpad = pad;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[30];
|
||||
char saveFile[30];
|
||||
int errorId;
|
||||
} DirInfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int fileVersion;
|
||||
u8 key[16];
|
||||
int sdkVersion;
|
||||
} EncryptFileInfo;
|
||||
|
||||
DirInfo dirList[128];
|
||||
int numDirList;
|
||||
DirInfo invalidDirList[128];
|
||||
int numInvalidDirList;
|
||||
|
||||
int FileExist(char* basePath, char* dirPath, char* fileName)
|
||||
{
|
||||
SceIoStat fileStat;
|
||||
char path[1024];
|
||||
sprintf(path,"%s%s/%s",basePath, dirPath, fileName);
|
||||
if(sceIoGetstat(path, &fileStat) < 0) // no file
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int FileRead(char* basePath, char* dirPath, char* fileName, u8* dataout, int size)
|
||||
{
|
||||
char path[1024];
|
||||
sprintf(path,"%s%s/%s",basePath, dirPath, fileName);
|
||||
SceUID fileId = sceIoOpen(path, PSP_O_RDONLY, 0777);
|
||||
if(fileId < 0)
|
||||
return -1;
|
||||
sceIoRead(fileId, dataout, size);
|
||||
sceIoClose(fileId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AddErrorDir(char* dirName, int error)
|
||||
{
|
||||
if(numInvalidDirList >= 128)
|
||||
return;
|
||||
DirInfo *inf = &invalidDirList[numInvalidDirList];
|
||||
strcpy(inf->name,dirName);
|
||||
inf->errorId = error;
|
||||
numInvalidDirList++;
|
||||
}
|
||||
|
||||
int UpdateValidDir(int isEncrypt)
|
||||
{
|
||||
numDirList = 0;
|
||||
numInvalidDirList = 0;
|
||||
|
||||
const char* pspPath = "ms0:/PSP/SAVEDATA/";
|
||||
|
||||
char* pathSrc;
|
||||
char* pathDst;
|
||||
if(isEncrypt)
|
||||
{
|
||||
pathSrc = menuList1[basePath];
|
||||
pathDst = pspPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
pathSrc = pspPath;
|
||||
pathDst = menuList1[basePath];
|
||||
}
|
||||
|
||||
int dfd;
|
||||
dfd = sceIoDopen(menuList1[basePath]);
|
||||
if(dfd >= 0)
|
||||
{
|
||||
SceIoDirent data;
|
||||
while(sceIoDread(dfd, &data) > 0 && numDirList < 128)
|
||||
{
|
||||
if(!(data.d_stat.st_attr & 0x10)) // is not a directory
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(data.d_name[0] == '.') // ignore "." and ".."
|
||||
continue;
|
||||
|
||||
if(FileExist(menuList1[basePath], data.d_name, "ENCRYPT_INFO.BIN") < 0)
|
||||
{
|
||||
AddErrorDir(data.d_name,1);
|
||||
continue;
|
||||
}
|
||||
|
||||
EncryptFileInfo encryptInfo;
|
||||
if(FileRead(menuList1[basePath], data.d_name, "ENCRYPT_INFO.BIN",(u8*)&encryptInfo,sizeof(encryptInfo)) < 0)
|
||||
{
|
||||
AddErrorDir(data.d_name,2);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(encryptInfo.fileVersion != ENCRYPT_FILE_VERSION) // Not good version
|
||||
{
|
||||
AddErrorDir(data.d_name,3);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(FileExist(pathSrc, data.d_name, "PARAM.SFO") < 0)
|
||||
{
|
||||
AddErrorDir(data.d_name,4);
|
||||
continue;
|
||||
}
|
||||
|
||||
u8 paramsfo[0x1330];
|
||||
if(FileRead(pathSrc, data.d_name, "PARAM.SFO",(u8*)¶msfo,0x1330) < 0)
|
||||
{
|
||||
AddErrorDir(data.d_name,5);
|
||||
continue;
|
||||
}
|
||||
|
||||
u8 *datafile;
|
||||
int listLen;
|
||||
if (find_psf_section("SAVEDATA_FILE_LIST", paramsfo, 0x1330,
|
||||
&datafile, &listLen) < 0)
|
||||
{
|
||||
AddErrorDir(data.d_name,6);
|
||||
continue;
|
||||
}
|
||||
if(datafile[0] == 0)
|
||||
{
|
||||
AddErrorDir(data.d_name,7);
|
||||
continue;
|
||||
}
|
||||
|
||||
char filename[32];
|
||||
strcpy(filename, (char*)datafile);
|
||||
|
||||
if(FileExist(pathSrc, data.d_name, filename) < 0)
|
||||
{
|
||||
AddErrorDir(data.d_name,8);
|
||||
continue;
|
||||
}
|
||||
|
||||
DirInfo *inf = &dirList[numDirList];
|
||||
inf->errorId = 0;
|
||||
strcpy(inf->name, data.d_name);
|
||||
strcpy(inf->saveFile, filename);
|
||||
|
||||
numDirList++;
|
||||
|
||||
}
|
||||
sceIoDclose(dfd);
|
||||
if(numDirList == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FileCopy(char* srcPath, char* destPath, char* fileName)
|
||||
{
|
||||
SceIoStat fileStat;
|
||||
char path[258];
|
||||
sprintf(path,"%s/%s",srcPath, fileName);
|
||||
|
||||
if(sceIoGetstat(path, &fileStat) < 0)
|
||||
return -1;
|
||||
u8* data = malloc(fileStat.st_size);
|
||||
|
||||
SceUID fileId = sceIoOpen(path, PSP_O_RDONLY, 0777);
|
||||
if(fileId < 0)
|
||||
{
|
||||
printf("Fail opening %s\n",path);
|
||||
return -1;
|
||||
}
|
||||
sceIoRead(fileId, data, fileStat.st_size);
|
||||
sceIoClose(fileId);
|
||||
|
||||
sprintf(path,"%s/%s",destPath, fileName);
|
||||
|
||||
fileId = sceIoOpen(path, PSP_O_WRONLY | PSP_O_CREAT, 0777);
|
||||
if(fileId < 0)
|
||||
{
|
||||
printf("Fail opening %s\n",path);
|
||||
return -1;
|
||||
}
|
||||
sceIoWrite(fileId, data, fileStat.st_size);
|
||||
sceIoClose(fileId);
|
||||
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
pspDebugScreenInit();
|
||||
|
||||
SceUID mod = pspSdkLoadStartModule ("flash0:/kd/chnnlsv.prx",PSP_MEMORY_PARTITION_KERNEL);
|
||||
if (mod < 0) {
|
||||
printf("Error 0x%08X loading/starting chnnlsv.prx.\n", mod);
|
||||
}
|
||||
|
||||
mod = pspSdkLoadStartModule ("kernelcall.prx",PSP_MEMORY_PARTITION_KERNEL);
|
||||
if (mod < 0) {
|
||||
printf("Error 0x%08X loading/starting kernelcall.prx.\n", mod);
|
||||
}
|
||||
|
||||
sceCtrlSetSamplingCycle(0);
|
||||
sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);
|
||||
for(;;)
|
||||
{
|
||||
printf("====================================================================");
|
||||
printf("PPSSPP Save Tool\n");
|
||||
printf("====================================================================\n\n\n");
|
||||
|
||||
switch(currentMenu)
|
||||
{
|
||||
|
||||
case 0:
|
||||
{
|
||||
int maxOption = 0;
|
||||
for(i = 0; menuList0[i]; i++)
|
||||
{
|
||||
if(i == selectedOption)
|
||||
printf(" > %s\n",menuList0[i]);
|
||||
else
|
||||
printf(" %s\n",menuList0[i]);
|
||||
maxOption++;
|
||||
}
|
||||
|
||||
int input = ProcessInput(maxOption, &selectedOption);
|
||||
if(input == 0)
|
||||
{
|
||||
currentMenu = 1;
|
||||
selectedOption = 0;
|
||||
}
|
||||
else if(input == 1)
|
||||
{
|
||||
currentMenu = 4;
|
||||
selectedOption = 0;
|
||||
}
|
||||
else if(input == 2)
|
||||
{
|
||||
sceKernelExitGame();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
case 1:
|
||||
{
|
||||
int maxOption = 0;
|
||||
printf("PPSSPP Decrypted Save Directory : \n");
|
||||
for(i = 0; menuList1[i]; i++)
|
||||
{
|
||||
if(i == selectedOption)
|
||||
printf(" > %s\n",menuList1[i]);
|
||||
else
|
||||
printf(" %s\n",menuList1[i]);
|
||||
maxOption++;
|
||||
}
|
||||
|
||||
int input = ProcessInput(maxOption, &selectedOption);
|
||||
if(input == maxOption-1)
|
||||
{
|
||||
if(currentMenu == 1)
|
||||
selectedOption = 0;
|
||||
else
|
||||
selectedOption = 1;
|
||||
currentMenu = 0;
|
||||
}
|
||||
else if(input >= 0)
|
||||
{
|
||||
basePath = selectedOption;
|
||||
if(currentMenu == 1)
|
||||
{
|
||||
currentMenu = 2;
|
||||
UpdateValidDir(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentMenu = 5;
|
||||
UpdateValidDir(0);
|
||||
}
|
||||
selectedOption = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
case 2:
|
||||
{
|
||||
int maxOption = 0;
|
||||
if(currentMenu == 2)
|
||||
printf("Save to encrypt : \n");
|
||||
else
|
||||
printf("Save to decrypt : \n");
|
||||
|
||||
if(numDirList == 0)
|
||||
{
|
||||
printf("No compatible data, see README for help on use\n");
|
||||
}
|
||||
for(i = 0; i < numDirList; i++)
|
||||
{
|
||||
if(i == selectedOption)
|
||||
printf(" > %s\n",dirList[i].name);
|
||||
else
|
||||
printf(" %s\n",dirList[i].name);
|
||||
maxOption++;
|
||||
}
|
||||
|
||||
for(i = 0; menuList2[i]; i++)
|
||||
{
|
||||
if((i+numDirList) == selectedOption)
|
||||
printf(" > %s\n",menuList2[i]);
|
||||
else
|
||||
printf(" %s\n",menuList2[i]);
|
||||
maxOption++;
|
||||
}
|
||||
|
||||
printf("\n Invalid path : \n");
|
||||
for(i = 0; i < numInvalidDirList && i < (22-numDirList); i++)
|
||||
{
|
||||
switch(invalidDirList[i].errorId)
|
||||
{
|
||||
case 1:
|
||||
printf(" %s : ENCRYPT_INFO.BIN not found\n",invalidDirList[i].name);
|
||||
break;
|
||||
case 2:
|
||||
printf(" %s : ENCRYPT_INFO.BIN read error\n",invalidDirList[i].name);
|
||||
break;
|
||||
case 3:
|
||||
printf(" %s : ENCRYPT_INFO.BIN wrong version\n",invalidDirList[i].name);
|
||||
break;
|
||||
case 4:
|
||||
printf(" %s : PARAM.SFO not found\n",invalidDirList[i].name);
|
||||
break;
|
||||
case 5:
|
||||
printf(" %s : PARAM.SFO read error\n",invalidDirList[i].name);
|
||||
break;
|
||||
case 6:
|
||||
printf(" %s : SAVEDATA_FILE_LIST not found in PARAM.SFO\n",invalidDirList[i].name);
|
||||
break;
|
||||
case 7:
|
||||
printf(" %s : no save name in SAVEDATA_FILE_LIST\n",invalidDirList[i].name);
|
||||
break;
|
||||
case 8:
|
||||
printf(" %s : no save found\n",invalidDirList[i].name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int input = ProcessInput(maxOption, &selectedOption);
|
||||
if(input == numDirList)
|
||||
{
|
||||
if(currentMenu == 2)
|
||||
currentMenu = 1;
|
||||
else
|
||||
currentMenu = 4;
|
||||
selectedOption = basePath;
|
||||
}
|
||||
else if(input >= 0)
|
||||
{
|
||||
if(currentMenu == 2)
|
||||
currentMenu = 3;
|
||||
else
|
||||
currentMenu = 6;
|
||||
workDir = input;
|
||||
selectedOption = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
case 3:
|
||||
{
|
||||
|
||||
EncryptFileInfo encryptInfo;
|
||||
if(FileRead(menuList1[basePath], dirList[workDir].name, "ENCRYPT_INFO.BIN",(u8*)&encryptInfo,sizeof(encryptInfo)) < 0)
|
||||
{
|
||||
printf("Can't read encrypt file\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Key : ");
|
||||
for(i = 0; i < 16; i++)
|
||||
printf(" %02x",(u8)encryptInfo.key[i]);
|
||||
printf("\n");
|
||||
printf("SDK Version : 0x%x\n",encryptInfo.sdkVersion);
|
||||
|
||||
char srcPath[128];
|
||||
char dstPath[128];
|
||||
if(currentMenu == 3)
|
||||
{
|
||||
sprintf(srcPath,"%s%s",menuList1[basePath], dirList[workDir].name);
|
||||
sprintf(dstPath,"ms0:/PSP/SAVEDATA/%s",dirList[workDir].name);
|
||||
sceIoMkdir(dstPath,0777);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(srcPath,"ms0:/PSP/SAVEDATA/%s",dirList[workDir].name);
|
||||
sprintf(dstPath,"%s%s",menuList1[basePath], dirList[workDir].name);
|
||||
}
|
||||
|
||||
int dfd;
|
||||
dfd = sceIoDopen(srcPath);
|
||||
if(dfd >= 0)
|
||||
{
|
||||
SceIoDirent dirinfo;
|
||||
while(sceIoDread(dfd, &dirinfo) > 0)
|
||||
{
|
||||
|
||||
if(!(dirinfo.d_stat.st_mode & 0x2000)) // is not a file
|
||||
continue;
|
||||
|
||||
if(strcmp(dirinfo.d_name,"ENCRYPT_INFO.BIN") == 0) // don't copy encrypt info
|
||||
continue;
|
||||
|
||||
FileCopy(srcPath, dstPath, dirinfo.d_name);
|
||||
|
||||
}
|
||||
sceIoDclose(dfd);
|
||||
}
|
||||
|
||||
if(currentMenu == 3)
|
||||
{
|
||||
|
||||
char decryptedFile[258], encryptedFile[258], srcSFO[258], dstSFO[258];
|
||||
sprintf(decryptedFile,"%s/%s",srcPath ,dirList[workDir].saveFile);
|
||||
sprintf(srcSFO,"%s/PARAM.SFO",srcPath);
|
||||
|
||||
sprintf(encryptedFile,"%s/%s",dstPath ,dirList[workDir].saveFile);
|
||||
sprintf(dstSFO,"%s/PARAM.SFO",dstPath);
|
||||
|
||||
printf("Encoding %s into %s\n",decryptedFile, encryptedFile);
|
||||
|
||||
int ret = encrypt_file(decryptedFile,
|
||||
encryptedFile,
|
||||
dirList[workDir].saveFile,
|
||||
srcSFO,
|
||||
dstSFO,
|
||||
encryptInfo.key[0] != 0 ? encryptInfo.key : NULL,
|
||||
GetSDKMainVersion(encryptInfo.sdkVersion)
|
||||
);
|
||||
|
||||
if(ret < 0) {
|
||||
printf("Error: encrypt_file() returned %d\n\n", ret);
|
||||
} else {
|
||||
printf("Successfully wrote %d bytes to\n", ret);
|
||||
printf(" %s\n", encryptedFile);
|
||||
printf("and updated hashes in\n");
|
||||
printf(" %s\n\n", dstSFO);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char decryptedFile[258], encryptedFile[258];
|
||||
sprintf(encryptedFile,"%s/%s",srcPath ,dirList[workDir].saveFile);
|
||||
sprintf(decryptedFile,"%s/%s",dstPath ,dirList[workDir].saveFile);
|
||||
|
||||
printf("Decoding %s into %s\n",encryptedFile, decryptedFile);
|
||||
|
||||
int ret = decrypt_file(decryptedFile, encryptedFile, encryptInfo.key[0] != 0 ? encryptInfo.key : NULL, GetSDKMainVersion(encryptInfo.sdkVersion));
|
||||
|
||||
if(ret < 0) {
|
||||
printf("Error: decrypt_file() returned %d\n\n", ret);
|
||||
} else {
|
||||
printf("Successfully wrote %d bytes to\n", ret);
|
||||
printf(" %s\n", decryptedFile);
|
||||
}
|
||||
}
|
||||
printf(" > Back\n");
|
||||
|
||||
int input = ProcessInput(1, &selectedOption);
|
||||
if(input >= 0)
|
||||
{
|
||||
if(currentMenu == 3)
|
||||
currentMenu = 2;
|
||||
else
|
||||
currentMenu = 5;
|
||||
selectedOption = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sceKernelExitGame();
|
||||
break;
|
||||
}
|
||||
|
||||
pspDebugScreenClear();
|
||||
sceDisplayWaitVblankStart();
|
||||
sceGuSwapBuffers();
|
||||
}
|
||||
return 0;
|
||||
}
|
114
Tools/SaveTool/psf.c
Normal file
114
Tools/SaveTool/psf.c
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* PSP Software Development Kit - http://www.pspdev.org
|
||||
* -----------------------------------------------------------------------
|
||||
* Licensed under the BSD license, see LICENSE in PSPSDK root for details.
|
||||
*
|
||||
* psf.c - PSF parsing routines
|
||||
*
|
||||
* Copyright (c) 2005 Jim Paris <jim@jtan.com>
|
||||
* Coypright (c) 2005 psp123
|
||||
*
|
||||
* $Id: psf.c 1560 2005-12-10 01:16:32Z jim $
|
||||
*/
|
||||
|
||||
#include "psf.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <pspchnnlsv.h>
|
||||
|
||||
/* Find to the named section in the PSF file, and return an
|
||||
absolute pointer to it and the section size. */
|
||||
int find_psf_section(const char *name,
|
||||
unsigned char *data,
|
||||
int dataLen,
|
||||
unsigned char **location,
|
||||
int *size)
|
||||
{
|
||||
unsigned short int nameLoc;
|
||||
int i, magicHead, strLoc, headLen, numSects;
|
||||
int sectCurLen, sectBufLen, sectBufLoc, curPos;
|
||||
|
||||
if (dataLen < 0x14)
|
||||
return -1;
|
||||
|
||||
/* Get the basics from the header */
|
||||
magicHead = *(unsigned int *)&data[0x00];
|
||||
strLoc = *(unsigned int *)&data[0x08];
|
||||
headLen = *(unsigned int *)&data[0x0C];
|
||||
numSects = *(unsigned int *)&data[0x10];
|
||||
|
||||
/* Do some error checking */
|
||||
if (magicHead != 0x46535000)
|
||||
return -2;
|
||||
|
||||
/* Verify strLoc is proper */
|
||||
if ((strLoc > headLen) || (strLoc >= dataLen))
|
||||
return -3;
|
||||
|
||||
/* Verify headLen is proper */
|
||||
if (headLen >= dataLen)
|
||||
return -4;
|
||||
|
||||
/* Verify numSects is proper */
|
||||
if (numSects != ((strLoc - 0x14) / 0x10))
|
||||
return -5;
|
||||
|
||||
/* Process all sections */
|
||||
for (i = 0; i < numSects; i++)
|
||||
{
|
||||
/* Get the curPos */
|
||||
curPos = 0x14 + (i * 0x10);
|
||||
|
||||
/* Verify curPos is proper */
|
||||
if (curPos >= strLoc)
|
||||
return -6;
|
||||
|
||||
/* Get some basic info about this section */
|
||||
nameLoc = *(unsigned short *)&data[curPos];
|
||||
sectCurLen = *(unsigned short *)&data[curPos + 0x04];
|
||||
sectBufLen = *(unsigned short *)&data[curPos + 0x08];
|
||||
sectBufLoc = *(unsigned short *)&data[curPos + 0x0C];
|
||||
|
||||
/* Do some error checking */
|
||||
if ((nameLoc < dataLen) && (sectCurLen < dataLen)
|
||||
&& (sectBufLen < dataLen) && (sectBufLoc < dataLen))
|
||||
{
|
||||
/* Check if this is the section we want */
|
||||
if (!stricmp((char *)&data[strLoc + nameLoc], name))
|
||||
{
|
||||
/* Update the location and size */
|
||||
*location = &data[headLen + sectBufLoc];
|
||||
*size = sectBufLen;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Section was not found if it makes it here */
|
||||
return -7;
|
||||
}
|
||||
|
||||
/* Find the named file inside the FILE_LIST, and return
|
||||
an absolute pointer to it. */
|
||||
int find_psf_datafile(const char *name,
|
||||
unsigned char *filelist,
|
||||
int size,
|
||||
unsigned char **location)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Process all files */
|
||||
for (i = 0; (i + 0x0d) <= size; i += 0x20)
|
||||
{
|
||||
/* Check if this is the filename we want */
|
||||
if (!strncasecmp((char *)&filelist[i], name, 0x0d)) {
|
||||
*location = &filelist[i];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* File was not found if it makes it here */
|
||||
return -1;
|
||||
}
|
29
Tools/SaveTool/psf.h
Normal file
29
Tools/SaveTool/psf.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* PSP Software Development Kit - http://www.pspdev.org
|
||||
* -----------------------------------------------------------------------
|
||||
* Licensed under the BSD license, see LICENSE in PSPSDK root for details.
|
||||
*
|
||||
* psf.h - Declarations for functions in psf.c
|
||||
*
|
||||
* Copyright (c) 2005 Jim Paris <jim@jtan.com>
|
||||
* Coypright (c) 2005 psp123
|
||||
*
|
||||
* $Id: psf.h 1559 2005-12-10 01:10:11Z jim $
|
||||
*/
|
||||
|
||||
#include <pspchnnlsv.h>
|
||||
|
||||
/* Find the named section in the PSF file, and return an
|
||||
absolute pointer to it and the section size. */
|
||||
int find_psf_section(const char *name,
|
||||
unsigned char *data,
|
||||
int dataLen,
|
||||
unsigned char **location,
|
||||
int *size);
|
||||
|
||||
/* Find the named file inside the FILE_LIST, and return
|
||||
an absolute pointer to it. */
|
||||
int find_psf_datafile(const char *name,
|
||||
unsigned char *filelist,
|
||||
int size,
|
||||
unsigned char **location);
|
Loading…
x
Reference in New Issue
Block a user