Fread / ftell, apparently broken on Windows, works fine on Linux

So, here is the problem, I am reading the level file for my game, it works fine under linux:

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

But under the windows, I get the following result:

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

Uh? Windows ftell stats with offset 200? Reading the file seems to give the same data, but fread uses (?) The ftell value to determine how many bytes remain in the file that can be read. Therefore, of course, I have problems 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 appropriate code (currently a bit ugly due to all the debugging 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 don’t understand what is going on here. Compilers are Visual Studio 10 one and GCC 4.4 under Linux.

+7
c linux windows file-io
source share
3 answers

Open the file in binary mode:

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

In text mode, translations can occur to match the operating system-specific encoding, for example. line to C. library

+20
source share

ftell and fseek will only work in byte offsets if you open the file in binary mode (ie "rb" instead of "r" ). Otherwise, you can use fseek only those things that were previously returned by ftell ; fseek result will not be a byte offset.

Binary mode makes a difference in windows, where text mode matches two carriage return characters, a string sequence for one newline character. In linux, no mapping is required.

+4
source share

You should not use ftell and fseek to determine the file size, as it can be the source of vurnerabilities:

https://www.securecoding.cert.org/confluence/display/c/FIO19-C.+Do+not+use+fseek%28%29+and+ftell%28%29+to+compute+the+size + of + a + regular + file

0
source share

All Articles