-
-
Save Bl4ckSh4rk/780a2c05aab9532dd9ea4c4aff4c01ea to your computer and use it in GitHub Desktop.
Pokémon Battle Revolution basic encryption/decryption functions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _CRT_SECURE_NO_WARNINGS | |
#define _SCL_SECURE_NO_WARNINGS | |
#include <cstdio> | |
#include <cstdlib> | |
#include <iostream> | |
#include <algorithm> | |
#include <cstdint> | |
using namespace std; | |
typedef uint8_t u8; | |
typedef uint16_t u16; | |
typedef uint32_t u32; | |
inline u16 read16(u8* buf) { | |
return (buf[0] << 8) | buf[1]; | |
} | |
inline void write16(u8* buf, u16 val) { | |
buf[1] = (u8)val; | |
buf[0] = (u8)(val >> 8); | |
} | |
inline u32 read32(u8* buf) { | |
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; | |
} | |
inline void write32(u8* buf, u32 val) { | |
buf[3] = (u8)val; | |
buf[2] = (u8)(val >> 8); | |
buf[1] = (u8)(val >> 16); | |
buf[0] = (u8)(val >> 24); | |
} | |
// THIS IS THE SAME ALGORITHM AS THE ONE USED IN Pokémon XD (with different boundaries) | |
inline void advanceKeys(u16 keys[4]) { | |
u16 a = keys[0] + 0x43, b = keys[1] + 0x29, c = keys[2] + 0x17, d = keys[3] + 0x13; | |
keys[0] = (a & 0xf) | ((b << 4) & 0xf0) | ((c << 8) & 0xf00) | ((d << 12) & 0xf000); | |
keys[1] = ((a >> 4) & 0xf) | (b & 0xf0) | ((c << 4) & 0xf00) | ((d << 8) & 0xf000); | |
keys[2] = (c & 0xf00) | ((b & 0xf00) >> 4) | ((a & 0xf00) >> 8) | ((d << 4) & 0xf000); | |
keys[3] = ((a >> 12) & 0xf) | ((b >> 8) & 0xf0) | ((c >> 4) & 0xf00) | (d & 0xf000); | |
} | |
void decryptDataStructure(u8* in, u8* out, size_t size) { | |
copy(in, in + 8, out); | |
u16 keys[4] = { 0 }; | |
for (size_t i = 0; i < 4; ++i) keys[i] = read16(in + 2 * i); | |
u16 tmp = 0; | |
for (size_t i = 8; i < size; i += 8) { | |
for (size_t j = 0; j < 4; ++j) { | |
tmp = read16(in + i + 2 * j); | |
tmp -= keys[j]; | |
write16(out + i + 2 * j, tmp); | |
} | |
advanceKeys(keys); | |
} | |
} | |
void encryptDataStructure(u8* in, u8* out, size_t size) { | |
copy(in, in + 8, out); | |
u16 keys[4] = { 0 }; | |
for (size_t i = 0; i < 4; ++i) keys[i] = read16(in + 2 * i); | |
u16 tmp = 0; | |
for (size_t i = 8; i < size; i += 8) { | |
for (size_t j = 0; j < 4; ++j) { | |
tmp = read16(in + i + 2 * j); | |
tmp += keys[j]; | |
write16(out + i + 2 * j, tmp); | |
} | |
advanceKeys(keys); | |
} | |
} | |
bool checkDataChecksum(u8* data, size_t size, size_t checksumOffset = 8, bool fix = false) { | |
// Decrypted save data | |
u8* currentChecksum_addr = data + checksumOffset; | |
u32 currentChecksum[16], checksum[16] = { 0 }; | |
for (size_t i = 0; i < 16; ++i) currentChecksum[i] = read32(currentChecksum_addr + 4 * i); | |
u8 checksumBackup[64]; | |
copy(currentChecksum_addr, currentChecksum_addr + 64, checksumBackup); | |
fill(currentChecksum_addr, currentChecksum_addr + 64, 0); | |
for (size_t i = 0; i < size; i += 2) { | |
u16 val = read16(data + i); | |
for (size_t j = 0; j < 16; ++j) | |
checksum[j] += (val >> j) & 1; | |
} | |
if (fix) { | |
for (size_t i = 0; i < 16; ++i) write32(currentChecksum_addr + 4 * i, checksum[i]); | |
} | |
else { | |
copy(checksumBackup, checksumBackup + 64, currentChecksum_addr); | |
} | |
return equal(checksum, checksum + 16, currentChecksum); | |
} | |
bool decryptSaveSlotAndCheckChecksums(u8* in, u8* out) { | |
decryptDataStructure(in, out, 0x1c0000); | |
return checkDataChecksum(out, 0x1c0000, 0x1bff80) && checkDataChecksum(out, 0x100, 8); // end - 0x80 | |
} | |
void encryptSaveSlotAndUpdateChecksums(u8* in, u8* out) { | |
checkDataChecksum(in, 0x100, 8, true); | |
checkDataChecksum(in, 0x1c0000, 0x1bff80, true); // end - 0x80 | |
encryptDataStructure(in, out, 0x1c0000); | |
} | |
bool decryptWiimoteTrainerCardAndCheckChecksum(u8* in, u8* out) { | |
decryptDataStructure(in, out, 0x780); | |
return checkDataChecksum(out, 0x780, 8); | |
} | |
void encryptWiimoteTrainerCardAndUpdateChecksum(u8* in, u8* out) { | |
checkDataChecksum(in, 0x780, 8, true); | |
encryptDataStructure(in, out, 0x780); | |
} | |
int main(void) { | |
u8* buf = new u8[0x380000]; | |
u8* outBuf = new u8[0x380000]; | |
string in = ""; | |
cout << "Decrypt or Encrypt PbrSaveData? [d/e]: "; | |
getline(cin, in); | |
if(in[0] == 'd') | |
{ | |
FILE *f = fopen("PbrSaveData", "rb"); | |
fread(buf, 1, 0x380000, f); | |
fclose(f); | |
for (size_t i = 0; i < 2; ++i) decryptSaveSlotAndCheckChecksums(buf + 0x1c0000*i, outBuf + 0x1c0000*i); | |
FILE *of1 = fopen("PbrSaveData_decrypted_current", "wb+"); | |
FILE *of2 = fopen("PbrSaveData_decrypted_backup", "wb+"); | |
u32 saveCount1 = read32(outBuf + 0x4c), saveCount2 = read32(outBuf + 0x1c0000 + 0x4c); | |
if (saveCount2 > saveCount1) swap(of1, of2); | |
fwrite(outBuf, 1, 0x1c0000, of1); | |
fwrite(outBuf + 0x1c0000, 1, 0x1c0000, of2); | |
fclose(of1); | |
fclose(of2); | |
} | |
else if(in[0] == 'e') | |
{ | |
FILE *of1 = fopen("PbrSaveData_decrypted_current", "rb"); | |
FILE *of2 = fopen("PbrSaveData_decrypted_backup", "rb"); | |
fread(buf, 1, 0x1C0000, of1); | |
fread(buf + 0x1C0000, 1, 0x1C0000, of2); | |
fclose(of1); | |
fclose(of2); | |
for (size_t i = 0; i < 2; ++i) encryptSaveSlotAndUpdateChecksums(buf + 0x1c0000*i, outBuf + 0x1c0000*i); | |
FILE *f = fopen("PbrSaveData", "wb+"); | |
fwrite(outBuf, 1, 0x380000, f); | |
fclose(f); | |
} | |
else return 0; | |
delete[] buf; | |
delete[] outBuf; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment