asset loading frame

This commit is contained in:
zlago 2025-11-05 21:50:08 +01:00
parent 180026281e
commit 857f368970
5 changed files with 386 additions and 5 deletions

View File

@ -5,11 +5,11 @@ MKDIR ?= mkdir -p
EXTENSION ?= out EXTENSION ?= out
CFLAGS ?= -Wall -Wpedantic -Wextra -g -Og CFLAGS ?= -Wall -Wpedantic -Wextra -g -Og
CXXFLAGS ?= -Wall -Wpedantic -Wextra -g -Og CXXFLAGS ?= -std=c++20 -Wall -Wpedantic -Wextra -g -Og
LDFLAGS ?= LDFLAGS ?=
NAME := suwi NAME := suwi
libs := m portaudio SDL3 libs := m z portaudio SDL3
includes := src/ includes := src/
cflags := $(addprefix -I,${includes}) ${CFLAGS} cflags := $(addprefix -I,${includes}) ${CFLAGS}
cxxflags := $(addprefix -I,${includes}) ${CXXFLAGS} cxxflags := $(addprefix -I,${includes}) ${CXXFLAGS}

25
src/ass.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#if defined(__cplusplus)
extern "C" {
#define restrict
#endif
#include <stdio.h>
enum ass_status {
ASS_OK, // success
ASS_BAD, // bad file
ASS_USER, // user callback returned non-zero
ASS_TRUNC, // truncated file
ASS_LAZY, // my lazy programming would mis-parse the file
ASS_MEM, // malloc failure
ASS_UNSUPPORTED, // unsupported format
};
enum ass_status assread(FILE *restrict file, int (*cb)(char const *restrict filename, size_t length, void const *restrict data, size_t size, void *restrict ctx), void *restrict ctx);
enum ass_status asswrite(FILE *restrict file, void (*cb)(char *restrict *, void *restrict *, size_t *restrict, void *restrict ctx), void *restrict ctx);
#if defined(__cplusplus)
}
#endif

148
src/assread.c Normal file
View File

@ -0,0 +1,148 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <zlib.h>
#include "ass.h"
static const char signature[10] = "\xdd\xdd\x0a" "Assets";
enum {
M_ZLIB,
};
#define ZBUF_SIZE (32 * 1024)
enum ass_status assread(FILE *restrict file, int (*cb)(char const *restrict filename, size_t length, void const *restrict data, size_t size, void *restrict ctx), void *restrict ctx) {
unsigned char zbuf[ZBUF_SIZE];
unsigned char *buf = NULL;
size_t read;
size_t bufsize = 0;
if ((read = fread(zbuf, 1, ZBUF_SIZE, file)) < 11) {
return ASS_BAD;
}
if (memcmp(zbuf, signature, 10) != 0) {
return ASS_BAD;
}
if (zbuf[10] != M_ZLIB) {
return ASS_UNSUPPORTED;
}
z_stream stream = {
.zalloc = Z_NULL,
.zfree = Z_NULL,
.opaque = Z_NULL,
.avail_in = read - 11,
.next_in = zbuf + 11,
};
enum ass_status status = ASS_OK;
int ret = inflateInit(&stream);
switch (ret) {
case Z_OK:
break;
case Z_MEM_ERROR:
return ASS_MEM;
case Z_VERSION_ERROR:
return ASS_UNSUPPORTED;
default:
return ASS_BAD;
}
buf = malloc(bufsize = 4096);
if (buf == NULL) {
status = ASS_MEM;
goto done;
}
stream.avail_out = 1;
stream.next_out = buf;
ret = inflate(&stream, Z_SYNC_FLUSH);
if (ret != Z_OK) {
status = ASS_BAD;
goto done;
}
if (stream.avail_in == 0) {
stream.avail_in = fread(zbuf, 1, ZBUF_SIZE, file);
stream.next_in = zbuf;
}
size_t outsize = buf[0];
do {
unsigned outsizebytes = 0;
for (unsigned m = 0x80; outsize & m; m = m >> 1) {
outsizebytes++;
outsize &= ~m;
}
if (outsizebytes) {
stream.avail_out = outsizebytes;
stream.next_out = buf;
while (stream.avail_out) {
ret = inflate(&stream, Z_SYNC_FLUSH);
switch (ret) {
case Z_OK:
break;
case Z_STREAM_END:
status = ASS_TRUNC;
goto done;
default:
status = ASS_BAD;
goto done;
}
if (stream.avail_in == 0) {
stream.avail_in = fread(zbuf, 1, ZBUF_SIZE, file);
stream.next_in = zbuf;
}
}
for (unsigned i = 0; i < outsizebytes; i++) {
outsize = (outsize << 8) | buf[i];
}
}
stream.avail_out = outsize + 1;
if (stream.avail_out != outsize + 1) {
status = ASS_LAZY;
goto done;
}
if (bufsize < outsize + 1) {
bufsize = outsize + 1;
void *tmp = realloc(buf, bufsize);
if (tmp == NULL) {
status = ASS_MEM;
goto done;
}
buf = tmp;
}
stream.next_out = buf;
while (stream.avail_out > 1) {
ret = inflate(&stream, Z_SYNC_FLUSH);
switch (ret) {
case Z_OK:
break;
case Z_STREAM_END:
if (stream.avail_out > 1) {
status = ASS_TRUNC;
goto done;
}
break;
default:
status = ASS_BAD;
goto done;
}
if (stream.avail_in == 0) {
stream.avail_in = fread(zbuf, 1, ZBUF_SIZE, file);
stream.next_in = zbuf;
}
}
size_t length = strnlen((char *) buf, outsize);
if (length == 0 || length == outsize) {
status = ASS_BAD;
goto done;
}
if (cb((char *) buf, length, buf + length + 1, outsize - length - 1, ctx)) {
status = ASS_USER;
goto done;
}
outsize = buf[outsize];
} while (ret != Z_STREAM_END);
done:
inflateEnd(&stream);
free(buf);
return status;
}

