0

I have to read a .pgm file for an assignment and decode the least significant bit in every byte to get some hidden text. I have the reading and writing code from the professor but when I try to read the file color has an issue being written to. I think I know how to actually get the hidden text out but the I/O always gives me trouble.

Thanks in advance for your help.

It wouldnt let me add the .pgm so i ranamed it as .png, so if you download it make sure to rename it to .pgm this is the .pgm image uploaded as a .png

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

typedef unsigned char uchar;

/****************************************/
/* Clear PGM (XV) comments.             */
/****************************************/
void pgmCommentClear(FILE* disk) {
    uchar  ch;
    fread(&ch, 1, 1, disk);
    if (ch != '#') {
        fseek(disk, -1, SEEK_CUR);
        return;
    }
    do {
        while (ch != '\n') fread(&ch, 1, 1, disk);
    } while (ch == '#');
    pgmCommentClear(disk);
}

/****************************************/
/* Read PGM formatted image (1D array). */
/****************************************/
uchar* PGM_FILE_READ_1D(char* FileName, int* Width, int* Height, int* color) {
    int   pmax;
    char  ch;
    char  type[3];
    uchar* Image;
    FILE* disk;
    if ((disk = fopen(FileName, "rb")) == NULL) {
        return NULL;
    }
    fscanf(disk, "%s", type);
    if (!strcmp(type, "P6")) *color = 1;
    else *color = 0;
    fread(&ch, 1, 1, disk);
    pgmCommentClear(disk);
    fscanf(disk, "%d", Width);
    fscanf(disk, "%d", Height);
    fscanf(disk, "%d", &pmax);
    fread(&ch, 1, 1, disk);
    if (*color == 1) {
        Image = (uchar*)calloc(*Height * *Width * 3, sizeof(uchar));
        fread(Image, 1, (*Height * *Width * 3), disk);
    }
    else {
        Image = (uchar*)calloc(*Height * *Width, sizeof(uchar));
        fread(Image, 1, (*Height * *Width), disk);
    }
    fclose(disk);
    return Image;
}

/****************************************/
/* Write PGM formatted image (1D array).*/
/****************************************/
void PGM_FILE_WRITE_1D(char* FileName, uchar* Image, int Width, int Height, int color) {
    FILE* disk;
    disk = fopen(FileName, "wb");
    if (color == 1) fprintf(disk, "P6\n");
    else fprintf(disk, "P5\n");
    fprintf(disk, "%d %d\n", Width, Height);
    fprintf(disk, "255\n");
    if (color == 1) {
        fwrite(Image, 1, (Height * Width * 3), disk);
    }
    else {
        fwrite(Image, 1, (Height * Width), disk);
    }
    fclose(disk);
}

int main(int argc, char const* argv[]) {


    // do command line args stuff for extra credit
    //if (argv == 2) { }

    // read the file
    //int width = 876
    //int height = 594
    
    uchar* image = PGM_FILE_READ_1D("./hw10.pgm", 876, 594, 0);

    printf("%c", *image);

}
FunkyMunky
  • 21
  • 5

1 Answers1

1

Pretty cool assignment, Ron Marsh ;-)

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

typedef unsigned char uchar;

/****************************************/
/* Clear PGM (XV) comments.             */
/****************************************/
void pgmCommentClear(FILE* disk) {
    uchar  ch;
    fread(&ch, 1, 1, disk);
    if (ch != '#') {
        fseek(disk, -1, SEEK_CUR);
        return;
    }
    do {
        while (ch != '\n') fread(&ch, 1, 1, disk);
    } while (ch == '#');
    pgmCommentClear(disk);
}

/****************************************/
/* Read PGM formatted image (1D array). */
/****************************************/
uchar* PGM_FILE_READ_1D(char* FileName, int* Width, int* Height, int* color) {
    int   pmax;
    char  ch;
    char  type[3];
    uchar* Image;
    FILE* disk;
    if ((disk = fopen(FileName, "rb")) == NULL) {
        return NULL;
    }
    fscanf(disk, "%s", type);
    if (!strcmp(type, "P6")) *color = 1;
    else *color = 0;
    fread(&ch, 1, 1, disk);
    pgmCommentClear(disk);
    fscanf(disk, "%d", Width);
    fscanf(disk, "%d", Height);
    fscanf(disk, "%d", &pmax);
    fread(&ch, 1, 1, disk);
    if (*color == 1) {
        Image = (uchar*)calloc(*Height * *Width * 3, sizeof(uchar));
        fread(Image, 1, (*Height * *Width * 3), disk);
    }
    else {
        Image = (uchar*)calloc(*Height * *Width, sizeof(uchar));
        fread(Image, 1, (*Height * *Width), disk);
    }
    fclose(disk);
    return Image;
}

