glx@9627: /* $Id$ */ glx@9627: glx@9627: /** @file grf.cpp */ glx@9627: glx@9626: #include "../stdafx.h" glx@9626: #include "../gfx.h" glx@9626: #include "../fileio.h" glx@9626: #include "../debug.h" glx@9626: #include "grf.hpp" glx@9626: rubidium@9628: bool SpriteLoaderGrf::LoadSprite(SpriteLoader::Sprite *sprite, const char *filename, uint32 file_pos) glx@9626: { glx@9626: /* Open the right file and go to the correct position */ glx@9626: FioSeekToFile(file_pos); glx@9626: glx@9626: /* Read the size and type */ glx@9626: int num = FioReadWord(); glx@9626: byte type = FioReadByte(); glx@9626: glx@9626: /* Type 0xFF indicates either a colormap or some other non-sprite info; we do not handle them here */ glx@9626: if (type == 0xFF) return false; glx@9626: glx@9626: sprite->height = FioReadByte(); glx@9626: sprite->width = FioReadWord(); glx@9626: sprite->x_offs = FioReadWord(); glx@9626: sprite->y_offs = FioReadWord(); glx@9626: glx@9626: /* 0x02 indicates it is a compressed sprite, so we can't rely on 'num' to be valid. glx@9626: * In case it is uncompressed, the size is 'num' - 8 (header-size). */ glx@9626: num = (type & 0x02) ? sprite->width * sprite->height : num - 8; glx@9626: glx@9626: /* XXX -- We should use a pre-located memory segment for this, malloc/free is pretty expensive */ glx@9626: byte *dest_orig = MallocT(num); glx@9626: byte *dest = dest_orig; glx@9626: glx@9626: /* Read the file, which has some kind of compression */ glx@9626: while (num > 0) { glx@9626: int8 code = FioReadByte(); glx@9626: glx@9626: if (code >= 0) { glx@9626: /* Plain bytes to read */ glx@9626: int size = (code == 0) ? 0x80 : code; glx@9626: num -= size; glx@9626: for (; size > 0; size--) { glx@9626: *dest = FioReadByte(); glx@9626: dest++; glx@9626: } glx@9626: } else { glx@9626: /* Copy bytes from earlier in the sprite */ glx@9626: const uint data_offset = ((code & 7) << 8) | FioReadByte(); glx@9626: int size = -(code >> 3); glx@9626: num -= size; glx@9626: for (; size > 0; size--) { glx@9626: *dest = *(dest - data_offset); glx@9626: dest++; glx@9626: } glx@9626: } glx@9626: } glx@9626: glx@9626: assert(num == 0); glx@9626: glx@9626: sprite->data = CallocT(sprite->width * sprite->height); glx@9626: glx@9626: /* When there are transparency pixels, this format has an other trick.. decode it */ glx@9626: if (type & 0x08) { glx@9626: for (int y = 0; y < sprite->height; y++) { glx@9626: bool last_item = false; glx@9626: /* Look up in the header-table where the real data is stored for this row */ glx@9626: int offset = (dest_orig[y * 2 + 1] << 8) | dest_orig[y * 2]; glx@9626: /* Go to that row */ glx@9626: dest = &dest_orig[offset]; glx@9626: glx@9626: do { glx@9626: SpriteLoader::CommonPixel *data; glx@9626: /* Read the header: glx@9626: * 0 .. 14 - length glx@9626: * 15 - last_item glx@9626: * 16 .. 31 - transparency bytes */ glx@9626: last_item = ((*dest) & 0x80) != 0; glx@9626: int length = (*dest++) & 0x7F; glx@9626: int skip = *dest++; glx@9626: glx@9626: data = &sprite->data[y * sprite->width + skip]; glx@9626: glx@9626: for (int x = 0; x < length; x++) { glx@9626: data->m = *dest; glx@9626: dest++; glx@9626: data++; glx@9626: } glx@9626: } while (!last_item); glx@9626: } glx@9626: } else { glx@9626: dest = dest_orig; glx@9626: for (int i = 0; i < sprite->width * sprite->height; i++) glx@9626: sprite->data[i].m = dest[i]; glx@9626: } glx@9626: rubidium@9628: /* Make sure to mark all transparent pixels transparent on the alpha channel too */ rubidium@9628: for (int i = 0; i < sprite->width * sprite->height; i++) rubidium@9628: if (sprite->data[i].m != 0) sprite->data[i].a = 0xFF; rubidium@9628: glx@9626: free(dest_orig); glx@9626: return true; glx@9626: }