Reading 16bit variables in x64 systems...

Alright, I am reading some data from an old DOS file into a buffer in memory. However, the variables in DOS and XP x64 are different sizes and I am not sure how to get just what I need out of the memory buffer. For example, a DOS 'long' is four bytes, but that is eight bytes on x64 hardware. If my data buffer is a megabyte or two, how can I point that new 64bit long at a point in that buffer and have it get only four bytes of data out of it so it will be correct?
Last edited on
What exactly do you intend to do with your buffer?
Just for reading and processing those variables it does not matter how long the variables are but you have to know their structure.
You can create a buffer of unsigned chars and read the whole file.
Then you can get each variable by its start address (maybe you have to change endianness I don't know).
E.g.
1
2
3
4
5
6
unsigned char *chars = new unsigned char[FILESIZE];
// read file and save it in the buffer chars
unsigned long *test;// 8 bytes
// set the address of the pointer at the place you wish
// to let you get the value of the next 8 bytes through *test
test = (unsigned long*) (chars+VARIABLE_START_POSITION);

You could also access the variables just by typecasting without to create a pointer to the address you wish of the data type with X bytes size.
Last edited on
I won't have to change anything. I do read the data into a buffer the size of the file. But here is where it gets tricky. The first four bytes are a DOS 'long" that gives the number of records in the file, which start behind the four bytes for the 'long'.
1
2
3
4
char pData[2048]; //2kb for this example
long lRecordCount;

lRecordCount = &(pData[0]);

Unless I am mistaken, that will work properly on 16bit and even 32bit systems, but my trouble arises when I hit 64bit machines. The above code would start at the beginning of the buffer, but on an x64 system, wouldn't it read eight bytes instead of four since a 64bit long is eight bytes? I have never tried this before so I am completely lost. if this was a file, I could simply use 'fstream' to read four bytes of the file, but that's no good here.

*EDIT*

A better example is below. Assume that 'pData' points to a buffer anywhere from a few kB to a few MB in size.
1
2
3
4
5
6
7
8
  //Get the required data from the buffer
  this->lPreRecordCount = (long)&(pData[0]);
  lJump = ((this->lPreRecordCount * 6) + 11);
  this->pHeader->lXPosition = (long)&(pData[lJump]);
  lJump += 4;
  this->pHeader->lYPosition = (long)&(pData[lJump]);
  lJump += 14;
  this->pHeader->sLocationID = (short)&(pData[lJump]);

See I don't need all of the data in the file to do my work, only specifics. In this example, I grab the X and Y coordinates for another part of my program, and then grab the unique location identifier. The data between this info is useless to me and I skip it.
Last edited on
I changed the above to unsigned long since it is 64 bit, my mistake.

Here a table from Wikipedia
1
2
3
4
5
Data model 	short 	int 	long 	long long 	pointers 	Sample operating systems
LLP64 		16 	32 	32 	64 		64 		Microsoft Win64 (X64/IA64)
LP64 		16 	32 	64 	64 		64 		Most UNIX and UNIX-like systems (Solaris, Linux, etc)
ILP64 		16 	64 	64 	64 		64 		HAL
SILP64 		64 	64 	64 	64 		64 		 ?

Either you know what data model you are looking for you can use a short int for example if that is the case if not you can do something like this when reading an unsigned variable with 32 bits for example (big endian):
1
2
// save 4 bytes no matter if lRecordCount is 4 or 8 bytes long
lRecordCount = (&(pData[0])) & 0xFFFFFFFF;

[edit]inserted another tab for the table[/edit]
Last edited on
Could you explain that method in detail? I will also need to read in both signed and unsigned 'short' variables, signed and unsigned 'int' variables, and unsigned 'long' variables. I do not believe that I need to read in any float or double data types. The DOS program used only short, int, and long, as well as char, but char is the same on all platforms.

*EDIT*

Will that method work on both 32bit and 64bit platforms? This application will be released on 32bit and 64bit Windows initially, and 32bit and 64bit Linux later.
Last edited on
Here the example for 1, 2, and 4 bytes signed and unsigned with the same method. I'll take a 8byte container even though not necessary (the 'container' has to be at least 4 bytes long or more to read 4 bytes):
1
2
3
4
5
6
7
8
9
10
11
12
13
unsigned long long *container;
unsigned short i;
unsigned char *chars = new unsigned char[20];
for(i = 0; i < 20; i++){chars[i] = 0xFF;}
// an example start byte is 10 from 0..19 (8 bytes)
container = (unsigned long long*)(chars+10);
// write unsigned 8 bit AND with 8bit set
cout << ((*container) & 0xFFULL) << "\n";
// write unsigned 16 bit AND with 16bit set
cout << ((*container) & 0xFFFFULL) << "\n";
// write unsigned 32 bit AND with 32bit set
cout << ((*container) & 0xFFFFFFFFULL) << "\n";<< "\n";
delete [] chars;

I'm sure there are niftier possiblities for the signed variables but this should work.
Yes, it does not matter if on a 32 bit or 64 bit platform (even if you declare the container as unsigned int *container; you will be able to read a variable of a maximum of 4 bytes)
[edit]On a 16 bit platform it wouldn't work with an integer ;)[/edit]
[edit2]sry signed was not correct like this, I will write a correct version later.[/edit2]
[edit3]
While writing the signe version which would have been quite a little longer than the unsigned a much better and easier version crossed my mind. It was stupid of me not to think of it first (signed/unsigned 8-64bit):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
unsigned long long *container;
unsigned short i;
unsigned char *chars = new unsigned char[20];
struct {unsigned long long nr:8;} u8bit;
struct {unsigned long long nr:16;} u16bit;
struct {unsigned long long nr:32;} u32bit;
struct {unsigned long long nr:64;} u64bit;
struct {signed long long nr:8;} s8bit;
struct {signed long long nr:16;} s16bit;
struct {signed long long nr:32;} s32bit;
struct {signed long long nr:64;} s64bit;

for(i = 0; i < 20; i++){chars[i] = 0xFF;}
// an example start byte is 10 from 0..19 (8 bytes)
container = (unsigned long long*)(chars+10);
// write unsigned 8 bit
u8bit.nr = *container;
cout <<  u8bit.nr << "\n";
// write unsigned 16 bit
u16bit.nr = *container;
cout << u16bit.nr << "\n";
// write unsigned 32 bit
u32bit.nr = *container;
cout << u32bit.nr << "\n";
// write unsigned 32 bit
u64bit.nr = *container;
cout << u64bit.nr << "\n";
// write signed 8 bit
s8bit.nr = *container;
cout <<  s8bit.nr << "\n";
// write signed 16 bit
s16bit.nr = *container;
cout << s16bit.nr << "\n";
// write signed 32 bit
s32bit.nr = *container;
cout << s32bit.nr << "\n";
// write signed 64 bit
s64bit.nr = *container;
cout << s64bit.nr << "\n";
delete [] chars;

[/edit3]
Last edited on
There's always boost/cstdint.hpp
There was actually a fairly easy answer all along that was overlooked. The code below works perfectly. I simply use a 32/64bit short for 16bit short/int, and a 32/64bit int for 16bit long. The rest is self-explanatory.
1
2
3
4
5
6
7
8
  //Get the required data from the buffer
  this->iPreRecordCount = *((signed int*)pData);
  lJump = ((this->iPreRecordCount * 6) + 11);
  this->pHeader->iXPosition = *((signed int*)(pData + lJump));
  lJump += 4;
  this->pHeader->iYPosition = *((signed int*)(pData + lJump));
  lJump += 14;
  this->pHeader->sLocationID = *((signed short int*)(pData + lJump));

Thanks for all the help!
Topic archived. No new replies allowed.