Load common object table and object placement data

This commit is contained in:
Lucas S. Vieira 2024-10-02 00:06:15 -03:00
parent 5812d18f13
commit d296245b32
11 changed files with 423 additions and 24 deletions

View file

@ -4,6 +4,8 @@
#include <stdint.h>
#include <psxgpu.h>
#include "object_state.h"
#define LEVEL_MAX_X_CHUNKS 255
#define LEVEL_MAX_Y_CHUNKS 31
#define LEVEL_ARENA_SIZE 131072
@ -51,10 +53,18 @@ typedef struct {
uint16_t *tiles;
} LevelLayerData;
#define MAX_OBJECTS_PER_CHUNK 15
typedef struct {
uint8_t num_objects;
ObjectState objects[MAX_OBJECTS_PER_CHUNK];
} ChunkObjectData;
typedef struct {
uint8_t num_layers;
uint8_t _unused0;
LevelLayerData *layers;
ChunkObjectData **objects;
uint16_t crectx, crecty;
uint16_t prectx, precty;
@ -75,4 +85,7 @@ void render_lvl(
int32_t cam_x, int32_t cam_y);
// Object-related. These are defined in object_state.c
void load_object_placement(const char *filename, LevelData *lvl);
#endif

View file

@ -2,22 +2,23 @@
#define MEMALLOC_H
#include <stdint.h>
#include <stdlib.h>
typedef struct {
uint32_t start;
uint32_t ptr;
uint32_t size;
uintptr_t start;
uintptr_t ptr;
size_t size;
} ArenaAllocator;
void alloc_arena_init(ArenaAllocator *arena, void *start, uint32_t size);
void alloc_arena_init(ArenaAllocator *arena, void *start, uintptr_t size);
void alloc_arena_free(ArenaAllocator *arena);
void *alloc_arena_malloc(ArenaAllocator *arena, uint32_t size);
void *alloc_arena_malloc(ArenaAllocator *arena, size_t size);
uint32_t alloc_arena_bytes_used(ArenaAllocator *arena);
uint32_t alloc_arena_bytes_free(ArenaAllocator *arena);
void fastalloc_init();
void fastalloc_free();
void *fastalloc_malloc(uint32_t size);
void *fastalloc_malloc(size_t size);
#endif

View file

@ -1,5 +1,5 @@
#ifndef OBJECT_H
#define OBJECT_H
#ifndef MODEL_H
#define MODEL_H
#include <stdint.h>
#include <psxgpu.h>

89
include/object.h Normal file
View file

@ -0,0 +1,89 @@
#ifndef OBJECT_H
#define OBJECT_H
#include <stdint.h>
#include "level.h"
/* ======================== */
/* OBJECT PROPERTY DATA */
/* ======================== */
typedef enum int8_t {
// Dummy objects
OBJ_DUMMY_RINGS_3V = -2,
OBJ_DUMMY_RINGS_3H = -1,
// Actual objects
OBJ_RING = 0x00,
OBJ_MONITOR = 0x01,
OBJ_SPIKES = 0x02,
OBJ_CHECKPOINT = 0x03,
OBJ_SPRING_YELLOW = 0x04,
OBJ_SPRING_RED = 0x05,
OBJ_SPRING_YELLOW_DIAGONAL = 0x06,
OBJ_SPRING_RED_DIAGONAL = 0x07,
OBJ_GOAL_SIGN = 0x08,
OBJ_SWITCH = 0x09,
} ObjectType;
#define MASK_FLIP_FLIPX 0x1 // Flip on X axis
#define MASK_FLIP_FLIPY 0x2 // Flip on Y axis
#define MASK_FLIP_ROTCW 0x4 // Rotated clockwise
#define MASK_FLIP_ROTCT 0x8 // Rotated counterclockwise
typedef enum uint8_t {
MONITOR_KIND_NONE = 0,
MONITOR_KIND_RING = 1,
MONITOR_KIND_SPEEDSHOES = 2,
MONITOR_KIND_SHIELD = 3,
MONITOR_KIND_INVINCIBILITY = 4,
MONITOR_KIND_1UP = 5,
MONITOR_KIND_SUPER = 6,
} ObjectMonitorKind;
/* ======================== */
/* OBJECT TABLE STRUCTURE */
/* ======================== */
typedef struct {
uint8_t u0, v0;
uint8_t w, h;
uint8_t flipmask;
} ObjectAnimFrame;
typedef struct {
ObjectAnimFrame *frames;
uint16_t num_frames;
int8_t loopback;
// TODO: number of animation frames?
} ObjectAnim;
typedef struct {
int16_t offsetx, offsety;
ObjectAnim *animations;
uint16_t num_animations;
} ObjectFrag;
// TODO: ObjectAnimState
typedef struct {
ObjectAnim *animations;
ObjectFrag *fragment;
// TODO: Global animation state here, could be NULL
uint16_t num_animations;
} ObjectTableEntry;
typedef struct {
uint8_t is_level_specific;
uint16_t num_entries;
ObjectTableEntry *entries;
} ObjectTable;
void load_object_table(const char *filename, ObjectTable *tbl);
void unload_object_table(ObjectTable *tbl);
#endif

22
include/object_state.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef OBJECT_STATE_H
#define OBJECT_STATE_H
#include <stdint.h>
/* ======================== */
/* OBJECT STATE STRUCTURE */
/* ======================== */
typedef struct {
uint8_t kind;
} MonitorExtra;
typedef struct {
uint16_t id;
uint8_t flipmask;
uint8_t props;
int16_t rx, ry; // Positions relative to chunk top-left corner
void *extra;
} ObjectState;
#endif

View file

@ -7,7 +7,7 @@
#define MAX_TILES 1400
static ArenaAllocator _level_arena = { 0 };
ArenaAllocator _level_arena = { 0 };
static uint8_t _arena_mem[LEVEL_ARENA_SIZE];
extern int debug_mode;
@ -174,18 +174,23 @@ load_lvl(LevelData *lvl, const char *filename)
lvl->num_layers = get_byte(bytes, &b);
lvl->_unused0 = 0; b += 1;
uint16_t max_tiles = 0;
lvl->layers = alloc_arena_malloc(&_level_arena, lvl->num_layers * sizeof(LevelLayerData));
for(uint8_t n_layer = 0; n_layer < lvl->num_layers; n_layer++) {
LevelLayerData *layer = &lvl->layers[n_layer];
layer->width = get_byte(bytes, &b);
layer->height = get_byte(bytes, &b);
uint16_t num_tiles = (uint16_t)layer->width * (uint16_t)layer->height;
if(num_tiles > max_tiles) max_tiles = num_tiles;
layer->tiles = alloc_arena_malloc(&_level_arena, num_tiles * sizeof(uint16_t));
for(uint16_t i = 0; i < num_tiles; i++) {
layer->tiles[i] = get_short_be(bytes, &b);
}
}
free(bytes);
// These values are static because we're always using the same
// coordinates for tpage and clut info. For some reason they're
// not loading correctly, but we don't really need them
@ -196,7 +201,12 @@ load_lvl(LevelData *lvl, const char *filename)
//lvl->clutmode = 0; // NOTE: This was set to tim->mode previously.
lvl->_unused1 = 0;
free(bytes);
// Initialize object state array within level map
printf("Allocating object array\n");
lvl->objects = alloc_arena_malloc(&_level_arena, max_tiles * sizeof(ChunkObjectData *));
for(uint16_t i = 0; i < max_tiles; i++) {
lvl->objects[i] = NULL;
}
}
// Level sprite buffer.

