diff options
Diffstat (limited to '')
-rw-r--r-- | cards.c | 137 |
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; +} |