I'm dabbling in a bit of Game Boy save state hacking, and I'm currently getting my head around reading from a binary file in C. I understand that char
is one byte so I'm reading in chars using the following code
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
int main () {
FILE *fp = NULL;
char buffer[100];
int filesize;
fp = fopen("save file.srm", "r "); //open
if(fp == NULL) printf("File loading error number %i\n", errno);
fseek(fp, 0L, SEEK_END); //seek to end to get file size
filesize = ftell(fp);
printf("Filesize is %i bytes\n",filesize);
fseek(fp, 0, SEEK_SET); //set read point at start of file
fread(buffer, sizeof buffer, 1, fp);//read into buffer
for(int i=70;i<80;i ) printf("%x\n", buffer[i]); //display
fclose(fp);
return(0);
}
The output I get is
Filesize is 32768 bytes
ffffff89
66
ffffff85
2
2
ffffff8b
44
ffffff83
c
0
I'm trying to load in bytes, so I want each row of the output to have maximum value 0xff. Can someone explain why I'm getting ffffff8b?
CodePudding user response:
if(fp == NULL) printf("File loading error number %i\n", errno);
When you detect an error, do not just print a message. Either exit the program or do something to correct for the error.
char buffer[10];
Use unsigned char
for working with raw data. char
may be signed, which can cause undesired effects.
fread(buffer, strlen(buffer) 1, 1, fp);
buffer
has not been initialized at this point, so the behavior of strlen(buffer)
is not defined by the C standard. In any case, you do not want to use the length of the string currently in buffer
as the size for fread
. You want the size of the array. So use sizeof buffer
(without the 1
).
for(int i=0;i<10;i )
Do not iterate to ten. Iterate to the number of bytes put into the buffer by fread
. fread
returns size_t
value that is the number of items read. If you use it as size_t n = fread(buffer, 1, sizeof buffer, fp);
, the number of items (in n
) will be the number of bytes read, since having 1
for the second argument says each item to read is one byte.
printf("%x\n", buffer[i]);
To print an unsigned char
, use %hhx
. Because your buffer
had signed char
elements, some of them were negative. When used in this printf
, they were promoted to negative int
values. Then, because of the %x
, printf
attempted to print them as unsigned int
values. All the extra bits from the negative values in two’s complement form showed up.
CodePudding user response:
Very simply: char
can be signed
or unsigned
by default: that's down to the compiler. In your case, it appears to be signed
.
When you pass the char
of buffer[i]
to printf()
, it is promoted to int
, and sign-extended if the original char
value had its top bit set. Hence anything that's in the range 0x80-0xff gets a lot of f
s prefixing the value.
If you declare buffer
to be unsigned char
, this problem should not occur. But you should, in combination with that, use "%hhx"
rather than "%x"
for your printf()
format, since the hh
length modifier forces printf()
to mask the input value so that only those bits applicable to an unsigned char
(given that you're using the x
specifier) are used.