Skip to content

Commit

Permalink
Auto detect save data type
Browse files Browse the repository at this point in the history
  • Loading branch information
illusion0001 committed Feb 29, 2024
1 parent 2ca05db commit 3053c83
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 90 deletions.
11 changes: 2 additions & 9 deletions .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,5 @@ Programs to decrypt/encrypt Persona 3 Reload saves

## Usage

Decrypt Save (Steam to Gamepass)
```
p3r-save decrypt <file>
```

Encrypt Save (Gamepass to Steam)
```
p3r-save encrypt <file>
```
- 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)
212 changes: 131 additions & 81 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>

char g_erronoMsg[256] = { 0 };
#define strerror_s_(buffer, sizeInBytes, errnum) \
Expand Down Expand Up @@ -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)
Expand All @@ -62,6 +63,18 @@ void write_file(const char* file_name, const unsigned char* data, size_t size)
fclose(file);
}

static int check_skipfile_exist(void)
{
FILE* file = NULL;
if (fopen_s(&file, "skip.txt", "rb") != 0)
{
return 0;
}
fclose(file);
printf_s("\nSkip file exist, closing instantly.");
return 1;
}

// 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)
{
Expand All @@ -81,135 +94,172 @@ 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 <file> OR %s encrypt <file>\n"
"Example: %s decrypt SaveData0001.sav\n\n"
"Usage: %s <file>\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"
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;
}

0 comments on commit 3053c83

Please sign in to comment.