Replace tinycdb with miniz; remove unnecessary files; use sokol_main
This commit is contained in:
parent
c27e1a3071
commit
59ad64bbba
47
Makefile
47
Makefile
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
7833
source/engine/miniz.c
Normal file
File diff suppressed because it is too large
Load diff
1422
source/engine/miniz.h
Normal file
1422
source/engine/miniz.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -7,7 +7,6 @@
|
|||
#define SOKOL_GLES3
|
||||
#elif __WIN32
|
||||
#define SOKOL_D3D11
|
||||
#define SOKOL_WIN32_FORCE_MAIN
|
||||
#elif __APPLE__
|
||||
#define SOKOL_METAL
|
||||
#endif
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -1 +0,0 @@
|
|||
{"textures":[{"extensions":{"KHR_texture_basisu":{""}}:{""""},""}]}
|
BIN
source/engine/thirdparty/cgltf/fuzz/data/Box.glb
vendored
BIN
source/engine/thirdparty/cgltf/fuzz/data/Box.glb
vendored
Binary file not shown.
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
224
source/engine/thirdparty/cgltf/fuzz/gltf.dict
vendored
224
source/engine/thirdparty/cgltf/fuzz/gltf.dict
vendored
|
@ -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"\""
|
22
source/engine/thirdparty/cgltf/fuzz/main.c
vendored
22
source/engine/thirdparty/cgltf/fuzz/main.c
vendored
|
@ -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;
|
||||
}
|
|
@ -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 )
|
38
source/engine/thirdparty/cgltf/test/main.c
vendored
38
source/engine/thirdparty/cgltf/test/main.c
vendored
|
@ -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;
|
||||
}
|
76
source/engine/thirdparty/cgltf/test/test_all.py
vendored
76
source/engine/thirdparty/cgltf/test/test_all.py
vendored
|
@ -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))
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
github: mackron
|
12
source/engine/thirdparty/dr_libs/.gitignore
vendored
12
source/engine/thirdparty/dr_libs/.gitignore
vendored
|
@ -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
|
3
source/engine/thirdparty/dr_libs/.gitmodules
vendored
3
source/engine/thirdparty/dr_libs/.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
|||
[submodule "tests/external/miniaudio"]
|
||||
path = tests/external/miniaudio
|
||||
url = https://github.com/dr-soft/miniaudio.git
|
4755
source/engine/thirdparty/dr_libs/old/dr.h
vendored
4755
source/engine/thirdparty/dr_libs/old/dr.h
vendored
File diff suppressed because it is too large
Load diff
3417
source/engine/thirdparty/dr_libs/old/dr_2d.h
vendored
3417
source/engine/thirdparty/dr_libs/old/dr_2d.h
vendored
File diff suppressed because it is too large
Load diff
5307
source/engine/thirdparty/dr_libs/old/dr_audio.h
vendored
5307
source/engine/thirdparty/dr_libs/old/dr_audio.h
vendored
File diff suppressed because it is too large
Load diff
4310
source/engine/thirdparty/dr_libs/old/dr_audio_ancient.h
vendored
4310
source/engine/thirdparty/dr_libs/old/dr_audio_ancient.h
vendored
File diff suppressed because it is too large
Load diff
8051
source/engine/thirdparty/dr_libs/old/dr_fs.h
vendored
8051
source/engine/thirdparty/dr_libs/old/dr_fs.h
vendored
File diff suppressed because it is too large
Load diff
1625
source/engine/thirdparty/dr_libs/old/dr_fsw.h
vendored
1625
source/engine/thirdparty/dr_libs/old/dr_fsw.h
vendored
File diff suppressed because it is too large
Load diff
17167
source/engine/thirdparty/dr_libs/old/dr_gui.h
vendored
17167
source/engine/thirdparty/dr_libs/old/dr_gui.h
vendored
File diff suppressed because it is too large
Load diff
676
source/engine/thirdparty/dr_libs/old/dr_math.h
vendored
676
source/engine/thirdparty/dr_libs/old/dr_math.h
vendored
|
@ -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/>
|
||||
*/
|
4512
source/engine/thirdparty/dr_libs/old/dr_mtl.h
vendored
4512
source/engine/thirdparty/dr_libs/old/dr_mtl.h
vendored
File diff suppressed because it is too large
Load diff
793
source/engine/thirdparty/dr_libs/old/dr_pcx.h
vendored
793
source/engine/thirdparty/dr_libs/old/dr_pcx.h
vendored
|
@ -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/>
|
||||
*/
|
||||
|
49
source/engine/thirdparty/dr_libs/tests/README.md
vendored
49
source/engine/thirdparty/dr_libs/tests/README.md
vendored
|
@ -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.
|
|
@ -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
|
|
@ -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%
|
|
@ -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
|
|
@ -1 +0,0 @@
|
|||
gcc ./opus/dr_opus_decoding.c -o ./bin/dr_opus_decoding.exe -std=c89 -ansi -pedantic -Wall -Wextra
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
#include "dr_flac_decoding.c"
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
#include "dr_flac_test_0.c"
|
|
@ -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;
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
#define DR_MP3_IMPLEMENTATION
|
||||
#include "../../dr_mp3.h"
|
||||
|
||||
#include "../common/dr_common.c"
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
#include "dr_opus_test_0.c"
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
#include "dr_wav_decoding.c"
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
#include "dr_wav_playback.c"
|
|
@ -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;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
#include "dr_wav_test_0.c"
|
2565
source/engine/thirdparty/dr_libs/wip/dr_opus.h
vendored
2565
source/engine/thirdparty/dr_libs/wip/dr_opus.h
vendored
File diff suppressed because it is too large
Load diff
318
source/engine/thirdparty/dr_libs/wip/dr_opus_log.txt
vendored
318
source/engine/thirdparty/dr_libs/wip/dr_opus_log.txt
vendored
|
@ -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.
|
|
@ -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"
|
||||
|
72
source/engine/thirdparty/quickjs/examples/fib.c
vendored
72
source/engine/thirdparty/quickjs/examples/fib.c
vendored
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
console.log("Hello World");
|
|
@ -1,6 +0,0 @@
|
|||
/* example of JS module */
|
||||
|
||||
import { fib } from "./fib_module.js";
|
||||
|
||||
console.log("Hello World");
|
||||
console.log("fib(10)=", fib(10));
|
|
@ -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"));
|
||||
})();
|
|
@ -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);
|
||||
})();
|
|
@ -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);
|
151
source/engine/thirdparty/quickjs/examples/point.c
vendored
151
source/engine/thirdparty/quickjs/examples/point.c
vendored
|
@ -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;
|
||||
}
|
|
@ -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));
|
|
@ -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();
|
|
@ -1,8 +0,0 @@
|
|||
*.json
|
||||
*.nim
|
||||
*.zig
|
||||
__pycache__/
|
||||
sokol-nim/
|
||||
sokol-zig/
|
||||
sokol-odin/
|
||||
sokol-rust/
|
40
source/engine/thirdparty/sokol/bindgen/README.md
vendored
40
source/engine/thirdparty/sokol/bindgen/README.md
vendored
|
@ -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
|
||||
...
|
||||
```
|
|
@ -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)
|
124
source/engine/thirdparty/sokol/bindgen/gen_ir.py
vendored
124
source/engine/thirdparty/sokol/bindgen/gen_ir.py
vendored
|
@ -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
|
613
source/engine/thirdparty/sokol/bindgen/gen_nim.py
vendored
613
source/engine/thirdparty/sokol/bindgen/gen_nim.py
vendored
|
@ -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)
|
500
source/engine/thirdparty/sokol/bindgen/gen_odin.py
vendored
500
source/engine/thirdparty/sokol/bindgen/gen_odin.py
vendored
|
@ -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)
|
893
source/engine/thirdparty/sokol/bindgen/gen_rust.py
vendored
893
source/engine/thirdparty/sokol/bindgen/gen_rust.py
vendored
|
@ -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")
|
|
@ -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
|
552
source/engine/thirdparty/sokol/bindgen/gen_zig.py
vendored
552
source/engine/thirdparty/sokol/bindgen/gen_zig.py
vendored
|
@ -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)
|
|
@ -1 +0,0 @@
|
|||
build/
|
164
source/engine/thirdparty/sokol/tests/CMakeLists.txt
vendored
164
source/engine/thirdparty/sokol/tests/CMakeLists.txt
vendored
|
@ -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)
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
|
@ -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
Loading…
Reference in a new issue