libjpeg decompress to RAW not working - c

I am on a RHEL 6.0 x86_64 box with the following version of libjpeg.
[mehoggan#hogganz400 jpeg_to_raw.c]$ rpm -qa libjpeg
libjpeg-6b-46.el6.x86_64
I have the following code which takes as its input a .jpeg file, and writes out a .raw file. When I run the program the size of the file expands, which leads me to believe the program is working:
[mehoggan#hogganz400 jpeg_to_raw.c]$ ls -l
total 600
-rwxrwxr-x 1 mehoggan mehoggan 10113 Dec 1 10:32 jpeg_to_raw
-rw-rw-r-- 1 mehoggan mehoggan 3311 Dec 1 10:32 jpeg_to_raw.c
-rw-rw-r-- 1 mehoggan mehoggan 75 Dec 1 10:27 Makefile
-rw-rw-r-- 1 mehoggan mehoggan 215205 Dec 1 09:19 test.jpg
-rw-rw-r-- 1 mehoggan mehoggan 374850 Dec 1 10:32 test_out.raw
However when I open up the file using Irfanview (and associated plugins) only a small portion of my image opens up. The code can be found below:
#include <stdio.h>
#include <jpeglib.h>
#include <stdlib.h>
#include <unistd.h>
/* we will be using this uninitialized pointer later to store raw, uncompressd image */
unsigned char *raw_image = NULL;
unsigned int size;
/**
* print the information for what was stored in the JPEG File
**/
void print_jpeg_info(struct jpeg_decompress_struct cinfo)
{
printf("JPEG File Information: \n");
printf("Image width and height: %d pixels and %d pixels.\n", cinfo.image_width, cinfo.image_height);
printf("Color components per pixel: %d.\n", cinfo.num_components);
printf("Color space: %d.\n", cinfo.jpeg_color_space);
printf("Raw flag is: %d.\n", cinfo.raw_data_out);
}
/**
* read_jpeg_file Reads from a jpeg file on disk specified by filename and saves into the
* raw_image buffer in an uncompressed format.
*
* \returns positive integer if successful, -1 otherwise
* \param *filename char string specifying the file name to read from
**/
int read_jpeg_file(char *filename)
{
/* these are standard libjpeg structures for reading(decompression) */
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
/* libjpeg data structure for storing one row, that is, scanline of an image */
JSAMPROW row_pointer[1];
FILE *infile = fopen(filename, "rb");
unsigned long location = 0;
int i = 0;
if (!infile) {
printf("Error opening jpeg file %s\n!", filename);
return -1;
}
/* here we set up the standard libjpeg error handler */
cinfo.err = jpeg_std_error(&jerr);
/* setup decompression process and source, then read JPEG header */
jpeg_create_decompress(&cinfo);
/* this makes the library read from infile */
jpeg_stdio_src(&cinfo, infile);
/* reading the image header which contains image information */
jpeg_read_header(&cinfo, TRUE);
print_jpeg_info(cinfo);
jpeg_start_decompress(&cinfo);
/* allocate memory to hold the uncompressed image */
size = cinfo.output_width*cinfo.output_height*cinfo.num_components;
raw_image = (unsigned char*)malloc(size);
/* now actually read the jpeg into the raw buffer */
row_pointer[0] = (unsigned char *)malloc(cinfo.output_width*cinfo.num_components);
/* read one scan line at a time */
while (cinfo.output_scanline < cinfo.image_height) {
jpeg_read_scanlines( &cinfo, row_pointer, 1 );
for (i=0; i<cinfo.image_width*cinfo.num_components;i++) {
raw_image[location++] = row_pointer[0][i];
}
}
/* wrap up decompression, destroy objects, free pointers and close open files */
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
free(row_pointer[0]);
fclose(infile);
/* yup, we succeeded! */
return 1;
}
int main(int argc, char *argv[])
{
char *infilename = "test.jpg";
if (read_jpeg_file(infilename) > 0) {
size_t count = size / sizeof(unsigned char*);
fprintf(stdout, "The number of unsigned chars in raw_image = %d\n", (int)count);
FILE *ofile = fopen("test_out.raw", "w+");
ssize_t data_out = fwrite(raw_image, count, sizeof(unsigned char), ofile);
fprintf(stdout, "%d", (int)data_out);
fclose(ofile);
}
else
return -1;
return 0;
}
What is your take on why the program is not writing out all the data? Or why is it possibly corrupting the data?
The makefile used to build this simple app is:
jpeg_to_raw : jpeg_to_raw.c
gcc jpeg_to_raw.c -Wall -o jpeg_to_raw -ljpeg

size_t count = size / sizeof(unsigned char*);
gets you only 1/4 of the raw-data written (actually, in 64 bit mode only 1/8th).
count should be same as size (counting chars, not pointers)

Related

Write long to linux char device driver?

I'm trying to write a character device driver in linux. Unfortunately it's not working for any numbers greater than 255.
I want this driver specifically to work with value of type long. Anytime I input a value greater than 255, the numbers wrong. 256 goes to 0 etc.
I've written a simple character device driver that shows the problem, there might be a lot of unused include statements as I copied my full driver and deleted almost everything:
chartest.c
#include <linux/init.h>
#include <linux/module.h> /* I mean this is a module after all! */
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/sched.h> /* For current task information */
#include <linux/fs.h> /* For file operations */
#include <linux/types.h> /* dev_t: device number data type */
#include <linux/cdev.h> /* cdev is the module data type that the kernel sees */
#include <asm/uaccess.h> /* For routines to copy data to/from user space */
#include <linux/uaccess.h>
#include <linux/slab.h> /* kmalloc/kfree */
MODULE_LICENSE("GPL");
#define DRIVER_NAME "chartest"
#define MAJOR_NUM 230
#define MINOR_NUM 0
struct cdev *cdev;
int test_device_open(struct inode *inode, struct file *fp) {
return 0;
}
int test_device_release(struct inode *inode, struct file *fp) {
return 0;
}
ssize_t test_read(struct file *fp, char __user *buffer, size_t count, loff_t *f_pos) {
return count;
}
ssize_t test_write(struct file *fp, const char __user *buffer, size_t count, loff_t *fpos) {
// We must validate the user's buffer and convert it to a long long
long userOperand;
unsigned char *userInput = NULL;
printk(KERN_NOTICE "Write Function Entered.\n");
printk(KERN_ALERT "Write count: %ld, Write fp: %lld\n", count, *fpos);
userInput = kmalloc(count, GFP_KERNEL);
get_user(*userInput, buffer);
printk(KERN_NOTICE "Value before cast: %ld\n", (long) *userInput);
userOperand = (long) *userInput;
printk(KERN_NOTICE "Value after cast: %ld\n", userOperand);
// Increment the file position pointer (in our case, always by 8)
*fpos += count;
kfree(userInput);
return count;
}
/*
* Declaration of function for open file operations
*/
static struct file_operations test_fops = {
.owner = THIS_MODULE,
.read = test_read,
.write = test_write,
.open = test_device_open,
.release = test_device_release,
};
// Initialization function
static int __init test_init(void)
{
// Register device number:
int err = 0;
dev_t device_number = MKDEV(MAJOR_NUM, MINOR_NUM);
err = register_chrdev_region(device_number, 1, DRIVER_NAME);
if (err < 0) {
printk(KERN_ALERT "Could not allocate device number.\n");
return err;
}
cdev = cdev_alloc();
cdev->owner = THIS_MODULE;
cdev->ops = &test_fops;
err = cdev_add(cdev, device_number, 1);
if (err) {
printk("Error allocating cdev.\n");
}
printk(KERN_ALERT "Test Initialized. Major Number: %d\n", MAJOR_NUM);
return 0;
}
// Exit function:
static void __exit test_exit(void)
{
dev_t device_number = MKDEV(MAJOR_NUM, MINOR_NUM);
// Remove char device */
cdev_del(cdev);
/* Unregister Device Number: */
unregister_chrdev_region(device_number, 1);
printk(KERN_ALERT "TestDriver %d destroyed.\n", MAJOR_NUM);
}
module_init(test_init);
module_exit(test_exit);
Small test program:
maintest.c:
#include <unistd.h>
#include <fcntl.h>
int main(void) {
long input = 256;
int fd = open("/dev/chartest0", O_RDWR);
write(fd, &input, sizeof(long));
close(fd);
return 0;
}
The printk statements gives the following output with the given input of 256:
Write Eunction Entered.
Write count: 8, Write fp: 0
Value before cast: 0
Value after cast: 0
This also fails with copy_from_user given an in put size of 8 bytes. It also fails when iterating through the buffer one byte at a time and copying the data. I've tried everything.
If you are graciously willing to help, compile with:
MakeFile
ifeq ($(KERNELRELEASE),)
# Assume the source tree is where the running kernel was built
# You should set KERNELDIR in the environment if it's elsewhere
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
# The current directory is passed to sub-makes as argument
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
.PHONY: modules modules_install clean
else
# called from kernel build system: just declare what our modules are
obj-m := chartest.o
endif
then in the same directory:
sudo insmod chartest.ko
finally:
sudo mknod -m 777 /dev/chartest0 c 230 0
Then you can compile maindriver.c and run it to test.
Can someone please help me fix this issue?
you can not use get_user the way you do:
from get_user doc
This macro copies a single simple variable from user space to kernel space. It supports simple types like char and int, but not larger data types like structures or arrays.ptr must have pointer-to-simple-variable type, and the result of dereferencing ptr must be assignable to x without a cast.
With get_user, you will only copy the first character.
You need to use copy_from_user, this function can copy array and structure, not only simple types:
ssize_t test_write(struct file *fp, const char __user *buffer, size_t count, loff_t *fpos) {
// We must validate the user's buffer and convert it to a long long
long userOperand;
unsigned char *userInput = NULL;
userInput = kmalloc(count, GFP_KERNEL);
printk(KERN_NOTICE "Write Function Entered.\n");
printk(KERN_ALERT "Write count: %ld, Write fp: %lld\n", count, *fpos);
/* warning, here you should test that count is exactly sizeof userInput */
copy_from_user(userInput, buffer, count);
userOperand = *(long*)userInput;
printk(KERN_NOTICE "Value after cast: %ld\n", userOperand);
// Increment the file position pointer (in our case, always by 8)
*fpos += count;
kfree(userInput);
return count;
}
You can also copy from char * to long in copy_from_user (no memory alloc in that case):
ssize_t test_write(struct file *fp, const char __user *buffer, size_t count, loff_t *fpos) {
// We must validate the user's buffer and convert it to a long long
long userOperand;
printk(KERN_NOTICE "Write Function Entered.\n");
printk(KERN_ALERT "Write count: %ld, Write fp: %lld\n", count, *fpos);
/* warning, here you should test that count is exactly sizeof userOperand */
copy_from_user(&userOperand, buffer, sizeof userOperand);
printk(KERN_NOTICE "Value after reading: %ld\n", userOperand);
// Increment the file position pointer (in our case, always by 8)
*fpos += count;
return count;
}
You use macro get_user incorrectly.
Its first argument should be literally a variable name, not the address of it.
Its second argument should be typed pointer to the user space data. And exactly type of that pointer is used for determine the size of the data to read.
Correct:
long userOperand;
...
get_user(userOperand, (const long __user*)buffer);
printk(KERN_NOTICE "Value written: %ld\n", userOperand);
Note to the case of the second argument: it is needed for read long instead of char (because buffer parameter is a pointer to the char).
Note, that code above doesn't use your userInput variable, so you don't need to define it and don't need to allocate memory.
Note, that .write method could be called with any count parameter which denotes number of bytes passed from the user. Since get_user always tries to read a predefined number of bytes (in your case this number is equal to the size of the long), it would be a user-friendly to check that count is equal to that number (or, at least, is not less than given number):
if (count != sizeof(long)) {
return -EINVAL;
}

Abort 6 issues when using memcpy()

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;
}

c - writing data to wav file

I'm working on an small tool that reads data from an wav file. This tool first extracts the header followed by separating the audio data into left and right channel. Audio files are just files with a sampling frequency of 44100Hz, 16Bit PCM and dual-channel.
After manipulating the data I want to write back the data to an output file and append 100 zeros on each channel. Here the problem occurs: first just half of the desired samples are append on each channel. Secondly the first half of the appended 'zeros' are random data.
See my code below
#include <stdlib.h>
#include <stdio.h>
#define BUFFSIZE 1024
#define NUM_ZEROS 100
#include <stdio.h>
#include <stdlib.h>
typedef struct header_file
{
char chunk_id[4];
int chunk_size;
char format[4];
char subchunk1_id[4];
int subchunk1_size;
short int audio_format;
short int num_channels;
int sample_rate;
int byte_rate;
short int block_align;
short int bits_per_sample;
char subchunk2_id[4];
int subchunk2_size;
} header;
typedef struct header_file* header_p;
int main(int argc, char** argv){
if( argc != 3 ){
printf("Wrong number of input arguments. Aborting.\n");
return -1;
}
char *inputFile = argv[1];
char *outputFile = argv[2];
FILE * infile = fopen(inputFile, "r+");
FILE * outfile = fopen(outputFile, "w+");
int count = 0; // For counting number of frames in wave file.
short int buff16[2*BUFFSIZE]; // short int used for 16 bit as input data format is 16 bit PCM audio
short int buffLeft[BUFFSIZE], buffRight[BUFFSIZE];
header_p meta = (header_p)malloc(sizeof(header)); // header_p points to a header struct that contains the wave file metadata fields
int nb, cnt; // variable storing number of bytes returned
printf("Buffers initialized.\n");
if (infile)
{
fread(meta, 1, sizeof(header), infile);
meta->subchunk2_size = meta->subchunk2_size + 2 * NUM_ZEROS;
fwrite(meta,1, sizeof(*meta), outfile);
while (!feof(infile))
{
nb = fread(buff16,1,BUFFSIZE,infile); // Reading data in chunks of BUFSIZE
count++; // Incrementing Number of frames
for(cnt = 0; cnt < nb/2; cnt++){
buffLeft[cnt] = buff16[2*cnt];
buffRight[cnt] = buff16[2*cnt+1];
}
/*
* TODO: INSERT SIGNAL PROCESSING PART
*/
for(cnt = 0; cnt < nb/2; cnt++){
buff16[2*cnt] = buffLeft[cnt];
buff16[2*cnt+1] = buffRight[cnt];
}
fwrite(buff16,1,nb,outfile);
}
for(cnt = 0; cnt < 2*NUM_ZEROS; cnt++){
buff16[cnt] = 0;
}
fwrite(buff16,1, 2*NUM_ZEROS,outfile);
printf("Number of frames in the input wave file are %d.\n", count);
}
fclose(infile);
fclose(outfile);
return 0;
}
Does somebody have an idea what I did wrong?
Are you sure that only a part of the added zeros is garbage?
You mess up the data size for fread and fwrite
Your buffers are short int:
short int buff16[2*BUFFSIZE]; // BUFFSIZE*2*sizeof(short) bytes
You read only 1/4 of that size:
nb = fread(buff16,1,BUFFSIZE,infile); // BUFFSIZE bytes
This reads BUFSIZE bytes as you only specify a size of 1 per element.
Instead of BUFFSIZE*2 shorts you only read BUFFSIZE bytes.
The return value is number of read elements, i.e. bytes again.
In your buffer that amount of data is only sufficient for nb/2 elements but you access buff16[0] .. buff16[nb-1] where second half of it was not read from the file.
Luckily you also do not write the second half back into the new file as the
same error with length is also present there.
And finally the same problem is present when you append the zero values to the file.
tl;dr
Change your size parameter for fread and fwrite to sizeof(short int).
You have
#define NUM_ZEROS 100
and
fwrite(buff16,1, 2*NUM_ZEROS,outfile);
Aim:
I want to write back the data to an output file and append 100 zeros
on each channel.
I think it should be 100 SAMPLES at each channel.
As you have 16bit PCM each sample is 2 bytes.
So one channel needs 200 bytes (zeros) to be written. Stereo means 400 bytes.
Your fwrite saves just 2*NUM_ZEROS so 200 bytes - this is answer to part about missing samples.
Additionally you declare
short int buff16[2*BUFFSIZE];
while reading just half of it and using half of half (nb/2) for processing. Than write full buffer (actually half of declared) with upper half full of random garbage from memory.

image recovery program has an issue

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.

How to check whether a file is in tar format?

I want to implement a check of a tar file.
I am not interested only to check the file extension, but I need surer way to check.
For example for zip format I could check some lead bytes. But what is the way for tar files?
Regards,
B
You can check file's header.
Here's the specification: http://www.gnu.org/software/tar/manual/html_node/Standard.html
Check the magic bytes at offset 257. If they match "ustar" including the null terminator, the file is probably a tar.
See: http://www.gnu.org/software/tar/manual/html_node/Standard.html
/* tar Header Block, from POSIX 1003.1-1990. */
/* POSIX header. */
struct posix_header
{ /* byte offset */
char name[100]; /* 0 */
char mode[8]; /* 100 */
char uid[8]; /* 108 */
char gid[8]; /* 116 */
char size[12]; /* 124 */
char mtime[12]; /* 136 */
char chksum[8]; /* 148 */
char typeflag; /* 156 */
char linkname[100]; /* 157 */
char magic[6]; /* 257 */
char version[2]; /* 263 */
char uname[32]; /* 265 */
char gname[32]; /* 297 */
char devmajor[8]; /* 329 */
char devminor[8]; /* 337 */
char prefix[155]; /* 345 */
/* 500 */
};
#define TMAGIC "ustar" /* ustar and a null */
#define TMAGLEN 6
In shell you can use the file command
file AFile.tar
AFile.tar: POSIX tar archive (GNU)
A C++ code that checks the first magic string in a file in order to determine if it is a tar file. If unfortunately "star" appears exactly on the 257 location in a non-TAR file, it will produce a false positive, which is very unlikely:
#include <fstream>
#include <stdexcept>
bool isTarFile(const std::string& filePath) {
std::ifstream file(filePath, std::ios::binary);
if (!file) {
throw std::runtime_error("Could not open file: " + filePath);
}
constexpr char magic[] = "ustar";
constexpr size_t magicLength = sizeof(magic) - 1;
constexpr size_t bufferSize = sizeof(magic);
char buffer[bufferSize];
// Seek to the magic string location in the header
file.seekg(257);
if (!file) {
throw std::runtime_error("Could not seek to position 257 of file: " + filePath);
}
// Read the magic number
file.read(buffer, bufferSize);
if (!file) {
throw std::runtime_error("Could not read from file: " + filePath);
}
// Check the file signature
return (std::strncmp(buffer, magic, magicLength) == 0);
}

Resources