diff --git a/Tools/SaveTool/Makefile b/Tools/SaveTool/Makefile new file mode 100644 index 000000000..fc7a38f73 --- /dev/null +++ b/Tools/SaveTool/Makefile @@ -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 diff --git a/Tools/SaveTool/README b/Tools/SaveTool/README new file mode 100644 index 000000000..0581e316b --- /dev/null +++ b/Tools/SaveTool/README @@ -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. + + + diff --git a/Tools/SaveTool/decrypt.c b/Tools/SaveTool/decrypt.c new file mode 100644 index 000000000..146572199 --- /dev/null +++ b/Tools/SaveTool/decrypt.c @@ -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; +} diff --git a/Tools/SaveTool/decrypt.h b/Tools/SaveTool/decrypt.h new file mode 100644 index 000000000..57b8881d6 --- /dev/null +++ b/Tools/SaveTool/decrypt.h @@ -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); diff --git a/Tools/SaveTool/encrypt.c b/Tools/SaveTool/encrypt.c new file mode 100644 index 000000000..8da226e15 --- /dev/null +++ b/Tools/SaveTool/encrypt.c @@ -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; +} diff --git a/Tools/SaveTool/encrypt.h b/Tools/SaveTool/encrypt.h new file mode 100644 index 000000000..2b06da21e --- /dev/null +++ b/Tools/SaveTool/encrypt.h @@ -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); diff --git a/Tools/SaveTool/hash.c b/Tools/SaveTool/hash.c new file mode 100644 index 000000000..0a83a4ca3 --- /dev/null +++ b/Tools/SaveTool/hash.c @@ -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; +} diff --git a/Tools/SaveTool/hash.h b/Tools/SaveTool/hash.h new file mode 100644 index 000000000..978627a69 --- /dev/null +++ b/Tools/SaveTool/hash.h @@ -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); diff --git a/Tools/SaveTool/kernelcall.prx b/Tools/SaveTool/kernelcall.prx new file mode 100644 index 000000000..350ee4017 Binary files /dev/null and b/Tools/SaveTool/kernelcall.prx differ diff --git a/Tools/SaveTool/kernelcall/Makefile b/Tools/SaveTool/kernelcall/Makefile new file mode 100644 index 000000000..a7138d7cd --- /dev/null +++ b/Tools/SaveTool/kernelcall/Makefile @@ -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 diff --git a/Tools/SaveTool/kernelcall/kernelcall.c b/Tools/SaveTool/kernelcall/kernelcall.c new file mode 100644 index 000000000..32f029168 --- /dev/null +++ b/Tools/SaveTool/kernelcall/kernelcall.c @@ -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; +} diff --git a/Tools/SaveTool/kernelcall/kernelcall.h b/Tools/SaveTool/kernelcall/kernelcall.h new file mode 100644 index 000000000..4baf4c2ed --- /dev/null +++ b/Tools/SaveTool/kernelcall/kernelcall.h @@ -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); diff --git a/Tools/SaveTool/main.c b/Tools/SaveTool/main.c new file mode 100644 index 000000000..54a2b34e9 --- /dev/null +++ b/Tools/SaveTool/main.c @@ -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; +} diff --git a/Tools/SaveTool/psf.c b/Tools/SaveTool/psf.c new file mode 100644 index 000000000..5f68b1cc7 --- /dev/null +++ b/Tools/SaveTool/psf.c @@ -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; +} diff --git a/Tools/SaveTool/psf.h b/Tools/SaveTool/psf.h new file mode 100644 index 000000000..261e241e8 --- /dev/null +++ b/Tools/SaveTool/psf.h @@ -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);