View file

@ -7,11 +7,12 @@ ArenaAllocator scratchpad_arena;
#define SCRATCHPAD_SIZE 1024
void
alloc_arena_init(ArenaAllocator *arena, void *start, uint32_t size)
alloc_arena_init(ArenaAllocator *arena, void *start, size_t size)
{
arena->start = (uint32_t)start;
uintptr_t st = (uintptr_t)start;
arena->start = st;
arena->ptr = arena->start;
arena->size = (uint32_t)size;
arena->size = size;
}
void
@ -21,12 +22,19 @@ alloc_arena_free(ArenaAllocator *arena)
}
void *
alloc_arena_malloc(ArenaAllocator *arena, uint32_t size)
alloc_arena_malloc(ArenaAllocator *arena, size_t size)
{
void *p = (void *)arena->ptr;
assert(arena->ptr + size < arena->start + arena->size);
arena->ptr += size;
return p;
// Align size so we don't get unaligned memory access
size += 8 - (size % 8);
uintptr_t p = (uintptr_t)arena->ptr;
uintptr_t align_diff = p % 8;
/* printf("Alotted size so far: %d / %d, requested: %lu\n", */
/* alloc_arena_bytes_used(arena), alloc_arena_bytes_free(arena), */
/* size); */
assert((arena->ptr + size) < (arena->start + arena->size));
arena->ptr += size + align_diff;
return (void *)p;
}
uint32_t
@ -54,7 +62,7 @@ fastalloc_free()
}
void *
fastalloc_malloc(uint32_t size)
fastalloc_malloc(size_t size)
{
return alloc_arena_malloc(&scratchpad_arena, size);
}

134
src/object.c Normal file
View file

