File input/output

J

John Williams

I'm writing a stagenography program to experiment with how it works.
The algorithm I'm using appears to be producing the correct
result...however I'm struggling with the file input. I never learned
file input/output very well (I self taught all the programming I
know...and my c++ book was not good) and so I'm not sure what's wrong
with this. The problem is the function void encodemsg(fstream *img,
fstream *msg, fstream *out, char key[])

on line 69 I attempt to set img->seekg(int) to the point I want to begin
the embedding at. However it constantly stays at -1 as the img->tellg()
on line 77 shows. I don't understand why since it should start at 87
since that is what I intend to have it set to on line 69. Any help
would be appreciated as well if anyone has a good c++ file operations
tutorial they know of it would be appreciated.


/* An example of stranography. This program hides another file (most likely a txt message) inside a
* 24 bit bmp image file. It changes the LSB of each byte of the image data. This alters the color
* of each pixel by a small amount that should be undetectable visually.
*/

#include <fstream>
#include <iostream>

#define BITS_CHAR (sizeof(char) * 8)
#define BMPSTART 55

using namespace std;

void encodechar(char *, char , char *);
void encodemsg(fstream *, fstream *, fstream *, char *);
void encodeint(char *, int, char*);
void decodechar(char *, char );
void decodemsg(fstream *, fstream *, char *);
int decodeint(char*);
int filesizer(fstream *);
bool fileopener(fstream *, char *, bool);
char cipher(char *, char *);
int cipherint(int , int);
bool checksize(fstream *, fstream *);

using namespace std;

bool checksize(fstream *image, fstream *message)
{
// Verifies the message is small enough to fit in the bitmap
if ((filesizer(message) * 8) > (filesizer(image) - (BMPSTART + 8 * sizeof(int))))
return false;
else
return true;
}

char cipher(char message, char key)
{
// XOR crypts the message to aid in protecting the message.
// Todo: Add better encryption
return (message ^ key);

}

int cipherint(int len, int key)
{
return (len ^ key);
}

void encodemsg(fstream *img, fstream *msg, fstream *out, char key[])
{
/* Acts as a driver to hide the work that goes into encoding the message from main.
*/
/* Possibly rewrite...since we are using an image copy already we might be able to
* change the fstream out to do both input and output saving us having to pass it in
* as well as simplifying the code.
*/


int i, j, key_size, msg_size, cryptsize, msgkey;
char imgbuffer[BITS_CHAR], outbuffer[BITS_CHAR], msgbuffer, bite, msgcrypt;
char imgintbuff[BITS_CHAR * sizeof(int)], intkey[sizeof (int)];
char outintbuff[BITS_CHAR * sizeof(int)];

key_size = strlen(key);

msg_size = filesizer(msg);

img->seekg(BMPSTART + 8 * (sizeof(int))); // Leave room for an int (msgsize)
out->seekp(BMPSTART + 8 * (sizeof(int))); // So we can encode the message size
msg->seekg(0);


for (i = 0; !msg->eof(); i++) { // Read the image and message, ecrypt the message
if (i >= key_size) // then encode the message into the output image
i = 0;
cout << img->tellg() << endl; // debug info
cout << out->tellp() << endl; // debug info
img->read(imgbuffer, BITS_CHAR);
msg->read(&msgbuffer, sizeof(char));
msgcrypt = cipher(msgbuffer, key);
encodechar(imgbuffer, msgcrypt, outbuffer);
out->write(outbuffer, BITS_CHAR);
}

out->seekp(BMPSTART);
img->seekg(BMPSTART);

for (i = 0; i < sizeof(int); i++) {
intkey = key;
}

msgkey = atoi(key);

img->read(imgintbuff, (BITS_CHAR * sizeof(int)));
cryptsize = cipherint(msg_size, msgkey);
encodeint(imgintbuff, cryptsize, outintbuff);
out->write(outintbuff, (BITS_CHAR * sizeof(int)));

return;
}

void decodemsg(fstream *img, fstream *msg, char key[])
{
// Acts as a driver between main and the function to decode the chars

int i, j, key_size, msg_len, msgkey;
char imgbuffer[BITS_CHAR], msgbuffer, msg_lenbuff[sizeof(int)];
char imgintbuff[BITS_CHAR * sizeof(int)], intkey[sizeof (int)];
char outintbuff[BITS_CHAR * sizeof(int)];

key_size = strlen(key);

img->seekg(BMPSTART);
msg->seekp(0);

for (i = 0; i < sizeof(int); i++) { // Decode and decrypt the message length from the image
intkey = key;
}

msgkey = atoi(key);

img->read(imgintbuff, (BITS_CHAR * sizeof(int)));
msg_len =decodeint(imgintbuff);
msg_len = cipherint(msg_len, msgkey);

for (i = 0, j = 0; i < msg_len; i++, j++) { //Read image then send it to be decoded and deciphered
if (j >= key_size)
j = 0;
img->read(imgbuffer, BITS_CHAR);
decodechar(imgbuffer, msgbuffer);
msgbuffer = cipher(msgbuffer, key[j]);
msg->write(&msgbuffer, sizeof(char));
}
return;
}

