about summary refs log tree commit diff
path: root/cards.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--cards.c137
1 files changed, 137 insertions, 0 deletions
diff --git a/cards.c b/cards.c
new file mode 100644
index 0000000..52445a1
--- /dev/null
+++ b/cards.c
@@ -0,0 +1,137 @@
+/* Copyright (C) 2019  C. McEnroe <june@causal.agency>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "cards.h"
+
+enum {
+	MZ_NEOff = 0x3C,
+
+	NE_ResTableOff = 0x24,
+
+	ResTable_AlignShift = 0x00,
+	ResTable_ResBlock = 0x02,
+
+	ResBlock_TypeID = 0x00,
+	ResBlock_ResCount = 0x02,
+	ResBlock_Resource = 0x08,
+
+	TypeID_Bitmap = 0x8002,
+
+	Resource_Off = 0x00,
+	Resource_Len = 0x02,
+	Resource_ID = 0x06,
+	Resource_Next = 0x0C,
+
+	IDMask = 0x7FFF,
+
+	BMPHeader_FileLen = 0x02,
+	BMPHeader_Reserved = 0x06,
+	BMPHeader_DataOff = 0x0A,
+	BMPHeader_DIBHeader = 0x0E,
+
+	DIBHeader_Len = 0x00,
+	DIBHeaderCore_Bits = 0x0A,
+
+	DIBHeaderCoreLen = 0x0C,
+};
+
+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;
+}
+
+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;
+}
+
+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;
+
+	uint16_t neOff = read16(ptr, MZ_NEOff);
+
+	if (len < neOff + NE_ResTableOff + 2) return -1;
+	if (ptr[neOff + 0] != 'N' || ptr[neOff + 1] != 'E') return -1;
+
+	uint16_t resTableOff = neOff + read16(ptr, neOff + NE_ResTableOff);
+
+	if (len < resTableOff + ResTable_ResBlock) return -1;
+
+	uint16_t alignShift = read16(ptr, resTableOff + ResTable_AlignShift);
+
+	uint16_t resBlockOff = resTableOff + ResTable_ResBlock;
+	uint16_t resCount;
+	for (;;) {
+		if (len < resBlockOff + ResBlock_Resource) return -1;
+
+		uint16_t typeID = read16(ptr, resBlockOff + ResBlock_TypeID);
+		resCount = read16(ptr, resBlockOff + ResBlock_ResCount);
+
+		if (!typeID) return -1;
+		if (typeID == TypeID_Bitmap) break;
+
+		resBlockOff += ResBlock_Resource + resCount * Resource_Next;
+	}
+
+	uint16_t resOff = resBlockOff + ResBlock_Resource;
+	for (uint16_t i = 0; i < resCount; ++i, resOff += Resource_Next) {
+		if (len < resOff + Resource_Next) return -1;
+
+		uint16_t id = read16(ptr, resOff + Resource_ID);
+		if ((id & IDMask) >= CardsLen) continue;
+
+		size_t dataOff = read16(ptr, resOff + Resource_Off);
+		size_t dataLen = read16(ptr, resOff + Resource_Len);
+		dataOff <<= alignShift;
+		dataLen <<= alignShift;
+
+		if (len < dataOff + dataLen) return -1;
+		if (dataLen < DIBHeaderCoreLen) return -1;
+
+		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));
+		}
+
+		uint8_t *bitmap = malloc(bmpFileLen);
+		if (!bitmap) return -1;
+
+		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);
+
+		cardsData[id & IDMask].ptr = bitmap;
+		cardsData[id & IDMask].len = bmpFileLen;
+	}
+
+	return 0;
+}