Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // decryption scheme credit to blazingstix's Quest for Glory character editor (https://github.com/Blazingstix/QFGImporter)
- #include <iostream>
- #include <fstream>
- #include <sstream>
- #include <algorithm>
- #include <stdlib.h>
- #include <vector>
- #include <iomanip>
- typedef unsigned short ushort;
- class import_character_qfg4
- {
- public:
- bool load_file(const std::string &filename);
- bool export_file(const std::string &filename);
- ushort &value(const std::string &value_name);
- const ushort &value(const std::string &value_name) const;
- std::string to_string() const;
- private:
- static const std::vector<std::pair<std::string, size_t> > value_names;
- bool parse_file(std::ifstream &file);
- bool encrypt_file(std::ofstream &file);
- void build_checksums(ushort &even_csum, ushort &odd_csum);
- std::vector<ushort> values;
- static const ushort initial_even_checksum = 0x00D0;
- static const ushort initial_xor = 0x0053;
- static const size_t expected_data_size = 240;
- std::string file_name;
- std::string character_name;
- };
- const std::vector<std::pair<std::string, size_t> > import_character_qfg4::value_names
- {
- {"character_class" , 0},
- {"crowns_upper" , 1},
- {"crowns_lower" , 2},
- {"inventory_bits" , 3},
- {"strength" , 4},
- {"intelligence" , 5},
- {"magic" , 16},
- {"communication" , 17},
- {"honor" , 18},
- {"acrobatics" , 19},
- {"experience" , 20},
- {"current_hp" , 21},
- {"current_sp" , 22},
- {"current_mp" , 23},
- {"open_skill" , 24},
- {"detect_magic_skill" , 25},
- {"trigger_skill" , 26},
- {"dazzle_skill" , 27},
- {"zap_skill" , 28},
- {"calm_skill" , 29},
- {"flame_dart_skill" , 30},
- {"fetch_skill" , 31},
- {"force_bolt_skill" , 32},
- {"levitate_skill" , 33},
- {"reversal_skill" , 34},
- {"juggling_lights_skill" , 35},
- {"summon_staff_skill" , 36},
- {"lightning_ball_skill" , 37},
- {"frost_bite_skill" , 38},
- {"ritual_of_release_skill" , 39},
- {"hide_skill" , 40},
- {"aura_skill" , 41},
- {"protection_skill" , 42},
- {"resistance_skill" , 43},
- {"glide_skill" , 44},
- {"even_checksum" , 54},
- {"odd_checksum" , 55}
- };
- bool import_character_qfg4::load_file(const std::string &filename){
- std::ifstream import_file(filename);
- bool ret = true;
- if (import_file.is_open())
- {
- ret = parse_file(import_file);
- }
- else
- {
- std::cerr << "Could not open file " << filename << std::endl;
- ret = false;
- }
- return ret;
- }
- bool import_character_qfg4::export_file(const std::string &filename){
- // Open in binary mode to avoid CRLF line endings
- std::ofstream export_file(filename, std::ios_base::binary | std::ios_base::out);
- bool ret = true;
- if (export_file.is_open())
- {
- export_file << ' ' << filename << ' ' << '\n';
- ret = encrypt_file(export_file);
- }
- else
- {
- std::cerr << "Could not open file " << filename << " for writing" << std::endl;
- ret = false;
- }
- return ret;
- }
- bool import_character_qfg4::encrypt_file(std::ofstream &file)
- {
- bool ret = true;
- file << character_name << '\n';
- build_checksums(value("even_checksum"), value("odd_checksum"));
- std::vector<ushort> encrypted_shorts;
- for (size_t i = 0; i < values.size(); i++)
- {
- ushort x;
- if (i == 0)
- {
- x = initial_xor;
- }
- else
- {
- x = encrypted_shorts[i - 1];
- }
- encrypted_shorts.push_back(x ^ values[i]);
- }
- std::stringstream data_string;
- for (ushort encrypted_short : encrypted_shorts)
- {
- data_string << std::hex << std::setw(2) << (encrypted_short / 100) << std::setw(2) << (encrypted_short%100);
- }
- file << data_string.str() << '\n';
- return ret;
- }
- bool import_character_qfg4::parse_file(std::ifstream &file)
- {
- bool ret = true;
- std::string temp_string;
- if (ret == true)
- {
- ret = static_cast<bool>(file >> file_name);
- if (ret == false)
- {
- std::cerr << "Could not read file name" << std::endl;
- }
- }
- if (ret == true)
- {
- ret = static_cast<bool>(file >> character_name);
- if (ret == false)
- {
- std::cerr << "Could not read character name" << std::endl;
- }
- }
- if (ret == true)
- {
- std::string data_string;
- while (std::getline(file, temp_string))
- {
- data_string += temp_string;
- }
- if (data_string.size() == 0)
- {
- std::cerr << "No data" << std::endl;
- ret = false;
- }
- else if ((data_string.size() % 4) != 0)
- {
- std::cerr << "Data size not divisible by 4" << std::endl;
- ret = false;
- }
- else
- {
- // For some reason, all leading byte 0s are replaced with spaces
- std::replace(data_string.begin(), data_string.end(), ' ', '0');
- // Deal with overflow errors when we have to
- size_t overflow_size = data_string.size() - expected_data_size;
- std::vector<ushort> encrypted_shorts;
- for (size_t i = 0; i < data_string.size(); i+=4)
- {
- // For whatever reason, each short is stored the ASCII hex representation of
- // 2 bytes: the first being the short value / 100, the second being the remainder. This makes no sense
- // as there is no guarantee that the short / 100 can fit in a byte. Deal with overflows as they arise.
- ushort byte1 = static_cast<ushort>(strtoul(data_string.substr(i, 2).c_str(), nullptr, 16));
- ushort byte2 = static_cast<ushort>(strtoul(data_string.substr(i+2, 2).c_str(), nullptr, 16));
- ushort combined = (byte1 * 100) + byte2;
- encrypted_shorts.push_back(combined);
- }
- values.clear();
- // Basic encryption scheme is encrypted_short[n] = decrypted_short[n] XOR decrypted_short[n-1]
- // with decrypted_short[n-1] = (magic) 0x0053. Nice thing about XOR is its reversability.
- for (size_t i = 0; i < encrypted_shorts.size(); i++)
- {
- ushort x;
- if (i == 0)
- {
- x = initial_xor;
- }
- else
- {
- x = encrypted_shorts[i - 1];
- }
- values.push_back(x ^ encrypted_shorts[i]);
- }
- }
- }
- return ret;
- }
- ushort &import_character_qfg4::value(const std::string &value_name)
- {
- auto iterator = std::find_if(value_names.begin(), value_names.end(), [&](auto s){return (value_name == s.first);});
- if (value_names.end() == iterator)
- {
- std::cerr << value_name << " is not a valid value name" << std::endl;
- static ushort dummy = 0;
- return dummy;
- }
- else
- {
- return values.at(iterator->second);
- }
- }
- const ushort &import_character_qfg4::value(const std::string &value_name) const
- {
- const auto iterator = std::find_if(value_names.begin(), value_names.end(), [&](const auto s){return (value_name == s.first);});
- if (value_names.end() == iterator)
- {
- std::cerr << value_name << " is not a valid value name" << std::endl;
- static const ushort dummy = 0;
- return dummy;
- }
- else
- {
- return values.at(iterator->second);
- }
- }
- std::string import_character_qfg4::to_string() const
- {
- std::stringstream s;
- for (const auto &iterator : value_names)
- {
- if (iterator.second < values.size())
- {
- if (iterator.first == "character_class")
- {
- s << iterator.first << " = ";
- switch (values.at(iterator.second))
- {
- case 0:
- s << "Fighter";
- break;
- case 1:
- s << "Magic User";
- break;
- case 2:
- s << "Thief";
- break;
- case 3:
- s << "Paladin";
- break;
- default:
- s << "Invalid Class";
- }
- s << '\n';
- }
- else{
- s << iterator.first << " = " << values.at(iterator.second) << '\n';
- }
- }
- }
- return s.str();
- }
- void import_character_qfg4::build_checksums(ushort &even_csum, ushort &odd_csum)
- {
- const size_t MAX_INDEX = (values.size() < 52 ? values.size() : 52);
- even_csum = 0;
- odd_csum = 0;
- for (size_t i = 0; i < MAX_INDEX; i+=2){
- even_csum += values[i];
- odd_csum += values[i+1];
- }
- even_csum += 0xD0;
- }
- void usage(const std::string &invocation)
- {
- std::cerr <<
- "Usage:\n" <<
- invocation << " --input filename [--output filename] [--set value_name=value...]\n"
- "\t--input The base file to modify\n"
- "\t--output The output file to write\n"
- "\t--set A value to set in the format value_name=value\n" << std::endl;
- }
- int main(int argc, char ** argv){
- std::string input_file_name;
- std::string output_file_name;
- std::vector<std::pair<std::string, ushort> > values_to_modify;
- bool failed = false;
- for (int iterator = 1; iterator < argc; iterator++)
- {
- const std::string param(argv[iterator]);
- if (param == "--input")
- {
- iterator++;
- if (iterator < argc)
- {
- input_file_name = std::string(argv[iterator]);
- }
- else
- {
- usage(argv[0]);
- }
- }
- else if (param == "--output")
- {
- iterator++;
- if (iterator < argc)
- {
- output_file_name = std::string(argv[iterator]);
- }
- else
- {
- usage(argv[0]);
- }
- }
- else if (param == "--set")
- {
- iterator++;
- if (iterator < argc)
- {
- const std::string name_value(argv[iterator]);
- size_t epos = name_value.find('=');
- if (epos != std::string::npos && (epos + 1) < name_value.size())
- {
- values_to_modify.push_back(std::make_pair(name_value.substr(0, epos),
- static_cast<ushort>(strtoul(name_value.substr(epos+1).c_str(), nullptr, 10))));
- }
- else
- {
- std::cerr << name_value << " is not a valid name=value pair" << std::endl;
- usage(argv[0]);
- }
- }
- else
- {
- usage(argv[0]);
- }
- }
- }
- import_character_qfg4 importer;
- if (input_file_name.size() > 0)
- {
- importer.load_file(input_file_name);
- std::cout << "Unmodified:\n" << importer.to_string() << std::endl;
- for (const auto &name_value : values_to_modify)
- {
- importer.value(name_value.first) = name_value.second;
- }
- std::cout << "Modified (checksums not updated):\n" << importer.to_string() << std::endl;
- if (output_file_name.size() > 0)
- {
- importer.export_file(output_file_name);
- }
- }
- else
- {
- usage(argv[0]);
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement