Sign in to follow this  
Bolthame

A good file type to use?

Recommended Posts

Hey all, I was wondering, is there a file type that a simple file I/O line or two could edit, but a use couldn't? I've been using text files for game saves so far but the people who play my games just edit the saves and get ubered in the game. Thanks for your help :D

Share this post


Link to post
Share on other sites
Depending on the technicality of your users, you could just add in a checksum for the file. That way your game can recalculate the checksum and store it, but your users can't - unless they reverse engineer the checksum method. That way you can also leave text files for your own ease-of-use, since you can use your own program to ignore the checksum in debug mode, say.

Share this post


Link to post
Share on other sites
You can roll your own binary format, but be aware that some users will eventually figure out how to edit it anyway by experimenting with the hex values. One really simple (simple to implement, simple to crack) thing you could do is have your program zip up the save game file and store it with some strange extension, like ".dat", but this is security by obscurity, which isn't worth much. You could also opt for one of the binary XML formats, but you'd have to find a parser that supports them. You could also encrypt the save file, either with a full-blown symmetric-key encryption algorithm or with something really simple like XOR.

Share this post


Link to post
Share on other sites
File types are windows type of thing without saving with the .txt extension it is still the same file. As baldurk pointed out a checksum is probably the best and easiest way, although you could:
write in binary mode
write in binary with an offset to the start.
hide it in a bitmap or something similar.
use huffman where your application knows or has access to the frequency table.
use huffman and store the frequency in the file much like a zip.

the strangest way I have seen someone do it is adding the ".exe" extension to the file which was just a plain text file.

Share this post


Link to post
Share on other sites
Heh, I forgot to mention I've only been programming for a few months now..

Also, the I/O will be working with ints, chars, and possibly floats, and will be writing to and from arrays, will this effect any of your suggestions?

I was thinking of using a Steagonos (or however you spell it) Locknote file, but I don't know the file extension nor how to set a password using a program.

Any thoughts on this?

Oh, and I feel embarassed asking this, but what's a checksum?

Share this post


Link to post
Share on other sites
Make sure you're not using a more complex solution than you need to - KISS :). If all you want is to make sure most average gamers can't edit the file very easily, a checksum or writing everything in a binary format might be the way to go. Although it's not complicated per se, reading and writing binary files does come with a whole new set of things to keep track of, and it might be just as easy to ignore that for now.

I'd say that almost certainly trying to figure out or implement some kind of complex encryption or compression is too much for your needs.

Also remember that no matter what you do, someone will figure out how to edit the file - all you can do is make it harder for them.

For the checksum you just do something as simple as summing all the values you store, multiplying by some prime and then taking the modulus vs. some other prime. Say: checksum = (sum_of_data * 102983) % 1879; [note: I don't promise this is a good checksum]

This means that for any given input, you get a single number that represents it. The number might not be unique (ie. two different inputs might give the same checksum), but generally you don't have to worry about that too much.

Then when you're writing you store the correct checksum, and when you're reading you read in the checksum for the file. You then calculate what the checksum *should* be for the data you just read in, and compare it with what you read in. if they are different, the file probably hasn't been modified by someone else. If they are, the file has been tampered with.

The limitation obviously is that someone might figure out the checksumming method, and recalculate it themselves.

Also, if this game isn't to be played online, how much do you really care that people cheat? If it *is* played online then you shouldn't be letting them store any important information :).

Share this post


Link to post
Share on other sites
Quote:
Original post by baldurk
Also, if this game isn't to be played online, how much do you really care that people cheat? If it *is* played online then you shouldn't be letting them store any important information :).


Heh, I'm flattered that you think it could be an online game, but alas, it's just a text based game that runs in DOS, hahaha.

Would saving it without a file extension work? 'Cause then it would just be a File and a user wouldn't be able to open it, but would the program still be able to I/O from it?

Share this post


Link to post
Share on other sites
Bolthame, you seem to be a bit confused about the nature of files. Changing the extension is certainly possible and no problem at all - both the user and the program still won't have any problem whatsoever reading the file. The extension is just there to give a hint about what the file possibly contains.

Share this post


Link to post
Share on other sites
Eh, sorry, I've only been learning to program for a month or so and file I/O is new to me, and so is most knowledge about files. Embarassingly so, I've been taking how files work for granted :(

Also, to save making a new thread, I'll ask some more questions in this one:

1: How can I get cin (or something that works the same) to work with spaces?
2: How can I have a program I/O to the clipboard?

Thanks for your help :)

Share this post


Link to post
Share on other sites
Well, a file is just a collection of bytes. It's not much different than if you were to declare a byte array in your program, except that this one is stored on your disc. "Text files" typically encode ASCII characters so they only use seven of the eight bits in each byte. What makes the difference between file formats is not the extension but the contents. Typically some file, say an image or a movie, contains the file format type encoded in the first couple of bytes in the file, that's how programs differentiate them.

DOS programs don't typically have a clipboard, which is a feature offered by the windowing system or graphical shell. Special libraries such as ncurses might offer a clipboard however. If you need one, you'll have to write it yourself, which is as simple as declaring an array to hold bytes or chars or something similar, but implementing text editing in your program to send data to and from that buffer might be more difficult.

Share this post


Link to post
Share on other sites
There is no such thing as a file the user cannot edit. There is not even such a thing as a file the user cannot edit meaningfully. There is, however, such a thing as a file the user cannot *easily figure out how to* edit meaningfully. It's usually not worth the effort to bother, though.

Quote:
Original post by Bolthame
1: How can I get cin (or something that works the same) to work with spaces?


That depends on what you mean by "work". I'll try to read your mind, though: The operator>> on an input stream (which is to say, a file stream that is open for input, or std::cin, or a stringstream that allows reading) reads one "word" (separated by whitespace) into a target std::string variable (or into memory pointed to by a char*, but you really shouldn't be doing this). To read a whole "line" (delimited by a return character, by default; but you can specify any character to act as the delimiter) into the variable, use the free function std::getline(stream instance, target string).

Quote:
2: How can I have a program I/O to the clipboard?


I assume you are talking about Windows. This is OS-specific (There are a lot of computers in the world still whose users would have *no idea* what you meant by "clipboard"), so you'll need to pull in Windows headers and do Windows-specific development. I don't know anything about this task in particular, but the best starting point to find out is MSDN.

Share this post


Link to post
Share on other sites
Here, I digged up some old library of mine allowing to read/write to the clipboard. Some stuff is library-specific but you should be able to figure it out.


bool WriteTextToClipboard(const String & text)
{
if(!OpenClipboard(NULL)) return false;

HGLOBAL bufferhandle;
EmptyClipboard();
bufferhandle = GlobalAlloc(GMEM_MOVEABLE, text.length() + 1); // +1 for null-terminator
if(bufferhandle)
{
char * buffer = (LPTSTR)GlobalLock(bufferhandle);
memcpy(buffer, text.strz(), text.length()+1); // +1 for null-terminator
GlobalUnlock(bufferhandle);
SetClipboardData(CF_TEXT, bufferhandle);
}

CloseClipboard();

return true;
}

bool LoadTextFromClipboard(String & text)
{
if(!OpenClipboard(NULL)) return false;

text = (const FEchar*)GetClipboardData(CF_TEXT);

CloseClipboard();

return true;
}




Now if you're really beginning, this might be a bit confusing, you might want to get to it later.

Share this post


Link to post
Share on other sites
Yeah, haha, that's way beyond me. Ah well, I'll do it later on when I'm working with windows programs instead of DOS :)

And thanks Zahlman for telling me about getline(), but what did you mean by string instance? (Sorry for asking such noobish questions, but I'm really very much a beginner at this :))

Share this post


Link to post
Share on other sites
Quote:
Original post by Bolthame
Yeah, haha, that's way beyond me. Ah well, I'll do it later on when I'm working with windows programs instead of DOS :)


Nitpicking point, but that's really DOS. DOS is an operating system. Windows XP (what I'm guessing you're using) is an operating system. Older versions of Windows ran on Top of DOS, but the thing you are using, the console, is just the command line interpretation of the windows XP operating system. The same functionality exists, just in different forms.

Okay, that aside-- stream instances. std::cin is an instance of a stream. std::fstream is a stream type that can be used to create an instance of a stream to read or write from files. std::stringstream is a stream type that can be used to create generic streams that aren't tied to a specific device (console, file, whatever). What do these things have in common? They inherit from a common base class. Because they all inherit from the same base class you can write the a function using only that base class and then you can pass any derived object to said base class. This page describes the inheritance heirarchy of all of the streams. std::getline probably expects an istream or any of its derivitives.

Hope that helps.

Share this post


Link to post
Share on other sites
Ahh, I think I get it now.

Also, one more thing:

Is there any way to take a letter from a word and change it?

What I need in a nut shell is a way to convert letters into numbers.

Thanks again for your help :)

Share this post


Link to post
Share on other sites
Quote:
Original post by Bolthame
Ahh, I think I get it now.

Also, one more thing:

Is there any way to take a letter from a word and change it?


The std::string class represents a mutable string, so yes. You can index into the string just as if it were an array of characters, and assign a different character at the appropriate position.

Quote:
What I need in a nut shell is a way to convert letters into numbers.


This is quite ambiguous. Please give some examples.

Share this post


Link to post
Share on other sites
Well what I'm doing is a challenge to create a program that works like the enigma code (you can't decode it unless you have the code the person who encoded it has). So what I have so far is, it takes a letter, makes it into a number (a = 1, b = 2, c = 3, etc), generates a random number (this is also the code used to decode the message), and then multiplies, adds, divides and subtracts by it, gives you the code and saves the encoded message, which will look like a load of random numbers. As part of the challenge, the message must be one letter per line, for some reason. So far I have this code:

#include <iostream>
#include <fstream>
#include <string>
#include <windows.h>
#include <cstdlib>
#include <time.h>

using namespace std;

int main()
{
string letter;
int letternumber;

srand(time(NULL));
int code = rand(); //This generates the random number which the letters will be encoded by
ifstream fin;
fin.open("Encode.txt"); //This loads the file in program input, so it inputs to the program, as this is the file where
while(! fin.eof() ) //the program gets the letters to encode.
{
getline (fin, letter);
if(letter == "a" || letter == "A")
{
fin.close();
letternumber = (1 * code) + code;
ofstream fout;
fout.open("Encoded_Message.txt");
fout << letternumber << "\n";
fout.close();
fin.open("Encode.txt");
}
if(letter == "b" || letter == "B")
{
fin.close();
letternumber = (2 * code) + code;
ofstream fout;
fout.open("Encoded_Message.txt");
fout << letternumber << "\n";
fout.close();
fin.open("Encode.txt");
}
if(letter == "c" || letter == "C")
{
fin.close();
letternumber = (3 * code) + code;
ofstream fout;
fout.open("Encoded_Message.txt");
fout << letternumber << "\n";
fout.close();
fin.open("Encode.txt");
}
if(letter == "d" || letter == "D")
{
fin.close();
letternumber = (4 * code) + code;
ofstream fout;
fout.open("Encoded_Message.txt");
fout << letternumber << "\n";
fout.close();
fin.open("Encode.txt");
}
if(letter == "e" || letter == "E")
{
fin.close();
letternumber = (5 * code) + code;
ofstream fout;
fout.open("Encoded_Message.txt");
fout << letternumber << "\n";
fout.close();
fin.open("Encode.txt");
}
}
fin.close();
system("CLS");
cout << "Encoding complete. Your decode code is: " << code << ".\n";
cout << "Please remember this code as it is the only way to decode your message.\n";
system("Pause");
return 0;
}


But it only encodes the first line, and then gets stuck, what's wrong with it?

Share this post


Link to post
Share on other sites
You open and close the file every time? So the only thing you see is the last thing written to the file, not the first (std::ofstream::open truncates the file unless you tell it otherwise).

Also, note that it is idiomatic to use the std::getline as the loop condition:

while( std::getline(fin,letter) ) {
// process input
}



It is also idiomatic to open a file stream at the same time as declaring it:

ifstream fin("Encode.txt");




In this example open both the read and write files just before the loop, and read from one and write to the other. Don't bother with opening and closing the file every loop, its too complicated. Anyway I don't see a reason to open and close the file continuously either.

As a hint, instead of writing the same code over and over to handle each different letter, recall that each ascii letter is given a number. A symbol representing this number in your C++ source code is 'a', 'b'...'z'. These are character literals and can be treated the same way you would treat a number.

Note that 'a'-'a' = 0. This may not look helpful, but 'b'-'a' = 1, and 'c' - 'a' = 2, and so on. This is almost what we need to get the sequence (a = 1, b = 2 ,c = 3).

Using the function "tolower" (I think it is in <ctype>) we can find the lower-case value of any character. From there you may be able to piece together an algorithm to get the numeric value of an arbitrary letter, in upper or lower case.

I will leave it to you to see if you can [smile].

[edit: also, you can use a std::string like an array. So to get the character value of the first letter in a string named str use str[0] [/edit]

Share this post


Link to post
Share on other sites
Quote:
Original post by Bolthame
Well what I'm doing is a challenge to create a program that works like the enigma code (you can't decode it unless you have the code the person who encoded it has).


A challenge from where?

Quote:
So what I have so far is, it takes a letter, makes it into a number (a = 1, b = 2, c = 3, etc), generates a random number (this is also the code used to decode the message), and then multiplies, adds, divides and subtracts by it, gives you the code and saves the encoded message, which will look like a load of random numbers.


::facepalm:: Please don't try to make up the algorithm for scrambling things. It's very easy to create something that *weakens* the encryption when you think you're strengthening it. Anyway, no matter what your algorithm is, if you just take a *single* random number and consider the letters of the message *separately*, you get the same effective result: a simple substitution cipher.

Quote:
As part of the challenge, the message must be one letter per line, for some reason. So far I have this code:


1) Don't compare the lines to various one-character string literals. Instead, check that the line is one character long, and then grab the first (and only) character. You don't need to compare the character to character literals, because characters are numbers: you can check them arithmetically.

(Also, note that '(x * code) + code' is equivalent to '(x + 1) * code', for all X; again, please don't try to make up the algorithms. Just doing the multiplication would give you all the benefit you can get out of this approach, and actually it's quite poor: not only is it subject to cryptographic analysis like any simple substitution cipher, but the "random" numbers in the output will be quite strongly patterned - once I know one letter value, I can guess the others easily.)

2) You absolutely do not need windows.h for this. Cut it out. Also, time.h is now ctime in C++.

3) It is very rare that you call .close() on any stream object. Here, it's the cause of your problem: you are closing and reopening the input file immediately, the first time through the loop, which resets the file reading to the beginning. Stream objects automatically .close() themselves when they go out of scope, so you only use it explicitly in very special circumstances. Similarly, you want to open the output file at the beginning of the loop, and keep it open until you've finished processing.

4) As noted, don't check for .eof() to loop here. Instead, use the reading expression as the loop conditional, as shown.

5) Normally, use the constructor of the stream to open it.

6) Don't artificially pause your programs at the end. (Also, clearing the screen is kind of overkill here.) In general, system() calls are a bad idea in C++, because if you really wanted/needed to call other programs to do your work, you should be writing a shell script instead.

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <ctime>

using namespace std;

int main() {
srand(time(NULL));
int code = rand();
ifstream fin("Encode.txt");
ofstream fout("Encoded_Message.txt");
string line;
while (!getline(fin, line) {
if (line.size() != 1) { continue; } // not a single-letter line.
char letter = line[0];
if (letter >= 'a' && letter <= 'z') {
fout << ((letter - 'a' + 1) * code) << "\n";
} else if (letter >= 'A' && letter <= 'Z') {
fout << ((letter - 'A' + 1) * code) << "\n";
}
}
cout << "Encoding complete. Your decode code is: " << code << ".\n";
cout << "Please remember this code as it is the only way to decode your message.\n";
}

Share this post


Link to post
Share on other sites
Hooah, got it working. Thanks guys :)

But, alas! Another problem! (You guys must be getting sick of me, just say if you are haha, I'm ok with it)

I have
ifstream fin ("Encoded Message.txt"); //Loads the file where the program gets the encoded message to decode
ofstream fout ("Decoded Message.txt"); //Loads the file where the decoded message will go
while(! fin.eof() ){
getline (fin, number);
number = (number - code)/code;

if(number == 1){
fout << "A";
}
}

fin.close();
fout.close();


This is only a little bit of the source file, the rest works fine, "number" is an int, and when I compile I get the error "no matching function for call to `getline(std::ifstream&, int&)' " relating to the "getline (fin, number);" line. What's going on here?

Share this post


Link to post
Share on other sites
Quote:
Original post by Bolthame
Hooah, got it working. Thanks guys :)

But, alas! Another problem! (You guys must be getting sick of me, just say if you are haha, I'm ok with it)

I have
ifstream fin ("Encoded Message.txt"); //Loads the file where the program gets the encoded message to decode
ofstream fout ("Decoded Message.txt"); //Loads the file where the decoded message will go
while(! fin.eof() ){
getline (fin, number);
number = (number - code)/code;

if(number == 1){
fout << "A";
}
}

fin.close();
fout.close();


This is only a little bit of the source file, the rest works fine, "number" is an int, and when I compile I get the error "no matching function for call to `getline(std::ifstream&, int&)' " relating to the "getline (fin, number);" line. What's going on here?


std::getline doesn't read ints. It reads lines of text (hence, "get line"), i.e., strings. Please study the provided code more closely.

And note that again, your math may not work as well as you'd expect, and again, you should use a similar trick to translate the number back to a letter after "decoding" it, and again, you shouldn't be calling .close() explicitly.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this