0

Relatively new to programming, been assigned to a task to create a C program to encode and decode 2 ppm images to receive a secret message. I've came up with code but its not getting past the image format if statement in the getPPM function. Any tips on what changes I could make to my code? Also in my main(), am I passing in the correct parameters?

typedef struct NODE {
    char *val;
    struct NODE *next;
} NODE;

/*
   Stores the RGB values of a pixel
 */
typedef struct Pixel {
    int red;
    int green;
    int blue;
} Pixel;

/*
   Stores the contents of a PPM file
 */
typedef struct PPM {
    char *format;
    NODE *comments;
    int width, height;
    int max;
    Pixel *arr;
} PPM;

NODE *copy(NODE *first)
{
    NODE *second = NULL, *previous = NULL;

    while (first != NULL) {
        NODE *temp = (NODE *)malloc(sizeof(NODE));
        temp->val = first->val;
        temp->next = NULL;

        if (second == NULL) {
            second = temp;
            previous = temp;
        } else {
            previous->next = temp;
            previous = temp;
        }
        first = first->next;
    }
    return second;
}

//Copy the PPM File Returning Pointer
struct PPM* createPPM(PPM *old)
{
    PPM* new = (PPM *)malloc(sizeof(PPM));

    strcpy(new->format, old->format);
    new->comments = copy(old->comments);

    new->height = old->height;
    new->width = old->width;
    new->max = old->max;

    new->arr = (Pixel *)malloc(old->height * old->width * sizeof(Pixel));
    memcpy(new->arr, old->arr, old->height * old->width * sizeof(Pixel));

    return new;
}

/*
   Returns the PPM struct from the file fd
 */
PPM *getPPM(FILE *fd) {
    PPM *image = (PPM *)malloc(sizeof(PPM));
    image->format = (char *)malloc(MAX_LEN);

    fgets(fd, MAX_LEN, image->format);
    sscanf(fd, "%s", image->format);

    if (strcmp(image->format, "P3") != 0) {
        printf("Invalid Image Type");
        exit(0);
    }
    char c = getc(fd);
    image->comments = (NODE*)malloc(sizeof(NODE));
    NODE *temp = image->comments;
    while ((c = getc(fd)) == '#') {
        fseek(fd, -1, SEEK_CUR);
        char str[50];
        fgets(str, 50, fd);
        temp->val = (char*)malloc(strnlen(str, 50));
        strcpy(temp->val, str);
        temp->val[strlen(temp->val) - 1] = 0;
        temp->next = (NODE*)malloc(sizeof(NODE));
        temp = temp->next;
        temp->next = NULL;
    }

    fseek(fd, -1, SEEK_CUR);
    fscanf(fd, "%d", &image->width);
    fscanf(fd, "%d", &image->height);
    fscanf(fd, "%d", &image->max);

    image->arr = (Pixel*)malloc(image->height * image->width * sizeof(Pixel));

    int t = 0;
    int j = 0;

    while (j < image->height * image->width) {
        t = fscanf(fd, "%d", &image->arr[j].red);
        t = fscanf(fd, "%d", &image->arr[j].green);
        t = fscanf(fd, "%d", &image->arr[j].blue);
        j = j + 1;
    }

    return image;
}

PPM *loadPPMFromFile(char *filename) {
    FILE *file;

    /* TODO: step 1, open the file */
    file = fopen("C:\\Users\\Olivia\\source\\repos\\f28hs-2020-21-cwk1-c\\PPMfiles", "r");

    /* step 2: Check that file has been loaded correctly; show error otherwise*/
    if (file != NULL) {
        PPM *ppm;
        /* ODO: step 3, get the PPM data from the file with the getPPM function */
        getPPM(file);

        if (ppm == NULL) {
            /* TODO: step 4, display error if file cannot been parsed into a PPM struct*/
            printf("File cannot be parsed.\n");
        }

        /* TODO: step 5, close the file */
        fclose(file);
        /* step 6: return the PPM */
        return ppm;
    } else {
        fclose(file);
        return NULL;
    }
}

/*
   Prints a PPM struct in the format of a PPM file
 */
void showPPM(PPM *ppm) {

    printf("%s\n", ppm->format); //print format

    //print comments
    NODE *n = ppm->comments;
    while (n->next != NULL) {
        printf("%s\n", n->val);
        n = n->next;
    }

    //print width, height and max
    printf("%d %d\n%d\n", ppm->width, ppm->height, ppm->max);

    //print the array containing the pixels
    int j;
    for (j = 0; j < ppm->height * ppm->width; ++j) {
        printf("%d %d %d\n", ppm->arr[j].red, ppm->arr[j].green, ppm->arr[j].blue);
    }

    return;

}

/*
   Encodes text into red field of PPM
   Returns the encoded PPM
 */
PPM *encode(char *text, PPM *i) {
    PPM *str = createPPM(i);
    int random;
    srand((unsigned)time(NULL));
    int randMax = (i->height * i->width) / (strlen(text) + 1);

    random = rand() % randMax;

    if (random < 1) {
        random = 1;
    }

    int k = 0;
    int j = random;

    //Red fields swapped with ASCII int
    while (k < strlen(text)) {
        if (str->arr[j].red == text[k]) {
            j = j + 1; // if the values are the same we encode in the next  pixel.
        } else {
            str->arr[j].red = text[k];
            k = k + 1;
            j = j + random;
        }
    }

    return str;
}


/*
   Compares 2 PPM structs and decodes the message hidden within
   Returns the decoded message if the images have the same dimensions
   Returns NULL otherwise
 */
char *decode(PPM *i1, PPM *i2) {
    int i = 0;
    int j = 0;

    char *str = (char*)malloc(sizeof(char));

    while (i < i1->height * i1->width) {
        if (i1->arr[i].red != i2->arr[i].red) {
            str[j] = i2->arr[i].red;
            j = j + 1;
        }
        i = i + 1;
    }

    str = realloc(str, i);

    return str;
    printf("%s", str);
}

/* TODO: Question 3 */
int main(int argc, char *argv[]) {

    /* check arguments */
    switch (argc) {
    case 2:
        /* TODO: not enough arguments, print an error message */
        printf("Not enough arguments. \n");
        break;
    case 3:
        if (strcmp(argv[1], "e") == 0) { //Argument "e" - encode PPM
            PPM *ppm = loadPPMFromFile(argv[2]);
            createPPM(ppm);

            /*Check that PPM is valid; continue if it is, exit otherwise */
            if (ppm != NULL) {
                PPM *encodedPPM;
                /* TODO: encode the ppm with the text and assign to encodedPPM */
                encodedPPM = encode(argv[2], encodedPPM);
                /*Check that no error occured*/
                if (encodedPPM == NULL) {
                    return EXIT_FAILURE;
                } else {
                    /* TODO: print a confirmation that encoding was successful */
                    printf("Encoding successful. \n");
                    /* TODO: print the PPM to standard output with showPPM */
                    showPPM(encodedPPM);
                    return EXIT_SUCCESS;
                }
            } else {
                return EXIT_FAILURE;
            }
        } else {
            printf("Unrecognised or incomplete command line.\n");
            return EXIT_FAILURE;
        }
        break;
    case 4:
        if (strcmp(argv[1], "d") == 0) { //Argument "d" - decode PPM
            PPM *comparisonPPM;
            PPM *encodedPPM;

            /* TODO: get comparison file filename from argv, load it with
               loadPPMFromFile then assign to comparisonPPM */

            comparisonPPM = loadPPMFromFile(argv[2]);
            /* TODO: get encoded file filename from argv, load it with
               loadPPMFromFile then assign to encodedPPM */

            encodedPPM = loadPPMFromFile(argv[2]);
            /*Check both PPMs are valid; continue if so, exit otherwise */
            if (comparisonPPM != NULL && encodedPPM != NULL) {
                char *decodedMsg;

                /* TODO: decode the encodedPPM with the comparisonPPM and assign to decodedMsg */
                decodedMsg = decode(encodedPPM, comparisonPPM);
                /*Check that the message has been decoded*/
                if (decodedMsg != NULL) {
                    /* TODO: print a confirmation message that the message was decoded */

                    printf("Decoded message:\n");

                    /* TODO: print the decoded message */
                    printf("%p",decodedMsg);
                    return EXIT_SUCCESS;
                } else {
                    return EXIT_FAILURE;
                }
            } else {
                return EXIT_FAILURE;
            }
        } else {
            fprintf(stderr, "Unrecognised or incomplete command line.\n\n");
            return EXIT_FAILURE;
        }
        break;
    default:
        fprintf(stderr, "Unrecognised or incomplete command line.\n\n");
        return EXIT_FAILURE;
    }
}
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
beepboop
  • 1
  • 3
  • 1
    This would be a good time to learn to use a debugger and / or a hex editor. With your debugger, set a breakpoint at the affected `if` statement and inspect the contents of `image->format` to see what they actually contain. With a hex editor, examine the first few bytes of the file to see what they actually are. This will allow you to better characterize the nature of the problem, which is the first step to producing a solution. – John Bollinger Feb 08 '21 at 13:52
  • `char c = getc(fd);` - did you mean to read a character there? - it gets overwritten by the `getc` in the `while` loop below it. – 500 - Internal Server Error Feb 08 '21 at 13:53
  • Or to some extent you can substitute good old print-statement debugging for the debugger inspection end. Or even just augment your existing error message to show the actual data observed. But these approaches might try to print non-text. – John Bollinger Feb 08 '21 at 13:56
  • You didn't need to delete your other question. I was about to write an answer to it... – fuz Mar 03 '21 at 13:13
  • 1
    Please don't make more work for other people by vandalizing your posts. By posting on the Stack Exchange network, you've granted a non-revocable right, under the [CC BY-SA 4.0 license](//creativecommons.org/licenses/by-sa/4.0/), for Stack Exchange to distribute that content (i.e. regardless of your future choices). By Stack Exchange policy, the non-vandalized version of the post is the one which is distributed. Thus, any vandalism will be reverted. If you want to know more about deleting a post please see: [How does deleting work?](//meta.stackexchange.com/q/5221) – Adrian Mole Mar 31 '21 at 09:23

1 Answers1

2

"Also in my main(), am I passing in the correct parameters?"

Yes, they are correct.

"not getting past the image format if statement in the getPPM function"

There are several issues with getPPM.
Overwriting c in the statements following this one:

char c = getc(fd);

is already covered in comments.

In the following:

sscanf(fd, "%s", image->format);
if (strcmp(image->format, "P3") != 0)
{

Because sscanf() will pick up the new line if there is one, strcmp() will fail the comparison for "P3". Suggestions are to do 1 or more of the following:

  • running in a debugger, place a break point at the if and view value of image->format
  • place if(image->format != NULL) printf("%s", image->format) after sscanf()
  • if you suspect \n or \r\n may be in the buffer, use image->format[strcspn(image->format, "\r\n")] = 0; to remove them.

Regarding the following, suggest you read the documentation on each C function, eg fgets():

This:

fgets(fd, MAX_LEN, image->format);
scanf(...

Should be

if(fgets(image->format, MAX_LEN, fd) != NULL)
{
    scanf(...

(Argument positions changed, and test before trying to use buffer.)

Aside: Because this is C, following malloc statements

PPM* image = (PPM*)malloc(sizeof(PPM));
image->format = (char*)malloc(MAX_LEN);

are more correctly written as

PPM* image = malloc(sizeof(*image));
image->format = malloc(MAX_LEN);

(Cast is removed from malloc() and note sizeof argument in the first statement. apply as applicable throughout your code.

ryyker
  • 22,849
  • 3
  • 43
  • 87