Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

fread/ftell apparently broken under Windows, works fine under Linux

So here's the problem, I'm reading a level file for my game, works fine under linux:

@0
@12
200x200 version 3
@16
973 blocks
@989
@993
18 zones

But under windows I get the following result:

@0
@212
200x200 version 3
@216
973 blocks
@1200
@1204
18 zones

Uh? The windows ftell stats with an offset of 200? Reading the file apparently yields the same data, but fread uses(?) the value of ftell to determine the how many bytes are left in the file that can be read. So of course I'm running into problems when reading at the end of the file:

@1425
zone#9 2x3 @ 66/9
@1425
zone#10 2x3 @ 66/9
@1425
zone#11 2x3 @ 66/9
@1425
zone#12 2x3 @ 66/9
@1425
zone#13 2x3 @ 66/9
@1425
zone#14 2x3 @ 66/9
etc.

This is the corresponding code(currently a bit ugly due to all the debug prints..):

void fread_all(void *ptr, size_t size, size_t count, FILE *stream) {
    fread(ptr, size, count, stream);
    printf("@%ld\n", ftell(stream));
}


bool map_load(struct Map *map, const char *file_name) {
    FILE *fp = fopen(file_name, "r");
    if (fp != NULL) {
        fseek(fp, 0, SEEK_SET);
        printf("@%ld\n", ftell(fp));

        // Header
        int *header = (int*)calloc(sizeof(int), 3);
        fread_all(header, sizeof(int), 3, fp);
        printf("%dx%d version %d\n", header[0], header[1], header[2]);

        map->pos_x = 0;
        map->pos_y = 0;
        map->map_x = 0;
        map->map_y = 0;
        map->size_x = header[0];
        map->size_y = header[1];
        map_zones_remove(map);        
        free(header);

        // Blocks
        unsigned int *block_size = (unsigned int*)malloc(sizeof(unsigned int));
        fread_all(block_size, sizeof(int), 1, fp);
        printf("%d blocks\n", *block_size);

        unsigned char *block_data = (unsigned char*)calloc(sizeof(unsigned char), *block_size);
        fread_all(block_data, sizeof(unsigned char), *block_size, fp);

        unsigned char *tmp = map->blocks;
        map->blocks = rle_decode(block_data, *block_size);
        free(tmp);
        free(block_size);
        free(block_data);

        // Zones
        int *zone_count = (int*)malloc(sizeof(int));
        fread_all(zone_count, sizeof(int), 1, fp);
        printf("%d zones\n", *zone_count);

        int *d = (int*)calloc(sizeof(int), 6);
        for(int i = 0, l = *zone_count; i < l; i++) {
            fread_all(d, sizeof(int), 6, fp);
            map_zone_create(map, d[0], d[1], d[2], d[3], d[4], d[5]);
            printf("zone#%d %dx%d @ %d/%d\n", i, d[2], d[3], d[0], d[1]);
        }
        map_platforms_create(map);

        free(zone_count);
        free(d);
        fclose(fp);
        return true;
    }
    return false;
}

I really have no clue what's going on here. Compilers are the Visual Studio 10 one and GCC 4.4 under Linux.

like image 736
Ivo Wetzel Avatar asked Jul 06 '10 15:07

Ivo Wetzel


2 Answers

Open your file in binary mode:

FILE *fp = fopen(file_name, "rb");

In text mode, there might be translations going on to match the operating system-dependent encoding of e.g. line feeds to the C library's.

like image 183
unwind Avatar answered Oct 29 '22 16:10

unwind


ftell and fseek are only going to work as byte offsets if you open the file in binary mode, (i.e. "rb" instead of "r"). Otherwise you can only fseek to things that have previously be returned by ftell; the result of fseek isn't going to be a byte offset.

Binary mode makes a difference on windows where text mode maps the two character carriage return, line feed sequence to a single new-line character. No mapping is needed on linux.

like image 42
CB Bailey Avatar answered Oct 29 '22 17:10

CB Bailey