void encodechar(char bitmap[BITS_CHAR], char message, char retbitmap[BITS_CHAR])
{
// Encdodes a char into a bitmap takes 8 bytes of the bit map.
// Todo: Make it so larger messages can be encoded at the exchange of image quality (use more bits)
int i, shift;

shift = (BITS_CHAR - 1);

for (i = 0; i < BITS_CHAR; i++, shift--) {
retbitmap = (bitmap & 0xFF) | ((message >> shift) & 0x01);
}
return;
}

void decodechar(char bitmap[BITS_CHAR], char message)
{
// Decode chars from the bytes of image data
int i;

message = 0x00;

for (i = 0; i < BITS_CHAR; i++) {
bitmap = bitmap & 0x01; // Mask out all except last bit
message = message | (bitmap << (BITS_CHAR - i)); //move bit to correct spot and OR it in
}
return;
}

void encodeint(char bitmap[BITS_CHAR * sizeof(int)], int len, char retbitmap[BITS_CHAR * sizeof(int)])
{
int i, shift;

shift = ((BITS_CHAR * sizeof(int)) - 1);

for (i = 0; i < (BITS_CHAR * sizeof(int)); i++, shift--) {
retbitmap = (bitmap & 0xFF) | ((len >> shift) & 0x01);
}
return;
}

int decodeint(char bitmap[BITS_CHAR * sizeof(int)])
{
int i, len;

len = 0x00;

for (i = 0; i < (BITS_CHAR * sizeof(int)); i++) {
bitmap = bitmap & 0x01; // Mask out all except last bit
len = len | (bitmap << ((BITS_CHAR * sizeof(int)) - i)); //move bit to spot and OR it in
}
return len;
}

void copyimage(fstream *image, fstream *output)
{
/* Duplicates the image. The encode work is all done on the copy of the image
*/
char buffer;
int i;

image->seekg(0);
image->seekp(0);

while (!image->eof()) {
image->read(&buffer, sizeof(char));
output->write(&buffer, sizeof(char));
}
return;
}

int filesizer(fstream *file)
{
// Determines the file size by seeking the end of the file
int size1 = 0, size2 = 0;

file->seekg(0, ios::end);
size1 = file->tellg();
file->seekp(0, ios::end);
size2 = file->tellp();

if (size2 > size1) {
size1 = size2;
}
file->seekg(0);
file->seekp(0);
return size1;
}

bool fileopener(fstream *file, char filename[], bool mode)
{
/* Used to open a file. Tests to make sure it's open and if it's goint to be used for input
* that the file has data in it.
*/
// Todo: make it confirm overwrite of nonempty output files

int size;

if (mode == 1) {
file->open(filename, ios::in);
if (file->is_open()) {
if (filesizer(file) <= 0) {
cerr << "Input file is empty!\n";
return false;
}
else return true;
}
else {
cerr << "File " << filename << " Failed to open!\n";
return false;
}
}
if (mode == 0) {
file->open(filename, ios::eek:ut);
if (file->is_open()) {
return true;
}
else {
cerr << "File " << filename << " Failed to open!\n";
return false;
}
}
return false;
}

int main(int argc, char *argv[])
{
int i;
fstream image, message, image_out;

if ((argc < 4) || (argc < 5)) {
cerr << "Usage: bmpmess <image_file> <message_file> [image_out] <key>\n";
return EXIT_FAILURE;
}

if (!fileopener(&image, argv[1], 1))
return EXIT_FAILURE;
if (argc == 4) {
if (!fileopener(&message, argv[2], 0))
return EXIT_FAILURE;
decodemsg(&image, &message, argv[3]);
}

if (argc == 5) {
if (!fileopener(&image_out, argv[3], 0))
return EXIT_FAILURE;
if (!fileopener(&message, argv[2], 1))
return EXIT_FAILURE;
if (!checksize(&image, &message)) {
cerr << "Image too small for message\n";
return EXIT_FAILURE;
}
copyimage(&image, &image_out);
encodemsg(&image, &message, &image_out, argv[4]);
}
return 0;
}
 
I

Ivan Vecerina

