townengine/src/twn_util.c

304 lines
7.7 KiB
C

#include "twn_util.h"
#include "twn_util_c.h"
#include "twn_engine_context_c.h"
#include <SDL2/SDL.h>
#include <physfsrwops.h>
#define STB_DS_IMPLEMENTATION
#define STBDS_ASSERT SDL_assert
#define STBDS_REALLOC(context,ptr,size) ((void)(context), SDL_realloc(ptr, size))
#define STBDS_FREE(context,ptr) ((void)(context), SDL_free(ptr))
#include <stb_ds.h>
#define STB_RECT_PACK_IMPLEMENTATION
#define STBRP_SORT SDL_qsort
#define STBRP_ASSERT SDL_assert
#include <stb_rect_pack.h>
#define STB_TRUETYPE_IMPLEMENTATION
#define STBTT_malloc(x,u) ((void)(u), SDL_malloc(x))
#define STBTT_free(x,u) ((void)(u), SDL_free(x))
#define STBTT_assert(x) SDL_assert(x)
#define STBTT_strlen(x) SDL_strlen(x)
#define STBTT_memcpy SDL_memcpy
#define STBTT_memset SDL_memset
#include <stb_truetype.h>
#include <stdarg.h>
void cry_impl(const char *file, const int line, const char *title, const char *text) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION,
"TEARS AT %s:%d: %s ... %s", file, line, title, text);
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, text, NULL);
}
static void log_impl(const char *restrict format, va_list args, SDL_LogPriority priority) {
SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION,
priority,
format,
args);
}
void log_info(const char *restrict format, ...) {
va_list args;
va_start(args, format);
log_impl(format, args, SDL_LOG_PRIORITY_INFO);
va_end(args);
}
void log_critical(const char *restrict format, ...) {
va_list args;
va_start(args, format);
log_impl(format, args, SDL_LOG_PRIORITY_CRITICAL);
va_end(args);
}
void log_warn(const char *restrict format, ...) {
va_list args;
va_start(args, format);
log_impl(format, args, SDL_LOG_PRIORITY_WARN);
va_end(args);
}
_Noreturn static void alloc_failure_death(void) {
log_critical("Allocation failure. Aborting NOW.");
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
"MEMORY ALLOCATION FAILURE",
"CRITICAL MEMORY ALLOCATION FAILED. "
"EXITING IMMEDIATELY!",
NULL);
die_abruptly();
}
_Noreturn void die_abruptly(void) {
/* a zombie window will linger if we don't at least try to quit SDL */
SDL_Quit();
abort();
}
void *cmalloc(size_t size) {
void *ptr = SDL_malloc(size);
if (ptr == NULL)
alloc_failure_death();
return ptr;
}
void *crealloc(void *ptr, size_t size) {
void *out = SDL_realloc(ptr, size);
if (out == NULL)
alloc_failure_death();
return out;
}
void *ccalloc(size_t num, size_t size) {
void *ptr = SDL_calloc(num, size);
if (ptr == NULL)
alloc_failure_death();
return ptr;
}
double clamp(double d, double min, double max) {
const double t = d < min ? min : d;
return t > max ? max : t;
}
float clampf(float f, float min, float max) {
const float t = f < min ? min : f;
return t > max ? max : t;
}
int clampi(int i, int min, int max) {
const int t = i < min ? min : i;
return t > max ? max : t;
}
int64_t file_to_bytes(const char *path, unsigned char **buf_out) {
SDL_RWops *handle = PHYSFSRWOPS_openRead(path);
if (handle == NULL) {
return -1;
}
int64_t data_size = SDL_RWseek(handle, 0, RW_SEEK_END);
SDL_RWseek(handle, 0, RW_SEEK_SET); /* reset offset into data */
*buf_out = cmalloc(data_size);
SDL_RWread(handle, *buf_out, sizeof **buf_out, data_size / sizeof **buf_out);
SDL_RWclose(handle); /* we got all we needed from the stream */
return data_size;
}
char *file_to_str(const char *path) {
SDL_RWops *handle = PHYSFSRWOPS_openRead(path);
if (handle == NULL) {
return NULL;
}
int64_t data_size = SDL_RWseek(handle, 0, RW_SEEK_END);
SDL_RWseek(handle, 0, RW_SEEK_SET); /* reset offset into data */
char *str_out = cmalloc(data_size + 1); /* data plus final null */
size_t len = data_size / sizeof *str_out;
SDL_RWread(handle, str_out, sizeof *str_out, len);
SDL_RWclose(handle); /* we got all we needed from the stream */
str_out[len] = '\0';
return str_out;
}
bool file_exists(const char *path) {
return PHYSFS_exists(path);
}
void textures_dump_atlases(void) {
PHYSFS_mkdir("/dump");
/* TODO: png instead of bmp */
const char string_template[] = "/dump/atlas%zd.bmp";
size_t i = 0;
for (; i < arrlenu(ctx.texture_cache.atlas_surfaces); ++i) {
char *buf = NULL;
SDL_asprintf(&buf, string_template, i);
SDL_RWops *handle = PHYSFSRWOPS_openWrite(buf);
if (handle == NULL) {
CRY("Texture atlas dump failed.", "File could not be opened");
return;
}
SDL_SaveBMP_RW(ctx.texture_cache.atlas_surfaces[i], handle, true);
log_info("Dumped atlas %zu to %s", i, buf);
SDL_free(buf);
}
}
bool strends(const char *str, const char *suffix) {
size_t str_length = SDL_strlen(str);
size_t suffix_length = SDL_strlen(suffix);
if (suffix_length > str_length)
return false;
return SDL_memcmp((str + str_length) - suffix_length, suffix, suffix_length) == 0;
}
bool overlap_rect(const Recti *a, const Recti *b, Recti *result) {
SDL_Rect a_sdl = { a->x, a->y, a->w, a->h };
SDL_Rect b_sdl = { b->x, b->y, b->w, b->h };
SDL_Rect result_sdl = { 0 };
bool intersection = SDL_IntersectRect(&a_sdl, &b_sdl, &result_sdl);
if (result != NULL)
*result = (Recti){ result_sdl.x, result_sdl.y, result_sdl.w, result_sdl.h };
return intersection;
}
bool overlap_frect(const Rect *a, const Rect *b, Rect *result) {
SDL_FRect a_sdl = { a->x, a->y, a->w, a->h };
SDL_FRect b_sdl = { b->x, b->y, b->w, b->h };
SDL_FRect result_sdl = { 0 };
bool intersection = SDL_IntersectFRect(&a_sdl, &b_sdl, &result_sdl);
if (result != NULL)
*result = (Rect){ result_sdl.x, result_sdl.y, result_sdl.w, result_sdl.h };
return intersection;
}
bool intersect_rect(const Recti *a, const Recti *b) {
SDL_Rect a_sdl = { a->x, a->y, a->w, a->h };
SDL_Rect b_sdl = { b->x, b->y, b->w, b->h };
return SDL_HasIntersection(&a_sdl, &b_sdl);
}
bool intersect_frect(const Rect *a, const Rect *b) {
SDL_FRect a_sdl = { a->x, a->y, a->w, a->h };
SDL_FRect b_sdl = { b->x, b->y, b->w, b->h };
return SDL_HasIntersectionF(&a_sdl, &b_sdl);
}
Rect to_frect(Recti rect) {
return (Rect) {
.h = (float)rect.h,
.w = (float)rect.w,
.x = (float)rect.x,
.y = (float)rect.y,
};
}
Vec2 frect_center(Rect rect) {
return (Vec2){
.x = rect.x + rect.w / 2,
.y = rect.y + rect.h / 2,
};
}
void tick_timer(int *value) {
*value = MAX(*value - 1, 0);
}
void tick_ftimer(float *value) {
*value = MAX(*value - ((float)ctx.game.delta_time / (float)ctx.clocks_per_second), 0.0f);
}
bool repeat_ftimer(float *value, float at) {
*value -= (float)ctx.game.delta_time / (float)ctx.clocks_per_second;
if (*value < 0.0f) {
*value += at;
return true;
}
return false;
}
/* TODO: handle utf8 */
char *expand_asterisk(const char *mask, const char *to) {
const char *offset = SDL_strchr(mask, '*');
if (!offset) {
CRY("Invalid path", "Asterisk should be given.");
return NULL;
}
size_t const mask_len = SDL_strlen(mask);
size_t const to_len = SDL_strlen(to);
char *str = SDL_malloc(mask_len + to_len); /* NULL included, replacing the original asterisk */
SDL_memcpy(str, mask, offset - mask);
SDL_memcpy(str + (offset - mask), to, to_len);
/* NULL byte is copied from here */
SDL_memcpy(str + (offset - mask) + to_len, offset + 1, mask_len - (offset - mask));
return str;
}