275 lines
8.7 KiB
C
275 lines
8.7 KiB
C
//
|
|
// Created by rov on 12/27/25.
|
|
//
|
|
|
|
#include "objects.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <SDL3/SDL.h>
|
|
#include "window.h"
|
|
|
|
bool Objects_Init(Objects_t *objects) {
|
|
memset(objects->spriteTable, 0, sizeof(objects->spriteTable));
|
|
|
|
for (int i = 0; i < THING_DATA_POOL_SIZE; i++) {
|
|
objects->thingDataPool[i] = malloc(1536);
|
|
|
|
if (objects->thingDataPool[i]) {
|
|
memset(objects->thingDataPool[i], 0, 1536);
|
|
}
|
|
}
|
|
|
|
memset(objects->activeObjectList, 0, sizeof(objects->activeObjectList));
|
|
memset(objects->activeTextures, 0, sizeof(objects->activeTextures));
|
|
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_CUSTOM, "Objects_Init: memory allocated");
|
|
|
|
if (!Objects_LoadData(objects)) {
|
|
return false;
|
|
}
|
|
|
|
return Objects_LoadSprites(objects);
|
|
}
|
|
|
|
bool Objects_LoadData(Objects_t *objects) {
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_CUSTOM, "Objects_LoadData: loading object data.");
|
|
|
|
SDL_IOStream *file = SDL_IOFromFile("MUDOBJ.CLI", "rb");
|
|
if (!file) {
|
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Objects_LoadData: couldn't open file MUDOBJ.CLI",
|
|
NULL);
|
|
return false;
|
|
}
|
|
|
|
int objectsLoaded = 0;
|
|
while (true) {
|
|
uint16_t src;
|
|
uint8_t categoryID, thingID;
|
|
|
|
// Read IDs
|
|
if (SDL_ReadIO(file, &categoryID, 1) != 1) {
|
|
break;
|
|
}
|
|
|
|
if (SDL_ReadIO(file, &thingID, 1) != 1) {
|
|
break;
|
|
}
|
|
|
|
// Store header info
|
|
ThingHeader *header = (ThingHeader *) ((uint8_t *) objects->thingDataPool[categoryID] + thingID * 6);
|
|
|
|
SDL_ReadIO(file, &header->properties, 2);
|
|
SDL_ReadIO(file, &src, 2);
|
|
header->flags = src + 4;
|
|
|
|
// Calculate total sprites needed
|
|
const int countWidth = (src >> 4 & 3) + 1;
|
|
const int countHeight = (src >> 6 & 3) + 1;
|
|
const int countPattern = (src & 0xF) + 1;
|
|
const int countLayers = (src >> 8 & 1) + 1;
|
|
const int countAnim = (src >> 9 & 1) + 1;
|
|
const int countSides = (src >> 10 & 1) + 1;
|
|
|
|
// Total sprites to read for this object
|
|
const int totalSprites = countWidth * countHeight * countPattern * countLayers * countAnim * countSides;
|
|
const size_t bufferSize = sizeof(uint16_t) + totalSprites * sizeof(uint16_t);
|
|
|
|
uint16_t *destBuffer = objects->activeObjectList[categoryID];
|
|
if (destBuffer == NULL) {
|
|
destBuffer = (uint16_t *) malloc(bufferSize);
|
|
if (!destBuffer) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Objects_LoadData: cannot allocate memory");
|
|
break;
|
|
}
|
|
|
|
destBuffer[0] = 0;
|
|
objects->activeObjectList[categoryID] = destBuffer;
|
|
} else {
|
|
const size_t currentCount = destBuffer[0];
|
|
const size_t newTotalSize = sizeof(uint16_t) + (currentCount + totalSprites) * sizeof(uint16_t);
|
|
|
|
destBuffer = (uint16_t *) realloc(destBuffer, newTotalSize);
|
|
objects->activeObjectList[categoryID] = destBuffer;
|
|
}
|
|
|
|
header->spriteIndex = destBuffer[0];
|
|
|
|
// Read sprites
|
|
// Pointer to where we start writing new sprites: Buffer + 1 (Skip count) + Current Count
|
|
uint16_t *writePtr = &destBuffer[1 + destBuffer[0]];
|
|
const size_t readCount = SDL_ReadIO(file, writePtr, totalSprites * 2);
|
|
|
|
// Update the item count in the buffer header
|
|
// Note: SDL_ReadIO returns bytes, so we divide by 2 for item count
|
|
const uint16_t itemsRead = (uint16_t) (readCount / 2);
|
|
destBuffer[0] += itemsRead;
|
|
objectsLoaded += itemsRead;
|
|
}
|
|
|
|
SDL_CloseIO(file);
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_CUSTOM, "Objects_LoadData: data loaded (%d objects).", objectsLoaded);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Objects_LoadSprites(Objects_t *objects) {
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_CUSTOM, "Objects_LoadSprites: loading object sprites.");
|
|
SDL_Renderer* renderer = Window_GetRenderer();
|
|
|
|
// Free existing sprites to prevent memory leaks
|
|
for (int i = 0; i < SPRITE_TABLE_SIZE; i++) {
|
|
if (objects->spriteTable[i] != NULL) {
|
|
free(objects->spriteTable[i]);
|
|
objects->spriteTable[i] = NULL;
|
|
}
|
|
}
|
|
|
|
SDL_IOStream *file = SDL_IOFromFile("MUDOBJ.SPR", "rb");
|
|
if (!file) {
|
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Objects_LoadSprites: couldn't open file MUDOBJ.SPR.",
|
|
NULL);
|
|
return false;
|
|
}
|
|
|
|
uint16_t spriteCount = 0;
|
|
if (SDL_ReadIO(file, &spriteCount, 2) != 2) {
|
|
SDL_CloseIO(file);
|
|
|
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Objects_LoadSprites: couldn't get sprite count.",
|
|
NULL);
|
|
return false;
|
|
}
|
|
|
|
int spritesLoaded = 0;
|
|
for (int i = 0; i < spriteCount; i++) {
|
|
// Read ID (2 bytes)
|
|
uint16_t spriteID;
|
|
if (SDL_ReadIO(file, &spriteID, 2) != 2) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Objects_LoadSprites: couldn't read sprite ID.");
|
|
break;
|
|
}
|
|
|
|
uint16_t spriteSize;
|
|
if (SDL_ReadIO(file, &spriteSize, 2) != 2) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Objects_LoadSprites: couldn't read sprite size.");
|
|
break;
|
|
}
|
|
|
|
// Ensure ID is within bounds
|
|
if (spriteID >= SPRITE_TABLE_SIZE) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_CUSTOM, "Objects_LoadSprites: Sprite ID %d exceeds table size (%d). Skipping.",
|
|
spriteID, SPRITE_TABLE_SIZE);
|
|
|
|
// Skip the data bytes so we don't desync the file stream
|
|
SDL_SeekIO(file, spriteSize - 2, SDL_IO_SEEK_CUR);
|
|
continue;
|
|
}
|
|
|
|
uint8_t *spriteBuffer = malloc(spriteSize);
|
|
if (!spriteBuffer) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Objects_LoadSprites: cannot allocate memory");
|
|
break;
|
|
}
|
|
|
|
objects->spriteTable[spriteID] = spriteBuffer;
|
|
|
|
// Store size in the first 2 bytes of the buffer
|
|
*(uint16_t *) spriteBuffer = spriteSize;
|
|
|
|
// Read data
|
|
// Read (Size - 2) bytes into Buffer + 2
|
|
const size_t bytesToRead = spriteSize - 2;
|
|
if (bytesToRead > 0) {
|
|
if (SDL_ReadIO(file, spriteBuffer + 2, bytesToRead) != bytesToRead) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Objects_LoadSprites: couldn't read full sprite data");
|
|
break;
|
|
}
|
|
}
|
|
|
|
objects->spriteTable[spriteID] = spriteBuffer;
|
|
objects->activeTextures[spriteID] = Objects_TextureFromRaw(renderer, spriteBuffer);
|
|
|
|
spritesLoaded++;
|
|
}
|
|
|
|
SDL_CloseIO(file);
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_CUSTOM, "Objects_LoadSprites: %d sprites loaded.", spritesLoaded);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Objects_Destroy(const Objects_t *objects) {
|
|
for (int i = 0; i < THING_DATA_POOL_SIZE; i++) {
|
|
free(objects->thingDataPool[i]);
|
|
}
|
|
|
|
for (int i = 0; i < ACTIVE_OBJECT_LIST_SIZE; i++) {
|
|
if (objects->activeTextures[i]) {
|
|
SDL_DestroyTexture(objects->activeTextures[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
SDL_Texture* Objects_GetSpriteTexture(const Objects_t *objects, const uint16_t spriteID) {
|
|
if (spriteID >= SPRITE_TABLE_SIZE) {
|
|
return NULL;
|
|
}
|
|
|
|
return objects->activeTextures[spriteID];
|
|
}
|
|
|
|
SDL_Texture* Objects_TextureFromRaw(SDL_Renderer* renderer, uint8_t* rawData) {
|
|
if (!rawData) {
|
|
return NULL;
|
|
}
|
|
|
|
const uint16_t totalSize = *(uint16_t*) rawData;
|
|
uint8_t* data = rawData + 2;
|
|
int readOffset = 0;
|
|
|
|
SDL_Surface* surface = SDL_CreateSurface(32, 32, SDL_PIXELFORMAT_ARGB8888);
|
|
if (!surface) {
|
|
return NULL;
|
|
}
|
|
|
|
SDL_LockSurface(surface);
|
|
uint32_t* pixels = surface->pixels;
|
|
|
|
memset(pixels, 0, 32 * 32 * 4);
|
|
|
|
int currentPixel = 0;
|
|
while (readOffset < totalSize - 2 && currentPixel < 1024) {
|
|
const uint16_t transparentRaw = *(uint16_t*)(data + readOffset);
|
|
readOffset += 2;
|
|
|
|
const uint16_t transparentBytes = transparentRaw % 0x5A0;
|
|
const int skipPixels = transparentBytes / 3;
|
|
|
|
currentPixel += skipPixels;
|
|
|
|
const uint16_t colorBytes = *(uint16_t*)(data + readOffset);
|
|
readOffset += 2;
|
|
|
|
const int drawPixels = colorBytes / 3;
|
|
for (int i = 0; i < drawPixels; i++) {
|
|
if (currentPixel >= 1024) {
|
|
break;
|
|
}
|
|
|
|
const uint8_t r = data[readOffset++];
|
|
const uint8_t g = data[readOffset++];
|
|
const uint8_t b = data[readOffset++];
|
|
|
|
pixels[currentPixel++] = SDL_MapRGBA(SDL_GetPixelFormatDetails(surface->format), NULL, r, g, b, 255);
|
|
}
|
|
}
|
|
|
|
SDL_UnlockSurface(surface);
|
|
|
|
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
|
|
SDL_DestroySurface(surface);
|
|
|
|
return texture;
|
|
}
|