Skip to content

Instantly share code, notes, and snippets.

@AltimorTASDK
Created December 10, 2015 12:30
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save AltimorTASDK/be1f7369af0b02c3816b to your computer and use it in GitHub Desktop.
Save AltimorTASDK/be1f7369af0b02c3816b to your computer and use it in GitHub Desktop.
#include <iostream>
#include <fstream>
#include <memory>
#include <string>
#include <algorithm>
void process_crypt_table(unsigned *crypt_table)
{
for (auto i = 0; i < 227; i++)
{
crypt_table[i] =
crypt_table[i + 397] ^
((crypt_table[i + 1] & 1) != 0 ? 0x9908B0DF : 0) ^
((crypt_table[i] ^ (crypt_table[i + 1] ^ crypt_table[i]) & 0x7FFFFFFE) >> 1);
}
for (auto i = 0; i < 396; i++)
{
crypt_table[i + 227] =
crypt_table[i] ^
((crypt_table[i + 228] & 1) != 0 ? 0x9908B0DF : 0) ^
((crypt_table[i + 227] ^ (crypt_table[i + 228] ^ crypt_table[i + 227]) & 0x7FFFFFFE) >> 1);
}
crypt_table[623] =
crypt_table[396] ^
((crypt_table[0] & 1) != 0 ? 0x9908B0DF : 0) ^
((crypt_table[623] ^ (crypt_table[0] ^ crypt_table[623]) & 0x7FFFFFFE) >> 1);
}
void decrypt(const char *name)
{
std::ifstream in(name, std::ios::binary);
if (!in)
{
std::cerr << "Couldn't open " << name << std::endl;
return;
}
auto cppname = std::string(name);
std::ofstream out(cppname + ".decrypted", std::ios::binary);
// Remove path
const auto slash_it = std::find(cppname.rbegin(), cppname.rend(), '/');
if (slash_it != cppname.rend())
cppname.erase(cppname.begin(), slash_it.base());
const auto bslash_it = std::find(cppname.rbegin(), cppname.rend(), '\\');
if (bslash_it != cppname.rend())
cppname.erase(cppname.begin(), bslash_it.base());
// Uppercase
std::transform(cppname.begin(), cppname.end(), cppname.begin(), toupper);
// Use the uppercase filename to seed the crypto algorithm
auto crypt_seed = 0;
for (auto &c : cppname)
crypt_seed = c + 137 * crypt_seed;
in.seekg(0, std::ios::end);
const auto size = (int)(in.tellg());
in.seekg(0);
auto buf = std::make_unique<unsigned[]>(size / 4);
in.read((char*)(buf.get()), size);
unsigned crypt_table[624];
crypt_table[0] = crypt_seed;
for (auto i = 1; i < 624; i++)
{
const auto last = crypt_table[i - 1];
crypt_table[i] = i + 0x6C078965 * (last ^ (last >> 30));
}
auto last_out = 0x43415046u;
for (auto i = 0; i < size / 4; i++)
{
if (i % 624 == 0)
process_crypt_table(crypt_table);
const auto entry = crypt_table[i % 624];
const auto key =
(((((((entry >> 11) ^ entry) & 0xFF3A58AD) << 7) ^ (entry >> 11) ^ entry) & 0xFFFFDF8C) << 15) ^
((((entry >> 11) ^ entry) & 0xFF3A58AD) << 7) ^
(entry >> 11) ^
entry;
last_out ^= buf[i] ^ (key ^ (key >> 18));
out.write((char*)(&last_out), 4);
}
}
int main(const int argc, const char *argv[])
{
if (argc <= 1)
{
std::cerr << "ggxrd_decrypter <filename1> <filename2> etc" << std::endl;
return EXIT_FAILURE;
}
for (auto i = 1; i < argc; i++)
decrypt(argv[i]);
return 0;
}
@SileNTViP
Copy link

For game Guilty Gear Xrd -REVELATOR- value last_out is 0x72642A6Fu;

@rakisaionji
Copy link

This works on Fate Grand Order Arcade too. UwU

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment