Public Group

File I/O and Encryption

This topic is 4723 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

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]

Share on other sites
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));

Share on other sites
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).}

Share on other sites
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!

Share on other sites
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...

Share on other sites
Quote:
 Original post by desertcubeAbout 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.

Share on other sites
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!

Share on other sites
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?

Share on other sites
atol is expecting a C string, not a std::string, try using atol(Num.c_str()) instead.

Share on other sites
...

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!

• 9
• 13
• 41
• 15
• 13