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, &paramsLen)) < 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*)&paramsfo,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);