/* * System Interface Library for games * Copyright (c) 2007-2020 Andrew Church * Released under the GNU GPL version 3 or later; NO WARRANTY is provided. * See the file COPYING.txt for details. * * src/utility/tex-file.c: Utility routines for reading the SIL custom * texture file format. */ #include "src/base.h" #include "src/endian.h" #include "src/texture.h" #include "src/utility/tex-file.h" /*************************************************************************/ /************************** Interface routines ***************************/ /*************************************************************************/ int tex_parse_header(const void *data, int size, TexFileHeader *header_ret) { if (UNLIKELY(!data) || UNLIKELY(size < 0) || UNLIKELY(!header_ret)) { DLOG("Invalid parameters: %p %d %p", data, size, header_ret); return 0; } if (UNLIKELY((uintptr_t)data % 4 != 0)) { DLOG("Input pointer %p is not 4-byte aligned", data); return 0; } const TexFileHeader *header_in = (const TexFileHeader *)data; if (size < (int)sizeof(TexFileHeader)) { DLOG("File too small for TexFileHeader (%d < %d)", size, (int)sizeof(TexFileHeader)); return 0; } if (memcmp(header_in->magic, TEX_FILE_MAGIC, sizeof(header_in->magic)) != 0) { DLOG("TEX signature not found"); return 0; } uint8_t format; uint8_t mipmaps; uint8_t opaque_bitmap; int16_t width; int16_t height; float scale; int32_t pixels_offset; int32_t pixels_size; int32_t bitmap_offset; int32_t bitmap_size; if (header_in->version == TEX_FILE_VERSION) { format = header_in->format; mipmaps = header_in->mipmaps; opaque_bitmap = header_in->opaque_bitmap; width = be_to_s16(header_in->width); height = be_to_s16(header_in->height); scale = be_to_s32(header_in->scale_int) / 65536.0f; pixels_offset = be_to_u32(header_in->pixels_offset); pixels_size = be_to_u32(header_in->pixels_size); bitmap_offset = be_to_u32(header_in->bitmap_offset); bitmap_size = be_to_u32(header_in->bitmap_size); } else if (header_in->version == 1) { typedef struct TexFileHeaderV1 TexFileHeaderV1; struct TexFileHeaderV1 { char magic[4]; uint8_t version; uint8_t pad1[3]; int16_t width, height; uint8_t scale; uint8_t format; uint8_t mipmaps; uint8_t opaque_bitmap; int32_t pixels_offset; int32_t pixels_size; int32_t bitmap_offset; int32_t bitmap_size; }; const TexFileHeaderV1 *header_v1 = (const TexFileHeaderV1 *)data; width = be_to_s16(header_v1->width); height = be_to_s16(header_v1->height); scale = header_v1->scale / 16.0f; format = header_v1->format; mipmaps = header_v1->mipmaps; opaque_bitmap = header_v1->opaque_bitmap; pixels_offset = be_to_s32(header_v1->pixels_offset); pixels_size = be_to_s32(header_v1->pixels_size); bitmap_offset = be_to_s32(header_v1->bitmap_offset); bitmap_size = be_to_s32(header_v1->bitmap_size); switch (format) { case 0x01: format = TEX_FORMAT_PALETTE8_RGBA8888; break; case 0x02: format = TEX_FORMAT_A8; break; case 0x80: format = TEX_FORMAT_PVRTC2_RGB; break; case 0x81: format = TEX_FORMAT_PVRTC4_RGB; break; case 0x82: format = TEX_FORMAT_PVRTC2_RGBA; break; case 0x83: format = TEX_FORMAT_PVRTC4_RGBA; break; } } else { DLOG("Invalid version %u", header_in->version); return 0; } if (opaque_bitmap && bitmap_size != (width+7)/8 * height) { DLOG("Opaque bitmap data is wrong size (%d, should be %d for %dx%d" " texture)", bitmap_size, ((width+7)/8) * height, width, height); return 0; } if (pixels_offset < 0) { DLOG("Pixel data offset is negative (%d)", pixels_offset); return 0; } if (pixels_offset > size) { DLOG("Pixel data starts beyond end of file (%d > %d)", pixels_offset, size); return 0; } /* Note the format of this comparison! This avoids misbehavior in the * case that adding pixels_offset + pixels_size causes overflow. */ if (pixels_size > size - pixels_offset) { DLOG("Pixel data extends past end of file (%d + %d > %d)", pixels_offset, pixels_size, size); return 0; } if (bitmap_offset < 0) { DLOG("Bitmap data offset is negative (%d)", pixels_offset); return 0; } if (bitmap_offset > size) { DLOG("Opaque bitmap data starts beyond end of file (%d > %d)", bitmap_offset, size); return 0; } if (bitmap_size > size - bitmap_offset) { DLOG("Opaque bitmap data extends past end of file (%d + %d > %d)", bitmap_offset, bitmap_size, size); return 0; } memcpy(header_ret->magic, TEX_FILE_MAGIC, sizeof(header_ret->magic)); header_ret->version = TEX_FILE_VERSION; header_ret->format = format; header_ret->mipmaps = mipmaps; header_ret->opaque_bitmap = opaque_bitmap; header_ret->width = width; header_ret->height = height; header_ret->scale = scale; header_ret->pixels_offset = pixels_offset; header_ret->pixels_size = pixels_size; header_ret->bitmap_offset = bitmap_offset; header_ret->bitmap_size = bitmap_size; return 1; } /*************************************************************************/ /*************************************************************************/