.GOB

  This file contains a whole set of various files: graphics, maps, sounds,
  musics, etc.

  == STRUCTURE
  UINT32    signature   MUST be 0x0A424F47 (or "GOB" followed by 0x0A)
   INT32    dirOffset   Position of the directory within the file
  CHAR[]    rawData     Where "files" are stored
   dir_t    dir         Directory

  dir_t
    {
    UINT32    numEntries  How many entries are stored in the directory
    ent_t     ent[]       Each entry
    }

  ent_t
    {
    UINT32    offset      Position of the first byte of data of this file
    UINT32    length      Length in bytes of this file
    CHAR[13]  name        Null-terminated file name
    }


.PAL

  PAL files are standard palette files of 256 colors. Each color is stored on
  three unsigned bytes (one per R, G and B attribute - in that order). Each
  attribute ranges from 0 to 63 (multiply each value by 4).


.BM

  BM files are index-color bitmaps, they are used for GUI display and textures.
  You're in for a treat because this format is a mess. There are two types of
  files: simple (one file = one texture) and complex (one file = multiple
  frames, used for animated textures and switches). If Width == 1 and Height ==
  (LOF - 32), the file is complex, it is simple otherwise. Why not use a flag or
  the unused padding at the end of the header instead? Go figure. Note that
  complex files cannot be compressed.

  == SIMPLE FILE

  [HEADER]         32 bytes
  [PIXEL POOL]     Width*Height bytes (or PoolSize bytes if compressed)
  [COLUMN OFFSET]  4*Width bytes of data (present only if compressed)

  HEADER, 32 bytes long

  4     UINT32    Magic         MUST be 0x1E204D42 (or ".BM" followed by 0x1E).
  2     UINT16    Width         Width in pixels.
  2     UINT16    Height        Height in pixels.
  2     UINT16    Width2        Width in pixels, sometimes unequal to Width.
  2     UINT16    Height2       Height in pixels, sometimes unequal to Height.
  1     UINT8     AlphaI        Transparent color index.
                                  0x36: normal
                                  0x3E: for transparent walls
                                  0x08: for weapons view
  1     UINT8     HeightLog     Equals LOG(Height)/LOG(2) so 2^SqrLog = Height.
  2     UINT16    Compression   Compression type.
                                  0x01: RLE-compressed
                                  0x02: RLE-compressed skips
  4     UINT32    PoolSize      Size of the pixel pool, in bytes.
  12    CHAR[12]  Padding       Skip.

  PIXEL POOL

  The pixel pool is organized by column (bottom to top) and then by row (left to
  right). The pixel pool usually only contains pixel information (the whole pool
  is Width * Height bytes long), but may also contain RLE descriptors if a
  compression is used (in this case, the pool is PoolSize bytes long).

  Uncompressed image (0x00): there is Width * Height bytes of data, each byte
  points to an index in the palette. All textures in the registered version of
  Dark Forces are uncompressed.

  Compressed image (0x01): the pixel pool contains pixel information and RLE
  descriptors. Each column is RLE-compressed using a technique identical to TGA
  files. Each post starts with a UINT8 descriptor containing two value: the most
  significant bit (0x80) enables repeat (if set) or direct color (if unset), the
  remaining 7 bits (0x7F) count the amount of pixels affected in the destination
  buffer. If repeat is set, read one pixel of information and write it multiple
  times. If repeat is unset, read multiple pixels and write them in the same
  order to the target buffer. The offset to the begining of each column is
  stored in a table made of Width entries, each entry is a UINT32 pointing to
  the begining of the pixel pool (offset 0 = first byte in the pixel pool, or
  33rd byte from the begining of the file). This table SHOULD be located at the
  end of the file (LOF - (Width *4)) or (32 + PoolSize).

  Compressed image (0x02): identical to compression 0x01 (same descriptor, same
  column offset table) with one slight difference: repeats are skipped entirely
  (do not read an index from the pool, and just skip the number of specified
  pixels in the destination buffer).

  == COMPLEX FILE

  [HEADER]         34 bytes
  [SUB OFFSETS]    4*numFrames
    [SUB HEADER]   28 bytes
    [PIXEL POOL]     Width*Height bytes

  You have to read the Sub Header and the Pixel Pool for each frame stored. The
  begining of each Sub Header is provided in Sub Offsets, a group of numFrames
  offsets stored as UINT32. The offset starts right after the 34 byte long
  header (offset[0] is located a byte[34]).

  HEADER, 34 bytes long

  4     UINT32    Magic         MUST be 0x1E204D42 (or ".BM" followed by 0x1E).
  2     UINT16    Width         MUST be 1.
  2     UINT16    Height        MUST be LOF - 32.
  2     UINT16    Width2        Skip.
  2     UINT16    numFrames     Number of images.
  1     UINT8     AlphaI        Skip.
  1     UINT8     HeightLog     Skip.
  2     UINT16    Compression   Skip.
  4     UINT32    PoolSize      Skip.
  12    CHAR[12]  Padding       Skip.
  1     UINT8     FPS           Frames per second.
  1     UINT8     Unknown       MUST be 2.

  SUB HEADER, 28 bytes long

  2     UINT16    Width         Width in pixels of this frame.
  2     UINT16    Height        Height in pixels of this frame.
  2     UINT16    Width2        Skip.
  2     UINT16    Height2       Skip.
  4     UINT32    DataSize      Skip.
  1     UINT8     HeightLog     Equals LOG(Height)/LOG(2) so 2^SqrLog = Height.
  11    CHAR[11]  Padding       Skip.
  1     UINT8     AlphaI        Transparent color index.
                                  0x36: opaque
                                  0x3E: transparent
  3     CHAR[3]   Padding       Skip.

  PIXEL POOL

  Identical to simple file (except there's no compression allowed at all).