asset loading frame
This commit is contained in:
parent
180026281e
commit
857f368970
@ -5,11 +5,11 @@ MKDIR ?= mkdir -p
|
||||
|
||||
EXTENSION ?= out
|
||||
CFLAGS ?= -Wall -Wpedantic -Wextra -g -Og
|
||||
CXXFLAGS ?= -Wall -Wpedantic -Wextra -g -Og
|
||||
CXXFLAGS ?= -std=c++20 -Wall -Wpedantic -Wextra -g -Og
|
||||
LDFLAGS ?=
|
||||
|
||||
NAME := suwi
|
||||
libs := m portaudio SDL3
|
||||
libs := m z portaudio SDL3
|
||||
includes := src/
|
||||
cflags := $(addprefix -I,${includes}) ${CFLAGS}
|
||||
cxxflags := $(addprefix -I,${includes}) ${CXXFLAGS}
|
||||
|
||||
25
src/ass.h
Normal file
25
src/ass.h
Normal 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
148
src/assread.c
Normal 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
146
src/asswrite.c
Normal 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;
|
||||
}
|
||||
68
src/main.c++
68
src/main.c++
@ -3,19 +3,43 @@
|
||||
#define SDL_MAIN_USE_CALLBACKS 1
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
|
||||
#include "ass.h"
|
||||
|
||||
#define WINDOW_WIDTH 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 {
|
||||
SDL_Window *window;
|
||||
SDL_GLContext context;
|
||||
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();
|
||||
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_MINOR_VERSION, 0);
|
||||
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);
|
||||
SDL_SetWindowMinimumSize(state->window, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
|
||||
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));
|
||||
|
||||
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);
|
||||
SDL_ShowWindow(state->window);
|
||||
|
||||
@ -85,11 +147,11 @@ SDL_AppResult SDL_AppEvent(void *, SDL_Event *evt) {
|
||||
}
|
||||
|
||||
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);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
SDL_GL_SwapWindow(state->window);
|
||||
SDL_GL_SwapWindow(state.window);
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user