158 lines
3.8 KiB
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;
|
|
}
|