Replace tinycdb with miniz; remove unnecessary files; use sokol_main

This commit is contained in:
John Alanbrook 2024-03-28 17:40:14 -05:00
parent c27e1a3071
commit 59ad64bbba
596 changed files with 9320 additions and 200324 deletions

View file

@ -79,8 +79,6 @@ else
endif
CPPFLAGS += -DHAVE_CEIL -DCP_USE_CGTYPES=0 -DCP_USE_DOUBLES=0 -DHAVE_FLOOR -DHAVE_FMOD -DHAVE_LRINT -DHAVE_LRINTF $(includeflag) -MD $(WARNING_FLAGS) -I. -DVER=\"$(SEM)\" -DCOM=\"$(COM)\" -DINFO=\"$(INFO)\" #-DENABLE_SINC_MEDIUM_CONVERTER -DENABLE_SINC_FAST_CONVERTER -DCP_COLLISION_TYPE_TYPE=uintptr_t -DCP_BITMASK_TYPE=uintptr_t
CPPFLAGS += -D_FILE_OFFSET_BITS=64 # for tinycdb
CPPFLAGS += -DCONFIG_VERSION=\"2024-02-14\" -DCONFIG_BIGNUM #for quickjs
# ENABLE_SINC_[BEST|FAST|MEDIUM]_CONVERTER
@ -149,14 +147,17 @@ endif
OBJS != find source -type f -name '*.c' | grep -vE 'test|tool|example|fuzz|main' | grep -vE 'quickjs'
CPPOBJS != find source -type f -name '*.cpp' | grep -vE 'test|tool|example|fuzz|main'
OBJS += $(CPPOBJS)
OBJS += source/engine/yugine.c
OBJS += $(shell find source/engine -type f -name '*.m')
QUICKJS := source/engine/thirdparty/quickjs
OBJS += $(addprefix $(QUICKJS)/, libregexp.c quickjs.c libunicode.c cutils.c libbf.c)
OBJS := $(patsubst %.cpp, %$(INFO).o, $(OBJS))
OBJS := $(patsubst %.c, %$(INFO).o,$(OBJS))
OBJS := $(patsubst %.m, %$(INFO).o, $(OBJS))
engineincs != find source/engine -maxdepth 1 -type d
includeflag != find source -type d -name include
includeflag += $(engineincs) source/engine/thirdparty/tinycdb source/shaders source/engine/thirdparty/sokol source/engine/thirdparty/stb source/engine/thirdparty/cgltf source/engine/thirdparty/TinySoundFont source/engine/thirdparty/dr_libs
includeflag += $(engineincs) source/shaders source/engine/thirdparty/sokol source/engine/thirdparty/stb source/engine/thirdparty/cgltf source/engine/thirdparty/TinySoundFont source/engine/thirdparty/dr_libs
includeflag += $(STEAM)/public
includeflag += source
includeflag := $(addprefix -I, $(includeflag))
@ -188,20 +189,12 @@ install: $(NAME)
@echo Copying to destination
cp -f $(NAME) $(DESTDIR)/$(APP)
$(NAME): libengine$(INFO).a libquickjs$(INFO).a $(DEPS)
$(NAME): $(OBJS) $(DEPS)
@echo Linking $(NAME)
$(CROSSWIN)$(LD) $^ $(CPPFLAGS) $(LDFLAGS) -L. $(LDPATHS) $(LDLIBS) -o $@
@echo Finished build
libengine$(INFO).a: $(OBJS)
@echo Archiving $@
$(CROSSWIN)$(AR) rcs $@ $(OBJS)
QUICKJS := source/engine/thirdparty/quickjs
libquickjs$(INFO).a: $(QUICKJS)/libregexp$(INFO).o $(QUICKJS)/quickjs$(INFO).o $(QUICKJS)/libunicode$(INFO).o $(QUICKJS)/cutils$(INFO).o $(QUICKJS)/libbf$(INFO).o
$(CROSSWIN)$(AR) rcs $@ $^
%$(INFO).o: %.c $(SHADERS)
%$(INFO).o: %.c source/engine/core.cdb.h $(SHADERS)
@echo Making C object $@
$(CROSSWIN)$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
@ -224,38 +217,18 @@ SCRIPTS := $(shell ls scripts/*.js*)
CORE != (ls icons/* fonts/*)
CORE := $(CORE) $(SCRIPTS)
CDB = source/engine/thirdparty/tinycdb
CDB_C != find $(CDB) -name *.c
CDB_O := $(patsubst %.c, %.o, $(CDB_C))
CDB_O := $(notdir $(CDB_O))
tools/libcdb.a: $(CDB_C)
cc -c $^
$(CROSSWIN)$(AR) rcs $@ $(CDB_O)
cdb: tools/cdb.c tools/libcdb.a
@echo Making cdb
cc $^ -I$(CDB) -o cdb
packer: tools/packer.c tools/libcdb.a
packer$(EXT): tools/packer.c source/engine/miniz.c
@echo Making packer
cc $^ -I$(CDB) -o packer
$(CC) -O2 $^ -Isource/engine -o packer
core.cdb: packer $(CORE)
core.cdb: packer$(EXT) $(CORE)
@echo Packing core.cdb
./packer $(CORE)
chmod 644 out.cdb
mv out.cdb core.cdb
./packer$(EXT) $@ $(CORE)
source/engine/core.cdb.h: core.cdb
@echo Making $@
xxd -i $< > $@
jsc: tools/jso.c tools/libquickjs.a
$(CC) $^ -lm -Iquickjs -o $@
tools/libquickjs.a: $(BIN)/libquickjs.a
cp -f $(BIN)/libquickjs.a tools
ICNSIZE = 16 32 128 256 512 1024
ICNNAME := $(addsuffix .png, $(ICNSIZE))
ICON = icons/moon.gif

View file

@ -115,8 +115,6 @@ console.trace = console.stack;
var say = console.say;
var print = console.print;
console.warn("GOT HERE");
console.doc = {
level: "Set level to output logging to console.",
info: "Output info level message.",
@ -143,7 +141,7 @@ function use(file, env, script)
console.info(`CACHE eval ${file} in ${profile.best_t(profile.now()-st)}`);
return;
}
console.info(`slurping ${file}`);
script ??= io.slurp(file);
script = `(function() { ${script}; })`;
var fn = os.eval(file,script);
@ -208,6 +206,7 @@ var timescale = 1;
var gggstart = game.engine_start;
game.engine_start = function(s) {
game.startengine = 1;
gggstart(function() {
world_start();
go_init();
@ -216,6 +215,8 @@ game.engine_start = function(s) {
}, process);
}
game.startengine = 0;
function process()
{
var dt = profile.secs(profile.now()) - frame_t;

View file

@ -278,7 +278,6 @@ Cmdline.register_order("play", function(argv) {
console.info(`Starting game with window size ${window.size} and render ${window.rendersize}.`);
game.engine_start(function() {
console.info(`eng start`);
global.mixin("scripts/sound.js");
global.app = actor.spawn("game.js");
if (project.icon) window.set_icon(project.icon);
@ -297,7 +296,7 @@ Cmdline.register_order("pack", function(str) {
say(`Packing into ${packname}`);
io.pack_engine(packname);
// io.pack_engine(packname);
io.chmod(packname, 666);
}, "Pack the game into the given name.", "NAME");
@ -479,6 +478,8 @@ function cmd_args(cmdargs)
}
Cmdline.orders[cmds[0]](cmds.slice(1));
if (!game.startengine)
os.exit(0);
}
Cmdline.register_order("clean", function(argv) {

View file

@ -2,7 +2,6 @@
#define SOKOL_TRACE_HOOKS
#define SOKOL_IMPL
#define SOKOL_NO_ENTRY
#include "sokol/sokol_audio.h"
#include "sokol/sokol_time.h"
#include "sokol/sokol_args.h"

View file

@ -644,6 +644,7 @@ JSValue js_os_sys(JSContext *js, JSValue this, int argc, JSValue *argv)
}
JSC_CCALL(os_quit, quit();)
JSC_CCALL(os_exit, exit(js2number(argv[0]));)
JSC_CCALL(os_reindex_static, cpSpaceReindexStatic(space));
JSC_CCALL(os_gc, script_gc());
JSC_SSCALL(os_eval, ret = script_eval(str, str2))
@ -712,6 +713,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, sys, 0),
MIST_FUNC_DEF(os, system, 1),
MIST_FUNC_DEF(os, quit, 0),
MIST_FUNC_DEF(os, exit, 1),
MIST_FUNC_DEF(os, reindex_static, 0),
MIST_FUNC_DEF(os, gc, 0),
MIST_FUNC_DEF(os, capture, 5),
@ -1007,8 +1009,6 @@ JSValue js_io_chmod(JSContext *js, JSValue this, int argc, JSValue *argv)
JSC_SCALL(io_save_qoa, save_qoa(str))
JSC_SCALL(io_pack_engine, pack_engine(str))
static const JSCFunctionListEntry js_io_funcs[] = {
MIST_FUNC_DEF(io, exists,1),
MIST_FUNC_DEF(io, ls, 0),
@ -1022,7 +1022,6 @@ static const JSCFunctionListEntry js_io_funcs[] = {
MIST_FUNC_DEF(io, slurpbytes, 1),
MIST_FUNC_DEF(io, slurpwrite, 2),
MIST_FUNC_DEF(io, save_qoa,1),
MIST_FUNC_DEF(io, pack_engine, 1),
};
JSC_CCALL(debug_draw_gameobject, gameobject_draw_debug(js2gameobject(argv[0]));)

7833
source/engine/miniz.c Normal file

File diff suppressed because it is too large Load diff

1422
source/engine/miniz.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,6 @@
#define SOKOL_GLES3
#elif __WIN32
#define SOKOL_D3D11
#define SOKOL_WIN32_FORCE_MAIN
#elif __APPLE__
#define SOKOL_METAL
#endif

View file

@ -15,7 +15,7 @@
#include "font.h"
#include <fcntl.h>
#include "cdb.h"
#include "miniz.h"
#ifndef __EMSCRIPTEN__
#include <ftw.h>
@ -40,8 +40,8 @@ struct dirent *c_dirent = NULL;
char pathbuf[MAXPATH + 1];
static struct cdb corecdb;
static struct cdb game_cdb;
static mz_zip_archive corecdb;
static mz_zip_archive game_cdb;
int LOADED_GAME = 0;
uint8_t *gamebuf;
@ -49,7 +49,7 @@ uint8_t *gamebuf;
static void response_cb(const sfetch_response_t *r)
{
if (r->fetched) {
cdb_initf(&game_cdb, r->data.ptr, r->data.size);
mz_zip_reader_init_mem(&game_cdb, r->data.ptr, r->data.size,0);
LOADED_GAME = 1;
}
if (r->finished) {
@ -78,7 +78,7 @@ void resources_init() {
}
});
cdb_initf(&corecdb, core_cdb, core_cdb_len);
mz_zip_reader_init_mem(&corecdb, core_cdb, core_cdb_len, 0);
}
char *get_filename_from_path(char *path, int extension) {
@ -160,6 +160,7 @@ char **ls(const char *path)
#else
void fill_extensions(char *paths, const char *path, const char *ext)
{};
char **ls(const char *path) { return NULL; }
#endif
char *str_replace_ext(const char *s, const char *newext) {
@ -172,32 +173,11 @@ char *str_replace_ext(const char *s, const char *newext) {
return ret;
}
FILE *path_open(const char *tag, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
vsnprintf(pathbuf, MAXPATH+1, fmt, args);
va_end(args);
FILE *f = fopen(pathbuf, tag);
return f;
}
void *cdb_slurp(struct cdb *cdb, const char *file, size_t *size)
{
unsigned vlen, vpos;
vpos = cdb_datapos(cdb);
vlen = cdb_datalen(cdb);
char *data = malloc(vlen+1);
cdb_read(cdb, data, vlen, vpos);
if (size) *size = vlen;
return data;
}
int fexists(const char *path)
{
int len = strlen(path);
if (cdb_find(&game_cdb, path, len)) return 1;
else if (cdb_find(&corecdb, path, len)) return 1;
if (mz_zip_reader_locate_file(&game_cdb, path, NULL, 0) != -1) return 1;
else if (mz_zip_reader_locate_file(&corecdb, path, NULL, 0) != -1) return 1;
else if (!access(path, R_OK)) return 1;
return 0;
@ -205,7 +185,6 @@ int fexists(const char *path)
void *os_slurp(const char *file, size_t *size)
{
YughInfo("Slurping %s from the OS.\n", file);
FILE *f;
jump:
@ -227,12 +206,13 @@ void *os_slurp(const char *file, size_t *size)
void *slurp_file(const char *filename, size_t *size)
{
void *ret;
if (!access(filename, R_OK))
return os_slurp(filename, size);
else if (cdb_find(&game_cdb, filename, strlen(filename)))
return cdb_slurp(&game_cdb, filename, size);
else if (cdb_find(&corecdb, filename, strlen(filename)))
return cdb_slurp(&corecdb, filename, size);
else if (ret = mz_zip_reader_extract_file_to_heap(&game_cdb, filename, size, 0))
return ret;
else if (ret = mz_zip_reader_extract_file_to_heap(&corecdb, filename, size, 0))
return ret;
return NULL;
}
@ -311,58 +291,3 @@ int slurp_write(const char *txt, const char *filename, size_t len) {
fclose(f);
return 0;
}
#ifndef __EMSCRIPTEN__
static struct cdb_make cdbm;
static const char *pack_ext[] = {".qoi", ".qoa", ".js", ".wav", ".mp3", ".png", ".sf2", ".midi", ".lvl", ".glsl", ".ttf", ".json", ".jso"};
static int ftw_pack(const char *path, const struct stat *sb, int flag)
{
if (flag != FTW_F) return 0;
int pack = 0;
char *ext = strrchr(path, '.');
if (!ext)
return 0;
for (int i = 0; i < 13; i++) {
if (!strcmp(ext, pack_ext[i])) {
pack = 1;
break;
}
}
if (!pack) return 0;
size_t len;
void *file = slurp_file(path, &len);
cdb_make_add(&cdbm, &path[2], strlen(&path[2]), file, len);
free(file);
return 0;
}
void pack_engine(const char *fname)
{
int fd;
char *key, *va;
unsigned klen, vlen;
fd = creat(fname, O_RDWR);
if (fd == -1) {
YughError("Couldn't make file at %s.", fname);
return;
}
cdb_make_start(&cdbm, fd);
ftw(".", ftw_pack, 20);
cdb_make_finish(&cdbm);
close(fd);
}
#else
void pack_engine(const char *fname){
YughError("Cannot pack engine on a web build.");
}
char **ls(const char *path) { return NULL; }
#endif

View file

@ -14,7 +14,6 @@ char *get_filename_from_path(char *path, int extension);
char *get_directory_from_path(char *path);
char *str_replace_ext(const char *s, const char *newext);
FILE *res_open(char *path, const char *tag);
FILE *path_open(const char *tag, const char *fmt, ...);
char **ls(const char *path);
int cp(const char *p1, const char *p2);
int fexists(const char *path);
@ -28,8 +27,6 @@ int slurp_write(const char *txt, const char *filename, size_t len);
char *seprint(char *fmt, ...);
void pack_engine(const char *fname);
static inline void *stbarrdup(void *mem, size_t size, int len) {
void *out = NULL;
arrsetlen(out, len);

View file

@ -73,11 +73,9 @@ JSValue script_eval(const char *file, const char *script)
void script_call_sym(JSValue sym, int argc, JSValue *argv) {
if (!JS_IsFunction(js, sym)) return;
JSValue g = JS_GetGlobalObject(js);
JSValue ret = JS_Call(js, sym, JS_GetGlobalObject(js), argc, argv);
JSValue ret = JS_Call(js, sym, JS_UNDEFINED, argc, argv);
js_print_exception(ret);
JS_FreeValue(js, ret);
JS_FreeValue(js,g);
}
void out_memusage(const char *file)

View file

@ -1,15 +0,0 @@
name: build
on: [push, pull_request]
jobs:
generic:
strategy:
matrix:
os: [windows, ubuntu, macos]
name: ${{matrix.os}}
runs-on: ${{matrix.os}}-latest
steps:
- uses: actions/checkout@v1
- name: test
run: cd test && python test_all.py

View file

@ -1 +0,0 @@
{"textures":[{"extensions":{"KHR_texture_basisu":{""}}:{""""},""}]}

Binary file not shown.

View file

@ -1,53 +0,0 @@
{
"scenes" : [
{
"nodes" : [ 0 ]
}
],
"nodes" : [
{
"mesh" : 0
}
],
"meshes" : [
{
"primitives" : [ {
"attributes" : {
"POSITION" : 0
}
} ]
}
],
"buffers" : [
{
"uri" : "data:application/octet-stream;base64,AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAA",
"byteLength" : 36
}
],
"bufferViews" : [
{
"buffer" : 0,
"byteOffset" : 0,
"byteLength" : 36,
"target" : 34962
}
],
"accessors" : [
{
"bufferView" : 0,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 3,
"type" : "VEC3",
"max" : [ 1.0, 1.0, 0.0 ],
"min" : [ 0.0, 0.0, 0.0 ]
}
],
"asset" : {
"version" : "2.0"
}
}

View file

@ -1,172 +0,0 @@
{
"accessors": [
{
"bufferView": 0,
"componentType": 5126,
"count": 2549,
"type": "VEC2"
},
{
"bufferView": 1,
"componentType": 5126,
"count": 2549,
"type": "VEC3"
},
{
"bufferView": 2,
"componentType": 5126,
"count": 2549,
"type": "VEC4"
},
{
"bufferView": 3,
"componentType": 5126,
"count": 2549,
"type": "VEC3",
"max": [
0.05445001,
0.130220339,
0.0544500239
],
"min": [
-0.05445001,
-0.130220339,
-0.0544500239
]
},
{
"bufferView": 4,
"componentType": 5123,
"count": 13530,
"type": "SCALAR"
}
],
"asset": {
"generator": "glTF Tools for Unity",
"version": "2.0"
},
"bufferViews": [
{
"buffer": 0,
"byteLength": 20392
},
{
"buffer": 0,
"byteOffset": 20392,
"byteLength": 30588
},
{
"buffer": 0,
"byteOffset": 50980,
"byteLength": 40784
},
{
"buffer": 0,
"byteOffset": 91764,
"byteLength": 30588
},
{
"buffer": 0,
"byteOffset": 122352,
"byteLength": 27060
}
],
"buffers": [
{
"uri": "WaterBottle.bin",
"byteLength": 149412
}
],
"images": [
{
"uri": "WaterBottle_baseColor.png"
},
{
"uri": "WaterBottle_occlusionRoughnessMetallic.png"
},
{
"uri": "WaterBottle_normal.png"
},
{
"uri": "WaterBottle_emissive.png"
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"TEXCOORD_0": 0,
"NORMAL": 1,
"TANGENT": 2,
"POSITION": 3
},
"indices": 4,
"material": 0
}
],
"name": "WaterBottle"
}
],
"materials": [
{
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 0
},
"metallicRoughnessTexture": {
"index": 1
}
},
"normalTexture": {
"index": 2
},
"occlusionTexture": {
"index": 1
},
"emissiveFactor": [
1.0,
1.0,
1.0
],
"emissiveTexture": {
"index": 3
},
"name": "BottleMat"
}
],
"nodes": [
{
"mesh": 0,
"rotation": [
0.0,
1.0,
0.0,
0.0
],
"name": "WaterBottle"
}
],
"scene": 0,
"scenes": [
{
"nodes": [
0
]
}
],
"textures": [
{
"source": 0
},
{
"source": 1
},
{
"source": 2
},
{
"source": 3
}
]
}

View file

@ -1,224 +0,0 @@
#
# AFL dictionary for JSON
# -----------------------
#
# Just the very basics.
#
# Inspired by a dictionary by Jakub Wilk <jwilk@jwilk.net>
#
"0"
",0"
":0"
"0:"
"-1.2e+3"
"true"
"false"
"null"
"\"\""
",\"\""
":\"\""
"\"\":"
"{}"
",{}"
":{}"
"{\"\":0}"
"{{}}"
"[]"
",[]"
":[]"
"[0]"
"[[]]"
"''"
"\\"
"\\b"
"\\f"
"\\n"
"\\r"
"\\t"
"\\u0000"
"\\x00"
"\\0"
"\\uD800\\uDC00"
"\\uDBFF\\uDFFF"
"\"\":0"
"//"
"/**/"
#
# AFL dictionary for GLTF core
# -----------------------
"5120"
"5121"
"5122"
"5123"
"5125"
"5126"
"\"BLEND\""
"\"CUBICSPLINE\""
"\"LINEAR\""
"\"MASK\""
"\"MAT2\""
"\"MAT3\""
"\"MAT4\""
"\"OPAQUE\""
"\"SCALAR\""
"\"STEP\""
"\"VEC2\""
"\"VEC3\""
"\"VEC4\""
"\"accessor\""
"\"accessors\""
"\"alphaCutoff\""
"\"alphaMode\""
"\"animations\""
"\"aspectRatio\""
"\"asset\""
"\"attributes\""
"\"baseColorFactor\""
"\"baseColorTexture\""
"\"bufferView\""
"\"bufferViews\""
"\"buffer\""
"\"buffers\""
"\"byteLength\""
"\"byteOffset\""
"\"byteStride\""
"\"camera\""
"\"cameras\""
"\"channel\""
"\"channels\""
"\"children\""
"\"componentType\""
"\"copyright\""
"\"count\""
"\"doubleSided\""
"\"emissiveFactor\""
"\"emissiveTexture\""
"\"extensionsRequired\""
"\"extensionsUsed\""
"\"extensions\""
"\"extras\""
"\"generator\""
"\"image\""
"\"images\""
"\"index\""
"\"indices\""
"\"input\""
"\"interpolation\""
"\"inverseBindMatrices\""
"\"joints\""
"\"magFilter\""
"\"material\""
"\"materials\""
"\"matrix\""
"\"max\""
"\"mesh\""
"\"meshes\""
"\"metallicFactor\""
"\"metallicRoughnessTexture\""
"\"mimeType\""
"\"minFilter\""
"\"minVersion\""
"\"min\""
"\"mode\""
"\"name\""
"\"node\""
"\"nodes\""
"\"normalTextureInfo\""
"\"normalTexture\""
"\"normalized\""
"\"occlusionTextureInfo\""
"\"occlusionTexture\""
"\"orthographic\""
"\"output\""
"\"path\""
"\"pbrMetallicRoughness\""
"\"perspective\""
"\"primitive\""
"\"primitives\""
"\"rotation\""
"\"roughnessFactor\""
"\"sampler\""
"\"samplers\""
"\"scale\""
"\"scene\""
"\"scenes\""
"\"skeleton\""
"\"skin\""
"\"skins\""
"\"source\""
"\"sparse\""
"\"strength\""
"\"target\""
"\"targets\""
"\"texCoord\""
"\"textureInfo\""
"\"texture\""
"\"textures\""
"\"translation\""
"\"type\""
"\"uri\""
"\"values\""
"\"version\""
"\"weights\""
"\"wrapS\""
"\"wrapT\""
"\"xmag\""
"\"yfov\""
"\"ymag\""
"\"zfar\""
"\"znear\""
#
# AFL dictionary for GLTF extensions
# -----------------------
"\"KHR_materials_unlit\""
"\"KHR_texture_basisu\""
"\"KHR_materials_pbrSpecularGlossiness\""
"\"diffuseFactor\""
"\"diffuseTexture\""
"\"specularFactor\""
"\"glossinessFactor\""
"\"specularGlossinessTexture\""
"\"KHR_texture_transform\""
"\"offset\""
"\"rotation\""
"\"scale\""
"\"texCoord\""
"\"KHR_lights_punctual\""
"\"color\""
"\"intensity\""
"\"type\""
"\"range\""
"\"innerConeAngle\""
"\"outerConeAngle\""
"\"KHR_materials_transmission\""
"\"transmissionFactor\""
"\"transmissionTexture\""
"\"KHR_materials_volume\""
"\"thicknessFactor\""
"\"thicknessTexture\""
"\"attenuationColor\""
"\"attenuationDistance\""
"\"KHR_materials_sheen\""
"\"sheenColorFactor\""
"\"sheenColorTexture\""
"\"sheenRoughnessFactor\""
"\"sheenRoughnessTexture\""
"\"KHR_materials_emissive_strength\""
"\"emissiveStrength"\""

View file

@ -1,22 +0,0 @@
/* How to fuzz:
clang main.c -O2 -g -fsanitize=address,fuzzer -o fuzz
cp -r data temp
./fuzz temp/ -dict=gltf.dict -jobs=12 -workers=12
*/
#define CGLTF_IMPLEMENTATION
#include "../cgltf.h"
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
{
cgltf_options options = {cgltf_file_type_invalid};
cgltf_data* data = NULL;
cgltf_result res = cgltf_parse(&options, Data, Size, &data);
if (res == cgltf_result_success)
{
cgltf_validate(data);
cgltf_free(data);
}
return 0;
}

View file

@ -1,68 +0,0 @@
cmake_minimum_required( VERSION 2.8 )
include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
set( EXE_NAME cgltf_test )
add_executable( ${EXE_NAME} main.c )
set_property( TARGET ${EXE_NAME} PROPERTY C_STANDARD 99 )
if(MSVC)
target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
set( EXE_NAME test_conversion )
add_executable( ${EXE_NAME} test_conversion.cpp )
set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
if(MSVC)
target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
set( EXE_NAME test_write )
add_executable( ${EXE_NAME} test_write.cpp )
set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
if(MSVC)
target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
set( EXE_NAME test_math )
add_executable( ${EXE_NAME} test_math.cpp )
set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
if(MSVC)
target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
set( EXE_NAME test_strings )
add_executable( ${EXE_NAME} test_strings.cpp )
set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
if(MSVC)
target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )

View file

@ -1,38 +0,0 @@
#define CGLTF_IMPLEMENTATION
#include "../cgltf.h"
#include <stdio.h>
int main(int argc, char** argv)
{
if (argc < 2)
{
printf("err\n");
return -1;
}
cgltf_options options;
memset(&options, 0, sizeof(cgltf_options));
cgltf_data* data = NULL;
cgltf_result result = cgltf_parse_file(&options, argv[1], &data);
if (result == cgltf_result_success)
result = cgltf_load_buffers(&options, data, argv[1]);
if (result == cgltf_result_success)
result = cgltf_validate(data);
printf("Result: %d\n", result);
if (result == cgltf_result_success)
{
printf("Type: %u\n", data->file_type);
printf("Meshes: %u\n", (unsigned)data->meshes_count);
}
cgltf_free(data);
return result;
}

View file

@ -1,76 +0,0 @@
#!/usr/bin/env python
import os, sys
from sys import platform
num_tested = 0
num_errors = 0
def get_executable_path(name):
if platform == "win32":
return "build\\Debug\\" + name
else:
return "build/" + name
def is_ascii(s):
return all(ord(c) < 128 for c in s)
def collect_files(path, type, name):
global num_tested
global num_errors
exe = get_executable_path(name)
for the_file in os.listdir(path):
file_path = os.path.join(os.path.normpath(path), the_file)
if os.path.isfile(file_path):
if the_file.endswith(type):
num_tested = num_tested +1
if is_ascii(file_path):
print("### " + name + " " + file_path)
result = os.system("{0} \"{1}\"".format(exe, file_path))
print("### Result: " + str(result) + "\n")
if result != 0:
num_errors = num_errors + 1
print("Error.")
sys.exit(1)
elif os.path.isdir(file_path):
collect_files(file_path, type, name)
if __name__ == "__main__":
if not os.path.exists("build/"):
os.makedirs("build/")
os.chdir("build/")
os.system("cmake ..")
if os.system("cmake --build .") != 0:
print("Unable to build.")
exit(1)
os.chdir("..")
if not os.path.exists("glTF-Sample-Models/"):
os.system("git init glTF-Sample-Models")
os.chdir("glTF-Sample-Models")
os.system("git remote add origin https://github.com/KhronosGroup/glTF-Sample-Models.git")
os.system("git config core.sparsecheckout true")
f = open(".git/info/sparse-checkout", "w+")
f.write("2.0/*\n")
f.close()
os.system("git pull --depth=1 origin master")
os.chdir("..")
collect_files("glTF-Sample-Models/2.0/", ".glb", "cgltf_test")
collect_files("glTF-Sample-Models/2.0/", ".gltf", "cgltf_test")
collect_files("glTF-Sample-Models/2.0/", ".glb", "test_conversion")
collect_files("glTF-Sample-Models/2.0/", ".gltf", "test_conversion")
collect_files("glTF-Sample-Models/2.0/", ".gltf", "test_write")
result = os.system(get_executable_path("test_math"))
if result != 0:
num_errors = num_errors + 1
print("Error.")
sys.exit(1)
result = os.system(get_executable_path("test_strings"))
if result != 0:
num_errors = num_errors + 1
print("Error.")
sys.exit(1)
print("Tested files: " + str(num_tested))
print("Errors: " + str(num_errors))

View file

@ -1,92 +0,0 @@
#define CGLTF_IMPLEMENTATION
#include "../cgltf.h"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <limits>
static bool is_near(cgltf_float a, cgltf_float b)
{
return std::abs(a - b) < 10 * std::numeric_limits<cgltf_float>::min();
}
int main(int argc, char** argv)
{
if (argc < 2)
{
printf("err\n");
return -1;
}
cgltf_options options = {};
cgltf_data* data = NULL;
cgltf_result result = cgltf_parse_file(&options, argv[1], &data);
if (result == cgltf_result_success)
result = cgltf_load_buffers(&options, data, argv[1]);
if (strstr(argv[1], "Draco"))
{
cgltf_free(data);
return 0;
}
if (result != cgltf_result_success)
return result;
//const cgltf_accessor* blobs = data->accessors;
cgltf_float element_float[16];
cgltf_uint element_uint[4];
for (cgltf_size blob_index = 0; blob_index < data->accessors_count; ++blob_index)
{
const cgltf_accessor* blob = data->accessors + blob_index;
if (blob->is_sparse)
{
cgltf_size nfloats = cgltf_num_components(blob->type) * blob->count;
cgltf_float* dense = (cgltf_float*) malloc(nfloats * sizeof(cgltf_float));
if (cgltf_accessor_unpack_floats(blob, dense, nfloats) < nfloats) {
printf("Unable to completely unpack a sparse accessor.\n");
return -1;
}
free(dense);
continue;
}
if (blob->has_max && blob->has_min)
{
cgltf_float min0 = std::numeric_limits<float>::max();
cgltf_float max0 = std::numeric_limits<float>::lowest();
for (cgltf_size index = 0; index < blob->count; index++)
{
cgltf_accessor_read_float(blob, index, element_float, 16);
min0 = std::min(min0, element_float[0]);
max0 = std::max(max0, element_float[0]);
}
if (!is_near(min0, blob->min[0]) || !is_near(max0, blob->max[0]))
{
printf("Computed [%f, %f] but expected [%f, %f]\n", min0, max0, blob->min[0], blob->max[0]);
return -1;
}
}
if (blob->has_max && blob->has_min && blob->component_type != cgltf_component_type_r_32f && blob->component_type != cgltf_component_type_invalid)
{
cgltf_uint min0 = std::numeric_limits<unsigned int>::max();
cgltf_uint max0 = std::numeric_limits<unsigned int>::lowest();
for ( cgltf_size index = 0; index < blob->count; index++ )
{
cgltf_accessor_read_uint( blob, index, element_uint, 4 );
min0 = std::min( min0, element_uint[0] );
max0 = std::max( max0, element_uint[0] );
}
if ( min0 != (unsigned int) blob->min[0] || max0 != (unsigned int) blob->max[0] )
{
printf( "Computed [%u, %u] but expected [%u, %u]\n", min0, max0, (unsigned int) blob->min[0], (unsigned int) blob->max[0] );
return -1;
}
}
}
cgltf_free(data);
return result;
}

View file

@ -1,52 +0,0 @@
#define CGLTF_IMPLEMENTATION
#include "../cgltf.h"
// Performs matrix-vector multiplication, as in (4x4) * (4x1) = (4x1)
static void transform(const cgltf_float matrix[16], const cgltf_float source[4], cgltf_float target[4]) {
target[0] = matrix[0] * source[0] + matrix[4] * source[1] + matrix[ 8] * source[2] + matrix[12] * source[3];
target[1] = matrix[1] * source[0] + matrix[5] * source[1] + matrix[ 9] * source[2] + matrix[13] * source[3];
target[2] = matrix[2] * source[0] + matrix[6] * source[1] + matrix[10] * source[2] + matrix[14] * source[3];
target[3] = matrix[3] * source[0] + matrix[7] * source[1] + matrix[11] * source[2] + matrix[15] * source[3];
}
static void set(cgltf_float target[3], float x, float y, float z) {
target[0] = x;
target[1] = y;
target[2] = z;
}
static void check(cgltf_float target[3], float x, float y, float z) {
if (target[0] != x || target[1] != y || target[2] != z) {
fprintf(stderr, "Mismatch detected.\n");
exit(1);
}
}
int main(int, char**)
{
cgltf_node node = {};
cgltf_float matrix[16];
cgltf_float source[4] = {1, 2, 3, 1};
cgltf_float target[4];
set(node.scale, 1, 1, 1);
set(node.translation, 1, 0, 0);
cgltf_node_transform_local(&node, matrix);
transform(matrix, source, target);
check(target, 2, 2, 3);
set(node.scale, 3, 1, 1);
set(node.translation, 0, 0, 0);
cgltf_node_transform_local(&node, matrix);
transform(matrix, source, target);
check(target, 3, 2, 3);
set(node.scale, 1, 3, 1);
set(node.translation, 1, 0, 0);
cgltf_node_transform_local(&node, matrix);
transform(matrix, source, target);
check(target, 2, 6, 3);
return 0;
}

View file

@ -1,57 +0,0 @@
#define CGLTF_IMPLEMENTATION
#include "../cgltf.h"
#include <cstring>
static void check(const char* a, const char* b, cgltf_size size) {
if (strcmp(a, b) != 0 || strlen(a) != size) {
fprintf(stderr, "Mismatch detected.\n");
exit(1);
}
}
int main(int, char**)
{
char string[64];
cgltf_size size;
// cgltf_decode_string
strcpy(string, "");
size = cgltf_decode_string(string);
check(string, "", size);
strcpy(string, "nothing to replace");
size = cgltf_decode_string(string);
check(string, "nothing to replace", size);
strcpy(string, "\\\" \\/ \\\\ \\b \\f \\r \\n \\t \\u0030");
size = cgltf_decode_string(string);
check(string, "\" / \\ \b \f \r \n \t 0", size);
strcpy(string, "test \\u121b\\u130d\\u1294\\u1276\\u127d test");
size = cgltf_decode_string(string);
check(string, "test ማግኔቶች test", size);
// cgltf_decode_uri
strcpy(string, "");
size = cgltf_decode_uri(string);
check(string, "", size);
strcpy(string, "nothing to replace");
size = cgltf_decode_uri(string);
check(string, "nothing to replace", size);
strcpy(string, "%2F%D0%BA%D0%B8%D1%80%D0%B8%D0%BB%D0%BB%D0%B8%D1%86%D0%B0");
size = cgltf_decode_uri(string);
check(string, "/кириллица", size);
strcpy(string, "test%20%E1%88%9B%E1%8C%8D%E1%8A%94%E1%89%B6%E1%89%BD%20test");
size = cgltf_decode_uri(string);
check(string, "test ማግኔቶች test", size);
strcpy(string, "%%2F%X%AX%%2F%%");
size = cgltf_decode_uri(string);
check(string, "%/%X%AX%/%%", size);
return 0;
}

View file

@ -1,45 +0,0 @@
#define CGLTF_IMPLEMENTATION
#define CGLTF_WRITE_IMPLEMENTATION
#include "../cgltf_write.h"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <limits>
int main(int argc, char** argv)
{
if (argc < 2)
{
printf("err\n");
return -1;
}
cgltf_options options = {};
cgltf_data* data0 = NULL;
cgltf_result result = cgltf_parse_file(&options, argv[1], &data0);
// Silently skip over files that are unreadable since this is a writing test.
if (result != cgltf_result_success)
{
return cgltf_result_success;
}
result = cgltf_write_file(&options, "out.gltf", data0);
if (result != cgltf_result_success)
{
return result;
}
cgltf_data* data1 = NULL;
result = cgltf_parse_file(&options, "out.gltf", &data1);
if (result != cgltf_result_success)
{
return result;
}
if (data0->meshes_count != data1->meshes_count) {
return -1;
}
cgltf_free(data1);
cgltf_free(data0);
return cgltf_result_success;
}

View file

@ -1 +0,0 @@
github: mackron

View file

@ -1,12 +0,0 @@
docs
tests/testvectors/opus/*
!tests/testvectors/opus/DO_NOT_DELETE.md
tests/bin/*
!tests/bin/DO_NOT_DELETE.md
tests/build
tests/flac/include
tests/flac/lib
tests/testvectors/flac/tests/*
!tests/testvectors/flac/tests/DO_NOT_DELETE.md
todo
_private

View file

@ -1,3 +0,0 @@
[submodule "tests/external/miniaudio"]
path = tests/external/miniaudio
url = https://github.com/dr-soft/miniaudio.git

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,676 +0,0 @@
// Public Domain. See "unlicense" statement at the end of this file.
// NOTE: This is still very much work in progress and is only being updated as I need it. You don't want to be using this library
// in its current state.
// QUICK NOTES
// - This library does not use SSE for its basic types (vec4, etc.). Rationale: 1) It keeps things simple; 2) SSE is not always
// faster than the FPU(s) on modern CPUs; 3) The library can always implement functions that work on __m128 variables directly
// in the future if the need arises; 4) It doesn't work well with the pass-by-value API this library uses.
// - Use DISABLE_SSE to disable SSE optimized functions.
// - Angles are always specified in radians, unless otherwise noted. Rationale: Consistency with the standard library and most
// other math libraries.
// - Use radians() and degrees() to convert between the two.
#ifndef dr_math_h
#define dr_math_h
#include <math.h>
#if defined(_MSC_VER)
#define DR_MATHCALL static __forceinline
#else
#define DR_MATHCALL static inline
#endif
#define DR_PI 3.14159265358979323846
#define DR_PIF 3.14159265358979323846f
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
float x;
float y;
float z;
float w;
} vec4;
typedef struct
{
float x;
float y;
float z;
} vec3;
typedef struct
{
float x;
float y;
} vec2;
typedef struct
{
vec4 col[4];
} mat4;
typedef struct
{
float x;
float y;
float z;
float w;
} quat;
// Radians to degrees.
DR_MATHCALL float dr_degrees(float radians)
{
return radians * 57.29577951308232087685f;
}
// Degrees to radians.
DR_MATHCALL float dr_radians(float degrees)
{
return degrees * 0.01745329251994329577f;
}
///////////////////////////////////////////////
//
// VEC4
//
///////////////////////////////////////////////
DR_MATHCALL vec4 vec4f(float x, float y, float z, float w)
{
vec4 result;
result.x = x;
result.y = y;
result.z = z;
result.w = w;
return result;
}
DR_MATHCALL vec4 vec4v(const float* v)
{
return vec4f(v[0], v[1], v[2], v[3]);
}
DR_MATHCALL vec4 vec4_zero()
{
return vec4f(0, 0, 0, 0);
}
DR_MATHCALL vec4 vec4_one()
{
return vec4f(1, 1, 1, 1);
}
DR_MATHCALL vec4 vec4_add(vec4 a, vec4 b)
{
return vec4f(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
}
DR_MATHCALL vec4 vec4_sub(vec4 a, vec4 b)
{
return vec4f(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
}
DR_MATHCALL vec4 vec4_mul(vec4 a, vec4 b)
{
return vec4f(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
}
DR_MATHCALL vec4 vec4_mul_1f(vec4 a, float x)
{
return vec4f(a.x * x, a.y * x, a.z * x, a.w * x);
}
DR_MATHCALL vec4 vec4_mul_mat4(vec4 v, mat4 m)
{
const vec4 m0 = m.col[0];
const vec4 m1 = m.col[1];
const vec4 m2 = m.col[2];
const vec4 m3 = m.col[3];
return vec4f(
m0.x*v.x + m0.y*v.y + m0.z*v.z + m0.w*v.w,
m1.x*v.x + m1.y*v.y + m1.z*v.z + m1.w*v.w,
m2.x*v.x + m2.y*v.y + m2.z*v.z + m2.w*v.w,
m3.x*v.x + m3.y*v.y + m3.z*v.z + m3.w*v.w
);
}
DR_MATHCALL vec4 vec4_div(vec4 a, vec4 b)
{
return vec4f(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);
}
///////////////////////////////////////////////
//
// VEC3
//
///////////////////////////////////////////////
DR_MATHCALL vec3 vec3f(float x, float y, float z)
{
vec3 result;
result.x = x;
result.y = y;
result.z = z;
return result;
}
DR_MATHCALL vec3 vec3v(const float* v)
{
return vec3f(v[0], v[1], v[2]);
}
DR_MATHCALL vec3 vec3_zero()
{
return vec3f(0, 0, 0);
}
DR_MATHCALL vec3 vec3_one()
{
return vec3f(1, 1, 1);
}
DR_MATHCALL vec3 vec3_add(vec3 a, vec3 b)
{
return vec3f(a.x + b.x, a.y + b.y, a.z + b.z);
}
DR_MATHCALL vec3 vec3_sub(vec3 a, vec3 b)
{
return vec3f(a.x - b.x, a.y - b.y, a.z - b.z);
}
DR_MATHCALL vec3 vec3_mul(vec3 a, vec3 b)
{
return vec3f(a.x * b.x, a.y * b.y, a.z * b.z);
}
DR_MATHCALL vec3 vec3_mul_1f(vec3 a, float x)
{
return vec3f(a.x * x, a.y * x, a.z * x);
}
DR_MATHCALL vec3 vec3_div(vec3 a, vec3 b)
{
return vec3f(a.x / b.x, a.y / b.y, a.z / b.z);
}
DR_MATHCALL float vec3_dot(vec3 a, vec3 b)
{
return a.x*b.x + a.y*b.y + a.z*b.z;
}
DR_MATHCALL float vec3_length2(vec3 a)
{
return vec3_dot(a, a);
}
DR_MATHCALL float vec3_length(vec3 a)
{
return sqrtf(vec3_length2(a));
}
DR_MATHCALL vec3 vec3_normalize(vec3 a)
{
float len = vec3_length(a);
return vec3f(
a.x / len,
a.y / len,
a.z / len
);
}
DR_MATHCALL vec3 vec3_cross(vec3 a, vec3 b)
{
return vec3f(
a.y*b.z - a.z*b.y,
a.z*b.x - a.x*b.z,
a.x*b.y - a.y*b.x
);
}
DR_MATHCALL vec3 vec3_triangle_normal(vec3 p1, vec3 p2, vec3 p3)
{
vec3 u = vec3_sub(p2, p1);
vec3 v = vec3_sub(p3, p1);
return vec3_normalize(vec3_cross(u, v));
}
///////////////////////////////////////////////
//
// VEC2
//
///////////////////////////////////////////////
DR_MATHCALL vec2 vec2f(float x, float y)
{
vec2 result;
result.x = x;
result.y = y;
return result;
}
DR_MATHCALL vec2 vec2v(const float* v)
{
return vec2f(v[0], v[1]);
}
DR_MATHCALL vec2 vec2_zero()
{
return vec2f(0, 0);
}
DR_MATHCALL vec2 vec2_one()
{
return vec2f(1, 1);
}
DR_MATHCALL vec2 vec2_add(vec2 a, vec2 b)
{
return vec2f(a.x + b.x, a.y + b.y);
}
DR_MATHCALL vec2 vec2_sub(vec2 a, vec2 b)
{
return vec2f(a.x - b.x, a.y - b.y);
}
DR_MATHCALL vec2 vec2_mul(vec2 a, vec2 b)
{
return vec2f(a.x * b.x, a.y * b.y);
}
DR_MATHCALL vec2 vec2_mul_1f(vec2 a, float x)
{
return vec2f(a.x * x, a.y * x);
}
DR_MATHCALL vec2 vec2_div(vec2 a, vec2 b)
{
return vec2f(a.x / b.x, a.y / b.y);
}
DR_MATHCALL float vec2_dot(vec2 a, vec2 b)
{
return a.x*b.x + a.y*b.y;
}
DR_MATHCALL float vec2_length2(vec2 a)
{
return vec2_dot(a, a);
}
DR_MATHCALL float vec2_length(vec2 a)
{
return sqrtf(vec2_length2(a));
}
DR_MATHCALL vec2 vec2_normalize(vec2 a)
{
float len = vec2_length(a);
return vec2f(
a.x / len,
a.y / len
);
}
DR_MATHCALL float vec2_angle(vec2 a, vec2 b)
{
return atanf(a.y / a.x) - atanf(b.y / b.x);
}
DR_MATHCALL vec2 vec2_rotate(vec2 a, float angleInRadians)
{
float c = cosf(angleInRadians);
float s = sinf(angleInRadians);
return vec2f(
a.x*c - a.y*s,
a.x*s + a.y*c
);
}
///////////////////////////////////////////////
//
// MAT4
//
///////////////////////////////////////////////
DR_MATHCALL mat4 mat4f(vec4 col0, vec4 col1, vec4 col2, vec4 col3)
{
mat4 result;
result.col[0] = col0;
result.col[1] = col1;
result.col[2] = col2;
result.col[3] = col3;
return result;
}
DR_MATHCALL mat4 mat4_identity()
{
mat4 result;
result.col[0] = vec4f(1, 0, 0, 0);
result.col[1] = vec4f(0, 1, 0, 0);
result.col[2] = vec4f(0, 0, 1, 0);
result.col[3] = vec4f(0, 0, 0, 1);
return result;
}
DR_MATHCALL mat4 mat4_ortho(float left, float right, float bottom, float top, float znear, float zfar)
{
float rml = right - left;
float tmb = top - bottom;
float fmn = zfar - znear;
float rpl = right + left;
float tpb = top + bottom;
float fpn = zfar + znear;
mat4 result;
result.col[0] = vec4f(2/rml, 0, 0, 0);
result.col[1] = vec4f(0, 2/tmb, 0, 0);
result.col[2] = vec4f(0, 0, -2/fmn, 0);
result.col[3] = vec4f(-(rpl/rml), -(tpb/tmb), -(fpn/fmn), 1);
return result;
}
DR_MATHCALL mat4 mat4_perspective(float fovy, float aspect, float znear, float zfar)
{
float f = (float)tan(DR_PI/2 - fovy/2);
mat4 result;
result.col[0] = vec4f(f / aspect, 0, 0, 0);
result.col[1] = vec4f(0, f, 0, 0);
result.col[2] = vec4f(0, 0, (zfar + znear) / (znear - zfar), -1);
result.col[3] = vec4f(0, 0, (2 * zfar * znear) / (znear - zfar), 0);
return result;
}
DR_MATHCALL mat4 mat4_vulkan_clip_correction()
{
mat4 result;
result.col[0] = vec4f(1, 0, 0, 0);
result.col[1] = vec4f(0, -1, 0, 0);
result.col[2] = vec4f(0, 0, 0.5f, 0);
result.col[3] = vec4f(0, 0, 0.5f, 1);
return result;
}
DR_MATHCALL mat4 mat4_translate(vec3 translation)
{
mat4 result;
result.col[0] = vec4f(1, 0, 0, 0);
result.col[1] = vec4f(0, 1, 0, 0);
result.col[2] = vec4f(0, 0, 1, 0);
result.col[3] = vec4f(translation.x, translation.y, translation.z, 1);
return result;
}
DR_MATHCALL mat4 mat4_rotate(float angleInRadians, vec3 axis)
{
float c = cosf(angleInRadians);
float s = sinf(angleInRadians);
float x = axis.x;
float y = axis.y;
float z = axis.z;
float xx = x*x;
float xy = x*y;
float xz = x*z;
float yy = y*y;
float yz = y*z;
float zz = z*z;
float xs = x*s;
float ys = y*s;
float zs = z*s;
mat4 result;
result.col[0] = vec4f(xx * (1 - c) + c, xy * (1 - c) - zs, xz * (1 - c) + ys, 0);
result.col[1] = vec4f(xy * (1 - c) + zs, yy * (1 - c) + c, yz * (1 - c) - xs, 0);
result.col[2] = vec4f(xz * (1 - c) - ys, yz * (1 - c) + xs, zz * (1 - c) + c, 0);
result.col[3] = vec4f(0, 0, 0, 1);
return result;
}
DR_MATHCALL mat4 mat4_scale(vec3 scale)
{
mat4 result;
result.col[0] = vec4f(scale.x, 0, 0, 0);
result.col[1] = vec4f(0, scale.y, 0, 0);
result.col[2] = vec4f(0, 0, scale.z, 0);
result.col[3] = vec4f(0, 0, 0, 1);
return result;
}
DR_MATHCALL mat4 mat4_mul(mat4 a, mat4 b)
{
const vec4 a0 = a.col[0];
const vec4 a1 = a.col[1];
const vec4 a2 = a.col[2];
const vec4 a3 = a.col[3];
const vec4 b0 = b.col[0];
const vec4 b1 = b.col[1];
const vec4 b2 = b.col[2];
const vec4 b3 = b.col[3];
mat4 result;
result.col[0] = vec4f(
a0.x*b0.x + a1.x*b0.y + a2.x*b0.z + a3.x*b0.w,
a0.y*b0.x + a1.y*b0.y + a2.y*b0.z + a3.y*b0.w,
a0.z*b0.x + a1.z*b0.y + a2.z*b0.z + a3.z*b0.w,
a0.w*b0.x + a1.w*b0.y + a2.w*b0.z + a3.w*b0.w
);
result.col[1] = vec4f(
a0.x*b1.x + a1.x*b1.y + a2.x*b1.z + a3.x*b1.w,
a0.y*b1.x + a1.y*b1.y + a2.y*b1.z + a3.y*b1.w,
a0.z*b1.x + a1.z*b1.y + a2.z*b1.z + a3.z*b1.w,
a0.w*b1.x + a1.w*b1.y + a2.w*b1.z + a3.w*b1.w
);
result.col[2] = vec4f(
a0.x*b2.x + a1.x*b2.y + a2.x*b2.z + a3.x*b2.w,
a0.y*b2.x + a1.y*b2.y + a2.y*b2.z + a3.y*b2.w,
a0.z*b2.x + a1.z*b2.y + a2.z*b2.z + a3.z*b2.w,
a0.w*b2.x + a1.w*b2.y + a2.w*b2.z + a3.w*b2.w
);
result.col[3] = vec4f(
a0.x*b3.x + a1.x*b3.y + a2.x*b3.z + a3.x*b3.w,
a0.y*b3.x + a1.y*b3.y + a2.y*b3.z + a3.y*b3.w,
a0.z*b3.x + a1.z*b3.y + a2.z*b3.z + a3.z*b3.w,
a0.w*b3.x + a1.w*b3.y + a2.w*b3.z + a3.w*b3.w
);
return result;
}
DR_MATHCALL vec4 mat4_mul_vec4(mat4 m, vec4 v)
{
const vec4 m0 = m.col[0];
const vec4 m1 = m.col[1];
const vec4 m2 = m.col[2];
const vec4 m3 = m.col[3];
return vec4f(
m0.x*v.x + m1.x*v.y + m2.x*v.z + m3.x*v.w,
m0.y*v.x + m1.y*v.y + m2.y*v.z + m3.y*v.w,
m0.z*v.x + m1.z*v.y + m2.z*v.z + m3.z*v.w,
m0.w*v.x + m1.w*v.y + m2.w*v.z + m3.w*v.w
);
}
///////////////////////////////////////////////
//
// QUAT
//
///////////////////////////////////////////////
DR_MATHCALL quat quatf(float x, float y, float z, float w)
{
quat result;
result.x = x;
result.y = y;
result.z = z;
result.w = w;
return result;
}
DR_MATHCALL quat quatv(const float* v)
{
return quatf(v[0], v[1], v[2], v[3]);
}
DR_MATHCALL quat quat_identity()
{
return quatf(0, 0, 0, 1);
}
///////////////////////////////////////////////
//
// TRANSFORM
//
///////////////////////////////////////////////
typedef struct
{
vec3 position;
quat rotation;
vec3 scale;
}transform_t;
DR_MATHCALL transform_t transform_init(vec3 position, quat rotation, vec3 scale)
{
transform_t result;
result.position = position;
result.rotation = rotation;
result.scale = scale;
return result;
}
DR_MATHCALL transform_t transform_identity()
{
transform_t result;
result.position = vec3_zero();
result.rotation = quat_identity();
result.scale = vec3_one();
return result;
}
DR_MATHCALL transform_t transform_translate(transform_t transform, vec3 offset)
{
transform_t result = transform;
result.position = vec3_add(transform.position, offset);
return result;
}
///////////////////////////////////////////////
//
// SSE IMPLEMENTATION
//
///////////////////////////////////////////////
// Not supporting SSE on x86/MSVC due to pass-by-value errors with aligned types.
#if (defined(_MSC_VER) && defined(_M_X64)) || defined(__SSE2__)
#define SUPPORTS_SSE
#endif
#if !defined(DISABLE_SSE) && defined(SUPPORTS_SSE)
#define ENABLE_SSE
#endif
#ifdef ENABLE_SSE
#if defined(__MINGW32__)
#include <intrin.h>
#endif
#include <emmintrin.h>
#endif
#ifdef __cplusplus
}
#endif
#endif //dr_math_h
/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
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 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.
For more information, please refer to <http://unlicense.org/>
*/

File diff suppressed because it is too large Load diff

View file

@ -1,793 +0,0 @@
// PCX image loader. Public domain. See "unlicense" statement at the end of this file.
// dr_pcx - v0.3.1 - 2018-09-11
//
// David Reid - mackron@gmail.com
// USAGE
//
// dr_pcx is a single-file library. To use it, do something like the following in one .c file.
// #define DR_PCX_IMPLEMENTATION
// #include "dr_pcx.h"
//
// You can then #include this file in other parts of the program as you would with any other header file. Do something like
// the following to load and decode an image:
//
// int width;
// int height;
// int components
// drpcx_uint8* pImageData = drpcx_load_file("my_image.pcx", DRPCX_FALSE, &width, &height, &components, 0);
// if (pImageData == NULL) {
// // Failed to load image.
// }
//
// ...
//
// drpcx_free(pImageData);
//
// The boolean parameter (second argument in the above example) is whether or not the image should be flipped upside down.
//
//
//
// OPTIONS
// #define these options before including this file.
//
// #define DR_PCX_NO_STDIO
// Disable drpcx_load_file().
//
//
//
// QUICK NOTES
// - 2-bpp/4-plane and 4-bpp/1-plane formats have not been tested.
#ifndef dr_pcx_h
#define dr_pcx_h
#include <stddef.h>
#if defined(_MSC_VER) && _MSC_VER < 1600
typedef signed char drpcx_int8;
typedef unsigned char drpcx_uint8;
typedef signed short drpcx_int16;
typedef unsigned short drpcx_uint16;
typedef signed int drpcx_int32;
typedef unsigned int drpcx_uint32;
typedef signed __int64 drpcx_int64;
typedef unsigned __int64 drpcx_uint64;
#else
#include <stdint.h>
typedef int8_t drpcx_int8;
typedef uint8_t drpcx_uint8;
typedef int16_t drpcx_int16;
typedef uint16_t drpcx_uint16;
typedef int32_t drpcx_int32;
typedef uint32_t drpcx_uint32;
typedef int64_t drpcx_int64;
typedef uint64_t drpcx_uint64;
#endif
typedef drpcx_uint8 drpcx_bool8;
typedef drpcx_uint32 drpcx_bool32;
#define DRPCX_TRUE 1
#define DRPCX_FALSE 0
#ifdef __cplusplus
extern "C" {
#endif
// Callback for when data is read. Return value is the number of bytes actually read.
typedef size_t (* drpcx_read_proc)(void* userData, void* bufferOut, size_t bytesToRead);
// Loads a PCX file using the given callbacks.
drpcx_uint8* drpcx_load(drpcx_read_proc onRead, void* pUserData, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents);
// Frees memory returned by drpcx_load() and family.
void drpcx_free(void* pReturnValueFromLoad);
#ifndef DR_PCX_NO_STDIO
// Loads an PCX file from an actual file.
drpcx_uint8* drpcx_load_file(const char* filename, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents);
#endif
// Helper for loading an PCX file from a block of memory.
drpcx_uint8* drpcx_load_memory(const void* data, size_t dataSize, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents);
#ifdef __cplusplus
}
#endif
#endif // dr_pcx_h
///////////////////////////////////////////////////////////////////////////////
//
// IMPLEMENTATION
//
///////////////////////////////////////////////////////////////////////////////
#ifdef DR_PCX_IMPLEMENTATION
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifndef DR_PCX_NO_STDIO
#include <stdio.h>
static size_t drpcx__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
{
return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData);
}
drpcx_uint8* drpcx_load_file(const char* filename, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents)
{
FILE* pFile;
#ifdef _MSC_VER
if (fopen_s(&pFile, filename, "rb") != 0) {
return NULL;
}
#else
pFile = fopen(filename, "rb");
if (pFile == NULL) {
return NULL;
}
#endif
drpcx_uint8* pImageData = drpcx_load(drpcx__on_read_stdio, pFile, flipped, x, y, internalComponents, desiredComponents);
fclose(pFile);
return pImageData;
}
#endif // DR_PCX_NO_STDIO
typedef struct
{
// A pointer to the beginning of the data. We use a char as the type here for easy offsetting.
const unsigned char* data;
size_t dataSize;
size_t currentReadPos;
} drpcx_memory;
static size_t drpcx__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead)
{
drpcx_memory* memory = (drpcx_memory*)pUserData;
assert(memory != NULL);
assert(memory->dataSize >= memory->currentReadPos);
size_t bytesRemaining = memory->dataSize - memory->currentReadPos;
if (bytesToRead > bytesRemaining) {
bytesToRead = bytesRemaining;
}
if (bytesToRead > 0) {
memcpy(bufferOut, memory->data + memory->currentReadPos, bytesToRead);
memory->currentReadPos += bytesToRead;
}
return bytesToRead;
}
drpcx_uint8* drpcx_load_memory(const void* data, size_t dataSize, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents)
{
drpcx_memory memory;
memory.data = (const unsigned char*)data;
memory.dataSize = dataSize;
memory.currentReadPos = 0;
return drpcx_load(drpcx__on_read_memory, &memory, flipped, x, y, internalComponents, desiredComponents);
}
typedef struct
{
drpcx_uint8 header;
drpcx_uint8 version;
drpcx_uint8 encoding;
drpcx_uint8 bpp;
drpcx_uint16 left;
drpcx_uint16 top;
drpcx_uint16 right;
drpcx_uint16 bottom;
drpcx_uint16 hres;
drpcx_uint16 vres;
drpcx_uint8 palette16[48];
drpcx_uint8 reserved1;
drpcx_uint8 bitPlanes;
drpcx_uint16 bytesPerLine;
drpcx_uint16 paletteType;
drpcx_uint16 screenSizeH;
drpcx_uint16 screenSizeV;
drpcx_uint8 reserved2[54];
} drpcx_header;
typedef struct
{
drpcx_read_proc onRead;
void* pUserData;
drpcx_bool32 flipped;
drpcx_header header;
drpcx_uint32 width;
drpcx_uint32 height;
drpcx_uint32 components; // 3 = RGB; 4 = RGBA. Only 3 and 4 are supported.
drpcx_uint8* pImageData;
} drpcx;
static drpcx_uint8 drpcx__read_byte(drpcx* pPCX)
{
drpcx_uint8 byte = 0;
pPCX->onRead(pPCX->pUserData, &byte, 1);
return byte;
}
static drpcx_uint8* drpcx__row_ptr(drpcx* pPCX, drpcx_uint32 row)
{
drpcx_uint32 stride = pPCX->width * pPCX->components;
drpcx_uint8* pRow = pPCX->pImageData;
if (pPCX->flipped) {
pRow += (pPCX->height - row - 1) * stride;
} else {
pRow += row * stride;
}
return pRow;
}
static drpcx_uint8 drpcx__rle(drpcx* pPCX, drpcx_uint8* pRLEValueOut)
{
drpcx_uint8 rleCount;
drpcx_uint8 rleValue;
rleValue = drpcx__read_byte(pPCX);
if ((rleValue & 0xC0) == 0xC0) {
rleCount = rleValue & 0x3F;
rleValue = drpcx__read_byte(pPCX);
} else {
rleCount = 1;
}
*pRLEValueOut = rleValue;
return rleCount;
}
drpcx_bool32 drpcx__decode_1bit(drpcx* pPCX)
{
drpcx_uint8 rleCount = 0;
drpcx_uint8 rleValue = 0;
switch (pPCX->header.bitPlanes)
{
case 1:
{
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
for (int bit = 0; (bit < 8) && ((x*8 + bit) < pPCX->width); ++bit) {
drpcx_uint8 mask = (1 << (7 - bit));
drpcx_uint8 paletteIndex = (rleValue & mask) >> (7 - bit);
pRow[0] = paletteIndex * 255;
pRow[1] = paletteIndex * 255;
pRow[2] = paletteIndex * 255;
pRow += 3;
}
}
}
return DRPCX_TRUE;
} break;
case 2:
case 3:
case 4:
{
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
for (int bit = 0; (bit < 8) && ((x*8 + bit) < pPCX->width); ++bit) {
drpcx_uint8 mask = (1 << (7 - bit));
drpcx_uint8 paletteIndex = (rleValue & mask) >> (7 - bit);
pRow[0] |= ((paletteIndex & 0x01) << c);
pRow += pPCX->components;
}
}
}
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
drpcx_uint8 paletteIndex = pRow[0];
for (drpcx_uint32 c = 0; c < pPCX->components; ++c) {
pRow[c] = pPCX->header.palette16[paletteIndex*3 + c];
}
pRow += pPCX->components;
}
}
return DRPCX_TRUE;
}
default: return DRPCX_FALSE;
}
}
drpcx_bool32 drpcx__decode_2bit(drpcx* pPCX)
{
drpcx_uint8 rleCount = 0;
drpcx_uint8 rleValue = 0;
switch (pPCX->header.bitPlanes)
{
case 1:
{
drpcx_uint8 paletteCGA[48];
paletteCGA[ 0] = 0x00; paletteCGA[ 1] = 0x00; paletteCGA[ 2] = 0x00; // #000000
paletteCGA[ 3] = 0x00; paletteCGA[ 4] = 0x00; paletteCGA[ 5] = 0xAA; // #0000AA
paletteCGA[ 6] = 0x00; paletteCGA[ 7] = 0xAA; paletteCGA[ 8] = 0x00; // #00AA00
paletteCGA[ 9] = 0x00; paletteCGA[10] = 0xAA; paletteCGA[11] = 0xAA; // #00AAAA
paletteCGA[12] = 0xAA; paletteCGA[13] = 0x00; paletteCGA[14] = 0x00; // #AA0000
paletteCGA[15] = 0xAA; paletteCGA[16] = 0x00; paletteCGA[17] = 0xAA; // #AA00AA
paletteCGA[18] = 0xAA; paletteCGA[19] = 0x55; paletteCGA[20] = 0x00; // #AA5500
paletteCGA[21] = 0xAA; paletteCGA[22] = 0xAA; paletteCGA[23] = 0xAA; // #AAAAAA
paletteCGA[24] = 0x55; paletteCGA[25] = 0x55; paletteCGA[26] = 0x55; // #555555
paletteCGA[27] = 0x55; paletteCGA[28] = 0x55; paletteCGA[29] = 0xFF; // #5555FF
paletteCGA[30] = 0x55; paletteCGA[31] = 0xFF; paletteCGA[32] = 0x55; // #55FF55
paletteCGA[33] = 0x55; paletteCGA[34] = 0xFF; paletteCGA[35] = 0xFF; // #55FFFF
paletteCGA[36] = 0xFF; paletteCGA[37] = 0x55; paletteCGA[38] = 0x55; // #FF5555
paletteCGA[39] = 0xFF; paletteCGA[40] = 0x55; paletteCGA[41] = 0xFF; // #FF55FF
paletteCGA[42] = 0xFF; paletteCGA[43] = 0xFF; paletteCGA[44] = 0x55; // #FFFF55
paletteCGA[45] = 0xFF; paletteCGA[46] = 0xFF; paletteCGA[47] = 0xFF; // #FFFFFF
drpcx_uint8 cgaBGColor = pPCX->header.palette16[0] >> 4;
drpcx_uint8 i = (pPCX->header.palette16[3] & 0x20) >> 5;
drpcx_uint8 p = (pPCX->header.palette16[3] & 0x40) >> 6;
//drpcx_uint8 c = (pPCX->header.palette16[3] & 0x80) >> 7; // Color or monochrome. How is monochrome handled?
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
for (int bit = 0; bit < 4; ++bit) {
if (x*4 + bit < pPCX->width) {
drpcx_uint8 mask = (3 << ((3 - bit) * 2));
drpcx_uint8 paletteIndex = (rleValue & mask) >> ((3 - bit) * 2);
drpcx_uint8 cgaIndex;
if (paletteIndex == 0) { // Background.
cgaIndex = cgaBGColor;
} else { // Foreground
cgaIndex = (((paletteIndex << 1) + p) + (i << 3));
}
pRow[0] = paletteCGA[cgaIndex*3 + 0];
pRow[1] = paletteCGA[cgaIndex*3 + 1];
pRow[2] = paletteCGA[cgaIndex*3 + 2];
pRow += 3;
}
}
}
}
// TODO: According to http://www.fysnet.net/pcxfile.htm, we should use the palette at the end of the file
// instead of the standard CGA palette if the version is equal to 5. With my test files the palette
// at the end of the file does not exist. Research this one.
if (pPCX->header.version == 5) {
drpcx_uint8 paletteMarker = drpcx__read_byte(pPCX);
if (paletteMarker == 0x0C) {
// TODO: Implement Me.
}
}
return DRPCX_TRUE;
};
case 4:
{
// NOTE: This is completely untested. If anybody knows where I can get a test file please let me know or send it through to me!
// TODO: Test Me.
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
for (int bitpair = 0; (bitpair < 4) && ((x*4 + bitpair) < pPCX->width); ++bitpair) {
drpcx_uint8 mask = (4 << (3 - bitpair));
drpcx_uint8 paletteIndex = (rleValue & mask) >> (3 - bitpair);
pRow[0] |= ((paletteIndex & 0x03) << (c*2));
pRow += pPCX->components;
}
}
}
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
drpcx_uint8 paletteIndex = pRow[0];
for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
pRow[c] = pPCX->header.palette16[paletteIndex*3 + c];
}
pRow += pPCX->components;
}
}
return DRPCX_TRUE;
};
default: return DRPCX_FALSE;
}
}
drpcx_bool32 drpcx__decode_4bit(drpcx* pPCX)
{
// NOTE: This is completely untested. If anybody knows where I can get a test file please let me know or send it through to me!
// TODO: Test Me.
if (pPCX->header.bitPlanes > 1) {
return DRPCX_FALSE;
}
drpcx_uint8 rleCount = 0;
drpcx_uint8 rleValue = 0;
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
for (int nibble = 0; (nibble < 2) && ((x*2 + nibble) < pPCX->width); ++nibble)
{
drpcx_uint8 mask = (4 << (1 - nibble));
drpcx_uint8 paletteIndex = (rleValue & mask) >> (1 - nibble);
pRow[0] |= ((paletteIndex & 0x0F) << (c*4));
pRow += pPCX->components;
}
}
}
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
drpcx_uint8 paletteIndex = pRow[0];
for (drpcx_uint32 c = 0; c < pPCX->components; ++c) {
pRow[c] = pPCX->header.palette16[paletteIndex*3 + c];
}
pRow += pPCX->components;
}
}
return DRPCX_TRUE;
}
drpcx_bool32 drpcx__decode_8bit(drpcx* pPCX)
{
drpcx_uint8 rleCount = 0;
drpcx_uint8 rleValue = 0;
drpcx_uint32 stride = pPCX->width * pPCX->components;
switch (pPCX->header.bitPlanes)
{
case 1:
{
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
if (x < pPCX->width) {
pRow[0] = rleValue;
pRow[1] = rleValue;
pRow[2] = rleValue;
pRow += 3;
}
}
}
// At this point we can know if we are dealing with a palette or a grayscale image by checking the next byte. If it's equal to 0x0C, we
// need to do a simple palette lookup.
drpcx_uint8 paletteMarker = drpcx__read_byte(pPCX);
if (paletteMarker == 0x0C) {
// A palette is present - we need to do a second pass.
drpcx_uint8 palette256[768];
if (pPCX->onRead(pPCX->pUserData, palette256, sizeof(palette256)) != sizeof(palette256)) {
return DRPCX_FALSE;
}
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
drpcx_uint8* pRow = pPCX->pImageData + (y * stride);
for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
drpcx_uint8 index = pRow[0];
pRow[0] = palette256[index*3 + 0];
pRow[1] = palette256[index*3 + 1];
pRow[2] = palette256[index*3 + 2];
pRow += 3;
}
}
}
return DRPCX_TRUE;
}
case 3:
case 4:
{
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
for (drpcx_uint32 c = 0; c < pPCX->components; ++c) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
if (x < pPCX->width) {
pRow[c] = rleValue;
pRow += pPCX->components;
}
}
}
}
return DRPCX_TRUE;
}
}
return DRPCX_TRUE;
}
drpcx_uint8* drpcx_load(drpcx_read_proc onRead, void* pUserData, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents)
{
if (onRead == NULL) return NULL;
if (desiredComponents > 4) return NULL;
drpcx pcx;
pcx.onRead = onRead;
pcx.pUserData = pUserData;
pcx.flipped = flipped;
if (onRead(pUserData, &pcx.header, sizeof(pcx.header)) != sizeof(pcx.header)) {
return NULL; // Failed to read the header.
}
if (pcx.header.header != 10) {
return NULL; // Not a PCX file.
}
if (pcx.header.encoding != 1) {
return NULL; // Not supporting non-RLE encoding. Would assume a value of 0 indicates raw, unencoded, but that is apparently never used.
}
if (pcx.header.bpp != 1 && pcx.header.bpp != 2 && pcx.header.bpp != 4 && pcx.header.bpp != 8) {
return NULL; // Unsupported pixel format.
}
if (pcx.header.left > pcx.header.right) {
drpcx_uint16 temp = pcx.header.left;
pcx.header.left = pcx.header.right;
pcx.header.right = temp;
}
if (pcx.header.top > pcx.header.bottom) {
drpcx_uint16 temp = pcx.header.top;
pcx.header.top = pcx.header.bottom;
pcx.header.bottom = temp;
}
pcx.width = pcx.header.right - pcx.header.left + 1;
pcx.height = pcx.header.bottom - pcx.header.top + 1;
pcx.components = (pcx.header.bpp == 8 && pcx.header.bitPlanes == 4) ? 4 : 3;
size_t dataSize = pcx.width * pcx.height * pcx.components;
pcx.pImageData = (drpcx_uint8*)calloc(1, dataSize); // <-- Clearing to zero is important! Required for proper decoding.
if (pcx.pImageData == NULL) {
return NULL; // Failed to allocate memory.
}
drpcx_bool32 result = DRPCX_FALSE;
switch (pcx.header.bpp)
{
case 1:
{
result = drpcx__decode_1bit(&pcx);
} break;
case 2:
{
result = drpcx__decode_2bit(&pcx);
} break;
case 4:
{
result = drpcx__decode_4bit(&pcx);
} break;
case 8:
{
result = drpcx__decode_8bit(&pcx);
} break;
}
if (!result) {
free(pcx.pImageData);
return NULL;
}
// There's an annoying amount of branching when loading PCX files so for simplicity I'm doing the component conversion as
// a second pass.
if (desiredComponents == 0) desiredComponents = pcx.components;
if (desiredComponents != (int)pcx.components) {
drpcx_uint8* pNewImageData = (drpcx_uint8*)malloc(pcx.width * pcx.height * desiredComponents);
if (pNewImageData == NULL) {
free(pcx.pImageData);
return NULL;
}
drpcx_uint8* pSrcData = pcx.pImageData;
drpcx_uint8* pDstData = pNewImageData;
if (desiredComponents < (int)pcx.components) {
// We're reducing the number of components. Just drop the excess.
for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
for (int c = 0; c < desiredComponents; ++c) {
pDstData[c] = pSrcData[c];
}
pSrcData += pcx.components;
pDstData += desiredComponents;
}
} else {
// We're increasing the number of components. Always ensure the alpha channel is set to 0xFF.
if (pcx.components == 1) {
for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
for (int c = 0; c < desiredComponents; ++c) {
pDstData[c] = pSrcData[0];
}
pSrcData += pcx.components;
pDstData += desiredComponents;
}
} else if (pcx.components == 2) {
for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
pDstData[0] = pSrcData[0];
pDstData[1] = pSrcData[1];
pDstData[2] = 0x00;
if (desiredComponents == 4) pDstData[3] = 0xFF;
pSrcData += pcx.components;
pDstData += desiredComponents;
}
} else {
assert(pcx.components == 3);
assert(desiredComponents == 4);
for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
pDstData[0] = pSrcData[0];
pDstData[1] = pSrcData[1];
pDstData[2] = pSrcData[2];
pDstData[3] = 0xFF;
pSrcData += pcx.components;
pDstData += desiredComponents;
}
}
}
free(pcx.pImageData);
pcx.pImageData = pNewImageData;
}
if (x) *x = pcx.width;
if (y) *y = pcx.height;
if (internalComponents) *internalComponents = pcx.components;
return pcx.pImageData;
}
void drpcx_free(void* pReturnValueFromLoad)
{
free(pReturnValueFromLoad);
}
#endif // DR_PCX_IMPLEMENTATION
// REVISION HISTORY
//
// v0.3.1 - 2018-09-11
// - Styling fixes.
// - Fix a typo.
//
// v0.3 - 2018-02-08
// - API CHANGE: Rename dr_* types to drpcx_*.
//
// v0.2c - 2018-02-07
// - Fix a crash.
//
// v0.2b - 2018-02-02
// - Fix compilation error.
//
// v0.2a - 2017-07-16
// - Change underlying type for booleans to unsigned.
//
// v0.2 - 2016-10-28
// - API CHANGE: Add a parameter to drpcx_load() and family to control the number of output components.
// - Use custom sized types rather than built-in ones to improve support for older MSVC compilers.
//
// v0.1c - 2016-10-23
// - A minor change to drpcx_bool8 and drpcx_bool32 types.
//
// v0.1b - 2016-10-11
// - Use drpcx_bool32 instead of the built-in "bool" type. The reason for this change is that it helps maintain API/ABI consistency
// between C and C++ builds.
//
// v0.1a - 2016-09-18
// - Change date format to ISO 8601 (YYYY-MM-DD)
//
// v0.1 - 2016-05-04
// - Initial versioned release.
// TODO
// - Test 2-bpp/4-plane and 4-bpp/1-plane formats.
/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
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 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.
For more information, please refer to <http://unlicense.org/>
*/

View file

@ -1,49 +0,0 @@
Building
========
Move into this directory and run the build script for the relevant platform. Run from this directory.
clear && ./build_opus && ./bin/dr_opus_test_0
Alternatively you can compile a specific test manually:
clear && gcc ./opus/dr_opus_test_0 -o ./bin/dr_opus_test_0 && ./bin/dr_opus_test_0
Test vectors will be loaded from the "testvectors" folder, relative to this directory. Therefore, you need to run
each test program from this directory:
./bin/dr_opus_test_0
Building and Running WAV Tests
------------------------------
The WAV tests use libsndfile as a benchmark. The tests programs dynamically link to libsndfile at runtime which
means you don't need to link to it at compile time. However, you will need the headers installed in a standard
location. The batch files for the Windows build will allow you to customize the include path. On the Windows build
you will need to drop two versions of libsndfile-1.dll into the bin directory. For the 32-bit build you need to
name it libsndfile-1-x86.dll and for the 64-bit build you need to name it libsndfile-1-x64.dll.
Test Vectors
============
In order to run certain tests you will need to download test vectors for the relevant project and place them into the
"testvectors" folder.
Opus
----
- Download both the original and new test vectors from https://opus-codec.org/testvectors/ and place them into
the "testvectors/opus" folder.
- Download the Ogg Opus test vectors from https://wiki.xiph.org/OggOpus/testvectors and place them into the
"testvectors/opus" folder.
- The folder structure should like like the following:
- testvectors
- opus
- opus_testvectors
- opus_newvectors
- oggopus
- failure_cases
- opus_multichannel_examples
FLAC
----
- Download the FLAC testbench from https://wiki.hydrogenaud.io/index.php?title=FLAC_decoder_testbench and place
them into the "testvectors/flac/testbench" folder.

View file

@ -1,4 +0,0 @@
#!/bin/sh
gcc ./flac/dr_flac_test_0.c -o ./bin/dr_flac_test_0 -std=c89 -ansi -pedantic -O3 -s -Wall -ldl
gcc ./flac/dr_flac_decoding.c -o ./bin/dr_flac_decoding -std=c89 -ansi -pedantic -Wall -O3 -s -lFLAC -ldl
gcc ./flac/dr_flac_seeking.c -o ./bin/dr_flac_seeking -std=c89 -ansi -pedantic -Wall -O3 -s -lFLAC -ldl

View file

@ -1,37 +0,0 @@
:: NOTES
::
:: These tests use libFLAC as a benchmark. Since Windows doesn't have good standard paths for library files, this
:: script will use "flac/include" as an additional search path for headers and "flac/lib/win32" as an additional
:: search path for libraries. You will need to place FLAC headers in the "flac/include/FLAC" directory and libogg
:: headers int the "flac/include/ogg" directory.
::
:: Examples are linked against "-lFLAC" and "-logg". These need to be placed in a standard directory or "flac/lib/win32".
@echo off
SET c_compiler=gcc
SET cpp_compiler=g++
:: Configure the "arch" option to test different instruction sets.
SET arch=
SET arch=-msse4.1
::SET arch=-mfpu=neon
:: libFLAC and libogg are required for benchmarking.
SET libFLAC=-I./flac/include -L./flac/lib/win32 -lFLAC -logg
:: C options
SET c_options=-std=c89 -ansi
:: C++ options
SET cpp_options=
SET options=-Wall -Wpedantic -pedantic -O3 -s -DNDEBUG %arch% %libFLAC%
SET buildc=%c_compiler% %c_options%
SET buildcpp=%cpp_compiler% %cpp_options%
@echo on
%buildc% ./flac/dr_flac_test_0.c -o ./bin/dr_flac_test_0.exe %options%
%buildcpp% ./flac/dr_flac_test_0.cpp -o ./bin/dr_flac_test_0_cpp.exe %options%
%buildc% ./flac/dr_flac_decoding.c -o ./bin/dr_flac_decoding.exe %options%
%buildcpp% ./flac/dr_flac_decoding.cpp -o ./bin/dr_flac_decoding_cpp.exe %options%

View file

@ -1,2 +0,0 @@
gcc ./mp3/dr_mp3_test_0.c -o ./bin/dr_mp3_test_0.exe -std=c89 -ansi -pedantic -Wall
g++ ./mp3/dr_mp3_test_0.cpp -o ./bin/dr_mp3_test_0.exe -pedantic -Wall

View file

@ -1 +0,0 @@
gcc ./opus/dr_opus_decoding.c -o ./bin/dr_opus_decoding.exe -std=c89 -ansi -pedantic -Wall -Wextra

View file

@ -1,38 +0,0 @@
:: NOTES
::
:: These tests use libsndfile as a benchmark. Since Windows doesn't have good standard paths for library files, this
:: script will use "wav/include" as an additional search path for headers. You will need to place sndfile.h to the
:: "wav/include" directory. Tests will link to libsndfile dynamically at run-time. On the Windows build you'll just
:: need to put a copy of the 32- and 64-bit versions of libsndfile-1.dll into the "bin" directory, with the names
:: libsndfile-1-x86.dll and libsndfile-1-x64.dll respectively. Both versions are required so that both the 32- and
:: 64-bit builds can be tested and benchmarked.
@echo off
SET c_compiler=gcc
SET cpp_compiler=g++
:: Configure the "arch" option to test different instruction sets.
SET arch=
SET arch=-msse4.1
::SET arch=-mfpu=neon
:: libsndfile is required for benchmarking.
SET libsndfile=-I./wav/include
:: C options
SET c_options=-std=c89 -ansi
:: C++ options
SET cpp_options=
SET options=-Wall -Wpedantic -pedantic -O3 -s -DNDEBUG %arch% %libsndfile%
SET buildc=%c_compiler% %c_options% %options%
SET buildcpp=%cpp_compiler% %cpp_options% %options%
@echo on
%buildc% ./wav/dr_wav_test_0.c -o ./bin/dr_wav_test_0.exe
%buildcpp% ./wav/dr_wav_test_0.cpp -o ./bin/dr_wav_test_0_cpp.exe
%buildc% ./wav/dr_wav_decoding.c -o ./bin/dr_wav_decoding.exe
%buildcpp% ./wav/dr_wav_decoding.cpp -o ./bin/dr_wav_decoding_cpp.exe

View file

@ -1,957 +0,0 @@
#ifdef _WIN32
#include <windows.h>
#endif
#if defined(_MSC_VER) || defined(__DMC__)
#else
#include <strings.h>
#endif
#include <stdio.h>
#include <stdarg.h>
#include <time.h> /* So we can seed the random number generator based on time. */
#include <errno.h>
#if !defined(_WIN32)
#include <dirent.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <dlfcn.h>
#endif
#include <stddef.h> /* For size_t. */
/* Sized types. Prefer built-in types. Fall back to stdint. */
#ifdef _MSC_VER
#if defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wlanguage-extension-token"
#pragma GCC diagnostic ignored "-Wlong-long"
#pragma GCC diagnostic ignored "-Wc++11-long-long"
#endif
typedef signed __int8 dr_int8;
typedef unsigned __int8 dr_uint8;
typedef signed __int16 dr_int16;
typedef unsigned __int16 dr_uint16;
typedef signed __int32 dr_int32;
typedef unsigned __int32 dr_uint32;
typedef signed __int64 dr_int64;
typedef unsigned __int64 dr_uint64;
#if defined(__clang__)
#pragma GCC diagnostic pop
#endif
#else
#define MA_HAS_STDINT
#include <stdint.h>
typedef int8_t dr_int8;
typedef uint8_t dr_uint8;
typedef int16_t dr_int16;
typedef uint16_t dr_uint16;
typedef int32_t dr_int32;
typedef uint32_t dr_uint32;
typedef int64_t dr_int64;
typedef uint64_t dr_uint64;
#endif
#ifdef MA_HAS_STDINT
typedef uintptr_t dr_uintptr;
#else
#if defined(_WIN32)
#if defined(_WIN64)
typedef dr_uint64 dr_uintptr;
#else
typedef dr_uint32 dr_uintptr;
#endif
#elif defined(__GNUC__)
#if defined(__LP64__)
typedef dr_uint64 dr_uintptr;
#else
typedef dr_uint32 dr_uintptr;
#endif
#else
typedef dr_uint64 dr_uintptr; /* Fallback. */
#endif
#endif
typedef dr_uint8 dr_bool8;
typedef dr_uint32 dr_bool32;
#define DR_TRUE 1
#define DR_FALSE 0
typedef void* dr_handle;
typedef void* dr_ptr;
typedef void (* dr_proc)(void);
#if defined(SIZE_MAX)
#define DR_SIZE_MAX SIZE_MAX
#else
#define DR_SIZE_MAX 0xFFFFFFFF
#endif
/*
Return Values:
0: Success
22: EINVAL
34: ERANGE
Not using symbolic constants for errors because I want to avoid #including errno.h
*/
int dr_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
{
size_t i;
if (dst == 0) {
return 22;
}
if (dstSizeInBytes == 0) {
return 34;
}
if (src == 0) {
dst[0] = '\0';
return 22;
}
for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
dst[i] = src[i];
}
if (i < dstSizeInBytes) {
dst[i] = '\0';
return 0;
}
dst[0] = '\0';
return 34;
}
int dr_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
{
size_t maxcount;
size_t i;
if (dst == 0) {
return 22;
}
if (dstSizeInBytes == 0) {
return 34;
}
if (src == 0) {
dst[0] = '\0';
return 22;
}
maxcount = count;
if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */
maxcount = dstSizeInBytes - 1;
}
for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
dst[i] = src[i];
}
if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
dst[i] = '\0';
return 0;
}
dst[0] = '\0';
return 34;
}
int dr_strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
{
char* dstorig;
if (dst == 0) {
return 22;
}
if (dstSizeInBytes == 0) {
return 34;
}
if (src == 0) {
dst[0] = '\0';
return 22;
}
dstorig = dst;
while (dstSizeInBytes > 0 && dst[0] != '\0') {
dst += 1;
dstSizeInBytes -= 1;
}
if (dstSizeInBytes == 0) {
return 22; /* Unterminated. */
}
while (dstSizeInBytes > 0 && src[0] != '\0') {
*dst++ = *src++;
dstSizeInBytes -= 1;
}
if (dstSizeInBytes > 0) {
dst[0] = '\0';
} else {
dstorig[0] = '\0';
return 34;
}
return 0;
}
int dr_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
{
char* dstorig;
if (dst == 0) {
return 22;
}
if (dstSizeInBytes == 0) {
return 34;
}
if (src == 0) {
return 22;
}
dstorig = dst;
while (dstSizeInBytes > 0 && dst[0] != '\0') {
dst += 1;
dstSizeInBytes -= 1;
}
if (dstSizeInBytes == 0) {
return 22; /* Unterminated. */
}
if (count == ((size_t)-1)) { /* _TRUNCATE */
count = dstSizeInBytes - 1;
}
while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) {
*dst++ = *src++;
dstSizeInBytes -= 1;
count -= 1;
}
if (dstSizeInBytes > 0) {
dst[0] = '\0';
} else {
dstorig[0] = '\0';
return 34;
}
return 0;
}
/*
String Helpers
*/
int dr_append_path(char* dst, size_t dstSize, const char* base, const char* other)
{
int err;
size_t len;
/* TODO: Return the correct error codes here. */
if (dst == NULL) {
return -1;
}
if (base == NULL || other == NULL) {
return -1;
}
err = dr_strcpy_s(dst, dstSize, base);
if (err != 0) {
return err;
}
len = strlen(dst);
if (len > 0) {
/* Append the slash if required. */
if (dst[len-1] != '/' && dst[len-1] != '\\') {
err = dr_strcat_s(dst, dstSize, "/");
if (err != 0) {
dst[0] = '\0';
return err;
}
len += 1; /* +1 to the length to account for the slash. */
}
}
err = dr_strcat_s(dst, dstSize, other);
if (err != 0) {
dst[0] = '\0';
return err;
}
/* Success. */
return 0;
}
const char* dr_path_file_name(const char* path)
{
const char* fileName = path;
if (path == NULL) {
return NULL;
}
/* We just loop through the path until we find the last slash. */
while (path[0] != '\0') {
if (path[0] == '/' || path[0] == '\\') {
fileName = path;
}
path += 1;
}
/* At this point the file name is sitting on a slash, so just move forward. */
while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
fileName += 1;
}
return fileName;
}
const char* dr_extension(const char* path)
{
const char* extension = path;
const char* lastoccurance = NULL;
if (path == NULL) {
return NULL;
}
/* Just find the last '.' and return. */
while (extension[0] != '\0') {
if (extension[0] == '.') {
extension += 1;
lastoccurance = extension;
}
extension += 1;
}
return (lastoccurance != 0) ? lastoccurance : extension;
}
dr_bool32 dr_extension_equal(const char* path, const char* extension)
{
const char* ext1;
const char* ext2;
if (path == NULL || extension == NULL) {
return 0;
}
ext1 = extension;
ext2 = dr_extension(path);
#if defined(_MSC_VER) || defined(__DMC__)
return _stricmp(ext1, ext2) == 0;
#else
return strcasecmp(ext1, ext2) == 0;
#endif
}
/*
File Iterator
dr_file_iterator state;
dr_file_iterator* pFile = dr_file_iterator_begin("the/folder/path", &state);
while (pFile != NULL) {
// Do something with pFile.
pFile = dr_file_iterator_next(pFile);
}
Limitations:
- Only supports file paths up to 256 characters.
*/
typedef struct
{
char folderPath[256];
char relativePath[256]; /* Relative to the original folder path. */
char absolutePath[256]; /* Concatenation of folderPath and relativePath. */
dr_bool32 isDirectory;
#ifdef _WIN32
HANDLE hFind;
#else
DIR* dir;
#endif
} dr_file_iterator;
dr_file_iterator* dr_file_iterator_begin(const char* pFolderPath, dr_file_iterator* pState)
{
#ifdef _WIN32
char searchQuery[MAX_PATH];
unsigned int searchQueryLength;
WIN32_FIND_DATAA ffd;
HANDLE hFind;
#else
DIR* dir;
#endif
if (pState == NULL) {
return NULL;
}
memset(pState, 0, sizeof(*pState));
if (pFolderPath == NULL) {
return NULL;
}
#ifdef _WIN32
dr_strcpy_s(searchQuery, sizeof(searchQuery), pFolderPath);
searchQueryLength = (unsigned int)strlen(searchQuery);
if (searchQueryLength >= MAX_PATH - 3) {
return NULL; /* Path is too long. */
}
searchQuery[searchQueryLength + 0] = '\\';
searchQuery[searchQueryLength + 1] = '*';
searchQuery[searchQueryLength + 2] = '\0';
hFind = FindFirstFileA(searchQuery, &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return NULL; /* Failed to begin search. */
}
/* Skip past "." and ".." directories. */
while (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) {
if (!FindNextFileA(hFind, &ffd)) {
FindClose(hFind);
return NULL; /* Couldn't find anything. */
}
}
pState->hFind = hFind;
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
pState->isDirectory = 1;
} else {
pState->isDirectory = 0;
}
dr_strcpy_s(pState->relativePath, sizeof(pState->relativePath), ffd.cFileName);
#else
dir = opendir(pFolderPath);
if (dir == NULL) {
return NULL; /* Couldn't find anything. */
}
/* Select the first file. */
for (;;) {
struct dirent* info;
struct stat fileinfo;
char filePath[4096];
info = readdir(dir);
if (info == NULL) {
closedir(dir);
return NULL;
}
if (strcmp(info->d_name, ".") == 0 || strcmp(info->d_name, "..") == 0) {
continue; /* Skip past "." and ".." directories. */
}
dr_strcpy_s(pState->relativePath, sizeof(pState->relativePath), info->d_name);
dr_append_path(filePath, sizeof(filePath), pFolderPath, pState->relativePath);
if (stat(filePath, &fileinfo) != 0) {
continue;
}
if (S_ISDIR(fileinfo.st_mode)) {
pState->isDirectory = 1;
} else {
pState->isDirectory = 0;
}
break;
}
pState->dir = dir;
#endif
/* Getting here means everything was successful. We can now set some state before returning. */
dr_strcpy_s(pState->folderPath, sizeof(pState->folderPath), pFolderPath);
dr_append_path(pState->absolutePath, sizeof(pState->absolutePath), pState->folderPath, pState->relativePath);
return pState;
}
dr_file_iterator* dr_file_iterator_next(dr_file_iterator* pState)
{
#ifdef _WIN32
WIN32_FIND_DATAA ffd;
#endif
if (pState == NULL) {
return NULL;
}
#ifdef _WIN32
if (!FindNextFileA(pState->hFind, &ffd)) {
/* Couldn't find anything else. */
FindClose(pState->hFind);
pState->hFind = NULL;
return NULL;
}
/* Found something. */
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
pState->isDirectory = 1;
} else {
pState->isDirectory = 0;
}
dr_strcpy_s(pState->relativePath, sizeof(pState->relativePath), ffd.cFileName);
#else
/* Enter a loop here so we can skip past "." and ".." directories. */
for (;;) {
struct dirent* info;
struct stat fileinfo;
char filePath[4096];
info = readdir(pState->dir);
if (info == NULL) {
closedir(pState->dir);
pState->dir = NULL;
return NULL;
}
if (strcmp(info->d_name, ".") == 0 || strcmp(info->d_name, "..") == 0) {
continue; /* Skip past "." and ".." directories. */
}
dr_strcpy_s(pState->relativePath, sizeof(pState->relativePath), info->d_name);
dr_append_path(filePath, sizeof(filePath), pState->folderPath, pState->relativePath);
/*printf("Done: %s\n", pState->relativePath);*/
if (stat(filePath, &fileinfo) != 0) {
continue;
}
if (S_ISDIR(fileinfo.st_mode)) {
pState->isDirectory = 1;
} else {
pState->isDirectory = 0;
}
break;
}
#endif
/* Success */
dr_append_path(pState->absolutePath, sizeof(pState->absolutePath), pState->folderPath, pState->relativePath);
return pState;
}
void dr_file_iterator_end(dr_file_iterator* pState)
{
if (pState == NULL) {
return;
}
#ifdef _WIN32
FindClose(pState->hFind);
pState->hFind = NULL;
#else
closedir(pState->dir);
pState->dir = NULL;
#endif
}
/*
File Management
Free file data with free().
*/
static int dr_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
{
#if defined(_MSC_VER) && _MSC_VER >= 1400
errno_t err;
#endif
if (ppFile != NULL) {
*ppFile = NULL; /* Safety. */
}
if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
return -1; /* Invalid args. */
}
#if defined(_MSC_VER) && _MSC_VER >= 1400
err = fopen_s(ppFile, pFilePath, pOpenMode);
if (err != 0) {
return err;
}
#else
#if defined(_WIN32) || defined(__APPLE__)
*ppFile = fopen(pFilePath, pOpenMode);
#else
#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
*ppFile = fopen64(pFilePath, pOpenMode);
#else
*ppFile = fopen(pFilePath, pOpenMode);
#endif
#endif
if (*ppFile == NULL) {
return errno;
}
#endif
return 0;
}
void* dr_open_and_read_file_with_extra_data(const char* pFilePath, size_t* pFileSizeOut, size_t extraBytes)
{
FILE* pFile;
size_t fileSize;
size_t bytesRead;
void* pFileData;
/* Safety. */
if (pFileSizeOut) {
*pFileSizeOut = 0;
}
if (pFilePath == NULL) {
return NULL;
}
/* TODO: Use 64-bit versions of the FILE APIs. */
if (dr_fopen(&pFile, pFilePath, "rb") != 0) {
return NULL;
}
fseek(pFile, 0, SEEK_END);
fileSize = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
/* Need to make sure we have enough room for the extra bytes, if any. */
if (fileSize == DR_SIZE_MAX && extraBytes > 0) {
fclose(pFile);
return NULL; /* File is too big. */
}
pFileData = malloc((size_t)fileSize + extraBytes); /* <-- Safe cast due to the check above. */
if (pFileData == NULL) {
fclose(pFile);
return NULL; /* Failed to allocate memory for the file. Good chance the file is too big. */
}
bytesRead = fread(pFileData, 1, (size_t)fileSize, pFile);
if (bytesRead != fileSize) {
free(pFileData);
fclose(pFile);
return NULL; /* Failed to read every byte from the file. */
}
fclose(pFile);
if (pFileSizeOut) {
*pFileSizeOut = (size_t)fileSize;
}
return pFileData;
}
void* dr_open_and_read_file(const char* pFilePath, size_t* pFileSizeOut)
{
return dr_open_and_read_file_with_extra_data(pFilePath, pFileSizeOut, 0);
}
dr_bool32 dr_argv_is_set(int argc, char** argv, const char* value)
{
int iarg;
for (iarg = 0; iarg < argc; ++iarg) {
if (strcmp(argv[iarg], value) == 0) {
return DR_TRUE;
}
}
return DR_FALSE;
}
int dr_vprintf_fixed(int width, const char* const format, va_list args)
{
int i;
int len;
char buffer[4096];
if (width <= 0) {
return -1; /* Width cannot be negative or 0. */
}
if ((unsigned int)width > sizeof(buffer)) {
return -1; /* Width is too big. */
}
/* We need to print this into a string (truncated). */
#if (defined(_MSC_VER) && _MSC_VER > 1200) || ((defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500) || defined(_ISOC99_SOURCE) || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L))
len = vsnprintf(buffer, width+1, format, args);
#else
len = vsprintf(buffer, format, args);
if (len > width) {
len = width;
}
buffer[len] = '\0';
#endif
printf("%s", buffer);
for (i = len; i < width; ++i) {
printf(" ");
}
return len;
}
int dr_printf_fixed(int width, const char* const format, ...)
{
int result;
va_list args;
va_start(args, format);
{
result = dr_vprintf_fixed(width, format, args);
}
va_end(args);
return result;
}
int dr_vprintf_fixed_with_margin(int width, int margin, const char* const format, va_list args)
{
int i;
/* Margin. */
for (i = 0; i < margin; ++i) {
printf(" ");
}
return dr_vprintf_fixed(width - margin, format, args);
}
int dr_printf_fixed_with_margin(int width, int margin, const char* const format, ...)
{
int result;
va_list args;
va_start(args, format);
{
result = dr_vprintf_fixed_with_margin(width, margin, format, args);
}
va_end(args);
return result;
}
#ifdef _WIN32
static LARGE_INTEGER g_DRTimerFrequency = {{0}};
double dr_timer_now()
{
LARGE_INTEGER counter;
if (g_DRTimerFrequency.QuadPart == 0) {
QueryPerformanceFrequency(&g_DRTimerFrequency);
}
QueryPerformanceCounter(&counter);
return counter.QuadPart / (double)g_DRTimerFrequency.QuadPart;
}
#else
#if _POSIX_C_SOURCE >= 199309L
#if defined(CLOCK_MONOTONIC)
#define MA_CLOCK_ID CLOCK_MONOTONIC
#else
#define MA_CLOCK_ID CLOCK_REALTIME
#endif
double dr_timer_now()
{
struct timespec newTime;
clock_gettime(CLOCK_MONOTONIC, &newTime);
return ((newTime.tv_sec * 1000000000LL) + newTime.tv_nsec) / 1000000000.0;
}
#else
double dr_timer_now()
{
struct timeval newTime;
gettimeofday(&newTime, NULL);
return ((newTime.tv_sec * 1000000) + newTime.tv_usec) / 1000000.0;
}
#endif
#endif
float dr_scale_to_range_f32(float x, float lo, float hi)
{
return lo + x*(hi-lo);
}
#define DR_LCG_M 2147483647
#define DR_LCG_A 48271
#define DR_LCG_C 0
static int g_drLCG;
void dr_seed(int seed)
{
g_drLCG = seed;
}
int dr_rand_s32()
{
int lcg = g_drLCG;
int r = (DR_LCG_A * lcg + DR_LCG_C) % DR_LCG_M;
g_drLCG = r;
return r;
}
unsigned int dr_rand_u32()
{
return (unsigned int)dr_rand_s32();
}
dr_uint64 dr_rand_u64()
{
return ((dr_uint64)dr_rand_u32() << 32) | dr_rand_u32();
}
double dr_rand_f64()
{
return dr_rand_s32() / (double)0x7FFFFFFF;
}
float dr_rand_f32()
{
return (float)dr_rand_f64();
}
float dr_rand_range_f32(float lo, float hi)
{
return dr_scale_to_range_f32(dr_rand_f32(), lo, hi);
}
int dr_rand_range_s32(int lo, int hi)
{
if (lo == hi) {
return lo;
}
return lo + dr_rand_u32() / (0xFFFFFFFF / (hi - lo + 1) + 1);
}
dr_uint64 dr_rand_range_u64(dr_uint64 lo, dr_uint64 hi)
{
if (lo == hi) {
return lo;
}
return lo + dr_rand_u64() / ((~(dr_uint64)0) / (hi - lo + 1) + 1);
}
void dr_pcm_s32_to_f32(void* dst, const void* src, dr_uint64 count)
{
float* dst_f32 = (float*)dst;
const dr_int32* src_s32 = (const dr_int32*)src;
dr_uint64 i;
for (i = 0; i < count; i += 1) {
double x = src_s32[i];
#if 0
x = x + 2147483648.0;
x = x * 0.0000000004656612873077392578125;
x = x - 1;
#else
x = x / 2147483648.0;
#endif
dst_f32[i] = (float)x;
}
}
void dr_pcm_s32_to_s16(void* dst, const void* src, dr_uint64 count)
{
dr_int16* dst_s16 = (dr_int16*)dst;
const dr_int32* src_s32 = (const dr_int32*)src;
dr_uint64 i;
for (i = 0; i < count; i += 1) {
dr_int32 x = src_s32[i];
x = x >> 16;
dst_s16[i] = (dr_int16)x;
}
}
dr_handle dr_dlopen(const char* filename)
{
dr_handle handle;
#ifdef _WIN32
handle = (dr_handle)LoadLibraryA(filename);
#else
handle = (dr_handle)dlopen(filename, RTLD_NOW);
#endif
return handle;
}
void dr_dlclose(dr_handle handle)
{
#ifdef _WIN32
FreeLibrary((HMODULE)handle);
#else
dlclose((void*)handle);
#endif
}
dr_proc dr_dlsym(dr_handle handle, const char* symbol)
{
dr_proc proc;
#ifdef _WIN32
proc = (dr_proc)GetProcAddress((HMODULE)handle, symbol);
#else
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
proc = (dr_proc)dlsym((void*)handle, symbol);
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#pragma GCC diagnostic pop
#endif
#endif
return proc;
}

@ -1 +0,0 @@
Subproject commit d1a166c83ab445b1c14bc83d37c84e18d172e5f5

View file

@ -1,332 +0,0 @@
#define DR_FLAC_IMPLEMENTATION
#include "../../dr_flac.h"
/* libFLAC has a warning in their header. *sigh*. */
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#if defined(__clang__)
#pragma GCC diagnostic ignored "-Wc99-extensions"
#endif
#endif
#include <FLAC/stream_decoder.h>
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
#include "../common/dr_common.c"
/*
The libflac object is used to make it easier to compare things against the reference implementation. I don't think
libFLAC's API is all that easy to use or intuitive so I'm wrapping it. This is deliberately simple. It decodes the
entire file into memory upon initialization.
*/
typedef struct
{
drflac_int32* pPCMFrames; /* Interleaved. */
drflac_uint64 pcmFrameCount;
drflac_uint64 pcmFrameCap; /* The capacity of the pPCMFrames buffer in PCM frames. */
drflac_uint32 channels;
drflac_uint32 sampleRate;
drflac_uint64 currentPCMFrame; /* The index of the PCM frame the decoder is currently sitting on. */
double decodeTimeInSeconds; /* The total amount of time it took to decode the file. This is used for profiling. */
drflac_uint8* pFileData;
size_t fileSizeInBytes;
size_t fileReadPos;
} libflac;
static FLAC__StreamDecoderReadStatus libflac__read_callback(const FLAC__StreamDecoder *pStreamDecoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
{
libflac* pDecoder = (libflac*)client_data;
size_t bytesToRead = *bytes;
size_t bytesRemaining = pDecoder->fileSizeInBytes - pDecoder->fileReadPos;
(void)pStreamDecoder;
if (bytesToRead > bytesRemaining) {
bytesToRead = bytesRemaining;
}
if (bytesToRead > 0) {
memcpy(buffer, pDecoder->pFileData + pDecoder->fileReadPos, bytesToRead);
pDecoder->fileReadPos += bytesToRead;
}
*bytes = bytesToRead;
return (bytesToRead == 0 && bytesRemaining == 0) ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}
static FLAC__StreamDecoderWriteStatus libflac__write_callback(const FLAC__StreamDecoder *pStreamDecoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
{
libflac* pDecoder = (libflac*)client_data;
drflac_uint32 pcmFramesInFLACFrame;
drflac_int32* pNextSampleInNewFrame;
drflac_uint32 i, j;
(void)pStreamDecoder;
pcmFramesInFLACFrame = frame->header.blocksize;
/* Make sure there's room in the buffer. */
if ((pDecoder->pcmFrameCount + pcmFramesInFLACFrame) > pDecoder->pcmFrameCap) {
drflac_int32* pNewPCMFrames;
pDecoder->pcmFrameCap *= 2;
if (pDecoder->pcmFrameCap < (pDecoder->pcmFrameCount + pcmFramesInFLACFrame)) {
pDecoder->pcmFrameCap = (pDecoder->pcmFrameCount + pcmFramesInFLACFrame);
}
pNewPCMFrames = (drflac_int32*)realloc(pDecoder->pPCMFrames, (size_t)(pDecoder->pcmFrameCap * pDecoder->channels * sizeof(drflac_int32)));
if (pNewPCMFrames == NULL) {
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}
pDecoder->pPCMFrames = pNewPCMFrames;
}
/* Make sure the decoded samples are interleaved. */
pNextSampleInNewFrame = pDecoder->pPCMFrames + (pDecoder->pcmFrameCount * pDecoder->channels);
for (i = 0; i < pcmFramesInFLACFrame; i += 1) {
for (j = 0; j < pDecoder->channels; ++j) {
*pNextSampleInNewFrame++ = buffer[j][i] << (32 - frame->header.bits_per_sample);
}
}
pDecoder->pcmFrameCount += pcmFramesInFLACFrame;
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
static FLAC__StreamDecoderLengthStatus libflac__length_callback(const FLAC__StreamDecoder *pStreamDecoder, FLAC__uint64 *stream_length, void *client_data)
{
libflac* pDecoder = (libflac*)client_data;
(void)pStreamDecoder;
*stream_length = pDecoder->fileSizeInBytes;
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
}
static void libflac__metadata_callback(const FLAC__StreamDecoder *pStreamDecoder, const FLAC__StreamMetadata *metadata, void *client_data)
{
libflac* pDecoder = (libflac*)client_data;
(void)pStreamDecoder;
/* Here is where we initialize the buffer for the decoded data. */
if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
pDecoder->pcmFrameCount = 0; /* Always set this to 0. Don't be tempted to set it to metadata->data.stream_info.total_samples. It's increment in libflac__write_callback. */
pDecoder->channels = metadata->data.stream_info.channels;
pDecoder->sampleRate = metadata->data.stream_info.sample_rate;
/* Allocate an initial block of memory if we know the total size. */
if (metadata->data.stream_info.total_samples > 0) {
pDecoder->pcmFrameCap = metadata->data.stream_info.total_samples;
pDecoder->pPCMFrames = (drflac_int32*)malloc((size_t)(pDecoder->pcmFrameCap * pDecoder->channels * sizeof(drflac_int32)));
if (pDecoder->pPCMFrames != NULL) {
DRFLAC_ZERO_MEMORY(pDecoder->pPCMFrames, (size_t)(pDecoder->pcmFrameCap * pDecoder->channels * sizeof(drflac_int32)));
}
}
}
}
static void libflac__error_callback(const FLAC__StreamDecoder *pStreamDecoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
{
(void)pStreamDecoder;
(void)status;
(void)client_data;
}
/*
Initializes a libflac object.
This will perform a full decode of the file using libFLAC. The time it takes to decode the file will be stored in decodeTimeInSeconds
for the purpose of profiling. The decoding process will load the entire file into memory before calling into libFLAC. This way profiling
will exclude any IO time.
*/
drflac_result libflac_init_file(const char* pFilePath, libflac* pDecoder)
{
FLAC__StreamDecoder* pStreamDecoder;
FLAC__bool libflacResult;
FLAC__StreamDecoderInitStatus libflacStatus;
double decodeTimeBeg;
double decodeTimeEnd;
DRFLAC_ZERO_MEMORY(pDecoder, sizeof(*pDecoder));
pDecoder->pFileData = (drflac_uint8*)dr_open_and_read_file(pFilePath, &pDecoder->fileSizeInBytes);
if (pDecoder->pFileData == NULL) {
return DRFLAC_ERROR; /* Failed to open the file. */
}
/* Initialize the libFLAC decoder. */
pStreamDecoder = FLAC__stream_decoder_new();
if (pDecoder == NULL) {
return DRFLAC_ERROR; /* Failed to create a new stream decoder. Out of memory. */
}
if (dr_extension_equal(pFilePath, "ogg") || dr_extension_equal(pFilePath, "oga") || dr_extension_equal(pFilePath, "ogv")) {
libflacStatus = FLAC__stream_decoder_init_ogg_stream(pStreamDecoder, libflac__read_callback, NULL, NULL, libflac__length_callback, NULL, libflac__write_callback, libflac__metadata_callback, libflac__error_callback, pDecoder);
} else {
libflacStatus = FLAC__stream_decoder_init_stream(pStreamDecoder, libflac__read_callback, NULL, NULL, libflac__length_callback, NULL, libflac__write_callback, libflac__metadata_callback, libflac__error_callback, pDecoder);
}
if (libflacStatus != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
FLAC__stream_decoder_delete(pStreamDecoder);
free(pDecoder->pFileData);
return DRFLAC_ERROR; /* Failed to initialize the stream. */
}
/* Read past the metadata first. This will fire the libflac__metadata_callback which will do a bit of initialization for us. */
libflacResult = FLAC__stream_decoder_process_until_end_of_metadata(pStreamDecoder);
if (libflacResult == DRFLAC_FALSE) {
FLAC__stream_decoder_delete(pStreamDecoder);
free(pDecoder->pFileData);
return DRFLAC_ERROR; /* Failed to read metadata from the FLAC stream. */
}
/* Now we can just decode the entire file. */
decodeTimeBeg = dr_timer_now();
libflacResult = FLAC__stream_decoder_process_until_end_of_stream(pStreamDecoder);
decodeTimeEnd = dr_timer_now();
pDecoder->decodeTimeInSeconds = decodeTimeEnd - decodeTimeBeg;
/* We're done with the libFLAC decoder. */
FLAC__stream_decoder_delete(pStreamDecoder);
pStreamDecoder = NULL;
/* Free the file data. */
free(pDecoder->pFileData);
pDecoder->pFileData = NULL;
pDecoder->fileSizeInBytes = 0;
pDecoder->fileReadPos = 0;
if (libflacResult == DRFLAC_FALSE) {
return DRFLAC_ERROR; /* Some error ocurred while decoding. */
}
return DRFLAC_SUCCESS;
}
void libflac_uninit(libflac* pDecoder)
{
if (pDecoder == NULL) {
return;
}
free(pDecoder->pPCMFrames);
}
drflac_uint64 libflac_read_pcm_frames_s32(libflac* pDecoder, drflac_uint64 framesToRead, drflac_int32* pBufferOut)
{
drflac_uint64 pcmFramesRemaining;
if (pDecoder == NULL) {
return 0;
}
pcmFramesRemaining = pDecoder->pcmFrameCount - pDecoder->currentPCMFrame;
if (framesToRead > pcmFramesRemaining) {
framesToRead = pcmFramesRemaining;
}
if (framesToRead == 0) {
return 0;
}
DRFLAC_COPY_MEMORY(pBufferOut, pDecoder->pPCMFrames + (pDecoder->currentPCMFrame * pDecoder->channels), (size_t)(framesToRead * pDecoder->channels * sizeof(drflac_int32)));
pDecoder->currentPCMFrame += framesToRead;
return framesToRead;
}
drflac_uint64 libflac_read_pcm_frames_f32(libflac* pDecoder, drflac_uint64 framesToRead, float* pBufferOut)
{
drflac_uint64 pcmFramesRemaining;
if (pDecoder == NULL) {
return 0;
}
pcmFramesRemaining = pDecoder->pcmFrameCount - pDecoder->currentPCMFrame;
if (framesToRead > pcmFramesRemaining) {
framesToRead = pcmFramesRemaining;
}
if (framesToRead == 0) {
return 0;
}
/* s32 to f32 */
dr_pcm_s32_to_f32(pBufferOut, pDecoder->pPCMFrames + (pDecoder->currentPCMFrame * pDecoder->channels), framesToRead * pDecoder->channels);
pDecoder->currentPCMFrame += framesToRead;
return framesToRead;
}
drflac_uint64 libflac_read_pcm_frames_s16(libflac* pDecoder, drflac_uint64 framesToRead, drflac_int16* pBufferOut)
{
drflac_uint64 pcmFramesRemaining;
if (pDecoder == NULL) {
return 0;
}
pcmFramesRemaining = pDecoder->pcmFrameCount - pDecoder->currentPCMFrame;
if (framesToRead > pcmFramesRemaining) {
framesToRead = pcmFramesRemaining;
}
if (framesToRead == 0) {
return 0;
}
/* s32 to f32 */
dr_pcm_s32_to_s16(pBufferOut, pDecoder->pPCMFrames + (pDecoder->currentPCMFrame * pDecoder->channels), framesToRead * pDecoder->channels);
pDecoder->currentPCMFrame += framesToRead;
return framesToRead;
}
drflac_bool32 libflac_seek_to_pcm_frame(libflac* pDecoder, drflac_uint64 targetPCMFrameIndex)
{
if (pDecoder == NULL) {
return DRFLAC_FALSE;
}
if (targetPCMFrameIndex > pDecoder->pcmFrameCount) {
return DRFLAC_FALSE; /* Trying to seek too far forward. */
}
pDecoder->currentPCMFrame = targetPCMFrameIndex;
return DRFLAC_TRUE;
}
/* Helper for printing CPU caps from dr_flac. */
void print_cpu_caps()
{
#if defined(DRFLAC_64BIT)
printf("64 Bit\n");
#else
printf("32 Bit\n");
#endif
drflac__init_cpu_caps();
#if defined(DRFLAC_X86) || defined(DRFLAC_X64)
#if defined(DRFLAC_X64)
printf("Architecture: x64\n");
#else
printf("Architecture: x86\n");
#endif
printf("Has SSE2: %s\n", drflac_has_sse2() ? "YES" : "NO");
printf("Has SSE4.1: %s\n", drflac_has_sse41() ? "YES" : "NO");
printf("Has LZCNT: %s\n", drflac__is_lzcnt_supported() ? "YES" : "NO");
#endif
}

View file

@ -1,825 +0,0 @@
/*#define DR_FLAC_NO_CRC*/
/*#define DR_FLAC_NO_SIMD*/
/*#define DR_FLAC_BUFFER_SIZE 4096*/
#include "dr_flac_common.c"
#define FILE_NAME_WIDTH 40
#define NUMBER_WIDTH 10
#define TABLE_MARGIN 2
#define DEFAULT_SOURCE_DIR "testvectors/flac/testbench"
drflac_result decode_test__read_and_compare_pcm_frames_s32(libflac* pLibFlac, drflac* pFlac, drflac_uint64 pcmFrameCount, drflac_int32* pPCMFrames_libflac, drflac_int32* pPCMFrames_drflac)
{
drflac_uint64 pcmFrameCount_libflac;
drflac_uint64 pcmFrameCount_drflac;
drflac_uint64 iPCMFrame;
/* To test decoding we just read a number of PCM frames from each decoder and compare. */
pcmFrameCount_libflac = libflac_read_pcm_frames_s32(pLibFlac, pcmFrameCount, pPCMFrames_libflac);
pcmFrameCount_drflac = drflac_read_pcm_frames_s32(pFlac, pcmFrameCount, pPCMFrames_drflac);
/* The total number of frames we decoded need to match. */
if (pcmFrameCount_libflac != pcmFrameCount_drflac) {
printf(" Decoded frame counts differ: pcmFrameCount=%d, libFLAC=%d, dr_flac=%d", (int)pcmFrameCount, (int)pcmFrameCount_libflac, (int)pcmFrameCount_drflac);
return DRFLAC_ERROR;
}
/* Each of the decoded PCM frames need to match. */
DRFLAC_ASSERT(pcmFrameCount_libflac == pcmFrameCount_drflac);
for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libflac; iPCMFrame += 1) {
drflac_int32* pPCMFrame_libflac = pPCMFrames_libflac + (iPCMFrame * pLibFlac->channels);
drflac_int32* pPCMFrame_drflac = pPCMFrames_drflac + (iPCMFrame * pLibFlac->channels);
drflac_uint32 iChannel;
drflac_bool32 hasError = DRFLAC_FALSE;
for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
if (pPCMFrame_libflac[iChannel] != pPCMFrame_drflac[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d\n", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
hasError = DRFLAC_TRUE;
break;
}
}
if (hasError) {
return DRFLAC_ERROR; /* Decoded frames do not match. */
}
}
/* Done. */
return DRFLAC_SUCCESS;
}
drflac_result decode_test__read_and_compare_pcm_frame_chunks_s32(libflac* pLibFlac, drflac* pFlac, drflac_uint64 pcmFrameChunkSize)
{
drflac_result result = DRFLAC_SUCCESS;
drflac_uint64 iPCMFrame;
drflac_int32* pPCMFrames_libflac;
drflac_int32* pPCMFrames_drflac;
/* Make sure the decoder's are seeked back to the start first. */
drflac_seek_to_pcm_frame(pFlac, 0);
libflac_seek_to_pcm_frame(pLibFlac, 0);
pPCMFrames_libflac = (drflac_int32*)malloc((size_t)(pcmFrameChunkSize * pLibFlac->channels * sizeof(drflac_int32)));
if (pPCMFrames_libflac == NULL) {
printf(" [libFLAC] Out of memory");
return DRFLAC_ERROR;
}
pPCMFrames_drflac = (drflac_int32*)malloc((size_t)(pcmFrameChunkSize * pLibFlac->channels * sizeof(drflac_int32)));
if (pPCMFrames_drflac == NULL) {
free(pPCMFrames_libflac);
printf(" [dr_flac] Out of memory");
return DRFLAC_ERROR;
}
for (iPCMFrame = 0; iPCMFrame < pLibFlac->pcmFrameCount; iPCMFrame += pcmFrameChunkSize) {
result = decode_test__read_and_compare_pcm_frames_s32(pLibFlac, pFlac, pcmFrameChunkSize, pPCMFrames_libflac, pPCMFrames_drflac);
if (result != DRFLAC_SUCCESS) {
break;
}
}
free(pPCMFrames_libflac);
free(pPCMFrames_drflac);
return result;
}
drflac_result decode_test__read_and_compare_pcm_frames_f32(libflac* pLibFlac, drflac* pFlac, drflac_uint64 pcmFrameCount, float* pPCMFrames_libflac, float* pPCMFrames_drflac)
{
drflac_uint64 pcmFrameCount_libflac;
drflac_uint64 pcmFrameCount_drflac;
drflac_uint64 iPCMFrame;
/* To test decoding we just read a number of PCM frames from each decoder and compare. */
pcmFrameCount_libflac = libflac_read_pcm_frames_f32(pLibFlac, pcmFrameCount, pPCMFrames_libflac);
pcmFrameCount_drflac = drflac_read_pcm_frames_f32(pFlac, pcmFrameCount, pPCMFrames_drflac);
/* The total number of frames we decoded need to match. */
if (pcmFrameCount_libflac != pcmFrameCount_drflac) {
printf(" Decoded frame counts differ: pcmFrameCount=%d, libFLAC=%d, dr_flac=%d", (int)pcmFrameCount, (int)pLibFlac->currentPCMFrame, (int)pFlac->currentPCMFrame);
return DRFLAC_ERROR;
}
/* Each of the decoded PCM frames need to match. */
DRFLAC_ASSERT(pcmFrameCount_libflac == pcmFrameCount_drflac);
for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libflac; iPCMFrame += 1) {
float* pPCMFrame_libflac = pPCMFrames_libflac + (iPCMFrame * pLibFlac->channels);
float* pPCMFrame_drflac = pPCMFrames_drflac + (iPCMFrame * pLibFlac->channels);
drflac_uint32 iChannel;
drflac_bool32 hasError = DRFLAC_FALSE;
for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
if (pPCMFrame_libflac[iChannel] != pPCMFrame_drflac[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
hasError = DRFLAC_TRUE;
break;
}
}
if (hasError) {
return DRFLAC_ERROR; /* Decoded frames do not match. */
}
}
/* Done. */
return DRFLAC_SUCCESS;
}
drflac_result decode_test__read_and_compare_pcm_frame_chunks_f32(libflac* pLibFlac, drflac* pFlac, drflac_uint64 pcmFrameChunkSize)
{
drflac_result result = DRFLAC_SUCCESS;
drflac_uint64 iPCMFrame;
float* pPCMFrames_libflac;
float* pPCMFrames_drflac;
/* Make sure the decoder's are seeked back to the start first. */
drflac_seek_to_pcm_frame(pFlac, 0);
libflac_seek_to_pcm_frame(pLibFlac, 0);
pPCMFrames_libflac = (float*)malloc((size_t)(pcmFrameChunkSize * pLibFlac->channels * sizeof(float)));
if (pPCMFrames_libflac == NULL) {
printf(" [libFLAC] Out of memory");
return DRFLAC_ERROR;
}
pPCMFrames_drflac = (float*)malloc((size_t)(pcmFrameChunkSize * pLibFlac->channels * sizeof(float)));
if (pPCMFrames_drflac == NULL) {
free(pPCMFrames_libflac);
printf(" [dr_flac] Out of memory");
return DRFLAC_ERROR;
}
for (iPCMFrame = 0; iPCMFrame < pLibFlac->pcmFrameCount; iPCMFrame += pcmFrameChunkSize) {
result = decode_test__read_and_compare_pcm_frames_f32(pLibFlac, pFlac, pcmFrameChunkSize, pPCMFrames_libflac, pPCMFrames_drflac);
if (result != DRFLAC_SUCCESS) {
break;
}
}
free(pPCMFrames_libflac);
free(pPCMFrames_drflac);
return result;
}
drflac_result decode_test__read_and_compare_pcm_frames_s16(libflac* pLibFlac, drflac* pFlac, drflac_uint64 pcmFrameCount, drflac_int16* pPCMFrames_libflac, drflac_int16* pPCMFrames_drflac)
{
drflac_uint64 pcmFrameCount_libflac;
drflac_uint64 pcmFrameCount_drflac;
drflac_uint64 iPCMFrame;
/* To test decoding we just read a number of PCM frames from each decoder and compare. */
pcmFrameCount_libflac = libflac_read_pcm_frames_s16(pLibFlac, pcmFrameCount, pPCMFrames_libflac);
pcmFrameCount_drflac = drflac_read_pcm_frames_s16(pFlac, pcmFrameCount, pPCMFrames_drflac);
/* The total number of frames we decoded need to match. */
if (pcmFrameCount_libflac != pcmFrameCount_drflac) {
printf(" Decoded frame counts differ: pcmFrameCount=%d, libFLAC=%d, dr_flac=%d", (int)pcmFrameCount, (int)pLibFlac->currentPCMFrame, (int)pFlac->currentPCMFrame);
return DRFLAC_ERROR;
}
/* Each of the decoded PCM frames need to match. */
DRFLAC_ASSERT(pcmFrameCount_libflac == pcmFrameCount_drflac);
for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libflac; iPCMFrame += 1) {
drflac_int16* pPCMFrame_libflac = pPCMFrames_libflac + (iPCMFrame * pLibFlac->channels);
drflac_int16* pPCMFrame_drflac = pPCMFrames_drflac + (iPCMFrame * pLibFlac->channels);
drflac_uint32 iChannel;
drflac_bool32 hasError = DRFLAC_FALSE;
for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
if (pPCMFrame_libflac[iChannel] != pPCMFrame_drflac[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
hasError = DRFLAC_TRUE;
break;
}
}
if (hasError) {
return DRFLAC_ERROR; /* Decoded frames do not match. */
}
}
/* Done. */
return DRFLAC_SUCCESS;
}
drflac_result decode_test__read_and_compare_pcm_frame_chunks_s16(libflac* pLibFlac, drflac* pFlac, drflac_uint64 pcmFrameChunkSize)
{
drflac_result result = DRFLAC_SUCCESS;
drflac_uint64 iPCMFrame;
drflac_int16* pPCMFrames_libflac;
drflac_int16* pPCMFrames_drflac;
/* Make sure the decoder's are seeked back to the start first. */
drflac_seek_to_pcm_frame(pFlac, 0);
libflac_seek_to_pcm_frame(pLibFlac, 0);
pPCMFrames_libflac = (drflac_int16*)malloc((size_t)(pcmFrameChunkSize * pLibFlac->channels * sizeof(drflac_int16)));
if (pPCMFrames_libflac == NULL) {
printf(" [libFLAC] Out of memory");
return DRFLAC_ERROR;
}
pPCMFrames_drflac = (drflac_int16*)malloc((size_t)(pcmFrameChunkSize * pLibFlac->channels * sizeof(drflac_int16)));
if (pPCMFrames_drflac == NULL) {
free(pPCMFrames_libflac);
printf(" [dr_flac] Out of memory");
return DRFLAC_ERROR;
}
for (iPCMFrame = 0; iPCMFrame < pLibFlac->pcmFrameCount; iPCMFrame += pcmFrameChunkSize) {
result = decode_test__read_and_compare_pcm_frames_s16(pLibFlac, pFlac, pcmFrameChunkSize, pPCMFrames_libflac, pPCMFrames_drflac);
if (result != DRFLAC_SUCCESS) {
break;
}
}
free(pPCMFrames_libflac);
free(pPCMFrames_drflac);
return result;
}
drflac_result decode_test_file_s32(libflac* pLibFlac, drflac* pFlac)
{
drflac_result result = DRFLAC_SUCCESS;
/* Start with reading the entire file in one go. */
if (result == DRFLAC_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s32(pLibFlac, pFlac, pLibFlac->pcmFrameCount);
}
/* Now try with reading one PCM frame at a time.*/
if (result == DRFLAC_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s32(pLibFlac, pFlac, 1);
}
/* Now test FLAC frame boundaries. */
if (result == DRFLAC_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s32(pLibFlac, pFlac, (pFlac->maxBlockSizeInPCMFrames > 0) ? pFlac->maxBlockSizeInPCMFrames : 4096);
}
return result;
}
drflac_result decode_test_file_f32(libflac* pLibFlac, drflac* pFlac)
{
drflac_result result = DRFLAC_SUCCESS;
/* Start with reading the entire file in one go. */
if (result == DRFLAC_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_f32(pLibFlac, pFlac, pLibFlac->pcmFrameCount);
}
/* Now try with reading one PCM frame at a time.*/
if (result == DRFLAC_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_f32(pLibFlac, pFlac, 1);
}
/* Now test FLAC frame boundaries. */
if (result == DRFLAC_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_f32(pLibFlac, pFlac, (pFlac->maxBlockSizeInPCMFrames > 0) ? pFlac->maxBlockSizeInPCMFrames : 4096);
}
return result;
}
drflac_result decode_test_file_s16(libflac* pLibFlac, drflac* pFlac)
{
drflac_result result = DRFLAC_SUCCESS;
/* Start with reading the entire file in one go. */
if (result == DRFLAC_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s16(pLibFlac, pFlac, pLibFlac->pcmFrameCount);
}
/* Now try with reading one PCM frame at a time.*/
if (result == DRFLAC_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s16(pLibFlac, pFlac, 1);
}
/* Now test FLAC frame boundaries. */
if (result == DRFLAC_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s16(pLibFlac, pFlac, (pFlac->maxBlockSizeInPCMFrames > 0) ? pFlac->maxBlockSizeInPCMFrames : 4096);
}
return result;
}
static void on_meta(void* pUserData, drflac_metadata* pMetadata)
{
(void)pUserData;
switch (pMetadata->type)
{
case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET:
{
/*
This section is here just to make it easy to test cuesheets. It's not designed to integrate cleanly with the output of this program
so I'm just selectively enabling and disabling this section as required.
*/
#if 0
drflac_cuesheet_track_iterator i;
drflac_cuesheet_track track;
printf("Cuesheet Found. Track Count = %d\n", (int)pMetadata->data.cuesheet.trackCount);
drflac_init_cuesheet_track_iterator(&i, pMetadata->data.cuesheet.trackCount, pMetadata->data.cuesheet.pTrackData);
while (drflac_next_cuesheet_track(&i, &track)) {
drflac_uint32 iTrackIndex;
printf("Cuesheet Track %d. Index Count = %d:\n", track.trackNumber, track.indexCount);
for (iTrackIndex = 0; iTrackIndex < track.indexCount; iTrackIndex += 1) {
printf(" Index %d - Offset %llu\n", iTrackIndex, track.pIndexPoints[iTrackIndex].offset);
}
}
#endif
} break;
}
}
drflac_result decode_test_file(const char* pFilePath)
{
/* To test seeking we just seek to our target PCM frame and then decode whatever is remaining and compare it against libFLAC. */
drflac_result result;
libflac libflac;
drflac* pFlac;
dr_printf_fixed_with_margin(FILE_NAME_WIDTH, TABLE_MARGIN, "%s", dr_path_file_name(pFilePath));
/* First load the decoder from libFLAC. */
result = libflac_init_file(pFilePath, &libflac);
if (result != DRFLAC_SUCCESS) {
printf(" Failed to open via libFLAC.");
return result;
}
/* Now load from dr_flac. */
pFlac = drflac_open_file_with_metadata(pFilePath, on_meta, NULL, NULL);
if (pFlac == NULL) {
printf(" Failed to open via dr_flac.");
libflac_uninit(&libflac);
return DRFLAC_ERROR; /* Failed to load dr_flac decoder. */
}
/* At this point we should have both libFLAC and dr_flac decoders open. We can now perform identical operations on each of them and compare. */
result = decode_test_file_s32(&libflac, pFlac);
if (result != DRFLAC_SUCCESS) {
drflac_close(pFlac);
libflac_uninit(&libflac);
return result;
}
result = decode_test_file_f32(&libflac, pFlac);
if (result != DRFLAC_SUCCESS) {
drflac_close(pFlac);
libflac_uninit(&libflac);
return result;
}
result = decode_test_file_s16(&libflac, pFlac);
if (result != DRFLAC_SUCCESS) {
drflac_close(pFlac);
libflac_uninit(&libflac);
return result;
}
/* We're done with our decoders. */
drflac_close(pFlac);
libflac_uninit(&libflac);
if (result == DRFLAC_SUCCESS) {
printf(" Passed");
}
return result;
}
drflac_result decode_test_directory(const char* pDirectoryPath)
{
dr_file_iterator iteratorState;
dr_file_iterator* pFile;
dr_printf_fixed(FILE_NAME_WIDTH, "%s", pDirectoryPath);
dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "RESULT");
printf("\n");
pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
while (pFile != NULL) {
drflac_result result;
/* Skip directories for now, but we may want to look at doing recursive file iteration. */
if (!pFile->isDirectory) {
result = decode_test_file(pFile->absolutePath);
(void)result;
printf("\n");
}
pFile = dr_file_iterator_next(pFile);
}
return DRFLAC_SUCCESS;
}
drflac_result decode_test()
{
drflac_result result = DRFLAC_SUCCESS;
/* Directories. */
{
result = decode_test_directory(DEFAULT_SOURCE_DIR);
(void)result;
}
return result;
}
drflac_result open_and_read_test_file_s32(libflac* pLibFlac, const char* pFilePath)
{
drflac_int32* pPCMFrames;
drflac_uint64 pcmFrameCount;
drflac_uint32 channels;
drflac_uint32 sampleRate;
drflac_uint64 iPCMFrame;
pPCMFrames = drflac_open_file_and_read_pcm_frames_s32(pFilePath, &channels, &sampleRate, &pcmFrameCount, NULL);
if (pPCMFrames == NULL) {
printf(" drflac_open_and_read failed.");
return DRFLAC_ERROR; /* Error decoding */
}
if (pcmFrameCount != pLibFlac->pcmFrameCount) {
printf(" Decoded frame counts differ: pcmFrameCount=%d, libFLAC=%d, dr_flac=%d", (int)pcmFrameCount, (int)pLibFlac->currentPCMFrame, (int)pcmFrameCount);
drflac_free(pPCMFrames, NULL);
return DRFLAC_ERROR;
}
for (iPCMFrame = 0; iPCMFrame < pLibFlac->pcmFrameCount; iPCMFrame += 1) {
drflac_int32* pPCMFrame_libflac = pLibFlac->pPCMFrames + (iPCMFrame * pLibFlac->channels);
drflac_int32* pPCMFrame_drflac = pPCMFrames + (iPCMFrame * pLibFlac->channels);
drflac_uint32 iChannel;
drflac_bool32 hasError = DRFLAC_FALSE;
for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
if (pPCMFrame_libflac[iChannel] != pPCMFrame_drflac[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
hasError = DRFLAC_TRUE;
break;
}
}
if (hasError) {
drflac_free(pPCMFrames, NULL);
return DRFLAC_ERROR; /* Decoded frames do not match. */
}
}
drflac_free(pPCMFrames, NULL);
return DRFLAC_SUCCESS;
}
drflac_result open_and_read_test_file_f32(libflac* pLibFlac, const char* pFilePath)
{
float* pPCMFrames;
drflac_uint64 pcmFrameCount;
drflac_uint32 channels;
drflac_uint32 sampleRate;
drflac_uint64 iPCMFrame;
pPCMFrames = drflac_open_file_and_read_pcm_frames_f32(pFilePath, &channels, &sampleRate, &pcmFrameCount, NULL);
if (pPCMFrames == NULL) {
printf(" drflac_open_and_read failed.");
return DRFLAC_ERROR; /* Error decoding */
}
if (pcmFrameCount != pLibFlac->pcmFrameCount) {
printf(" Decoded frame counts differ: pcmFrameCount=%d, libFLAC=%d, dr_flac=%d", (int)pcmFrameCount, (int)pLibFlac->currentPCMFrame, (int)pcmFrameCount);
drflac_free(pPCMFrames, NULL);
return DRFLAC_ERROR;
}
for (iPCMFrame = 0; iPCMFrame < pLibFlac->pcmFrameCount; iPCMFrame += 1) {
drflac_int32* pPCMFrame_libflac = pLibFlac->pPCMFrames + (iPCMFrame * pLibFlac->channels);
float* pPCMFrame_drflac = pPCMFrames + (iPCMFrame * pLibFlac->channels);
drflac_uint32 iChannel;
drflac_bool32 hasError = DRFLAC_FALSE;
for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
if ((pPCMFrame_libflac[iChannel] / 2147483648.0) != pPCMFrame_drflac[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
hasError = DRFLAC_TRUE;
break;
}
}
if (hasError) {
drflac_free(pPCMFrames, NULL);
return DRFLAC_ERROR; /* Decoded frames do not match. */
}
}
drflac_free(pPCMFrames, NULL);
return DRFLAC_SUCCESS;
}
drflac_result open_and_read_test_file_s16(libflac* pLibFlac, const char* pFilePath)
{
drflac_int16* pPCMFrames;
drflac_uint64 pcmFrameCount;
drflac_uint32 channels;
drflac_uint32 sampleRate;
drflac_uint64 iPCMFrame;
pPCMFrames = drflac_open_file_and_read_pcm_frames_s16(pFilePath, &channels, &sampleRate, &pcmFrameCount, NULL);
if (pPCMFrames == NULL) {
printf(" drflac_open_and_read failed.");
return DRFLAC_ERROR; /* Error decoding */
}
if (pcmFrameCount != pLibFlac->pcmFrameCount) {
printf(" Decoded frame counts differ: pcmFrameCount=%d, libFLAC=%d, dr_flac=%d", (int)pcmFrameCount, (int)pLibFlac->currentPCMFrame, (int)pcmFrameCount);
drflac_free(pPCMFrames, NULL);
return DRFLAC_ERROR;
}
for (iPCMFrame = 0; iPCMFrame < pLibFlac->pcmFrameCount; iPCMFrame += 1) {
drflac_int32* pPCMFrame_libflac = pLibFlac->pPCMFrames + (iPCMFrame * pLibFlac->channels);
drflac_int16* pPCMFrame_drflac = pPCMFrames + (iPCMFrame * pLibFlac->channels);
drflac_uint32 iChannel;
drflac_bool32 hasError = DRFLAC_FALSE;
for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
if ((pPCMFrame_libflac[iChannel] >> 16) != pPCMFrame_drflac[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
hasError = DRFLAC_TRUE;
break;
}
}
if (hasError) {
drflac_free(pPCMFrames, NULL);
return DRFLAC_ERROR; /* Decoded frames do not match. */
}
}
drflac_free(pPCMFrames, NULL);
return DRFLAC_SUCCESS;
}
drflac_result open_and_read_test_file(const char* pFilePath)
{
drflac_result result;
libflac libflac;
dr_printf_fixed_with_margin(FILE_NAME_WIDTH, TABLE_MARGIN, "%s", dr_path_file_name(pFilePath));
/* First load the decoder from libFLAC. */
result = libflac_init_file(pFilePath, &libflac);
if (result != DRFLAC_SUCCESS) {
printf(" Failed to open via libFLAC.");
return result;
}
result = open_and_read_test_file_s32(&libflac, pFilePath);
if (result != DRFLAC_SUCCESS) {
return result;
}
open_and_read_test_file_f32(&libflac, pFilePath);
if (result != DRFLAC_SUCCESS) {
return result;
}
open_and_read_test_file_s16(&libflac, pFilePath);
if (result != DRFLAC_SUCCESS) {
return result;
}
libflac_uninit(&libflac);
if (result == DRFLAC_SUCCESS) {
printf(" Passed");
}
return result;
}
drflac_result open_and_read_test_directory(const char* pDirectoryPath)
{
dr_file_iterator iteratorState;
dr_file_iterator* pFile;
dr_printf_fixed(FILE_NAME_WIDTH, "%s", pDirectoryPath);
dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "RESULT");
printf("\n");
pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
while (pFile != NULL) {
drflac_result result;
/* Skip directories for now, but we may want to look at doing recursive file iteration. */
if (!pFile->isDirectory) {
result = open_and_read_test_file(pFile->absolutePath);
(void)result;
printf("\n");
}
pFile = dr_file_iterator_next(pFile);
}
return DRFLAC_SUCCESS;
}
drflac_result open_and_read_test()
{
drflac_result result = DRFLAC_SUCCESS;
/* Directories. */
{
result = open_and_read_test_directory(DEFAULT_SOURCE_DIR);
(void)result;
}
return result;
}
drflac_result decode_profiling_file(const char* pFilePath)
{
drflac_result result;
libflac libflac;
drflac* pFlac;
drflac_int32* pTempBuffer;
double decodeTimeBeg;
double decodeTimeEnd;
double drflacDecodeTimeInSeconds;
void* pFileData;
size_t fileSizeInBytes;
dr_printf_fixed_with_margin(FILE_NAME_WIDTH, 2, "%s", dr_path_file_name(pFilePath));
/* libFLAC */
result = libflac_init_file(pFilePath, &libflac);
if (result != DRFLAC_SUCCESS) {
printf(" [libFLAC] Failed to load file");
return result;
}
/* dr_flac */
pFileData = dr_open_and_read_file(pFilePath, &fileSizeInBytes);
if (pFileData == NULL) {
printf(" Failed to load file");
return DRFLAC_ERROR; /* Failed to open the file. */
}
pFlac = drflac_open_memory(pFileData, fileSizeInBytes, NULL);
if (pFlac == NULL) {
free(pFileData);
printf(" [dr_flac] Failed to load file.");
return DRFLAC_ERROR;
}
/* libFLAC decode time. */
dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "%.2fms", libflac.decodeTimeInSeconds*1000);
/* dr_flac decode time. */
pTempBuffer = (drflac_int32*)malloc((size_t)(libflac.pcmFrameCount * libflac.channels * sizeof(drflac_int32)));
if (pTempBuffer == NULL) {
libflac_uninit(&libflac);
drflac_close(pFlac);
free(pFileData);
printf(" Out of memory.");
return DRFLAC_ERROR; /* Out of memory. */
}
DRFLAC_ZERO_MEMORY(pTempBuffer, (size_t)(libflac.pcmFrameCount * libflac.channels * sizeof(drflac_int32)));
decodeTimeBeg = dr_timer_now();
drflac_read_pcm_frames_s32(pFlac, libflac.pcmFrameCount, pTempBuffer);
decodeTimeEnd = dr_timer_now();
free(pTempBuffer);
free(pFileData);
drflacDecodeTimeInSeconds = decodeTimeEnd - decodeTimeBeg;
dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "%.2fms", drflacDecodeTimeInSeconds*1000);
/* Difference. */
dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "%d%%", (int)(drflacDecodeTimeInSeconds/libflac.decodeTimeInSeconds * 100));
libflac_uninit(&libflac);
drflac_close(pFlac);
return DRFLAC_SUCCESS;
}
drflac_result decode_profiling_directory(const char* pDirectoryPath)
{
dr_file_iterator iteratorState;
dr_file_iterator* pFile;
drflac_bool32 foundError = DRFLAC_FALSE;
dr_printf_fixed(FILE_NAME_WIDTH, "%s", pDirectoryPath);
dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "libFLAC");
dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "dr_flac");
printf("\n");
pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
while (pFile != NULL) {
drflac_result result;
/* Skip directories for now, but we may want to look at doing recursive file iteration. */
if (!pFile->isDirectory) {
result = decode_profiling_file(pFile->absolutePath);
if (result != DRFLAC_SUCCESS) {
foundError = DRFLAC_TRUE;
}
printf("\n");
}
pFile = dr_file_iterator_next(pFile);
}
return (foundError) ? DRFLAC_ERROR : DRFLAC_SUCCESS;
}
drflac_result decode_profiling()
{
drflac_result result = DRFLAC_SUCCESS;
/* Directories. */
{
result = decode_profiling_directory(DEFAULT_SOURCE_DIR);
}
return result;
}
int main(int argc, char** argv)
{
drflac_result result = DRFLAC_SUCCESS;
drflac_bool32 doTesting = DRFLAC_TRUE;
drflac_bool32 doProfiling = DRFLAC_TRUE;
/* This program has two main parts. The first is just a normal functionality test. The second is a profiling of the different seeking methods. */
if (dr_argv_is_set(argc, argv, "--onlyprofile")) {
doTesting = DRFLAC_FALSE;
}
print_cpu_caps();
/* Exhaustive seek test. */
if (doTesting) {
printf("=======================================================================\n");
printf("DECODE TESTING\n");
printf("=======================================================================\n");
result = decode_test();
if (result != DRFLAC_SUCCESS) {
return (int)result; /* Don't continue if an error occurs during testing. */
}
printf("\n");
printf("=======================================================================\n");
printf("OPEN-AND-READ TESTING - drflac_open_*_and_read_pcm_frames_*()\n");
printf("=======================================================================\n");
result = open_and_read_test();
if (result != DRFLAC_SUCCESS) {
return (int)result; /* Don't continue if an error occurs during testing. */
}
printf("\n");
} else {
printf("=======================================================================\n");
printf("WARNING: Correctness Tests Disabled\n");
printf("=======================================================================\n");
}
/* Profiling. */
if (doProfiling) {
printf("=======================================================================\n");
printf("DECODE PROFILING (LOWER IS BETTER)\n");
printf("=======================================================================\n");
result = decode_profiling();
printf("\n");
}
/*getchar();*/
return (int)result;
}

View file

@ -1 +0,0 @@
#include "dr_flac_decoding.c"

View file

@ -1,483 +0,0 @@
/*#define DR_FLAC_NO_CRC*/
#include "dr_flac_common.c"
#define PROFILING_NAME_WIDTH 40
#define PROFILING_NUMBER_WIDTH 10
#define PROFILING_NUMBER_MARGIN 2
typedef struct
{
double totalSeconds_BruteForce;
double totalSeconds_BinarySearch;
double totalSeconds_SeekTable;
} profiling_state;
void profiling_state_init(profiling_state* pProfiling)
{
DRFLAC_ZERO_MEMORY(pProfiling, sizeof(*pProfiling));
}
profiling_state profiling_state_sum(const profiling_state* pA, const profiling_state* pB)
{
profiling_state result;
result.totalSeconds_BruteForce = pA->totalSeconds_BruteForce + pB->totalSeconds_BruteForce;
result.totalSeconds_BinarySearch = pA->totalSeconds_BinarySearch + pB->totalSeconds_BinarySearch;
result.totalSeconds_SeekTable = pA->totalSeconds_SeekTable + pB->totalSeconds_SeekTable;
return result;
}
drflac_result seek_test_pcm_frame(libflac* pLibFlac, drflac* pFlac, drflac_uint64 targetPCMFrameIndex)
{
drflac_bool32 seekResult;
drflac_uint64 pcmFrameCount_libflac;
drflac_uint64 pcmFrameCount_drflac;
drflac_int32* pPCMFrames_libflac;
drflac_int32* pPCMFrames_drflac;
drflac_uint64 iPCMFrame;
if (pFlac->_noSeekTableSeek == DRFLAC_FALSE && pFlac->_noBinarySearchSeek == DRFLAC_TRUE && pFlac->_noBruteForceSeek == DRFLAC_TRUE) {
if (pFlac->seekpointCount == 0) {
printf(" No seek table");
return DRFLAC_ERROR;
}
}
/*
To test seeking we just seek to the PCM frame, and then decode the rest of the file. If the PCM frames we read
differs between the two implementations there's something wrong with one of them (probably dr_flac).
*/
seekResult = libflac_seek_to_pcm_frame(pLibFlac, targetPCMFrameIndex);
if (seekResult == DRFLAC_FALSE) {
printf(" [libFLAC] Failed to seek to PCM frame @ %d", (int)targetPCMFrameIndex);
return DRFLAC_ERROR;
}
seekResult = drflac_seek_to_pcm_frame(pFlac, targetPCMFrameIndex);
if (seekResult == DRFLAC_FALSE) {
printf(" [dr_flac] Failed to seek to PCM frame @ %d", (int)targetPCMFrameIndex);
return DRFLAC_ERROR;
}
if (pLibFlac->currentPCMFrame != pFlac->currentPCMFrame) {
printf(" Current PCM frame inconsistent @ %d: libFLAC=%d, dr_flac=%d", (int)targetPCMFrameIndex, (int)pLibFlac->currentPCMFrame, (int)pFlac->currentPCMFrame);
return DRFLAC_ERROR;
}
/*
Now we decode the rest of the file and compare the samples. Note that we try reading the _entire_ file, not just the leftovers, to ensure we
haven't seeked too short.
*/
pPCMFrames_libflac = (drflac_int32*)malloc((size_t)(pLibFlac->pcmFrameCount * pLibFlac->channels * sizeof(drflac_int32)));
if (pPCMFrames_libflac == NULL) {
printf(" [libFLAC] Out of memory");
return DRFLAC_ERROR;
}
pcmFrameCount_libflac = libflac_read_pcm_frames_s32(pLibFlac, pLibFlac->pcmFrameCount, pPCMFrames_libflac);
pPCMFrames_drflac = (drflac_int32*)malloc((size_t)(pLibFlac->pcmFrameCount * pLibFlac->channels * sizeof(drflac_int32)));
if (pPCMFrames_drflac == NULL) {
free(pPCMFrames_libflac);
printf(" [dr_flac] Out of memory");
return DRFLAC_ERROR;
}
pcmFrameCount_drflac = drflac_read_pcm_frames_s32(pFlac, pLibFlac->pcmFrameCount, pPCMFrames_drflac);
/* The total number of frames we decoded need to match. */
if (pcmFrameCount_libflac != pcmFrameCount_drflac) {
free(pPCMFrames_drflac);
free(pPCMFrames_libflac);
printf(" Decoded frame counts differ @ %d: libFLAC=%d, dr_flac=%d", (int)targetPCMFrameIndex, (int)pLibFlac->currentPCMFrame, (int)pFlac->currentPCMFrame);
return DRFLAC_ERROR;
}
/* Each of the decoded PCM frames need to match. */
DRFLAC_ASSERT(pcmFrameCount_libflac == pcmFrameCount_drflac);
for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libflac; iPCMFrame += 1) {
drflac_int32* pPCMFrame_libflac = pPCMFrames_libflac + (iPCMFrame * pLibFlac->channels);
drflac_int32* pPCMFrame_drflac = pPCMFrames_drflac + (iPCMFrame * pLibFlac->channels);
drflac_uint32 iChannel;
drflac_bool32 hasError = DRFLAC_FALSE;
for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
if (pPCMFrame_libflac[iChannel] != pPCMFrame_drflac[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: targetPCMFrameIndex=%d", (int)iPCMFrame, iChannel, (int)targetPCMFrameIndex);
hasError = DRFLAC_TRUE;
break;
}
}
if (hasError) {
free(pPCMFrames_drflac);
free(pPCMFrames_libflac);
return DRFLAC_ERROR; /* Decoded frames do not match. */
}
}
/* Done. */
free(pPCMFrames_drflac);
free(pPCMFrames_libflac);
return DRFLAC_SUCCESS;
}
drflac_result seek_test_file(const char* pFilePath)
{
/* To test seeking we just seek to our target PCM frame and then decode whatever is remaining and compare it against libFLAC. */
drflac_result result;
libflac libflac;
drflac* pFlac;
drflac_uint32 iteration;
drflac_uint32 totalIterationCount = 10;
dr_printf_fixed_with_margin(PROFILING_NAME_WIDTH, PROFILING_NUMBER_MARGIN, "%s", dr_path_file_name(pFilePath));
/* First load the decoder from libFLAC. */
result = libflac_init_file(pFilePath, &libflac);
if (result != DRFLAC_SUCCESS) {
printf(" Failed to open via libFLAC.");
return result;
}
/* Now load from dr_flac. */
pFlac = drflac_open_file(pFilePath, NULL);
if (pFlac == NULL) {
printf(" Failed to open via dr_flac.");
libflac_uninit(&libflac);
return DRFLAC_ERROR; /* Failed to load dr_flac decoder. */
}
/* Use these to use specific seeking methods. Set all to false to use the normal prioritization (seek table, then binary search, then brute force). */
pFlac->_noSeekTableSeek = DRFLAC_FALSE;
pFlac->_noBinarySearchSeek = DRFLAC_FALSE;
pFlac->_noBruteForceSeek = DRFLAC_FALSE;
/* At this point we should have both libFLAC and dr_flac decoders open. We can now perform identical operations on each of them and compare. */
/* Start with the basics: Seek to the very end, and then the very start. */
if (result == DRFLAC_SUCCESS) {
result = seek_test_pcm_frame(&libflac, pFlac, libflac.pcmFrameCount);
}
if (result == DRFLAC_SUCCESS) {
result = seek_test_pcm_frame(&libflac, pFlac, 0);
}
/* Now we'll try seeking to random locations. */
dr_seed(1234);
iteration = 0;
while (result == DRFLAC_SUCCESS && iteration < totalIterationCount) {
dr_uint64 targetPCMFrame = dr_rand_range_u64(0, libflac.pcmFrameCount);
if (targetPCMFrame > libflac.pcmFrameCount) {
DRFLAC_ASSERT(DRFLAC_FALSE); /* Should never hit this, but if we do it means our random number generation routine is wrong. */
}
result = seek_test_pcm_frame(&libflac, pFlac, targetPCMFrame);
iteration += 1;
}
/* We're done with our decoders. */
drflac_close(pFlac);
libflac_uninit(&libflac);
if (result == DRFLAC_SUCCESS) {
printf(" Passed");
}
return result;
}
drflac_result seek_test_directory(const char* pDirectoryPath)
{
dr_file_iterator iteratorState;
dr_file_iterator* pFile;
dr_printf_fixed(PROFILING_NAME_WIDTH, "%s", pDirectoryPath);
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "RESULT");
printf("\n");
pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
while (pFile != NULL) {
drflac_result result;
/* Skip directories for now, but we may want to look at doing recursive file iteration. */
if (!pFile->isDirectory) {
result = seek_test_file(pFile->absolutePath);
(void)result;
printf("\n");
}
pFile = dr_file_iterator_next(pFile);
}
return DRFLAC_SUCCESS;
}
drflac_result seek_test()
{
drflac_result result = DRFLAC_SUCCESS;
/* Directories. */
{
result = seek_test_directory("testvectors/flac/tests");
(void)result;
}
return result;
}
drflac_result seek_profiling_drflac_and_close(drflac* pFlac, double* pProcessingTime)
{
drflac_result result = DRFLAC_SUCCESS;
int i;
if (pFlac == NULL) {
result = DRFLAC_INVALID_ARGS;
goto done;
}
if (pFlac->totalPCMFrameCount == 0) {
result = DRFLAC_INVALID_ARGS;
goto done;
}
if (pProcessingTime != NULL) {
*pProcessingTime = 0;
}
/* Seek back to the start to keep everything normalized. */
drflac_seek_to_pcm_frame(pFlac, 0);
/* Random seek points based on a seed. */
dr_seed(1234);
/*dr_seed(4321);*/
for (i = 0; i < 100; ++i) {
double startTime;
double endTime;
dr_uint64 targetPCMFrame = dr_rand_range_u64(0, pFlac->totalPCMFrameCount);
startTime = dr_timer_now();
{
drflac_seek_to_pcm_frame(pFlac, targetPCMFrame);
}
endTime = dr_timer_now();
if (pProcessingTime != NULL) {
*pProcessingTime += (endTime - startTime);
}
}
done:
drflac_close(pFlac);
return result;
}
drflac_result seek_profiling_file__seek_table(const char* pFilePath, double* pProcessingTime)
{
drflac* pFlac;
if (pFilePath == NULL) {
return DRFLAC_INVALID_ARGS;
}
pFlac = drflac_open_file(pFilePath, NULL);
if (pFlac == NULL) {
return DRFLAC_ERROR;
}
pFlac->_noSeekTableSeek = DRFLAC_FALSE;
pFlac->_noBinarySearchSeek = DRFLAC_TRUE;
pFlac->_noBruteForceSeek = DRFLAC_TRUE;
return seek_profiling_drflac_and_close(pFlac, pProcessingTime);
}
drflac_result seek_profiling_file__binary_search(const char* pFilePath, double* pProcessingTime)
{
drflac* pFlac;
if (pFilePath == NULL) {
return DRFLAC_INVALID_ARGS;
}
pFlac = drflac_open_file(pFilePath, NULL);
if (pFlac == NULL) {
return DRFLAC_ERROR;
}
pFlac->_noSeekTableSeek = DRFLAC_TRUE;
pFlac->_noBinarySearchSeek = DRFLAC_FALSE;
pFlac->_noBruteForceSeek = DRFLAC_TRUE;
return seek_profiling_drflac_and_close(pFlac, pProcessingTime);
}
drflac_result seek_profiling_file__brute_force(const char* pFilePath, double* pProcessingTime)
{
drflac* pFlac;
if (pFilePath == NULL) {
return DRFLAC_INVALID_ARGS;
}
pFlac = drflac_open_file(pFilePath, NULL);
if (pFlac == NULL) {
return DRFLAC_ERROR;
}
pFlac->_noSeekTableSeek = DRFLAC_TRUE;
pFlac->_noBinarySearchSeek = DRFLAC_TRUE;
pFlac->_noBruteForceSeek = DRFLAC_FALSE;
return seek_profiling_drflac_and_close(pFlac, pProcessingTime);
}
drflac_result seek_profiling_file(const char* pFilePath, profiling_state* pProfiling)
{
drflac_result result;
profiling_state_init(pProfiling);
/*
There are different seeking modes, and each one is profiled so that we can compare the results:
- Brute Force
- Binary Search
- Seek Table
In order to keep the total run time fair, we can only include files with a seek table.
*/
dr_printf_fixed_with_margin(PROFILING_NAME_WIDTH, 2, "%s", dr_path_file_name(pFilePath));
/* Start off with the seek table version. If this fails we don't bother continuing. */
#if 1
result = seek_profiling_file__seek_table(pFilePath, &pProfiling->totalSeconds_SeekTable);
if (result != DRFLAC_SUCCESS) {
return result;
}
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "%f", pProfiling->totalSeconds_SeekTable);
#else
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "");
#endif
#if 1
result = seek_profiling_file__binary_search(pFilePath, &pProfiling->totalSeconds_BinarySearch);
if (result != DRFLAC_SUCCESS) {
return result;
}
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "%f", pProfiling->totalSeconds_BinarySearch);
#else
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "");
#endif
#if 1
result = seek_profiling_file__brute_force(pFilePath, &pProfiling->totalSeconds_BruteForce);
if (result != DRFLAC_SUCCESS) {
return result;
}
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "%f", pProfiling->totalSeconds_BruteForce);
#else
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "");
#endif
return DRFLAC_SUCCESS;
}
drflac_result seek_profiling_directory(const char* pDirectoryPath, profiling_state* pProfiling)
{
dr_file_iterator iteratorState;
dr_file_iterator* pFile;
profiling_state_init(pProfiling);
dr_printf_fixed(PROFILING_NAME_WIDTH, "%s", pDirectoryPath);
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "S/Table");
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "Bin Srch");
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "B/Force");
printf("\n");
pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
while (pFile != NULL) {
drflac_result result;
profiling_state fileProfiling;
/* Skip directories for now, but we may want to look at doing recursive file iteration. */
if (!pFile->isDirectory) {
result = seek_profiling_file(pFile->absolutePath, &fileProfiling);
if (result == DRFLAC_SUCCESS) {
*pProfiling = profiling_state_sum(pProfiling, &fileProfiling);
}
printf("\n");
}
pFile = dr_file_iterator_next(pFile);
}
return DRFLAC_SUCCESS;
}
drflac_result seek_profiling()
{
drflac_result result = DRFLAC_SUCCESS;
profiling_state globalProfiling;
profiling_state_init(&globalProfiling);
/* Directories. */
{
profiling_state directoryProfiling;
result = seek_profiling_directory("testvectors/flac/tests", &directoryProfiling);
if (result == DRFLAC_SUCCESS) {
globalProfiling = profiling_state_sum(&globalProfiling, &directoryProfiling);
}
}
return result;
}
int main(int argc, char** argv)
{
drflac_result result = DRFLAC_SUCCESS;
drflac_bool32 doTesting = DRFLAC_TRUE;
drflac_bool32 doProfiling = DRFLAC_TRUE;
/* This program has two main parts. The first is just a normal functionality test. The second is a profiling of the different seeking methods. */
if (dr_argv_is_set(argc, argv, "--onlyprofile")) {
doTesting = DRFLAC_FALSE;
}
/* Exhaustive seek test. */
if (doTesting) {
printf("=======================================================================\n");
printf("SEEK TESTING\n");
printf("=======================================================================\n");
result = seek_test();
if (result != DRFLAC_SUCCESS) {
return (int)result; /* Don't continue if an error occurs during testing. */
}
printf("\n");
} else {
printf("=======================================================================\n");
printf("WARNING: Correctness Tests Disabled\n");
printf("=======================================================================\n");
}
/* Profiling. */
if (doProfiling) {
printf("=======================================================================\n");
printf("SEEK PROFILING\n");
printf("=======================================================================\n");
result = seek_profiling();
printf("\n");
}
/*getchar();*/
return (int)result;
}

View file

@ -1,11 +0,0 @@
#define DR_FLAC_IMPLEMENTATION
#include "../../dr_flac.h"
#include "../common/dr_common.c"
int main(int argc, char** argv)
{
(void)argc;
(void)argv;
return 0;
}

View file

@ -1 +0,0 @@
#include "dr_flac_test_0.c"

View file

@ -1,76 +0,0 @@
/*
* Fuzz tester for dr_flac.h
*
* compile with
* clang -g -O1 -fsanitize=fuzzer,address -o fuzz_dr_flac fuzz_dr_flac.c
*
* and run ./fuzz_dr_flac to run fuzz testing
*
* Other sanitizers are possible, for example
* -fsanitize=fuzzer,memory
* -fsanitize=fuzzer,undefined
*
* For more options, run ./fuzz_dr_flac -help=1
*
* If a problem is found, the problematic input is saved and can be
* rerun (with for example a debugger) with
*
* ./fuzz_dr_flac file
*
*/
#include <stdint.h>
#define DR_FLAC_IMPLEMENTATION
#define DR_FLAC_NO_CRC
#define DR_FLAC_NO_STDIO
#include "../../dr_flac.h"
#define MIN(a,b) (((a)<(b))?(a):(b))
uint8_t fuzz_flacstream[4096] = {0};
size_t fuzz_flacstream_position;
size_t fuzz_flacstream_length;
static size_t read_fuzz_flacstream(void* pUserData, void* bufferOut, size_t bytesToRead)
{
size_t readsize = MIN(bytesToRead, fuzz_flacstream_length-fuzz_flacstream_position);
if (readsize > 0) {
memcpy(bufferOut, fuzz_flacstream+fuzz_flacstream_position, readsize);
fuzz_flacstream_position += readsize;
return readsize;
} else {
return 0;
}
}
static drflac_bool32 seek_fuzz_flacstream(void* pUserData, int offset, drflac_seek_origin origin)
{
if ((int)fuzz_flacstream_position+offset < 0 || (int)fuzz_flacstream_position+offset > fuzz_flacstream_length) {
return 1;
} else {
fuzz_flacstream_position = (int)fuzz_flacstream_position+offset;
return 0;
}
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
if (size > 2) {
drflac * drflac_fuzzer;
drflac_int32 drflac_fuzzer_out[2048] = {0}; /* 256 samples over 8 channels */
drflac_container container = data[0] & 1 ? drflac_container_native : drflac_container_ogg;
memcpy(fuzz_flacstream, data, (size-1)<4096?(size-1):4096);
fuzz_flacstream_position = 0;
fuzz_flacstream_length = size-1;
drflac_fuzzer = drflac_open_relaxed(read_fuzz_flacstream, seek_fuzz_flacstream, container, NULL, NULL);
while (drflac_read_pcm_frames_s32(drflac_fuzzer, 256, drflac_fuzzer_out));
drflac_close(drflac_fuzzer);
}
return 0;
}

View file

@ -1,4 +0,0 @@
#define DR_MP3_IMPLEMENTATION
#include "../../dr_mp3.h"
#include "../common/dr_common.c"

View file

@ -1,161 +0,0 @@
#include "dr_mp3_common.c"
#define MA_NO_DECODING
#define MA_NO_ENCODING
#define MINIAUDIO_IMPLEMENTATION
#include "../external/miniaudio/miniaudio.h"
void data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
{
drmp3* pMP3;
pMP3 = (drmp3*)pDevice->pUserData;
DRMP3_ASSERT(pMP3 != NULL);
if (pDevice->playback.format == ma_format_f32) {
drmp3_read_pcm_frames_f32(pMP3, frameCount, pFramesOut);
} else if (pDevice->playback.format == ma_format_s16) {
drmp3_read_pcm_frames_s16(pMP3, frameCount, pFramesOut);
} else {
DRMP3_ASSERT(DRMP3_FALSE); /* Should never get here. */
}
(void)pFramesIn;
}
int do_decoding_validation(const char* pFilePath)
{
int result = 0;
drmp3 mp3Memory;
drmp3 mp3File;
size_t dataSize;
void* pData;
/*
When opening from a memory buffer, dr_mp3 will take a different path for decoding which is optimized to reduce data movement. Since it's
running on a separate path, we need to ensure it's returning consistent results with the other code path which will be used when decoding
from a file.
*/
/* Initialize the memory decoder. */
pData = dr_open_and_read_file(pFilePath, &dataSize);
if (pData == NULL) {
printf("Failed to open file \"%s\"", pFilePath);
return -1;
}
if (!drmp3_init_memory(&mp3Memory, pData, dataSize, NULL)) {
free(pData);
printf("Failed to init MP3 decoder \"%s\"", pFilePath);
return -1;
}
/* Initialize the file decoder. */
if (!drmp3_init_file(&mp3File, pFilePath, NULL)) {
drmp3_uninit(&mp3Memory);
free(pData);
printf("Failed to open file \"%s\"", pFilePath);
return -1;
}
DRMP3_ASSERT(mp3Memory.channels == mp3File.channels);
/* Compare. */
for (;;) {
drmp3_uint64 iSample;
drmp3_uint64 pcmFrameCountMemory;
drmp3_uint64 pcmFrameCountFile;
drmp3_int16 pcmFramesMemory[4096];
drmp3_int16 pcmFramesFile[4096];
pcmFrameCountMemory = drmp3_read_pcm_frames_s16(&mp3Memory, DRMP3_COUNTOF(pcmFramesMemory) / mp3Memory.channels, pcmFramesMemory);
pcmFrameCountFile = drmp3_read_pcm_frames_s16(&mp3File, DRMP3_COUNTOF(pcmFramesFile) / mp3File.channels, pcmFramesFile);
/* Check the frame count first. */
if (pcmFrameCountMemory != pcmFrameCountFile) {
printf("Frame counts differ: memory = %d; file = %d\n", (int)pcmFrameCountMemory, (int)pcmFrameCountFile);
result = -1;
break;
}
/* Check individual frames. */
DRMP3_ASSERT(pcmFrameCountMemory == pcmFrameCountFile);
for (iSample = 0; iSample < pcmFrameCountMemory * mp3Memory.channels; iSample += 1) {
if (pcmFramesMemory[iSample] != pcmFramesFile[iSample]) {
printf("Samples differ: memory = %d; file = %d\n", (int)pcmFramesMemory[iSample], (int)pcmFramesFile[iSample]);
result = -1;
break;
}
}
/* We've reached the end if we didn't return any PCM frames. */
if (pcmFrameCountMemory == 0 || pcmFrameCountFile == 0) {
break;
}
}
drmp3_uninit(&mp3File);
drmp3_uninit(&mp3Memory);
free(pData);
return result;
}
int main(int argc, char** argv)
{
drmp3 mp3;
ma_result resultMA;
ma_device_config deviceConfig;
ma_device device;
const char* pInputFilePath;
if (argc < 2) {
printf("No input file.\n");
return -1;
}
pInputFilePath = argv[1];
/* Quick validation test first. */
if (do_decoding_validation(pInputFilePath) != 0) {
return -1;
}
if (!drmp3_init_file(&mp3, pInputFilePath, NULL)) {
printf("Failed to open file \"%s\"", pInputFilePath);
return -1;
}
deviceConfig = ma_device_config_init(ma_device_type_playback);
deviceConfig.playback.format = ma_format_s16;
deviceConfig.playback.channels = mp3.channels;
deviceConfig.sampleRate = mp3.sampleRate;
deviceConfig.dataCallback = data_callback;
deviceConfig.pUserData = &mp3;
resultMA = ma_device_init(NULL, &deviceConfig, &device);
if (resultMA != MA_SUCCESS) {
drmp3_uninit(&mp3);
printf("Failed to initialize playback device: %s.\n", ma_result_description(resultMA));
return -1;
}
resultMA = ma_device_start(&device);
if (resultMA != MA_SUCCESS) {
ma_device_uninit(&device);
drmp3_uninit(&mp3);
printf("Failed to start playback device: %s.\n", ma_result_description(resultMA));
return -1;
}
printf("Press Enter to quit...");
getchar();
/* We're done. */
ma_device_uninit(&device);
drmp3_uninit(&mp3);
return 0;
}

View file

@ -1,12 +0,0 @@
#define DR_MP3_IMPLEMENTATION
#include "../../dr_mp3.h"
#include "../common/dr_common.c"
int main(int argc, char** argv)
{
(void)argc;
(void)argv;
return 0;
}

View file

@ -1,205 +0,0 @@
/*#define DR_OPUS_DEBUGGING*/
#define DR_OPUS_IMPLEMENTATION
#include "../../wip/dr_opus.h"
#include "../common/dr_common.c"
const char* dropus_mode_to_string(dropus_mode mode) /* Move this into dr_opus.h? */
{
switch (mode)
{
case dropus_mode_silk: return "SILK";
case dropus_mode_celt: return "CELT";
case dropus_mode_hybrid: return "Hybrid";
default: break;
}
return "Unknown";
}
#include <stdio.h>
/* Forward declare our debugging entry point if necessary. */
#ifdef DR_OPUS_DEBUGGING
int main_debugging(int argc, char** argv);
#endif
dropus_result test_standard_vector(const char* pFilePath)
{
dropus_result result;
dropus_stream stream;
void* pFileData;
size_t fileSize;
const dropus_uint8* pRunningData8;
size_t runningPos;
dropus_uint32 iPacket = 0; /* For error reporting. */
/* Load the entire file into memory first. */
pFileData = dr_open_and_read_file(pFilePath, &fileSize);
if (pFileData == NULL) {
return DROPUS_ERROR; /* File not found. */
}
/* Now initialize the stream in preparation for decoding packets. */
result = dropus_stream_init(&stream);
if (result != DROPUS_SUCCESS) {
free(pFileData);
return result;
}
/*
Here is where we scan the test vector. The way the file is structured is quite simple. It is made up of a list of Opus packets, each
of which include an 8 byte header where the first 4 bytes is the size in bytes of the Opus packet (little endian) and the next 4 bytes
contain the 32-bit range state which we'll use for validation.
*/
pRunningData8 = (const dropus_uint8*)pFileData;
runningPos = 0;
/* For each packet... */
while (runningPos < fileSize) {
dropus_result decodeResult;
dropus_uint32 packetSize;
dropus_uint32 rangeState;
memcpy(&packetSize, pRunningData8 + 0, 4);
memcpy(&rangeState, pRunningData8 + 4, 4);
packetSize = dropus__be2host_32(packetSize);
rangeState = dropus__be2host_32(rangeState);
pRunningData8 += 8;
runningPos += 8;
/* Safety. Break if we've run out of data. */
if ((runningPos + packetSize) > fileSize) {
printf("WARNING: Ran out of data before the end of the file.\n");
break;
}
decodeResult = dropus_stream_decode_packet(&stream, pRunningData8, packetSize);
if (decodeResult != DROPUS_SUCCESS) {
result = DROPUS_ERROR;
printf("Failed to decode packet %d\n", iPacket);
}
printf("Opus Packet %d: Mode=%s\n", iPacket, dropus_mode_to_string(dropus_toc_mode(stream.packet.toc)));
pRunningData8 += packetSize;
runningPos += packetSize;
iPacket += 1;
}
free(pFileData);
return result;
}
dropus_result test_standard_vectors_folder(const char* pFolderPath)
{
dropus_bool32 foundError = DROPUS_FALSE;
dr_file_iterator iteratorState;
dr_file_iterator* pFile;
pFile = dr_file_iterator_begin(pFolderPath, &iteratorState);
while (pFile != NULL) {
/* Only look at files with the extension "bit". */
if (dr_extension_equal(pFile->relativePath, "bit")) {
if (test_standard_vector(pFile->absolutePath) != DROPUS_SUCCESS) {
foundError = DROPUS_TRUE;
}
}
pFile = dr_file_iterator_next(pFile);
}
if (foundError) {
return DROPUS_ERROR;
} else {
return DROPUS_SUCCESS;
}
}
dropus_result test_standard_vectors()
{
dropus_bool32 foundError = DROPUS_FALSE;
/* Two groups of standard test vectors. The original vectors and the "new" vectors. */
/* Original vectors. */
if (test_standard_vectors_folder("testvectors/opus/opus_testvectors") != DROPUS_SUCCESS) {
foundError = DROPUS_TRUE;
}
/* New vectors. */
if (test_standard_vectors_folder("testvectors/opus/opus_newvectors") != DROPUS_SUCCESS) {
foundError = DROPUS_TRUE;
}
if (foundError) {
return DROPUS_ERROR;
} else {
return DROPUS_SUCCESS;
}
}
dropus_result test_ogg_vectors()
{
dropus_result result;
/* Ogg Opus test vectors are in the "oggopus" folder. */
result = DROPUS_SUCCESS;
return result;
}
int main(int argc, char** argv)
{
dropus_result result;
/* Do debugging stuff first. */
#ifdef DR_OPUS_DEBUGGING
main_debugging(argc, argv);
#endif
result = test_standard_vector("testvectors/opus/opus_testvectors/testvector02.bit");
if (result != DROPUS_SUCCESS) {
/*return (int)result;*/
}
/* Test standard vectors first. */
result = test_standard_vectors();
if (result != DROPUS_SUCCESS) {
/*return (int)result;*/
}
/* Ogg Opus. */
result = test_ogg_vectors();
if (result != DROPUS_SUCCESS) {
/*return (int)result;*/
}
(void)argc;
(void)argv;
return 0;
}
#ifdef DR_OPUS_DEBUGGING
int main_debugging(int argc, char** argv)
{
dr_file_iterator iterator;
dr_file_iterator* pFile = dr_file_iterator_begin("testvectors/opus/oggopus", &iterator);
while (pFile != NULL) {
if (pFile->isDirectory) {
printf("DIRECTORY: %s : %s\n", pFile->relativePath, pFile->absolutePath);
} else {
printf("FILE: %s : %s\n", pFile->relativePath, pFile->absolutePath);
}
pFile = dr_file_iterator_next(pFile);
}
(void)argc;
(void)argv;
return 0;
}
#endif

View file

@ -1,9 +0,0 @@
#define DR_OPUS_IMPLEMENTATION
#include "../../wip/dr_opus.h"
int main(int argc, char** argv)
{
(void)argc;
(void)argv;
return 0;
}

View file

@ -1 +0,0 @@
#include "dr_opus_test_0.c"

View file

@ -1,326 +0,0 @@
#define DR_WAV_IMPLEMENTATION
#include "../../dr_wav.h"
#include <sndfile.h>
#include "../common/dr_common.c"
dr_handle g_libsndfile = NULL;
typedef SNDFILE* (* pfn_sf_open_virtual)(SF_VIRTUAL_IO* sfvirtual, int mode, SF_INFO* sfinfo, void* user_data);
typedef int (* pfn_sf_close) (SNDFILE* sndfile);
typedef sf_count_t (* pfn_sf_readf_short) (SNDFILE *sndfile, short *ptr, sf_count_t frames);
typedef sf_count_t (* pfn_sf_readf_int) (SNDFILE *sndfile, int *ptr, sf_count_t frames);
typedef sf_count_t (* pfn_sf_readf_float) (SNDFILE *sndfile, float *ptr, sf_count_t frames);
typedef sf_count_t (* pfn_sf_readf_double)(SNDFILE *sndfile, double *ptr, sf_count_t frames);
typedef sf_count_t (* pfn_sf_seek) (SNDFILE *sndfile, sf_count_t frames, int whence);
pfn_sf_open_virtual libsndfile__sf_open_virtual;
pfn_sf_close libsndfile__sf_close;
pfn_sf_readf_short libsndfile__sf_readf_short;
pfn_sf_readf_int libsndfile__sf_readf_int;
pfn_sf_readf_float libsndfile__sf_readf_float;
pfn_sf_readf_double libsndfile__sf_readf_double;
pfn_sf_seek libsndfile__sf_seek;
drwav_result libsndfile_init_api()
{
unsigned int i;
const char* pFileNames[] = {
#if defined(_WIN32)
#if defined(_WIN64)
"libsndfile-1-x64.dll",
#else
"libsndfile-1-x86.dll",
#endif
"libsndfile-1.dll"
#else
"libsndfile-1.so",
"libsndfile.so.1"
#endif
};
if (g_libsndfile != NULL) {
return DRWAV_INVALID_OPERATION; /* Already initialized. */
}
for (i = 0; i < sizeof(pFileNames)/sizeof(pFileNames[0]); i += 1) {
g_libsndfile = dr_dlopen(pFileNames[i]);
if (g_libsndfile != NULL) {
break;
}
}
if (g_libsndfile == NULL) {
return DRWAV_ERROR; /* Unable to load libsndfile-1.so/dll. */
}
libsndfile__sf_open_virtual = (pfn_sf_open_virtual)dr_dlsym(g_libsndfile, "sf_open_virtual");
libsndfile__sf_close = (pfn_sf_close) dr_dlsym(g_libsndfile, "sf_close");
libsndfile__sf_readf_short = (pfn_sf_readf_short) dr_dlsym(g_libsndfile, "sf_readf_short");
libsndfile__sf_readf_int = (pfn_sf_readf_int) dr_dlsym(g_libsndfile, "sf_readf_int");
libsndfile__sf_readf_float = (pfn_sf_readf_float) dr_dlsym(g_libsndfile, "sf_readf_float");
libsndfile__sf_readf_double = (pfn_sf_readf_double)dr_dlsym(g_libsndfile, "sf_readf_double");
libsndfile__sf_seek = (pfn_sf_seek) dr_dlsym(g_libsndfile, "sf_seek");
return DRWAV_SUCCESS;
}
void libsndfile_uninit_api()
{
if (g_libsndfile == NULL) {
return; /* Invalid operation. Not initialized. */
}
dr_dlclose(g_libsndfile);
g_libsndfile = NULL;
}
typedef struct
{
SNDFILE* pHandle;
SF_INFO info;
drwav_uint8* pFileData;
size_t fileSizeInBytes;
size_t fileReadPos;
} libsndfile;
sf_count_t libsndfile__on_filelen(void *user_data)
{
libsndfile* pSndFile = (libsndfile*)user_data;
return (sf_count_t)pSndFile->fileSizeInBytes;
}
sf_count_t libsndfile__on_seek(sf_count_t offset, int whence, void *user_data)
{
libsndfile* pSndFile = (libsndfile*)user_data;
switch (whence)
{
case SF_SEEK_SET:
{
pSndFile->fileReadPos = (size_t)offset;
} break;
case SF_SEEK_CUR:
{
pSndFile->fileReadPos += (size_t)offset;
} break;
case SF_SEEK_END:
{
pSndFile->fileReadPos = pSndFile->fileSizeInBytes - (size_t)offset;
} break;
}
return (sf_count_t)pSndFile->fileReadPos;
}
sf_count_t libsndfile__on_read(void *ptr, sf_count_t count, void *user_data)
{
libsndfile* pSndFile = (libsndfile*)user_data;
DRWAV_COPY_MEMORY(ptr, pSndFile->pFileData + pSndFile->fileReadPos, (size_t)count);
pSndFile->fileReadPos += (size_t)count;
return count;
}
sf_count_t libsndfile__on_write(const void *ptr, sf_count_t count, void *user_data)
{
/* We're not doing anything with writing. */
(void)ptr;
(void)count;
(void)user_data;
return 0;
}
sf_count_t libsndfile__on_tell(void *user_data)
{
libsndfile* pSndFile = (libsndfile*)user_data;
return (sf_count_t)pSndFile->fileReadPos;
}
drwav_result libsndfile_init_file(const char* pFilePath, libsndfile* pSndFile)
{
SF_VIRTUAL_IO callbacks;
if (pFilePath == NULL || pSndFile == NULL) {
return DRWAV_INVALID_ARGS;
}
DRWAV_ZERO_MEMORY(pSndFile, sizeof(*pSndFile));
/* We use libsndfile's virtual IO technique because we want to load from memory to make speed benchmarking fairer. */
pSndFile->pFileData = (drwav_uint8*)dr_open_and_read_file(pFilePath, &pSndFile->fileSizeInBytes);
if (pSndFile->pFileData == NULL) {
return DRWAV_ERROR; /* Failed to open the file. */
}
DRWAV_ZERO_MEMORY(&callbacks, sizeof(callbacks));
callbacks.get_filelen = libsndfile__on_filelen;
callbacks.seek = libsndfile__on_seek;
callbacks.read = libsndfile__on_read;
callbacks.write = libsndfile__on_write;
callbacks.tell = libsndfile__on_tell;
pSndFile->pHandle = libsndfile__sf_open_virtual(&callbacks, SFM_READ, &pSndFile->info, pSndFile);
if (pSndFile->pHandle == NULL) {
free(pSndFile->pFileData);
return DRWAV_ERROR;
}
return DRWAV_SUCCESS;
}
void libsndfile_uninit(libsndfile* pSndFile)
{
if (pSndFile == NULL) {
return;
}
libsndfile__sf_close(pSndFile->pHandle);
free(pSndFile->pFileData);
}
drwav_uint64 libsndfile_read_pcm_frames_s16(libsndfile* pSndFile, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
{
if (pSndFile == NULL || pBufferOut == NULL) {
return 0;
}
/* Unfortunately it looks like libsndfile does not return correct integral values when the source file is floating point. */
if ((pSndFile->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT) {
/* Read as float and convert. */
drwav_uint64 totalFramesRead = 0;
while (totalFramesRead < framesToRead) {
float temp[4096];
drwav_uint64 framesRemaining = framesToRead - totalFramesRead;
drwav_uint64 framesReadThisIteration;
drwav_uint64 framesToReadThisIteration = sizeof(temp)/sizeof(temp[0]) / pSndFile->info.channels;
if (framesToReadThisIteration > framesRemaining) {
framesToReadThisIteration = framesRemaining;
}
framesReadThisIteration = libsndfile__sf_readf_float(pSndFile->pHandle, temp, (sf_count_t)framesToReadThisIteration);
drwav_f32_to_s16(pBufferOut, temp, (size_t)(framesReadThisIteration*pSndFile->info.channels));
totalFramesRead += framesReadThisIteration;
pBufferOut += framesReadThisIteration*pSndFile->info.channels;
/* If we read less frames than we requested we've reached the end of the file. */
if (framesReadThisIteration < framesToReadThisIteration) {
break;
}
}
return totalFramesRead;
} else if ((pSndFile->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_DOUBLE) {
/* Read as double and convert. */
drwav_uint64 totalFramesRead = 0;
while (totalFramesRead < framesToRead) {
double temp[4096];
drwav_uint64 framesRemaining = framesToRead - totalFramesRead;
drwav_uint64 framesReadThisIteration;
drwav_uint64 framesToReadThisIteration = sizeof(temp)/sizeof(temp[0]) / pSndFile->info.channels;
if (framesToReadThisIteration > framesRemaining) {
framesToReadThisIteration = framesRemaining;
}
framesReadThisIteration = libsndfile__sf_readf_double(pSndFile->pHandle, temp, (sf_count_t)framesToReadThisIteration);
drwav_f64_to_s16(pBufferOut, temp, (size_t)(framesReadThisIteration*pSndFile->info.channels));
totalFramesRead += framesReadThisIteration;
pBufferOut += framesReadThisIteration*pSndFile->info.channels;
/* If we read less frames than we requested we've reached the end of the file. */
if (framesReadThisIteration < framesToReadThisIteration) {
break;
}
}
return totalFramesRead;
} else {
return libsndfile__sf_readf_short(pSndFile->pHandle, pBufferOut, framesToRead);
}
}
drwav_uint64 libsndfile_read_pcm_frames_f32(libsndfile* pSndFile, drwav_uint64 framesToRead, float* pBufferOut)
{
if (pSndFile == NULL || pBufferOut == NULL) {
return 0;
}
return libsndfile__sf_readf_float(pSndFile->pHandle, pBufferOut, framesToRead);
}
drwav_uint64 libsndfile_read_pcm_frames_s32(libsndfile* pSndFile, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
{
if (pSndFile == NULL || pBufferOut == NULL) {
return 0;
}
/* Unfortunately it looks like libsndfile does not return correct integral values when the source file is floating point. */
if ((pSndFile->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT) {
/* Read floats and convert. */
drwav_uint64 totalFramesRead = 0;
while (totalFramesRead < framesToRead) {
float temp[4096];
drwav_uint64 framesRemaining = framesToRead - totalFramesRead;
drwav_uint64 framesReadThisIteration;
drwav_uint64 framesToReadThisIteration = sizeof(temp)/sizeof(temp[0]) / pSndFile->info.channels;
if (framesToReadThisIteration > framesRemaining) {
framesToReadThisIteration = framesRemaining;
}
framesReadThisIteration = libsndfile__sf_readf_float(pSndFile->pHandle, temp, (sf_count_t)framesToReadThisIteration);
drwav_f32_to_s32(pBufferOut, temp, (size_t)(framesReadThisIteration*pSndFile->info.channels));
totalFramesRead += framesReadThisIteration;
pBufferOut += framesReadThisIteration*pSndFile->info.channels;
/* If we read less frames than we requested we've reached the end of the file. */
if (framesReadThisIteration < framesToReadThisIteration) {
break;
}
}
return totalFramesRead;
} else if ((pSndFile->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_DOUBLE) {
/* Read doubles and convert. */
drwav_uint64 totalFramesRead = 0;
while (totalFramesRead < framesToRead) {
double temp[4096];
drwav_uint64 framesRemaining = framesToRead - totalFramesRead;
drwav_uint64 framesReadThisIteration;
drwav_uint64 framesToReadThisIteration = sizeof(temp)/sizeof(temp[0]) / pSndFile->info.channels;
if (framesToReadThisIteration > framesRemaining) {
framesToReadThisIteration = framesRemaining;
}
framesReadThisIteration = libsndfile__sf_readf_double(pSndFile->pHandle, temp, (sf_count_t)framesToReadThisIteration);
drwav_f64_to_s32(pBufferOut, temp, (size_t)(framesReadThisIteration*pSndFile->info.channels));
totalFramesRead += framesReadThisIteration;
pBufferOut += framesReadThisIteration*pSndFile->info.channels;
/* If we read less frames than we requested we've reached the end of the file. */
if (framesReadThisIteration < framesToReadThisIteration) {
break;
}
}
return totalFramesRead;
} else {
return libsndfile__sf_readf_int(pSndFile->pHandle, pBufferOut, framesToRead);
}
}
drwav_bool32 libsndfile_seek_to_pcm_frame(libsndfile* pSndFile, drwav_uint64 targetPCMFrameIndex)
{
if (pSndFile == NULL) {
return DRWAV_FALSE;
}
return libsndfile__sf_seek(pSndFile->pHandle, (sf_count_t)targetPCMFrameIndex, SF_SEEK_SET) == (sf_count_t)targetPCMFrameIndex;
}

View file

@ -1,446 +0,0 @@
#define DR_WAV_LIBSNDFILE_COMPAT
#include "dr_wav_common.c"
#define FILE_NAME_WIDTH 40
#define NUMBER_WIDTH 10
#define TABLE_MARGIN 2
#define DEFAULT_SOURCE_DIR "testvectors/wav/tests"
drwav_result decode_test__read_and_compare_pcm_frames_s32(libsndfile* pSndFile, drwav* pWav, drwav_uint64 pcmFrameCount, drwav_int32* pPCMFrames_libsndfile, drwav_int32* pPCMFrames_drwav)
{
drwav_uint64 pcmFrameCount_libsndfile;
drwav_uint64 pcmFrameCount_drwav;
drwav_uint64 iPCMFrame;
/* To test decoding we just read a number of PCM frames from each decoder and compare. */
pcmFrameCount_libsndfile = libsndfile_read_pcm_frames_s32(pSndFile, pcmFrameCount, pPCMFrames_libsndfile);
pcmFrameCount_drwav = drwav_read_pcm_frames_s32(pWav, pcmFrameCount, pPCMFrames_drwav);
/* The total number of frames we decoded need to match. */
if (pcmFrameCount_libsndfile != pcmFrameCount_drwav) {
printf(" Decoded frame counts differ: pcmFrameCount=%d, libsndfile=%d, dr_wav=%d", (int)pcmFrameCount, (int)pcmFrameCount_libsndfile, (int)pcmFrameCount_drwav);
return DRWAV_ERROR;
}
/* Each of the decoded PCM frames need to match. */
DRWAV_ASSERT(pcmFrameCount_libsndfile == pcmFrameCount_drwav);
for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libsndfile; iPCMFrame += 1) {
drwav_int32* pPCMFrame_libsndfile = pPCMFrames_libsndfile + (iPCMFrame * pWav->channels);
drwav_int32* pPCMFrame_drwav = pPCMFrames_drwav + (iPCMFrame * pWav->channels);
drwav_uint32 iChannel;
drwav_bool32 hasError = DRWAV_FALSE;
for (iChannel = 0; iChannel < pWav->channels; iChannel += 1) {
if (pPCMFrame_libsndfile[iChannel] != pPCMFrame_drwav[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
hasError = DRWAV_TRUE;
break;
}
}
if (hasError) {
return DRWAV_ERROR; /* Decoded frames do not match. */
}
}
/* Done. */
return DRWAV_SUCCESS;
}
drwav_result decode_test__read_and_compare_pcm_frame_chunks_s32(libsndfile* pSndFile, drwav* pWav, drwav_uint64 pcmFrameChunkSize)
{
drwav_result result = DRWAV_SUCCESS;
drwav_uint64 iPCMFrame;
drwav_int32* pPCMFrames_libsndfile;
drwav_int32* pPCMFrames_drwav;
/* Make sure the decoder's are seeked back to the start first. */
drwav_seek_to_pcm_frame(pWav, 0);
libsndfile_seek_to_pcm_frame(pSndFile, 0);
pPCMFrames_libsndfile = (drwav_int32*)malloc((size_t)(pcmFrameChunkSize * pWav->channels * sizeof(drwav_int32)));
if (pPCMFrames_libsndfile == NULL) {
printf(" [libsndfile] Out of memory");
return DRWAV_ERROR;
}
pPCMFrames_drwav = (drwav_int32*)malloc((size_t)(pcmFrameChunkSize * pWav->channels * sizeof(drwav_int32)));
if (pPCMFrames_drwav == NULL) {
free(pPCMFrames_libsndfile);
printf(" [dr_wav] Out of memory");
return DRWAV_ERROR;
}
for (iPCMFrame = 0; iPCMFrame < pWav->totalPCMFrameCount; iPCMFrame += pcmFrameChunkSize) {
result = decode_test__read_and_compare_pcm_frames_s32(pSndFile, pWav, pcmFrameChunkSize, pPCMFrames_libsndfile, pPCMFrames_drwav);
if (result != DRWAV_SUCCESS) {
break;
}
}
free(pPCMFrames_libsndfile);
free(pPCMFrames_drwav);
return result;
}
drwav_result decode_test__read_and_compare_pcm_frames_f32(libsndfile* pSndFile, drwav* pWav, drwav_uint64 pcmFrameCount, float* pPCMFrames_libsndfile, float* pPCMFrames_drwav)
{
drwav_uint64 pcmFrameCount_libsndfile;
drwav_uint64 pcmFrameCount_drwav;
drwav_uint64 iPCMFrame;
/* To test decoding we just read a number of PCM frames from each decoder and compare. */
pcmFrameCount_libsndfile = libsndfile_read_pcm_frames_f32(pSndFile, pcmFrameCount, pPCMFrames_libsndfile);
pcmFrameCount_drwav = drwav_read_pcm_frames_f32(pWav, pcmFrameCount, pPCMFrames_drwav);
/* The total number of frames we decoded need to match. */
if (pcmFrameCount_libsndfile != pcmFrameCount_drwav) {
printf(" Decoded frame counts differ: pcmFrameCount=%d, libsndfile=%d, dr_wav=%d", (int)pcmFrameCount, (int)pcmFrameCount_libsndfile, (int)pcmFrameCount_drwav);
return DRWAV_ERROR;
}
/* Each of the decoded PCM frames need to match. */
DRWAV_ASSERT(pcmFrameCount_libsndfile == pcmFrameCount_drwav);
for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libsndfile; iPCMFrame += 1) {
float* pPCMFrame_libsndfile = pPCMFrames_libsndfile + (iPCMFrame * pWav->channels);
float* pPCMFrame_drwav = pPCMFrames_drwav + (iPCMFrame * pWav->channels);
drwav_uint32 iChannel;
drwav_bool32 hasError = DRWAV_FALSE;
for (iChannel = 0; iChannel < pWav->channels; iChannel += 1) {
if (pPCMFrame_libsndfile[iChannel] != pPCMFrame_drwav[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
hasError = DRWAV_TRUE;
break;
}
}
if (hasError) {
return DRWAV_ERROR; /* Decoded frames do not match. */
}
}
/* Done. */
return DRWAV_SUCCESS;
}
drwav_result decode_test__read_and_compare_pcm_frame_chunks_f32(libsndfile* pSndFile, drwav* pWav, drwav_uint64 pcmFrameChunkSize)
{
drwav_result result = DRWAV_SUCCESS;
drwav_uint64 iPCMFrame;
float* pPCMFrames_libsndfile;
float* pPCMFrames_drwav;
/* Make sure the decoder's are seeked back to the start first. */
drwav_seek_to_pcm_frame(pWav, 0);
libsndfile_seek_to_pcm_frame(pSndFile, 0);
pPCMFrames_libsndfile = (float*)malloc((size_t)(pcmFrameChunkSize * pWav->channels * sizeof(float)));
if (pPCMFrames_libsndfile == NULL) {
printf(" [libsndfile] Out of memory");
return DRWAV_ERROR;
}
pPCMFrames_drwav = (float*)malloc((size_t)(pcmFrameChunkSize * pWav->channels * sizeof(float)));
if (pPCMFrames_drwav == NULL) {
free(pPCMFrames_libsndfile);
printf(" [dr_wav] Out of memory");
return DRWAV_ERROR;
}
for (iPCMFrame = 0; iPCMFrame < pWav->totalPCMFrameCount; iPCMFrame += pcmFrameChunkSize) {
result = decode_test__read_and_compare_pcm_frames_f32(pSndFile, pWav, pcmFrameChunkSize, pPCMFrames_libsndfile, pPCMFrames_drwav);
if (result != DRWAV_SUCCESS) {
break;
}
}
free(pPCMFrames_libsndfile);
free(pPCMFrames_drwav);
return result;
}
drwav_result decode_test__read_and_compare_pcm_frames_s16(libsndfile* pSndFile, drwav* pWav, drwav_uint64 pcmFrameCount, drwav_int16* pPCMFrames_libsndfile, drwav_int16* pPCMFrames_drwav)
{
drwav_uint64 pcmFrameCount_libsndfile;
drwav_uint64 pcmFrameCount_drwav;
drwav_uint64 iPCMFrame;
/* To test decoding we just read a number of PCM frames from each decoder and compare. */
pcmFrameCount_libsndfile = libsndfile_read_pcm_frames_s16(pSndFile, pcmFrameCount, pPCMFrames_libsndfile);
pcmFrameCount_drwav = drwav_read_pcm_frames_s16(pWav, pcmFrameCount, pPCMFrames_drwav);
/* The total number of frames we decoded need to match. */
if (pcmFrameCount_libsndfile != pcmFrameCount_drwav) {
printf(" Decoded frame counts differ: pcmFrameCount=%d, libsndfile=%d, dr_wav=%d", (int)pcmFrameCount, (int)pcmFrameCount_libsndfile, (int)pcmFrameCount_drwav);
return DRWAV_ERROR;
}
/* Each of the decoded PCM frames need to match. */
DRWAV_ASSERT(pcmFrameCount_libsndfile == pcmFrameCount_drwav);
for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libsndfile; iPCMFrame += 1) {
drwav_int16* pPCMFrame_libsndfile = pPCMFrames_libsndfile + (iPCMFrame * pWav->channels);
drwav_int16* pPCMFrame_drwav = pPCMFrames_drwav + (iPCMFrame * pWav->channels);
drwav_uint32 iChannel;
drwav_bool32 hasError = DRWAV_FALSE;
for (iChannel = 0; iChannel < pWav->channels; iChannel += 1) {
if (pPCMFrame_libsndfile[iChannel] != pPCMFrame_drwav[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
hasError = DRWAV_TRUE;
break;
}
}
if (hasError) {
return DRWAV_ERROR; /* Decoded frames do not match. */
}
}
/* Done. */
return DRWAV_SUCCESS;
}
drwav_result decode_test__read_and_compare_pcm_frame_chunks_s16(libsndfile* pSndFile, drwav* pWav, drwav_uint64 pcmFrameChunkSize)
{
drwav_result result = DRWAV_SUCCESS;
drwav_uint64 iPCMFrame;
drwav_int16* pPCMFrames_libsndfile;
drwav_int16* pPCMFrames_drwav;
/* Make sure the decoder's are seeked back to the start first. */
drwav_seek_to_pcm_frame(pWav, 0);
libsndfile_seek_to_pcm_frame(pSndFile, 0);
pPCMFrames_libsndfile = (drwav_int16*)malloc((size_t)(pcmFrameChunkSize * pWav->channels * sizeof(drwav_int16)));
if (pPCMFrames_libsndfile == NULL) {
printf(" [libsndfile] Out of memory");
return DRWAV_ERROR;
}
pPCMFrames_drwav = (drwav_int16*)malloc((size_t)(pcmFrameChunkSize * pWav->channels * sizeof(drwav_int16)));
if (pPCMFrames_drwav == NULL) {
free(pPCMFrames_libsndfile);
printf(" [dr_wav] Out of memory");
return DRWAV_ERROR;
}
for (iPCMFrame = 0; iPCMFrame < pWav->totalPCMFrameCount; iPCMFrame += pcmFrameChunkSize) {
result = decode_test__read_and_compare_pcm_frames_s16(pSndFile, pWav, pcmFrameChunkSize, pPCMFrames_libsndfile, pPCMFrames_drwav);
if (result != DRWAV_SUCCESS) {
break;
}
}
free(pPCMFrames_libsndfile);
free(pPCMFrames_drwav);
return result;
}
drwav_result decode_test_file_s32(libsndfile* pSndFile, drwav* pWav)
{
drwav_result result = DRWAV_SUCCESS;
/* Start with reading the entire file in one go. */
if (result == DRWAV_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s32(pSndFile, pWav, pWav->totalPCMFrameCount);
}
/* Now try with reading one PCM frame at a time.*/
if (result == DRWAV_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s32(pSndFile, pWav, 1);
}
return result;
}
drwav_result decode_test_file_f32(libsndfile* pSndFile, drwav* pWav)
{
drwav_result result = DRWAV_SUCCESS;
/* Start with reading the entire file in one go. */
if (result == DRWAV_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_f32(pSndFile, pWav, pWav->totalPCMFrameCount);
}
/* Now try with reading one PCM frame at a time.*/
if (result == DRWAV_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_f32(pSndFile, pWav, 1);
}
return result;
}
drwav_result decode_test_file_s16(libsndfile* pSndFile, drwav* pWav)
{
drwav_result result = DRWAV_SUCCESS;
/* Start with reading the entire file in one go. */
if (result == DRWAV_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s16(pSndFile, pWav, pWav->totalPCMFrameCount);
}
/* Now try with reading one PCM frame at a time.*/
if (result == DRWAV_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s16(pSndFile, pWav, 1);
}
return result;
}
drwav_result decode_test_file(const char* pFilePath)
{
/* To test seeking we just seek to our target PCM frame and then decode whatever is remaining and compare it against libsndfile. */
drwav_result result;
libsndfile libsndfile;
drwav wav;
dr_printf_fixed_with_margin(FILE_NAME_WIDTH, TABLE_MARGIN, "%s", dr_path_file_name(pFilePath));
/* First load the decoder from libsndfile. */
result = libsndfile_init_file(pFilePath, &libsndfile);
if (result != DRWAV_SUCCESS) {
printf(" Failed to open via libsndfile.");
return result;
}
/* Now load from dr_wav. */
if (!drwav_init_file_with_metadata(&wav, pFilePath, 0, NULL)) {
printf(" Failed to open via dr_wav.");
libsndfile_uninit(&libsndfile);
return DRWAV_ERROR; /* Failed to load dr_wav decoder. */
}
/* At this point we should have both libsndfile and dr_wav decoders open. We can now perform identical operations on each of them and compare. */
result = decode_test_file_s32(&libsndfile, &wav);
if (result != DRWAV_SUCCESS) {
drwav_uninit(&wav);
libsndfile_uninit(&libsndfile);
return result;
}
result = decode_test_file_f32(&libsndfile, &wav);
if (result != DRWAV_SUCCESS) {
drwav_uninit(&wav);
libsndfile_uninit(&libsndfile);
return result;
}
result = decode_test_file_s16(&libsndfile, &wav);
if (result != DRWAV_SUCCESS) {
drwav_uninit(&wav);
libsndfile_uninit(&libsndfile);
return result;
}
/* We're done with our decoders. */
drwav_uninit(&wav);
libsndfile_uninit(&libsndfile);
if (result == DRWAV_SUCCESS) {
printf(" Passed");
}
return result;
}
drwav_result decode_test_directory(const char* pDirectoryPath)
{
dr_file_iterator iteratorState;
dr_file_iterator* pFile;
dr_printf_fixed(FILE_NAME_WIDTH, "%s", pDirectoryPath);
dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "RESULT");
printf("\n");
pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
while (pFile != NULL) {
drwav_result result;
/* Skip directories for now, but we may want to look at doing recursive file iteration. */
if (!pFile->isDirectory) {
result = decode_test_file(pFile->absolutePath);
(void)result;
printf("\n");
}
pFile = dr_file_iterator_next(pFile);
}
return DRWAV_SUCCESS;
}
drwav_result decode_test()
{
drwav_result result = DRWAV_SUCCESS;
/* Directories. */
{
result = decode_test_directory(DEFAULT_SOURCE_DIR);
(void)result;
}
return result;
}
drwav_result decode_profiling()
{
return DRWAV_SUCCESS;
}
int main(int argc, char** argv)
{
drwav_result result = DRWAV_SUCCESS;
drwav_bool32 doTesting = DRWAV_TRUE;
drwav_bool32 doProfiling = DRWAV_TRUE;
if (dr_argv_is_set(argc, argv, "--onlyprofile")) {
doTesting = DRWAV_FALSE;
}
if (libsndfile_init_api() != DRWAV_SUCCESS) {
printf("Failed to initialize libsndfile API.");
return -1;
}
if (doTesting) {
printf("=======================================================================\n");
printf("DECODE TESTING\n");
printf("=======================================================================\n");
result = decode_test();
if (result != DRWAV_SUCCESS) {
goto done; /* Don't continue if an error occurs during testing. */
}
printf("\n");
} else {
printf("=======================================================================\n");
printf("WARNING: Correctness Tests Disabled\n");
printf("=======================================================================\n");
}
/* Profiling. */
if (doProfiling) {
printf("=======================================================================\n");
printf("DECODE PROFILING (LOWER IS BETTER)\n");
printf("=======================================================================\n");
result = decode_profiling();
printf("\n");
}
done:
libsndfile_uninit_api();
return (int)result;
}

View file

@ -1 +0,0 @@
#include "dr_wav_decoding.c"

View file

@ -1,68 +0,0 @@
#define DR_WAV_IMPLEMENTATION
#include "../../dr_wav.h"
#include <math.h>
void generate_sine_wave(float* pOutput, drwav_uint64 frameCount, drwav_uint32 channels, drwav_uint32 sampleRate, float frequency, float* t)
{
float x = *t;
float a = 1.0f / sampleRate;
drwav_uint64 iFrame;
drwav_uint32 iChannel;
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
float s = (float)(sin(3.1415965*2 * x * frequency) * 0.25);
for (iChannel = 0; iChannel < channels; iChannel += 1) {
pOutput[iFrame*channels + iChannel] = s;
}
x += a;
}
*t = x;
}
int main(int argc, char** argv)
{
drwav_data_format format;
drwav wav;
float t = 0;
float tempFrames[4096];
drwav_uint64 totalFramesToWrite;
drwav_uint64 totalFramesWritten = 0;
if (argc < 2) {
printf("No output file specified.\n");
return -1;
}
format.container = drwav_container_riff;
format.format = DR_WAVE_FORMAT_IEEE_FLOAT;
format.channels = 2;
format.sampleRate = 44100;
format.bitsPerSample = 32;
if (!drwav_init_file_write(&wav, argv[1], &format, NULL)) {
printf("Failed to open file.\n");
return -1;
}
totalFramesToWrite = format.sampleRate * 1;
totalFramesWritten = 0;
while (totalFramesToWrite > totalFramesWritten) {
drwav_uint64 framesRemaining = totalFramesToWrite - totalFramesWritten;
drwav_uint64 framesToWriteNow = drwav_countof(tempFrames) / format.channels;
if (framesToWriteNow > framesRemaining) {
framesToWriteNow = framesRemaining;
}
generate_sine_wave(tempFrames, framesToWriteNow, format.channels, format.sampleRate, 400, &t);
drwav_write_pcm_frames(&wav, framesToWriteNow, tempFrames);
totalFramesWritten += framesToWriteNow;
}
drwav_uninit(&wav);
return 0;
}

View file

@ -1,70 +0,0 @@
#define MA_NO_DECODING
#define MA_NO_ENCODING
#define MA_IMPLEMENTATION
#include "../../../miniaudio/miniaudio.h"
#define DR_WAV_IMPLEMENTATION
#include "../../dr_wav.h"
#include "../common/dr_common.c"
drwav g_wav;
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
/* Assuming format is always s16 for now. */
if (pDevice->playback.format == ma_format_s16) {
drwav_read_pcm_frames_s16(&g_wav, frameCount, (drwav_int16*)pOutput);
} else if (pDevice->playback.format == ma_format_f32) {
drwav_read_pcm_frames_f32(&g_wav, frameCount, (float*)pOutput);
} else {
/* Unsupported format. */
}
(void)pInput;
}
int main(int argc, char** argv)
{
ma_result result;
ma_device device;
ma_device_config deviceConfig;
if (argc < 2) {
printf("No input file specified.");
return -1;
}
if (!drwav_init_file_with_metadata(&g_wav, argv[1], 0, NULL)) {
printf("Failed to load file: %s", argv[1]);
return -1;
}
deviceConfig = ma_device_config_init(ma_device_type_playback);
deviceConfig.playback.format = ma_format_s16;
deviceConfig.playback.channels = g_wav.channels;
deviceConfig.sampleRate = g_wav.sampleRate;
deviceConfig.dataCallback = data_callback;
result = ma_device_init(NULL, &deviceConfig, &device);
if (result != MA_SUCCESS) {
printf("Failed to initialize playback device.");
drwav_uninit(&g_wav);
return -1;
}
result = ma_device_start(&device);
if (result != MA_SUCCESS) {
printf("Failed to start playback device.");
ma_device_uninit(&device);
drwav_uninit(&g_wav);
return -1;
}
printf("Press Enter to quit...");
getchar();
ma_device_uninit(&device);
drwav_uninit(&g_wav);
return 0;
}

View file

@ -1 +0,0 @@
#include "dr_wav_playback.c"

View file

@ -1,11 +0,0 @@
#define DR_WAV_IMPLEMENTATION
#include "../../dr_wav.h"
#include "../common/dr_common.c"
int main(int argc, char** argv)
{
(void)argc;
(void)argv;
return 0;
}

View file

@ -1 +0,0 @@
#include "dr_wav_test_0.c"

File diff suppressed because it is too large Load diff

View file

@ -1,318 +0,0 @@
This is a log of my development of dr_opus - a single file public domain Opus decoder in C. The purpose of this log is
to track the development of the project and log how I solved various problems.
References:
RFC 6716 - https://tools.ietf.org/html/rfc6716 - Definition of the Opus Audio Codec
RFC 7845 - https://tools.ietf.org/html/rfc7845 - Ogg Encapsulation for the Opus Audio Codec
The main reference for development is RFC 6716. An important detail with this specification is in Section 1
The primary normative part of this specification is provided by the
source code in Appendix A. Only the decoder portion of this software
is normative, though a significant amount of code is shared by both
the encoder and decoder. Section 6 provides a decoder conformance
test. The decoder contains a great deal of integer and fixed-point
arithmetic that needs to be performed exactly, including all rounding
considerations, so any useful specification requires domain-specific
symbolic language to adequately define these operations.
Additionally, any conflict between the symbolic representation and
the included reference implementation must be resolved. For the
practical reasons of compatibility and testability, it would be
advantageous to give the reference implementation priority in any
disagreement. The C language is also one of the most widely
understood, human-readable symbolic representations for machine
behavior. For these reasons, this RFC uses the reference
implementation as the sole symbolic representation of the codec.
What this is basically saying is that the source code should be considered the main point of reference for the format of
the Opus bit stream. We cannot, however, just willy nilly take code from the reference implementation because that would
cause licensing problems. However, I'm going to make a prediction before I even begin: I think RFC 6716 with a bit
intuition, problem solving and help from others it can be figured out. Indeed, I think RFC 6716 may even be enough, and
that perhaps the authors of said specification refer to the source code as a self-defence mechanism to guard themselves
from possible subtle errors in the specification. If I'm wrong, the log below will track it.
Lets begin...
2018/11/30 6:55 PM
==================
- Inserted skeleton code with licensing information. Dual licensed as Public Domain _or_ MIT, whichever you prefer. API
is undecided as of yet.
- Decided on supporting C89. Not yet decided on how the standard library will be used.
- Deciding for now to use the init() style of API from dr_wav and dr_mp3 which takes a pre-allocated decoder object. May
need to change this to dr_flac style open() (or perhaps new()/delete()) if the size of the structure is too big.
- dr_wav, etc. use booleans for results status so I'm copying that for consistency.
- Added boilerplate for standard sized types (copied from mini_al).
- Added a comment at the top of the file to show that this isn't complete (even though it's in the "wip" folder!).
- Added boilerplate for read and seek callbacks (copied from dr_flac).
- Added shell APIs:
- dropus_init()
- Not requiring onSeek for the moment because I'm hoping that we can make that optional somehow, with restriction.
- dropus_uninit()
- Does nothing at the moment, but in future will need to close a file when the decoder is opened against a file.
- Added some common overrideable macros (taken from dr_flac):
- DROPUS_ASSERT
- DROPUS_COPY_MEMORY
- DROPUS_ZERO_MEMORY
- DROPUS_ZERO_OBJECT
- Decided to use the C standard library for now. This simplifies things like memset() and assert(). Currently using the
following headers:
- stddef.h
- stdint.h
- stdlib.h
- string.h
- assert.h
- Added some infrastructure for future APIs: dropus_init_file() and dropus_init_memory()
- In dr_flac, dr_wav and dr_mp3, a mistake was made with how the internal file handle is managed by the decoder. In
these libraries the pUserData member of the decoder structure is recycled to hold the FILE pointer. dr_opus is
changing this to be it's own member of the main decoder structure.
- The internal members for use with dropus_init_memory() use the same system as dr_mp3. See drmp3.memory.
- Added dropus_init_file()
- All init() APIs call dropus_init_internal() which is where the main initialization work is done.
- This is using the stdio FILE API.
- dropus_uninit() has been updated to close the file.
- Added dropus_fopen() for compiler compatibility. Taken from dr_flac, but added the "mode" parameter for consistency
with fopen().
- Added dropus_fclose() for namespace consistency with dropus_fopen().
- Added dropus_init_memory()
- onRead and onSeek callbacks taken from dr_mp3.
- INITIAL COMMIT
2018/12/01 12:46 PM
===================
- Downloading all official test vectors in preparation for future testing.
2018/12/01 03:58 PM
===================
- Decided to figure out the different encapsulations for Opus so I can figure out a design for data flow.
- Ogg: https://tools.ietf.org/html/rfc7845
- Matroska: https://wiki.xiph.org/MatroskaOpus
- WebM: https://www.webmproject.org/docs/container/
- MPEG-TS: https://wiki.xiph.org/OpusTS
- Mysterious encapsulation used by the official test vectors. Looking at this it's hard to tell what they are using for
this, but I'm assuming it's not documented. Will need to figure that one out for myself later.
- Since WebM is a subset of Matroska, can we combine those together to avoid code duplication?
- Upon reading into Ogg and MPEG-TS encapsulation (not looked into Matroska in detail yet) it looks like handling large
channel counts is going to be a pain. It can support up to 255 channels, which are made up of multiple streams. My
instict is telling me that we won't be able to use a fixed sized data structure and instead might require dynamic
allocations. Darn...
- Leaning towards a two-tier architecture - a low-level decoder for raw Opus streams, and a high-level API for encapsulated
streams. Opus packets support only 1 or 2 channels which means the size of the structure _can_ be fixed.
- Currently I'm thinking something like the following:
- struct dropus_stream - low-level Opus stream decoding
- struct dropus - high-level encapsulated Opus stream decoding
- struct dropus_ogg (Ogg Opus)
- struct dropus_mk (Matroska)
- struct dropus_ts (MPEG-TS)
- struct dropus_tv (That annoying non-standard and undocumented encapsulation used by the test vectors)
- The maximum number of samples per frame (per-channel) is 960 because:
- The maximum frame size for wideband (16kHz) is 60: 16*60 = 960
- The maximum frame size for fullband (48kHz) is 20: 48*20 = 960
- To account for stereo frames we would need to multiply 960 by 2: 960*2 = 1920
- This is small enough that it can be stored statically inside the dropus_stream structure.
- It looks like low level Opus streams won't be able to do efficient seeking without an encapsulation.
- An Opus stream is made up of packets. Each packet has 1 or more frames. Each frame can be made up of 1 or 2 channels.
- The size of the packet in bytes is unknown at the Opus stream level - it depends on the encapsulation to know the
size of the packet. This could be a bit of a problem because we may need to know the size of a frame in order to know
how much data we need from the client. I'm wondering if the low-level API should look something like the following:
- dropus_result dropus_stream_decode_packet(dropus_stream* pOpusStream, const void* pCompressedData, size_t compressedDataSize);
- This will require the caller to have information on the size of the packet.
- Added struct dropus_stream_packet, dropus_stream_frame.
- Each packet contains a byte that defines the structure of the packet. It's called the TOC byte
(Section 3.1. The TOC Byte) in the RFC 6716.
- Added skeleton implementations for dropus_stream_init() and dropus_stream_decode_packet().
- Added helpers for decoding the different parts of TOC byte.
- dropus_toc_config(), dropus_toc_s() and dropus_toc_c()
- Added enum for the three different modes as outlined in Section 3.1 of RFC 6716. Also added an API to extract this
mode from the config component of the TOC: dropus_toc_config_mode() and dropus_toc_mode().
- Added API to extract sample rate from the TOC: dropus_toc_config_sample_rate() and dropus_toc_sample_rate()
- Added API to extract the size of an Opus frame in PCM frames from the TOC:
- dropus_toc_config_frame_size_in_pcm_frames() and dropus_toc_frame_size_in_pcm_frames().
- COMMIT
2018/12/02 09:45 AM
===================
- Compiling for the first time. Targetting C89 so need to get it all working property right from the start.
- "test" folder added to my local repository. Not version controlled yet as I'm not sure I want to include all of
the test stuff in the repository. Will definitely not be version controlling the test vectors. Perhaps use a submodule?
- Have decided to not support the mysterious encapsulation format used by the test vectors in dr_opus itself. Instead
I'm just doing it manually in the test program.
- After attempting to build the C++ version, it appears Clang will attempt to #include stdint.h from Visual Studio's
standard library which fails. May need to come up with a way to avoid the dependency on stdint.h. Same thing also
happens with stdlib.h.
- Have cleaned up the sized types stuff for Clang to avoid the dependency on stdint, however errors are still happening
due to the use of stdlib.h. Leaving the Clang C++ issue for now. It's technically a Clang issue rather than dr_opus.
- COMMIT
- Have decided to version control test programs, but not test vectors. Instead I'm putting a note in a readme about
where to download and place the test vectors.
- In my experience I have found that having a testing infrastructure set up earlier saves a lot of time in the long run
so the next thing I have decided to work on is a test program. It makes sense to test against the official test vectors,
so the first thing is to figure out this undocumented encapsulation format...
Test Vector Encapsulation Format
--------------------------------
- All test vectors seem to start with two bytes of 0x00.
- This does not feel like a magic number or identifier.
- Noticed that first 4 bytes add up to a relatively small number. A counter of some kind in little endian format?
- A pattern can be seen in testvector02.bit:
- The bytes 00 00 00 XX can been seen repeating. This indicates some kind of blocking.
- From the first 4 bytes you see the hex value of 0x0000001E which equals 30 in decimal
- Counting forward 30 bytes brings us 4 bytes before the next set of 00 00 00 XX bytes. Definitely starting to feel like
the first 4 bytes are a byte counter.
- Skipping past those four bytes and looking at the next 00 00 00 XX bytes we get 0x0000001D which is 29 in decimal.
- Again, counting forward by that number of bytes brings us to 4 bytes prior to the next 00 00 00 XX block. First 4
bytes are definitely a byte counter.
- It makes sense that each of these blocks are an Opus packet.
- Still don't know what the second set of 4 bytes could represent... Does it matter for our purposes? All we care about
is the raw Opus bit stream.
- A Google search for "opus test vector format" brings up the following result (a PDF document):
- https://www.ietf.org/proceedings/82/slides/codec-4.pdf
- From the "Force Multipliers" page:
- "The range coder has 32 bits of state which must match between the encoder and decoder"
- "Provides a checksum of all encoding and decoding decisions"
- "opus_demo bitstreams include the range value with every packet and test for mismatches"
- Willing to bet that the extra 4 bytes are that "32 bits of state". This is actually good for dr_opus.
- I think the format is just a list of Opus packets with 8 bytes of header data for each one:
4 bytes: Opus packet size in bytes
4 bytes: 32 bit of range coder state
[Opus Packet]
--------------------------------
- The test program can either hard code a list of vector files or we can enumerate over files in known directories. I'm
preferring the latter to make it easier to add new vectors. It's also consistent with the way dr_flac does it which has
been pretty good in the past.
- Started work on a "common" lib for test programs. First thing is the new file iterator.
- File iterator now done, but only supporting Windows currently.
- Have implemented the logic for loading test vectors and the byte counter (and I'm assuming the range state) is actually
in big-endian, not little-endian. I'm an idiot!
- Copying endian management APIs from dr_flac over to dr_opus.
- Basic infrastructure for testing against the standard test vectors is now done.
- COMMIT
2018/12/03 6:15 PM
==================
- The packet enumeration in the test program seems to work so far, but I'm still not 100% sure that my assumptions are
correct. Adding more validation to dropus_stream_decode_packet() to increase my certainty. The next stage is to
implement all of the main packet validation.
- Question for later. How are "Discontinuous Transmission (DTX) or lost packet" handled? Should
dropus_stream_decode_packet() return an error in this case?
- Code for validating packet codes 0, 1 and 2 are now in, however untested so far. Code 3 is a bit more complicated and
right now I need to eat dinner...
- COMMIT
2018/12/03 6:40 PM
==================
- Code 3 CBR is done (no VBR yet). Still untested.
- Code 3 VBR is done. Still untested.
- Testing and debugging against the standard test vectors coming next.
- Fixed a few compiler warnings.
- COMMIT
2019/03/08 6:28 PM
==================
- Starting on range decoding. From figure 4 in RFC 6716 the diagram shows that both the SILK and CELT decoders draw
their data from the Range Decoder. Therefore the Range Decoder needs to be figured out before SILK and CELT decoding.
I have no prior knowledge on range coding, so hopefully RFC 6716 has enough information for a complete enough
implementation for purpose of Opus decoding.
- Section 4.1.1 of RGC 6716 is how to initialize the decoder. It references a variable called "rng", but doesn't
specificy it's possible ranges. Setting to a 32-bit unsigned integer for now.
- In this section, it says "or containing zero if there are no bytes in this Opus frame". I'm assuming we do this
by looping over each frame in order...
- Just noticed that the end of section 4.1.1 says "rng > 2**23". So "rng" needs to be 32-bits.
- And now I've just noticed that section 4.1 explicitly says the following: "Both val and rng are 32-bit unsigned integer
values." Problem solved.
- Having read over the range coding stuff in RFC 6716 I had some difficulty figuring out where symbols frequencies are
being stored. They're not stored, but rather specified in the specification in a per-context basis. In the spec there
are references to "PDFs" which is like this "{f[0], f[1]}/ft". Example: "{1, 1}/2". Simple, now that I've finally
figured it out...
- I can therefore not do the range decoder independant of some higher level context. I will start with the SILK decoder
since that's the next section...
2019/03/09 6:52 AM
==================
- I think I'm getting a feel for the requirements for the range decoding API. I've decided for now to use a structure
for this called dropus_range_decoder.
- dropus_range_decoder_init() implemented based on section 4.1.1.
- Added stub for dropus_stream_decode_frame(). I think the idea is that we iterate over each frame in the packet and
decode them in order? Not fully sure yet how frame transitions will work, so may need to rethink this.
- I'm still not sure if the range decoder is supposed to be initialized once per frame or once per packet...
- After more testing it turns out my test program wasn't reporting errors properly. *Sigh*. These bugs were originating
from the packet decoding section.
- COMMIT
- Section 4.1.2 mentions that there's two steps to decoding a symbol. The first step seems easy enough. Implemented in
dropus_range_decoder_fs().
- The second step requires updating "val" and "rng" of the range decoder based on (fl[k], fh[k] and ft). Implemented in
dropus_range_decoder_update(pRangeDecoder, fl, fh, ft).
- Normalization of the range decoder is easy (RFC 6716 - 4.1.2.1). Implemented in dropus_range_decoder_normalize().
- I think at this point we have the foundations done for the range decoder. Still more to do for CELT, but I think I
now have enough to hack away a bit more at the SILK-specific data.
- COMMIT
- I'm still not 100% sure how range decoding works - I know it depends on the context of the specific thing being
decoded, but not sure how the index "k" is handled. The first part of the SILK frame is the VAD flags PDF={1,1}/2.
Do I use the value of k (which I think is 0 or 1) to know whether or not the bit is set? I am going to assume this
for now.
2019/03/10 7:40 AM
==================
- At the moment I am using what feels like a very cumbersome way of decoding "k" from the range decoder. I'm calling
dropus_range_decoder_fs(), followed by a function called dropus_range_decoder_k() to retrieve "k", and then
dropus_range_decoder_update() at the end of it. I can simplify this into a single function, I think, which I'm
calling dropus_range_decode() which will perform the "fs" decode, perform the update, then return "k".
- As I'm writing dropus_range_decoder_decode() I've realized that I can simplify my dropus_range_decoder_update()
function to do both the "k" lookup and the the update from section 4.1.2. It just needs to be changed to take
the "context" probabilities and "fs" and to return "k".
- This _seems_ to work. Or at least, the results are consistent with my first attempt. Committing.
- Added dropus_Q13() for doing the stereo weight lookup (RFC 7616 - Table 7).
- Section 4.2.7.1 mentions that the weights are linearly interpolated with the weights from the previous frame. What
factor do I use for the interpolation?
- Finished initial work on RFC 7616 - Section 4.2.7.1. Still don't know how the interpolation part works. I'm hoping
this is explained later in the spec.
- COMMIT
- Completed initial implementation of RFC 7616 - Section 4.2.7.2.
2020/03/29 6:33 AM
==================
- After a whole year, time to take another look at this. Only problem is, I've forgotten everything! Let's do a
quick pass on the high level stuff to get it consistent with dr_flac. Two mistakes I made with dr_flac: Not having
a low-level API that works on the FLAC frame level; and returning booleans instead of result codes. The first one
is already being handled, but lets update the high level APIs to use result codes.
- High level APIs now updated to return result codes.
- People are going to want to log errors: added dropus_result_description() to retrieve a human readable description
of a result code for logging and error reporting purposes.
- I've had reports of people having compilation errors with inlining before. Need to bring over the INLINE macro from
dr_flac to ensure we don't get more reports about that.
- I've had people request the ability to customize whether or not public APIs are marked as static or extern in other
libraries, so adding the DROPUS_API decoration. It's better to do this sooner than later, because this happened with
miniaudio recently which was a ~40K line library at the time, and it was nightmare to update!
- There will be a requirement to allocate memory for high level APIs. Support for allocation macros and allocation
callbacks need to be added now so we can avoid breaking APIs later.
- There have been requests in the past to support loading files from a wchar_t encoded path. Adding support by pulling
in custom fopen() and wfopen() implementations from dr_flac.
- Have not yet compiled on VC6 so lets do a pass on that to make it easier for us later.
- VC6 is now compiling clean. Time to check GCC and Clang in strict C89 mode.
- Getting compiler errors about some Linux specific code for handling byte swaps. Removed and replaced with a cross-
platform solution.
- From what I can see it looks like our boilerplate is now up to date with dr_flac, dr_wav and dr_mp3. Time to get onto
some actual Opus decoding.
- Looks like a typo in dropus_range_decoder_update() for handling the `fl[k] > 0` case. Changed this, but now getting
a division by zero. Debugging...
- And fixed. The last paragraph in RFC 6716 - Section 4.1.2 is the part I was missing:
After the updates, implemented by ec_dec_update() (entdec.c), the
decoder normalizes the range using the procedure in the next section,
and returns the index k.
- Reading through dropus_stream_decode_packet() to refresh my memory and some stuff needs cleaning up. Important from
now on to distinguish between PCM frames and Opus frames. All variables referring to one of the other needs to be
prefixed with "pcm" or "opus", depending on it's use. Example: opusFrameCount / pcmFrameCount.
- Added infrastructure for handling CELT and Hybrid frames when the time comes. For now still focused on getting SILK
frames working.
- Reviewing SILK decoding logic to refresh my memory and looks like I've misinterpreted the encoding for per-frame
LBRR flags. When there's more than one SILK frame, the first LBRR flag is just used to determine whether or not
the per-frame LBRR flags is present. I was _always_ reading the LBRR flags regardless of the value of the primary
LBRR flag.
2020/03/30 7:30 AM
==================
- The decoding of SILK frames needs to be put in their own function in order to handle LBRR and regular SILK frames.
Looking at the spec, there's some complicated branching logic for determine what needs to be decoded. I've put in
a rough draft as a start, but needs some work. Will continue on this later.

View file

@ -1,166 +0,0 @@
name: ci
on:
pull_request:
paths:
- '**'
- '!.gitignore'
- '!LICENSE'
- '!TODO'
- '!doc/**'
- '!examples/**'
- '.github/workflows/ci.yml'
push:
branches:
- '*'
jobs:
linux:
name: Linux (Ubuntu)
runs-on: ubuntu-latest
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Build
run: |
make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y
- name: Stats
run: |
./qjs -qd
- name: Run built-in tests
run: |
make test
- name: Run microbench
run: |
make microbench
linux-asan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Build
run: |
make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y CONFIG_ASAN=y
- name: Run built-in tests
env:
ASAN_OPTIONS: halt_on_error=1
run: |
make CONFIG_ASAN=y test
linux-msan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Build
env:
CC: clang
run: |
make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y CONFIG_MSAN=y CONFIG_CLANG=y
- name: Run built-in tests
env:
MSAN_OPTIONS: halt_on_error=1
run: |
make CONFIG_MSAN=y CONFIG_CLANG=y test
linux-ubsan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Build
run: |
make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y CONFIG_UBSAN=y
- name: Run built-in tests
env:
UBSAN_OPTIONS: halt_on_error=1
run: |
make CONFIG_UBSAN=y test
macos:
name: macOS
runs-on: macos-latest
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: Build
run: |
make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y
- name: Stats
run: |
./qjs -qd
- name: Run built-in tests
run: |
make test
macos-asan:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: |
make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y CONFIG_ASAN=y
- name: Run built-in tests
env:
ASAN_OPTIONS: halt_on_error=1
run: |
make CONFIG_ASAN=y test
macos-ubsan:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: |
make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y CONFIG_UBSAN=y
- name: Run built-in tests
env:
UBSAN_OPTIONS: halt_on_error=1
run: |
make CONFIG_UBSAN=y test
freebsd:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build + test
uses: vmactions/freebsd-vm@v1
with:
usesh: true
prepare: |
pkg install -y gmake
run: |
gmake
./qjs -qd
gmake test
qemu-alpine:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
platform:
- i386
- arm32v6
- arm32v7
- arm64v8
- s390x
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Get qemu
run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
- name: Run tests on ${{ matrix.platform }}
run: docker run --rm --interactive --mount type=bind,source=$(pwd),target=/host ${{ matrix.platform }}/alpine sh -c "apk add git patch make gcc libc-dev && cd /host && make test"

View file

@ -1,72 +0,0 @@
/*
* QuickJS: Example of C module
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* 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.
*/
#include "../quickjs.h"
#define countof(x) (sizeof(x) / sizeof((x)[0]))
static int fib(int n)
{
if (n <= 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
static JSValue js_fib(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
int n, res;
if (JS_ToInt32(ctx, &n, argv[0]))
return JS_EXCEPTION;
res = fib(n);
return JS_NewInt32(ctx, res);
}
static const JSCFunctionListEntry js_fib_funcs[] = {
JS_CFUNC_DEF("fib", 1, js_fib ),
};
static int js_fib_init(JSContext *ctx, JSModuleDef *m)
{
return JS_SetModuleExportList(ctx, m, js_fib_funcs,
countof(js_fib_funcs));
}
#ifdef JS_SHARED_LIBRARY
#define JS_INIT_MODULE js_init_module
#else
#define JS_INIT_MODULE js_init_module_fib
#endif
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
{
JSModuleDef *m;
m = JS_NewCModule(ctx, module_name, js_fib_init);
if (!m)
return NULL;
JS_AddModuleExportList(ctx, m, js_fib_funcs, countof(js_fib_funcs));
return m;
}

View file

@ -1,10 +0,0 @@
/* fib module */
export function fib(n)
{
if (n <= 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
}

View file

@ -1 +0,0 @@
console.log("Hello World");

View file

@ -1,6 +0,0 @@
/* example of JS module */
import { fib } from "./fib_module.js";
console.log("Hello World");
console.log("fib(10)=", fib(10));

View file

@ -1,68 +0,0 @@
/*
* PI computation in Javascript using the QuickJS bigdecimal type
* (decimal floating point)
*/
"use strict";
/* compute PI with a precision of 'prec' digits */
function calc_pi(prec) {
const CHUD_A = 13591409m;
const CHUD_B = 545140134m;
const CHUD_C = 640320m;
const CHUD_C3 = 10939058860032000m; /* C^3/24 */
const CHUD_DIGITS_PER_TERM = 14.18164746272548; /* log10(C/12)*3 */
/* return [P, Q, G] */
function chud_bs(a, b, need_G) {
var c, P, Q, G, P1, Q1, G1, P2, Q2, G2, b1;
if (a == (b - 1n)) {
b1 = BigDecimal(b);
G = (2m * b1 - 1m) * (6m * b1 - 1m) * (6m * b1 - 5m);
P = G * (CHUD_B * b1 + CHUD_A);
if (b & 1n)
P = -P;
G = G;
Q = b1 * b1 * b1 * CHUD_C3;
} else {
c = (a + b) >> 1n;
[P1, Q1, G1] = chud_bs(a, c, true);
[P2, Q2, G2] = chud_bs(c, b, need_G);
P = P1 * Q2 + P2 * G1;
Q = Q1 * Q2;
if (need_G)
G = G1 * G2;
else
G = 0m;
}
return [P, Q, G];
}
var n, P, Q, G;
/* number of serie terms */
n = BigInt(Math.ceil(prec / CHUD_DIGITS_PER_TERM)) + 10n;
[P, Q, G] = chud_bs(0n, n, false);
Q = BigDecimal.div(Q, (P + Q * CHUD_A),
{ roundingMode: "half-even",
maximumSignificantDigits: prec });
G = (CHUD_C / 12m) * BigDecimal.sqrt(CHUD_C,
{ roundingMode: "half-even",
maximumSignificantDigits: prec });
return Q * G;
}
(function() {
var r, n_digits, n_bits;
if (typeof scriptArgs != "undefined") {
if (scriptArgs.length < 2) {
print("usage: pi n_digits");
return;
}
n_digits = scriptArgs[1] | 0;
} else {
n_digits = 1000;
}
/* we add more digits to reduce the probability of bad rounding for
the last digits */
r = calc_pi(n_digits + 20);
print(r.toFixed(n_digits, "down"));
})();

View file

@ -1,66 +0,0 @@
/*
* PI computation in Javascript using the QuickJS bigfloat type
* (binary floating point)
*/
"use strict";
/* compute PI with a precision of 'prec' bits */
function calc_pi() {
const CHUD_A = 13591409n;
const CHUD_B = 545140134n;
const CHUD_C = 640320n;
const CHUD_C3 = 10939058860032000n; /* C^3/24 */
const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */
/* return [P, Q, G] */
function chud_bs(a, b, need_G) {
var c, P, Q, G, P1, Q1, G1, P2, Q2, G2;
if (a == (b - 1n)) {
G = (2n * b - 1n) * (6n * b - 1n) * (6n * b - 5n);
P = BigFloat(G * (CHUD_B * b + CHUD_A));
if (b & 1n)
P = -P;
G = BigFloat(G);
Q = BigFloat(b * b * b * CHUD_C3);
} else {
c = (a + b) >> 1n;
[P1, Q1, G1] = chud_bs(a, c, true);
[P2, Q2, G2] = chud_bs(c, b, need_G);
P = P1 * Q2 + P2 * G1;
Q = Q1 * Q2;
if (need_G)
G = G1 * G2;
else
G = 0l;
}
return [P, Q, G];
}
var n, P, Q, G;
/* number of serie terms */
n = BigInt(Math.ceil(BigFloatEnv.prec / CHUD_BITS_PER_TERM)) + 10n;
[P, Q, G] = chud_bs(0n, n, false);
Q = Q / (P + Q * BigFloat(CHUD_A));
G = BigFloat((CHUD_C / 12n)) * BigFloat.sqrt(BigFloat(CHUD_C));
return Q * G;
}
(function() {
var r, n_digits, n_bits;
if (typeof scriptArgs != "undefined") {
if (scriptArgs.length < 2) {
print("usage: pi n_digits");
return;
}
n_digits = scriptArgs[1];
} else {
n_digits = 1000;
}
n_bits = Math.ceil(n_digits * Math.log2(10));
/* we add more bits to reduce the probability of bad rounding for
the last digits */
BigFloatEnv.setPrec( () => {
r = calc_pi();
print(r.toFixed(n_digits, BigFloatEnv.RNDZ));
}, n_bits + 32);
})();

View file

@ -1,118 +0,0 @@
/*
* PI computation in Javascript using the BigInt type
*/
"use strict";
/* return floor(log2(a)) for a > 0 and 0 for a = 0 */
function floor_log2(a)
{
var k_max, a1, k, i;
k_max = 0n;
while ((a >> (2n ** k_max)) != 0n) {
k_max++;
}
k = 0n;
a1 = a;
for(i = k_max - 1n; i >= 0n; i--) {
a1 = a >> (2n ** i);
if (a1 != 0n) {
a = a1;
k |= (1n << i);
}
}
return k;
}
/* return ceil(log2(a)) for a > 0 */
function ceil_log2(a)
{
return floor_log2(a - 1n) + 1n;
}
/* return floor(sqrt(a)) (not efficient but simple) */
function int_sqrt(a)
{
var l, u, s;
if (a == 0n)
return a;
l = ceil_log2(a);
u = 1n << ((l + 1n) / 2n);
/* u >= floor(sqrt(a)) */
for(;;) {
s = u;
u = ((a / s) + s) / 2n;
if (u >= s)
break;
}
return s;
}
/* return pi * 2**prec */
function calc_pi(prec) {
const CHUD_A = 13591409n;
const CHUD_B = 545140134n;
const CHUD_C = 640320n;
const CHUD_C3 = 10939058860032000n; /* C^3/24 */
const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */
/* return [P, Q, G] */
function chud_bs(a, b, need_G) {
var c, P, Q, G, P1, Q1, G1, P2, Q2, G2;
if (a == (b - 1n)) {
G = (2n * b - 1n) * (6n * b - 1n) * (6n * b - 5n);
P = G * (CHUD_B * b + CHUD_A);
if (b & 1n)
P = -P;
Q = b * b * b * CHUD_C3;
} else {
c = (a + b) >> 1n;
[P1, Q1, G1] = chud_bs(a, c, true);
[P2, Q2, G2] = chud_bs(c, b, need_G);
P = P1 * Q2 + P2 * G1;
Q = Q1 * Q2;
if (need_G)
G = G1 * G2;
else
G = 0n;
}
return [P, Q, G];
}
var n, P, Q, G;
/* number of serie terms */
n = BigInt(Math.ceil(Number(prec) / CHUD_BITS_PER_TERM)) + 10n;
[P, Q, G] = chud_bs(0n, n, false);
Q = (CHUD_C / 12n) * (Q << prec) / (P + Q * CHUD_A);
G = int_sqrt(CHUD_C << (2n * prec));
return (Q * G) >> prec;
}
function main(args) {
var r, n_digits, n_bits, out;
if (args.length < 1) {
print("usage: pi n_digits");
return;
}
n_digits = args[0] | 0;
/* we add more bits to reduce the probability of bad rounding for
the last digits */
n_bits = BigInt(Math.ceil(n_digits * Math.log2(10))) + 32n;
r = calc_pi(n_bits);
r = ((10n ** BigInt(n_digits)) * r) >> n_bits;
out = r.toString();
print(out[0] + "." + out.slice(1));
}
var args;
if (typeof scriptArgs != "undefined") {
args = scriptArgs;
args.shift();
} else if (typeof arguments != "undefined") {
args = arguments;
} else {
/* default: 1000 digits */
args=[1000];
}
main(args);

View file

@ -1,151 +0,0 @@
/*
* QuickJS: Example of C module with a class
*
* Copyright (c) 2019 Fabrice Bellard
*
* 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.
*/
#include "../quickjs.h"
#include <math.h>
#define countof(x) (sizeof(x) / sizeof((x)[0]))
/* Point Class */
typedef struct {
int x;
int y;
} JSPointData;
static JSClassID js_point_class_id;
static void js_point_finalizer(JSRuntime *rt, JSValue val)
{
JSPointData *s = JS_GetOpaque(val, js_point_class_id);
/* Note: 's' can be NULL in case JS_SetOpaque() was not called */
js_free_rt(rt, s);
}
static JSValue js_point_ctor(JSContext *ctx,
JSValueConst new_target,
int argc, JSValueConst *argv)
{
JSPointData *s;
JSValue obj = JS_UNDEFINED;
JSValue proto;
s = js_mallocz(ctx, sizeof(*s));
if (!s)
return JS_EXCEPTION;
if (JS_ToInt32(ctx, &s->x, argv[0]))
goto fail;
if (JS_ToInt32(ctx, &s->y, argv[1]))
goto fail;
/* using new_target to get the prototype is necessary when the
class is extended. */
proto = JS_GetPropertyStr(ctx, new_target, "prototype");
if (JS_IsException(proto))
goto fail;
obj = JS_NewObjectProtoClass(ctx, proto, js_point_class_id);
JS_FreeValue(ctx, proto);
if (JS_IsException(obj))
goto fail;
JS_SetOpaque(obj, s);
return obj;
fail:
js_free(ctx, s);
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
static JSValue js_point_get_xy(JSContext *ctx, JSValueConst this_val, int magic)
{
JSPointData *s = JS_GetOpaque2(ctx, this_val, js_point_class_id);
if (!s)
return JS_EXCEPTION;
if (magic == 0)
return JS_NewInt32(ctx, s->x);
else
return JS_NewInt32(ctx, s->y);
}
static JSValue js_point_set_xy(JSContext *ctx, JSValueConst this_val, JSValue val, int magic)
{
JSPointData *s = JS_GetOpaque2(ctx, this_val, js_point_class_id);
int v;
if (!s)
return JS_EXCEPTION;
if (JS_ToInt32(ctx, &v, val))
return JS_EXCEPTION;
if (magic == 0)
s->x = v;
else
s->y = v;
return JS_UNDEFINED;
}
static JSValue js_point_norm(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSPointData *s = JS_GetOpaque2(ctx, this_val, js_point_class_id);
if (!s)
return JS_EXCEPTION;
return JS_NewFloat64(ctx, sqrt((double)s->x * s->x + (double)s->y * s->y));
}
static JSClassDef js_point_class = {
"Point",
.finalizer = js_point_finalizer,
};
static const JSCFunctionListEntry js_point_proto_funcs[] = {
JS_CGETSET_MAGIC_DEF("x", js_point_get_xy, js_point_set_xy, 0),
JS_CGETSET_MAGIC_DEF("y", js_point_get_xy, js_point_set_xy, 1),
JS_CFUNC_DEF("norm", 0, js_point_norm),
};
static int js_point_init(JSContext *ctx, JSModuleDef *m)
{
JSValue point_proto, point_class;
/* create the Point class */
JS_NewClassID(&js_point_class_id);
JS_NewClass(JS_GetRuntime(ctx), js_point_class_id, &js_point_class);
point_proto = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, point_proto, js_point_proto_funcs, countof(js_point_proto_funcs));
point_class = JS_NewCFunction2(ctx, js_point_ctor, "Point", 2, JS_CFUNC_constructor, 0);
/* set proto.constructor and ctor.prototype */
JS_SetConstructor(ctx, point_class, point_proto);
JS_SetClassProto(ctx, js_point_class_id, point_proto);
JS_SetModuleExport(ctx, m, "Point", point_class);
return 0;
}
JSModuleDef *js_init_module(JSContext *ctx, const char *module_name)
{
JSModuleDef *m;
m = JS_NewCModule(ctx, module_name, js_point_init);
if (!m)
return NULL;
JS_AddModuleExport(ctx, m, "Point");
return m;
}

View file

@ -1,6 +0,0 @@
/* example of JS module importing a C module */
import { fib } from "./fib.so";
console.log("Hello World");
console.log("fib(10)=", fib(10));

View file

@ -1,40 +0,0 @@
/* example of JS module importing a C module */
import { Point } from "./point.so";
function assert(b, str)
{
if (b) {
return;
} else {
throw Error("assertion failed: " + str);
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y);
this.color = color;
}
get_color() {
return this.color;
}
};
function main()
{
var pt, pt2;
pt = new Point(2, 3);
assert(pt.x === 2);
assert(pt.y === 3);
pt.x = 4;
assert(pt.x === 4);
assert(pt.norm() == 5);
pt2 = new ColorPoint(2, 3, 0xffffff);
assert(pt2.x === 2);
assert(pt2.color === 0xffffff);
assert(pt2.get_color() === 0xffffff);
}
main();

View file

@ -1,8 +0,0 @@
*.json
*.nim
*.zig
__pycache__/
sokol-nim/
sokol-zig/
sokol-odin/
sokol-rust/

View file

@ -1,40 +0,0 @@
## Language Binding Generation Scripts
> REMINDER: we can pass `-fparse-all-comments` to the clang ast-dump command line which adds the following node types to the ast-dump.json: FullComment, ParagraphComment, TextComment. This might allow us to preserve comments in the language bindings (might be useful as part of a bigger change to make sokol header comments autodoc and Intellisense-friendly)
### Zig
First make sure that clang and python3 are in the path:
```
> clang --version
> python3 --version
```
...on Windows I simply install those with scoop:
```
> scoop install llvm
> scoop install python
```
To update the Zig bindings:
```
> cd sokol/bindgen
> git clone https://github.com/floooh/sokol-zig
> git clone https://github.com/floooh/sokol-nim
> git clone https://github.com/floooh/sokol-odin
> git clone https://github.com/floooh/sokol-rust
> python3 gen_all.py
```
Test and run samples:
```
> cd sokol/bindgen/sokol-zig
> zig build run-clear
> zig build run-triangle
> zig build run-cube
...
```

View file

@ -1,37 +0,0 @@
import os, gen_nim, gen_zig, gen_odin, gen_rust
tasks = [
[ '../sokol_log.h', 'slog_', [] ],
[ '../sokol_gfx.h', 'sg_', [] ],
[ '../sokol_app.h', 'sapp_', [] ],
[ '../sokol_glue.h', 'sglue_', ['sg_'] ],
[ '../sokol_time.h', 'stm_', [] ],
[ '../sokol_audio.h', 'saudio_', [] ],
[ '../util/sokol_gl.h', 'sgl_', ['sg_'] ],
[ '../util/sokol_debugtext.h', 'sdtx_', ['sg_'] ],
[ '../util/sokol_shape.h', 'sshape_', ['sg_'] ],
]
# Odin
gen_odin.prepare()
for task in tasks:
[c_header_path, main_prefix, dep_prefixes] = task
gen_odin.gen(c_header_path, main_prefix, dep_prefixes)
# Nim
gen_nim.prepare()
for task in tasks:
[c_header_path, main_prefix, dep_prefixes] = task
gen_nim.gen(c_header_path, main_prefix, dep_prefixes)
# Zig
gen_zig.prepare()
for task in tasks:
[c_header_path, main_prefix, dep_prefixes] = task
gen_zig.gen(c_header_path, main_prefix, dep_prefixes)
# Rust
gen_rust.prepare()
for task in tasks:
[c_header_path, main_prefix, dep_prefixes] = task
gen_rust.gen(c_header_path, main_prefix, dep_prefixes)

View file

@ -1,124 +0,0 @@
#-------------------------------------------------------------------------------
# Generate an intermediate representation of a clang AST dump.
#-------------------------------------------------------------------------------
import json, sys, subprocess
def is_api_decl(decl, prefix):
if 'name' in decl:
return decl['name'].startswith(prefix)
elif decl['kind'] == 'EnumDecl':
# an anonymous enum, check if the items start with the prefix
return decl['inner'][0]['name'].lower().startswith(prefix)
else:
return False
def is_dep_decl(decl, dep_prefixes):
for prefix in dep_prefixes:
if is_api_decl(decl, prefix):
return True
return False
def dep_prefix(decl, dep_prefixes):
for prefix in dep_prefixes:
if is_api_decl(decl, prefix):
return prefix
return None
def filter_types(str):
return str.replace('_Bool', 'bool')
def parse_struct(decl):
outp = {}
outp['kind'] = 'struct'
outp['name'] = decl['name']
outp['fields'] = []
for item_decl in decl['inner']:
if item_decl['kind'] != 'FieldDecl':
sys.exit(f"ERROR: Structs must only contain simple fields ({decl['name']})")
item = {}
if 'name' in item_decl:
item['name'] = item_decl['name']
item['type'] = filter_types(item_decl['type']['qualType'])
outp['fields'].append(item)
return outp
def parse_enum(decl):
outp = {}
if 'name' in decl:
outp['kind'] = 'enum'
outp['name'] = decl['name']
needs_value = False
else:
outp['kind'] = 'consts'
needs_value = True
outp['items'] = []
for item_decl in decl['inner']:
if item_decl['kind'] == 'EnumConstantDecl':
item = {}
item['name'] = item_decl['name']
if 'inner' in item_decl:
const_expr = item_decl['inner'][0]
if const_expr['kind'] != 'ConstantExpr':
sys.exit(f"ERROR: Enum values must be a ConstantExpr ({item_decl['name']}), is '{const_expr['kind']}'")
if const_expr['valueCategory'] != 'rvalue' and const_expr['valueCategory'] != 'prvalue':
sys.exit(f"ERROR: Enum value ConstantExpr must be 'rvalue' or 'prvalue' ({item_decl['name']}), is '{const_expr['valueCategory']}'")
if not ((len(const_expr['inner']) == 1) and (const_expr['inner'][0]['kind'] == 'IntegerLiteral')):
sys.exit(f"ERROR: Enum value ConstantExpr must have exactly one IntegerLiteral ({item_decl['name']})")
item['value'] = const_expr['inner'][0]['value']
if needs_value and 'value' not in item:
sys.exit(f"ERROR: anonymous enum items require an explicit value")
outp['items'].append(item)
return outp
def parse_func(decl):
outp = {}
outp['kind'] = 'func'
outp['name'] = decl['name']
outp['type'] = filter_types(decl['type']['qualType'])
outp['params'] = []
if 'inner' in decl:
for param in decl['inner']:
if param['kind'] != 'ParmVarDecl':
print(f" >> warning: ignoring func {decl['name']} (unsupported parameter type)")
return None
outp_param = {}
outp_param['name'] = param['name']
outp_param['type'] = filter_types(param['type']['qualType'])
outp['params'].append(outp_param)
return outp
def parse_decl(decl):
kind = decl['kind']
if kind == 'RecordDecl':
return parse_struct(decl)
elif kind == 'EnumDecl':
return parse_enum(decl)
elif kind == 'FunctionDecl':
return parse_func(decl)
else:
return None
def clang(csrc_path):
cmd = ['clang', '-Xclang', '-ast-dump=json', '-c' ]
cmd.append(csrc_path)
return subprocess.check_output(cmd)
def gen(header_path, source_path, module, main_prefix, dep_prefixes):
ast = clang(source_path)
inp = json.loads(ast)
outp = {}
outp['module'] = module
outp['prefix'] = main_prefix
outp['dep_prefixes'] = dep_prefixes
outp['decls'] = []
for decl in inp['inner']:
is_dep = is_dep_decl(decl, dep_prefixes)
if is_api_decl(decl, main_prefix) or is_dep:
outp_decl = parse_decl(decl)
if outp_decl is not None:
outp_decl['is_dep'] = is_dep
outp_decl['dep_prefix'] = dep_prefix(decl, dep_prefixes)
outp['decls'].append(outp_decl)
with open(f'{module}.json', 'w') as f:
f.write(json.dumps(outp, indent=2));
return outp

View file

@ -1,613 +0,0 @@
#-------------------------------------------------------------------------------
# Generate Nim bindings
#
# Nim coding style:
# - type identifiers are PascalCase, everything else is camelCase
# - reference: https://nim-lang.org/docs/nep1.html
#-------------------------------------------------------------------------------
import gen_ir
import gen_util as util
import os, shutil, sys
module_names = {
'slog_': 'log',
'sg_': 'gfx',
'sapp_': 'app',
'stm_': 'time',
'saudio_': 'audio',
'sgl_': 'gl',
'sdtx_': 'debugtext',
'sshape_': 'shape',
'sglue_': 'glue',
}
c_source_paths = {
'slog_': 'sokol-nim/src/sokol/c/sokol_log.c',
'sg_': 'sokol-nim/src/sokol/c/sokol_gfx.c',
'sapp_': 'sokol-nim/src/sokol/c/sokol_app.c',
'stm_': 'sokol-nim/src/sokol/c/sokol_time.c',
'saudio_': 'sokol-nim/src/sokol/c/sokol_audio.c',
'sgl_': 'sokol-nim/src/sokol/c/sokol_gl.c',
'sdtx_': 'sokol-nim/src/sokol/c/sokol_debugtext.c',
'sshape_': 'sokol-nim/src/sokol/c/sokol_shape.c',
'sglue_': 'sokol-nim/src/sokol/c/sokol_glue.c',
}
c_callbacks = [
'slog_func',
]
ignores = [
'sdtx_printf',
'sdtx_vprintf',
]
overrides = {
'sgl_error': 'sgl_get_error',
'sgl_deg': 'sgl_as_degrees',
'sgl_rad': 'sgl_as_radians',
'SGL_NO_ERROR': 'SGL_ERROR_NO_ERROR',
'SG_BUFFERTYPE_VERTEXBUFFER': 'SG_BUFFERTYPE_VERTEX_BUFFER',
'SG_BUFFERTYPE_INDEXBUFFER': 'SG_BUFFERTYPE_INDEX_BUFFER',
'SG_ACTION_DONTCARE': 'SG_ACTION_DONT_CARE',
'ptr': 'addr', # range ptr
'func': 'fn',
'slog_func': 'fn',
}
enumPrefixOverrides = {
# sokol_gfx.h
'LOADACTION': 'loadAction',
'STOREACTION': 'storeAction',
'PIXELFORMAT': 'pixelFormat',
'RESOURCESTATE': 'resourceState',
'BUFFERTYPE': 'bufferType',
'INDEXTYPE': 'indexType',
'IMAGETYPE': 'imageType',
'SAMPLERTYPE': 'samplerType',
'CUBEFACE': 'cubeFace',
'SHADERSTAGE': 'shaderStage',
'PRIMITIVETYPE': 'primitiveType',
'BORDERCOLOR': 'borderColor',
'VERTEXFORMAT': 'vertexFormat',
'VERTEXSTEP': 'vertexStep',
'UNIFORMTYPE': 'uniformType',
'UNIFORMLAYOUT': 'uniformLayout',
'CULLMODE': 'cullMode',
'FACEWINDING': 'faceWinding',
'COMPAREFUNC': 'compareFunc',
'STENCILOP': 'stencilOp',
'BLENDFACTOR': 'blendFactor',
'BLENDOP': 'blendOp',
'COLORMASK': 'colorMask',
# sokol_app.h
'EVENTTYPE': 'eventType',
'KEYCODE': 'keyCode',
'MOUSEBUTTON': 'mouseButton',
}
prim_types = {
'int': 'int32',
'bool': 'bool',
'char': 'char',
'int8_t': 'int8',
'uint8_t': 'uint8',
'int16_t': 'int16',
'uint16_t': 'uint16',
'int32_t': 'int32',
'uint32_t': 'uint32',
'int64_t': 'int64',
'uint64_t': 'uint64',
'float': 'float32',
'double': 'float64',
'uintptr_t': 'uint',
'intptr_t': 'int',
'size_t': 'int', # not a bug, Nim's sizeof() returns int
}
prim_defaults = {
'int': '0',
'bool': 'false',
'int8_t': '0',
'uint8_t': '0',
'int16_t': '0',
'uint16_t': '0',
'int32_t': '0',
'uint32_t': '0',
'int64_t': '0',
'uint64_t': '0',
'float': '0.0f',
'double': '0.0',
'uintptr_t': '0',
'intptr_t': '0',
'size_t': '0'
}
common_prim_types = """
array
untyped typed void
bool byte char
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64
float float32 float64
string
cchar cint csize_t
cfloat cdouble
cstring
pointer
""".split()
keywords = """
addr and as asm
bind block break
case cast concept const continue converter
defer discard distinct div do
elif else end enum except export
finally for from func
if import in include interface is isnot iterator
let
macro method mixin mod
nil not notin
object of or out
proc ptr
raise ref return
shl shr static
template try tuple type
using
var
when while
xor
yield
""".split() + common_prim_types
struct_types = []
enum_types = []
out_lines = ''
def reset_globals():
global struct_types
global enum_types
global out_lines
struct_types = []
enum_types = []
out_lines = ''
def l(s):
global out_lines
out_lines += s + '\n'
def as_nim_prim_type(s):
return prim_types[s]
# prefix_bla_blub(_t) => (dep.)BlaBlub
def as_nim_type_name(s, prefix):
parts = s.lower().split('_')
dep = parts[0] + '_'
outp = ''
if not s.startswith(prefix) and dep in module_names:
outp = module_names[dep] + '.'
for part in parts[1:]:
# ignore '_t' type postfix
if (part != 't'):
outp += part.capitalize()
return outp
def check_override(name, default=None):
if name in overrides:
return overrides[name]
elif default is None:
return name
else:
return default
def check_ignore(name):
return name in ignores
def is_power_of_two(val):
return val == 0 or val & (val - 1) == 0
def wrap_keywords(s):
if s in keywords:
return f'`{s}`'
else:
return s
# prefix_bla_blub => blaBlub
def as_camel_case(s, prefix, wrap=True):
outp = s.lower()
if outp.startswith(prefix):
outp = outp[len(prefix):]
parts = outp.lstrip('_').split('_')
outp = parts[0]
for part in parts[1:]:
outp += part.capitalize()
if wrap:
outp = wrap_keywords(outp)
return outp
# PREFIX_ENUM_BLA_BLO => blaBlo
def as_enum_item_name(s, wrap=True):
outp = s.lstrip('_')
parts = outp.split('_')[1:]
if parts[0] in enumPrefixOverrides:
parts[0] = enumPrefixOverrides[parts[0]]
else:
parts[0] = parts[0].lower()
outp = parts[0]
for part in parts[1:]:
outp += part.capitalize()
if wrap:
outp = wrap_keywords(outp)
return outp
def is_prim_type(s):
return s in prim_types
def is_struct_type(s):
return s in struct_types
def is_enum_type(s):
return s in enum_types
def is_const_prim_ptr(s):
for prim_type in prim_types:
if s == f"const {prim_type} *":
return True
return False
def is_prim_ptr(s):
for prim_type in prim_types:
if s == f"{prim_type} *":
return True
return False
def is_const_struct_ptr(s):
for struct_type in struct_types:
if s == f"const {struct_type} *":
return True
return False
def type_default_value(s):
return prim_defaults[s]
def funcptr_args(field_type, prefix):
tokens = field_type[field_type.index('(*)')+4:-1].split(',')
s = ""
n = 0
for token in tokens:
n += 1
arg_ctype = token.strip()
if s != "":
s += ", "
arg_nimtype = as_nim_type(arg_ctype, prefix)
if arg_nimtype == "":
return "" # fun(void)
s += f"a{n}:{arg_nimtype}"
if s == "a1:void":
s = ""
return s
def funcptr_result(field_type, prefix):
ctype = field_type[:field_type.index('(*)')].strip()
return as_nim_type(ctype, prefix)
def as_nim_type(ctype, prefix, struct_ptr_as_value=False):
if ctype == "void":
return ""
elif is_prim_type(ctype):
return as_nim_prim_type(ctype)
elif is_struct_type(ctype):
return as_nim_type_name(ctype, prefix)
elif is_enum_type(ctype):
return as_nim_type_name(ctype, prefix)
elif util.is_string_ptr(ctype):
return "cstring"
elif util.is_void_ptr(ctype) or util.is_const_void_ptr(ctype):
return "pointer"
elif is_const_struct_ptr(ctype):
nim_type = as_nim_type(util.extract_ptr_type(ctype), prefix)
if struct_ptr_as_value:
return f"{nim_type}"
else:
return f"ptr {nim_type}"
elif is_prim_ptr(ctype) or is_const_prim_ptr(ctype):
return f"ptr {as_nim_type(util.extract_ptr_type(ctype), prefix)}"
elif util.is_func_ptr(ctype):
args = funcptr_args(ctype, prefix)
res = funcptr_result(ctype, prefix)
if res != "":
res = ":" + res
return f"proc({args}){res} {{.cdecl.}}"
elif util.is_1d_array_type(ctype):
array_ctype = util.extract_array_type(ctype)
array_sizes = util.extract_array_sizes(ctype)
return f'array[{array_sizes[0]}, {as_nim_type(array_ctype, prefix)}]'
elif util.is_2d_array_type(ctype):
array_ctype = util.extract_array_type(ctype)
array_sizes = util.extract_array_sizes(ctype)
return f'array[{array_sizes[0]}, array[{array_sizes[1]}, {as_nim_type(array_ctype, prefix)}]]'
else:
sys.exit(f"ERROR as_nim_type: {ctype}")
def as_nim_struct_name(struct_decl, prefix):
struct_name = check_override(struct_decl['name'])
nim_type = f'{as_nim_type_name(struct_name, prefix)}'
return nim_type
def as_nim_field_name(field_decl, prefix, check_private=True):
field_name = as_camel_case(check_override(field_decl['name']), prefix)
if check_private:
is_private = field_decl['name'].startswith('_')
if not is_private:
field_name += "*"
return field_name
def as_nim_field_type(struct_decl, field_decl, prefix):
return as_nim_type(check_override(f"{struct_decl['name']}.{field_decl['name']}", default=field_decl['type']), prefix)
def gen_struct(decl, prefix):
l(f"type {as_nim_struct_name(decl, prefix)}* = object")
for field in decl['fields']:
l(f" {as_nim_field_name(field, prefix)}:{as_nim_field_type(decl, field, prefix)}")
l("")
def gen_consts(decl, prefix):
l("const")
for item in decl['items']:
item_name = check_override(item['name'])
l(f" {as_camel_case(item_name, prefix)}* = {item['value']}")
l("")
def gen_enum(decl, prefix):
item_names_by_value = {}
value = -1
has_explicit_values = False
for item in decl['items']:
item_name = check_override(item['name'])
if item_name.endswith("_NUM") or item_name.endswith("_FORCE_U32"):
continue
else:
if 'value' in item:
has_explicit_values = True
value = int(item['value'])
else:
value += 1
item_names_by_value[value] = as_enum_item_name(item_name)
enum_name_nim = as_nim_type_name(decl['name'], prefix)
l('type')
l(f" {enum_name_nim}* {{.size:sizeof(int32).}} = enum")
if has_explicit_values:
# Nim requires explicit enum values to be declared in ascending order
for value in sorted(item_names_by_value):
name = item_names_by_value[value]
l(f" {name} = {value},")
else:
for name in item_names_by_value.values():
l(f" {name},")
l("")
# returns C prototype compatible function args (with pointers)
def funcdecl_args_c(decl, prefix):
s = ""
func_name = decl['name']
for param_decl in decl['params']:
if s != "":
s += ", "
arg_name = param_decl['name']
arg_type = check_override(f'{func_name}.{arg_name}', default=param_decl['type'])
s += f"{as_camel_case(arg_name, prefix)}:{as_nim_type(arg_type, prefix)}"
return s
# returns Nim function args (pass structs by value)
def funcdecl_args_nim(decl, prefix):
s = ""
func_name = decl['name']
for param_decl in decl['params']:
if s != "":
s += ", "
arg_name = param_decl['name']
arg_type = check_override(f'{func_name}.{arg_name}', default=param_decl['type'])
s += f"{as_camel_case(arg_name, prefix)}:{as_nim_type(arg_type, prefix, struct_ptr_as_value=True)}"
return s
def funcdecl_result(decl, prefix):
func_name = decl['name']
decl_type = decl['type']
result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip())
nim_res_type = as_nim_type(result_type, prefix)
if nim_res_type == "":
nim_res_type = "void"
return nim_res_type
def gen_func_nim(decl, prefix):
c_func_name = decl['name']
nim_func_name = as_camel_case(check_override(c_func_name), prefix, wrap=False)
nim_res_type = funcdecl_result(decl, prefix)
if c_func_name in c_callbacks:
l(f"proc {nim_func_name}*({funcdecl_args_c(decl, prefix)}):{nim_res_type} {{.cdecl, importc:\"{c_func_name}\".}}")
else:
l(f"proc c_{nim_func_name}({funcdecl_args_c(decl, prefix)}):{nim_res_type} {{.cdecl, importc:\"{c_func_name}\".}}")
l(f"proc {wrap_keywords(nim_func_name)}*({funcdecl_args_nim(decl, prefix)}):{nim_res_type} =")
s = f" c_{nim_func_name}("
for i, param_decl in enumerate(decl['params']):
if i > 0:
s += ", "
arg_name = param_decl['name']
arg_type = param_decl['type']
if is_const_struct_ptr(arg_type):
s += f"addr({arg_name})"
else:
s += arg_name
s += ")"
l(s)
l("")
def gen_array_converters(decl, prefix):
for field in decl['fields']:
if util.is_array_type(field['type']):
array_type = util.extract_array_type(field['type'])
array_sizes = util.extract_array_sizes(field['type'])
struct_name = as_nim_struct_name(decl, prefix)
field_name = as_nim_field_name(field, prefix, check_private=False)
array_base_type = as_nim_type(array_type, prefix)
if util.is_1d_array_type(field['type']):
n = array_sizes[0]
l(f'converter to{struct_name}{field_name}*[N:static[int]](items: array[N, {array_base_type}]): array[{n}, {array_base_type}] =')
l(f' static: assert(N <= {n})')
l(f' for index,item in items.pairs: result[index]=item')
l('')
elif util.is_2d_array_type(field['type']):
x = array_sizes[1]
y = array_sizes[0]
l(f'converter to{struct_name}{field_name}*[Y:static[int], X:static[int]](items: array[Y, array[X, {array_base_type}]]): array[{y}, array[{x}, {array_base_type}]] =')
l(f' static: assert(X <= {x})')
l(f' static: assert(Y <= {y})')
l(f' for indexY,itemY in items.pairs:')
l(f' for indexX, itemX in itemY.pairs:')
l(f' result[indexY][indexX] = itemX')
l('')
else:
sys.exit('Unsupported converter array dimension (> 2)!')
def pre_parse(inp):
global struct_types
global enum_types
for decl in inp['decls']:
kind = decl['kind']
if kind == 'struct':
struct_types.append(decl['name'])
elif kind == 'enum':
enum_name = decl['name']
enum_types.append(enum_name)
def gen_imports(inp, dep_prefixes):
for dep_prefix in dep_prefixes:
dep_module_name = module_names[dep_prefix]
l(f'import {dep_module_name}')
l('')
def gen_extra(inp):
if inp['prefix'] in ['sg_']:
# FIXME: remove when sokol-shdc has been integrated!
l('when defined gl:')
l(' const gl* = true')
l(' const d3d11* = false')
l(' const metal* = false')
l('elif defined windows:')
l(' const gl* = false')
l(' const d3d11* = true')
l(' const metal* = false')
l('elif defined macosx:')
l(' const gl* = false')
l(' const d3d11* = false')
l(' const metal* = true')
l('elif defined linux:')
l(' const gl* = true')
l(' const d3d11* = false')
l(' const metal* = false')
l('else:')
l(' error("unsupported platform")')
l('')
if inp['prefix'] in ['sg_', 'sapp_']:
l('when defined windows:')
l(' when not defined vcc:')
l(' {.passl:"-lkernel32 -luser32 -lshell32 -lgdi32".}')
l(' when defined gl:')
l(' {.passc:"-DSOKOL_GLCORE33".}')
l(' else:')
l(' {.passc:"-DSOKOL_D3D11".}')
l(' when not defined vcc:')
l(' {.passl:"-ld3d11 -ldxgi".}')
l('elif defined macosx:')
l(' {.passc:"-x objective-c".}')
l(' {.passl:"-framework Cocoa -framework QuartzCore".}')
l(' when defined gl:')
l(' {.passc:"-DSOKOL_GLCORE33".}')
l(' {.passl:"-framework OpenGL".}')
l(' else:')
l(' {.passc:"-DSOKOL_METAL".}')
l(' {.passl:"-framework Metal -framework MetalKit".}')
l('elif defined linux:')
l(' {.passc:"-DSOKOL_GLCORE33".}')
l(' {.passl:"-lX11 -lXi -lXcursor -lGL -lm -ldl -lpthread".}')
l('else:')
l(' error("unsupported platform")')
l('')
if inp['prefix'] in ['saudio_']:
l('when defined windows:')
l(' when not defined vcc:')
l(' {.passl:"-lkernel32 -lole32".}')
l('elif defined macosx:')
l(' {.passl:"-framework AudioToolbox".}')
l('elif defined linux:')
l(' {.passl:"-lasound -lm -lpthread".}')
l('else:')
l(' error("unsupported platform")')
l('')
if inp['prefix'] in ['sg_']:
l('## Convert a 4-element tuple of numbers to a gfx.Color')
l('converter toColor*[R:SomeNumber,G:SomeNumber,B:SomeNumber,A:SomeNumber](rgba: tuple [r:R,g:G,b:B,a:A]):Color =')
l(' Color(r:rgba.r.float32, g:rgba.g.float32, b:rgba.b.float32, a:rgba.a.float32)')
l('')
l('## Convert a 3-element tuple of numbers to a gfx.Color')
l('converter toColor*[R:SomeNumber,G:SomeNumber,B:SomeNumber](rgba: tuple [r:R,g:G,b:B]):Color =')
l(' Color(r:rgba.r.float32, g:rgba.g.float32, b:rgba.b.float32, a:1.float32)')
l('')
# NOTE: this simplistic to_Range() converter has various issues, some of them dangerous:
# - doesn't work as expected for slice types
# - it's very easy to create a range that points to invalid memory
# (so far observed for stack-allocated structs <= 16 bytes)
#if inp['prefix'] in ['sg_', 'sdtx_', 'sshape_']:
# l('# helper function to convert "anything" into a Range')
# l('converter to_Range*[T](source: T): Range =')
# l(' Range(addr: source.addr, size: source.sizeof.uint)')
# l('')
c_source_path = '/'.join(c_source_paths[inp['prefix']].split('/')[3:])
l('{.passc:"-DSOKOL_NIM_IMPL".}')
l('when defined(release):')
l(' {.passc:"-DNDEBUG".}')
l(f'{{.compile:"{c_source_path}".}}')
def gen_module(inp, dep_prefixes):
l('## machine generated, do not edit')
l('')
gen_imports(inp, dep_prefixes)
pre_parse(inp)
prefix = inp['prefix']
for decl in inp['decls']:
if not decl['is_dep']:
kind = decl['kind']
if kind == 'consts':
gen_consts(decl, prefix)
elif not check_ignore(decl['name']):
if kind == 'struct':
gen_struct(decl, prefix)
gen_array_converters(decl, prefix)
elif kind == 'enum':
gen_enum(decl, prefix)
elif kind == 'func':
gen_func_nim(decl, prefix)
gen_extra(inp)
def prepare():
print('=== Generating Nim bindings:')
if not os.path.isdir('sokol-nim/src/sokol'):
os.makedirs('sokol-nim/src/sokol')
if not os.path.isdir('sokol-nim/src/sokol/c'):
os.makedirs('sokol-nim/src/sokol/c')
def gen(c_header_path, c_prefix, dep_c_prefixes):
if not c_prefix in module_names:
print(f' >> warning: skipping generation for {c_prefix} prefix...')
return
global out_lines
module_name = module_names[c_prefix]
c_source_path = c_source_paths[c_prefix]
print(f' {c_header_path} => {module_name}')
reset_globals()
shutil.copyfile(c_header_path, f'sokol-nim/src/sokol/c/{os.path.basename(c_header_path)}')
ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes)
gen_module(ir, dep_c_prefixes)
output_path = f"sokol-nim/src/sokol/{ir['module']}.nim"
with open(output_path, 'w', newline='\n') as f_outp:
f_outp.write(out_lines)

View file

@ -1,500 +0,0 @@
#-------------------------------------------------------------------------------
# gen_odin.py
#
# Generate Odin bindings.
#-------------------------------------------------------------------------------
import gen_ir
import gen_util as util
import os, shutil, sys
bindings_root = 'sokol-odin'
c_root = f'{bindings_root}/c'
module_root = f'{bindings_root}/sokol'
module_names = {
'slog_': 'log',
'sg_': 'gfx',
'sapp_': 'app',
'stm_': 'time',
'saudio_': 'audio',
'sgl_': 'gl',
'sdtx_': 'debugtext',
'sshape_': 'shape',
'sglue_': 'glue',
}
system_libs = {
'sg_': {
'windows': {
'd3d11': "",
'gl': "",
},
'macos': {
'metal': '"system:Cocoa.framework","system:QuartzCore.framework","system:Metal.framework","system:MetalKit.framework"',
'gl': '"system:Cocoa.framework","system:QuartzCore.framework","system:OpenGL.framework"'
},
'linux': {
'gl': '"system:GL", "system:dl", "system:pthread"'
}
},
'sapp_': {
'windows': {
'd3d11': '',
'gl': '',
},
'macos': {
'metal': '"system:Cocoa.framework","system:QuartzCore.framework","system:Metal.framework","system:MetalKit.framework"',
'gl': '"system:Cocoa.framework","system:QuartzCore.framework","system:OpenGL.framework"',
},
'linux': {
'gl': '"system:X11", "system:Xi", "system:Xcursor", "system:GL", "system:dl", "system:pthread"'
}
},
'saudio_': {
'windows': {
'd3d11': '',
'gl': '',
},
'macos': {
'metal': '"system:AudioToolbox.framework"',
'gl': '"system:AudioToolbox.framework"',
},
'linux': {
'gl': '"system:asound", "system:dl", "system:pthread"',
}
}
}
c_source_names = {
'slog_': 'sokol_log.c',
'sg_': 'sokol_gfx.c',
'sapp_': 'sokol_app.c',
'sapp_sg': 'sokol_glue.c',
'stm_': 'sokol_time.c',
'saudio_': 'sokol_audio.c',
'sgl_': 'sokol_gl.c',
'sdtx_': 'sokol_debugtext.c',
'sshape_': 'sokol_shape.c',
'sglue_': 'sokol_glue.c',
}
ignores = [
'sdtx_printf',
'sdtx_vprintf',
'sg_install_trace_hooks',
'sg_trace_hooks',
]
# NOTE: syntax for function results: "func_name.RESULT"
overrides = {
'context': 'ctx', # reserved keyword
'SGL_NO_ERROR': 'SGL_ERROR_NO_ERROR',
}
prim_types = {
'int': 'c.int',
'bool': 'bool',
'char': 'u8',
'int8_t': 'i8',
'uint8_t': 'u8',
'int16_t': 'i16',
'uint16_t': 'u16',
'int32_t': 'i32',
'uint32_t': 'u32',
'int64_t': 'i64',
'uint64_t': 'u64',
'float': 'f32',
'double': 'f64',
'uintptr_t': 'u64',
'intptr_t': 'i64',
'size_t': 'u64'
}
prim_defaults = {
'int': '0',
'bool': 'false',
'int8_t': '0',
'uint8_t': '0',
'int16_t': '0',
'uint16_t': '0',
'int32_t': '0',
'uint32_t': '0',
'int64_t': '0',
'uint64_t': '0',
'float': '0.0',
'double': '0.0',
'uintptr_t': '0',
'intptr_t': '0',
'size_t': '0'
}
struct_types = []
enum_types = []
enum_items = {}
out_lines = ''
def reset_globals():
global struct_types
global enum_types
global enum_items
global out_lines
struct_types = []
enum_types = []
enum_items = {}
out_lines = ''
def l(s):
global out_lines
out_lines += s + '\n'
def check_override(name, default=None):
if name in overrides:
return overrides[name]
elif default is None:
return name
else:
return default
def check_ignore(name):
return name in ignores
# PREFIX_BLA_BLUB to BLA_BLUB, prefix_bla_blub to bla_blub
def as_snake_case(s, prefix):
outp = s
if outp.lower().startswith(prefix):
outp = outp[len(prefix):]
return outp
def get_odin_module_path(c_prefix):
return f'{module_root}/{module_names[c_prefix]}'
def get_csource_path(c_prefix):
return f'{c_root}/{c_source_names[c_prefix]}'
def make_odin_module_directory(c_prefix):
path = get_odin_module_path(c_prefix)
if not os.path.isdir(path):
os.makedirs(path)
def as_prim_type(s):
return prim_types[s]
# prefix_bla_blub(_t) => (dep.)Bla_Blub
def as_struct_or_enum_type(s, prefix):
parts = s.lower().split('_')
outp = '' if s.startswith(prefix) else f'{parts[0]}.'
for part in parts[1:]:
# ignore '_t' type postfix
if (part != 't'):
outp += part.capitalize()
outp += '_'
outp = outp[:-1]
return outp
# PREFIX_ENUM_BLA_BLUB => BLA_BLUB, _PREFIX_ENUM_BLA_BLUB => BLA_BLUB
def as_enum_item_name(s):
outp = s.lstrip('_')
parts = outp.split('_')[2:]
outp = '_'.join(parts)
if outp[0].isdigit():
outp = '_' + outp
return outp
def enum_default_item(enum_name):
return enum_items[enum_name][0]
def is_prim_type(s):
return s in prim_types
def is_int_type(s):
return s == "int"
def is_struct_type(s):
return s in struct_types
def is_enum_type(s):
return s in enum_types
def is_const_prim_ptr(s):
for prim_type in prim_types:
if s == f"const {prim_type} *":
return True
return False
def is_prim_ptr(s):
for prim_type in prim_types:
if s == f"{prim_type} *":
return True
return False
def is_const_struct_ptr(s):
for struct_type in struct_types:
if s == f"const {struct_type} *":
return True
return False
def type_default_value(s):
return prim_defaults[s]
def map_type(type, prefix, sub_type):
if sub_type not in ['c_arg', 'odin_arg', 'struct_field']:
sys.exit(f"Error: map_type(): unknown sub_type '{sub_type}")
if type == "void":
return ""
elif is_prim_type(type):
if sub_type == 'odin_arg':
# for Odin args, maps C int (32-bit) to Odin int (pointer-sized),
# and the C bool type to Odin's bool type
if type == 'int' or type == 'uint32_t':
return 'int'
elif type == 'bool':
return 'bool'
return as_prim_type(type)
elif is_struct_type(type):
return as_struct_or_enum_type(type, prefix)
elif is_enum_type(type):
return as_struct_or_enum_type(type, prefix)
elif util.is_void_ptr(type):
return "rawptr"
elif util.is_const_void_ptr(type):
return "rawptr"
elif util.is_string_ptr(type):
return "cstring"
elif is_const_struct_ptr(type):
# pass Odin struct args by value, not by pointer
if sub_type == 'odin_arg':
return f"{as_struct_or_enum_type(util.extract_ptr_type(type), prefix)}"
else:
return f"^{as_struct_or_enum_type(util.extract_ptr_type(type), prefix)}"
elif is_prim_ptr(type):
return f"^{as_prim_type(util.extract_ptr_type(type))}"
elif is_const_prim_ptr(type):
return f"^{as_prim_type(util.extract_ptr_type(type))}"
elif util.is_1d_array_type(type):
array_type = util.extract_array_type(type)
array_sizes = util.extract_array_sizes(type)
return f"[{array_sizes[0]}]{map_type(array_type, prefix, sub_type)}"
elif util.is_2d_array_type(type):
array_type = util.extract_array_type(type)
array_sizes = util.extract_array_sizes(type)
return f"[{array_sizes[0]}][{array_sizes[1]}]{map_type(array_type, prefix, sub_type)}"
elif util.is_func_ptr(type):
res_type = funcptr_result_c(type, prefix)
res_str = '' if res_type == '' else f' -> {res_type}'
return f'proc "c" ({funcptr_args_c(type, prefix)}){res_str}'
else:
sys.exit(f"Error map_type(): unknown type '{type}'")
def funcdecl_args_c(decl, prefix):
s = ''
func_name = decl['name']
for param_decl in decl['params']:
if s != '':
s += ', '
param_name = param_decl['name']
param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type'])
if is_const_struct_ptr(param_type):
s += f"#by_ptr {param_name}: {map_type(param_type, prefix, 'odin_arg')}"
elif is_int_type(param_type):
s += f"#any_int {param_name}: {map_type(param_type, prefix, 'c_arg')}"
else:
s += f"{param_name}: {map_type(param_type, prefix, 'c_arg')}"
return s
def funcptr_args_c(field_type, prefix):
tokens = field_type[field_type.index('(*)')+4:-1].split(',')
s = ''
arg_index = 0
for token in tokens:
arg_type = token.strip()
if s != '':
s += ', '
c_arg = map_type(arg_type, prefix, 'c_arg')
if c_arg == '':
return ''
else:
s += f'a{arg_index}: {c_arg}'
arg_index += 1
return s
def funcptr_result_c(field_type, prefix):
res_type = field_type[:field_type.index('(*)')].strip()
return map_type(res_type, prefix, 'c_arg')
def funcdecl_result_c(decl, prefix):
func_name = decl['name']
decl_type = decl['type']
res_c_type = decl_type[:decl_type.index('(')].strip()
return map_type(check_override(f'{func_name}.RESULT', default=res_c_type), prefix, 'c_arg')
def get_system_libs(module, platform, backend):
if module in system_libs:
if platform in system_libs[module]:
if backend in system_libs[module][platform]:
libs = system_libs[module][platform][backend]
if libs != '':
return f", {libs}"
return ''
def gen_c_imports(inp, c_prefix, prefix):
clib_prefix = f'sokol_{inp["module"]}'
clib_import = f'{clib_prefix}_clib'
windows_d3d11_libs = get_system_libs(prefix, 'windows', 'd3d11')
windows_gl_libs = get_system_libs(prefix, 'windows', 'gl')
macos_metal_libs = get_system_libs(prefix, 'macos', 'metal')
macos_gl_libs = get_system_libs(prefix, 'macos', 'gl')
linux_gl_libs = get_system_libs(prefix, 'linux', 'gl')
l( 'import "core:c"')
l( 'when ODIN_OS == .Windows {')
l( ' when #config(SOKOL_USE_GL,false) {')
l(f' when ODIN_DEBUG == true {{ foreign import {clib_import} {{ "{clib_prefix}_windows_x64_gl_debug.lib"{windows_gl_libs} }} }}')
l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_windows_x64_gl_release.lib"{windows_gl_libs} }} }}')
l( ' } else {')
l(f' when ODIN_DEBUG == true {{ foreign import {clib_import} {{ "{clib_prefix}_windows_x64_d3d11_debug.lib"{windows_d3d11_libs} }} }}')
l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_windows_x64_d3d11_release.lib"{windows_d3d11_libs} }} }}')
l( ' }')
l( '} else when ODIN_OS == .Darwin {')
l( ' when #config(SOKOL_USE_GL,false) {')
l( ' when ODIN_ARCH == .arm64 {')
l(f' when ODIN_DEBUG == true {{ foreign import {clib_import} {{ "{clib_prefix}_macos_arm64_gl_debug.a"{macos_gl_libs} }} }}')
l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_macos_arm64_gl_release.a"{macos_gl_libs} }} }}')
l( ' } else {')
l(f' when ODIN_DEBUG == true {{ foreign import {clib_import} {{ "{clib_prefix}_macos_x64_gl_debug.a"{macos_gl_libs} }} }}')
l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_macos_x64_gl_release.a"{macos_gl_libs} }} }}')
l( ' }')
l( ' } else {')
l( ' when ODIN_ARCH == .arm64 {')
l(f' when ODIN_DEBUG == true {{ foreign import {clib_import} {{ "{clib_prefix}_macos_arm64_metal_debug.a"{macos_metal_libs} }} }}')
l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_macos_arm64_metal_release.a"{macos_metal_libs} }} }}')
l( ' } else {')
l(f' when ODIN_DEBUG == true {{ foreign import {clib_import} {{ "{clib_prefix}_macos_x64_metal_debug.a"{macos_metal_libs} }} }}')
l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_macos_x64_metal_release.a"{macos_metal_libs} }} }}')
l( ' }')
l( ' }')
l( '}')
l( 'else {')
l(f' when ODIN_DEBUG == true {{ foreign import {clib_import} {{ "{clib_prefix}_linux_x64_gl_debug.a"{linux_gl_libs} }} }}')
l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_linux_x64_gl_release.a"{linux_gl_libs} }} }}')
l( '}')
# Need to special case sapp_sg to avoid Odin's context keyword
if c_prefix == "sapp_sg":
l(f'@(default_calling_convention="c")')
else:
l(f'@(default_calling_convention="c", link_prefix="{c_prefix}")')
l(f"foreign {clib_import} {{")
prefix = inp['prefix']
for decl in inp['decls']:
if decl['kind'] == 'func' and not decl['is_dep'] and not check_ignore(decl['name']):
args = funcdecl_args_c(decl, prefix)
res_type = funcdecl_result_c(decl, prefix)
res_str = '' if res_type == '' else f'-> {res_type}'
# Need to special case sapp_sg to avoid Odin's context keyword
if c_prefix == "sapp_sg":
l(f' @(link_name="{decl["name"]}")')
l(f" {check_override(as_snake_case(decl['name'], c_prefix))} :: proc({args}) {res_str} ---")
else:
l(f" {as_snake_case(decl['name'], c_prefix)} :: proc({args}) {res_str} ---")
l('}')
def gen_consts(decl, prefix):
for item in decl['items']:
item_name = check_override(item['name'])
l(f"{as_snake_case(item_name, prefix)} :: {item['value']}")
def gen_struct(decl, prefix):
c_struct_name = check_override(decl['name'])
struct_name = as_struct_or_enum_type(c_struct_name, prefix)
l(f'{struct_name} :: struct {{')
for field in decl['fields']:
field_name = check_override(field['name'])
field_type = map_type(check_override(f'{c_struct_name}.{field_name}', default=field['type']), prefix, 'struct_field')
# any field name starting with _ is considered private
if field_name.startswith('_'):
l(f' _ : {field_type},')
else:
l(f' {field_name} : {field_type},')
l('}')
def gen_enum(decl, prefix):
enum_name = check_override(decl['name'])
l(f'{as_struct_or_enum_type(enum_name, prefix)} :: enum i32 {{')
for item in decl['items']:
item_name = as_enum_item_name(check_override(item['name']))
if item_name != 'FORCE_U32':
if 'value' in item:
l(f" {item_name} = {item['value']},")
else:
l(f" {item_name},")
l('}')
def gen_imports(dep_prefixes):
for dep_prefix in dep_prefixes:
dep_module_name = module_names[dep_prefix]
l(f'import {dep_prefix[:-1]} "../{dep_module_name}"')
l('')
def gen_helpers(inp):
if inp['prefix'] == 'sdtx_':
l('import "core:fmt"')
l('import "core:strings"')
l('printf :: proc(s: string, args: ..any) {')
l(' fstr := fmt.tprintf(s, ..args)')
l(' putr(strings.unsafe_string_to_cstring(fstr), len(fstr))')
l('}')
def gen_module(inp, c_prefix, dep_prefixes):
pre_parse(inp)
l('// machine generated, do not edit')
l('')
l(f"package sokol_{inp['module']}")
gen_imports(dep_prefixes)
gen_helpers(inp)
prefix = inp['prefix']
gen_c_imports(inp, c_prefix, prefix)
for decl in inp['decls']:
if not decl['is_dep']:
kind = decl['kind']
if kind == 'consts':
gen_consts(decl, prefix)
elif not check_ignore(decl['name']):
if kind == 'struct':
gen_struct(decl, prefix)
elif kind == 'enum':
gen_enum(decl, prefix)
def pre_parse(inp):
global struct_types
global enum_types
for decl in inp['decls']:
kind = decl['kind']
if kind == 'struct':
struct_types.append(decl['name'])
elif kind == 'enum':
enum_name = decl['name']
enum_types.append(enum_name)
enum_items[enum_name] = []
for item in decl['items']:
enum_items[enum_name].append(as_enum_item_name(item['name']))
def prepare():
print('=== Generating Odin bindings:')
if not os.path.isdir(c_root):
os.makedirs(c_root)
if not os.path.isdir(module_root):
os.makedirs(module_root)
def gen(c_header_path, c_prefix, dep_c_prefixes):
if not c_prefix in module_names:
print(f' >> warning: skipping generation for {c_prefix} prefix...')
return
reset_globals()
make_odin_module_directory(c_prefix)
print(f' {c_header_path} => {module_names[c_prefix]}')
shutil.copyfile(c_header_path, f'{c_root}/{os.path.basename(c_header_path)}')
csource_path = get_csource_path(c_prefix)
module_name = module_names[c_prefix]
ir = gen_ir.gen(c_header_path, csource_path, module_name, c_prefix, dep_c_prefixes)
gen_module(ir, c_prefix, dep_c_prefixes)
with open(f"{module_root}/{ir['module']}/{ir['module']}.odin", 'w', newline='\n') as f_outp:
f_outp.write(out_lines)

View file

@ -1,893 +0,0 @@
# -------------------------------------------------------------------------------
# Generate rust bindings.
#
# rust coding style:
# - types are PascalCase
# - otherwise snake_case
# -------------------------------------------------------------------------------
import gen_ir
import os, shutil, sys
import gen_util as util
module_names = {
"slog_": "log",
"sg_": "gfx",
"sapp_": "app",
"stm_": "time",
"saudio_": "audio",
"sgl_": "gl",
"sdtx_": "debugtext",
"sshape_": "shape",
"simgui_": "imgui",
"sglue_": "glue",
}
module_requires_rust_feature = {
module_names["simgui_"]: "imgui",
}
c_source_paths = {
"slog_": "sokol-rust/src/sokol/c/sokol_log.c",
"sg_": "sokol-rust/src/sokol/c/sokol_gfx.c",
"sapp_": "sokol-rust/src/sokol/c/sokol_app.c",
"stm_": "sokol-rust/src/sokol/c/sokol_time.c",
"saudio_": "sokol-rust/src/sokol/c/sokol_audio.c",
"sgl_": "sokol-rust/src/sokol/c/sokol_gl.c",
"sdtx_": "sokol-rust/src/sokol/c/sokol_debugtext.c",
"sshape_": "sokol-rust/src/sokol/c/sokol_shape.c",
"simgui_": "sokol-rust/src/sokol/c/sokol_imgui.c",
"sglue_": "sokol-rust/src/sokol/c/sokol_glue.c",
}
ignores = [
"sdtx_printf",
"sdtx_vprintf",
"simgui_add_key_event",
# "sg_install_trace_hooks",
# "sg_trace_hooks",
]
range_struct_name = "Range"
# functions that need to be exposed as 'raw' C callbacks without a rust wrapper function
c_callbacks = ["slog_func"]
# NOTE: syntax for function results: "func_name.RESULT"
overrides = {
"type": "_type",
"ref": "_ref",
"sg_apply_uniforms.ub_index": "uintptr_t",
"sg_draw.base_element": "uintptr_t",
"sg_draw.num_elements": "uintptr_t",
"sg_draw.num_instances": "uintptr_t",
"sshape_element_range_t.base_element": "uintptr_t",
"sshape_element_range_t.num_elements": "uintptr_t",
"sdtx_font.font_index": "uintptr_t",
"sdtx_move": "sdtx_move_cursor",
"sdtx_move_x": "sdtx_move_cursor_x",
"sdtx_move_y": "sdtx_move_cursor_y",
"sg_image_type::SG_IMAGETYPE_2D": "SG_IMAGEYPE_DIM2",
"sg_image_type::SG_IMAGETYPE_3D": "SG_IMAGETYPE_DIM3",
"sapp_keycode::SAPP_KEYCODE_0": "SAPP_KEYCODE_NUM0",
"sapp_keycode::SAPP_KEYCODE_1": "SAPP_KEYCODE_NUM1",
"sapp_keycode::SAPP_KEYCODE_2": "SAPP_KEYCODE_NUM2",
"sapp_keycode::SAPP_KEYCODE_3": "SAPP_KEYCODE_NUM3",
"sapp_keycode::SAPP_KEYCODE_4": "SAPP_KEYCODE_NUM4",
"sapp_keycode::SAPP_KEYCODE_5": "SAPP_KEYCODE_NUM5",
"sapp_keycode::SAPP_KEYCODE_6": "SAPP_KEYCODE_NUM6",
"sapp_keycode::SAPP_KEYCODE_7": "SAPP_KEYCODE_NUM7",
"sapp_keycode::SAPP_KEYCODE_8": "SAPP_KEYCODE_NUM8",
"sapp_keycode::SAPP_KEYCODE_9": "SAPP_KEYCODE_NUM9",
# "sgl_error": "sgl_get_error", # 'error' is reserved in zig
# "sgl_deg": "sgl_as_degrees",
# "sgl_rad": "sgl_as_radians",
# "sg_context_desc.color_format": "int",
# "SGL_NO_ERROR": "SGL_ERROR_NO_ERROR",
# "sg_context_desc.depth_format": "int",
}
prim_types = {
"int": "i32",
"bool": "bool",
"char": "core::ffi::c_char",
"int8_t": "i8",
"uint8_t": "u8",
"int16_t": "i16",
"uint16_t": "u16",
"int32_t": "i32",
"uint32_t": "u32",
"int64_t": "i64",
"uint64_t": "u64",
"float": "f32",
"double": "f64",
"uintptr_t": "usize",
"intptr_t": "isize",
"size_t": "usize",
}
prim_defaults = {
"int": "0",
"bool": "false",
"int8_t": "0",
"uint8_t": "0",
"int16_t": "0",
"uint16_t": "0",
"int32_t": "0",
"uint32_t": "0",
"int64_t": "0",
"uint64_t": "0",
"float": "0.0",
"double": "0.0",
"uintptr_t": "0",
"intptr_t": "0",
"size_t": "0",
"char": "0",
}
special_constant_types = {
"SG_INVALID_ID": "u32",
"SAPP_MODIFIER_SHIFT": "u32",
"SAPP_MODIFIER_CTRL": "u32",
"SAPP_MODIFIER_ALT": "u32",
"SAPP_MODIFIER_SUPER": "u32",
"SAPP_MODIFIER_LMB": "u32",
"SAPP_MODIFIER_RMB": "u32",
"SAPP_MODIFIER_MMB": "u32",
}
struct_types = []
enum_types = []
enum_items = {}
out_lines = ""
def reset_globals():
global struct_types
global enum_types
global enum_items
global out_lines
struct_types = []
enum_types = []
enum_items = {}
out_lines = ""
def l(s):
global out_lines
out_lines += s + "\n"
def as_rust_prim_type(s):
return prim_types[s]
def as_upper_snake_case(s, prefix):
outp = s.lower()
if outp.startswith(prefix):
outp = outp[len(prefix):]
return outp.upper()
# prefix_bla_blub(_t) => (dep::)BlaBlub
def as_rust_struct_type(s, prefix):
parts = s.lower().split("_")
outp = "" if s.startswith(prefix) else f"{parts[0]}::"
for part in parts[1:]:
# ignore '_t' type postfix
if part != "t":
outp += part.capitalize()
return outp
# prefix_bla_blub(_t) => (dep::)BlaBlub
def as_rust_enum_type(s, prefix):
parts = s.lower().split("_")
outp = "" if s.startswith(prefix) else f"{parts[0]}::"
for part in parts[1:]:
# ignore '_t' type postfix
if part != "t":
outp += part.capitalize()
return outp
def check_override(name, default=None):
if name in overrides:
return overrides[name]
elif default is None:
return name
else:
return default
def check_ignore(name):
return name in ignores
# PREFIX_ENUM_BLA_BLA => BlaBla, _PREFIX_ENUM_BLA_BLA => BlaBla
def as_enum_item_name(s):
parts = s.lstrip("_").split("_")
outp = ""
for i, part in enumerate(parts[2:]):
# TODO: What to do with enum fields starting with numbers?
outp += part.capitalize()
return outp
def enum_default_item(enum_name):
return enum_items[enum_name][0]
def is_prim_type(s):
return s in prim_types
def is_struct_type(s):
return s in struct_types
def is_enum_type(s):
return s in enum_types
def is_const_prim_ptr(s):
for prim_type in prim_types:
if s == f"const {prim_type} *":
return True
return False
def is_prim_ptr(s):
for prim_type in prim_types:
if s == f"{prim_type} *":
return True
return False
def is_const_struct_ptr(s):
for struct_type in struct_types:
if s == f"const {struct_type} *":
return True
return False
def is_struct_ptr(s):
for struct_type in struct_types:
if s == f"{struct_type} *":
return True
return False
def type_default_value(s):
return prim_defaults[s]
def as_c_arg_type(arg_prefix, arg_type, prefix):
# NOTE: if arg_prefix is None, the result is used as return value
pre = "" if arg_prefix is None else arg_prefix
if arg_type == "void":
return ""
elif is_prim_type(arg_type):
return pre + as_rust_prim_type(arg_type)
elif is_struct_type(arg_type):
return pre + as_rust_struct_type(arg_type, prefix)
elif is_enum_type(arg_type):
return pre + as_rust_enum_type(arg_type, prefix)
elif util.is_void_ptr(arg_type):
return pre + "*mut core::ffi::c_void"
elif util.is_const_void_ptr(arg_type):
return pre + "*const core::ffi::c_void"
elif util.is_string_ptr(arg_type):
return pre + "*const core::ffi::c_char"
elif is_const_struct_ptr(arg_type):
return pre + f"*const {as_rust_struct_type(util.extract_ptr_type(arg_type), prefix)}"
elif is_struct_ptr(arg_type):
return pre + f"*mut {as_rust_struct_type(util.extract_ptr_type(arg_type), prefix)}"
elif is_prim_ptr(arg_type):
return pre + f"*mut {as_rust_prim_type(util.extract_ptr_type(arg_type))}"
elif is_const_prim_ptr(arg_type):
return pre + f"*const {as_rust_prim_type(util.extract_ptr_type(arg_type))}"
else:
sys.exit(f"ERROR as_c_arg_type(): {arg_type}")
def as_rust_arg_type(arg_prefix, arg_type, prefix):
# NOTE: if arg_prefix is None, the result is used as return value
pre = "" if arg_prefix is None else arg_prefix
if arg_type == "void":
return ""
elif is_prim_type(arg_type):
return pre + as_rust_prim_type(arg_type)
elif is_struct_type(arg_type):
return pre + as_rust_struct_type(arg_type, prefix)
elif is_enum_type(arg_type):
return pre + as_rust_enum_type(arg_type, prefix)
elif util.is_void_ptr(arg_type):
return pre + "*mut core::ffi::c_void"
elif util.is_const_void_ptr(arg_type):
return pre + "*const core::ffi::c_void"
elif util.is_string_ptr(arg_type):
return pre + "&str"
elif is_const_struct_ptr(arg_type):
return pre + f"&{as_rust_struct_type(util.extract_ptr_type(arg_type), prefix)}"
elif is_struct_ptr(arg_type):
return pre + f"&mut {as_rust_struct_type(util.extract_ptr_type(arg_type), prefix)}"
elif is_prim_ptr(arg_type):
return pre + f"&mut {as_rust_prim_type(util.extract_ptr_type(arg_type))}"
elif is_const_prim_ptr(arg_type):
return pre + f"&{as_rust_prim_type(util.extract_ptr_type(arg_type))}"
else:
sys.exit(f"ERROR as_rust_arg_type(): {arg_type}")
def is_rust_string(rust_type):
return rust_type == "&str" or rust_type == " -> &'static str"
# get C-style arguments of a function pointer as string
def funcptr_args_c(field_type, prefix):
tokens = field_type[field_type.index("(*)") + 4: -1].split(",")
s = ""
for token in tokens:
arg_type = token.strip()
if s != "":
s += ", "
c_arg = as_c_arg_type(None, arg_type, prefix)
if c_arg == "void":
return ""
else:
s += c_arg
return s
# get C-style result of a function pointer as string
def funcptr_result_c(field_type):
res_type = field_type[: field_type.index("(*)")].strip()
if res_type == "void":
return ""
elif is_prim_type(res_type):
return f" -> {as_rust_prim_type(res_type)}"
elif util.is_const_void_ptr(res_type):
return " -> *const core::ffi::c_void"
elif util.is_void_ptr(res_type):
return " -> *mut core::ffi::c_void"
else:
sys.exit(f"ERROR funcptr_result_c(): {field_type}")
def funcdecl_args_c(decl, prefix):
s = ""
func_name = decl["name"]
for param_decl in decl["params"]:
if s != "":
s += ", "
param_name = param_decl["name"]
param_type = check_override(
f"{func_name}.{param_name}", default=param_decl["type"]
)
s += f"{as_c_arg_type(f'{param_name}: ', param_type, prefix)}"
return s
def funcdecl_args_rust(decl, prefix):
s = ""
func_name = decl["name"]
for param_decl in decl["params"]:
if s != "":
s += ", "
param_name = param_decl["name"]
param_type = check_override(
f"{func_name}.{param_name}", default=param_decl["type"]
)
s += f"{as_rust_arg_type(f'{param_name}: ', param_type, prefix)}"
return s
def funcdecl_result_c(decl, prefix):
func_name = decl["name"]
decl_type = decl["type"]
result_type = check_override(
f"{func_name}.RESULT", default=decl_type[: decl_type.index("(")].strip()
)
it = as_c_arg_type(None, result_type, prefix)
if it == "()" or it == "":
return ""
else:
return f" -> {it}"
def funcdecl_result_rust(decl, prefix):
func_name = decl["name"]
decl_type = decl["type"]
result_type = check_override(
f"{func_name}.RESULT", default=decl_type[: decl_type.index("(")].strip()
)
rust_res_type = as_rust_arg_type(None, result_type, prefix)
if is_rust_string(rust_res_type):
rust_res_type = "&'static str"
if rust_res_type == "":
return ""
else:
return f" -> {rust_res_type }"
def gen_struct(decl, prefix):
struct_name = check_override(decl["name"])
rust_type = as_rust_struct_type(struct_name, prefix)
rust_struct_type = rust_type
struct_lines = []
default_lines = []
for field in decl["fields"]:
field_name = check_override(field["name"])
field_type = check_override(
f"{struct_name}.{field_name}", default=field["type"]
)
if is_prim_type(field_type):
struct_lines.append(
f"pub {field_name}: {as_rust_prim_type(field_type)}"
)
default_lines.append(
f"{field_name}: {type_default_value(field_type)}"
)
elif is_struct_type(field_type):
struct_lines.append(
f"pub {field_name}: {as_rust_struct_type(field_type, prefix)}"
)
default_lines.append(
f"{field_name}: {as_rust_struct_type(field_type, prefix)}::new()"
)
elif is_enum_type(field_type):
struct_lines.append(
f"pub {field_name}: {as_rust_enum_type(field_type, prefix)}"
)
default_lines.append(
f"{field_name}: {as_rust_enum_type(field_type, prefix)}::new()"
)
elif util.is_string_ptr(field_type):
struct_lines.append(
f"pub {field_name}: *const core::ffi::c_char"
)
default_lines.append(
f"{field_name}: core::ptr::null()"
)
elif util.is_const_void_ptr(field_type):
struct_lines.append(
f"pub {field_name}: *const core::ffi::c_void"
)
default_lines.append(
f"{field_name}: core::ptr::null()"
)
elif util.is_void_ptr(field_type):
struct_lines.append(
f"pub {field_name}: *mut core::ffi::c_void"
)
default_lines.append(
f"{field_name}: core::ptr::null_mut()"
)
elif is_const_prim_ptr(field_type):
struct_lines.append(
f"pub {field_name}: *const {as_rust_prim_type(util.extract_ptr_type(field_type))}"
)
default_lines.append(
f"{field_name}: core::ptr::null()"
)
elif is_prim_ptr(field_type):
struct_lines.append(
f"pub {field_name}: *mut {as_rust_prim_type(util.extract_ptr_type(field_type))}"
)
default_lines.append(
f"{field_name}: core::ptr::null_mut()"
)
elif is_const_struct_ptr(field_type):
struct_lines.append(
f"pub {field_name}: *const {as_rust_struct_type(util.extract_ptr_type(field_type), prefix)}"
)
default_lines.append(
f"{field_name}: core::ptr::null()"
)
elif is_struct_ptr(field_type):
struct_lines.append(
f"pub {field_name}: *mut {as_rust_struct_type(util.extract_ptr_type(field_type), prefix)}"
)
default_lines.append(
f"{field_name}: core::ptr::null_mut()"
)
elif util.is_func_ptr(field_type):
struct_lines.append(
f"pub {field_name}: Option<extern \"C\" fn({funcptr_args_c(field_type, prefix)}){funcptr_result_c(field_type)}>"
)
default_lines.append(
f"{field_name}: None"
)
elif util.is_1d_array_type(field_type):
array_type = util.extract_array_type(field_type)
array_sizes = util.extract_array_sizes(field_type)
if is_prim_type(array_type) or is_struct_type(array_type):
if is_prim_type(array_type):
rust_type = as_rust_prim_type(array_type)
def_val = type_default_value(array_type)
elif is_struct_type(array_type) or is_enum_type(array_type):
rust_type = as_rust_struct_type(array_type, prefix)
def_val = f"{rust_type}::new()"
else:
sys.exit(f"ERROR gen_struct is_1d_array_type: {array_type}")
t0 = f"[{rust_type}; {array_sizes[0]}]"
# t1 = f"&{rust_type}"
struct_lines.append(
f"pub {field_name}: {t0}"
)
default_lines.append(
f"{field_name}: [{def_val}; {array_sizes[0]}]"
)
elif util.is_const_void_ptr(array_type):
struct_lines.append(
f"pub {field_name}: [*const core::ffi::c_void; {array_sizes[0]}]"
)
default_lines.append(
f"{field_name}: [core::ptr::null(); {array_sizes[0]}]"
)
else:
sys.exit(
f"ERROR gen_struct: array {field_name}: {field_type} => [{array_type}: {array_sizes[0]}]"
)
elif util.is_2d_array_type(field_type):
array_type = util.extract_array_type(field_type)
array_sizes = util.extract_array_sizes(field_type)
if is_prim_type(array_type):
rust_type = as_rust_prim_type(array_type)
def_val = type_default_value(array_type)
elif is_struct_type(array_type):
rust_type = as_rust_struct_type(array_type, prefix)
def_val = f"{rust_type}::new()"
else:
sys.exit(f"ERROR gen_struct is_2d_array_type: {array_type}")
struct_lines.append(
f"pub {field_name}: [[{rust_type}; {array_sizes[1]}]; {array_sizes[0]}]"
)
default_lines.append(
f"{field_name}: [[{def_val}; {array_sizes[1]}]; {array_sizes[0]}]"
)
else:
sys.exit(f"ERROR gen_struct: {field_name}: {field_type};")
#
# TODO: Is this the best way to have zero-initialization with support for constants?
# core::mem::zeroed() cleaner?
#
l("#[repr(C)]")
l("#[derive(Copy, Clone, Debug)]")
l(f"pub struct {rust_struct_type} {{")
for line in struct_lines:
l(f" {line},")
l("}")
l(f"impl {rust_struct_type} {{")
l(" pub const fn new() -> Self {")
l(" Self {")
for line in default_lines:
l(f" {line},")
l(" }")
l(" }")
l("}")
l(f"impl Default for {rust_struct_type} {{")
l(" fn default() -> Self {")
l(" Self::new()")
l(" }")
l("}")
def gen_consts(decl, prefix):
for item in decl["items"]:
#
# TODO: What type should these constants have? Currently giving all `usize`
# unless specifically overridden by `special_constant_types`
#
item_name = check_override(item["name"])
if item_name in special_constant_types:
special_type = special_constant_types[item_name]
l(f"pub const {as_upper_snake_case(item_name, prefix)}: {special_type} = {item['value']};")
else:
l(f"pub const {as_upper_snake_case(item_name, prefix)}: usize = {item['value']};")
def gen_enum(decl, prefix):
enum_name = check_override(decl["name"])
names = [
as_enum_item_name(check_override(f"{decl['name']}::{item['name']}", item['name'])) for item in decl["items"]
]
is_u32 = False
for name in names:
if name == "ForceU32":
is_u32 = True
break
l("#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]")
if is_u32:
l("#[repr(u32)]")
else:
l("#[repr(i32)]")
rust_enum_name = as_rust_enum_type(enum_name, prefix)
l(f"pub enum {rust_enum_name} {{")
for item_name, item in zip(names, decl["items"]):
if item_name != "ForceU32":
if "value" in item:
l(f" {item_name} = {item['value']},")
else:
l(f" {item_name},")
l("}")
default_item = enum_default_item(enum_name)
l(f"impl {rust_enum_name} {{")
l(" pub const fn new() -> Self {")
l(f" Self::{default_item}")
l(" }")
l("}")
l(f"impl Default for {rust_enum_name} {{")
l(" fn default() -> Self {")
l(f" Self::{default_item}")
l(" }")
l("}")
def gen_func_c(decl, prefix):
l("pub extern \"C\" {")
l(f" fn {decl['name']}({funcdecl_args_c(decl, prefix)}){funcdecl_result_c(decl, prefix)};")
l("}")
def gen_c_funcs(funcs):
l("pub mod ffi {")
l(" #![allow(unused_imports)]")
l(" use super::*;")
l(" extern \"C\" {")
for decl, prefix in funcs:
l(f" pub fn {decl['name']}({funcdecl_args_c(decl, prefix)}){funcdecl_result_c(decl, prefix)};")
l(" }")
l("}")
def gen_rust_funcs(funcs):
for decl, prefix in funcs:
gen_func_rust(decl, prefix)
def gen_func_rust(decl, prefix):
c_func_name = decl["name"]
rust_func_name = util.as_lower_snake_case(check_override(decl["name"]), prefix)
rust_res_type = funcdecl_result_rust(decl, prefix)
if c_func_name in c_callbacks:
c_res_type = funcdecl_result_c(decl, prefix)
l("#[inline]")
l(f'pub extern "C" fn {c_func_name}({funcdecl_args_c(decl, prefix)}){c_res_type} {{')
l(" unsafe {")
s = f" ffi::{c_func_name}("
for i, param_decl in enumerate(decl["params"]):
if i > 0:
s += ", "
arg_name = param_decl["name"]
s += arg_name
s += ")"
l(s)
l(" }")
l("}")
else:
l("#[inline]")
l(f"pub fn {rust_func_name}({funcdecl_args_rust(decl, prefix)}){rust_res_type} {{")
for i, param_decl in enumerate(decl["params"]):
arg_name = param_decl["name"]
arg_type = param_decl["type"]
if util.is_string_ptr(arg_type):
l(f" let tmp_{i} = std::ffi::CString::new({arg_name}).unwrap();")
l(" unsafe {")
if is_rust_string(rust_res_type):
# special case: convert C string to rust string slice
s = f" c_char_ptr_to_rust_str(ffi::{c_func_name}("
else:
s = f" ffi::{c_func_name}("
for i, param_decl in enumerate(decl["params"]):
if i > 0:
s += ", "
arg_name = param_decl["name"]
arg_type = param_decl["type"]
if util.is_string_ptr(arg_type):
s += f"tmp_{i}.as_ptr()"
else:
s += arg_name
if is_rust_string(rust_res_type):
s += ")"
s += ")"
l(s)
l(" }")
l("}")
def pre_parse(inp):
global struct_types
global enum_types
for decl in inp["decls"]:
kind = decl["kind"]
if kind == "struct":
struct_types.append(decl["name"])
elif kind == "enum":
enum_name = decl["name"]
enum_types.append(enum_name)
enum_items[enum_name] = []
for item in decl["items"]:
enum_items[enum_name].append(as_enum_item_name(item["name"]))
def gen_imports(inp, dep_prefixes):
for dep_prefix in dep_prefixes:
dep_module_name = module_names[dep_prefix]
# l(f'const {dep_prefix[:-1]} = @import("{dep_module_name}.rs");')
l(f'use crate::{dep_module_name} as {dep_prefix[:-1]};')
l("")
def gen_helpers(inp):
l("/// Helper function to convert a C string to a rust string slice")
l("#[inline]")
l("fn c_char_ptr_to_rust_str(c_char_ptr: *const core::ffi::c_char) -> &'static str {")
l(" let c_str = unsafe { core::ffi::CStr::from_ptr(c_char_ptr) };")
l(" c_str.to_str().expect(\"c_char_ptr contained invalid Utf8 Data\")")
l("}")
l("")
if inp['prefix'] in ['sg_', 'sdtx_', 'sshape_', 'sapp_']:
l("/// Helper function to cast a rust slice into a sokol Range")
l(f"pub fn slice_as_range<T>(data: &[T]) -> {range_struct_name} {{")
l(f" {range_struct_name} {{ size: std::mem::size_of_val(data), ptr: data.as_ptr() as *const _ }}")
l("}")
l("/// Helper function to cast a rust reference into a sokol Range")
l(f"pub fn value_as_range<T>(value: &T) -> {range_struct_name} {{")
l(f" {range_struct_name} {{ size: std::mem::size_of::<T>(), ptr: value as *const T as *const _ }}")
l("}")
l("")
l(f"impl<T> From<&[T]> for {range_struct_name} {{")
l(" #[inline]")
l(" fn from(data: &[T]) -> Self {")
l(" slice_as_range(data)")
l(" }")
l("}")
l(f"impl<T> From<&T> for {range_struct_name} {{")
l(" #[inline]")
l(" fn from(value: &T) -> Self {")
l(" value_as_range(value)")
l(" }")
l("}")
l("")
# if inp["prefix"] == "sdtx_":
# l("/// std.fmt compatible Writer")
# l("pub const Writer = struct {")
# l(" pub const Error = error { };")
# l(" pub fn writeAll(self: Writer, bytes: []const u8) Error!void {")
# l(" _ = self;")
# l(" for (bytes) |byte| {")
# l(" putc(byte);")
# l(" }")
# l(" }")
# l(" pub fn writeByteNTimes(self: Writer, byte: u8, n: u64) Error!void {")
# l(" _ = self;")
# l(" var i: u64 = 0;")
# l(" while (i < n): (i += 1) {")
# l(" putc(byte);")
# l(" }")
# l(" }")
# l("};")
# l("// std.fmt-style formatted print")
# l("pub fn print(comptime fmt: anytype, args: anytype) void {")
# l(" var writer: Writer = .{};")
# l(' @import("std").fmt.format(writer, fmt, args) catch {};')
# l("}")
# l("")
def gen_module(inp, dep_prefixes):
module = inp['module']
if module in module_requires_rust_feature:
feature = module_requires_rust_feature[module]
l(f"//! To use this module, enable the feature \"{feature}\"")
l("// machine generated, do not edit")
l("")
l("#![allow(dead_code)]")
l("#![allow(unused_imports)]")
l("")
gen_imports(inp, dep_prefixes)
gen_helpers(inp)
pre_parse(inp)
prefix = inp["prefix"]
funcs = []
for decl in inp["decls"]:
#
# HACK: gen_ir.py accidentally marks all sg_imgui_ declarations as is_dep since sg_imgui
# depends on sg_a but also starts with sg_... Fix gen_ir.py to remove this hack
#
dep_hack = False
if module == "gfx_imgui":
dep_hack = "name" in decl and decl["name"].startswith("sg_imgui_")
if not decl["is_dep"] or dep_hack:
kind = decl["kind"]
if kind == "consts":
gen_consts(decl, prefix)
elif not check_ignore(decl["name"]):
if kind == "struct":
gen_struct(decl, prefix)
elif kind == "enum":
gen_enum(decl, prefix)
elif kind == "func":
funcs.append((decl, prefix))
gen_c_funcs(funcs)
gen_rust_funcs(funcs)
def prepare():
print("=== Generating Rust bindings:")
if not os.path.isdir("sokol-rust/src/sokol"):
os.makedirs("sokol-rust/src/sokol")
if not os.path.isdir("sokol-rust/src/sokol/c"):
os.makedirs("sokol-rust/src/sokol/c")
with open("sokol-rust/src/lib.rs", "w", newline="\n") as f_outp:
f_outp.write("//! Automatically generated sokol bindings for Rust\n\n")
def gen(c_header_path, c_prefix, dep_c_prefixes):
if c_prefix not in module_names:
print(f' >> warning: skipping generation for {c_prefix} prefix...')
return
module_name = module_names[c_prefix]
c_source_path = c_source_paths[c_prefix]
print(f' {c_header_path} => {module_name}')
reset_globals()
c_path_in_project = f'sokol-rust/src/sokol/c/{os.path.basename(c_header_path)}'
shutil.copyfile(c_header_path, c_path_in_project)
ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes)
gen_module(ir, dep_c_prefixes)
output_path = f"sokol-rust/src/{ir['module']}.rs"
with open(output_path, 'w', newline='\n') as f_outp:
f_outp.write(out_lines)
with open("sokol-rust/src/lib.rs", "a", newline="\n") as f_outp:
module = ir['module']
if module in module_requires_rust_feature:
feature = module_requires_rust_feature[module]
f_outp.write(f"/// Enable feature \"{feature}\" to use\n")
f_outp.write(f"#[cfg(feature=\"{feature}\")]\n")
f_outp.write(f"pub mod {module};\n")

View file

@ -1,57 +0,0 @@
# common utility functions for all bindings generators
import re
re_1d_array = re.compile("^(?:const )?\w*\s*\*?\[\d*\]$")
re_2d_array = re.compile("^(?:const )?\w*\s*\*?\[\d*\]\[\d*\]$")
def is_1d_array_type(s):
return re_1d_array.match(s) is not None
def is_2d_array_type(s):
return re_2d_array.match(s) is not None
def is_array_type(s):
return is_1d_array_type(s) or is_2d_array_type(s)
def extract_array_type(s):
return s[:s.index('[')].strip()
def extract_array_sizes(s):
return s[s.index('['):].replace('[', ' ').replace(']', ' ').split()
def is_string_ptr(s):
return s == "const char *"
def is_const_void_ptr(s):
return s == "const void *"
def is_void_ptr(s):
return s == "void *"
def is_func_ptr(s):
return '(*)' in s
def extract_ptr_type(s):
tokens = s.split()
if tokens[0] == 'const':
return tokens[1]
else:
return tokens[0]
# PREFIX_BLA_BLUB to bla_blub
def as_lower_snake_case(s, prefix):
outp = s.lower()
if outp.startswith(prefix):
outp = outp[len(prefix):]
return outp
# prefix_bla_blub => blaBlub, PREFIX_BLA_BLUB => blaBlub
def as_lower_camel_case(s, prefix):
outp = s.lower()
if outp.startswith(prefix):
outp = outp[len(prefix):]
parts = outp.split('_')
outp = parts[0]
for part in parts[1:]:
outp += part.capitalize()
return outp

View file

@ -1,552 +0,0 @@
#-------------------------------------------------------------------------------
# Generate Zig bindings.
#
# Zig coding style:
# - types are PascalCase
# - functions are camelCase
# - otherwise snake_case
#-------------------------------------------------------------------------------
import gen_ir
import os, shutil, sys
import gen_util as util
module_names = {
'slog_': 'log',
'sg_': 'gfx',
'sapp_': 'app',
'stm_': 'time',
'saudio_': 'audio',
'sgl_': 'gl',
'sdtx_': 'debugtext',
'sshape_': 'shape',
'sglue_': 'glue',
}
c_source_paths = {
'slog_': 'sokol-zig/src/sokol/c/sokol_log.c',
'sg_': 'sokol-zig/src/sokol/c/sokol_gfx.c',
'sapp_': 'sokol-zig/src/sokol/c/sokol_app.c',
'stm_': 'sokol-zig/src/sokol/c/sokol_time.c',
'saudio_': 'sokol-zig/src/sokol/c/sokol_audio.c',
'sgl_': 'sokol-zig/src/sokol/c/sokol_gl.c',
'sdtx_': 'sokol-zig/src/sokol/c/sokol_debugtext.c',
'sshape_': 'sokol-zig/src/sokol/c/sokol_shape.c',
'sglue_': 'sokol-zig/src/sokol/c/sokol_glue.c',
}
ignores = [
'sdtx_printf',
'sdtx_vprintf',
'sg_install_trace_hooks',
'sg_trace_hooks',
]
# functions that need to be exposed as 'raw' C callbacks without a Zig wrapper function
c_callbacks = [
'slog_func'
]
# NOTE: syntax for function results: "func_name.RESULT"
overrides = {
'sgl_error': 'sgl_get_error', # 'error' is reserved in Zig
'sgl_deg': 'sgl_as_degrees',
'sgl_rad': 'sgl_as_radians',
'sg_apply_uniforms.ub_index': 'uint32_t',
'sg_draw.base_element': 'uint32_t',
'sg_draw.num_elements': 'uint32_t',
'sg_draw.num_instances': 'uint32_t',
'sshape_element_range_t.base_element': 'uint32_t',
'sshape_element_range_t.num_elements': 'uint32_t',
'sdtx_font.font_index': 'uint32_t',
'SGL_NO_ERROR': 'SGL_ERROR_NO_ERROR',
}
prim_types = {
'int': 'i32',
'bool': 'bool',
'char': 'u8',
'int8_t': 'i8',
'uint8_t': 'u8',
'int16_t': 'i16',
'uint16_t': 'u16',
'int32_t': 'i32',
'uint32_t': 'u32',
'int64_t': 'i64',
'uint64_t': 'u64',
'float': 'f32',
'double': 'f64',
'uintptr_t': 'usize',
'intptr_t': 'isize',
'size_t': 'usize'
}
prim_defaults = {
'int': '0',
'bool': 'false',
'int8_t': '0',
'uint8_t': '0',
'int16_t': '0',
'uint16_t': '0',
'int32_t': '0',
'uint32_t': '0',
'int64_t': '0',
'uint64_t': '0',
'float': '0.0',
'double': '0.0',
'uintptr_t': '0',
'intptr_t': '0',
'size_t': '0'
}
struct_types = []
enum_types = []
enum_items = {}
out_lines = ''
def reset_globals():
global struct_types
global enum_types
global enum_items
global out_lines
struct_types = []
enum_types = []
enum_items = {}
out_lines = ''
def l(s):
global out_lines
out_lines += s + '\n'
def as_zig_prim_type(s):
return prim_types[s]
# prefix_bla_blub(_t) => (dep.)BlaBlub
def as_zig_struct_type(s, prefix):
parts = s.lower().split('_')
outp = '' if s.startswith(prefix) else f'{parts[0]}.'
for part in parts[1:]:
# ignore '_t' type postfix
if (part != 't'):
outp += part.capitalize()
return outp
# prefix_bla_blub(_t) => (dep.)BlaBlub
def as_zig_enum_type(s, prefix):
parts = s.lower().split('_')
outp = '' if s.startswith(prefix) else f'{parts[0]}.'
for part in parts[1:]:
if (part != 't'):
outp += part.capitalize()
return outp
def check_override(name, default=None):
if name in overrides:
return overrides[name]
elif default is None:
return name
else:
return default
def check_ignore(name):
return name in ignores
# PREFIX_ENUM_BLA => Bla, _PREFIX_ENUM_BLA => Bla
def as_enum_item_name(s):
outp = s.lstrip('_')
parts = outp.split('_')[2:]
outp = '_'.join(parts)
if outp[0].isdigit():
outp = '_' + outp
return outp
def enum_default_item(enum_name):
return enum_items[enum_name][0]
def is_prim_type(s):
return s in prim_types
def is_struct_type(s):
return s in struct_types
def is_enum_type(s):
return s in enum_types
def is_const_prim_ptr(s):
for prim_type in prim_types:
if s == f"const {prim_type} *":
return True
return False
def is_prim_ptr(s):
for prim_type in prim_types:
if s == f"{prim_type} *":
return True
return False
def is_const_struct_ptr(s):
for struct_type in struct_types:
if s == f"const {struct_type} *":
return True
return False
def type_default_value(s):
return prim_defaults[s]
def as_c_arg_type(arg_type, prefix):
if arg_type == "void":
return "void"
elif is_prim_type(arg_type):
return as_zig_prim_type(arg_type)
elif is_struct_type(arg_type):
return as_zig_struct_type(arg_type, prefix)
elif is_enum_type(arg_type):
return as_zig_enum_type(arg_type, prefix)
elif util.is_void_ptr(arg_type):
return "?*anyopaque"
elif util.is_const_void_ptr(arg_type):
return "?*const anyopaque"
elif util.is_string_ptr(arg_type):
return "[*c]const u8"
elif is_const_struct_ptr(arg_type):
return f"[*c]const {as_zig_struct_type(util.extract_ptr_type(arg_type), prefix)}"
elif is_prim_ptr(arg_type):
return f"[*c]{as_zig_prim_type(util.extract_ptr_type(arg_type))}"
elif is_const_prim_ptr(arg_type):
return f"[*c]const {as_zig_prim_type(util.extract_ptr_type(arg_type))}"
else:
sys.exit(f"Error as_c_arg_type(): {arg_type}")
def as_zig_arg_type(arg_prefix, arg_type, prefix):
# NOTE: if arg_prefix is None, the result is used as return value
pre = "" if arg_prefix is None else arg_prefix
if arg_type == "void":
if arg_prefix is None:
return "void"
else:
return ""
elif is_prim_type(arg_type):
return pre + as_zig_prim_type(arg_type)
elif is_struct_type(arg_type):
return pre + as_zig_struct_type(arg_type, prefix)
elif is_enum_type(arg_type):
return pre + as_zig_enum_type(arg_type, prefix)
elif util.is_void_ptr(arg_type):
return pre + "?*anyopaque"
elif util.is_const_void_ptr(arg_type):
return pre + "?*const anyopaque"
elif util.is_string_ptr(arg_type):
return pre + "[:0]const u8"
elif is_const_struct_ptr(arg_type):
# not a bug, pass const structs by value
return pre + f"{as_zig_struct_type(util.extract_ptr_type(arg_type), prefix)}"
elif is_prim_ptr(arg_type):
return pre + f"*{as_zig_prim_type(util.extract_ptr_type(arg_type))}"
elif is_const_prim_ptr(arg_type):
return pre + f"*const {as_zig_prim_type(util.extract_ptr_type(arg_type))}"
else:
sys.exit(f"ERROR as_zig_arg_type(): {arg_type}")
def is_zig_string(zig_type):
return zig_type == "[:0]const u8"
# get C-style arguments of a function pointer as string
def funcptr_args_c(field_type, prefix):
tokens = field_type[field_type.index('(*)')+4:-1].split(',')
s = ""
for token in tokens:
arg_type = token.strip()
if s != "":
s += ", "
c_arg = as_c_arg_type(arg_type, prefix)
if c_arg == "void":
return ""
else:
s += c_arg
return s
# get C-style result of a function pointer as string
def funcptr_result_c(field_type):
res_type = field_type[:field_type.index('(*)')].strip()
if res_type == 'void':
return 'void'
elif is_prim_type(res_type):
return as_zig_prim_type(res_type)
elif util.is_const_void_ptr(res_type):
return '?*const anyopaque'
elif util.is_void_ptr(res_type):
return '?*anyopaque'
else:
sys.exit(f"ERROR funcptr_result_c(): {field_type}")
def funcdecl_args_c(decl, prefix):
s = ""
func_name = decl['name']
for param_decl in decl['params']:
if s != "":
s += ", "
param_name = param_decl['name']
param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type'])
s += as_c_arg_type(param_type, prefix)
return s
def funcdecl_args_zig(decl, prefix):
s = ""
func_name = decl['name']
for param_decl in decl['params']:
if s != "":
s += ", "
param_name = param_decl['name']
param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type'])
s += f"{as_zig_arg_type(f'{param_name}: ', param_type, prefix)}"
return s
def funcdecl_result_c(decl, prefix):
func_name = decl['name']
decl_type = decl['type']
result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip())
return as_c_arg_type(result_type, prefix)
def funcdecl_result_zig(decl, prefix):
func_name = decl['name']
decl_type = decl['type']
result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip())
zig_res_type = as_zig_arg_type(None, result_type, prefix)
return zig_res_type
def gen_struct(decl, prefix):
struct_name = check_override(decl['name'])
zig_type = as_zig_struct_type(struct_name, prefix)
l(f"pub const {zig_type} = extern struct {{")
for field in decl['fields']:
field_name = check_override(field['name'])
field_type = check_override(f'{struct_name}.{field_name}', default=field['type'])
if is_prim_type(field_type):
l(f" {field_name}: {as_zig_prim_type(field_type)} = {type_default_value(field_type)},")
elif is_struct_type(field_type):
l(f" {field_name}: {as_zig_struct_type(field_type, prefix)} = .{{}},")
elif is_enum_type(field_type):
l(f" {field_name}: {as_zig_enum_type(field_type, prefix)} = .{enum_default_item(field_type)},")
elif util.is_string_ptr(field_type):
l(f" {field_name}: [*c]const u8 = null,")
elif util.is_const_void_ptr(field_type):
l(f" {field_name}: ?*const anyopaque = null,")
elif util.is_void_ptr(field_type):
l(f" {field_name}: ?*anyopaque = null,")
elif is_const_prim_ptr(field_type):
l(f" {field_name}: ?[*]const {as_zig_prim_type(util.extract_ptr_type(field_type))} = null,")
elif util.is_func_ptr(field_type):
l(f" {field_name}: ?*const fn ({funcptr_args_c(field_type, prefix)}) callconv(.C) {funcptr_result_c(field_type)} = null,")
elif util.is_1d_array_type(field_type):
array_type = util.extract_array_type(field_type)
array_sizes = util.extract_array_sizes(field_type)
if is_prim_type(array_type) or is_struct_type(array_type):
if is_prim_type(array_type):
zig_type = as_zig_prim_type(array_type)
def_val = type_default_value(array_type)
elif is_struct_type(array_type):
zig_type = as_zig_struct_type(array_type, prefix)
def_val = '.{}'
elif is_enum_type(array_type):
zig_type = as_zig_enum_type(array_type, prefix)
def_val = '.{}'
else:
sys.exit(f"ERROR gen_struct is_1d_array_type: {array_type}")
t0 = f"[{array_sizes[0]}]{zig_type}"
t1 = f"[_]{zig_type}"
l(f" {field_name}: {t0} = {t1}{{{def_val}}} ** {array_sizes[0]},")
elif util.is_const_void_ptr(array_type):
l(f" {field_name}: [{array_sizes[0]}]?*const anyopaque = [_]?*const anyopaque{{null}} ** {array_sizes[0]},")
else:
sys.exit(f"ERROR gen_struct: array {field_name}: {field_type} => {array_type} [{array_sizes[0]}]")
elif util.is_2d_array_type(field_type):
array_type = util.extract_array_type(field_type)
array_sizes = util.extract_array_sizes(field_type)
if is_prim_type(array_type):
zig_type = as_zig_prim_type(array_type)
def_val = type_default_value(array_type)
elif is_struct_type(array_type):
zig_type = as_zig_struct_type(array_type, prefix)
def_val = ".{}"
else:
sys.exit(f"ERROR gen_struct is_2d_array_type: {array_type}")
t0 = f"[{array_sizes[0]}][{array_sizes[1]}]{zig_type}"
l(f" {field_name}: {t0} = [_][{array_sizes[1]}]{zig_type}{{[_]{zig_type}{{{def_val}}} ** {array_sizes[1]}}} ** {array_sizes[0]},")
else:
sys.exit(f"ERROR gen_struct: {field_name}: {field_type};")
l("};")
def gen_consts(decl, prefix):
for item in decl['items']:
item_name = check_override(item['name'])
l(f"pub const {util.as_lower_snake_case(item_name, prefix)} = {item['value']};")
def gen_enum(decl, prefix):
enum_name = check_override(decl['name'])
l(f"pub const {as_zig_enum_type(enum_name, prefix)} = enum(i32) {{")
for item in decl['items']:
item_name = as_enum_item_name(check_override(item['name']))
if item_name != "FORCE_U32":
if 'value' in item:
l(f" {item_name} = {item['value']},")
else:
l(f" {item_name},")
l("};")
def gen_func_c(decl, prefix):
l(f"pub extern fn {decl['name']}({funcdecl_args_c(decl, prefix)}) {funcdecl_result_c(decl, prefix)};")
def gen_func_zig(decl, prefix):
c_func_name = decl['name']
zig_func_name = util.as_lower_camel_case(check_override(decl['name']), prefix)
if c_func_name in c_callbacks:
# a simple forwarded C callback function
l(f"pub const {zig_func_name} = {c_func_name};")
else:
zig_res_type = funcdecl_result_zig(decl, prefix)
l(f"pub fn {zig_func_name}({funcdecl_args_zig(decl, prefix)}) {zig_res_type} {{")
if is_zig_string(zig_res_type):
# special case: convert C string to Zig string slice
s = f" return cStrToZig({c_func_name}("
elif zig_res_type != 'void':
s = f" return {c_func_name}("
else:
s = f" {c_func_name}("
for i, param_decl in enumerate(decl['params']):
if i > 0:
s += ", "
arg_name = param_decl['name']
arg_type = param_decl['type']
if is_const_struct_ptr(arg_type):
s += f"&{arg_name}"
elif util.is_string_ptr(arg_type):
s += f"@ptrCast({arg_name})"
else:
s += arg_name
if is_zig_string(zig_res_type):
s += ")"
s += ");"
l(s)
l("}")
def pre_parse(inp):
global struct_types
global enum_types
for decl in inp['decls']:
kind = decl['kind']
if kind == 'struct':
struct_types.append(decl['name'])
elif kind == 'enum':
enum_name = decl['name']
enum_types.append(enum_name)
enum_items[enum_name] = []
for item in decl['items']:
enum_items[enum_name].append(as_enum_item_name(item['name']))
def gen_imports(inp, dep_prefixes):
l('const builtin = @import("builtin");')
for dep_prefix in dep_prefixes:
dep_module_name = module_names[dep_prefix]
l(f'const {dep_prefix[:-1]} = @import("{dep_module_name}.zig");')
l('')
def gen_helpers(inp):
l('// helper function to convert a C string to a Zig string slice')
l('fn cStrToZig(c_str: [*c]const u8) [:0]const u8 {')
l(' return @import("std").mem.span(c_str);')
l('}')
if inp['prefix'] in ['sg_', 'sdtx_', 'sshape_']:
l('// helper function to convert "anything" to a Range struct')
l('pub fn asRange(val: anytype) Range {')
l(' const type_info = @typeInfo(@TypeOf(val));')
l(' switch (type_info) {')
l(' .Pointer => {')
l(' switch (type_info.Pointer.size) {')
l(' .One => return .{ .ptr = val, .size = @sizeOf(type_info.Pointer.child) },')
l(' .Slice => return .{ .ptr = val.ptr, .size = @sizeOf(type_info.Pointer.child) * val.len },')
l(' else => @compileError("FIXME: Pointer type!"),')
l(' }')
l(' },')
l(' .Struct, .Array => {')
l(' @compileError("Structs and arrays must be passed as pointers to asRange");')
l(' },')
l(' else => {')
l(' @compileError("Cannot convert to range!");')
l(' },')
l(' }')
l('}')
l('')
if inp['prefix'] == 'sdtx_':
l('// std.fmt compatible Writer')
l('pub const Writer = struct {')
l(' pub const Error = error{};')
l(' pub fn writeAll(self: Writer, bytes: []const u8) Error!void {')
l(' _ = self;')
l(' for (bytes) |byte| {')
l(' putc(byte);')
l(' }')
l(' }')
l(' pub fn writeByteNTimes(self: Writer, byte: u8, n: usize) Error!void {')
l(' _ = self;')
l(' var i: u64 = 0;')
l(' while (i < n) : (i += 1) {')
l(' putc(byte);')
l(' }')
l(' }')
l(' pub fn writeBytesNTimes(self: Writer, bytes: []const u8, n: usize) Error!void {')
l(' var i: usize = 0;')
l(' while (i < n) : (i += 1) {')
l(' try self.writeAll(bytes);')
l(' }')
l(' }')
l('};')
l('// std.fmt-style formatted print')
l('pub fn print(comptime fmt: anytype, args: anytype) void {')
l(' const writer: Writer = .{};')
l(' @import("std").fmt.format(writer, fmt, args) catch {};')
l('}')
l('')
def gen_module(inp, dep_prefixes):
l('// machine generated, do not edit')
l('')
gen_imports(inp, dep_prefixes)
gen_helpers(inp)
pre_parse(inp)
prefix = inp['prefix']
for decl in inp['decls']:
if not decl['is_dep']:
kind = decl['kind']
if kind == 'consts':
gen_consts(decl, prefix)
elif not check_ignore(decl['name']):
if kind == 'struct':
gen_struct(decl, prefix)
elif kind == 'enum':
gen_enum(decl, prefix)
elif kind == 'func':
gen_func_c(decl, prefix)
gen_func_zig(decl, prefix)
def prepare():
print('=== Generating Zig bindings:')
if not os.path.isdir('sokol-zig/src/sokol'):
os.makedirs('sokol-zig/src/sokol')
if not os.path.isdir('sokol-zig/src/sokol/c'):
os.makedirs('sokol-zig/src/sokol/c')
def gen(c_header_path, c_prefix, dep_c_prefixes):
if not c_prefix in module_names:
print(f' >> warning: skipping generation for {c_prefix} prefix...')
return
module_name = module_names[c_prefix]
c_source_path = c_source_paths[c_prefix]
print(f' {c_header_path} => {module_name}')
reset_globals()
shutil.copyfile(c_header_path, f'sokol-zig/src/sokol/c/{os.path.basename(c_header_path)}')
ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes)
gen_module(ir, dep_c_prefixes)
output_path = f"sokol-zig/src/sokol/{ir['module']}.zig"
with open(output_path, 'w', newline='\n') as f_outp:
f_outp.write(out_lines)

View file

@ -1 +0,0 @@
build/

View file

@ -1,164 +0,0 @@
cmake_minimum_required(VERSION 3.20)
project(sokol-test)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 11)
# SOKOL_GLCORE33, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU, SOKOL_DUMMY
set(SOKOL_BACKEND "SOKOL_DUMMY_BACKEND" CACHE STRING "Select 3D backend API")
set_property(CACHE SOKOL_BACKEND PROPERTY STRINGS SOKOL_GLCORE33 SOKOL_METAL SOKOL_D3D11 SOKOL_DUMMY_BACKEND)
option(SOKOL_FORCE_EGL "Force EGL with GLCORE33 backend" OFF)
option(SOKOL_FORCE_SLES "Force SLES in sokol-audio Android backend" OFF)
option(USE_ARC "Enable/disable ARC" OFF)
option(USE_ANALYZER "Enable/disable clang analyzer" OFF)
if (CMAKE_SYSTEM_NAME STREQUAL Emscripten)
set(EMSCRIPTEN 1)
elseif (CMAKE_SYSTEM_NAME STREQUAL iOS)
set(OSX_IOS 1)
elseif (CMAKE_SYSTEM_NAME STREQUAL Android)
set(ANDROID 1)
elseif (CMAKE_SYSTEM_NAME STREQUAL Linux)
set(LINUX 1)
elseif (CMAKE_SYSTEM_NAME STREQUAL Darwin)
set(OSX_MACOS 1)
elseif (CMAKE_SYSTEM_NAME STREQUAL Windows)
set(WINDOWS 1)
else()
message(FATAL_ERROR "Unrecognized CMAKE_SYSTEM_NAME")
endif()
message(">> CMAKE_CXX_COMPILER_ID: ${CMAKE_CXX_COMPILER_ID}")
message(">> SOKOL_BACKEND: ${SOKOL_BACKEND}")
message(">> SOKOL_FORCE_EGL: ${SOKOL_FORCE_EGL}")
message(">> SOKOL_FORCE_SLES: ${SOKOL_FORCE_SLES}")
if (OSX_IOS OR OSX_MACOS)
if (USE_ARC)
message(">> ObjC ARC ENABLED")
else()
message(">> ObjC ARC DISABLED")
endif()
endif()
message(">> BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
message(">> TOOLCHAIN: ${CMAKE_TOOLCHAIN_FILE}")
set(c_flags)
set(cxx_flags)
set(link_flags)
set(system_libs)
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(c_flags ${c_flags} /W4 /WX /D_CRT_SECURE_NO_WARNINGS)
set(cxx_flags ${cxx_flags} /W4 /WX /EHsc /D_CRT_SECURE_NO_WARNINGS)
else()
set(c_flags ${c_flags} -Wall -Wextra -Werror -Wsign-conversion -Wstrict-prototypes)
set(cxx_flags ${cxx_flags} -Wall -Wextra -Werror -Wsign-conversion -fno-rtti -fno-exceptions)
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(c_flags ${c_flags} -Wno-missing-field-initializers)
set(cxx_flags ${cxx_flags} -Wno-missing-field-initializers)
endif()
if (USE_ANALYZER)
# FIXME: consider using clang-tidy via CMAKE_CXX_CLANG_TIDY: https://ortogonal.github.io/cmake-clang-tidy/
# with the default settings this spams the output with irrelevant C++ coding style warnings in 3rd party libs though
message(">> Configuring for static code analysis")
set(c_flags ${c_flags} --analyze -Xanalyzer -analyzer-opt-analyze-headers)
set(cxx_flags ${cxx_flags} --analyze -Xanalyzer -analyzer-opt-analyze-headers)
set(link_flags ${link_flags} --analyze -Wno-unused-command-line-argument)
endif()
endif()
if (EMSCRIPTEN)
set(CMAKE_EXECUTABLE_SUFFIX ".html")
set(link_flags ${link_flags} -sNO_FILESYSTEM=1 -sASSERTIONS=0 -sMALLOC=emmalloc -sINITIAL_MEMORY=33554432 --closure=1)
if (SOKOL_BACKEND STREQUAL SOKOL_WGPU)
set(link_flags ${link_flags} -sUSE_WEBGPU=1)
else()
set(link_flags ${link_flags} -sUSE_WEBGL2=1)
endif()
elseif (OSX_IOS)
set(exe_type MACOSX_BUNDLE)
if (USE_ARC)
set(c_flags ${c_flags} -fobjc-arc)
set(cxx_flags ${cxx_flags} -fobjc-arc)
endif()
set(system_libs ${system_libs} "-framework Foundation" "-framework UIKit" "-framework AudioToolbox" "-framework AVFoundation")
if (SOKOL_BACKEND STREQUAL SOKOL_METAL)
set(system_libs ${system_libs} "-framework Metal" "-framework MetalKit")
else()
set(system_libs ${system_libs} "-framework OpenGLES" "-framework GLKit")
endif()
elseif (ANDROID)
if (SOKOL_FORCE_SLES)
set(system_libs ${system_libs} GLESv3 EGL OpenSLES log android)
else()
set(system_libs ${system_libs} GLESv3 EGL aaudio log android)
endif()
elseif (LINUX)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
if ((SOKOL_BACKEND STREQUAL SOKOL_GLES3) OR SOKOL_FORCE_EGL)
set(system_libs ${system_libs} X11 Xi Xcursor EGL GL asound dl m Threads::Threads)
else()
set(system_libs ${system_libs} X11 Xi Xcursor GL asound dl m Threads::Threads)
endif()
elseif (OSX_MACOS)
set(exe_type MACOSX_BUNDLE)
if (USE_ARC)
set(c_flags ${c_flags} -fobjc-arc)
set(cxx_flags ${cxx_flags} -fobjc-arc)
endif()
set(system_libs ${system_libs} "-framework QuartzCore" "-framework Cocoa" "-framework AudioToolbox")
if (SOKOL_BACKEND STREQUAL SOKOL_METAL)
set(system_libs ${system_libs} "-framework MetalKit" "-framework Metal")
else()
set(system_libs ${system_libs} "-framework OpenGL")
endif()
elseif (WINDOWS)
set(exe_type WIN32)
endif()
macro(configure_common target)
if (SOKOL_FORCE_EGL)
target_compile_definitions(${target} PRIVATE SOKOL_FORCE_EGL)
endif()
if (SOKOL_FORCE_SLES)
target_compile_definitions(${target} PRIVATE SAUDIO_ANDROID_SLES)
endif()
target_compile_definitions(${target} PRIVATE ${SOKOL_BACKEND})
target_link_options(${target} PRIVATE ${link_flags})
target_link_libraries(${target} PRIVATE ${system_libs})
target_include_directories(${target} PRIVATE ../.. ../../util)
target_include_directories(${target} PRIVATE ../ext)
endmacro()
macro(configure_osx_properties target)
if (OSX_IOS)
target_compile_definitions(${target} PRIVATE GLES_SILENCE_DEPRECATION)
endif()
set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${target}")
set_target_properties(${target} PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "${target}")
set_target_properties(${target} PROPERTIES MACOSX_BUNDLE_PRODUCT_NAME "${target}")
set_target_properties(${target} PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "${target}")
endmacro()
macro(configure_c target)
configure_common(${target})
target_compile_options(${target} PRIVATE ${c_flags})
if (OSX_MACOS OR OSX_IOS)
target_compile_options(${target} PRIVATE -x objective-c)
configure_osx_properties(${target})
endif()
endmacro()
macro(configure_cxx target)
configure_common(${target})
target_compile_options(${target} PRIVATE ${cxx_flags})
if (OSX_MACOS OR OSX_IOS)
target_compile_options(${target} PRIVATE -x objective-c++)
configure_osx_properties(${target})
endif()
endmacro()
add_subdirectory(ext)
add_subdirectory(compile)
add_subdirectory(functional)

View file

@ -1,720 +0,0 @@
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 21,
"patch": 0
},
"configurePresets": [
{
"name": "macos_gl_debug",
"generator": "Ninja",
"binaryDir": "build/macos_gl_debug",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLCORE33",
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "macos_gl_release",
"generator": "Ninja",
"binaryDir": "build/macos_gl_release",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLCORE33",
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "macos_gl_analyze",
"generator": "Ninja",
"binaryDir": "build/macos_gl_analyze",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLCORE33",
"CMAKE_BUILD_TYPE": "Debug",
"USE_ANALYZER": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "macos_metal_debug",
"generator": "Ninja",
"binaryDir": "build/macos_metal_debug",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_METAL",
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "macos_metal_release",
"generator": "Ninja",
"binaryDir": "build/macos_metal_release",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_METAL",
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "macos_metal_analyze",
"generator": "Ninja",
"binaryDir": "build/macos_metal_analyze",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_METAL",
"CMAKE_BUILD_TYPE": "Debug",
"USE_ANALYZER": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "macos_arc_gl_debug",
"generator": "Ninja",
"binaryDir": "build/macos_arc_gl_debug",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLCORE33",
"USE_ARC": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "macos_arc_gl_release",
"generator": "Ninja",
"binaryDir": "build/macos_arc_gl_release",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLCORE33",
"USE_ARC": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "macos_arc_gl_analyze",
"generator": "Ninja",
"binaryDir": "build/macos_arc_gl_analyze",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLCORE33",
"USE_ARC": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_BUILD_TYPE": "Debug",
"USE_ANALYZER": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "macos_arc_metal_debug",
"generator": "Ninja",
"binaryDir": "build/macos_arc_metal_debug",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_METAL",
"USE_ARC": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "macos_arc_metal_release",
"generator": "Ninja",
"binaryDir": "build/macos_arc_metal_release",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_METAL",
"USE_ARC": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "macos_arc_metal_analyze",
"generator": "Ninja",
"binaryDir": "build/macos_arc_metal_analyze",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_METAL",
"USE_ARC": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_BUILD_TYPE": "Debug",
"USE_ANALYZER": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "ios_gl",
"generator": "Xcode",
"binaryDir": "build/ios_gl",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLES3",
"CMAKE_SYSTEM_NAME": "iOS"
}
},
{
"name": "ios_gl_analyze",
"generator": "Ninja",
"binaryDir": "build/ios_gl_analyze",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLES3",
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_SYSTEM_NAME": "iOS",
"USE_ANALYZER": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "ios_metal",
"generator": "Xcode",
"binaryDir": "build/ios_metal",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_METAL",
"CMAKE_SYSTEM_NAME": "iOS"
}
},
{
"name": "ios_metal_analyze",
"generator": "Ninja",
"binaryDir": "build/ios_metal_analyze",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_METAL",
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_SYSTEM_NAME": "iOS",
"USE_ANALYZER": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "ios_arc_gl",
"generator": "Xcode",
"binaryDir": "build/ios_arc_gl",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLES3",
"USE_ARC": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_SYSTEM_NAME": "iOS"
}
},
{
"name": "ios_arc_gl_analyze",
"generator": "Ninja",
"binaryDir": "build/ios_arc_gl_analyze",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLES3",
"USE_ARC": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_SYSTEM_NAME": "iOS",
"USE_ANALYZER": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "ios_arc_metal",
"generator": "Xcode",
"binaryDir": "build/ios_arc_metal",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_METAL",
"USE_ARC": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_SYSTEM_NAME": "iOS"
}
},
{
"name": "ios_arc_metal_analyze",
"generator": "Ninja",
"binaryDir": "build/ios_arc_metal_analyze",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_METAL",
"USE_ARC": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_SYSTEM_NAME": "iOS",
"USE_ANALYZER": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "linux_gl_debug",
"generator": "Ninja",
"binaryDir": "build/linux_gl_debug",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLCORE33",
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "linux_gl_release",
"generator": "Ninja",
"binaryDir": "build/linux_gl_release",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLCORE33",
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "linux_gl_analyze",
"generator": "Ninja",
"binaryDir": "build/linux_gl_analyze",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLCORE33",
"CMAKE_BUILD_TYPE": "Debug",
"USE_ANALYZER": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "linux_gles3_debug",
"generator": "Ninja",
"binaryDir": "build/linux_gles3_debug",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLES3",
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "linux_gles3_release",
"generator": "Ninja",
"binaryDir": "build/linux_gles3_release",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLES3",
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "linux_gles3_analyze",
"generator": "Ninja",
"binaryDir": "build/linux_gles3_analyze",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLES3",
"CMAKE_BUILD_TYPE": "Debug",
"USE_ANALYZER": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "linux_gl_egl_debug",
"generator": "Ninja",
"binaryDir": "build/linux_gl_egl_debug",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLCORE33",
"SOKOL_FORCE_EGL": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "linux_gl_egl_release",
"generator": "Ninja",
"binaryDir": "build/linux_gl_egl_release",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLCORE33",
"SOKOL_FORCE_EGL": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "emsc_webgl2_debug",
"generator": "Ninja",
"binaryDir": "build/emsc_webgl2_debug",
"toolchainFile": "build/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLES3",
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "emsc_webgl2_release",
"generator": "Ninja",
"binaryDir": "build/emsc_webgl2_release",
"toolchainFile": "build/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLES3",
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "emsc_wgpu_debug",
"generator": "Ninja",
"binaryDir": "build/emsc_wgpu_debug",
"toolchainFile": "build/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_WGPU",
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "emsc_wgpu_release",
"generator": "Ninja",
"binaryDir": "build/emsc_wgpu_release",
"toolchainFile": "build/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_WGPU",
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "android_debug",
"generator": "Ninja",
"binaryDir": "build/android_debug",
"toolchainFile": "build/android_sdk/ndk-bundle/build/cmake/android.toolchain.cmake",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLES3",
"ANDROID_ABI": "armeabi-v7a",
"ANDROID_PLATFORM": "android-28",
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "android_release",
"generator": "Ninja",
"binaryDir": "build/android_release",
"toolchainFile": "build/android_sdk/ndk-bundle/build/cmake/android.toolchain.cmake",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLES3",
"ANDROID_ABI": "armeabi-v7a",
"ANDROID_PLATFORM": "android-28",
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "android_sles_debug",
"generator": "Ninja",
"binaryDir": "build/android_sles_debug",
"toolchainFile": "build/android_sdk/ndk-bundle/build/cmake/android.toolchain.cmake",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLES3",
"ANDROID_ABI": "armeabi-v7a",
"ANDROID_PLATFORM": "android-28",
"CMAKE_BUILD_TYPE": "Debug",
"SOKOL_FORCE_SLES": {
"type": "BOOL",
"value": "ON"
}
}
},
{
"name": "android_sles_release",
"generator": "Ninja",
"binaryDir": "build/android_sles_release",
"toolchainFile": "build/android_sdk/ndk-bundle/build/cmake/android.toolchain.cmake",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLES3",
"ANDROID_ABI": "armeabi-v7a",
"ANDROID_PLATFORM": "android-28",
"CMAKE_BUILD_TYPE": "Release",
"SOKOL_FORCE_SLES": {
"type": "BOOL",
"value": "ON"
}
}
},
{
"name": "win_gl",
"binaryDir": "build/win_gl",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLCORE33"
}
},
{
"name": "win_gl_analyze",
"generator": "Ninja",
"binaryDir": "build/win_gl_analyze",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_GLCORE33",
"CMAKE_BUILD_TYPE": "Debug",
"USE_ANALYZER": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "win_d3d11",
"binaryDir": "build/win_d3d11",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_D3D11"
}
},
{
"name": "win_d3d11_analyze",
"generator": "Ninja",
"binaryDir": "build/win_d3d11_analyze",
"cacheVariables": {
"SOKOL_BACKEND": "SOKOL_D3D11",
"CMAKE_BUILD_TYPE": "Debug",
"USE_ANALYZER": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
}
],
"buildPresets": [
{
"name": "macos_gl_debug",
"configurePreset": "macos_gl_debug"
},
{
"name": "macos_gl_release",
"configurePreset": "macos_gl_release"
},
{
"name": "macos_gl_analyze",
"configurePreset": "macos_gl_analyze"
},
{
"name": "macos_metal_debug",
"configurePreset": "macos_metal_debug"
},
{
"name": "macos_metal_release",
"configurePreset": "macos_metal_release"
},
{
"name": "macos_metal_analyze",
"configurePreset": "macos_metal_analyze"
},
{
"name": "macos_arc_gl_debug",
"configurePreset": "macos_arc_gl_debug"
},
{
"name": "macos_arc_gl_release",
"configurePreset": "macos_arc_gl_release"
},
{
"name": "macos_arc_gl_analyze",
"configurePreset": "macos_arc_gl_analyze"
},
{
"name": "macos_arc_metal_debug",
"configurePreset": "macos_arc_metal_debug"
},
{
"name": "macos_arc_metal_release",
"configurePreset": "macos_arc_metal_release"
},
{
"name": "macos_arc_metal_analyze",
"configurePreset": "macos_arc_metal_analyze"
},
{
"name": "ios_gl_debug",
"configurePreset": "ios_gl",
"configuration": "Debug",
"nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ]
},
{
"name": "ios_gl_release",
"configurePreset": "ios_gl",
"configuration": "Release",
"nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ]
},
{
"name": "ios_gl_analyze",
"configurePreset": "ios_gl_analyze"
},
{
"name": "ios_metal_debug",
"configurePreset": "ios_metal",
"configuration": "Debug",
"nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ]
},
{
"name": "ios_metal_release",
"configurePreset": "ios_metal",
"configuration": "Release",
"nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ]
},
{
"name": "ios_metal_analyze",
"configurePreset": "ios_metal_analyze"
},
{
"name": "ios_arc_gl_debug",
"configurePreset": "ios_arc_gl",
"configuration": "Debug",
"nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ]
},
{
"name": "ios_arc_gl_release",
"configurePreset": "ios_arc_gl",
"configuration": "Release",
"nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ]
},
{
"name": "ios_arc_gl_analyze",
"configurePreset": "ios_arc_gl_analyze"
},
{
"name": "ios_arc_metal_debug",
"configurePreset": "ios_arc_metal",
"configuration": "Debug",
"nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ]
},
{
"name": "ios_arc_metal_release",
"configurePreset": "ios_arc_metal",
"configuration": "Release",
"nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ]
},
{
"name": "ios_arc_metal_analyze",
"configurePreset": "ios_arc_metal_analyze"
},
{
"name": "linux_gl_debug",
"configurePreset": "linux_gl_debug"
},
{
"name": "linux_gl_release",
"configurePreset": "linux_gl_release"
},
{
"name": "linux_gl_analyze",
"configurePreset": "linux_gl_analyze"
},
{
"name": "linux_gles3_debug",
"configurePreset": "linux_gles3_debug"
},
{
"name": "linux_gles3_release",
"configurePreset": "linux_gles3_release"
},
{
"name": "linux_gles3_analyze",
"configurePreset": "linux_gles3_analyze"
},
{
"name": "linux_gl_egl_debug",
"configurePreset": "linux_gl_egl_debug"
},
{
"name": "linux_gl_egl_release",
"configurePreset": "linux_gl_egl_release"
},
{
"name": "emsc_webgl2_debug",
"configurePreset": "emsc_webgl2_debug"
},
{
"name": "emsc_webgl2_release",
"configurePreset": "emsc_webgl2_release"
},
{
"name": "emsc_wgpu_debug",
"configurePreset": "emsc_wgpu_debug"
},
{
"name": "emsc_wgpu_release",
"configurePreset": "emsc_wgpu_release"
},
{
"name": "android_debug",
"configurePreset": "android_debug"
},
{
"name": "android_release",
"configurePreset": "android_release"
},
{
"name": "android_sles_debug",
"configurePreset": "android_sles_debug"
},
{
"name": "android_sles_release",
"configurePreset": "android_sles_release"
},
{
"name": "win_gl_debug",
"configurePreset": "win_gl",
"configuration": "Debug"
},
{
"name": "win_gl_release",
"configurePreset": "win_gl",
"configuration": "Release"
},
{
"name": "win_gl_analyze",
"configurePreset": "win_gl_analyze"
},
{
"name": "win_d3d11_debug",
"configurePreset": "win_d3d11",
"configuration": "Debug"
},
{
"name": "win_d3d11_release",
"configurePreset": "win_d3d11",
"configuration": "Release"
},
{
"name": "win_d3d11_analyze",
"configurePreset": "win_d3d11_analyze"
}
]
}

View file

@ -1,6 +0,0 @@
set -e
source test_common.sh
build ios_gl_analyze ios_gl_analyze
build ios_metal_analyze ios_metal_analyze
build ios_arc_gl_analyze ios_arc_gl_analyze
build ios_arc_metal_analyze ios_arc_metal_analyze

View file

@ -1,6 +0,0 @@
#!/usr/bin/env bash
set -e
source test_common.sh
build linux_gl_analyze linux_gl_analyze
build linux_gles3_analyze linux_gles3_analyze

Some files were not shown because too many files have changed in this diff Show more