I am getting Abort 6 error and I have looked it up. I don't see where in my code a strcpy() is used but possibly memcpy() could be the issue? Not sure where and why its happening but it only starting appearing when I implementing the root directory update in the code.
NOTE: I have left out dir.name and will implement later when this other issue resolves.
It prints out the print functions but seems to abort when implementing the root directory code below.
The global file is below:
#include "../sifs.h"
#include "md5.h"
// CONCRETE STRUCTURES AND CONSTANTS USED THROUGHOUT THE SIFS LIBRARY.
// DO NOT CHANGE ANYTHING IN THIS FILE.
#define SIFS_MIN_BLOCKSIZE 1024
#define SIFS_MAX_NAME_LENGTH 32 // including the NULL byte
#define SIFS_MAX_ENTRIES 24 // for both directory and file entries
#define SIFS_UNUSED 'u'
#define SIFS_DIR 'd'
#define SIFS_FILE 'f'
#define SIFS_DATABLOCK 'b'
#define SIFS_ROOTDIR_BLOCKID 0
typedef struct {
size_t blocksize;
uint32_t nblocks;
} SIFS_VOLUME_HEADER;
typedef char SIFS_BIT; // SIFS_UNUSED, SIFS_DIR, ...
typedef uint32_t SIFS_BLOCKID;
// DEFINITION OF EACH DIRECTORY BLOCK - MUST FIT INSIDE A SINGLE BLOCK
typedef struct {
char name[SIFS_MAX_NAME_LENGTH];
time_t modtime; // time last modified <- time()
uint32_t nentries;
struct {
SIFS_BLOCKID blockID; // of the entry's subdirectory or file
uint32_t fileindex; // into a SIFS_FILEBLOCK's filenames[]
} entries[SIFS_MAX_ENTRIES];
} SIFS_DIRBLOCK;
// DEFINITION OF EACH FILE BLOCK - MUST FIT INSIDE A SINGLE BLOCK
typedef struct {
time_t modtime; // time first file added <- time()
size_t length; // length of files' contents in bytes
unsigned char md5[MD5_BYTELEN];
SIFS_BLOCKID firstblockID;
uint32_t nfiles; // n files with identical contents
char filenames[SIFS_MAX_ENTRIES][SIFS_MAX_NAME_LENGTH];
} SIFS_FILEBLOCK;
FILE1.C
#include "sifs-internal.h"
// make a new directory within an existing volume
int SIFS_mkdir(const char *volumename, const char *dirname)
{
FILE *fp = fopen(volumename, "r+");
if(fp != NULL){
//read in the header information
SIFS_VOLUME_HEADER header;
fread(&header,sizeof header,1,fp);
printf("%zu\n", header.blocksize);
printf("%u", header.nblocks);
// Check for vaidty of variables
if (header.nblocks == 0 || header.blocksize < SIFS_MIN_BLOCKSIZE) {
SIFS_errno = SIFS_EINVAL;
return 1;
}
//read in the bitmap information
SIFS_BIT bitmap[header.nblocks];
fseek(fp ,sizeof header, SEEK_SET);
fread(&bitmap, header.nblocks,1,fp);
for (int i = 1; i < header.nblocks; i++){
if (bitmap[i] == SIFS_UNUSED){
bitmap[i] = SIFS_DIR;
// update the root directory
char rootblock[header.blocksize];
SIFS_DIRBLOCK rootdir_block;
fseek(fp, sizeof header + sizeof bitmap, SEEK_SET);
fread(&rootdir_block, header.blocksize,1,fp);
for (int j = 0; j < SIFS_MAX_ENTRIES; j++){
rootdir_block.entries[j].blockID = i;
rootdir_block.nentries += rootdir_block.nentries + 1;
break;
}
//write the new updated root dir into the vol
memset(rootblock, 0, sizeof rootblock);
memcpy(rootblock, &rootdir_block, sizeof(rootdir_block));
fseek(fp ,sizeof header + sizeof bitmap, SEEK_SET);
fwrite(rootblock, sizeof rootblock, 1, fp);
char oneblock[header.blocksize];
time_t now;
SIFS_DIRBLOCK dir;
memset(&dir, 0, sizeof(dir)); // set all to zero
memset(oneblock, 0, sizeof oneblock);
dir.modtime = time(&now);
dir.nentries = 0;
//set the block to 0 and assign memory of dir into block
memcpy(oneblock, &dir, sizeof(dir));
//write the dir data into the volume
fseek(fp ,sizeof header + sizeof bitmap + i * header.blocksize, SEEK_SET);
fwrite(oneblock, sizeof oneblock, 1, fp);
//memset(oneblock, 0, sizeof oneblock);
//Update the bitmap
fseek(fp ,sizeof header, SEEK_SET);
fwrite(bitmap, sizeof bitmap, 1, fp);
fclose(fp);
return 0;
}
}
}
SIFS_errno = SIFS_ENOTYET;
return 1;
}
Related
I'm using the ar.h's structure : struct ar_hdr to retrieve informations inside my archive file (lib.a) using read to iterate over it, and i'm running into a little problem if the file is truncated.
In using the C language and when the file was truncated it currently makes me get a segmentation fault.
Is there any way to check if the file is truncated beforehand ? like by using stat or stuff like that ?
Thanks in advance
PS: be free to tell me if my question wasn't really understandable and clear
#define SIZE atoi(ar->ar_size)
struct ar_hdr *get_header(int fd)
{
struct ar_hdr *ar = (struct ar_hdr *)malloc(sizeof(struct ar_hdr));
if (read(fd, ar, sizeof(struct ar_hdr)) != sizeof(struct ar_hdr)) {
free(ar);
return NULL;
}
return ar;
}
int handle_ar_files(int fd, char *names[2], int ret)
{
struct ar_hdr *ar = NULL;
void *buf = NULL;
int index = 0;
while ((ar = get_header(fd)) != NULL) {
index = 0;
buf = malloc(SIZE);
if (ar->ar_name[0] == '/') {
my_free(ar, buf, (int [2]){fd, SIZE}, 1);
continue;
}
for (; ar->ar_name[index] && ar->ar_name[index] != '/'; index++);
ar->ar_name[index] = 0;
if ((read(fd, buf, SIZE)) != SIZE)
return my_free(ar, buf, (int [2]){fd, SIZE}, 0);
if ((parse_ar(buf, SIZE, (char *[2]){names[1], ar->ar_name})) == 84)
return my_free(ar, buf, (int [2]){fd, SIZE}, 0);
else
my_free(ar, buf, (int [2]){fd, SIZE}, 0);
}
return ret;
}
void *buf32 = mmap(NULL, st->st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (strncmp((char *)buf32, ARMAG, SARMAG) == 0) {
read(fd, tmp, SARMAG);
return handle_ar_files(fd, names, 0);
}
#ifndef _AR_H
#define _AR_H 1
#include <sys/cdefs.h>
/* Archive files start with the ARMAG identifying string. Then follows a
`struct ar_hdr', and as many bytes of member file data as its `ar_size'
member indicates, for each member file. */
#define ARMAG "!<arch>\n" /* String that begins an archive file. */
#define SARMAG 8 /* Size of that string. */
#define ARFMAG "`\n" /* String in ar_fmag at end of each header. */
__BEGIN_DECLS
struct ar_hdr
{
char ar_name[16]; /* Member file name, sometimes / terminated. */
char ar_date[12]; /* File date, decimal seconds since Epoch. */
char ar_uid[6], ar_gid[6]; /* User and group IDs, in ASCII decimal. */
char ar_mode[8]; /* File mode, in ASCII octal. */
char ar_size[10]; /* File size, in ASCII decimal. */
char ar_fmag[2]; /* Always contains ARFMAG. */
};
__END_DECLS
#endif /* ar.h */
I have a problem with reading pixels from bmp file. It might be something with padding at the end of row or the base64 padding. I have no clue. I've been struggling with this for some days and can't move on because the next task requires this one to be solved.
I only share important parts of the code, since reading bmp header worked fine (tests had 0 failures).
bmp.c
struct pixel* read_data(FILE* stream, const struct bmp_header* header){
if(stream == NULL || header == NULL){
return 0;
}
// w == 1 && p == 1; w == 2 && p == 2; w == 3 && p == 3; w == 4 && p == 0
int padding = header->width % 4;
int num_of_pixels = header->width * header->height;
struct pixel* Pixel[num_of_pixels];
fseek(stream, 54, SEEK_SET); //move 54B (header size)
int index_p = 0;
for(int i = 0; i < header->height; i++){
for(int j = 0; j < header->width; j++){
Pixel[index_p] = malloc(sizeof(struct pixel));
fread(&(Pixel[index_p]->blue), 1, 1, stream);
fread(&(Pixel[index_p]->green), 1, 1, stream);
fread(&(Pixel[index_p]->red), 1, 1, stream);
index_p++;
}
fseek(stream, padding, SEEK_CUR); //padding at the end of row
}
return *Pixel;
}
bmp.h
struct pixel {
uint8_t blue;
uint8_t green;
uint8_t red;
//uint8_t alpha;
} __attribute__((__packed__));
/**
* Read the pixels
*
* Reads the data (pixels) from stream representing the image. If the stream
* is not open or header is not provided, returns `NULL`.
*
* #param stream opened stream, where the image data are located
* #param header the BMP header structure
* #return the pixels of the image or `NULL` if stream or header are broken
*/
struct pixel* read_data(FILE* stream, const struct bmp_header* header);
header if needed (basically we use only 24bit color)
struct bmp_header{
uint16_t type; // "BM" (0x42, 0x4D)
uint32_t size; // file size
uint16_t reserved1; // not used (0)
uint16_t reserved2; // not used (0)
uint32_t offset; // offset to image data (54B)
uint32_t dib_size; // DIB header size (40B)
uint32_t width; // width in pixels
uint32_t height; // height in pixels
uint16_t planes; // 1
uint16_t bpp; // bits per pixel (24)
uint32_t compression; // compression type (0/1/2) 0
uint32_t image_size; // size of picture in bytes, 0
uint32_t x_ppm; // X Pixels per meter (0)
uint32_t y_ppm; // X Pixels per meter (0)
uint32_t num_colors; // number of colors (0)
uint32_t important_colors; // important colors (0)
} __attribute__((__packed__));
main.c I do not need to assign any variables to called functions because we have a program for testing this, I just have to call them in main
int main(){
struct bmp_header* header;
FILE *stream = fopen("./assets/square.2x3.bmp", "rb");
header = read_bmp_header(stream);
read_data(stream, header);
read_bmp(stream);
struct bmp_image* image;
image = malloc(sizeof(struct bmp_image));
free_bmp_image(image);
fclose(stream);
return 0;
}
testing (there are more tests, but this should be enough)
1:
FILE* stream = "Qk0+AAAAAAAAADYAAAAoAAAAAgAAAAEAAAABABgAAAAAAAgAAAAjLgAAIy4AAAAAAAAAAAAA/wAAAP8AAAA="; // base64 encoded stream
struct bmp_header* header = read_bmp_header(stream);
fseek(stream, offset, SEEK_SET);
Assertion 'read_data(stream, header) == "/wAAAP8A"' failed. [got "/wAAFctV"]
2:
FILE* stream = "Qk1GAAAAAAAAADYAAAAoAAAAAgAAAAIAAAABABgAAAAAABAAAAAjLgAAIy4AAAAAAAAAAAAA/wAAAAAAAAAAAP8A/wAAAA=="; // base64 encoded stream
struct bmp_header* header = read_bmp_header(stream);
fseek(stream, offset, SEEK_SET);
Assertion 'read_data(stream, header) == "/wAAAAAAAAD/AP8A"' failed. [got "/wAAAAAAAAAAAAAA"]
So after the "==" is expected result and in the brackets is my the result from my code. As I mentioned, it might be something with padding, since it starts well but doesn't end well.
Thanks for help.
Short Answer: Set padding to (4-((3*width)%4))%4
Long answer:
Your code included:
int padding = header->width % 4;
//Some lines of code
fseek(stream, padding, SEEK_CUR);
In a bitmap, padding is added until each row is a multiple of 4 bytes. You took padding as width % 4.
First off, each pixel takes 3 bytes(for rgb). So it should be (3*width)%4. Next, we need to subtract it from 4 bytes (Since padding is 4-pixels occupied). So padding would be 4-((3*width)%4). Another small modification, if (3*width)%4==0 then padding would come to be 4 (whereas, we expect it to be 0). So we take another mod4 just to be sure
So padding would come out to be (4-((3*width)%4))%4
EDIT:
As pointed out by user Craig Estey in the comments, its better to use sizeof(struct pixel) instead of 3
The following code is part of my program that tries to extract image features from a bitmap file. I need to extract information (width and height only) about the image and also make copies of it. These images are of 2048X 2168 resolution, 8-bit greyscale.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NAMELENGTH 301
long getImageInfo(FILE* Finput, long offset, int nchars){
unsigned char *ptrChar;
unsigned char dummy;
long value=0L;
int i;
dummy='0';
ptrChar=&dummy;
fseek(Finput,offset,SEEK_SET);
for (i=1; i<=nchars; i++){
fread(ptrChar,sizeof(char),1,Finput);
value += ((long) ((*ptrChar)*pow(256,(i-1))));
}
return(value);
}
void copyImageInfo(FILE* Finput, FILE* Foutput){
unsigned char *ptrChar;
unsigned char dummy;
long offset;
int i;
dummy='0';
ptrChar=&dummy;
int byte_size = ((int) getImageInfo(Finput,34,4));
/* copying header: signature, image width & height, number bit/pixel, image size, number of colors */
offset=0L;
fseek(Finput,offset,SEEK_SET);
fseek(Foutput,offset,SEEK_SET);
for (i=0; i<=54; i++){
fread(ptrChar,sizeof(char),1,Finput);
fwrite(ptrChar,sizeof(char),1,Foutput);
}
/* copying pixel data */
/* This part of the code may not be complete */
offset=54L;
fseek(Finput,offset,SEEK_SET);
fseek(Foutput,offset,SEEK_SET);
for (i=0; i<=byte_size; i++){
fread(ptrChar,sizeof(char),1,Finput);
fwrite(ptrChar,sizeof(char),1,Foutput);
}
}
int main(int argc, char *argv[]){
FILE* Finput;
FILE* Foutput;
char input_name[NAMELENGTH];
char output_name[NAMELENGTH];
char job_name[NAMELENGTH];
char history_name[NAMELENGTH];
unsigned char *ptrChar;
int pix_width,pix_height;
int bit_pixel,byte_size,ncolors;
strcpy(input_name,argv[1]); /* first argument contains path to the image file */
strcpy(job_name, argv[1]);
job_name[strlen(job_name)-4]='\0';
sprintf(history_name,"%s%s",job_name,"_hist.txt"); /* history file stores image header information */
if( (Finput=fopen(input_name,"r"))==NULL ){
fprintf(stderr,"\n ERROR: file %s could not be opened for reading\n",input_name);
exit(-1);
}
else{
fseek(Finput,0L,SEEK_END);
if (getImageInfo(Finput,0,2)!=19778) fprintf(stdout,"\n WARNING: wrong BMP signature!\n");
pix_width = ((int) getImageInfo(Finput,18,4));
pix_height = ((int) getImageInfo(Finput,22,4));
bit_pixel = ((int) getImageInfo(Finput,28,2));
byte_size = ((int) getImageInfo(Finput,34,4));
ncolors = ((int) getImageInfo(Finput,46,4));
fprintf(stdout,"\n width pixels=%d",pix_width);
fprintf(stdout,"\n height pixels=%d",pix_height);
fprintf(stdout,"\n bits per pixel=%d",bit_pixel);
fprintf(stdout,"\n image data size=%d",byte_size);
fprintf(stdout,"\n number colors=%d\n",ncolors);
/* history file */
if ( (Foutput=fopen(history_name,"a"))==NULL ){
fprintf(stderr,"\n ERROR: file %s could not be opened for appending\n",history_name);
exit(-1);
}
else{
fprintf(Foutput,"Path to Image: %s ",input_name);
fprintf(Foutput,"\n\t 18 - biWidth - width pixels=%d",pix_width);
fprintf(Foutput,"\n\t 22 - biHeight - height pixels=%d",pix_height);
fprintf(Foutput,"\n\t 28 - biBitCount - bits per pixel=%d",bit_pixel);
fprintf(Foutput,"\n\t 34 - biSizeImage - image data size=%d",byte_size);
fprintf(Foutput,"\n\t 46 - biClrUsed - number colors=%d\n",ncolors);
fclose(Foutput);
}
sprintf(output_name,"%s%s",job_name,"_copy.bmp");
if ( (Foutput=fopen(output_name,"wb"))==NULL ){
fprintf(stderr,"\n ERROR: file %s could not be opened for writing\n",output_name);
exit(-1);
}
else{
copyImageInfo(Finput,Foutput);
fclose(Foutput);
}
}
fclose(Finput);
}
Unfortunately, my original image files are in tif format. So when I convert them, I am unable to preserve some of the header file information. One online tool lets me preserve the bits-per-pixel (8 bit) but then I lose the biSizeImage (which turns to 0). Another conversion tool gets me the size correctly but image bitrate changes to 24. (link1, link2)
An example of my original tif image (temporary_link1) and the corresponding BMP image (temporary_link2)
When I run the above, I am able to copy the header information correctly but not the pixel data. If this can be copied using some other method (like for example by comparing the EOF), it may be a good alternative. I am unsure about calculating the padding and also the direction of writing.
Any guidance would be appreciated starting with the correct conversion of tif format to my required bmp format. Evidently I am new to image formatting and compression.
Output:
width pixels=2048
height pixels=2168
bits per pixel=8
image data size=0
number colors=0
getImageInfo is incorrect. The integer values are supposed to be saved in little-endian format. It should be read as follows:
unsigned int getImageInfo(FILE *fin, long offset, int nchars)
{
fseek(fin, offset, SEEK_SET);
unsigned int value = 0;
for(int i = 0; i < nchars; i++)
{
unsigned char dummy = '0';
fread((char*)&dummy, sizeof(char), 1, fin);
value += dummy << (8 * i);
}
return value;
}
byte_size is not guaranteed to be set to the correct value. This should be roughly equal to width * height * bit_pixel. Use this formula.
byte_size = ((pix_width * bit_pixel + 31) / 32) * 4 * pix_height;
Moreover, 8-bit image includes color table of size 1024. This table is immediately after 54 byte header, and before the pixel data. Read it as follows:
unsigned int getImageInfo(FILE *fin, long offset, int nchars)
{
fseek(fin, offset, SEEK_SET);
unsigned int value = 0;
for(int i = 0; i < nchars; i++)
{
unsigned char dummy = '0';
fread((char*)&dummy, sizeof(char), 1, fin);
value += dummy << (8 * i);
}
return value;
}
void copyImageInfo(FILE* Finput, FILE* Foutput)
{
int w = getImageInfo(Finput, 18, 4);
int h = getImageInfo(Finput, 22, 4);
int bit_pixel = getImageInfo(Finput, 28, 2);
int byte_size = ((w * bit_pixel + 31) / 32) * 4 * h;
int ncolors = getImageInfo(Finput, 46, 4);
fprintf(stdout, "\n width pixels=%d", w);
fprintf(stdout, "\n height pixels=%d", h);
fprintf(stdout, "\n bits per pixel=%d", bit_pixel);
fprintf(stdout, "\n image data size=%d", byte_size);
fprintf(stdout, "\n number colors=%d\n", ncolors);
char header[54]; //bitmap header
char *pixels = malloc(byte_size); //pixel data
fseek(Finput, 0, SEEK_SET);
fseek(Foutput, 0, SEEK_SET);
fread(header, sizeof(header), 1, Finput);
fwrite(header, sizeof(header), 1, Foutput);
if(bit_pixel <= 8)
{
//color table
int colors_size = 4 * (1 << bit_pixel);
char *colors = malloc(colors_size);
fread(colors, 1, colors_size, Finput);
fwrite(colors, 1, colors_size, Foutput);
free(colors);
}
fread(pixels, 1, byte_size, Finput);
fwrite(pixels, 1, byte_size, Foutput);
free(pixels);
}
int main(void)
{
char input_name[] = "input.bmp";
char output_name[] = "output.bmp";
FILE *Finput = fopen(input_name, "rb");
if(!Finput)
{
fprintf(stderr, "\n ERROR: file %s\n", input_name);
exit(-1);
}
FILE *Foutput = fopen(output_name, "wb");
if(!Foutput)
{
fprintf(stderr, "\n ERROR: file %s\n", input_name);
fclose(Finput);
exit(-1);
}
if(getImageInfo(Finput, 0, 2) != 19778)
fprintf(stdout, "\n WARNING: wrong BMP signature!\n");
else
copyImageInfo(Finput, Foutput);
fclose(Foutput);
fclose(Finput);
return 0;
}
I submitted this problem set, but I'm unable to get a full mark grade because the exit code "is expected to be 0 and not a 1". However, if you take a look at the code (the recover.c file), the exit code is 0. What is wrong? The program accomplishes everything it was made for, which is to read through bits in a corrupted file and find the bits that make up a JPG file and write them in a separate file. The only problem I'm having is this aforementioned exit code issue. Please help!
recover.c file
#include <stdio.h>
#include <cs50.h>
#include <stdlib.h>
#include "bmp.h"
int main (void){
FILE* card_ptr = fopen("card.raw","r");
if (card_ptr == NULL){
fprintf(stderr,"File Not Found!");
return 1;
}
BYTE buffer[512];
bool found_jpg = false;
FILE* new_jpg_ptr;
int file_counter = 0;
while(fread(buffer,1,512,card_ptr)!=0x00){
if(buffer[0]== 0xff && buffer[1]== 0xd8 && buffer[2]==0xff && (buffer[3] & 0xf0)== 0xe0){
if(!found_jpg){
char filename[8];
sprintf(filename, "%03i.jpg", file_counter++);
found_jpg = true;
new_jpg_ptr = fopen(filename,"w");
if(new_jpg_ptr == NULL){
return 2;
}
fwrite(buffer,1,512,new_jpg_ptr);
}
else {
fclose(new_jpg_ptr);
char filename[8];
sprintf(filename, "%03i.jpg", file_counter++);
found_jpg = true;
new_jpg_ptr = fopen(filename,"w");
if(new_jpg_ptr == NULL){
return 3;
}
fwrite(buffer,1,512, new_jpg_ptr);
}
}
else {
if(found_jpg){
fwrite(buffer,1,512, new_jpg_ptr);
}
}
}
fclose(new_jpg_ptr);
fclose(card_ptr);
return 0;
}
bmp.h file
/**
* BMP-related data types based on Microsoft's own.
*/
#include <stdint.h>
/**
* Common Data Types
*
* The data types in this section are essentially aliases for C/C++
* primitive data types.
*
* Adapted from https://msdn.microsoft.com/en-us/library/cc230309.aspx.
* See http://en.wikipedia.org/wiki/Stdint.h for more on stdint.h.
*/
typedef uint8_t BYTE;
typedef uint32_t DWORD;
typedef int32_t LONG;
typedef uint16_t WORD;
/**
* BITMAPFILEHEADER
*
* The BITMAPFILEHEADER structure contains information about the type, size,
* and layout of a file that contains a DIB [device-independent bitmap].
*
* Adapted from https://msdn.microsoft.com/en-us/library/dd183374(v=vs.85).aspx.
*/
typedef struct
{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} __attribute__((__packed__))
BITMAPFILEHEADER;
/**
* BITMAPINFOHEADER
*
* The BITMAPINFOHEADER structure contains information about the
* dimensions and color format of a DIB [device-independent bitmap].
*
* Adapted from https://msdn.microsoft.com/en-us/library/dd183376(v=vs.85).aspx.
*/
typedef struct
{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} __attribute__((__packed__))
BITMAPINFOHEADER;
/**
* RGBTRIPLE
*
* This structure describes a color consisting of relative intensities of
* red, green, and blue.
*
* Adapted from https://msdn.microsoft.com/en-us/library/dd162939(v=vs.85).aspx.
*/
typedef struct
{
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;
} __attribute__((__packed__))
RGBTRIPLE;
I experienced the same problem. My program worked as expected with no obvious reasons for a return code of 1. As you have, I had also included a header file with some definitions in it. Placing those definitions in the core file solved my problem. Perhaps if you move the needed definitions from your bmp.h file into recover.c, check50 will be able to compile.
My solution if you're interested.
// Recover any forgotten JPEGS from a given forensic image
# include <stdio.h>
# include <stdbool.h>
# include <stdint.h>
typedef uint8_t BYTE;
typedef struct
{
BYTE byte_1;
BYTE byte_2;
BYTE byte_3;
BYTE byte_4_min;
BYTE byte_4_max;
} __attribute__((__packed__))
SIG;
// declare helper function prototypes
int check_args(int argc, char *argv[]);
bool signature_is_present(BYTE potential_sig[4], SIG jpeg_sig);
SIG set_jpeg_signature(void);
// main program
int main (int argc, char *argv[])
{
// check input for validity
int check_args_value = check_args(argc, argv);
if (check_args_value != 0)
{
return check_args_value;
}
// call function to set default jpeg signature
SIG jpeg_sig = set_jpeg_signature();
// open buffers and declare other necessary variables for later use
BYTE FAT_block[512];
BYTE first_byte[1];
int signature_offset = -4;
int counter = 0;
// open forensic image
FILE * inptr = fopen(argv[1], "r");
FILE * outptr = NULL;
// loop through file searching for first '255'
while (fread(first_byte, sizeof(BYTE), 1, inptr) == 1)
{
// check if byte is 255, the first byte of a jpeg signature
if (*first_byte == jpeg_sig.byte_1)
{
// check for signature
BYTE possible_sig[4];
// back pointer up one byte to compensate for already discovered 255
fseek(inptr, -1, SEEK_CUR);
// read four bytes from file that could potentially be a signature.
fread(possible_sig, sizeof(possible_sig), 1, inptr);
if (signature_is_present(possible_sig, jpeg_sig))
{
// close previously open write file, if any.
if (outptr != NULL)
{
fclose(outptr);
//Increment counter for image signatures found.
counter++;
}
// setup name for image file
char file_name[8] = {};
snprintf(file_name, 8, "%.3i.jpg\n", counter);
// move pointer back 4 bytes after checking what they contain
fseek(inptr, signature_offset, SEEK_CUR);
//read FAT block from forensic image into buffer
fread(FAT_block, sizeof(FAT_block), 1, inptr);
// open new output file based on current signature
outptr = fopen(file_name, "w");
// write FAT block buffer to file
fwrite(FAT_block, sizeof(FAT_block), 1, outptr);
}
else // byte is 255 but not part of a signature
{
if (outptr != NULL)
{
// move pointer back 4 bytes after checking what they contain
fseek(inptr, signature_offset, SEEK_CUR);
//read FAT block from forensic image into buffer
fread(FAT_block, sizeof(FAT_block), 1, inptr);
// write FAT block buffer to file
fwrite(FAT_block, sizeof(FAT_block), 1, outptr);
}
}
}
else // if byte is not 255
{
if (outptr != NULL)
{
//back up one byte
fseek(inptr, -1, SEEK_CUR);
//read FAT block from forensic image into buffer
fread(FAT_block, sizeof(FAT_block), 1, inptr);
// write FAT block buffer to file
fwrite(FAT_block, sizeof(FAT_block), 1, outptr);
}
}
}
// close files
fclose(inptr);
if (outptr != NULL)
{
fclose(outptr);
}
return 0;
}
// helper functions
int check_args(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Invalid Input.\nForensic image file must be provided.\n");
return 1;
}
FILE *inptr = fopen(argv[1], "r");
if (inptr == NULL)
{
fprintf(stderr, "File does not exist.\n");
return 2;
}
fclose(inptr);
return 0;
}
bool signature_is_present(BYTE potential_sig[4], SIG jpeg_sig)
{
if( potential_sig[0] == jpeg_sig.byte_1 &&
potential_sig[1] == jpeg_sig.byte_2 &&
potential_sig[2] == jpeg_sig.byte_3 &&
potential_sig[3] >= jpeg_sig.byte_4_min &&
potential_sig[3] <= jpeg_sig.byte_4_max )
{
return true;
}
return false;
}
SIG set_jpeg_signature(void)
{
SIG jpeg_sig;
jpeg_sig.byte_1 = 255;
jpeg_sig.byte_2 = 216;
jpeg_sig.byte_3 = 255;
jpeg_sig.byte_4_min = 224;
jpeg_sig.byte_4_max = 239;
return jpeg_sig;
}
Well basically I wrote this program for my computer course (shoutout CS50) that recovers images from a .raw file. I have managed to have the program recover 48 of the 50 files in that file.
The issue im having right now with the program is that the program cannot recover both the first and the second file located on .raw. It either reads and writes the very first file (this girl in a snowy background) or the second file on the .raw (guy behind books).
For some reason if I change fopen from write to append I can switch between the photo of the girl and the guy, but I cant seem to be able to open both.
https://github.com/CoreData/cs50/blob/master/pset4/jpg/card.raw
This is the link to card.raw, unfortunately its not the same one that Im using but even using this one you get two different images for image1.jpg depending on whether you have fopen with an "a" or "w".
Any ideas???
if you guys want any additional info just let me know
#include <stdio.h>
#include <stdlib.h>
#include "bmp2.h"
int main(void)
{
/*OPEN CARD FILE*/
char* infile = "card.raw";;
FILE* card = fopen(infile, "r");
if (card == NULL)
{
printf("Could not open %s.\n", "card.raw");
return 2;
}
int f = 0, c = 0, l = 0, x = 128, imageno = 1;
// c signals that a jpg is being written
// l size control, 0 means 0 jpgs
FILE* images;
char* title = (char*)malloc(15);
/*repeat until end of card*/
do
{
//read one block into buffer
INTROJPG *buffer = (INTROJPG*)malloc(sizeof(INTROJPG)*x);
for (int i = 0; i < 128; i++)
{
fread(&buffer[i], sizeof(INTROJPG), 1, card);
}
if (buffer[0].first == 0xff && buffer[0].second == 0xd8 && buffer[0].third == 0xff)
{
sprintf(title, "image%d.jpg", imageno); //change jpg title
if (f == 1) //close previous jpg
{
fclose(images);
imageno++;
}
images = fopen(title, "w");
f = 1; //very first jpg has been opened
c = 1; //jpg open
l++; //jpg count + 1
}
//jpg already open?
if (c == 1)
{
for (int i = 0; i < 128; i++)
{
fwrite(&buffer[i], sizeof(INTROJPG), 1, images);
}
}
free(buffer);
}
while (l < 50);
free(title);
return 5;
//close any remaining files
}
and this is my bmp2.h file
#include <stdint.h>
/**
* Common Data Types
*
* The data types in this section are essentially aliases for C/C++
* primitive data types.
*
* Adapted from http://msdn.microsoft.com/en-us/library/cc230309.aspx.
* See http://en.wikipedia.org/wiki/Stdint.h for more on stdint.h.
*/
typedef uint8_t BYTE;
typedef uint32_t DWORD;
typedef int32_t LONG;
typedef uint16_t WORD;
/**
* BITMAPFILEHEADER
*
* The BITMAPFILEHEADER structure contains information about the type, size,
* and layout of a file that contains a DIB [device-independent bitmap].
*
* Adapted from http://msdn.microsoft.com/en-us/library/dd183374(VS.85).aspx.
*/
typedef struct
{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} __attribute__((__packed__))
BITMAPFILEHEADER;
/**
* BITMAPINFOHEADER
*
* The BITMAPINFOHEADER structure contains information about the
* dimensions and color format of a DIB [device-independent bitmap].
*
* Adapted from http://msdn.microsoft.com/en-us/library/dd183376(VS.85).aspx.
*/
typedef struct
{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} __attribute__((__packed__))
BITMAPINFOHEADER;
/**
* RGBTRIPLE
*
* This structure describes a color consisting of relative intensities of
* red, green, and blue.
*
* Adapted from http://msdn.microsoft.com/en-us/library/aa922590.aspx.
*/
typedef struct
{
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;
} __attribute__((__packed__))
RGBTRIPLE;
typedef struct
{
BYTE first;
BYTE second;
BYTE third;
BYTE fourth;
} __attribute__((__packed__))
INTROJPG;
typedef struct
{
BYTE image;
}
BYTEIMAGE;
First things first, I'll try to improve a few things in your code. I've also done this pset and it is nice to help others.
INTROJPG *buffer = (INTROJPG*)malloc(sizeof(INTROJPG)*x);
At this part, you know that the size of both INTROJPG and x are constant, so there is no need to constantly allocate and free memory at every iteration, that takes much more time than simply creating a normal array. Also, why is the buffer a pointer to INTROJPG? If it is only to test for a header at each iteration, I don't think it is worth it, you could simply access the first 4 bytes of a normal BYTE array.
I'd create a static array of 512 BYTEs (the struct on the library), because this is the size you are constantly allocating and freeing and also you are using BYTEs, not INTROJPGs.
Second, at this section and another similar one:
for (int i = 0; i < 128; i++)
{
fread(&buffer[i], sizeof(INTROJPG), 1, card);
}
There is absolutely no need for this loop or, again, even using INTROJPG. You are always reading and writing 512 bytes, you could use:
fread(buffer, 4, 128, card);
// or even better
fread(buffer, 512, 1, card);
Now about your problem, I've tested your code (without any modifications) multiple times and found nothing wrong with image1.jpg and image2.jpg. Yes, I changed "w" mode to "a" and vice-versa.
However, your code is faulty in regard to the last image, your last image is image49.jpg, when it should be image50.jpg, and your image49.jpg does not even open, and that's because the loop is finished before the rest of image49.jpg is stored, i.e., you are storing only the first 512 bytes of image49.jpg.
To fix that, I've changed the condition of the do-while loop to keep going until the end of the card file, IIRC the problem guarantees the last block being part of the last image or something like that, if not, it's up to you to fix this little problem!
#include <stdio.h>
#include <stdlib.h>
#include "bmp2.h"
int main(void)
{
/*OPEN CARD FILE*/
char* infile = "card.raw";;
FILE* card = fopen(infile, "r");
if (card == NULL)
{
printf("Could not open %s.\n", "card.raw");
return 2;
}
int f = 0, c = 0, imageno = 1;
// c signals that a jpg is being written
// l size control, 0 means 0 jpgs
FILE* images;
char title[25];
BYTE buffer[512];
/*repeat until end of card*/
do
{
fread(buffer, 512, 1, card);
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff)
{
sprintf(title, "image%d.jpg", imageno); //change jpg title
if (f == 1) //close previous jpg
{
fclose(images);
imageno++;
}
images = fopen(title, "w");
f = 1; //very first jpg has been opened
c = 1; //jpg open
}
//jpg already open?
if (c == 1) fwrite(buffer, 512, 1, images);
}
while (!feof(card));
return 5;
//close any remaining files
}
One last thing, why are you returning 5 at the end of the program? Just curious.