: I'm writing a stagenography program to experiment with how it works.
You mean Steganography, I assume.

: The algorithm I'm using appears to be producing the correct
: result...however I'm struggling with the file input. I never learned
: file input/output very well (I self taught all the programming I
: know...and my c++ book was not good) and so I'm not sure what's wrong
: with this. The problem is the function void encodemsg(fstream *img,
: fstream *msg, fstream *out, char key[])

One key problem I saw while skimming through the code is that
you appear to be opening the files in *text* mode, instead
of binary mode. For instance:
file->open(filename, ios::in);
Because you are processing binary files (as .bmp-s are), you should
be writing:
file->open(filename, ios::in||ios::binary );
Files opened in text mode are likely to choke on the first null
byte, or cause other problems with newline character converstions.

Also, for binary i/o, you should use the streambuf instances
themselves, rather than the stream classes.

Finally you have to deal with possibly inconsistent error
reporting (exceptions vs. error flags, depending on the
configuration of a specific instance), cryptic member function
names (sbumpc, etc), and a sub-par performance on most platforms.

: on line 69 I attempt to set img->seekg(int) to the point I want
: to begin the embedding at. However it constantly stays at -1 as
: the img->tellg() on line 77 shows.
: I don't understand why since it should start at 87
: since that is what I intend to have it set to on line 69. Any help
: would be appreciated as well if anyone has a good c++ file operations
: tutorial they know of it would be appreciated.


Sorry, no specific suggestions come to mind beyond what a trivial
google or yahoo search will bring up. (e.g.
http://www.digilife.be/quickreferences/PT/The C++ IOStreams Library.pdf)

Truth is, I am among the (many) C++ programmers who find
that the standard C++ file I/O is ill-suited for binary I/O.
It is usually easier and more efficient to use the C (fopen etc)
file i/o interface, with thin wrapper implementing RAII.
[ I tend to use platform-specific io tools, which take advantage
of memory-mapping, when available, for better performance ]

But I hope that the rest of my advice will help,
Ivan
 
R

Ron Natalie

John said:
I'm writing a stagenography program to experiment with how it works. The
algorithm I'm using appears to be producing the correct result...however
I'm struggling with the file input. I never learned file input/output
very well (I self taught all the programming I know...and my c++ book
was not good) and so I'm not sure what's wrong with this. The problem
is the function void encodemsg(fstream *img, fstream *msg, fstream *out,
char key[])

on line 69 I attempt to set img->seekg(int) to the point I want to begin
the embedding at. However it constantly stays at -1 as the img->tellg()
on line 77 shows. I don't understand why since it should start at 87
since that is what I intend to have it set to on line 69. Any help
would be appreciated as well if anyone has a good c++ file operations
tutorial they know of it would be appreciated.
First off, PLEASE MARK the lines you are referring to. Nobody here wants
to count the lines in your program.

Your program has a number of robustness issues and downright logic
problems. Your first misconception is how eof() works. eof()
returns true only after a read operation as returned error. You
fail to check for this.

Your fileopener should open the streams in binary mode.

The fact that tell is returning -1 is a sign that your stream is
probably in an error state. You never seem to EVER check
for errors in your program. I haven't looked at it, but I
suspect your filesizer hit the EOF and never bothered to clear
it. Try calling clear to reset the error flags.
 
M

mlimber

One key problem I saw while skimming through the code is that
you appear to be opening the files in *text* mode, instead
of binary mode. For instance:
file->open(filename, ios::in);
Because you are processing binary files (as .bmp-s are), you should
be writing:
file->open(filename, ios::in||ios::binary );

Of course you meant:

file->open(filename, ios::in | ios::binary ); // logical or

This is probably the big problem.
Files opened in text mode are likely to choke on the first null
byte, or cause other problems with newline character converstions.

Also, for binary i/o, you should use the streambuf instances
themselves, rather than the stream classes.

It's fine to use fstream's read and write member functions, but you
may want to look into Ivan's suggestions if you run into performance
issues.

Beyond Ivan's advice, I'd suggest you start using references rather
than pointers for your function parameters because they're less typing
and less error prone. Also, get rid of the magic numbers for file mode
(substitute your own enum, or just use ios::in and ios::eek:ut), or
better yet, prefer ifstream and ofstream to fstream when you don't
need both reading and writing for a file. And don't use
fstream::eof(). Prefer something like:

while( my_stream )
{
// Do reading/writing with my_stream
}

See http://www.parashift.com/c++-faq-lite/input-output.html#faq-15.5

Cheers! --M
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,525
Members
44,997
Latest member
mileyka

Latest Threads

Top