Advertisement
kpeterson413

qfg4editor

Oct 2nd, 2017
127
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.71 KB | None | 0 0
  1. // decryption scheme credit to blazingstix's Quest for Glory character editor (https://github.com/Blazingstix/QFGImporter)
  2.  
  3. #include <iostream>
  4. #include <fstream>
  5. #include <sstream>
  6. #include <algorithm>
  7. #include <stdlib.h>
  8. #include <vector>
  9. #include <iomanip>
  10.  
  11. typedef unsigned short ushort;
  12.  
  13. class import_character_qfg4
  14. {
  15.     public:
  16.         bool load_file(const std::string &filename);
  17.         bool export_file(const std::string &filename);
  18.         ushort &value(const std::string &value_name);
  19.         const ushort &value(const std::string &value_name) const;
  20.         std::string to_string() const;
  21.     private:
  22.         static const std::vector<std::pair<std::string, size_t> > value_names;
  23.         bool parse_file(std::ifstream &file);
  24.         bool encrypt_file(std::ofstream &file);
  25.         void build_checksums(ushort &even_csum, ushort &odd_csum);
  26.         std::vector<ushort> values;
  27.         static const ushort initial_even_checksum = 0x00D0;
  28.         static const ushort initial_xor = 0x0053;
  29.         static const size_t expected_data_size = 240;
  30.         std::string file_name;
  31.         std::string character_name;
  32. };
  33.  
  34. const std::vector<std::pair<std::string, size_t> > import_character_qfg4::value_names
  35. {
  36.     {"character_class"  , 0},
  37.     {"crowns_upper"     , 1},
  38.     {"crowns_lower"     , 2},
  39.     {"inventory_bits"   , 3},
  40.     {"strength"         , 4},
  41.     {"intelligence"     , 5},
  42.     {"magic"            , 16},
  43.     {"communication"    , 17},
  44.     {"honor"            , 18},
  45.     {"acrobatics"       , 19},
  46.     {"experience"       , 20},
  47.     {"current_hp"       , 21},
  48.     {"current_sp"       , 22},
  49.     {"current_mp"       , 23},
  50.     {"open_skill"       , 24},
  51.     {"detect_magic_skill"   , 25},
  52.     {"trigger_skill"    , 26},
  53.     {"dazzle_skill"     , 27},
  54.     {"zap_skill"        , 28},
  55.     {"calm_skill"       , 29},
  56.     {"flame_dart_skill" , 30},
  57.     {"fetch_skill"      , 31},
  58.     {"force_bolt_skill" , 32},
  59.     {"levitate_skill"   , 33},
  60.     {"reversal_skill"   , 34},
  61.     {"juggling_lights_skill"    , 35},
  62.     {"summon_staff_skill"   , 36},
  63.     {"lightning_ball_skill" , 37},
  64.     {"frost_bite_skill" , 38},
  65.     {"ritual_of_release_skill"  , 39},
  66.     {"hide_skill"       , 40},
  67.     {"aura_skill"       , 41},
  68.     {"protection_skill" , 42},
  69.     {"resistance_skill" , 43},
  70.     {"glide_skill"      , 44},
  71.     {"even_checksum"    , 54},
  72.     {"odd_checksum"     , 55}
  73. };
  74.  
  75. bool import_character_qfg4::load_file(const std::string &filename){
  76.     std::ifstream import_file(filename);
  77.     bool ret = true;
  78.     if (import_file.is_open())
  79.     {
  80.         ret = parse_file(import_file);
  81.     }
  82.     else
  83.     {
  84.         std::cerr << "Could not open file " << filename << std::endl;
  85.         ret = false;
  86.     }
  87.     return ret;
  88. }
  89.  
  90. bool import_character_qfg4::export_file(const std::string &filename){
  91.     // Open in binary mode to avoid CRLF line endings
  92.     std::ofstream export_file(filename, std::ios_base::binary | std::ios_base::out);
  93.     bool ret = true;
  94.     if (export_file.is_open())
  95.     {
  96.         export_file << ' ' << filename << ' ' << '\n';
  97.         ret = encrypt_file(export_file);
  98.     }
  99.     else
  100.     {
  101.         std::cerr << "Could not open file " << filename << " for writing" << std::endl;
  102.         ret = false;
  103.     }
  104.     return ret;
  105. }
  106.  
  107. bool import_character_qfg4::encrypt_file(std::ofstream &file)
  108. {
  109.     bool ret = true;
  110.     file << character_name << '\n';
  111.     build_checksums(value("even_checksum"), value("odd_checksum"));
  112.     std::vector<ushort> encrypted_shorts;
  113.     for (size_t i = 0; i < values.size(); i++)
  114.     {
  115.         ushort x;
  116.         if (i == 0)
  117.         {
  118.             x = initial_xor;
  119.         }
  120.         else
  121.         {
  122.             x = encrypted_shorts[i - 1];
  123.         }
  124.         encrypted_shorts.push_back(x ^ values[i]);
  125.     }
  126.     std::stringstream data_string;
  127.     for (ushort encrypted_short : encrypted_shorts)
  128.     {
  129.         data_string << std::hex << std::setw(2) << (encrypted_short / 100) << std::setw(2) << (encrypted_short%100);
  130.     }
  131.     file << data_string.str() << '\n';
  132.     return ret;
  133. }
  134.  
  135. bool import_character_qfg4::parse_file(std::ifstream &file)
  136. {
  137.     bool ret = true;
  138.     std::string temp_string;
  139.     if (ret == true)
  140.     {
  141.         ret = static_cast<bool>(file >> file_name);
  142.         if (ret == false)
  143.         {
  144.             std::cerr << "Could not read file name" << std::endl;
  145.         }
  146.     }
  147.     if (ret == true)
  148.     {
  149.         ret = static_cast<bool>(file >> character_name);
  150.         if (ret == false)
  151.         {
  152.             std::cerr << "Could not read character name" << std::endl;
  153.         }
  154.     }
  155.     if (ret == true)
  156.     {
  157.         std::string data_string;
  158.         while (std::getline(file, temp_string))
  159.         {
  160.             data_string += temp_string;
  161.         }
  162.         if (data_string.size() == 0)
  163.         {
  164.             std::cerr << "No data" << std::endl;
  165.             ret = false;
  166.         }
  167.         else if ((data_string.size() % 4) != 0)
  168.         {
  169.             std::cerr << "Data size not divisible by 4" << std::endl;
  170.             ret = false;
  171.         }
  172.         else
  173.         {
  174.             // For some reason, all leading byte 0s are replaced with spaces
  175.             std::replace(data_string.begin(), data_string.end(), ' ', '0');
  176.             // Deal with overflow errors when we have to
  177.             size_t overflow_size = data_string.size() - expected_data_size;
  178.             std::vector<ushort> encrypted_shorts;
  179.             for (size_t i = 0; i < data_string.size(); i+=4)
  180.             {
  181.                 // For whatever reason, each short is stored the ASCII hex representation of
  182.                 // 2 bytes: the first being the short value / 100, the second being the remainder. This makes no sense
  183.                 // as there is no guarantee that the short / 100 can fit in a byte. Deal with overflows as they arise.
  184.                 ushort byte1 = static_cast<ushort>(strtoul(data_string.substr(i, 2).c_str(), nullptr, 16));
  185.                 ushort byte2 = static_cast<ushort>(strtoul(data_string.substr(i+2, 2).c_str(), nullptr, 16));
  186.                 ushort combined = (byte1 * 100) + byte2;
  187.                 encrypted_shorts.push_back(combined);
  188.             }
  189.             values.clear();
  190.             // Basic encryption scheme is encrypted_short[n] = decrypted_short[n] XOR decrypted_short[n-1]
  191.             // with decrypted_short[n-1] = (magic) 0x0053. Nice thing about XOR is its reversability.
  192.             for (size_t i = 0; i < encrypted_shorts.size(); i++)
  193.             {
  194.                 ushort x;
  195.                 if (i == 0)
  196.                 {
  197.                     x = initial_xor;
  198.                 }
  199.                 else
  200.                 {
  201.                     x = encrypted_shorts[i - 1];
  202.                 }
  203.                 values.push_back(x ^ encrypted_shorts[i]);
  204.             }
  205.         }
  206.     }
  207.     return ret;
  208. }
  209.  
  210. ushort &import_character_qfg4::value(const std::string &value_name)
  211. {
  212.     auto iterator = std::find_if(value_names.begin(), value_names.end(), [&](auto s){return (value_name == s.first);});
  213.     if (value_names.end() == iterator)
  214.     {
  215.         std::cerr << value_name << " is not a valid value name" << std::endl;
  216.         static ushort dummy = 0;
  217.         return dummy;
  218.     }
  219.     else
  220.     {
  221.         return values.at(iterator->second);
  222.     }
  223. }
  224.  
  225. const ushort &import_character_qfg4::value(const std::string &value_name) const
  226. {
  227.     const auto iterator = std::find_if(value_names.begin(), value_names.end(), [&](const auto s){return (value_name == s.first);});
  228.     if (value_names.end() == iterator)
  229.     {
  230.         std::cerr << value_name << " is not a valid value name" << std::endl;
  231.         static const ushort dummy = 0;
  232.         return dummy;
  233.     }
  234.     else
  235.     {
  236.         return values.at(iterator->second);
  237.     }
  238. }
  239.  
  240. std::string import_character_qfg4::to_string() const
  241. {
  242.     std::stringstream s;
  243.     for (const auto &iterator : value_names)
  244.     {
  245.         if (iterator.second < values.size())
  246.         {
  247.             if (iterator.first == "character_class")
  248.             {
  249.                 s << iterator.first << " = ";
  250.                 switch (values.at(iterator.second))
  251.                 {
  252.                     case 0:
  253.                         s << "Fighter";
  254.                         break;
  255.                     case 1:
  256.                         s << "Magic User";
  257.                         break;
  258.                     case 2:
  259.                         s << "Thief";
  260.                         break;
  261.                     case 3:
  262.                         s << "Paladin";
  263.                         break;
  264.                     default:
  265.                         s << "Invalid Class";
  266.                 }
  267.                 s << '\n';
  268.             }
  269.             else{
  270.                 s << iterator.first << " = " << values.at(iterator.second) << '\n';
  271.             }
  272.         }
  273.     }
  274.     return s.str();
  275. }
  276.  
  277. void import_character_qfg4::build_checksums(ushort &even_csum, ushort &odd_csum)
  278. {
  279.     const size_t MAX_INDEX = (values.size() < 52 ? values.size() : 52);
  280.     even_csum = 0;
  281.     odd_csum = 0;
  282.     for (size_t i = 0; i < MAX_INDEX; i+=2){
  283.         even_csum += values[i];
  284.         odd_csum += values[i+1];
  285.     }
  286.     even_csum += 0xD0;
  287. }
  288.  
  289. void usage(const std::string &invocation)
  290. {
  291.     std::cerr <<
  292.     "Usage:\n" <<
  293.     invocation << " --input filename [--output filename] [--set value_name=value...]\n"
  294.     "\t--input     The base file to modify\n"
  295.     "\t--output    The output file to write\n"
  296.     "\t--set       A value to set in the format value_name=value\n" << std::endl;
  297. }
  298.  
  299. int main(int argc, char ** argv){
  300.     std::string input_file_name;
  301.     std::string output_file_name;
  302.     std::vector<std::pair<std::string, ushort> > values_to_modify;
  303.     bool failed = false;
  304.    
  305.     for (int iterator = 1; iterator < argc; iterator++)
  306.     {
  307.         const std::string param(argv[iterator]);
  308.         if (param == "--input")
  309.         {
  310.             iterator++;
  311.             if (iterator < argc)
  312.             {
  313.                 input_file_name = std::string(argv[iterator]);
  314.             }
  315.             else
  316.             {
  317.                 usage(argv[0]);
  318.             }
  319.         }
  320.         else if (param == "--output")
  321.         {
  322.             iterator++;
  323.             if (iterator < argc)
  324.             {
  325.                 output_file_name = std::string(argv[iterator]);
  326.             }
  327.             else
  328.             {
  329.                 usage(argv[0]);
  330.             }
  331.         }
  332.         else if (param == "--set")
  333.         {
  334.             iterator++;
  335.             if (iterator < argc)
  336.             {
  337.                 const std::string name_value(argv[iterator]);
  338.                 size_t epos = name_value.find('=');
  339.                 if (epos != std::string::npos && (epos + 1) < name_value.size())
  340.                 {
  341.                     values_to_modify.push_back(std::make_pair(name_value.substr(0, epos),
  342.                         static_cast<ushort>(strtoul(name_value.substr(epos+1).c_str(), nullptr, 10))));
  343.                 }
  344.                 else
  345.                 {
  346.                     std::cerr << name_value << " is not a valid name=value pair" << std::endl;
  347.                     usage(argv[0]);
  348.                 }
  349.             }
  350.             else
  351.             {
  352.                 usage(argv[0]);
  353.             }
  354.         }
  355.     }
  356.     import_character_qfg4 importer;
  357.     if (input_file_name.size() > 0)
  358.     {
  359.         importer.load_file(input_file_name);
  360.         std::cout << "Unmodified:\n" << importer.to_string() << std::endl;
  361.         for (const auto &name_value : values_to_modify)
  362.         {
  363.             importer.value(name_value.first) = name_value.second;
  364.         }
  365.         std::cout << "Modified (checksums not updated):\n" << importer.to_string() << std::endl;
  366.         if (output_file_name.size() > 0)
  367.         {
  368.             importer.export_file(output_file_name);
  369.         }
  370.     }
  371.     else
  372.     {
  373.         usage(argv[0]);
  374.     }
  375. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement