0

If this runs on mac32, it runs fine; if I run it on win32, I will get an unexpected end of file at pixel 139, and if I comment out the fseek (which on my test .tga image will just seek 0 bytes so shouldn't make a difference) I get the unexpected end of file at pixel 35. The header is read completely fine though; it's just the data.

Are the 2 functions implemented differently for different compilers/architecture?

struct TGAHeader
{   
    char    idlength;
    char    colourmaptype;
    char    datatypecode;
    short   colourmaporigin;
    short   colourmaplength;
    char    colourmapdepth;
    short   x_origin;
    short   y_origin;
    short   width;
    short   height;
    char    bitsperpixel;
    char    imagedescriptor;
};    

bool read()
{
    printf("Reading TGA file...\n");

    header.idlength = fgetc(TGAFile);
    header.colourmaptype = fgetc(TGAFile);
    header.datatypecode = fgetc(TGAFile);
    fread(&header.colourmaporigin,2,1,TGAFile);
    fread(&header.colourmaplength,2,1,TGAFile);
    header.colourmapdepth = fgetc(TGAFile);
    fread(&header.x_origin,2,1,TGAFile);
    fread(&header.y_origin,2,1,TGAFile);
    fread(&header.width,2,1,TGAFile);
    fread(&header.height,2,1,TGAFile);
    header.bitsperpixel = fgetc(TGAFile);
    header.imagedescriptor = fgetc(TGAFile);

    printf("...TGA file has been read\n\n");
    printf("ID length:         %d\n",header.idlength);
    printf("Colourmap type:    %d\n",header.colourmaptype);
    printf("Image type:        %d\n",header.datatypecode);
    printf("Colour map offset: %d\n",header.colourmaporigin);
    printf("Colour map length: %d\n",header.colourmaplength);
    printf("Colour map depth:  %d\n",header.colourmapdepth);
    printf("X origin:          %d\n",header.x_origin);
    printf("Y origin:          %d\n",header.y_origin);
    printf("Width:             %d\n",header.width);
    printf("Height:            %d\n",header.height);
    printf("Bits per pixel:    %d\n",header.bitsperpixel);
    printf("Descriptor:        %d\n",header.imagedescriptor);

    bytesToRead = header.bitsperpixel / 8;

    if(!errCheck())
    {
        m_bLoaded = false;
        return false;
    }
    //set stream position indicator to the start of the data
    fseek(TGAFile,(header.idlength+(header.colourmaptype * header.colourmaplength)),SEEK_CUR);

    //allocate space to store data
    releaseTGAData();
    pixelData = new unsigned char*[header.width*header.height];
    for(int i=0;i<header.width*header.height;i++)
        pixelData[i] = new unsigned char[bytesToRead];

    for(int i=0;i<header.width * header.height;i++)
    {
        if (fread(pixelData[i],1,bytesToRead,TGAFile) != bytesToRead)
        {
            printf("Unexpected end of file at pixel %d\n",i);
            m_bLoaded = false;
            releaseTGAData();
            return false;
        }
    }
    fclose(TGAFile);

    return true;
}

edit: this is the load function

bool load(const char* inFilename = NULL)
{
    m_bLoaded = false;
    if(NULL!=filename)
        delete [] filename;
    filename = new char[128];

    //if the filename has not been selected by the programmer
    if(NULL==inFilename)
    {
        //ask the user for it
        printf("please enter the filename of an uncompressed TGA file:\n");
        scanf("%s", filename);
    }
    else sprintf(filename, inFilename);

    if ((TGAFile = fopen(filename,"r")) == NULL)
    {
        printf("File open failed, please try again\n");
        return false;
    }
    if(!read())
        return false;

    m_bLoaded=true;
    return true;
}
cool mr croc
  • 725
  • 1
  • 13
  • 33

3 Answers3

2

The problem is that you open the file in text mode. On Windows the input function will then convert the byte sequence 0x0d 0x0a (which is Windows newline sequence"\r\n") into a single character.

Windows will most likely also check for the byte 0x19 (I think) which is CTRL-Z and is the end-of-file character in Windows.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
1

Your problem is most likely caused by the different way windows handles reading from a file when non binary files are handled, but a more efficient solution would be to use #pragma pack or attribute (alligned(1)) for the header struct and read the whole header in one fread call

Radu Chivu
  • 1,057
  • 7
  • 17
  • Please do **not** read the whole header (or any `struct`) in one `fread` call; this leads to unmaintainable code. For example, changing the compiler or, especially, the architecture (x86 vs PowerPC) will break the code in hard-to-detect ways: files created in one setting will be read just fine, but the same file will be considered corrupted by the same program when compiled in a different setting. – cjb Jan 28 '13 at 01:23
  • Furthermore, using one `fread` is barely even slightly faster because the file will be read into memory in large blocks regardless of how many bytes you request and the slight delay of individually moving bytes into a `struct` versus all at once is dwarfed by the IO operation itself. – cjb Jan 28 '13 at 01:28
  • How are 20 calls to an IO function more maintainable than a single one. Unless you read and write byte by byte or use only chars, this is a problem you're going to have to solve regardless of how you write the code – Radu Chivu Jan 28 '13 at 05:55
  • Also on the second thing, the compiler does not optimise I/O api calls, if you call fread 20 times it's going to make 20 I/O operations, if you don't believe me, then try to write or read a big file (1 000 000 bytes) byte by byte in a for loop and just once with a single call and time the difference. – Radu Chivu Jan 28 '13 at 06:08
  • for a related discussion on this topic, see: http://stackoverflow.com/questions/859535/how-do-i-convert-a-big-endian-struct-to-a-little-endian-struct. – cjb Jan 28 '13 at 16:37
  • Multiple calls to `fread` do not always result in multiple IO operations because the OS will typically buffer large blocks in memory. However, @radu is correct in that each `fread` can result in a new _system call_ and that this will be slow for reading very large files (but not nearly as slow as IO). The proper way to optimize for this situation is to read into your own buffer (a vector or array of bytes), and then read this buffer field-by-field into a `struct`. – cjb Jan 28 '13 at 16:41
1

Since the file contains binary data, you must use "rb" as the open mode in the call to fopen() on Windows. On Unix, the b is optional (but harmless), so if your code is going to be portable, use the same mode on both systems.

If you open the file on Windows without specifying the b, you get 'CRLF' mapping (so carriage return line feed sequences "\r\n" are mapped to newline"\n" — where newline is just another name for line feed). And the first control-Z byte marks the end of file.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278