Jump to content
  • Advertisement
Sign in to follow this  
TheShadow344

File I/O and Encryption

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

If you intended to correct an error in the post then please contact us.

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 this post


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

Share this post


Link to post
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 this post


Link to post
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 this post


Link to post
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 this post


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

Share this post


Link to post
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 this post


Link to post
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 RawData
int 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 this post


Link to post
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!

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!