@ -0,0 +1,134 @@
#include <stdio.h>
#include <stdlib.h>
#include "object.h"
#include "memalloc.h"
#include "util.h"
extern ArenaAllocator _level_arena;
void
_load_animation(ObjectAnim *animation, uint8_t *bytes, uint32_t *b)
{
animation->frames = NULL;
animation->num_frames = get_short_be(bytes, b);
animation->loopback = get_byte(bytes, b);
if(animation->num_frames > 0) {
animation->frames = alloc_arena_malloc(
&_level_arena,
sizeof(ObjectAnimFrame) * animation->num_frames);
for(uint16_t i = 0; i < animation->num_frames; i++) {
ObjectAnimFrame *frame = &animation->frames[i];
frame->u0 = get_byte(bytes, b);
frame->v0 = get_byte(bytes, b);
frame->w = get_byte(bytes, b);
frame->h = get_byte(bytes, b);
frame->flipmask = get_byte(bytes, b);
}
}
}
void
load_object_table(const char *filename, ObjectTable *tbl)
{
uint8_t *bytes;
uint32_t b, length;
bytes = file_read(filename, &length);
if(bytes == NULL) {
printf("Error reading OTD file %s from the CD.\n", filename);
return;
}
b = 0;
tbl->is_level_specific = get_byte(bytes, &b);
tbl->num_entries = get_short_be(bytes, &b);
if(tbl->num_entries == 0) goto end;
tbl->entries = alloc_arena_malloc(
&_level_arena,
sizeof(ObjectTableEntry) * tbl->num_entries);
printf("Entries at %p\n", tbl->entries);
for(uint16_t i = 0; i < tbl->num_entries; i++) {
printf("Reading entry %d.\n", i);
ObjectTableEntry *entry = &tbl->entries[i];
printf("Init animations and fragment\n");
entry->animations = NULL;
entry->fragment = NULL;
printf("OK\n");
uint8_t _id = get_byte(bytes, &b); // Entry ID, always sequential; discarded
printf("Registered id: %d\n", _id);
uint8_t has_fragment = get_byte(bytes, &b);
entry->num_animations = get_short_be(bytes, &b);
if(entry->num_animations > 0) {
printf("Start reading animations...\n");
entry->animations = alloc_arena_malloc(
&_level_arena,
sizeof(ObjectAnim) * entry->num_animations);
for(uint16_t j = 0; j < entry->num_animations; j++) {
printf("Reading animation %d.\n", j);
ObjectAnim *animation = &entry->animations[j];
_load_animation(animation, bytes, &b);
}
}
if(has_fragment) {
printf("Start reading fragment...\n");
ObjectFrag *fragment = alloc_arena_malloc(
&_level_arena,
sizeof(ObjectFrag));
entry->fragment = fragment;
fragment->offsetx = get_short_be(bytes, &b);
fragment->offsety = get_short_be(bytes, &b);
fragment->num_animations = get_short_be(bytes, &b);
fragment->animations = alloc_arena_malloc(
&_level_arena,
sizeof(ObjectAnim) * fragment->num_animations);
for(uint16_t j = 0; j < fragment->num_animations; j++) {
printf("Reading fragment animation %d.\n", j);
ObjectAnim *animation = &fragment->animations[j];
_load_animation(animation, bytes, &b);
}
}
}
end:
free(bytes);
printf("Loaded %d object types.\n", tbl->num_entries);
}
void
unload_object_table(ObjectTable *tbl)
{
// Since the object table has to be heap-allocated, we need to unload it
for(uint16_t i = 0; i < tbl->num_entries; i++) {
ObjectTableEntry *entry = &tbl->entries[i];
for(uint16_t j = 0; j < entry->num_animations; j++) {
ObjectAnim *animation = &entry->animations[j];
free(animation->frames);
}
if(entry->num_animations > 0) free(entry->animations);
if(entry->fragment) {
for(uint16_t j = 0; j < entry->fragment->num_animations; j++) {
ObjectAnim *animation = &entry->fragment->animations[j];
free(animation->frames);
}
if(entry->fragment->num_animations > 0)
free(entry->fragment->animations);
free(entry->fragment);
}
}
free(tbl->entries);
}

100
src/object_state.c Normal file
View file

