// 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();
}
}
File I/O and Encryption
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...
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]
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).
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 :)
- 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:
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!
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.
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!
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...
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?
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?
...
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!!
Thanks to everyone for their help!
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
Popular Topics
Advertisement