File input/output

Discussion in 'C++' started by John Williams, Mar 15, 2007.

  1. 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;
    }
    John Williams, Mar 15, 2007
    #1
    1. Advertising

  2. "John Williams" <> wrote in message
    news:fS9Kh.46515$...
    : 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

    --
    http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form
    Brainbench MVP for C++ <> http://www.brainbench.com
    Ivan Vecerina, Mar 15, 2007
    #2
    1. Advertising

  3. John Williams

    Ron Natalie Guest

    John Williams wrote:
    > 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.
    Ron Natalie, Mar 15, 2007
    #3
  4. John Williams

    mlimber Guest

    On Mar 15, 9:00 am, "Ivan Vecerina"
    <> wrote:
    > "John Williams" <> wrote in message
    >
    > 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
    mlimber, Mar 15, 2007
    #4
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Emel
    Replies:
    2
    Views:
    5,841
    nish a
    Jan 29, 2010
  2. =?Utf-8?B?Q2FybG8gTWFyY2hlc29uaQ==?=

    Read Input, Write Output (File) with Umlaute

    =?Utf-8?B?Q2FybG8gTWFyY2hlc29uaQ==?=, May 26, 2005, in forum: ASP .Net
    Replies:
    4
    Views:
    2,091
    =?Utf-8?B?Q2FybG8gTWFyY2hlc29uaQ==?=
    May 26, 2005
  3. Krick
    Replies:
    1
    Views:
    1,438
    John C. Bollinger
    Aug 15, 2003
  4. Replies:
    3
    Views:
    382
  5. Tarkeshwar
    Replies:
    2
    Views:
    182
Loading...

Share This Page