Skip to content

Instantly share code, notes, and snippets.

@Bl4ckSh4rk
Forked from TuxSH/functions.cpp
Last active January 8, 2022 16:50
Show Gist options
  • Save Bl4ckSh4rk/780a2c05aab9532dd9ea4c4aff4c01ea to your computer and use it in GitHub Desktop.
Save Bl4ckSh4rk/780a2c05aab9532dd9ea4c4aff4c01ea to your computer and use it in GitHub Desktop.
Pokémon Battle Revolution basic encryption/decryption functions
#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