File I/O and Encryption

Started by
8 comments, last by TheShadow344 18 years, 10 months ago
I just started a project and am currently working on programming a set of core libraries that I can use within my games. Right now, I'm working on writing a game save / load routine. I've managed to create a WriteFile() function within the cDataPackage class. Here's the code, as it may help another person in the process...

// Assumes that <fstream> and <string> libraries have been included

void cDataPackage::WriteFile(char *FileName)
{
  // String to hold raw integer data
  string ModData;
  ModData += "|";
  
  // Write all elements of RawData
  for(unsigned short x = 0; x < NumElement; x++)
  {
    // Write number to string
    char *Character;
    ModData += _ltoa(RawData[x], Character, 10);
    ModData += "|";
  }
  
  // Open file for writing
  FILE.open(FileName, ios::out | ios::trunc );
  
  // Perform save only if file was opened successfully
  if(FILE.is_open())
  {
    // Use XOR encryption
    unsigned short k = 0;
    for(unsigned long x = 0; x < ModData.length(); x++)
    {
      ModData[x] ^= Key[k];
      
      // Configure key
      k++;
      if(k == Key.length()) { k = 0; }
    }
    
    // Write encrypted data to file
    FILE << ModData;
    
    // Close file
    FILE.close();
  }
}



NOTE: The 'RawData', 'FILE', and 'Key' variables are all contained within the cDataPackage class. My problem is that I can't seem to access the file that I created and encrypted through the above function. In the ReadFile() function, I want to extract the contents of the file I saved and store them in a string (as defined through the <string> library). That's all I need to figure out, as I already have a general idea of how to extract data from the string and store it back in the RawData array. Thanks in advance, and I hope this may help other people who are having the same problem. Perhaps after this is all done, I'll post the complete cDataPackage class... [Edited by - TheShadow344 on June 18, 2005 2:42:37 AM]
Advertisement
I'm not entirely sure what FILE is, but it looks like it might be a std::fstream because of the use of the stream operator, but I'd change the name as it's actually used for C file I/O.

Anyways, here's how i'd copy a file to a string, leaving it untouched (i.e. no skipping of whitespace etc).
#include <algorithm>#include <fstream>#include <iterator>#include <string>	const std::string fileName = "C:\\MyFile.txt";	std::ifstream file(fileName.c_str(), std::ios::binary); //Open file in binary so it remains exactly how it is on disk	if (!file)		throw std::runtime_error("Unable to open file:" + fileName);	file.unsetf(std::ios::skipws); //Stop the stream from skipping over white space.		std::string fileContents;	typedef std::istream_iterator<char> file_iterator;	std::copy(file_iterator(file), file_iterator(), std::back_insert_iterator<std::string>(fileContents));
Good, but you lose points for:

- Creating a const std::string only to .c_str() it later - for things that really will be constant and won't be involved in other string manipulations, an actual string literal (char *, etc.) is quite sufficient.

- Using '\\' rather than '/' in the file path.

- Not warning the OP to output in binary mode as well as inputting that way ;)

- Is unsetting 'skipws' really needed for a *binary* file? o_O

- Not making use of the helper function "std::back_inserter()" to simplify the creation of the back_insert_iterator (it is analogous to std::make_pair() for std::pair objects).

As for the writing code, I have a few suggestions :)

// Also include <sstream> with the other includes// First, I recommend putting this free function "in your library"; IMX (writing// snippets for the gamedev forums ;) ) the functionality is quite useful :)template <typename T>T getItem(istream& in) {  T result;  in >> result;  return result;}void cDataPackage::WriteFile(char *FileName) {  // Get the file *first*, and actually throw an exception if there are any  // problems, so that the user can know about it and do something  // Also, we don't need a class member for this  // Don't need to specify ios::out for an output stream.  ofstream ofs(FileName, ios::trunc | ios::binary);  ofs.exceptions(ofstream::badbit ); // can't set eofbit or failbit for output  // Now comes the fun part :D  stringstream ModData("|");  // Output all the data into the stringstream, which behaves like a normal  // stream except "in memory" like a string.  // Oh, setting a specific type for a loop counter is a really silly   // optimization :)  for(int x = 0; x < NumElement; x++) {    // Write number to string    ModData << RawData[x] << '|';  }  // Feed the string one character at a time to output, doing encryption along  // the way.  int keylen = Key.length();  int datalen = ModData.length();  for(int x = 0; x < datalen; x++) {    //char current;    //ModData >> current;    //current ^= Key[x % keylen]; // see, don't need to track k separately    //ofs << current;    // With the above utility function, we can write:    ofs << (getItem<char>(ModData) ^ Key[x % keylen]);    // Slick :D  }  // Since we created the fstream as a local, it will automatically be closed  // at the end of the function (destructor called at end of scope).}
OK, the reason I used a std::string was so I could write this:
throw std::runtime_error("Unable to open file:" + fileName);

Yeah, it was lazy of me, but I didn't realise you could make fstream object throw (I assumed that's what ofs.exceptions(ofstream::badbit ); does)

About unsetting the skipws in a binary file, I didn't used to think I needed to, but I've actually come across problems before when using the iterators, it seems they will skip white space regardless of the file mode.

And I didn't know about the std::back_inserter(), although I've always thought there should be some function to simplify it [grin]

Anyways, ++ratings for you sir!
Thanks to everyone for their generous help in both the topic at hand and how I may improve my code. I'll be sure to make these modifications and reply back later...
Quote:Original post by desertcube
About unsetting the skipws in a binary file, I didn't used to think I needed to, but I've actually come across problems before when using the iterators, it seems they will skip white space regardless of the file mode.


