diff options
Diffstat (limited to 'cards.c')
-rw-r--r-- | cards.c | 214 |
1 files changed, 129 insertions, 85 deletions
diff --git a/cards.c b/cards.c index 52445a1..6369d40 100644 --- a/cards.c +++ b/cards.c @@ -14,124 +14,168 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <errno.h> #include <stdlib.h> #include <string.h> #include "cards.h" -enum { - MZ_NEOff = 0x3C, +static struct SDL_Surface * +dibSurface(struct SDL_RWops *rw, Uint32 offset, Uint32 length) { + Uint32 bitmapLength = 0x0E + length; - NE_ResTableOff = 0x24, - - ResTable_AlignShift = 0x00, - ResTable_ResBlock = 0x02, - - ResBlock_TypeID = 0x00, - ResBlock_ResCount = 0x02, - ResBlock_Resource = 0x08, - - TypeID_Bitmap = 0x8002, + void *buffer = malloc(bitmapLength); + if (!buffer) { + SDL_SetError("malloc error: %s", strerror(errno)); + return NULL; + } - Resource_Off = 0x00, - Resource_Len = 0x02, - Resource_ID = 0x06, - Resource_Next = 0x0C, + struct SDL_RWops *bitmap = SDL_RWFromMem(buffer, bitmapLength); + if (!bitmap) { + free(buffer); + return NULL; + } - IDMask = 0x7FFF, + if (SDL_RWseek(rw, offset, RW_SEEK_SET) < 0) goto fail; + Uint32 dibHeaderLength = SDL_ReadLE32(rw); + Uint32 bitmapDataOffset = 0x0E + dibHeaderLength; - BMPHeader_FileLen = 0x02, - BMPHeader_Reserved = 0x06, - BMPHeader_DataOff = 0x0A, - BMPHeader_DIBHeader = 0x0E, + if (dibHeaderLength == 0x0C) { + if (SDL_RWseek(rw, 0x06, RW_SEEK_CUR) < 0) goto fail; + Uint16 bitsPerPixel = SDL_ReadLE16(rw); + bitmapDataOffset += 3 * (1 << bitsPerPixel); - DIBHeader_Len = 0x00, - DIBHeaderCore_Bits = 0x0A, + } else if (dibHeaderLength == 0x28) { + if (SDL_RWseek(rw, 0x0A, RW_SEEK_CUR) < 0) goto fail; + Uint16 bitsPerPixel = SDL_ReadLE16(rw); + if (SDL_RWseek(rw, 0x10, RW_SEEK_CUR) < 0) goto fail; + Uint32 paletteLength = SDL_ReadLE32(rw); - DIBHeaderCoreLen = 0x0C, -}; + if (!paletteLength) paletteLength = 1 << bitsPerPixel; + bitmapDataOffset += 4 * paletteLength; -static uint16_t read16(const uint8_t *ptr, size_t off) { - return (uint16_t)ptr[off] | (uint16_t)ptr[off + 1] << 8; -} -static uint32_t read32(const uint8_t *ptr, size_t off) { - return (uint32_t)ptr[off] - | (uint32_t)ptr[off + 1] << 8 - | (uint32_t)ptr[off + 2] << 16 - | (uint32_t)ptr[off + 3] << 24; -} + } else { + SDL_SetError("unrecognized DIB header length %u", dibHeaderLength); + goto fail; + } -static void write32(uint8_t *ptr, size_t off, uint32_t val) { - ptr[off + 0] = val & 0xFF; - ptr[off + 1] = val >> 8 & 0xFF; - ptr[off + 2] = val >> 16 & 0xFF; - ptr[off + 3] = val >> 24; + SDL_WriteU8(bitmap, 'B'); + SDL_WriteU8(bitmap, 'M'); + SDL_WriteLE32(bitmap, bitmapLength); + SDL_WriteLE16(bitmap, 0); // reserved + SDL_WriteLE16(bitmap, 0); // reserved + SDL_WriteLE32(bitmap, bitmapDataOffset); + + if (SDL_RWseek(rw, offset, RW_SEEK_SET) < 0) goto fail; + if (SDL_RWread(rw, &buffer[SDL_RWtell(bitmap)], length, 1) < 1) goto fail; + + SDL_RWseek(bitmap, 0, RW_SEEK_SET); + SDL_Surface *surface = SDL_LoadBMP_RW(bitmap, 1); + free(buffer); + return surface; + +fail: + SDL_RWclose(bitmap); + free(buffer); + return NULL; } -int cardsLoad(const uint8_t *ptr, size_t len) { - if (len < MZ_NEOff + 2) return -1; - if (ptr[0] != 'M' || ptr[1] != 'Z') return -1; +struct Cards *Cards_Load(struct SDL_RWops *rw) { + struct Cards *cards = calloc(1, sizeof(*cards)); + if (!cards) { + SDL_SetError("calloc error: %s", strerror(errno)); + return NULL; + } - uint16_t neOff = read16(ptr, MZ_NEOff); + if (SDL_RWseek(rw, 0, RW_SEEK_SET) < 0) goto fail; + if (SDL_ReadU8(rw) != 'M' || SDL_ReadU8(rw) != 'Z') { + SDL_SetError("invalid MZ signature"); + goto fail; + } - if (len < neOff + NE_ResTableOff + 2) return -1; - if (ptr[neOff + 0] != 'N' || ptr[neOff + 1] != 'E') return -1; + if (SDL_RWseek(rw, 0x3C, RW_SEEK_SET) < 0) goto fail; + Uint16 neOffset = SDL_ReadLE16(rw); - uint16_t resTableOff = neOff + read16(ptr, neOff + NE_ResTableOff); + if (SDL_RWseek(rw, neOffset, RW_SEEK_SET) < 0) goto fail; + if (SDL_ReadU8(rw) != 'N' || SDL_ReadU8(rw) != 'E') { + SDL_SetError("invalid NE signature"); + goto fail; + } - if (len < resTableOff + ResTable_ResBlock) return -1; + if (SDL_RWseek(rw, neOffset + 0x24, RW_SEEK_SET) < 0) goto fail; + Uint16 resourceTableOffset = neOffset + SDL_ReadLE16(rw); - uint16_t alignShift = read16(ptr, resTableOff + ResTable_AlignShift); + if (SDL_RWseek(rw, resourceTableOffset, RW_SEEK_SET) < 0) goto fail; + Uint16 alignmentShift = SDL_ReadLE16(rw); - uint16_t resBlockOff = resTableOff + ResTable_ResBlock; - uint16_t resCount; + Uint16 resourceCount; for (;;) { - if (len < resBlockOff + ResBlock_Resource) return -1; - - uint16_t typeID = read16(ptr, resBlockOff + ResBlock_TypeID); - resCount = read16(ptr, resBlockOff + ResBlock_ResCount); + Uint16 typeID = SDL_ReadLE16(rw); + resourceCount = SDL_ReadLE16(rw); + SDL_ReadLE32(rw); // reserved - if (!typeID) return -1; - if (typeID == TypeID_Bitmap) break; + if (!typeID) { + SDL_SetError("no bitmap resources"); + goto fail; + } + if (typeID == 0x8002) break; - resBlockOff += ResBlock_Resource + resCount * Resource_Next; + if (SDL_RWseek(rw, 0x0C * resourceCount, RW_SEEK_CUR) < 0) goto fail; } - uint16_t resOff = resBlockOff + ResBlock_Resource; - for (uint16_t i = 0; i < resCount; ++i, resOff += Resource_Next) { - if (len < resOff + Resource_Next) return -1; + for (Uint16 i = 0; i < resourceCount; ++i) { + Uint16 offset = SDL_ReadLE16(rw); + Uint16 length = SDL_ReadLE16(rw); + SDL_ReadLE16(rw); // flags + Uint16 id = SDL_ReadLE16(rw); + SDL_ReadLE32(rw); // reserved - uint16_t id = read16(ptr, resOff + Resource_ID); - if ((id & IDMask) >= CardsLen) continue; + id &= 0x7FFF; + if (id >= Cards_CardLen) continue; - size_t dataOff = read16(ptr, resOff + Resource_Off); - size_t dataLen = read16(ptr, resOff + Resource_Len); - dataOff <<= alignShift; - dataLen <<= alignShift; + Sint64 next = SDL_RWtell(rw); + if (next < 0) goto fail; - if (len < dataOff + dataLen) return -1; - if (dataLen < DIBHeaderCoreLen) return -1; + cards->surfaces[id] = dibSurface( + rw, + (Uint32)offset << alignmentShift, + (Uint32)length << alignmentShift + ); + if (!cards->surfaces[id]) goto fail; + + if (SDL_RWseek(rw, next, RW_SEEK_SET) < 0) goto fail; + } - uint32_t dibHeaderLen = read32(ptr, dataOff + DIBHeader_Len); - uint32_t bmpFileLen = BMPHeader_DIBHeader + dataLen; - uint32_t bmpDataOff = BMPHeader_DIBHeader + dibHeaderLen; - if (dibHeaderLen == DIBHeaderCoreLen) { - bmpDataOff += 3 * (1 << read16(ptr, dataOff + DIBHeaderCore_Bits)); + int suits[4] = { Cards_Club, Cards_Diamond, Cards_Heart, Cards_Spade }; + for (int suit = 0; suit < 4; ++suit) { + for (int rank = Cards_A; rank <= Cards_K; ++rank) { + if (cards->surfaces[suits[suit] + rank]) continue; + SDL_SetError("missing resource %d", suits[suit] + rank); + goto fail; } + } + for (int i = Cards_Empty; i <= Cards_Back12; ++i) { + if (cards->surfaces[i]) continue; + SDL_SetError("missing resource %d", i); + goto fail; + } + for (int i = Cards_X; i <= Cards_O; ++i) { + if (cards->surfaces[i]) continue; + SDL_SetError("missing resource %d", i); + goto fail; + } - uint8_t *bitmap = malloc(bmpFileLen); - if (!bitmap) return -1; + return cards; - bitmap[0] = 'B'; - bitmap[1] = 'M'; - write32(bitmap, BMPHeader_FileLen, bmpFileLen); - write32(bitmap, BMPHeader_Reserved, 0); - write32(bitmap, BMPHeader_DataOff, bmpDataOff); - memcpy(&bitmap[BMPHeader_DIBHeader], &ptr[dataOff], dataLen); +fail: + Cards_Free(cards); + return NULL; +} - cardsData[id & IDMask].ptr = bitmap; - cardsData[id & IDMask].len = bmpFileLen; +void Cards_Free(struct Cards *cards) { + for (int i = 0; i < Cards_CardLen; ++i) { + if (!cards->surfaces[i]) continue; + SDL_FreeSurface(cards->surfaces[i]); } - - return 0; + free(cards); } |