@ -0,0 +1,100 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "object.h"
#include "object_state.h"
#include "util.h"
#include "memalloc.h"
extern ArenaAllocator _level_arena;
void
_emplace_object(
ChunkObjectData *data,
uint8_t is_level_specific,
int8_t type, uint8_t flipmask, int32_t vx, int32_t vy, void *extra)
{
ObjectState *state = &data->objects[data->num_objects++];
assert(data->num_objects < MAX_OBJECTS_PER_CHUNK);
state->id = type + (is_level_specific ? 100 : 0);
state->flipmask = flipmask;
state->rx = vx & 0x7f;
state->ry = vy & 0x7f;
state->extra = extra;
}
void
load_object_placement(const char *filename, LevelData *lvl)
{
uint8_t *bytes;
uint32_t b, length;
bytes = file_read(filename, &length);
if(bytes == NULL) {
printf("Error reading OTD file %s from the CD.\n", filename);
return;
}
b = 0;
uint16_t created_objects = 0;
uint16_t num_objects = get_short_be(bytes, &b);
for(uint16_t i = 0; i < num_objects; i++) {
uint8_t is_level_specific = get_byte(bytes, &b);
int8_t type = get_byte(bytes, &b);
uint8_t flipmask = get_byte(bytes, &b);
int32_t vx = get_long_be(bytes, &b);
int32_t vy = get_long_be(bytes, &b);
void *extra = NULL;
switch(type) {
default: break;
case OBJ_MONITOR:
extra = alloc_arena_malloc(&_level_arena, sizeof(MonitorExtra));
((MonitorExtra *)extra)->kind = get_byte(bytes, &b);
break;
}
// Get chunk at position
int32_t cx = vx >> 7;
int32_t cy = vy >> 7;
int32_t chunk_pos;
if((cx < 0) || (cx >= lvl->layers[0].width)) chunk_pos = -1;
else if((cy < 0) || (cy >= lvl->layers[0].height)) chunk_pos = -1;
else chunk_pos = (cy * lvl->layers[0].width) + cx;
ChunkObjectData *data = lvl->objects[chunk_pos];
if(!data) {
data = alloc_arena_malloc(&_level_arena, sizeof(ChunkObjectData));
lvl->objects[chunk_pos] = data;
}
if(type < 0) {
// This is a dummy object, so create others in its place.
switch(type) {
case OBJ_DUMMY_RINGS_3V:
_emplace_object(data, 0, OBJ_RING, 0, vx, vy - 24, NULL);
_emplace_object(data, 0, OBJ_RING, 0, vx, vy, NULL);
_emplace_object(data, 0, OBJ_RING, 0, vx, vy + 24, NULL);
created_objects += 3;
break;
case OBJ_DUMMY_RINGS_3H:
_emplace_object(data, 0, OBJ_RING, 0, vx - 24, vy, NULL);
_emplace_object(data, 0, OBJ_RING, 0, vx, vy, NULL);
_emplace_object(data, 0, OBJ_RING, 0, vx + 24, vy, NULL);
created_objects += 3;
break;
default: break;
}
} else {
_emplace_object(data, is_level_specific, type, flipmask, vx, vy, extra);
created_objects++;
}
}
printf("Loaded %d objects.\n", created_objects);
free(bytes);
}

View file

@ -23,7 +23,7 @@ void
screen_disclaimer_load()
{
screen_disclaimer_data *data = screen_alloc(sizeof(screen_disclaimer_data));
uint32_t length;
uint32_t length; // 153624 B
data->disclaimer_bg = file_read("\\MISC\\DISK.TIM;1", &length);
data->disclaimer_timer = 0;
}

View file

@ -15,6 +15,7 @@
#include "level.h"
#include "timer.h"
#include "model.h"
#include "object.h"
extern int debug_mode;
@ -24,10 +25,11 @@ static uint8_t music_channel = 0;
static Player player;
TileMap16 map16;
TileMap128 map128;
LevelData leveldata;
Camera camera;
TileMap16 map16;
TileMap128 map128;
LevelData leveldata;
Camera camera;
ObjectTable obj_table_common;
#define CHANNELS_PER_BGM 3
static uint32_t bgm_loop_sectors[] = {
@ -73,6 +75,7 @@ static uint32_t bgm_loop_sectors[] = {
// Forward function declarations
static void level_load_player();
static void level_load_level();
static void level_unload_level();
// TODO!!!
typedef struct {
@ -99,6 +102,7 @@ screen_level_unload(void *)
{
sound_stop_xa();
level_reset();
level_unload_level();
sound_reset_mem();
screen_free();
}
@ -356,6 +360,17 @@ level_load_level()
printf("Loading %s...\n", filename0);
load_lvl(&leveldata, filename0);
// Load common objects
printf("Loading common object table...\n");
load_object_table("\\LEVELS\\COMMON\\OBJ.OTD", &obj_table_common);
// Load level objects
// TODO
// Load object positioning on level
snprintf(filename0, 255, "%s\\Z%1u.OMP;1", basepath, (level & 0x01) + 1);
load_object_placement(filename0, &leveldata);
// Pre-allocate and initialize level primitive buffer
prepare_renderer(&leveldata);
@ -376,3 +391,10 @@ level_load_level()
sound_play_xa(filename0, 0, music_channel, bgm_loop_sectors[level]);
}
static void
level_unload_level()
{
// Object tables are heap-allocated
//unload_object_table(&obj_table_common);
}