diff --git a/.github/README.md b/.github/README.md index b2b03cf..46be41a 100644 --- a/.github/README.md +++ b/.github/README.md @@ -3,12 +3,6 @@ Programs to decrypt/encrypt Persona 3 Reload saves ## Usage -Decrypt Save (Steam to Gamepass) -``` -p3r-save decrypt -``` - -Encrypt Save (Gamepass to Steam) -``` -p3r-save encrypt -``` +- Download `p3r-save.exe` binary from [Release](https://github.com/illusion0001/P3R-Save-EnDecryptor/releases/latest) page. (You may need to **Assets** dropdown) +- Drag and drop save file into `p3r-save.exe`. + - Optionally you may create a file `skip.txt` next to program executable to skip the wait time before program closes. (for automated use, command line) diff --git a/main.c b/main.c index dde316f..fe3f5ca 100644 --- a/main.c +++ b/main.c @@ -2,6 +2,7 @@ #include #include #include +#include char g_erronoMsg[256] = { 0 }; #define strerror_s_(buffer, sizeInBytes, errnum) \ @@ -42,7 +43,7 @@ unsigned char* read_file(const char* file_name, size_t* size) return data; } -void write_file(const char* file_name, const unsigned char* data, size_t size) +void write_file(const char* file_name, const unsigned char* data, const size_t size) { FILE* file = NULL; if (fopen_s(&file, file_name, "wb") != 0) @@ -62,6 +63,22 @@ void write_file(const char* file_name, const unsigned char* data, size_t size) fclose(file); } +#define SKIP_FILE "skip.txt" + +static int check_skipfile_exist(void) +{ + FILE* file = NULL; + if (fopen_s(&file, SKIP_FILE, "rb") != 0) + { + return 0; + } + fclose(file); + printf_s("\nSkip file (" SKIP_FILE ") exist, closing instantly."); + return 1; +} + +#undef SKIP_FILE + // 0x14106A7D0 bytes 32 CA 0F B6 C1 0F B6 D1 C0 E8 04 80 E2 03 24 03 C0 E2 04 0A C2 80 E1 CC 0A C1 C3 unsigned char decrypt_byte(unsigned char data, unsigned char key) { @@ -81,25 +98,34 @@ void Sleep(int dwMilliseconds); #define WAIT_TIME 10 #define ONE_SEC (1 * 1000) // 1ms * 1000 +static void wait_program_quit(void) +{ +#ifndef _DEBUG + if (!check_skipfile_exist()) + { + int s = 0; + while (s < WAIT_TIME) + { + Sleep(ONE_SEC); + s++; + putchar('.'); + } + } +#endif +} + static void show_invalid_arg(const char* program_name) { printf_s("Invalid arguments\n" - "Usage: %s decrypt OR %s encrypt \n" - "Example: %s decrypt SaveData0001.sav\n\n" + "Usage: %s \n" + "Example: %s SaveData0001.sav\n\n" "Program will exit in %d seconds", - program_name, program_name, program_name, WAIT_TIME); - int s = 0; - while (s < WAIT_TIME) - { - Sleep(ONE_SEC); - s++; - putchar('.'); - } + program_name, program_name, WAIT_TIME); + wait_program_quit(); putchar('\n'); exit(EXIT_FAILURE); } -#undef WAIT_TIME #undef ONE_SEC #define SAVE_KEY "ae5zeitaix1joowooNgie3fahP5Ohph" @@ -107,109 +133,137 @@ const char* g_OrSaveKey = SAVE_KEY; const size_t g_keylen = (sizeof(SAVE_KEY) - 1); #undef SAVE_KEY +enum +{ + ENCRYPT_GVAS_MAGIC = 0x0B650015, + DECRYPT_GVAS_MAGIC = 0x53415647, +}; + +static void tell_save_magic(const uint32_t file_magic) +{ + switch (file_magic) + { + case ENCRYPT_GVAS_MAGIC: + { + printf_s("Detected encrypted save file. (0x%08x == 0x%08x)\n", file_magic, ENCRYPT_GVAS_MAGIC); + break; + } + case DECRYPT_GVAS_MAGIC: + { + printf_s("Detected decrypted save file. (0x%08x == 0x%08x)\n", file_magic, DECRYPT_GVAS_MAGIC); + break; + } + default: + { + printf_s("Unknown save file. (0x%08x)\n", file_magic); + break; + } + } +} + +static int check_magic(const unsigned char* data, const size_t data_size, const uint32_t magic, uint32_t* out_magic) +{ + if (data_size < sizeof(uint32_t)) + { + return 0; + } + uint32_t file_magic = *((uint32_t*)data); + if (out_magic) + { + *out_magic = file_magic; + } + return file_magic == magic; +} + int main(int argc, char** argv) { printf_s("p3r-save: Built " __DATE__ " @ " __TIME__ "\n"); - if (argc < 3) + if (argc < 2) { show_invalid_arg(argv[0]); } - size_t filesize = 0; - if (strcmp(argv[1], "decrypt") == 0 || strcmp(argv[1], "-d") == 0) + const char* save_path = argv[1]; + if (save_path && *save_path != '\0') { - unsigned char* test_data = read_file(argv[2], &filesize); - unsigned char* decrypted_data = (unsigned char*)malloc(filesize); - if (!decrypted_data) + size_t filesize = 0; + unsigned char* save_data = read_file(save_path, &filesize); + uint32_t file_magic = 0; + if (check_magic(save_data, filesize, ENCRYPT_GVAS_MAGIC, &file_magic)) { - strerror_s_(g_erronoMsg, sizeof(g_erronoMsg), errno); - printf_s("Error allocating memory: %s\n", g_erronoMsg); - exit(EXIT_FAILURE); - } + tell_save_magic(file_magic); + unsigned char* decrypted_data = (unsigned char*)malloc(filesize); + if (!decrypted_data) + { + strerror_s_(g_erronoMsg, sizeof(g_erronoMsg), errno); + printf_s("Error allocating memory: %s\n", g_erronoMsg); + exit(EXIT_FAILURE); + } - size_t key_idx = 0; + size_t key_idx = 0; - for (size_t i = 0; i < filesize; ++i) - { - if (key_idx >= g_keylen) + for (size_t i = 0; i < filesize; ++i) { - // reset index - key_idx = 0; + if (key_idx >= g_keylen) + { + // reset index + key_idx = 0; + } + decrypted_data[i] = decrypt_byte(save_data[i], g_OrSaveKey[key_idx]); + key_idx++; } - decrypted_data[i] = decrypt_byte(test_data[i], g_OrSaveKey[key_idx]); - key_idx++; - } - - write_file("decrypt_out.sav", decrypted_data, filesize); - if (test_data) - { - printf_s("free test_data: 0x%p\n", test_data); - free(test_data); - } - else - { - printf_s("failed to free test_data: 0x%p\n", test_data); - } - if (decrypted_data) - { - printf_s("free decrypted_data: 0x%p\n", decrypted_data); - free(decrypted_data); + write_file("decrypt_out.sav", decrypted_data, filesize); } - else + else if (check_magic(save_data, filesize, DECRYPT_GVAS_MAGIC, &file_magic)) { - printf_s("failed to free decrypted_data: 0x%p\n", decrypted_data); - } - } - else if (strcmp(argv[1], "encrypt") == 0 || strcmp(argv[1], "-e") == 0) - { - unsigned char* test_data = read_file(argv[2], &filesize); - unsigned char* encrypted_data = (unsigned char*)malloc(filesize); - - if (!encrypted_data) - { - strerror_s_(g_erronoMsg, sizeof(g_erronoMsg), errno); - printf_s("Error allocating memory: %s\n", g_erronoMsg); - exit(EXIT_FAILURE); - } + tell_save_magic(file_magic); + unsigned char* encrypted_data = (unsigned char*)malloc(filesize); + if (!encrypted_data) + { + strerror_s_(g_erronoMsg, sizeof(g_erronoMsg), errno); + printf_s("Error allocating memory: %s\n", g_erronoMsg); + exit(EXIT_FAILURE); + } - size_t key_idx = 0; + size_t key_idx = 0; - for (size_t i = 0; i < filesize; ++i) - { - if (key_idx >= g_keylen) + for (size_t i = 0; i < filesize; ++i) { - // reset index - key_idx = 0; + if (key_idx >= g_keylen) + { + // reset index + key_idx = 0; + } + encrypted_data[i] = encrypt_byte(save_data[i], g_OrSaveKey[key_idx]); + key_idx++; } - encrypted_data[i] = encrypt_byte(test_data[i], g_OrSaveKey[key_idx]); - key_idx++; - } - - write_file("encrypt_out.sav", encrypted_data, filesize); - if (test_data) - { - printf_s("free test_data: 0x%p\n", test_data); - free(test_data); + write_file("encrypt_out.sav", encrypted_data, filesize); } else { - printf_s("failed to free test_data: 0x%p\n", test_data); + tell_save_magic(file_magic); + printf_s("Invalid save file.\n"); + exit(EXIT_FAILURE); } - if (encrypted_data) + + if (save_data) { - printf_s("free encrypted_data: 0x%p\n", encrypted_data); - free(encrypted_data); + printf_s("free save_data: 0x%p\n", save_data); + free(save_data); } else { - printf_s("failed to free encrypted_data: 0x%p\n", encrypted_data); + printf_s("failed to free save_data: 0x%p\n", save_data); } } else { show_invalid_arg(argv[0]); } + printf_s("Program will exit in %d seconds", WAIT_TIME); + wait_program_quit(); + putchar('\n'); return 0; }