summary refs log tree commit diff
path: root/cards.c
diff options
context:
space:
mode:
Diffstat (limited to 'cards.c')
-rw-r--r--cards.c214
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);
 }