diff options
| -rw-r--r-- | Makefile | 37 | ||||
| -rwxr-xr-x[-rw-r--r--] | build.sh | 43 | ||||
| -rw-r--r-- | kon/konassets.h | 598 | ||||
| -rw-r--r-- | src/impl/konassets.c | 2 | ||||
| -rw-r--r-- | src/impl/konfont.c | 1 | ||||
| -rw-r--r-- | src/impl/miniaudio.c | 2 | ||||
| -rw-r--r-- | src/impl/stb_image.c | 2 |
7 files changed, 675 insertions, 10 deletions
@@ -12,9 +12,16 @@ MODOBJS ?= WARN = -std=c99 -Wall -Wextra -Wpedantic KONFLAGS = $(WARN) $(MODEFLAGS) $(DEFS) -I$(KONDIR) -I$(EXTDIR) +EXTFLAGS = -std=gnu99 -w $(MODEFLAGS) $(DEFS) -I$(EXTDIR) COMPILE = @mkdir -p $(OBJDIR); echo "\# compile $@"; $(CC) $(KONFLAGS) -c +COMPILEEXT = @mkdir -p $(OBJDIR); echo "\# compile $@"; $(CC) $(EXTFLAGS) -c + +ASSETDIR ?= assets +PACKFLAGS ?= BIN = $(OBJDIR)/$(PROGRAM)$(EXE) +PACKER = $(OBJDIR)/konpack$(EXE) +PACKFILE = $(OBJDIR)/data.kpk CORE = $(OBJDIR)/engine.o $(OBJDIR)/konwinlib.o $(OBJDIR)/konsofren.o \ $(OBJDIR)/kontime.o $(OBJDIR)/koninput.o OBJS = $(CORE) $(MODOBJS) @@ -49,10 +56,36 @@ $(OBJDIR)/kontoml.o: $(SRCDIR)/impl/kontoml.c $(KONDIR)/kontoml.h $(OBJDIR)/kontile.o: $(SRCDIR)/impl/kontile.c $(KONDIR)/kontile.h $(KONDIR)/konsofren.h $(COMPILE) $(SRCDIR)/impl/kontile.c -o $@ +$(OBJDIR)/konassets.o: $(SRCDIR)/impl/konassets.c $(KONDIR)/konassets.h $(KONDIR)/konpack.h $(KONDIR)/konsofren.h $(EXTDIR)/*.h + $(COMPILE) $(SRCDIR)/impl/konassets.c -o $@ + +$(OBJDIR)/konaudio.o: $(SRCDIR)/impl/konaudio.c $(KONDIR)/konaudio.h $(KONDIR)/konassets.h $(EXTDIR)/miniaudio.h + $(COMPILE) $(SRCDIR)/impl/konaudio.c -o $@ + +$(OBJDIR)/stb_image.o: $(SRCDIR)/impl/stb_image.c $(EXTDIR)/stb_image.h + $(COMPILEEXT) $(SRCDIR)/impl/stb_image.c -o $@ + +$(OBJDIR)/miniaudio.o: $(SRCDIR)/impl/miniaudio.c $(EXTDIR)/miniaudio.h + $(COMPILEEXT) $(SRCDIR)/impl/miniaudio.c -o $@ + +$(OBJDIR)/konpack.o: $(SRCDIR)/impl/konpack.c $(KONDIR)/konpack.h + $(COMPILE) $(SRCDIR)/impl/konpack.c -o $@ + +tools: $(PACKER) + +$(PACKER): tools/konpack.c $(KONDIR)/konpack.h + @mkdir -p $(OBJDIR) + @echo "# link $@" + @$(CC) $(KONFLAGS) -o $@ tools/konpack.c + +pack: $(PACKER) + @find $(ASSETDIR) -type f | sort > $(OBJDIR)/assets.lst + $(PACKER) -c $(PACKFILE) $(PACKFLAGS) -b $(ASSETDIR) -l $(OBJDIR)/assets.lst + run: $(BIN) ./$(BIN) clean: - rm -f $(OBJS) $(BIN) + rm -f $(OBJDIR)/*.o $(BIN) $(PACKER) $(PACKFILE) $(OBJDIR)/assets.lst -.PHONY: all run clean +.PHONY: all run clean tools pack @@ -7,20 +7,22 @@ platform=native mode=release objdir=obj modules= +developer=0 usage() { - echo "usage: build.sh [-d] [-p platform] [-m module,...] [-o objdir] [target ...]" - echo "modules: font toml tile" + echo "usage: build.sh [-d] [-D] [-p platform] [-m module,...] [-o objdir] [target ...]" + echo "modules: font toml tile pack assets image audio" echo "platforms: native linux bsd windows" - echo "targets: all run clean" + echo "targets: all run clean tools pack" exit 1 } -while getopts dm:o:p: opt; do +while getopts dDm:o:p: opt; do case $opt in d) mode=debug ;; - m) modules=$OPTARG ;; + D) developer=1 ;; + m) modules="$modules $(echo "$OPTARG" | tr ',' ' ')" ;; o) objdir=$OPTARG ;; p) platform=$OPTARG ;; *) usage ;; @@ -56,19 +58,44 @@ esac if [ $mode = debug ]; then MODEFLAGS="-g -O0 -DKON_DEBUG" + developer=1 else MODEFLAGS="-O2 -DNDEBUG" fi +[ $developer = 1 ] && MODEFLAGS="$MODEFLAGS -DKON_DEVELOPER" MODOBJS= -for m in $(echo "$modules" | tr ',' ' '); do + +addobj() +{ + case " $MODOBJS " in + *" $objdir/$1.o "*) ;; + *) MODOBJS="$MODOBJS $objdir/$1.o" ;; + esac +} + +for m in $modules; do DEFS="$DEFS -DKON_MODULE_$(echo "$m" | tr 'a-z' 'A-Z')" case $m in - font|toml|tile) MODOBJS="$MODOBJS $objdir/kon$m.o" ;; + font|toml|tile|pack) addobj "kon$m" ;; + assets) addobj konassets ;; + image) addobj stb_image ;; + audio) + addobj miniaudio + addobj konaudio + case $platform in + linux) LDLIBS="$LDLIBS -lpthread -ldl" ;; + bsd) LDLIBS="$LDLIBS -lpthread" ;; + esac + ;; *) echo "build.sh: unknown module '$m'" >&2; usage ;; esac done +case " $modules " in +*" assets "*) addobj konpack ;; +esac + OBJDIR=$objdir export CC EXE LDLIBS MODEFLAGS DEFS OBJDIR MODOBJS @@ -79,5 +106,5 @@ if [ "$(cat "$OBJDIR/.flags" 2>/dev/null)" != "$flags" ]; then echo "$flags" > "$OBJDIR/.flags" fi -echo "===> platform $platform, $mode build, modules: ${modules:-none}" +echo "===> platform $platform, $mode build, modules:${modules:- none}" exec ${MAKE:-make} ${*:-all} diff --git a/kon/konassets.h b/kon/konassets.h new file mode 100644 index 0000000..0971166 --- /dev/null +++ b/kon/konassets.h @@ -0,0 +1,598 @@ +/* SPDX-License-Identifier: MIT */ +/* + * konassets.h + * + * Copyright (c) 2026 KonAki + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef KONASSETS_H +#define KONASSETS_H + +#include <stdint.h> +#include <stddef.h> +#include <stdio.h> + +#include "konpack.h" +#include "konsofren.h" + +/*** definition ***/ + +typedef struct kon_assets kon_assets_t; + +/* what a loaded file turns into, picked from the file extension. anything unknown is a blob */ +typedef enum kon_assetType { + KON_ASSET_BLOB = 0, + KON_ASSET_IMAGE, + KON_ASSET_SOUND, + KON_ASSET_CUSTOM = 16 +} kon_assetType_t; + +typedef struct kon_blob { + uint8_t *data; /* has one extra 0 byte after the end, so text files can be used as strings */ + size_t size; +} kon_blob_t; + +/* 16 bit interleaved samples, played back at sampleRate */ +typedef struct kon_sound { + int16_t *frames; + uint64_t frameCount; + uint32_t channels; + uint32_t sampleRate; +} kon_sound_t; + +/* load gets the raw file and returns your object, or NULL. set *bytes to what it uses for the debug view. + name and extensions (".lvl .map", lowercase, space separated) have to stay valid for as long as assets does */ +typedef struct kon_assetLoader { + const char *name; + const char *extensions; + void *(*load)(const uint8_t *data, size_t size, size_t *bytes); + void (*unload)(void *object); +} kon_assetLoader_t; + +typedef struct kon_assetInfo { + const char *name; + const char *typeName; + int type; + int refs; + size_t bytes; + int loose; /* 1 if it came from the loose folder instead of the pack, developer builds only */ +} kon_assetInfo_t; + +/* pack and looseDir can each be NULL. the pack stays yours, close it after kon_assetsDestroy. + with KON_DEVELOPER defined files in looseDir win over the pack so you can edit without repacking, + without it looseDir is ignored and not even compiled in */ +kon_assets_t *kon_assetsCreate(kon_pack_t *pack, const char *looseDir); +void kon_assetsDestroy(kon_assets_t *assets); + +/* returns the type id to use with kon_assetGetAs, or -1 */ +int kon_assetsRegister(kon_assets_t *assets, const kon_assetLoader_t *loader); + +/* loads the file the first time and counts a use every time. the object belongs to the manager, do not free it. + NULL if it is not found or fails to load, see kon_assetsError */ +void *kon_assetGet(kon_assets_t *assets, const char *name); + +/* same but NULL if the file is not of that type, without counting a use */ +void *kon_assetGetAs(kon_assets_t *assets, const char *name, int type); + +#define kon_assetBlob(assets, name) ((kon_blob_t *)kon_assetGetAs((assets), (name), KON_ASSET_BLOB)) +#define kon_assetImage(assets, name) ((kon_image *)kon_assetGetAs((assets), (name), KON_ASSET_IMAGE)) +#define kon_assetSound(assets, name) ((kon_sound_t *)kon_assetGetAs((assets), (name), KON_ASSET_SOUND)) + +/* gives back one use. nothing is freed until kon_assetsCollect */ +void kon_assetRelease(kon_assets_t *assets, const char *name); + +/* frees everything nobody is using, returns how many it freed */ +int kon_assetsCollect(kon_assets_t *assets); + +/* for the debug view */ +int kon_assetsCount(const kon_assets_t *assets); +int kon_assetsInfo(const kon_assets_t *assets, int index, kon_assetInfo_t *info); +size_t kon_assetsBytes(const kon_assets_t *assets); +void kon_assetsDump(const kon_assets_t *assets, FILE *out); + +const char *kon_assetsError(const kon_assets_t *assets); + +/*** implementation ***/ + +#ifdef KONASSETS_IMPLEMENTATION + +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#ifdef KON_MODULE_IMAGE +#include "stb_image.h" +#endif + +#ifdef KON_MODULE_AUDIO +#include "miniaudio.h" +#endif + +#define KON_ASSET_LOADERS_ 32 + +typedef struct kon_assetEntry_ { + char *name; + void *object; + int type; + int refs; + size_t bytes; + int loose; +} kon_assetEntry_t; + +struct kon_assets { + kon_pack_t *pack; + char *looseDir; + kon_assetLoader_t loaders[KON_ASSET_LOADERS_]; + int loaderUsed[KON_ASSET_LOADERS_]; + int nextCustom; + kon_assetEntry_t *entries; + int count, capacity; + const char *error; +}; + +/*** built in loaders ***/ + +static void *kon_loadBlob_(const uint8_t *data, size_t size, size_t *bytes) { + kon_blob_t *blob = malloc(sizeof(kon_blob_t)); + if (!blob) return NULL; + + blob->data = malloc(size + 1); + if (!blob->data) { + free(blob); + return NULL; + } + + memcpy(blob->data, data, size); + blob->data[size] = 0; + blob->size = size; + *bytes = size; + return blob; +} + +static void kon_unloadBlob_(void *object) { + kon_blob_t *blob = object; + free(blob->data); + free(blob); +} + +#ifdef KON_MODULE_IMAGE + +static void *kon_loadImageAsset_(const uint8_t *data, size_t size, size_t *bytes) { + int width, height, channels; + unsigned char *pixels; + kon_image *image; + + if (size > INT_MAX) return NULL; + + pixels = stbi_load_from_memory(data, (int)size, &width, &height, &channels, 4); + if (!pixels) return NULL; + + image = kon_loadImage(pixels, width, height, konFormatRGBA8); + stbi_image_free(pixels); + + if (image) *bytes = (size_t)width * (size_t)height * 4; + return image; +} + +static void kon_unloadImageAsset_(void *object) { + kon_freeImage(object); +} + +#endif + +#ifdef KON_MODULE_AUDIO + +#define KON_SOUND_MAX_BYTES_ ((uint64_t)1 << 30) + +static void *kon_loadSoundAsset_(const uint8_t *data, size_t size, size_t *bytes) { + ma_decoder_config config = ma_decoder_config_init(ma_format_s16, 0, 0); + ma_decoder decoder; + kon_sound_t *sound; + int16_t *frames = NULL; + uint64_t total = 0, capacity = 0; + uint32_t channels; + + if (ma_decoder_init_memory(data, size, &config, &decoder) != MA_SUCCESS) return NULL; + + channels = (uint32_t)decoder.outputChannels; + if (channels == 0) { + ma_decoder_uninit(&decoder); + return NULL; + } + + /* the length is not always known up front, so read until the decoder runs dry */ + for (;;) { + ma_uint64 got = 0; + + if (total == capacity) { + uint64_t grown = capacity ? capacity * 2 : 16384; + int16_t *more; + + if (grown * channels * sizeof(int16_t) > KON_SOUND_MAX_BYTES_) break; + more = realloc(frames, (size_t)(grown * channels * sizeof(int16_t))); + if (!more) { + free(frames); + ma_decoder_uninit(&decoder); + return NULL; + } + frames = more; + capacity = grown; + } + + if (ma_decoder_read_pcm_frames(&decoder, frames + total * channels, capacity - total, &got) != MA_SUCCESS && got == 0) break; + if (got == 0) break; + total += got; + } + + sound = malloc(sizeof(kon_sound_t)); + if (!sound || total == 0) { + free(sound); + free(frames); + ma_decoder_uninit(&decoder); + return NULL; + } + + sound->frames = frames; + sound->frameCount = total; + sound->channels = channels; + sound->sampleRate = (uint32_t)decoder.outputSampleRate; + *bytes = (size_t)(total * channels * sizeof(int16_t)); + + ma_decoder_uninit(&decoder); + return sound; +} + +static void kon_unloadSoundAsset_(void *object) { + kon_sound_t *sound = object; + free(sound->frames); + free(sound); +} + +#endif + +/*** private helper ***/ + +static int kon_extensionMatches_(const char *name, const char *extensions) { + const char *dot = strrchr(name, '.'); + const char *slash = strrchr(name, '/'); + size_t length; + + if (!dot || (slash && dot < slash) || !extensions) return 0; + length = strlen(dot); + + while (*extensions) { + const char *end; + size_t tokenLength; + + while (*extensions == ' ') extensions++; + end = extensions; + while (*end && *end != ' ') end++; + tokenLength = (size_t)(end - extensions); + + if (tokenLength == length) { + size_t i; + for (i = 0; i < length; i++) { + char c = dot[i]; + if (c >= 'A' && c <= 'Z') c = (char)(c - 'A' + 'a'); + if (c != extensions[i]) break; + } + if (i == length) return 1; + } + extensions = end; + } + return 0; +} + +static int kon_nameIsSafe_(const char *name) { + const char *segment = name; + + if (!name || name[0] == '\0' || name[0] == '/') return 0; + + for (const char *c = name; ; c++) { + if (*c == '\\' || *c == ':') return 0; + if (*c == '/' || *c == '\0') { + if (c - segment == 2 && segment[0] == '.' && segment[1] == '.') return 0; + if (*c == '\0') break; + segment = c + 1; + } + } + return 1; +} + +static int kon_typeForName_(const kon_assets_t *assets, const char *name) { + for (int i = 1; i < KON_ASSET_LOADERS_; i++) { + if (assets->loaderUsed[i] && kon_extensionMatches_(name, assets->loaders[i].extensions)) return i; + } + return KON_ASSET_BLOB; +} + +static kon_assetEntry_t *kon_findEntry_(const kon_assets_t *assets, const char *name) { + for (int i = 0; i < assets->count; i++) { + if (strcmp(assets->entries[i].name, name) == 0) return &assets->entries[i]; + } + return NULL; +} + +static uint8_t *kon_readFile_(kon_assets_t *assets, const char *name, size_t *size, int *loose) { + *loose = 0; + +#ifdef KON_DEVELOPER + if (assets->looseDir) { + size_t length = strlen(assets->looseDir) + strlen(name) + 2; + char *path = malloc(length); + FILE *f; + + if (!path) { + assets->error = "out of memory"; + return NULL; + } + snprintf(path, length, "%s/%s", assets->looseDir, name); + f = fopen(path, "rb"); + free(path); + + if (f) { + long end; + uint8_t *data = NULL; + + if (fseek(f, 0, SEEK_END) == 0 && (end = ftell(f)) >= 0 && fseek(f, 0, SEEK_SET) == 0) { + data = malloc((size_t)end + 1); + if (data && fread(data, 1, (size_t)end, f) == (size_t)end) { + data[end] = 0; + *size = (size_t)end; + *loose = 1; + fclose(f); + return data; + } + free(data); + } + fclose(f); + assets->error = "could not read the loose file"; + return NULL; + } + } +#endif + + if (assets->pack) { + int index = kon_packFind(assets->pack, name); + if (index >= 0) { + uint8_t *data = kon_packRead(assets->pack, index, size); + if (!data) assets->error = kon_packError(); + return data; + } + } + + assets->error = "file not found"; + return NULL; +} + +static void kon_unloadEntry_(kon_assets_t *assets, kon_assetEntry_t *entry) { + if (assets->loaderUsed[entry->type] && assets->loaders[entry->type].unload) { + assets->loaders[entry->type].unload(entry->object); + } + free(entry->name); +} + +/*** assets ***/ + +static void kon_addLoader_(kon_assets_t *assets, int type, const char *name, const char *extensions, + void *(*load)(const uint8_t *, size_t, size_t *), void (*unload)(void *)) { + assets->loaders[type].name = name; + assets->loaders[type].extensions = extensions; + assets->loaders[type].load = load; + assets->loaders[type].unload = unload; + assets->loaderUsed[type] = 1; +} + +kon_assets_t *kon_assetsCreate(kon_pack_t *pack, const char *looseDir) { + kon_assets_t *assets = calloc(1, sizeof(kon_assets_t)); + if (!assets) return NULL; + + assets->pack = pack; + assets->error = "no error"; + assets->nextCustom = KON_ASSET_CUSTOM; + +#ifdef KON_DEVELOPER + if (looseDir) { + assets->looseDir = malloc(strlen(looseDir) + 1); + if (!assets->looseDir) { + free(assets); + return NULL; + } + strcpy(assets->looseDir, looseDir); + } +#else + (void)looseDir; +#endif + + kon_addLoader_(assets, KON_ASSET_BLOB, "blob", NULL, kon_loadBlob_, kon_unloadBlob_); +#ifdef KON_MODULE_IMAGE + kon_addLoader_(assets, KON_ASSET_IMAGE, "image", ".png .jpg .jpeg .bmp .tga .gif", kon_loadImageAsset_, kon_unloadImageAsset_); +#endif +#ifdef KON_MODULE_AUDIO + kon_addLoader_(assets, KON_ASSET_SOUND, "sound", ".wav .mp3 .flac", kon_loadSoundAsset_, kon_unloadSoundAsset_); +#endif + + return assets; +} + +void kon_assetsDestroy(kon_assets_t *assets) { + if (!assets) return; + + for (int i = 0; i < assets->count; i++) kon_unloadEntry_(assets, &assets->entries[i]); + free(assets->entries); + free(assets->looseDir); + free(assets); +} + +int kon_assetsRegister(kon_assets_t *assets, const kon_assetLoader_t *loader) { + int type; + + if (!assets || !loader || !loader->load || !loader->extensions || !loader->name) return -1; + if (assets->nextCustom >= KON_ASSET_LOADERS_) return -1; + + type = assets->nextCustom++; + kon_addLoader_(assets, type, loader->name, loader->extensions, loader->load, loader->unload); + return type; +} + +void *kon_assetGet(kon_assets_t *assets, const char *name) { + return kon_assetGetAs(assets, name, -1); +} + +void *kon_assetGetAs(kon_assets_t *assets, const char *name, int type) { + kon_assetEntry_t *entry; + uint8_t *data; + size_t size = 0, bytes = 0; + int loose, found; + void *object; + + if (!assets) return NULL; + if (!kon_nameIsSafe_(name)) { + assets->error = "bad asset name"; + return NULL; + } + + entry = kon_findEntry_(assets, name); + if (entry) { + if (type >= 0 && entry->type != type) { + assets->error = "asset is a different type"; + return NULL; + } + entry->refs++; + return entry->object; + } + + found = kon_typeForName_(assets, name); + if (type >= 0 && found != type) { + assets->error = "asset is a different type"; + return NULL; + } + + data = kon_readFile_(assets, name, &size, &loose); + if (!data) return NULL; + + object = assets->loaders[found].load(data, size, &bytes); + free(data); + if (!object) { + assets->error = "could not decode the file"; + return NULL; + } + + if (assets->count == assets->capacity) { + int capacity = assets->capacity ? assets->capacity * 2 : 32; + kon_assetEntry_t *grown = realloc(assets->entries, (size_t)capacity * sizeof(kon_assetEntry_t)); + if (!grown) { + assets->loaders[found].unload(object); + assets->error = "out of memory"; + return NULL; + } + assets->entries = grown; + assets->capacity = capacity; + } + + entry = &assets->entries[assets->count]; + entry->name = malloc(strlen(name) + 1); + if (!entry->name) { + assets->loaders[found].unload(object); + assets->error = "out of memory"; + return NULL; + } + strcpy(entry->name, name); + entry->object = object; + entry->type = found; + entry->refs = 1; + entry->bytes = bytes; + entry->loose = loose; + assets->count++; + + return object; +} + +void kon_assetRelease(kon_assets_t *assets, const char *name) { + kon_assetEntry_t *entry; + + if (!assets || !name) return; + entry = kon_findEntry_(assets, name); + if (entry && entry->refs > 0) entry->refs--; +} + +int kon_assetsCollect(kon_assets_t *assets) { + int kept = 0, freed = 0; + + if (!assets) return 0; + + for (int i = 0; i < assets->count; i++) { + if (assets->entries[i].refs > 0) { + assets->entries[kept++] = assets->entries[i]; + } else { + kon_unloadEntry_(assets, &assets->entries[i]); + freed++; + } + } + assets->count = kept; + return freed; +} + +int kon_assetsCount(const kon_assets_t *assets) { + return assets ? assets->count : 0; +} + +int kon_assetsInfo(const kon_assets_t *assets, int index, kon_assetInfo_t *info) { + const kon_assetEntry_t *entry; + + if (!assets || !info || index < 0 || index >= assets->count) return 0; + entry = &assets->entries[index]; + + info->name = entry->name; + info->typeName = assets->loaderUsed[entry->type] ? assets->loaders[entry->type].name : "?"; + info->type = entry->type; + info->refs = entry->refs; + info->bytes = entry->bytes; + info->loose = entry->loose; + return 1; +} + +size_t kon_assetsBytes(const kon_assets_t *assets) { + size_t total = 0; + + if (!assets) return 0; + for (int i = 0; i < assets->count; i++) total += assets->entries[i].bytes; + return total; +} + +void kon_assetsDump(const kon_assets_t *assets, FILE *out) { + kon_assetInfo_t info; + + if (!assets || !out) return; + + fprintf(out, "%d assets, %lu bytes\n", assets->count, (unsigned long)kon_assetsBytes(assets)); + for (int i = 0; kon_assetsInfo(assets, i, &info); i++) { + fprintf(out, "%-32s %-8s refs %-3d %10lu bytes%s\n", info.name, info.typeName, info.refs, (unsigned long)info.bytes, info.loose ? " loose" : ""); + } +} + +const char *kon_assetsError(const kon_assets_t *assets) { + return assets ? assets->error : "no assets"; +} + +#endif /* KONASSETS_IMPLEMENTATION */ + +#endif diff --git a/src/impl/konassets.c b/src/impl/konassets.c new file mode 100644 index 0000000..af67fb1 --- /dev/null +++ b/src/impl/konassets.c @@ -0,0 +1,2 @@ +#define KONASSETS_IMPLEMENTATION +#include "konassets.h" diff --git a/src/impl/konfont.c b/src/impl/konfont.c index 04c3bd8..9c92abc 100644 --- a/src/impl/konfont.c +++ b/src/impl/konfont.c @@ -1,2 +1,3 @@ +#define STB_TRUETYPE_IMPLEMENTATION #define KONFONT_IMPLEMENTATION #include "konfont.h" diff --git a/src/impl/miniaudio.c b/src/impl/miniaudio.c new file mode 100644 index 0000000..afcddad --- /dev/null +++ b/src/impl/miniaudio.c @@ -0,0 +1,2 @@ +#define MINIAUDIO_IMPLEMENTATION +#include "miniaudio.h" diff --git a/src/impl/stb_image.c b/src/impl/stb_image.c new file mode 100644 index 0000000..8ddfd1f --- /dev/null +++ b/src/impl/stb_image.c @@ -0,0 +1,2 @@ +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" |
