0

There is a code excerpt from official Quake 2 source code:

unsigned        *buf;
dheader_t       header;
...
header = *(dheader_t *)buf; // #1
    for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
        ((int *)&header)[i] = LittleLong ( ((int *)&header)[i]); // #2

Can someone please explain me in the most possible details what do the line #1 and then #2 really do because I'm little or more confused...

P.S

Here is the rest of the definitions if it helps:

int     LittleLong (int l) {return _LittleLong(l);}
...
typedef struct
{
    int         ident;
    int         version;    
    lump_t      lumps[HEADER_LUMPS];
} dheader_t;

P.S. 2

I've linked above the original full source file code if needed.

2
  • From what I recall, the various Quake (and therefore also Minecraft) code bases are known to use dirty hacks all over. Most of the code here is wildly undefined behavior. Commented Jul 8, 2022 at 9:43
  • 1
    Work from the inside out with expressions such as these. Commented Jul 8, 2022 at 9:44

1 Answer 1

4

This is some seriously brittle code and you shouldn't write code like this.

What it does is to go through the struct int by int, then does something with each such int inside _LittleLong. Very likely this function performs a 32 bit conversion from a big endian integer to a little endian one. Meaning that the source you are looking at is likely something related to reception of IP packages.

Checking at what the code does step by step:

  • for (i=0 ; i<sizeof(dheader_t)/4 ; i++) is a sloppier way of writing sizeof(dheader_t)/sizeof(int). That is: iterate through the struct int by int, chunks of 32 bits.
  • (int *)&header converts from a dheader_t* to a int*. This is actually well-defined by a special rule in C that allows us to convert from a pointer to a struct to a pointer to its first member or vice versa and the first member is int.
  • However, doing so is only well-defined for the first member. Instead they take the converted int* and apply array dereferencing on it: ((int *)&header)[i]. This is undefined behavior in C, a so-called strict aliasing violation, and could also cause alignment problems in some situations. Bad.
  • The int read from the struct through this dereferencing is then passed along to LittleLong which very likely does a big -> little endian conversion.
  • ((int *)&header)[i] = and here it is written back to where it was grabbed from.

Better, safer, well-defined and possibly faster code could look like:

void endianify (dheader_t* header)
{
  _Static_assert(sizeof(dheader_t)%sizeof(uint32_t)==0,
                 "Broken struct: dheader_t");

  unsigned char* start = (unsigned char*)header;
  unsigned char* end   = start + sizeof(dheader_t);
  
  for(unsigned char* i=start; i!=end; i+=sizeof(uint32_t))
  {
    uint32_t tmp;
    memcpy(&tmp,i,sizeof(uint32_t));
    i[0]= (tmp >> 24) & 0xFF;
    i[1]= (tmp >> 16) & 0xFF;
    i[2]= (tmp >>  8) & 0xFF;
    i[3]= (tmp >>  0) & 0xFF;
  }
}

Disassembly:

endianify:
    mov     eax, DWORD PTR [rdi]
    bswap   eax
    mov     DWORD PTR [rdi], eax
    mov     eax, DWORD PTR [rdi+4]
    bswap   eax
    mov     DWORD PTR [rdi+4], eax
    mov     eax, DWORD PTR [rdi+8]
    bswap   eax
    mov     DWORD PTR [rdi+8], eax
    mov     eax, DWORD PTR [rdi+12]
    bswap   eax
    mov     DWORD PTR [rdi+12], eax
    mov     eax, DWORD PTR [rdi+16]
    bswap   eax
    mov     DWORD PTR [rdi+16], eax
    ret
Sign up to request clarification or add additional context in comments.

2 Comments

So the construct (int *)&header)[i] says: take the adders to the header structure and treat it as the pointer to an i-sized array of int's?
@darro911 Yes, pretty much.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.