A std::istream_iterator will skip, a std::istreambuf_iterator will not. The first does formatted input, the second unformatted.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
I must admit I've actually learned a lot from this thread - I never really do that much IO stream stuff but it's always good to know how to better use them.

Seems there's always something to learn about the Standard C++ Library!
Me again.

I've managed to write and read the data from the file without problems!! Now I just need to extract the numbers from the string I used to get the file contents. All numbers are seperated by a "|" character...

// Extract contents of ModData and place into RawDataint SearchPos = 0;for(int x = 0; x < 10; x++){  // Find beginning and end positions of number  int Begin = (ModData.find("|", SearchPos)+1);  int End = ModData.find("|", (SearchPos+1));  // Write Num to RawData  string Num = ModData.substr(Begin, (End-Begin));  RawData[x] = atol(Num);  SearchPos = End;}

When I do this, I get an error...

cannot convert `std::string' to `const char*' for argument `1' to `long int atol(const char*)'

Obviously, there's a problem with how I'm using the "atol()" function to convert a string into a long integer. Any ideas how to fix this?
atol is expecting a C string, not a std::string, try using atol(Num.c_str()) instead.
...

Thanks for the help. I just can't believe that it was that simple. I've known about the 'c_str()' function but never needed to use it.[grin]

Now everything is working!!

#include <fstream>#include <string>#include <iterator>using namespace std;////////////////////////////////////////////////////////////////////////////////////////////////////////// cDataPackage Class                                                                                 //// Uses arrays and encryption to save and load game data from disk.                                   //////////////////////////////////////////////////////////////////////////////////////////////////////////class cDataPackage{  // Protected data - cannot be accessed from outside the class  protected:    string Key;    long RawData[10];      // Public data - can be accessed from outside the class  public:    cDataPackage(string NewKey);    ~cDataPackage();        void InputRaw(int ID, int InputValue);    long ExtractRaw(int ID);    void WriteFile(char *FileName);    void ReadFile(char *FileName);};////////////////////////////////////////////////////////////////////////////////////////////////////////// cDataPackage::cDataPackage()                                                                       //// Constructs an instance of the cDataPackage class. Creates a key for the encryption system.         //// Initializes all items in the RawData array.                                                        //////////////////////////////////////////////////////////////////////////////////////////////////////////cDataPackage::cDataPackage(string NewKey){  // Create new encryption key  Key = NewKey;    // Initialize all elements  for(int x = 0; x < 10; x++)  {    RawData[x] = 0;  }}////////////////////////////////////////////////////////////////////////////////////////////////////////// cDataPackage::~cDataPackage()                                                                      //// Destroys an instance of the cDataPackage class.                                                    //////////////////////////////////////////////////////////////////////////////////////////////////////////cDataPackage::~cDataPackage(){}////////////////////////////////////////////////////////////////////////////////////////////////////////// cDataPackage::InputRaw()                                                                           //// Inputs data into the RawData array.                                                                //////////////////////////////////////////////////////////////////////////////////////////////////////////void cDataPackage::InputRaw(int ID, int InputValue){  // Write data to array  RawData[ID] = InputValue;}////////////////////////////////////////////////////////////////////////////////////////////////////////// cDataPackage::ExtractRaw()                                                                         //// Extracts data from the RawData array.                                                              //////////////////////////////////////////////////////////////////////////////////////////////////////////long cDataPackage::ExtractRaw(int ID){  return(RawData[ID]);}////////////////////////////////////////////////////////////////////////////////////////////////////////// cDataPackage::WriteFile()                                                                          //// Writes elements of the RawData array to the ModData string. The string is then encrypted using XOR //// encryption and written to a file (*.sav).                                                          //////////////////////////////////////////////////////////////////////////////////////////////////////////void cDataPackage::WriteFile(char *FileName){  // String to hold raw integer data  string ModData;  ModData += "|";    // Write all elements of RawData  for(int x = 0; x < 10; x++)  {    // Write number to string    char *Character;    ModData += _ltoa(RawData[x], Character, 10);    ModData += "|";  }    // Open file for writing  ofstream FILE;  FILE.open(FileName, ios::trunc );    // Perform save only if file was opened successfully  if(FILE.is_open())  {    // Use XOR encryption - write data to file    for(int x = 0; x < ModData.length(); x++)    {      ModData[x] ^= Key[x % Key.length()];    }        // Write data to file    FILE << ModData;        // Close file    FILE.close();  }}////////////////////////////////////////////////////////////////////////////////////////////////////////// cDataPackage::ReadFile()                                                                           //// Stores the contents of the file in a string, de-encrypts the contents of the string using XOR      //// encryption, and writes all elements to the RawData array.                                          //////////////////////////////////////////////////////////////////////////////////////////////////////////void cDataPackage::ReadFile(char *FileName){  // Open file for reading  ifstream FILE(FileName);    // Perform only if file was opened successfully  if(FILE.is_open())  {    // Don't skip over white-space    FILE.unsetf(std::ios::skipws);        // Create string to hold file contents    string ModData;        // Load file contents into string    typedef istream_iterator<char> file_iterator;  	copy(file_iterator(FILE), file_iterator(), back_insert_iterator<string>(ModData));  	  	// Close file  	FILE.close();        // Use XOR encryption    for(int x = 0; x < ModData.length(); x++)    {      ModData[x] ^= Key[x % Key.length()];    }      	// Extract contents of ModData and place into RawData  	int SearchPos = 0;    for(int x = 0; x < 10; x++)    {      int Begin = (ModData.find("|", SearchPos)+1);      int End = ModData.find("|", (SearchPos+1));      string Num = ModData.substr(Begin, (End-Begin));      RawData[x] = atol(Num.c_str());      SearchPos = End;    }  }}


Thanks to everyone for their help!

This topic is closed to new replies.

Advertisement