2020-05-04 17:54:23 +02:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <fstream>
|
|
|
|
#include <zlib.h>
|
|
|
|
#include "Base64.hpp"
|
|
|
|
|
|
|
|
constexpr int CHUNK = 16384;
|
|
|
|
constexpr int WINDOW_BITS = 15;
|
|
|
|
|
2020-05-05 12:18:28 +02:00
|
|
|
//Returns true if it was able to open the given file for reading
|
2020-05-05 13:40:52 +02:00
|
|
|
bool IsFile(const std::string& filename) {
|
2020-05-05 12:18:28 +02:00
|
|
|
std::ifstream file(filename);
|
|
|
|
const bool ret = file.good();
|
|
|
|
file.close();
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-05-04 17:54:23 +02:00
|
|
|
//Reads the contents of a given file into a string
|
|
|
|
std::string FileToStr(const std::string& filename) {
|
|
|
|
std::ifstream file(filename, std::ios::binary);
|
|
|
|
if (!file.good())
|
|
|
|
return "";
|
|
|
|
std::string ret(std::istreambuf_iterator<char>(file), {});
|
|
|
|
file.close();
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Writes the contents of a string to the given file
|
2020-05-05 12:18:28 +02:00
|
|
|
// returns false if it was unable to open the file
|
|
|
|
bool StrToFile(const std::string& filename, const std::string& data) {
|
|
|
|
std::ofstream ofile(filename, std::ios::binary);
|
|
|
|
if(!ofile.good())
|
|
|
|
return false;
|
2020-05-04 17:54:23 +02:00
|
|
|
ofile << data;
|
|
|
|
ofile.close();
|
2020-05-05 12:18:28 +02:00
|
|
|
return true;
|
2020-05-04 17:54:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//XORs each byte of a string reference
|
|
|
|
void Xor_Str(std::string& str, unsigned char key) {
|
|
|
|
for (auto& c : str) {
|
|
|
|
c = c^key;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-05 13:40:52 +02:00
|
|
|
std::string GZipDecompress(const std::string& in) {
|
2020-05-04 17:54:23 +02:00
|
|
|
//Initialize all members of strm to be 0
|
|
|
|
z_stream strm = {};
|
|
|
|
//Output buffer
|
|
|
|
char out[CHUNK];
|
|
|
|
//Holds return value
|
|
|
|
std::string final_output;
|
|
|
|
//Initialize GZip decompression; except on failure
|
|
|
|
if(inflateInit2(&strm, WINDOW_BITS + 16) != Z_OK)
|
|
|
|
throw(std::exception());
|
|
|
|
//Tell zlib where to find the data we want to decompress
|
|
|
|
strm.avail_in = in.size();
|
|
|
|
strm.next_in = (Bytef*)in.data();
|
|
|
|
|
|
|
|
do {
|
|
|
|
//Tell zlib where to store the decompressed output data
|
|
|
|
strm.avail_out = CHUNK;
|
|
|
|
strm.next_out = (Bytef*)out;
|
|
|
|
//Decompress our input data; except on failure
|
|
|
|
switch (inflate(&strm, Z_FINISH)) {
|
|
|
|
case Z_NEED_DICT:
|
|
|
|
throw(std::exception());
|
|
|
|
case Z_DATA_ERROR:
|
|
|
|
throw(std::exception());
|
|
|
|
case Z_MEM_ERROR:
|
|
|
|
throw(std::exception());
|
|
|
|
}
|
|
|
|
//How many bytes we received into our output buffer
|
|
|
|
unsigned int have = CHUNK - strm.avail_out;
|
|
|
|
//Append received data to our return string
|
|
|
|
final_output.append(reinterpret_cast<const char*>(out), have);
|
|
|
|
} while (strm.avail_out == 0);
|
|
|
|
//Clean up and return
|
|
|
|
inflateEnd(&strm);
|
|
|
|
return final_output;
|
|
|
|
}
|
|
|
|
|
2020-05-05 13:40:52 +02:00
|
|
|
std::string GZipCompress(const std::string& in) {
|
2020-05-04 17:54:23 +02:00
|
|
|
//Initialize all members of strm to be 0
|
|
|
|
z_stream strm = {};
|
|
|
|
//Output buffer
|
|
|
|
unsigned char out[CHUNK];
|
|
|
|
//Return value
|
|
|
|
std::string final_output;
|
|
|
|
//Initialize GZip compression; except on failure
|
|
|
|
if (deflateInit(&strm, Z_DEFAULT_COMPRESSION) != Z_OK)
|
|
|
|
throw(std::exception());
|
|
|
|
//Tell zlib where to find the data we want to compress
|
|
|
|
strm.avail_in = in.size();
|
|
|
|
strm.next_in = (Bytef*)in.data();
|
|
|
|
|
|
|
|
do {
|
|
|
|
//Tell zlib where to store the compressed output data
|
|
|
|
strm.avail_out = CHUNK;
|
|
|
|
strm.next_out = out;
|
|
|
|
//Compress our input data; except on failure
|
|
|
|
if(deflate(&strm, Z_FINISH) == Z_STREAM_ERROR)
|
|
|
|
throw(std::exception());
|
|
|
|
//How many bytes we received into our output buffer
|
|
|
|
unsigned int have = CHUNK - strm.avail_out;
|
|
|
|
//Append received data to our return string
|
|
|
|
final_output.append(reinterpret_cast<const char*>(out), have);
|
|
|
|
} while (strm.avail_out == 0);
|
|
|
|
//Clean up and return
|
|
|
|
deflateEnd(&strm);
|
|
|
|
return final_output;
|
|
|
|
}
|
|
|
|
|
2020-05-05 13:40:52 +02:00
|
|
|
/** @brief Decrypt GD save files or level data *
|
|
|
|
* @param in_data encrypted input data *
|
|
|
|
* @param xor11 whether or not the data should be xored with 11; *
|
|
|
|
* select true for gamesave or false for level data *
|
|
|
|
* @retval decrypted data **/
|
|
|
|
std::string Decrypt(const std::string& in_data, bool xor11) {
|
|
|
|
//Retun value
|
|
|
|
std::string ret = in_data;
|
|
|
|
//Xor with value 11 if nescessary
|
|
|
|
if(xor11)
|
|
|
|
Xor_Str(ret, 11);
|
2020-05-04 17:54:23 +02:00
|
|
|
//replace - with + and _ with /
|
|
|
|
for (auto& i : ret) {
|
|
|
|
if (i == '-')
|
|
|
|
i = '+';
|
|
|
|
if (i == '_')
|
|
|
|
i = '/';
|
|
|
|
}
|
|
|
|
//Decrypt with Base64
|
|
|
|
ret = base64::Decode(ret);
|
|
|
|
//Decompress with GZip
|
|
|
|
ret = GZipDecompress(ret);
|
|
|
|
//Return
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-05-05 13:40:52 +02:00
|
|
|
/** @brief Encrypt GD save files or level data *
|
|
|
|
* @param in_data decrypted input data *
|
|
|
|
* @param xor11 whether or not the data should be xored with 11; *
|
|
|
|
* select true for gamesave or false for level data *
|
|
|
|
* @retval encrypted data **/
|
|
|
|
std::string Encrypt(const std::string& in_data, bool xor11) {
|
|
|
|
//Calculate crc32 checksum and size
|
|
|
|
const uLong crc = crc32(0L, Z_NULL, 0);
|
|
|
|
const uint32_t crc32_sum = crc32(crc, (Bytef*)in_data.data(), in_data.size());
|
|
|
|
const uint32_t dataSize = in_data.size();
|
2020-05-04 17:54:23 +02:00
|
|
|
//Compress with GZip
|
2020-05-05 13:40:52 +02:00
|
|
|
std::string ret = GZipCompress(in_data);
|
2020-05-04 17:54:23 +02:00
|
|
|
//Remove first 2 and last 4 chars of string
|
|
|
|
ret = ret.substr(2, ret.size() - 4 - 2);
|
|
|
|
//Add header, checksum and size
|
|
|
|
char header[10] = {'\x1f', '\x8b', '\x08', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x0b'};
|
2020-05-05 13:40:52 +02:00
|
|
|
ret = std::string(header, sizeof(header)) +
|
2020-05-04 17:54:23 +02:00
|
|
|
ret +
|
|
|
|
std::string((const char*)&crc32_sum, sizeof(crc32_sum)) + //Use raw binary data
|
|
|
|
std::string((const char*)&dataSize, sizeof(dataSize)); //Use raw binary data
|
|
|
|
//Encrypt with Base64
|
|
|
|
ret = base64::Encode(ret);
|
|
|
|
//replace + with - and / with _
|
|
|
|
for (auto& i : ret) {
|
|
|
|
if (i == '+')
|
|
|
|
i = '-';
|
|
|
|
if (i == '/')
|
|
|
|
i = '_';
|
|
|
|
}
|
2020-05-05 13:40:52 +02:00
|
|
|
//Xor with value 11 if nescessary
|
|
|
|
if(xor11)
|
|
|
|
Xor_Str(ret, 11);
|
2020-05-04 17:54:23 +02:00
|
|
|
//Return
|
|
|
|
return ret;
|
|
|
|
}
|