joeylib2/src/core/asset.c

158 lines
3.8 KiB
C

// Asset loader: parses .jas blobs into JoeyAssetT, with companion
// helpers for file I/O and palette application.
//
// The .jas format is little-endian byte-stream so the same binary
// works on every target -- 6502/x86 (LE) read it directly, 68k (BE)
// reassembles via byte-by-byte decode in jasDecodeWord.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "joey/asset.h"
#include "joey/palette.h"
#define JAS_HEADER_SIZE 44
#define JAS_PIXELS_OFFSET JAS_HEADER_SIZE
#define JAS_PALETTE_OFFSET 12
#define JAS_PALETTE_ENTRIES 16
#define JAS_MAX_BYTES (1L << 24) // 16MB sanity cap on file size
// ----- Prototypes -----
static uint16_t jasDecodeWord(const uint8_t *p);
static bool jasMagicMatches(const uint8_t *data);
// ----- Internal helpers -----
static uint16_t jasDecodeWord(const uint8_t *p) {
return (uint16_t)((uint16_t)p[0] | ((uint16_t)p[1] << 8));
}
static bool jasMagicMatches(const uint8_t *data) {
return data[0] == 'J' && data[1] == 'A' && data[2] == 'S' && data[3] == '1';
}
// ----- Public API (alphabetical) -----
void joeyAssetApplyPalette(SurfaceT *dst, uint8_t paletteIndex, const JoeyAssetT *asset) {
if (dst == NULL || asset == NULL) {
return;
}
if (!asset->hasPalette) {
return;
}
paletteSet(dst, paletteIndex, asset->palette);
}
void joeyAssetFree(JoeyAssetT *asset) {
if (asset == NULL) {
return;
}
// The const cast is intentional: loader-owned assets carry
// pixels we allocated. Static / embedded assets point at .rodata
// and the contract documents that they must not be passed here.
free((void *)asset->pixels);
free(asset);
}
JoeyAssetT *joeyAssetFromMem(const uint8_t *data, uint32_t length) {
JoeyAssetT *asset;
uint8_t *pixelBuf;
uint32_t rowBytes;
uint32_t pixelLen;
uint16_t i;
if (data == NULL || length < JAS_HEADER_SIZE) {
return NULL;
}
if (!jasMagicMatches(data)) {
return NULL;
}
asset = (JoeyAssetT *)malloc(sizeof(JoeyAssetT));
if (asset == NULL) {
return NULL;
}
memset(asset, 0, sizeof(*asset));
asset->width = jasDecodeWord(data + 4);
asset->height = jasDecodeWord(data + 6);
asset->hasPalette = data[8] != 0;
for (i = 0; i < JAS_PALETTE_ENTRIES; i++) {
asset->palette[i] = jasDecodeWord(data + JAS_PALETTE_OFFSET + (i * 2));
}
if (asset->width == 0 || asset->height == 0) {
free(asset);
return NULL;
}
rowBytes = (uint32_t)((asset->width + 1) >> 1);
pixelLen = rowBytes * (uint32_t)asset->height;
if (length < (JAS_PIXELS_OFFSET + pixelLen)) {
free(asset);
return NULL;
}
pixelBuf = (uint8_t *)malloc(pixelLen);
if (pixelBuf == NULL) {
free(asset);
return NULL;
}
memcpy(pixelBuf, data + JAS_PIXELS_OFFSET, pixelLen);
asset->pixels = pixelBuf;
return asset;
}
JoeyAssetT *joeyAssetLoadFile(const char *path) {
FILE *fp;
uint8_t *buf;
long size;
size_t readBytes;
JoeyAssetT *asset;
if (path == NULL) {
return NULL;
}
fp = fopen(path, "rb");
if (fp == NULL) {
return NULL;
}
if (fseek(fp, 0L, SEEK_END) != 0) {
fclose(fp);
return NULL;
}
size = ftell(fp);
if (size <= 0 || size > JAS_MAX_BYTES) {
fclose(fp);
return NULL;
}
if (fseek(fp, 0L, SEEK_SET) != 0) {
fclose(fp);
return NULL;
}
buf = (uint8_t *)malloc((size_t)size);
if (buf == NULL) {
fclose(fp);
return NULL;
}
readBytes = fread(buf, 1, (size_t)size, fp);
fclose(fp);
if (readBytes != (size_t)size) {
free(buf);
return NULL;
}
asset = joeyAssetFromMem(buf, (uint32_t)size);
free(buf);
return asset;
}