/****************************************/
/* Write PGM formatted image (1D array).*/
/****************************************/
void PGM_FILE_WRITE_1D(char* FileName, uchar* Image, int Width, int Height, int color) {
    FILE* disk;
    disk = fopen(FileName, "wb");
    if (color == 1) fprintf(disk, "P6\n");
    else fprintf(disk, "P5\n");
    fprintf(disk, "%d %d\n", Width, Height);
    fprintf(disk, "255\n");
    if (color == 1) {
        fwrite(Image, 1, (Height * Width * 3), disk);
    }
    else {
        fwrite(Image, 1, (Height * Width), disk);
    }
    fclose(disk);
}

int main(int argc, char const* argv[]) {

    // do command line args stuff for extra credit
    char newMsg[100] = "";
    if (argc >= 2) 
        for (int i = 1; i < argc; i++) {
            strcat(newMsg, argv[i]);
            strcat(newMsg, " ");
        }
        else
            strcat(newMsg, "Greetings from the other side");
    strcat(newMsg, "\0");

    int width, height, color;
    
    // read the file
    uchar* image = PGM_FILE_READ_1D("./hw10.pgm", &width, &height, &color);
    printf("width = %d, height = %d, color = %d\n", width, height, color);

    // Read encoded message
    int size = width * height, bitCount = 8;
    uchar hiddenChar = 0;
    for (int i = 0; i < size; i++) {
        //shift least-significant bit left `bitCount` times and overlay onto `hidden`
        hiddenChar |= ((image[i] & 0x01) << --bitCount); 
        // After 8 bits we have the hidden byte
        if (bitCount == 0) {
            // If character is null terminator then we're done
            if (hiddenChar == 0) 
                break;
            // Print hidden character and reset
            printf("%c", hiddenChar);
            bitCount = 8;
            hiddenChar = 0;
        }
    }

    // Encode new message
    int len = strlen(newMsg) + 1;
    for (int i = 0; i < len; i++) 
        for (int j = 0; j < 8; j++) 
            if ( newMsg[i] & (0x01 << (7-j) ) )                 
                image[i * 8 + j] |= 0x01;
            else
                image[i * 8 + j] &= 0xfe;
    PGM_FILE_WRITE_1D("./hw10-out.pgm", image, width, height, color);

    return 0;
}
  • Was it that obvious? Whoops. Thanks for the code. – FunkyMunky Dec 05 '22 at 18:17
  • You don't have to but I would appreciate if you would post the extra credit part as well. – FunkyMunky Dec 05 '22 at 18:20
  • Extra credit: File name can be passed in on command line, otherwise defaults to "./hw10.pgm". Maybe you can mark this are the right answer now ;-) – Anthony Kelly Dec 05 '22 at 18:31
  • How did you write to the .pgm for extra credit. Did you just bitshift left while AND-ing between the image and the text on every 8th bit? I guess I'm just having troubles thinking about looping or shifting through without destroying the data. – FunkyMunky Dec 05 '22 at 18:55
  • It doesn't write anything, just reads the least-significant bit for each byte, performs a logical OR and bitshifts left each time. After eight bytes we have created a new byte with the hidden character, which is just printer and the process is repeated until hit the null terminator character. I don't know exactly what your extra credit task was but I just saw the comment about processing argv and thought it was jsut to pass the file name in on the command line. If there's more to it than that I think I'll leave the rest to you ;-) – Anthony Kelly Dec 05 '22 at 19:11
  • My bad I should've explained that the extra credit was. It's basically just writing a hidden message to a .pgm I'm just confused on how i can loop through and edit ever 8th bit using bit shifting because wouldn't that damage the data? Edit: can i just loop through using the char[i] style and then just OR the two bytes together? – FunkyMunky Dec 05 '22 at 19:48
  • Code updated to encode new message, which can be passed in on command line, into new file called, hw10-out.pgm. I'm done :-) – Anthony Kelly Dec 06 '22 at 10:59