Created
October 30, 2015 22:09
-
-
Save TuxSH/6a3a6b48b8a5564f6215 to your computer and use it in GitHub Desktop.
Pokémon Battle Revolution basic encryption/decryption functions
This file contains hidden or 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]; | |
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); | |
delete[] buf; | |
delete[] outBuf; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I've decrypted my battle revolution save file using this code but how do I re-encrypt it so I can put it back into battle revolution after I've edited it with a hex editor?