146
src/asswrite.c Normal file
View File

@ -0,0 +1,146 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <zlib.h>
#include "ass.h"
static const char signature[10] = "\xdd\xdd\x0a" "Assets";
enum {
M_ZLIB,
};
#define ZBUF_SIZE (32 * 1024)
enum ass_status asswrite(FILE *restrict file, void (*cb)(char *restrict *, void *restrict *, size_t *restrict, void *restrict ctx), void *restrict ctx) {
int wrote_any_files_at_all = 0;
unsigned char zbuf[ZBUF_SIZE];
fwrite(signature, 10, 1, file);
fputc(M_ZLIB, file);
z_stream stream = {
.zalloc = Z_NULL,
.zfree = Z_NULL,
.opaque = Z_NULL,
.avail_in = 0,
.next_in = NULL,
.avail_out = ZBUF_SIZE,
.next_out = zbuf,
};
int ret = deflateInit(&stream, Z_BEST_COMPRESSION);
switch (ret) {
case Z_OK:
break;
case Z_MEM_ERROR:
return ASS_MEM;
case Z_VERSION_ERROR:
return ASS_UNSUPPORTED;
default:
return ASS_BAD;
}
char *filename = NULL;
void *filedata = NULL;
size_t filesize = 0;
enum ass_status status = ASS_OK;
while (1) {
cb(&filename, &filedata, &filesize, ctx);
if ((filename == NULL || filename[0] == '\0') && filedata == NULL) {
break;
} else if ((filename == NULL || filename[0] == '\0') || filedata == NULL) {
status = ASS_USER;
goto done;
}
if (filesize == 0) {
continue;
}
wrote_any_files_at_all = 1;
size_t length = strlen(filename);
unsigned char buf[9];
unsigned bytes = 0;
size_t total = length + 1 + filesize;
if (total <= length || total <= filesize) {
status = ASS_TRUNC;
goto done;
}
for (bytes = 0; bytes < sizeof (size_t); bytes++) {
size_t byte = total >> (bytes * 8);
if ((byte & (0x7f >> bytes)) == byte) {
break;
}
buf[8 - bytes] = total >> (bytes * 8);
}
buf[8 - bytes] = (total >> (bytes * 8)) | (0xff00 >> bytes);
stream.avail_in = bytes + 1;
stream.next_in = buf + 8 - bytes;
do {
if (stream.avail_out == 0) {
fwrite(zbuf, 1, ZBUF_SIZE, file);
stream.next_out = zbuf;
stream.avail_out = ZBUF_SIZE;
}
ret = deflate(&stream, Z_NO_FLUSH);
if (ret != Z_OK) {
status = ASS_BAD;
goto done;
}
} while (stream.avail_out == 0);
stream.avail_in = length + 1;
if (stream.avail_in != length + 1) {
status = ASS_LAZY;
goto done;
}
stream.next_in = (void *) filename;
do {
if (stream.avail_out == 0) {
fwrite(zbuf, 1, ZBUF_SIZE, file);
stream.next_out = zbuf;
stream.avail_out = ZBUF_SIZE;
}
ret = deflate(&stream, Z_NO_FLUSH);
if (ret != Z_OK) {
status = ASS_BAD;
goto done;
}
} while (stream.avail_out == 0);
stream.avail_in = filesize;
if (stream.avail_in != filesize) {
status = ASS_LAZY;
goto done;
}
stream.next_in = filedata;
do {
if (stream.avail_out == 0) {
fwrite(zbuf, 1, ZBUF_SIZE, file);
stream.next_out = zbuf;
stream.avail_out = ZBUF_SIZE;
}
ret = deflate(&stream, Z_NO_FLUSH);
if (ret != Z_OK) {
status = ASS_BAD;
goto done;
}
} while (stream.avail_out == 0);
}
do {
if (stream.avail_out == 0) {
fwrite(zbuf, 1, ZBUF_SIZE, file);
stream.next_out = zbuf;
stream.avail_out = ZBUF_SIZE;
}
ret = deflate(&stream, Z_FINISH);
if (ret != Z_OK && ret != Z_STREAM_END) {
status = ASS_BAD;
goto done;
}
} while (stream.avail_out == 0);
fwrite(zbuf, 1, ZBUF_SIZE - stream.avail_out, file);
done:
deflateEnd(&stream);
cb(&filename, &filedata, NULL, ctx);
if (status == ASS_OK && wrote_any_files_at_all == 0) {
return ASS_TRUNC;
}
return status;
}

View File

@ -3,19 +3,43 @@
#define SDL_MAIN_USE_CALLBACKS 1 #define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL_main.h> #include <SDL3/SDL_main.h>
#include <unordered_map>
#include <string>
#include <cstdio> #include <cstdio>
#include <ctime> #include <ctime>
#include "ass.h"
#define WINDOW_WIDTH 128 #define WINDOW_WIDTH 128
#define WINDOW_HEIGHT 128 #define WINDOW_HEIGHT 128
#if 0
// something something string_view faster but actually cryptic error messages so w/e future me problem
#include <string_view>
struct string_hash {
using is_transparent = void;
[[nodiscard]] size_t operator()(const char *txt) const {
return std::hash<std::string_view>{}(txt);
}
[[nodiscard]] size_t operator()(std::string_view txt) const {
return std::hash<std::string_view>{}(txt);
}
[[nodiscard]] size_t operator()(const std::string &txt) const {
return std::hash<std::string>{}(txt);
}
};
std::unordered_map<std::string, unsigned, string_hash, std::equal_to<>> programs, textures;
#endif
struct state { struct state {
SDL_Window *window; SDL_Window *window;
SDL_GLContext context; SDL_GLContext context;
float color; float color;
std::unordered_map<std::string, unsigned> programs, textures;
}; };
SDL_AppResult SDL_AppInit(void **appstate, int, char **) { SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
clock_t begin = clock(); clock_t begin = clock();
auto state = new (struct state); auto state = new (struct state);
@ -24,8 +48,10 @@ SDL_AppResult SDL_AppInit(void **appstate, int, char **) {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_SetHint(SDL_HINT_MAIN_CALLBACK_RATE, "60");
state->window = SDL_CreateWindow("suwi", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL /*| SDL_WINDOW_RESIZABLE*/ | SDL_WINDOW_TRANSPARENT); state->window = SDL_CreateWindow("suwi", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL /*| SDL_WINDOW_RESIZABLE*/ | SDL_WINDOW_TRANSPARENT);
SDL_SetWindowMinimumSize(state->window, WINDOW_WIDTH, WINDOW_HEIGHT);
state->context = SDL_GL_CreateContext(state->window); state->context = SDL_GL_CreateContext(state->window);
@ -36,6 +62,42 @@ SDL_AppResult SDL_AppInit(void **appstate, int, char **) {
} }
fprintf(stderr, "info: GLES%u.%u\n", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version)); fprintf(stderr, "info: GLES%u.%u\n", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version));
FILE *file;
if (argc < 2) {
std::string path = argv[0];
auto s = path.rfind('.');
if (s != path.npos) {
path.erase(s);
}
path.append(".assets");
file = fopen(path.c_str(), "rb");
if (file == NULL) {
perror(path.c_str());
return SDL_APP_FAILURE;
}
} else {
file = fopen(argv[1], "rb");
if (file == NULL) {
perror(argv[1]);
return SDL_APP_FAILURE;
}
}
if (assread(file, [](char const *name, size_t length, void const *data, size_t size, /* struct state *state */ void *ctx) -> int {
auto &state = *(struct state *) ctx;
std::string filename = std::string(name, length);
(void) name;
(void) length;
(void) data;
(void) size;
if (filename.ends_with(".glsl")) {
state.programs[filename];
}
state.textures[filename];
return 0;
}, state)) {
return SDL_APP_FAILURE;
}
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
SDL_ShowWindow(state->window); SDL_ShowWindow(state->window);
@ -85,11 +147,11 @@ SDL_AppResult SDL_AppEvent(void *, SDL_Event *evt) {
} }
SDL_AppResult SDL_AppIterate(void *appstate) { SDL_AppResult SDL_AppIterate(void *appstate) {
auto state = (struct state *) appstate; auto &state = *(struct state *) appstate;
glClearColor(0.5, 0.5, 0.5, 0.0); glClearColor(0.5, 0.5, 0.5, 0.0);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
SDL_GL_SwapWindow(state->window); SDL_GL_SwapWindow(state.window);
return SDL_APP_CONTINUE; return SDL_APP_CONTINUE;
} }