main
Dominik Madarász 2023-10-13 12:59:44 +02:00
parent fb2aefca71
commit 3da7b6dc9a
43 changed files with 9189 additions and 392 deletions

View File

@ -19,8 +19,10 @@ if "%1"=="help" (
echo %0 [web] ; run Python webserver in html5 dir
echo %0 [pull] ; pull changes from origin
echo %0 [push] ; prepare for commit, stage changes and commit them
echo %0 [dstat] ; show depot changes
echo %0 [dpush] ; push depot changes
echo %0 [depot] ; sync depot changes
echo %0 [fuse] ; fuse all binaries and cooked zipfiles found together
echo %0 [git] ; prepare for commit
echo %0 [vps] ; upload the release to VPS
echo %0 [tidy] ; clean up temp files
@ -138,7 +140,7 @@ if "%1"=="git" (
rem call make.bat docs
call make.bat amalgamation
call make.bat split
rem call make.bat split
rem rd /q /s engine\split
rem md engine\split
@ -227,6 +229,17 @@ if "%1"=="join" (
exit /b
)
rem fuse binaries and zipfiles
if "%1"=="fuse" (
setlocal enableDelayedExpansion
if "%2"=="cook" (
del *.zip 2> nul 1> nul & tools\cook --cook-jobs=1
)
for %%i in (*.exe) do set "var=%%i" && if not "!var:~0,6!"=="fused_" ( copy /y !var! fused_!var! 2>nul 1>nul & tools\ark fused_!var! *.zip )
endlocal
exit /b
)
rem check memory api calls
if "%1"=="checkmem" (
findstr /RNC:"[^_xv]realloc[(]" engine\v4k.c engine\split\v4k*

@ -1 +1 @@
Subproject commit 18db6da2d1c9b1dbf954be2692f289ca54ec81d8
Subproject commit b82b5ab54dae8de6498e566956b6163b3f4c27cd

View File

@ -1902,6 +1902,8 @@ typedef struct audio_handle* audio_t;
float audio_volume_clip(float gain);
float audio_volume_stream(float gain);
float audio_volume_master(float gain);
int audio_mute(int mute);
int audio_muted();
int ui_audio();
enum AUDIO_FLAGS {
AUDIO_1CH = 0,
@ -2103,6 +2105,10 @@ typedef union json_t { char* s; double f; int64_t i; uintptr_t p; union json_t*
void* dll(const char *filename, const char *symbol);
vec3 editor_pick(float mouse_x, float mouse_y);
char* editor_path(const char *path);
float* editor_getf(const char *key);
int* editor_geti(const char *key);
char** editor_gets(const char *key);
int editor_send(const char *cmd, const char *optional_value);
char* dialog_load();
char* dialog_save();
int gizmo(vec3 *pos, vec3 *rot, vec3 *sca);
@ -3069,6 +3075,8 @@ typedef vec3i guid;
void app_crash();
void app_singleton(const char *guid);
bool app_open(const char *folder_file_or_url);
const char* app_loadfile();
const char* app_savefile();
char* callstack( int traces );
int callstackf( FILE *fp, int traces );
void die(const char *message);
@ -3140,7 +3148,7 @@ PANEL_OPEN = 1,
int ui_label2_toolbar(const char *label, const char *icons);
int ui_slider(const char *label, float *value);
int ui_slider2(const char *label, float *value, const char *caption);
int ui_contextual_end();
int ui_contextual_end(int close);
int ui_collapse_clicked();
int ui_collapse_end();
int ui_panel_end();

View File

@ -15292,9 +15292,12 @@ API int audio_play_gain_pitch( audio_t a, int flags, float gain, float pitch
API int audio_play_gain_pitch_pan( audio_t a, int flags, float gain, float pitch, float pan/*0*/ );
API int audio_stop( audio_t a );
API float audio_volume_clip(float gain); // set fx volume if gain is in [0..1] range. return current fx volume in any case
API float audio_volume_stream(float gain); // set bgm volume if gain is in [0..1] range. return current bgm volume in any case
API float audio_volume_master(float gain); // set master volume if gain is in [0..1] range. return current master volume in any case
API float audio_volume_clip(float gain); // set fx volume if gain is in [0..1] range. returns current fx volume in any case
API float audio_volume_stream(float gain); // set bgm volume if gain is in [0..1] range. returns current bgm volume in any case
API float audio_volume_master(float gain); // set master volume if gain is in [0..1] range. returns current master volume in any case
API int audio_mute(int mute);
API int audio_muted();
API int ui_audio();
@ -15683,6 +15686,11 @@ API void* dll(const char *filename, const char *symbol);
API vec3 editor_pick(float mouse_x, float mouse_y);
API char* editor_path(const char *path);
API float* editor_getf(const char *key);
API int* editor_geti(const char *key);
API char** editor_gets(const char *key);
API int editor_send(const char *cmd, const char *optional_value);
// open file dialog
API char* dialog_load();
@ -16520,7 +16528,9 @@ extern API int profiler_enabled; ///-
// @todo: nested structs? pointers in members?
// @todo: declare TYPEDEF(vec3, float[3]), TYPEDEF(mat4, vec4[4]/*float[16]*/)
#ifndef ifdef_objapi
#define ifdef_objapi(T,...) __VA_ARGS__
#endif
typedef struct reflected_t {
unsigned id, objtype;
@ -17519,9 +17529,10 @@ char* strtok_s(char* str,const char* delimiters,char** context); // tcc misses t
#if 1
#define each_substring(str, delims, keyname) \
( int len_ = strlen(str) + 1; len_; len_ = 0 ) \
for( char buf_[1024], *ptr_ = len_ < 1024 ? buf_ : REALLOC(0, len_), *lit_ = (char*)(str), *_bak = (snprintf(ptr_, len_, "%s", lit_), ptr_); _bak; _bak = 0, (ptr_ == buf_ ? 0 : REALLOC(ptr_, 0)) ) \
for( char *next_token = 0, *keyname = strtok_r(_bak, delims, &next_token); keyname; keyname = strtok_r(NULL, delims, &next_token) )
( char *str_ = (char*)(str); str_; str_ = 0 ) \
for( int len_ = strlen(str_) + 1, heap_ = len_ < 1024; len_ > 1; len_ = 0 ) \
for( char *ptr_ = (heap_ ? REALLOC(0, len_) : ALLOCA(len_)), *cpy_ = (snprintf(ptr_, len_, "%s", str_), ptr_); ptr_; (heap_ ? REALLOC(ptr_, 0) : 0), ptr_ = 0 ) \
for( char *next_token = 0, *keyname = strtok_r(cpy_, delims, &next_token); keyname; keyname = strtok_r(NULL, delims, &next_token) )
#else
#define each_substring(str, delims, keyname) \
( char** tokens_ = strsplit((str), (delims)), *keyname = 0; tokens_; tokens_ = 0) \
@ -17676,6 +17687,10 @@ API void app_crash();
API void app_singleton(const char *guid);
API bool app_open(const char *folder_file_or_url);
API const char* app_loadfile();
API const char* app_savefile();
API char* callstack( int traces ); // write callstack into a temporary string. <0 traces to invert order. do not free().
API int callstackf( FILE *fp, int traces ); // write callstack to file. <0 traces to invert order.
@ -17775,7 +17790,7 @@ API int ui_label2_float(const char *label, float value);
API int ui_label2_toolbar(const char *label, const char *icons);
API int ui_slider(const char *label, float *value);
API int ui_slider2(const char *label, float *value, const char *caption);
API int ui_contextual_end();
API int ui_contextual_end(int close);
API int ui_collapse_clicked();
API int ui_collapse_end();
API int ui_panel_end();
@ -252217,6 +252232,7 @@ unsigned file_decode(FILE* in, FILE* out, FILE *logfile) { // multi decoder
typedef struct zip zip;
zip* zip_open(const char *file, const char *mode /*r,w,a*/);
zip* zip_open_handle(FILE*fp, const char *mode /*r,w,a*/);
// only for (w)rite or (a)ppend mode
bool zip_append_file(zip*, const char *entryname, const char *comment, FILE *in, unsigned compress_level);
@ -252432,7 +252448,7 @@ int jzReadEndRecord(FILE *fp, JZEndRecord *endRecord) {
// Read ZIP file global directory. Will move within file. Returns Z_OK, or error code
// Callback is called for each record, until callback returns zero
int jzReadCentralDirectory(FILE *fp, JZEndRecord *endRecord, JZRecordCallback callback, void *user_data) {
int jzReadCentralDirectory(FILE *fp, JZEndRecord *endRecord, JZRecordCallback callback, void *user_data, void *user_data2) {
JZGlobalFileHeader fileHeader;
if(fseek(fp, endRecord->centralDirectoryOffset, SEEK_SET)) {
@ -252447,6 +252463,8 @@ int jzReadCentralDirectory(FILE *fp, JZEndRecord *endRecord, JZRecordCallback ca
return ERR(JZ_ERRNO, "Couldn't read file header #%d!", i);
}
fileHeader.relativeOffsetOflocalHeader += (uintptr_t)user_data2;
JZGlobalFileHeader *g = &fileHeader, copy = *g;
FPRINTF(stdout, "\tsignature: %u %#x\n", g->signature, g->signature); // 0x02014B50
FPRINTF(stdout, "\tversionMadeBy: %u %#x\n", g->versionMadeBy, g->versionMadeBy); // unsupported
@ -252939,11 +252957,7 @@ common:;
// zip common
zip* zip_open(const char *file, const char *mode /*r,w,a*/) {
struct stat buffer;
int exists = (stat(file, &buffer) == 0);
if( mode[0] == 'a' && !exists ) mode = "wb";
FILE *fp = fopen(file, mode[0] == 'w' ? "wb" : mode[0] == 'a' ? "a+b" : "rb");
zip* zip_open_handle(FILE *fp, const char *mode) {
if( !fp ) return ERR(NULL, "cannot open file for %s mode", mode);
zip zero = {0}, *z = (zip*)REALLOC(0, sizeof(zip));
if( !z ) return fclose(fp), ERR(NULL, "out of mem"); else *z = zero;
@ -252954,12 +252968,17 @@ zip* zip_open(const char *file, const char *mode /*r,w,a*/) {
if( mode[0] == 'r' || mode[0] == 'a' ) {
z->in = fp;
unsigned long long seekcur = ftell(z->in);
JZEndRecord jzEndRecord = {0};
if(jzReadEndRecord(fp, &jzEndRecord) != JZ_OK) {
REALLOC(z, 0);
return fclose(fp), ERR(NULL, "Couldn't read ZIP file end record.");
}
if(jzReadCentralDirectory(fp, &jzEndRecord, zip__callback, z) != JZ_OK) {
jzEndRecord.centralDirectoryOffset += seekcur;
if(jzReadCentralDirectory(fp, &jzEndRecord, zip__callback, z, (void*)(uintptr_t)seekcur ) != JZ_OK) {
REALLOC(z, 0);
return fclose(fp), ERR(NULL, "Couldn't read ZIP file central directory.");
}
@ -252987,6 +253006,14 @@ zip* zip_open(const char *file, const char *mode /*r,w,a*/) {
return fclose(fp), ERR(NULL, "Unknown open mode %s", mode);
}
zip* zip_open(const char *file, const char *mode /*r,w,a*/) {
struct stat buffer;
int exists = (stat(file, &buffer) == 0);
if( mode[0] == 'a' && !exists ) mode = "wb";
FILE *fp = fopen(file, mode[0] == 'w' ? "wb" : mode[0] == 'a' ? "a+b" : "rb");
return zip_open_handle(fp, mode);
}
void zip_close(zip* z) {
if( z->out && z->count ) {
// prepare end record
@ -253166,8 +253193,7 @@ tar *tar_open(const char *filename, const char *mode) {
*t = zero;
t->in = in;
tar__parse(in, tar__push_entry, t);
return t;
return tar__parse(in, tar__push_entry, t) ? t : NULL;
}
int tar_find(tar *t, const char *entryname) {
@ -331461,16 +331487,26 @@ char* tempvl(const char *fmt, va_list vl) {
int reqlen = sz;
#if 0
int heap = 0;
enum { STACK_ALLOC = 16384 };
static __thread char buf[STACK_ALLOC];
#else
enum { STACK_ALLOC = 128*1024 };
int heap = 1;
static __thread int STACK_ALLOC = 128*1024;
static __thread char *buf = 0; if(!buf) buf = REALLOC(0, STACK_ALLOC); // @leak
#endif
static __thread int cur = 0, len = STACK_ALLOC - 1; //printf("string stack %d/%d\n", cur, STACK_ALLOC);
static __thread int cur = 0; //printf("string stack %d/%d\n", cur, STACK_ALLOC);
assert(reqlen < STACK_ALLOC && "no stack enough, increase STACK_ALLOC variable above");
char* ptr = buf + (cur *= (cur+reqlen) < len, (cur += reqlen) - reqlen);
if( reqlen >= STACK_ALLOC ) {
tty_color(RED);
printf("no stack enough, increase STACK_ALLOC variable above (reqlen:%d) (fmt: %s)\n", reqlen, fmt);
tty_color(0);
//assert(reqlen < STACK_ALLOC);
STACK_ALLOC = reqlen * 2;
buf = REALLOC(0, STACK_ALLOC);
}
char* ptr = buf + (cur *= (cur+reqlen) < (STACK_ALLOC - 1), (cur += reqlen) - reqlen);
/*stbsp_*/vsnprintf( ptr, sz, fmt, vl );
return (char *)ptr;
@ -332366,10 +332402,17 @@ float audio_volume_master(float gain) {
mixer.gain = volume_master;
return sqrt( volume_master );
}
int audio_mute(int mute) {
static bool muted = 0; do_once muted = flag("--mute") || flag("--muted");
if( mute >= 0 && mute <= 1 ) muted = mute;
return muted;
}
int audio_muted() {
return audio_mute(-1);
}
int audio_play_gain_pitch_pan( audio_t a, int flags, float gain, float pitch, float pan ) {
static bool muted = 0; do_once muted = flag("--mute") || flag("--muted");
if(muted) return 1;
if(audio_muted()) return 1;
if( flags & AUDIO_IGNORE_MIXER_GAIN ) {
// do nothing, gain used as-is
@ -335115,7 +335158,7 @@ array(struct fs) zipscan_filter(int threadid, int numthreads) {
// skip if list item does not belong to this thread bucket
uint64_t hash = hash_str(fname);
unsigned bucket = (hash >> 32) % numthreads;
unsigned bucket = (hash /*>> 32*/) % numthreads;
if(bucket != threadid) continue;
array_push(fs, fs_now[i]);
@ -335372,10 +335415,8 @@ bool cook_start( const char *cook_ini, const char *masks, int flags ) {
char *s = strchr( ART, ';' ); if(s) *s = 0;
char *w = strchr( ART, ' ' ); if(w) *w = 0;
char *out = 0; const char *sep = "";
const char *v4k_title = getenv("V4K_TITLE");
for each_substring(ART, ",", t) {
char *tmp = file_pathabs(va("%s%s", HOME, t)) + ART_LEN;
PRINTF("ART mount+=%s\n", tmp);
for(int i = 0; tmp[i]; ++i) if(tmp[i]=='\\') tmp[i] = '/';
strcatf(&out, "%s%s%s", sep, tmp, strendi(tmp, "/") ? "" : "/");
assert( out[strlen(out) - 1] == '/' );
@ -336162,7 +336203,7 @@ bool file_delete(const char *pathfile) {
}
bool file_copy(const char *src, const char *dst) {
int ok = 0, BUFSIZE = 1 << 20; // 1 MiB
static __thread char *buffer = 0; do_once buffer = REALLOC(0, BUFSIZE);
static __thread char *buffer = 0; do_once buffer = REALLOC(0, BUFSIZE); // @leak
for( FILE *in = fopen(src, "rb"); in; fclose(in), in = 0) {
for( FILE *out = fopen(dst, "wb"); out; fclose(out), out = 0, ok = 1) {
for( int n; !!(n = fread( buffer, 1, BUFSIZE, in )); ){
@ -336487,6 +336528,8 @@ void vfs_reload() {
#if defined(EMSCRIPTEN)
vfs_mount("index.zip");
#else
// mount fused executables
vfs_mount(va("%s%s%s", app_path(), app_name(), ifdef(win32, ".exe", "")));
/* // old way
for( int i = 0; i < JOBS_MAX; ++i) {
if( vfs_mount(va(".art[%02x].zip", i)) ) continue;
@ -336506,6 +336549,34 @@ void vfs_reload() {
}
}
#define ARK1 'ArK\x1'
#define ARK1_PADDING (512 - 40) // 472
#define ARK_PRINTF(f,...) 0 // printf(f,__VA_ARGS__)
#define ARK_SWAP32(x) (x)
#define ARK_SWAP64(x) (x)
#define ARK_REALLOC REALLOC
static uint64_t ark_fget64( FILE *in ) { uint64_t v; fread( &v, 1, 8, in ); return ARK_SWAP64(v); }
void ark_list( const char *infile, zip **z ) {
for( FILE *in = fopen(infile, "rb"); in; fclose(in), in = 0 )
while(!feof(in)) {
if( 0 != (ftell(in) % ARK1_PADDING) ) fseek(in, ARK1_PADDING - (ftell(in) % ARK1_PADDING), SEEK_CUR);
ARK_PRINTF("Reading at #%d\n", (int)ftell(in));
uint64_t mark = ark_fget64(in);
if( mark != ARK1 ) continue;
uint64_t stamp = ark_fget64(in);
uint64_t datalen = ark_fget64(in);
uint64_t datahash = ark_fget64(in);
uint64_t namelen = ark_fget64(in);
*z = zip_open_handle(in, "rb");
return;
}
}
static
bool vfs_mount_(const char *path, array(struct vfs_entry) *entries) {
zip *z = NULL; tar *t = NULL; pak *p = NULL; dir *d = NULL;
@ -336515,6 +336586,7 @@ bool vfs_mount_(const char *path, array(struct vfs_entry) *entries) {
if( !is_folder ) z = zip_open(path, "rb");
if( !is_folder && !z ) t = tar_open(path, "rb");
if( !is_folder && !z && !t ) p = pak_open(path, "rb");
if( !is_folder && !z && !t && !p ) ark_list(path, &z); // last resort. try as .ark
if( !is_folder && !z && !t && !p ) return 0;
// normalize input -> "././" to ""
@ -336699,9 +336771,9 @@ if( found && *found == 0 ) {
const char *lookup_id = /*file_normalize_with_folder*/(pathfile);
// search (last item)
static char last_item[256] = { 0 };
static void *last_ptr = 0;
static int last_size = 0;
static __thread char last_item[256] = { 0 };
static __thread void *last_ptr = 0;
static __thread int last_size = 0;
if( !strcmpi(lookup_id, last_item)) {
ptr = last_ptr;
size = last_size;
@ -336858,6 +336930,10 @@ void* cache_insert(const char *pathfile, void *ptr, int size) { // append key/va
if( !MAX_CACHED_FILES ) return 0;
if( !ptr || !size ) return 0;
// keep cached files within limits
static thread_mutex_t mutex, *init = 0; if(!init) thread_mutex_init(init = &mutex);
thread_mutex_lock(&mutex);
// append to cache
archive_dir zero = {0}, *old = dir_cache;
*(dir_cache = REALLOC(0, sizeof(archive_dir))) = zero;
@ -336867,7 +336943,8 @@ void* cache_insert(const char *pathfile, void *ptr, int size) { // append key/va
dir_cache->data = REALLOC(0, size+1);
memcpy(dir_cache->data, ptr, size); size[(char*)dir_cache->data] = 0; // copy+terminator
// keep cached files within limits
void *found = 0;
static int added = 0;
if( added < MAX_CACHED_FILES ) {
++added;
@ -336876,15 +336953,18 @@ void* cache_insert(const char *pathfile, void *ptr, int size) { // append key/va
for( archive_dir *prev = dir_cache, *dir = prev; dir ; prev = dir, dir = dir->next ) {
if( !dir->next ) {
prev->next = 0; // break link
void *data = dir->data;
found = dir->data;
dir->path = REALLOC(dir->path, 0);
dir->data = REALLOC(dir->data, 0);
dir = REALLOC(dir, 0);
return data;
break;
}
}
}
return 0;
thread_mutex_unlock(&mutex);
return found;
}
// ----------------------------------------------------------------------------
@ -343657,8 +343737,8 @@ static map(unsigned, reflected_t) reflects;
static map(unsigned, array(reflected_t)) members;
void reflected_printf(reflected_t *r) {
printf("id:%u objtype:%u sz:%u name:%s info:%s addr:%p parent:%u type:%s",
r->id, r->objtype, r->sz, r->name ? r->name : "", r->info ? r->info : "", r->addr, r->parent, r->type ? r->type : "");
printf("name:%s info:'%s' id:%u objtype:%u sz:%u addr:%p parent:%u type:%s",
r->name ? r->name : "", r->info ? r->info : "", r->id, r->objtype, r->sz, r->addr, r->parent, r->type ? r->type : "");
}
void reflected_printf_all() {
for each_map_ptr(reflects, unsigned, k, reflected_t, p) {
@ -351231,6 +351311,26 @@ bool app_open(const char *link) {
return app_open_url(link);
}
const char* app_loadfile() {
const char *windowTitle = NULL;
const char *defaultPathFile = NULL;
const char *filterHints = NULL; // "image files"
const char *filters[] = { "*.*" };
int allowMultipleSelections = 0;
tinyfd_assumeGraphicDisplay = 1;
return tinyfd_openFileDialog( windowTitle, defaultPathFile, countof(filters), filters, filterHints, allowMultipleSelections );
}
const char* app_savefile() {
const char *windowTitle = NULL;
const char *defaultPathFile = NULL;
const char *filterHints = NULL; // "image files"
const char *filters[] = { "*.*" };
tinyfd_assumeGraphicDisplay = 1;
return tinyfd_saveFileDialog( windowTitle, defaultPathFile, countof(filters), filters, filterHints );
}
// ----------------------------------------------------------------------------
// tests
@ -352817,11 +352917,12 @@ int ui_collapse_end() {
int ui_contextual() {
struct nk_rect bounds = nk_widget_bounds(ui_ctx);
struct nk_rect bounds = nk_widget_bounds(ui_ctx); // = nk_window_get_bounds(ui_ctx);
bounds.y -= 25;
return ui_popups() ? 0 : nk_contextual_begin(ui_ctx, 0, nk_vec2(150, 300), bounds);
}
int ui_contextual_end() {
int ui_contextual_end(int close) {
if(close) nk_contextual_close(ui_ctx);
nk_contextual_end(ui_ctx);
return 1;
}
@ -352832,7 +352933,7 @@ int ui_submenu(const char *options) {
for( int i = 0; i < array_count(tokens) ; ++i ) {
if( ui_button_transparent(tokens[i]) ) choice = i + 1;
}
ui_contextual_end();
ui_contextual_end(0);
}
return choice;
}
@ -352966,8 +353067,10 @@ int ui_label(const char *text) {
int ui_label2(const char *label, const char *text_) {
nk_layout_row_dynamic(ui_ctx, 0, 2);
int align1 = label[0] == '>' ? (label++, NK_TEXT_RIGHT) : label[0] == '=' ? (label++, NK_TEXT_CENTERED) : label[0] == '<' ? (label++, NK_TEXT_LEFT) : NK_TEXT_LEFT;
int align2 = text_[0] == '>' ? (text_++, NK_TEXT_RIGHT) : text_[0] == '=' ? (text_++, NK_TEXT_CENTERED) : text_[0] == '<' ? (text_++, NK_TEXT_LEFT) : NK_TEXT_LEFT;
int align1 = NK_TEXT_LEFT;
int align2 = NK_TEXT_LEFT;
if( label ) align1 = label[0] == '>' ? (label++, NK_TEXT_RIGHT) : label[0] == '=' ? (label++, NK_TEXT_CENTERED) : label[0] == '<' ? (label++, NK_TEXT_LEFT) : NK_TEXT_LEFT;
if( text_ ) align2 = text_[0] == '>' ? (text_++, NK_TEXT_RIGHT) : text_[0] == '=' ? (text_++, NK_TEXT_CENTERED) : text_[0] == '<' ? (text_++, NK_TEXT_LEFT) : NK_TEXT_LEFT;
ui_label_(label, align1);
const struct nk_input *input = &ui_ctx->input;
@ -354702,12 +354805,38 @@ int window_frame_begin() {
if( may_render_stats ) {
if( has_menu ? ui_window("Debug " ICON_MD_SETTINGS, 0) : ui_panel("Debug " ICON_MD_SETTINGS, 0) ) {
#if 1
static char *filter = 0;
static int time_factor = 0;
static int playing = 0;
static int paused = 0;
int advance_frame = 0;
static int do_filter = 0;
static int do_profile = 0;
static int do_extra = 0;
char *EDITOR_TOOLBAR_ICONS = va("%s;%s;%s;%s;%s;%s;%s;%s",
do_filter ? ICON_MD_CLOSE : ICON_MD_SEARCH,
ICON_MD_PLAY_ARROW,
paused ? ICON_MD_SKIP_NEXT : ICON_MD_PAUSE,
ICON_MD_FAST_FORWARD,
ICON_MD_STOP,
ICON_MD_REPLAY,
ICON_MD_FACE,
ICON_MD_MENU
);
if( input_down(KEY_F) ) if( input(KEY_LCTRL) || input(KEY_RCTRL) ) do_filter ^= 1;
int choice = ui_toolbar(ICON_MD_SEARCH ";");
if( choice == 1 ) do_filter = 1;
int choice = ui_toolbar(EDITOR_TOOLBAR_ICONS);
if( choice == 1 ) do_filter ^= 1, do_profile = 0, do_extra = 0;
if( choice == 2 ) playing = 1, paused = 0;
if( choice == 3 ) advance_frame = !!paused, paused = 1;
if( choice == 4 ) paused = 0, time_factor = (++time_factor) % 4;
if( choice == 5 ) playing = 0, paused = 0, advance_frame = 0, time_factor = 0;
if( choice == 6 ) window_reload();
if( choice == 7 ) do_filter = 0, do_profile ^= 1, do_extra = 0;
if( choice == 8 ) do_filter = 0, do_profile = 0, do_extra ^= 1;
static char *filter = 0;
if( do_filter ) {
ui_string(ICON_MD_CLOSE " Filter " ICON_MD_SEARCH, &filter);
if( ui_label_icon_clicked_L.x > 0 && ui_label_icon_clicked_L.x <= 24 ) { // if clicked on CANCEL icon (1st icon)
@ -354717,13 +354846,44 @@ int window_frame_begin() {
if( filter ) filter[0] = '\0';
}
char *filter_mask = filter && filter[0] ? va("*%s*", filter) : "*";
#endif
static char *username = 0;
static char *userpass = 0;
if( do_profile ) {
ui_string(ICON_MD_FACE " Username", &username);
ui_string(ICON_MD_FACE " Password", &userpass);
}
if( do_extra ) {
int choice2 = ui_label2_toolbar(NULL,
ICON_MD_VIEW_IN_AR
ICON_MD_MESSAGE
ICON_MD_TIPS_AND_UPDATES ICON_MD_LIGHTBULB ICON_MD_LIGHTBULB_OUTLINE
ICON_MD_IMAGE_SEARCH ICON_MD_INSERT_PHOTO
ICON_MD_VIDEOGAME_ASSET ICON_MD_VIDEOGAME_ASSET_OFF
ICON_MD_VOLUME_UP ICON_MD_VOLUME_OFF // audio_volume_master(-1) > 0
ICON_MD_TROUBLESHOOT ICON_MD_SCHEMA ICON_MD_MENU
);
}
int open = 0, clicked_or_toggled = 0;
#define ui_collapse_filtered(lbl,id) (strmatchi(lbl,filter_mask) && ui_collapse(lbl,id))
for( int p = (open = ui_collapse_filtered(ICON_MD_FOLDER_SPECIAL " Art", "Debug.Art")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
#define EDITOR_UI_COLLAPSE(f,...) \
for( int macro(p) = (open = ui_collapse_filtered(f,__VA_ARGS__)), macro(dummy) = (clicked_or_toggled = ui_collapse_clicked()); macro(p); ui_collapse_end(), macro(p) = 0)
EDITOR_UI_COLLAPSE(ICON_MD_BUG_REPORT " Bugs 0", "Debug.Bugs") {
// @todo. parse /bugs.ini, includes saved screenshots & videos.
// @todo. screenshot include parseable level, position screen markers (same info as /bugs.ini)
}
// Art and bookmarks
EDITOR_UI_COLLAPSE(ICON_MD_FOLDER_SPECIAL " Art", "Debug.Art") {
bool inlined = true;
const char *file = 0;
if( ui_browse(&file, &inlined) ) {
@ -354731,47 +354891,78 @@ int window_frame_begin() {
app_exec(va("%s %s%s%s", ifdef(win32, "start \"\"", ifdef(osx, "open", "xdg-open")), sep, file, sep));
}
}
for( int p = (open = ui_collapse_filtered(ICON_MD_ROCKET_LAUNCH " AI", "Debug.AI")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_BOOKMARK " Bookmarks", "Debug.Bookmarks") { /* @todo */ }
// E,C,S,W
EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Scene", "Debug.Scene") {
EDITOR_UI_COLLAPSE(ICON_MD_BUBBLE_CHART/*ICON_MD_SCATTER_PLOT*/ " Entities", "Debug.Entities") { /* @todo */ }
EDITOR_UI_COLLAPSE(ICON_MD_TUNE " Components", "Debug.Components") { /* @todo */ }
EDITOR_UI_COLLAPSE(ICON_MD_PRECISION_MANUFACTURING " Systems", "Debug.Systems") { /* @todo */ }
EDITOR_UI_COLLAPSE(ICON_MD_PUBLIC " Levels", "Debug.Levels") {
//node_edit(editor.edit.down,&editor.edit);
}
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Init", "Debug.HierarchyInit") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Draw", "Debug.HierarchyDraw") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Tick", "Debug.HierarchyTick") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Edit", "Debug.HierarchyEdit") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Quit", "Debug.HierarchyQuit") { /* @todo */ }
// node_edit(&editor.init,&editor.init);
// node_edit(&editor.draw,&editor.draw);
// node_edit(&editor.tick,&editor.tick);
// node_edit(&editor.edit,&editor.edit);
// node_edit(&editor.quit,&editor.quit);
}
EDITOR_UI_COLLAPSE(ICON_MD_ROCKET_LAUNCH " AI", "Debug.AI") {
// @todo
}
for( int p = (open = ui_collapse_filtered(ICON_MD_VOLUME_UP " Audio", "Debug.Audio")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_VOLUME_UP " Audio", "Debug.Audio") {
ui_audio();
}
for( int p = (open = ui_collapse_filtered(ICON_MD_VIDEOCAM " Camera", "Debug.Camera")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_VIDEOCAM " Camera", "Debug.Camera") {
ui_camera( camera_get_active() );
}
for( int p = (open = ui_collapse_filtered(ICON_MD_BUILD " Cook", "Debug.Cook")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
// @todo
EDITOR_UI_COLLAPSE(ICON_MD_MONITOR " Display", "Debug.Display") {
// @todo: fps lock, fps target, aspect ratio, fullscreen
char *text = va("%s;%s;%s",
window_has_fullscreen() ? ICON_MD_FULLSCREEN_EXIT : ICON_MD_FULLSCREEN,
ICON_MD_PHOTO_CAMERA,
record_active() ? ICON_MD_VIDEOCAM_OFF : ICON_MD_VIDEOCAM
);
int choice = ui_toolbar(text);
if( choice == 1 ) editor_send("key_fullscreen",0);
if( choice == 2 ) editor_send("key_screenshot",0);
if( choice == 3 ) editor_send("key_record",0);
}
for( int p = (open = ui_collapse_filtered(ICON_MD_SIGNAL_CELLULAR_ALT " Network", "Debug.Network")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
// @todo
}
for( int p = (open = ui_collapse_filtered(ICON_MD_CONTENT_PASTE " Scripts", "Debug.Scripts")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
// @todo
}
for( int p = (open = ui_collapse_filtered(ICON_MD_MOVIE " FXs", "Debug.FXs")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
ui_fxs();
}
for( int p = (open = ui_collapse_filtered(ICON_MD_SPEED " Profiler", "Debug.Profiler")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
ui_profiler();
}
for( int p = (open = ui_collapse_filtered(ICON_MD_STAR_HALF " Shaders", "Debug.Shaders")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
ui_shaders();
}
for( int p = (open = ui_collapse_filtered(ICON_MD_KEYBOARD " Keyboard", "Debug.Keyboard")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_KEYBOARD " Keyboard", "Debug.Keyboard") {
ui_keyboard();
}
for( int p = (open = ui_collapse_filtered(ICON_MD_MOUSE " Mouse", "Debug.Mouse")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_MOUSE " Mouse", "Debug.Mouse") {
ui_mouse();
}
for( int p = (open = ui_collapse_filtered(ICON_MD_GAMEPAD " Gamepads", "Debug.Gamepads")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_GAMEPAD " Gamepads", "Debug.Gamepads") {
for( int q = 0; q < 4; ++q ) {
for( int r = (open = ui_collapse(va("Gamepad #%d",q+1), va("Debug.Gamepads%d",q))), dummy = (clicked_or_toggled = ui_collapse_clicked()); r; ui_collapse_end(), r = 0) {
ui_gamepad(q);
}
}
}
for( int p = (open = ui_collapse_filtered(ICON_MD_VIEW_QUILT " UI", "Debug.UI")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_CONTENT_PASTE " Scripts", "Debug.Scripts") {
// @todo
}
EDITOR_UI_COLLAPSE(ICON_MD_STAR_HALF " Shaders", "Debug.Shaders") {
ui_shaders();
}
EDITOR_UI_COLLAPSE(ICON_MD_MOVIE " FXs", "Debug.FXs") {
ui_fxs();
}
EDITOR_UI_COLLAPSE(ICON_MD_VIEW_QUILT " UI", "Debug.UI") {
int choice = ui_toolbar(ICON_MD_RECYCLING " Reset layout;" ICON_MD_SAVE_AS " Save layout");
if( choice == 1 ) ui_layout_all_reset("*");
if( choice == 2 ) file_delete(WINDOWS_INI), ui_layout_all_save_disk("*");
@ -354784,8 +354975,61 @@ int window_frame_begin() {
}
}
EDITOR_UI_COLLAPSE(ICON_MD_SAVINGS " Budgets", "Debug.Budgets") {
// @todo. // mem,fps,gfx,net,hdd,... also logging
}
EDITOR_UI_COLLAPSE(ICON_MD_WIFI/*ICON_MD_SIGNAL_CELLULAR_ALT*/ " Network 0/0 KiB", "Debug.Network") {
// @todo
// SIGNAL_CELLULAR_1_BAR SIGNAL_CELLULAR_2_BAR
}
EDITOR_UI_COLLAPSE(va(ICON_MD_SPEED " Profiler %5.2f/%d", window_fps(), (int)window_fps_target()), "Debug.Profiler") {
ui_profiler();
}
EDITOR_UI_COLLAPSE(va(ICON_MD_STORAGE " Storage %s", xstats()), "Debug.Storage") {
// @todo
}
// logic: either plug icon (power saving off) or one of the following ones (power saving on):
// if 0% batt (no batt): battery alert
// if discharging: battery levels [alert,0..6,full]
// if charging: battery charging
int battery_read = app_battery();
int battery_level = abs(battery_read);
int battery_discharging = battery_read < 0 && battery_level < 100;
const char *power_icon_label = ICON_MD_POWER " Power";
if( battery_level ) {
const char *battery_levels[9] = { // @todo: remap [7%..100%] -> [0..1] ?
ICON_MD_BATTERY_ALERT,ICON_MD_BATTERY_0_BAR,ICON_MD_BATTERY_1_BAR,
ICON_MD_BATTERY_2_BAR,ICON_MD_BATTERY_3_BAR,ICON_MD_BATTERY_4_BAR,
ICON_MD_BATTERY_5_BAR,ICON_MD_BATTERY_6_BAR,ICON_MD_BATTERY_FULL,
};
power_icon_label = (const char*)va("%s Power %d%%",
battery_discharging ? battery_levels[(int)((9-1)*clampf(battery_level/100.f,0,1))] : ICON_MD_BATTERY_CHARGING_FULL,
battery_level);
}
EDITOR_UI_COLLAPSE(power_icon_label, "Debug.Power") {
int choice = ui_toolbar( ICON_MD_POWER ";" ICON_MD_BOLT );
if( choice == 1 ) editor_send("key_battery","0");
if( choice == 2 ) editor_send("key_battery","1");
}
EDITOR_UI_COLLAPSE(ICON_MD_EXTENSION " Plugins", "Debug.Plugins") {
// @todo. include VCS
EDITOR_UI_COLLAPSE(ICON_MD_BUILD " Cook", "Debug.Cook") {
// @todo
}
}
(has_menu ? ui_window_end : ui_panel_end)();
}
API int editor_tick();
editor_tick();
}
#if 0 // deprecated
@ -356186,6 +356430,61 @@ int ui_bt(bt_t *b) {
// editing:
// nope > functions: add/rem property
#define ICON_PLAY ICON_MD_PLAY_ARROW
#define ICON_PAUSE ICON_MD_PAUSE
#define ICON_STOP ICON_MD_STOP
#define ICON_CANCEL ICON_MD_CLOSE
#define ICON_WARNING ICON_MD_WARNING
#define ICON_BROWSER ICON_MD_FOLDER_SPECIAL
#define ICON_OUTLINER ICON_MD_VIEW_IN_AR
#define ICON_BUILD ICON_MD_BUILD
#define ICON_SCREENSHOT ICON_MD_PHOTO_CAMERA
#define ICON_CAMERA_ON ICON_MD_VIDEOCAM
#define ICON_CAMERA_OFF ICON_MD_VIDEOCAM_OFF
#define ICON_GAMEPAD_ON ICON_MD_VIDEOGAME_ASSET
#define ICON_GAMEPAD_OFF ICON_MD_VIDEOGAME_ASSET_OFF
#define ICON_AUDIO_ON ICON_MD_VOLUME_UP
#define ICON_AUDIO_OFF ICON_MD_VOLUME_OFF
#define ICON_WINDOWED ICON_MD_FULLSCREEN_EXIT
#define ICON_FULLSCREEN ICON_MD_FULLSCREEN
#define ICON_LIGHTS_ON ICON_MD_LIGHTBULB
#define ICON_LIGHTS_OFF ICON_MD_LIGHTBULB_OUTLINE
#define ICON_RENDER_BASIC ICON_MD_IMAGE_SEARCH
#define ICON_RENDER_FULL ICON_MD_INSERT_PHOTO
#define ICON_SIGNAL ICON_MD_SIGNAL_CELLULAR_ALT
#define ICON_DISK ICON_MD_STORAGE
#define ICON_RATE ICON_MD_SPEED
#define ICON_CLOCK ICON_MD_TODAY
#define ICON_CHRONO ICON_MD_TIMELAPSE
#define ICON_SETTINGS ICON_MD_SETTINGS
#define ICON_LANGUAGE ICON_MD_G_TRANSLATE
#define ICON_PERSONA ICON_MD_FACE
#define ICON_SOCIAL ICON_MD_MESSAGE
#define ICON_GAME ICON_MD_ROCKET_LAUNCH
#define ICON_KEYBOARD ICON_MD_KEYBOARD
#define ICON_MOUSE ICON_MD_MOUSE
#define ICON_GAMEPAD ICON_MD_GAMEPAD
#define ICON_MONITOR ICON_MD_MONITOR
#define ICON_WIFI ICON_MD_WIFI
#define ICON_BUDGET ICON_MD_SAVINGS
#define ICON_NEW_FOLDER ICON_MD_CREATE_NEW_FOLDER
#define ICON_PLUGIN ICON_MD_EXTENSION
#define ICON_RESTART ICON_MD_REPLAY
#define ICON_QUIT ICON_MD_CLOSE
#define ICON_POWER ICON_MD_BOLT // ICON_MD_POWER
#define ICON_BATTERY_CHARGING ICON_MD_BATTERY_CHARGING_FULL
#define ICON_BATTERY_LEVELS \
ICON_MD_BATTERY_ALERT, \
ICON_MD_BATTERY_0_BAR,ICON_MD_BATTERY_1_BAR, \
ICON_MD_BATTERY_2_BAR,ICON_MD_BATTERY_3_BAR, \
ICON_MD_BATTERY_4_BAR,ICON_MD_BATTERY_5_BAR, \
ICON_MD_BATTERY_6_BAR,ICON_MD_BATTERY_FULL
char *editor_path(const char *path) {
return va("%s/%s", EDITOR, path);
}
@ -356250,6 +356549,75 @@ int editor_ui_bits8(const char *label, uint8_t *enabled) { // @to deprecate
return clicked | (copy ^ *enabled);
}
typedef union editor_var {
int i;
float f;
char *s;
} editor_var;
static map(char*,editor_var) editor_vars;
float *editor_getf(const char *key) {
if(!editor_vars) map_init_str(editor_vars);
editor_var *found = map_find_or_add(editor_vars, (char*)key, ((editor_var){0}) );
return &found->f;
}
int *editor_geti(const char *key) {
if(!editor_vars) map_init_str(editor_vars);
editor_var *found = map_find_or_add(editor_vars, (char*)key, ((editor_var){0}) );
return &found->i;
}
char **editor_gets(const char *key) {
if(!editor_vars) map_init_str(editor_vars);
editor_var *found = map_find_or_add(editor_vars, (char*)key, ((editor_var){0}) );
if(!found->s) found->s = stringf("%s","");
return &found->s;
}
int editor_send(const char *cmd, const char *optional_value) {
unsigned *gamepads = editor_geti("gamepads"); // 0 off, mask gamepad1(1), gamepad2(2), gamepad3(4), gamepad4(8)...
unsigned *renders = editor_geti("renders"); // 0 off, mask: 1=lit, 2=ddraw, 3=whiteboxes
float *speed = editor_getf("speed"); // <0 num of frames to advance, 0 paused, [0..1] slomo, 1 play regular speed, >1 fast-forward (x2/x4/x8)
unsigned *powersave = editor_geti("powersave");
char *name;
/**/ if( !strcmp(cmd, "key_quit" )) record_stop(), exit(0);
else if( !strcmp(cmd, "key_stop" )) window_pause(1);
else if( !strcmp(cmd, "key_mute" )) audio_volume_master( 1 ^ !!audio_volume_master(-1) );
else if( !strcmp(cmd, "key_pause" )) window_pause( window_has_pause() ^ 1 );
else if( !strcmp(cmd, "key_reload" )) window_reload();
else if( !strcmp(cmd, "key_battery" )) *powersave = optional_value ? !!atoi(optional_value) : *powersave ^ 1;
else if( !strcmp(cmd, "key_browser" )) ui_show("File Browser", ui_visible("File Browser") ^ true);
else if( !strcmp(cmd, "key_outliner" )) ui_show("Outliner", ui_visible("Outliner") ^ true);
else if( !strcmp(cmd, "key_record" )) if(record_active()) record_stop(); else
name = file_counter(va("%s.mp4",app_name())), window_record(name), ui_notify(va("Video capturing: %s", name), date_string());
else if( !strcmp(cmd, "key_screenshot" )) name = file_counter(va("%s.png",app_name())), window_screenshot(name), ui_notify(va("Screenshot: %s", name), date_string());
else if( !strcmp(cmd, "key_profiler" )) ui_show("Profiler", profiler_enable(ui_visible("Profiler") ^ true));
else if( !strcmp(cmd, "key_fullscreen" )) record_stop(), window_fullscreen( window_has_fullscreen() ^ 1 ); // framebuffer resizing corrupts video stream, so stop any recording beforehand
else if( !strcmp(cmd, "key_gamepad" )) *gamepads = (*gamepads & ~1u) | ((*gamepads & 1) ^ 1);
else if( !strcmp(cmd, "key_lit" )) *renders = (*renders & ~1u) | ((*renders & 1) ^ 1);
else if( !strcmp(cmd, "key_ddraw" )) *renders = (*renders & ~2u) | ((*renders & 2) ^ 2);
else alert(va("editor could not handle `%s` command.", cmd));
return 0;
}
int editor_tick() {
enum { editor_hz = 60 };
enum { editor_hz_mid = 18 };
enum { editor_hz_low = 5 };
if( *editor_geti("powersave") ) {
// adaptive framerate
int app_on_background = !window_has_focus();
int hz = app_on_background ? editor_hz_low : editor_hz_mid;
window_fps_lock( hz < 5 ? 5 : hz );
} else {
// window_fps_lock( editor_hz );
}
return 0;
}
static int gizmo__mode;
static int gizmo__active;
static int gizmo__hover;
@ -356361,26 +356729,6 @@ int gizmo(vec3 *pos, vec3 *rot, vec3 *sca) {
return modified;
}
char* dialog_load() {
const char *windowTitle = NULL;
const char *defaultPathFile = NULL;
const char *filterHints = NULL; // "image files"
const char *filters[] = { "*.*" };
int allowMultipleSelections = 0;
tinyfd_assumeGraphicDisplay = 1;
return tinyfd_openFileDialog( windowTitle, defaultPathFile, countof(filters), filters, filterHints, allowMultipleSelections );
}
char* dialog_save() {
const char *windowTitle = NULL;
const char *defaultPathFile = NULL;
const char *filterHints = NULL; // "image files"
const char *filters[] = { "*.*" };
tinyfd_assumeGraphicDisplay = 1;
return tinyfd_saveFileDialog( windowTitle, defaultPathFile, countof(filters), filters, filterHints );
}
// -- localization kit
static const char *kit_lang = "enUS", *kit_langs =
@ -356554,18 +356902,20 @@ static void v4k_pre_init() {
// window_swap();
}
static void v4k_post_init(float refresh_rate) {
int i;
// cook cleanup
cook_stop();
vfs_reload();
// init subsystems that depend on cooked assets now. ui_init() is special case and needs to be safely in single thread
ui_init();
// init more subsystems; beware of VFS mounting, as some of these may need cooked assets at this point
int i;
#if 1 // #ifdef PARALLEL_INIT
#pragma omp parallel for
#endif
for( i = 0; i <= 3; ++i) {
/**/ if( i == 0 ) ui_init(), scene_init(); // init these on thread #0, since both will be compiling shaders, and shaders need to be compiled from the very same thread than glfwMakeContextCurrent() was set up
/**/ if( i == 0 ) scene_init(); // init these on thread #0, since both will be compiling shaders, and shaders need to be compiled from the very same thread than glfwMakeContextCurrent() was set up
else if( i == 1 ) audio_init(0); // initialize audio after cooking // reasoning for this: do not launch audio threads while cooks are in progress, so there is more cpu for cooking actually
else if( i == 2 ) script_init(), kit_init(), midi_init();
else if( i == 3 ) input_init(), network_init();

View File

@ -31,6 +31,7 @@
typedef struct zip zip;
zip* zip_open(const char *file, const char *mode /*r,w,a*/);
zip* zip_open_handle(FILE*fp, const char *mode /*r,w,a*/);
// only for (w)rite or (a)ppend mode
bool zip_append_file(zip*, const char *entryname, const char *comment, FILE *in, unsigned compress_level);
@ -246,7 +247,7 @@ int jzReadEndRecord(FILE *fp, JZEndRecord *endRecord) {
// Read ZIP file global directory. Will move within file. Returns Z_OK, or error code
// Callback is called for each record, until callback returns zero
int jzReadCentralDirectory(FILE *fp, JZEndRecord *endRecord, JZRecordCallback callback, void *user_data) {
int jzReadCentralDirectory(FILE *fp, JZEndRecord *endRecord, JZRecordCallback callback, void *user_data, void *user_data2) {
JZGlobalFileHeader fileHeader;
if(fseek(fp, endRecord->centralDirectoryOffset, SEEK_SET)) {
@ -261,6 +262,8 @@ int jzReadCentralDirectory(FILE *fp, JZEndRecord *endRecord, JZRecordCallback ca
return ERR(JZ_ERRNO, "Couldn't read file header #%d!", i);
}
fileHeader.relativeOffsetOflocalHeader += (uintptr_t)user_data2;
JZGlobalFileHeader *g = &fileHeader, copy = *g;
FPRINTF(stdout, "\tsignature: %u %#x\n", g->signature, g->signature); // 0x02014B50
FPRINTF(stdout, "\tversionMadeBy: %u %#x\n", g->versionMadeBy, g->versionMadeBy); // unsupported
@ -753,11 +756,7 @@ common:;
// zip common
zip* zip_open(const char *file, const char *mode /*r,w,a*/) {
struct stat buffer;
int exists = (stat(file, &buffer) == 0);
if( mode[0] == 'a' && !exists ) mode = "wb";
FILE *fp = fopen(file, mode[0] == 'w' ? "wb" : mode[0] == 'a' ? "a+b" : "rb");
zip* zip_open_handle(FILE *fp, const char *mode) {
if( !fp ) return ERR(NULL, "cannot open file for %s mode", mode);
zip zero = {0}, *z = (zip*)REALLOC(0, sizeof(zip));
if( !z ) return fclose(fp), ERR(NULL, "out of mem"); else *z = zero;
@ -768,12 +767,17 @@ zip* zip_open(const char *file, const char *mode /*r,w,a*/) {
if( mode[0] == 'r' || mode[0] == 'a' ) {
z->in = fp;
unsigned long long seekcur = ftell(z->in);
JZEndRecord jzEndRecord = {0};
if(jzReadEndRecord(fp, &jzEndRecord) != JZ_OK) {
REALLOC(z, 0);
return fclose(fp), ERR(NULL, "Couldn't read ZIP file end record.");
}
if(jzReadCentralDirectory(fp, &jzEndRecord, zip__callback, z) != JZ_OK) {
jzEndRecord.centralDirectoryOffset += seekcur;
if(jzReadCentralDirectory(fp, &jzEndRecord, zip__callback, z, (void*)(uintptr_t)seekcur ) != JZ_OK) {
REALLOC(z, 0);
return fclose(fp), ERR(NULL, "Couldn't read ZIP file central directory.");
}
@ -801,6 +805,14 @@ zip* zip_open(const char *file, const char *mode /*r,w,a*/) {
return fclose(fp), ERR(NULL, "Unknown open mode %s", mode);
}
zip* zip_open(const char *file, const char *mode /*r,w,a*/) {
struct stat buffer;
int exists = (stat(file, &buffer) == 0);
if( mode[0] == 'a' && !exists ) mode = "wb";
FILE *fp = fopen(file, mode[0] == 'w' ? "wb" : mode[0] == 'a' ? "a+b" : "rb");
return zip_open_handle(fp, mode);
}
void zip_close(zip* z) {
if( z->out && z->count ) {
// prepare end record
@ -980,8 +992,7 @@ tar *tar_open(const char *filename, const char *mode) {
*t = zero;
t->in = in;
tar__parse(in, tar__push_entry, t);
return t;
return tar__parse(in, tar__push_entry, t) ? t : NULL;
}
int tar_find(tar *t, const char *entryname) {

View File

@ -385,10 +385,17 @@ float audio_volume_master(float gain) {
mixer.gain = volume_master;
return sqrt( volume_master );
}
int audio_mute(int mute) {
static bool muted = 0; do_once muted = flag("--mute") || flag("--muted");
if( mute >= 0 && mute <= 1 ) muted = mute;
return muted;
}
int audio_muted() {
return audio_mute(-1);
}
int audio_play_gain_pitch_pan( audio_t a, int flags, float gain, float pitch, float pan ) {
static bool muted = 0; do_once muted = flag("--mute") || flag("--muted");
if(muted) return 1;
if(audio_muted()) return 1;
if( flags & AUDIO_IGNORE_MIXER_GAIN ) {
// do nothing, gain used as-is

View File

@ -24,9 +24,12 @@ API int audio_play_gain_pitch( audio_t a, int flags, float gain, float pitch
API int audio_play_gain_pitch_pan( audio_t a, int flags, float gain, float pitch, float pan/*0*/ );
API int audio_stop( audio_t a );
API float audio_volume_clip(float gain); // set fx volume if gain is in [0..1] range. return current fx volume in any case
API float audio_volume_stream(float gain); // set bgm volume if gain is in [0..1] range. return current bgm volume in any case
API float audio_volume_master(float gain); // set master volume if gain is in [0..1] range. return current master volume in any case
API float audio_volume_clip(float gain); // set fx volume if gain is in [0..1] range. returns current fx volume in any case
API float audio_volume_stream(float gain); // set bgm volume if gain is in [0..1] range. returns current bgm volume in any case
API float audio_volume_master(float gain); // set master volume if gain is in [0..1] range. returns current master volume in any case
API int audio_mute(int mute);
API int audio_muted();
API int ui_audio();

View File

@ -364,7 +364,7 @@ array(struct fs) zipscan_filter(int threadid, int numthreads) {
// skip if list item does not belong to this thread bucket
uint64_t hash = hash_str(fname);
unsigned bucket = (hash >> 32) % numthreads;
unsigned bucket = (hash /*>> 32*/) % numthreads;
if(bucket != threadid) continue;
array_push(fs, fs_now[i]);
@ -621,10 +621,8 @@ bool cook_start( const char *cook_ini, const char *masks, int flags ) {
char *s = strchr( ART, ';' ); if(s) *s = 0;
char *w = strchr( ART, ' ' ); if(w) *w = 0;
char *out = 0; const char *sep = "";
const char *v4k_title = getenv("V4K_TITLE");
for each_substring(ART, ",", t) {
char *tmp = file_pathabs(va("%s%s", HOME, t)) + ART_LEN;
PRINTF("ART mount+=%s\n", tmp);
for(int i = 0; tmp[i]; ++i) if(tmp[i]=='\\') tmp[i] = '/';
strcatf(&out, "%s%s%s", sep, tmp, strendi(tmp, "/") ? "" : "/");
assert( out[strlen(out) - 1] == '/' );

View File

@ -1,6 +1,61 @@
// editing:
// nope > functions: add/rem property
#define ICON_PLAY ICON_MD_PLAY_ARROW
#define ICON_PAUSE ICON_MD_PAUSE
#define ICON_STOP ICON_MD_STOP
#define ICON_CANCEL ICON_MD_CLOSE
#define ICON_WARNING ICON_MD_WARNING
#define ICON_BROWSER ICON_MD_FOLDER_SPECIAL
#define ICON_OUTLINER ICON_MD_VIEW_IN_AR
#define ICON_BUILD ICON_MD_BUILD
#define ICON_SCREENSHOT ICON_MD_PHOTO_CAMERA
#define ICON_CAMERA_ON ICON_MD_VIDEOCAM
#define ICON_CAMERA_OFF ICON_MD_VIDEOCAM_OFF
#define ICON_GAMEPAD_ON ICON_MD_VIDEOGAME_ASSET
#define ICON_GAMEPAD_OFF ICON_MD_VIDEOGAME_ASSET_OFF
#define ICON_AUDIO_ON ICON_MD_VOLUME_UP
#define ICON_AUDIO_OFF ICON_MD_VOLUME_OFF
#define ICON_WINDOWED ICON_MD_FULLSCREEN_EXIT
#define ICON_FULLSCREEN ICON_MD_FULLSCREEN
#define ICON_LIGHTS_ON ICON_MD_LIGHTBULB
#define ICON_LIGHTS_OFF ICON_MD_LIGHTBULB_OUTLINE
#define ICON_RENDER_BASIC ICON_MD_IMAGE_SEARCH
#define ICON_RENDER_FULL ICON_MD_INSERT_PHOTO
#define ICON_SIGNAL ICON_MD_SIGNAL_CELLULAR_ALT
#define ICON_DISK ICON_MD_STORAGE
#define ICON_RATE ICON_MD_SPEED
#define ICON_CLOCK ICON_MD_TODAY
#define ICON_CHRONO ICON_MD_TIMELAPSE
#define ICON_SETTINGS ICON_MD_SETTINGS
#define ICON_LANGUAGE ICON_MD_G_TRANSLATE
#define ICON_PERSONA ICON_MD_FACE
#define ICON_SOCIAL ICON_MD_MESSAGE
#define ICON_GAME ICON_MD_ROCKET_LAUNCH
#define ICON_KEYBOARD ICON_MD_KEYBOARD
#define ICON_MOUSE ICON_MD_MOUSE
#define ICON_GAMEPAD ICON_MD_GAMEPAD
#define ICON_MONITOR ICON_MD_MONITOR
#define ICON_WIFI ICON_MD_WIFI
#define ICON_BUDGET ICON_MD_SAVINGS
#define ICON_NEW_FOLDER ICON_MD_CREATE_NEW_FOLDER
#define ICON_PLUGIN ICON_MD_EXTENSION
#define ICON_RESTART ICON_MD_REPLAY
#define ICON_QUIT ICON_MD_CLOSE
#define ICON_POWER ICON_MD_BOLT // ICON_MD_POWER
#define ICON_BATTERY_CHARGING ICON_MD_BATTERY_CHARGING_FULL
#define ICON_BATTERY_LEVELS \
ICON_MD_BATTERY_ALERT, \
ICON_MD_BATTERY_0_BAR,ICON_MD_BATTERY_1_BAR, \
ICON_MD_BATTERY_2_BAR,ICON_MD_BATTERY_3_BAR, \
ICON_MD_BATTERY_4_BAR,ICON_MD_BATTERY_5_BAR, \
ICON_MD_BATTERY_6_BAR,ICON_MD_BATTERY_FULL
char *editor_path(const char *path) {
return va("%s/%s", EDITOR, path);
}
@ -65,6 +120,75 @@ int editor_ui_bits8(const char *label, uint8_t *enabled) { // @to deprecate
return clicked | (copy ^ *enabled);
}
typedef union editor_var {
int i;
float f;
char *s;
} editor_var;
static map(char*,editor_var) editor_vars;
float *editor_getf(const char *key) {
if(!editor_vars) map_init_str(editor_vars);
editor_var *found = map_find_or_add(editor_vars, (char*)key, ((editor_var){0}) );
return &found->f;
}
int *editor_geti(const char *key) {
if(!editor_vars) map_init_str(editor_vars);
editor_var *found = map_find_or_add(editor_vars, (char*)key, ((editor_var){0}) );
return &found->i;
}
char **editor_gets(const char *key) {
if(!editor_vars) map_init_str(editor_vars);
editor_var *found = map_find_or_add(editor_vars, (char*)key, ((editor_var){0}) );
if(!found->s) found->s = stringf("%s","");
return &found->s;
}
int editor_send(const char *cmd, const char *optional_value) {
unsigned *gamepads = editor_geti("gamepads"); // 0 off, mask gamepad1(1), gamepad2(2), gamepad3(4), gamepad4(8)...
unsigned *renders = editor_geti("renders"); // 0 off, mask: 1=lit, 2=ddraw, 3=whiteboxes
float *speed = editor_getf("speed"); // <0 num of frames to advance, 0 paused, [0..1] slomo, 1 play regular speed, >1 fast-forward (x2/x4/x8)
unsigned *powersave = editor_geti("powersave");
char *name;
/**/ if( !strcmp(cmd, "key_quit" )) record_stop(), exit(0);
else if( !strcmp(cmd, "key_stop" )) window_pause(1);
else if( !strcmp(cmd, "key_mute" )) audio_volume_master( 1 ^ !!audio_volume_master(-1) );
else if( !strcmp(cmd, "key_pause" )) window_pause( window_has_pause() ^ 1 );
else if( !strcmp(cmd, "key_reload" )) window_reload();
else if( !strcmp(cmd, "key_battery" )) *powersave = optional_value ? !!atoi(optional_value) : *powersave ^ 1;
else if( !strcmp(cmd, "key_browser" )) ui_show("File Browser", ui_visible("File Browser") ^ true);
else if( !strcmp(cmd, "key_outliner" )) ui_show("Outliner", ui_visible("Outliner") ^ true);
else if( !strcmp(cmd, "key_record" )) if(record_active()) record_stop(); else
name = file_counter(va("%s.mp4",app_name())), window_record(name), ui_notify(va("Video capturing: %s", name), date_string());
else if( !strcmp(cmd, "key_screenshot" )) name = file_counter(va("%s.png",app_name())), window_screenshot(name), ui_notify(va("Screenshot: %s", name), date_string());
else if( !strcmp(cmd, "key_profiler" )) ui_show("Profiler", profiler_enable(ui_visible("Profiler") ^ true));
else if( !strcmp(cmd, "key_fullscreen" )) record_stop(), window_fullscreen( window_has_fullscreen() ^ 1 ); // framebuffer resizing corrupts video stream, so stop any recording beforehand
else if( !strcmp(cmd, "key_gamepad" )) *gamepads = (*gamepads & ~1u) | ((*gamepads & 1) ^ 1);
else if( !strcmp(cmd, "key_lit" )) *renders = (*renders & ~1u) | ((*renders & 1) ^ 1);
else if( !strcmp(cmd, "key_ddraw" )) *renders = (*renders & ~2u) | ((*renders & 2) ^ 2);
else alert(va("editor could not handle `%s` command.", cmd));
return 0;
}
int editor_tick() {
enum { editor_hz = 60 };
enum { editor_hz_mid = 18 };
enum { editor_hz_low = 5 };
if( *editor_geti("powersave") ) {
// adaptive framerate
int app_on_background = !window_has_focus();
int hz = app_on_background ? editor_hz_low : editor_hz_mid;
window_fps_lock( hz < 5 ? 5 : hz );
} else {
// window_fps_lock( editor_hz );
}
return 0;
}
static int gizmo__mode;
static int gizmo__active;
static int gizmo__hover;
@ -176,26 +300,6 @@ int gizmo(vec3 *pos, vec3 *rot, vec3 *sca) {
return modified;
}
char* dialog_load() {
const char *windowTitle = NULL;
const char *defaultPathFile = NULL;
const char *filterHints = NULL; // "image files"
const char *filters[] = { "*.*" };
int allowMultipleSelections = 0;
tinyfd_assumeGraphicDisplay = 1;
return tinyfd_openFileDialog( windowTitle, defaultPathFile, countof(filters), filters, filterHints, allowMultipleSelections );
}
char* dialog_save() {
const char *windowTitle = NULL;
const char *defaultPathFile = NULL;
const char *filterHints = NULL; // "image files"
const char *filters[] = { "*.*" };
tinyfd_assumeGraphicDisplay = 1;
return tinyfd_saveFileDialog( windowTitle, defaultPathFile, countof(filters), filters, filterHints );
}
// -- localization kit
static const char *kit_lang = "enUS", *kit_langs =

View File

@ -9,6 +9,11 @@
API vec3 editor_pick(float mouse_x, float mouse_y);
API char* editor_path(const char *path);
API float* editor_getf(const char *key);
API int* editor_geti(const char *key);
API char** editor_gets(const char *key);
API int editor_send(const char *cmd, const char *optional_value);
// open file dialog
API char* dialog_load();

View File

@ -257,7 +257,7 @@ bool file_delete(const char *pathfile) {
}
bool file_copy(const char *src, const char *dst) {
int ok = 0, BUFSIZE = 1 << 20; // 1 MiB
static __thread char *buffer = 0; do_once buffer = REALLOC(0, BUFSIZE);
static __thread char *buffer = 0; do_once buffer = REALLOC(0, BUFSIZE); // @leak
for( FILE *in = fopen(src, "rb"); in; fclose(in), in = 0) {
for( FILE *out = fopen(dst, "wb"); out; fclose(out), out = 0, ok = 1) {
for( int n; !!(n = fread( buffer, 1, BUFSIZE, in )); ){
@ -582,6 +582,8 @@ void vfs_reload() {
#if defined(EMSCRIPTEN)
vfs_mount("index.zip");
#else
// mount fused executables
vfs_mount(va("%s%s%s", app_path(), app_name(), ifdef(win32, ".exe", "")));
/* // old way
for( int i = 0; i < JOBS_MAX; ++i) {
if( vfs_mount(va(".art[%02x].zip", i)) ) continue;
@ -601,6 +603,34 @@ void vfs_reload() {
}
}
#define ARK1 'ArK\x1'
#define ARK1_PADDING (512 - 40) // 472
#define ARK_PRINTF(f,...) 0 // printf(f,__VA_ARGS__)
#define ARK_SWAP32(x) (x)
#define ARK_SWAP64(x) (x)
#define ARK_REALLOC REALLOC
static uint64_t ark_fget64( FILE *in ) { uint64_t v; fread( &v, 1, 8, in ); return ARK_SWAP64(v); }
void ark_list( const char *infile, zip **z ) {
for( FILE *in = fopen(infile, "rb"); in; fclose(in), in = 0 )
while(!feof(in)) {
if( 0 != (ftell(in) % ARK1_PADDING) ) fseek(in, ARK1_PADDING - (ftell(in) % ARK1_PADDING), SEEK_CUR);
ARK_PRINTF("Reading at #%d\n", (int)ftell(in));
uint64_t mark = ark_fget64(in);
if( mark != ARK1 ) continue;
uint64_t stamp = ark_fget64(in);
uint64_t datalen = ark_fget64(in);
uint64_t datahash = ark_fget64(in);
uint64_t namelen = ark_fget64(in);
*z = zip_open_handle(in, "rb");
return;
}
}
static
bool vfs_mount_(const char *path, array(struct vfs_entry) *entries) {
zip *z = NULL; tar *t = NULL; pak *p = NULL; dir *d = NULL;
@ -610,6 +640,7 @@ bool vfs_mount_(const char *path, array(struct vfs_entry) *entries) {
if( !is_folder ) z = zip_open(path, "rb");
if( !is_folder && !z ) t = tar_open(path, "rb");
if( !is_folder && !z && !t ) p = pak_open(path, "rb");
if( !is_folder && !z && !t && !p ) ark_list(path, &z); // last resort. try as .ark
if( !is_folder && !z && !t && !p ) return 0;
// normalize input -> "././" to ""
@ -794,9 +825,9 @@ if( found && *found == 0 ) {
const char *lookup_id = /*file_normalize_with_folder*/(pathfile);
// search (last item)
static char last_item[256] = { 0 };
static void *last_ptr = 0;
static int last_size = 0;
static __thread char last_item[256] = { 0 };
static __thread void *last_ptr = 0;
static __thread int last_size = 0;
if( !strcmpi(lookup_id, last_item)) {
ptr = last_ptr;
size = last_size;
@ -953,6 +984,10 @@ void* cache_insert(const char *pathfile, void *ptr, int size) { // append key/va
if( !MAX_CACHED_FILES ) return 0;
if( !ptr || !size ) return 0;
// keep cached files within limits
static thread_mutex_t mutex, *init = 0; if(!init) thread_mutex_init(init = &mutex);
thread_mutex_lock(&mutex);
// append to cache
archive_dir zero = {0}, *old = dir_cache;
*(dir_cache = REALLOC(0, sizeof(archive_dir))) = zero;
@ -962,7 +997,8 @@ void* cache_insert(const char *pathfile, void *ptr, int size) { // append key/va
dir_cache->data = REALLOC(0, size+1);
memcpy(dir_cache->data, ptr, size); size[(char*)dir_cache->data] = 0; // copy+terminator
// keep cached files within limits
void *found = 0;
static int added = 0;
if( added < MAX_CACHED_FILES ) {
++added;
@ -971,15 +1007,18 @@ void* cache_insert(const char *pathfile, void *ptr, int size) { // append key/va
for( archive_dir *prev = dir_cache, *dir = prev; dir ; prev = dir, dir = dir->next ) {
if( !dir->next ) {
prev->next = 0; // break link
void *data = dir->data;
found = dir->data;
dir->path = REALLOC(dir->path, 0);
dir->data = REALLOC(dir->data, 0);
dir = REALLOC(dir, 0);
return data;
break;
}
}
}
return 0;
thread_mutex_unlock(&mutex);
return found;
}
// ----------------------------------------------------------------------------

View File

@ -19,18 +19,20 @@ static void v4k_pre_init() {
// window_swap();
}
static void v4k_post_init(float refresh_rate) {
int i;
// cook cleanup
cook_stop();
vfs_reload();
// init subsystems that depend on cooked assets now. ui_init() is special case and needs to be safely in single thread
ui_init();
// init more subsystems; beware of VFS mounting, as some of these may need cooked assets at this point
int i;
#if 1 // #ifdef PARALLEL_INIT
#pragma omp parallel for
#endif
for( i = 0; i <= 3; ++i) {
/**/ if( i == 0 ) ui_init(), scene_init(); // init these on thread #0, since both will be compiling shaders, and shaders need to be compiled from the very same thread than glfwMakeContextCurrent() was set up
/**/ if( i == 0 ) scene_init(); // init these on thread #0, since both will be compiling shaders, and shaders need to be compiled from the very same thread than glfwMakeContextCurrent() was set up
else if( i == 1 ) audio_init(0); // initialize audio after cooking // reasoning for this: do not launch audio threads while cooks are in progress, so there is more cpu for cooking actually
else if( i == 2 ) script_init(), kit_init(), midi_init();
else if( i == 3 ) input_init(), network_init();

View File

@ -8,8 +8,8 @@ static map(unsigned, reflected_t) reflects;
static map(unsigned, array(reflected_t)) members;
void reflected_printf(reflected_t *r) {
printf("id:%u objtype:%u sz:%u name:%s info:%s addr:%p parent:%u type:%s",
r->id, r->objtype, r->sz, r->name ? r->name : "", r->info ? r->info : "", r->addr, r->parent, r->type ? r->type : "");
printf("name:%s info:'%s' id:%u objtype:%u sz:%u addr:%p parent:%u type:%s",
r->name ? r->name : "", r->info ? r->info : "", r->id, r->objtype, r->sz, r->addr, r->parent, r->type ? r->type : "");
}
void reflected_printf_all() {
for each_map_ptr(reflects, unsigned, k, reflected_t, p) {

View File

@ -4,7 +4,9 @@
// @todo: nested structs? pointers in members?
// @todo: declare TYPEDEF(vec3, float[3]), TYPEDEF(mat4, vec4[4]/*float[16]*/)
#ifndef ifdef_objapi
#define ifdef_objapi(T,...) __VA_ARGS__
#endif
typedef struct reflected_t {
unsigned id, objtype;

View File

@ -8,16 +8,26 @@ char* tempvl(const char *fmt, va_list vl) {
int reqlen = sz;
#if 0
int heap = 0;
enum { STACK_ALLOC = 16384 };
static __thread char buf[STACK_ALLOC];
#else
enum { STACK_ALLOC = 128*1024 };
int heap = 1;
static __thread int STACK_ALLOC = 128*1024;
static __thread char *buf = 0; if(!buf) buf = REALLOC(0, STACK_ALLOC); // @leak
#endif
static __thread int cur = 0, len = STACK_ALLOC - 1; //printf("string stack %d/%d\n", cur, STACK_ALLOC);
static __thread int cur = 0; //printf("string stack %d/%d\n", cur, STACK_ALLOC);
assert(reqlen < STACK_ALLOC && "no stack enough, increase STACK_ALLOC variable above");
char* ptr = buf + (cur *= (cur+reqlen) < len, (cur += reqlen) - reqlen);
if( reqlen >= STACK_ALLOC ) {
tty_color(RED);
printf("no stack enough, increase STACK_ALLOC variable above (reqlen:%d) (fmt: %s)\n", reqlen, fmt);
tty_color(0);
//assert(reqlen < STACK_ALLOC);
STACK_ALLOC = reqlen * 2;
buf = REALLOC(0, STACK_ALLOC);
}
char* ptr = buf + (cur *= (cur+reqlen) < (STACK_ALLOC - 1), (cur += reqlen) - reqlen);
/*stbsp_*/vsnprintf( ptr, sz, fmt, vl );
return (char *)ptr;

View File

@ -21,9 +21,10 @@ char* strtok_s(char* str,const char* delimiters,char** context); // tcc misses t
#if 1
#define each_substring(str, delims, keyname) \
( int len_ = strlen(str) + 1; len_; len_ = 0 ) \
for( char buf_[1024], *ptr_ = len_ < 1024 ? buf_ : REALLOC(0, len_), *lit_ = (char*)(str), *_bak = (snprintf(ptr_, len_, "%s", lit_), ptr_); _bak; _bak = 0, (ptr_ == buf_ ? 0 : REALLOC(ptr_, 0)) ) \
for( char *next_token = 0, *keyname = strtok_r(_bak, delims, &next_token); keyname; keyname = strtok_r(NULL, delims, &next_token) )
( char *str_ = (char*)(str); str_; str_ = 0 ) \
for( int len_ = strlen(str_) + 1, heap_ = len_ < 1024; len_ > 1; len_ = 0 ) \
for( char *ptr_ = (heap_ ? REALLOC(0, len_) : ALLOCA(len_)), *cpy_ = (snprintf(ptr_, len_, "%s", str_), ptr_); ptr_; (heap_ ? REALLOC(ptr_, 0) : 0), ptr_ = 0 ) \
for( char *next_token = 0, *keyname = strtok_r(cpy_, delims, &next_token); keyname; keyname = strtok_r(NULL, delims, &next_token) )
#else
#define each_substring(str, delims, keyname) \
( char** tokens_ = strsplit((str), (delims)), *keyname = 0; tokens_; tokens_ = 0) \

View File

@ -850,6 +850,26 @@ bool app_open(const char *link) {
return app_open_url(link);
}
const char* app_loadfile() {
const char *windowTitle = NULL;
const char *defaultPathFile = NULL;
const char *filterHints = NULL; // "image files"
const char *filters[] = { "*.*" };
int allowMultipleSelections = 0;
tinyfd_assumeGraphicDisplay = 1;
return tinyfd_openFileDialog( windowTitle, defaultPathFile, countof(filters), filters, filterHints, allowMultipleSelections );
}
const char* app_savefile() {
const char *windowTitle = NULL;
const char *defaultPathFile = NULL;
const char *filterHints = NULL; // "image files"
const char *filters[] = { "*.*" };
tinyfd_assumeGraphicDisplay = 1;
return tinyfd_saveFileDialog( windowTitle, defaultPathFile, countof(filters), filters, filterHints );
}
// ----------------------------------------------------------------------------
// tests

View File

@ -38,6 +38,10 @@ API void app_crash();
API void app_singleton(const char *guid);
API bool app_open(const char *folder_file_or_url);
API const char* app_loadfile();
API const char* app_savefile();
API char* callstack( int traces ); // write callstack into a temporary string. <0 traces to invert order. do not free().
API int callstackf( FILE *fp, int traces ); // write callstack to file. <0 traces to invert order.

View File

@ -1401,11 +1401,12 @@ int ui_collapse_end() {
int ui_contextual() {
struct nk_rect bounds = nk_widget_bounds(ui_ctx);
struct nk_rect bounds = nk_widget_bounds(ui_ctx); // = nk_window_get_bounds(ui_ctx);
bounds.y -= 25;
return ui_popups() ? 0 : nk_contextual_begin(ui_ctx, 0, nk_vec2(150, 300), bounds);
}
int ui_contextual_end() {
int ui_contextual_end(int close) {
if(close) nk_contextual_close(ui_ctx);
nk_contextual_end(ui_ctx);
return 1;
}
@ -1416,7 +1417,7 @@ int ui_submenu(const char *options) {
for( int i = 0; i < array_count(tokens) ; ++i ) {
if( ui_button_transparent(tokens[i]) ) choice = i + 1;
}
ui_contextual_end();
ui_contextual_end(0);
}
return choice;
}
@ -1550,8 +1551,10 @@ int ui_label(const char *text) {
int ui_label2(const char *label, const char *text_) {
nk_layout_row_dynamic(ui_ctx, 0, 2);
int align1 = label[0] == '>' ? (label++, NK_TEXT_RIGHT) : label[0] == '=' ? (label++, NK_TEXT_CENTERED) : label[0] == '<' ? (label++, NK_TEXT_LEFT) : NK_TEXT_LEFT;
int align2 = text_[0] == '>' ? (text_++, NK_TEXT_RIGHT) : text_[0] == '=' ? (text_++, NK_TEXT_CENTERED) : text_[0] == '<' ? (text_++, NK_TEXT_LEFT) : NK_TEXT_LEFT;
int align1 = NK_TEXT_LEFT;
int align2 = NK_TEXT_LEFT;
if( label ) align1 = label[0] == '>' ? (label++, NK_TEXT_RIGHT) : label[0] == '=' ? (label++, NK_TEXT_CENTERED) : label[0] == '<' ? (label++, NK_TEXT_LEFT) : NK_TEXT_LEFT;
if( text_ ) align2 = text_[0] == '>' ? (text_++, NK_TEXT_RIGHT) : text_[0] == '=' ? (text_++, NK_TEXT_CENTERED) : text_[0] == '<' ? (text_++, NK_TEXT_LEFT) : NK_TEXT_LEFT;
ui_label_(label, align1);
const struct nk_input *input = &ui_ctx->input;

View File

@ -60,7 +60,7 @@ API int ui_label2_float(const char *label, float value);
API int ui_label2_toolbar(const char *label, const char *icons);
API int ui_slider(const char *label, float *value);
API int ui_slider2(const char *label, float *value, const char *caption);
API int ui_contextual_end();
API int ui_contextual_end(int close);
API int ui_collapse_clicked();
API int ui_collapse_end();
API int ui_panel_end();

View File

@ -536,12 +536,38 @@ int window_frame_begin() {
if( may_render_stats ) {
if( has_menu ? ui_window("Debug " ICON_MD_SETTINGS, 0) : ui_panel("Debug " ICON_MD_SETTINGS, 0) ) {
#if 1
static char *filter = 0;
static int time_factor = 0;
static int playing = 0;
static int paused = 0;
int advance_frame = 0;
static int do_filter = 0;
static int do_profile = 0;
static int do_extra = 0;
char *EDITOR_TOOLBAR_ICONS = va("%s;%s;%s;%s;%s;%s;%s;%s",
do_filter ? ICON_MD_CLOSE : ICON_MD_SEARCH,
ICON_MD_PLAY_ARROW,
paused ? ICON_MD_SKIP_NEXT : ICON_MD_PAUSE,
ICON_MD_FAST_FORWARD,
ICON_MD_STOP,
ICON_MD_REPLAY,
ICON_MD_FACE,
ICON_MD_MENU
);
if( input_down(KEY_F) ) if( input(KEY_LCTRL) || input(KEY_RCTRL) ) do_filter ^= 1;
int choice = ui_toolbar(ICON_MD_SEARCH ";");
if( choice == 1 ) do_filter = 1;
int choice = ui_toolbar(EDITOR_TOOLBAR_ICONS);
if( choice == 1 ) do_filter ^= 1, do_profile = 0, do_extra = 0;
if( choice == 2 ) playing = 1, paused = 0;
if( choice == 3 ) advance_frame = !!paused, paused = 1;
if( choice == 4 ) paused = 0, time_factor = (++time_factor) % 4;
if( choice == 5 ) playing = 0, paused = 0, advance_frame = 0, time_factor = 0;
if( choice == 6 ) window_reload();
if( choice == 7 ) do_filter = 0, do_profile ^= 1, do_extra = 0;
if( choice == 8 ) do_filter = 0, do_profile = 0, do_extra ^= 1;
static char *filter = 0;
if( do_filter ) {
ui_string(ICON_MD_CLOSE " Filter " ICON_MD_SEARCH, &filter);
if( ui_label_icon_clicked_L.x > 0 && ui_label_icon_clicked_L.x <= 24 ) { // if clicked on CANCEL icon (1st icon)
@ -551,13 +577,44 @@ int window_frame_begin() {
if( filter ) filter[0] = '\0';
}
char *filter_mask = filter && filter[0] ? va("*%s*", filter) : "*";
#endif
static char *username = 0;
static char *userpass = 0;
if( do_profile ) {
ui_string(ICON_MD_FACE " Username", &username);
ui_string(ICON_MD_FACE " Password", &userpass);
}
if( do_extra ) {
int choice2 = ui_label2_toolbar(NULL,
ICON_MD_VIEW_IN_AR
ICON_MD_MESSAGE
ICON_MD_TIPS_AND_UPDATES ICON_MD_LIGHTBULB ICON_MD_LIGHTBULB_OUTLINE
ICON_MD_IMAGE_SEARCH ICON_MD_INSERT_PHOTO
ICON_MD_VIDEOGAME_ASSET ICON_MD_VIDEOGAME_ASSET_OFF
ICON_MD_VOLUME_UP ICON_MD_VOLUME_OFF // audio_volume_master(-1) > 0
ICON_MD_TROUBLESHOOT ICON_MD_SCHEMA ICON_MD_MENU
);
}
int open = 0, clicked_or_toggled = 0;
#define ui_collapse_filtered(lbl,id) (strmatchi(lbl,filter_mask) && ui_collapse(lbl,id))
for( int p = (open = ui_collapse_filtered(ICON_MD_FOLDER_SPECIAL " Art", "Debug.Art")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
#define EDITOR_UI_COLLAPSE(f,...) \
for( int macro(p) = (open = ui_collapse_filtered(f,__VA_ARGS__)), macro(dummy) = (clicked_or_toggled = ui_collapse_clicked()); macro(p); ui_collapse_end(), macro(p) = 0)
EDITOR_UI_COLLAPSE(ICON_MD_BUG_REPORT " Bugs 0", "Debug.Bugs") {
// @todo. parse /bugs.ini, includes saved screenshots & videos.
// @todo. screenshot include parseable level, position screen markers (same info as /bugs.ini)
}
// Art and bookmarks
EDITOR_UI_COLLAPSE(ICON_MD_FOLDER_SPECIAL " Art", "Debug.Art") {
bool inlined = true;
const char *file = 0;
if( ui_browse(&file, &inlined) ) {
@ -565,47 +622,78 @@ int window_frame_begin() {
app_exec(va("%s %s%s%s", ifdef(win32, "start \"\"", ifdef(osx, "open", "xdg-open")), sep, file, sep));
}
}
for( int p = (open = ui_collapse_filtered(ICON_MD_ROCKET_LAUNCH " AI", "Debug.AI")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_BOOKMARK " Bookmarks", "Debug.Bookmarks") { /* @todo */ }
// E,C,S,W
EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Scene", "Debug.Scene") {
EDITOR_UI_COLLAPSE(ICON_MD_BUBBLE_CHART/*ICON_MD_SCATTER_PLOT*/ " Entities", "Debug.Entities") { /* @todo */ }
EDITOR_UI_COLLAPSE(ICON_MD_TUNE " Components", "Debug.Components") { /* @todo */ }
EDITOR_UI_COLLAPSE(ICON_MD_PRECISION_MANUFACTURING " Systems", "Debug.Systems") { /* @todo */ }
EDITOR_UI_COLLAPSE(ICON_MD_PUBLIC " Levels", "Debug.Levels") {
//node_edit(editor.edit.down,&editor.edit);
}
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Init", "Debug.HierarchyInit") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Draw", "Debug.HierarchyDraw") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Tick", "Debug.HierarchyTick") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Edit", "Debug.HierarchyEdit") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Quit", "Debug.HierarchyQuit") { /* @todo */ }
// node_edit(&editor.init,&editor.init);
// node_edit(&editor.draw,&editor.draw);
// node_edit(&editor.tick,&editor.tick);
// node_edit(&editor.edit,&editor.edit);
// node_edit(&editor.quit,&editor.quit);
}
EDITOR_UI_COLLAPSE(ICON_MD_ROCKET_LAUNCH " AI", "Debug.AI") {
// @todo
}
for( int p = (open = ui_collapse_filtered(ICON_MD_VOLUME_UP " Audio", "Debug.Audio")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_VOLUME_UP " Audio", "Debug.Audio") {
ui_audio();
}
for( int p = (open = ui_collapse_filtered(ICON_MD_VIDEOCAM " Camera", "Debug.Camera")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_VIDEOCAM " Camera", "Debug.Camera") {
ui_camera( camera_get_active() );
}
for( int p = (open = ui_collapse_filtered(ICON_MD_BUILD " Cook", "Debug.Cook")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
// @todo
EDITOR_UI_COLLAPSE(ICON_MD_MONITOR " Display", "Debug.Display") {
// @todo: fps lock, fps target, aspect ratio, fullscreen
char *text = va("%s;%s;%s",
window_has_fullscreen() ? ICON_MD_FULLSCREEN_EXIT : ICON_MD_FULLSCREEN,
ICON_MD_PHOTO_CAMERA,
record_active() ? ICON_MD_VIDEOCAM_OFF : ICON_MD_VIDEOCAM
);
int choice = ui_toolbar(text);
if( choice == 1 ) editor_send("key_fullscreen",0);
if( choice == 2 ) editor_send("key_screenshot",0);
if( choice == 3 ) editor_send("key_record",0);
}
for( int p = (open = ui_collapse_filtered(ICON_MD_SIGNAL_CELLULAR_ALT " Network", "Debug.Network")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
// @todo
}
for( int p = (open = ui_collapse_filtered(ICON_MD_CONTENT_PASTE " Scripts", "Debug.Scripts")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
// @todo
}
for( int p = (open = ui_collapse_filtered(ICON_MD_MOVIE " FXs", "Debug.FXs")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
ui_fxs();
}
for( int p = (open = ui_collapse_filtered(ICON_MD_SPEED " Profiler", "Debug.Profiler")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
ui_profiler();
}
for( int p = (open = ui_collapse_filtered(ICON_MD_STAR_HALF " Shaders", "Debug.Shaders")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
ui_shaders();
}
for( int p = (open = ui_collapse_filtered(ICON_MD_KEYBOARD " Keyboard", "Debug.Keyboard")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_KEYBOARD " Keyboard", "Debug.Keyboard") {
ui_keyboard();
}
for( int p = (open = ui_collapse_filtered(ICON_MD_MOUSE " Mouse", "Debug.Mouse")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_MOUSE " Mouse", "Debug.Mouse") {
ui_mouse();
}
for( int p = (open = ui_collapse_filtered(ICON_MD_GAMEPAD " Gamepads", "Debug.Gamepads")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_GAMEPAD " Gamepads", "Debug.Gamepads") {
for( int q = 0; q < 4; ++q ) {
for( int r = (open = ui_collapse(va("Gamepad #%d",q+1), va("Debug.Gamepads%d",q))), dummy = (clicked_or_toggled = ui_collapse_clicked()); r; ui_collapse_end(), r = 0) {
ui_gamepad(q);
}
}
}
for( int p = (open = ui_collapse_filtered(ICON_MD_VIEW_QUILT " UI", "Debug.UI")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_CONTENT_PASTE " Scripts", "Debug.Scripts") {
// @todo
}
EDITOR_UI_COLLAPSE(ICON_MD_STAR_HALF " Shaders", "Debug.Shaders") {
ui_shaders();
}
EDITOR_UI_COLLAPSE(ICON_MD_MOVIE " FXs", "Debug.FXs") {
ui_fxs();
}
EDITOR_UI_COLLAPSE(ICON_MD_VIEW_QUILT " UI", "Debug.UI") {
int choice = ui_toolbar(ICON_MD_RECYCLING " Reset layout;" ICON_MD_SAVE_AS " Save layout");
if( choice == 1 ) ui_layout_all_reset("*");
if( choice == 2 ) file_delete(WINDOWS_INI), ui_layout_all_save_disk("*");
@ -618,8 +706,61 @@ int window_frame_begin() {
}
}
EDITOR_UI_COLLAPSE(ICON_MD_SAVINGS " Budgets", "Debug.Budgets") {
// @todo. // mem,fps,gfx,net,hdd,... also logging
}
EDITOR_UI_COLLAPSE(ICON_MD_WIFI/*ICON_MD_SIGNAL_CELLULAR_ALT*/ " Network 0/0 KiB", "Debug.Network") {
// @todo
// SIGNAL_CELLULAR_1_BAR SIGNAL_CELLULAR_2_BAR
}
EDITOR_UI_COLLAPSE(va(ICON_MD_SPEED " Profiler %5.2f/%d", window_fps(), (int)window_fps_target()), "Debug.Profiler") {
ui_profiler();
}
EDITOR_UI_COLLAPSE(va(ICON_MD_STORAGE " Storage %s", xstats()), "Debug.Storage") {
// @todo
}
// logic: either plug icon (power saving off) or one of the following ones (power saving on):
// if 0% batt (no batt): battery alert
// if discharging: battery levels [alert,0..6,full]
// if charging: battery charging
int battery_read = app_battery();
int battery_level = abs(battery_read);
int battery_discharging = battery_read < 0 && battery_level < 100;
const char *power_icon_label = ICON_MD_POWER " Power";
if( battery_level ) {
const char *battery_levels[9] = { // @todo: remap [7%..100%] -> [0..1] ?
ICON_MD_BATTERY_ALERT,ICON_MD_BATTERY_0_BAR,ICON_MD_BATTERY_1_BAR,
ICON_MD_BATTERY_2_BAR,ICON_MD_BATTERY_3_BAR,ICON_MD_BATTERY_4_BAR,
ICON_MD_BATTERY_5_BAR,ICON_MD_BATTERY_6_BAR,ICON_MD_BATTERY_FULL,
};
power_icon_label = (const char*)va("%s Power %d%%",
battery_discharging ? battery_levels[(int)((9-1)*clampf(battery_level/100.f,0,1))] : ICON_MD_BATTERY_CHARGING_FULL,
battery_level);
}
EDITOR_UI_COLLAPSE(power_icon_label, "Debug.Power") {
int choice = ui_toolbar( ICON_MD_POWER ";" ICON_MD_BOLT );
if( choice == 1 ) editor_send("key_battery","0");
if( choice == 2 ) editor_send("key_battery","1");
}
EDITOR_UI_COLLAPSE(ICON_MD_EXTENSION " Plugins", "Debug.Plugins") {
// @todo. include VCS
EDITOR_UI_COLLAPSE(ICON_MD_BUILD " Cook", "Debug.Cook") {
// @todo
}
}
(has_menu ? ui_window_end : ui_panel_end)();
}
API int editor_tick();
editor_tick();
}
#if 0 // deprecated

View File

@ -234245,6 +234245,7 @@ unsigned file_decode(FILE* in, FILE* out, FILE *logfile) { // multi decoder
typedef struct zip zip;
zip* zip_open(const char *file, const char *mode /*r,w,a*/);
zip* zip_open_handle(FILE*fp, const char *mode /*r,w,a*/);
// only for (w)rite or (a)ppend mode
bool zip_append_file(zip*, const char *entryname, const char *comment, FILE *in, unsigned compress_level);
@ -234460,7 +234461,7 @@ int jzReadEndRecord(FILE *fp, JZEndRecord *endRecord) {
// Read ZIP file global directory. Will move within file. Returns Z_OK, or error code
// Callback is called for each record, until callback returns zero
int jzReadCentralDirectory(FILE *fp, JZEndRecord *endRecord, JZRecordCallback callback, void *user_data) {
int jzReadCentralDirectory(FILE *fp, JZEndRecord *endRecord, JZRecordCallback callback, void *user_data, void *user_data2) {
JZGlobalFileHeader fileHeader;
if(fseek(fp, endRecord->centralDirectoryOffset, SEEK_SET)) {
@ -234475,6 +234476,8 @@ int jzReadCentralDirectory(FILE *fp, JZEndRecord *endRecord, JZRecordCallback ca
return ERR(JZ_ERRNO, "Couldn't read file header #%d!", i);
}
fileHeader.relativeOffsetOflocalHeader += (uintptr_t)user_data2;
JZGlobalFileHeader *g = &fileHeader, copy = *g;
FPRINTF(stdout, "\tsignature: %u %#x\n", g->signature, g->signature); // 0x02014B50
FPRINTF(stdout, "\tversionMadeBy: %u %#x\n", g->versionMadeBy, g->versionMadeBy); // unsupported
@ -234967,11 +234970,7 @@ common:;
// zip common
zip* zip_open(const char *file, const char *mode /*r,w,a*/) {
struct stat buffer;
int exists = (stat(file, &buffer) == 0);
if( mode[0] == 'a' && !exists ) mode = "wb";
FILE *fp = fopen(file, mode[0] == 'w' ? "wb" : mode[0] == 'a' ? "a+b" : "rb");
zip* zip_open_handle(FILE *fp, const char *mode) {
if( !fp ) return ERR(NULL, "cannot open file for %s mode", mode);
zip zero = {0}, *z = (zip*)REALLOC(0, sizeof(zip));
if( !z ) return fclose(fp), ERR(NULL, "out of mem"); else *z = zero;
@ -234982,12 +234981,17 @@ zip* zip_open(const char *file, const char *mode /*r,w,a*/) {
if( mode[0] == 'r' || mode[0] == 'a' ) {
z->in = fp;
unsigned long long seekcur = ftell(z->in);
JZEndRecord jzEndRecord = {0};
if(jzReadEndRecord(fp, &jzEndRecord) != JZ_OK) {
REALLOC(z, 0);
return fclose(fp), ERR(NULL, "Couldn't read ZIP file end record.");
}
if(jzReadCentralDirectory(fp, &jzEndRecord, zip__callback, z) != JZ_OK) {
jzEndRecord.centralDirectoryOffset += seekcur;
if(jzReadCentralDirectory(fp, &jzEndRecord, zip__callback, z, (void*)(uintptr_t)seekcur ) != JZ_OK) {
REALLOC(z, 0);
return fclose(fp), ERR(NULL, "Couldn't read ZIP file central directory.");
}
@ -235015,6 +235019,14 @@ zip* zip_open(const char *file, const char *mode /*r,w,a*/) {
return fclose(fp), ERR(NULL, "Unknown open mode %s", mode);
}
zip* zip_open(const char *file, const char *mode /*r,w,a*/) {
struct stat buffer;
int exists = (stat(file, &buffer) == 0);
if( mode[0] == 'a' && !exists ) mode = "wb";
FILE *fp = fopen(file, mode[0] == 'w' ? "wb" : mode[0] == 'a' ? "a+b" : "rb");
return zip_open_handle(fp, mode);
}
void zip_close(zip* z) {
if( z->out && z->count ) {
// prepare end record
@ -235194,8 +235206,7 @@ tar *tar_open(const char *filename, const char *mode) {
*t = zero;
t->in = in;
tar__parse(in, tar__push_entry, t);
return t;
return tar__parse(in, tar__push_entry, t) ? t : NULL;
}
int tar_find(tar *t, const char *entryname) {

View File

@ -525,16 +525,26 @@ char* tempvl(const char *fmt, va_list vl) {
int reqlen = sz;
#if 0
int heap = 0;
enum { STACK_ALLOC = 16384 };
static __thread char buf[STACK_ALLOC];
#else
enum { STACK_ALLOC = 128*1024 };
int heap = 1;
static __thread int STACK_ALLOC = 128*1024;
static __thread char *buf = 0; if(!buf) buf = REALLOC(0, STACK_ALLOC); // @leak
#endif
static __thread int cur = 0, len = STACK_ALLOC - 1; //printf("string stack %d/%d\n", cur, STACK_ALLOC);
static __thread int cur = 0; //printf("string stack %d/%d\n", cur, STACK_ALLOC);
assert(reqlen < STACK_ALLOC && "no stack enough, increase STACK_ALLOC variable above");
char* ptr = buf + (cur *= (cur+reqlen) < len, (cur += reqlen) - reqlen);
if( reqlen >= STACK_ALLOC ) {
tty_color(RED);
printf("no stack enough, increase STACK_ALLOC variable above (reqlen:%d) (fmt: %s)\n", reqlen, fmt);
tty_color(0);
//assert(reqlen < STACK_ALLOC);
STACK_ALLOC = reqlen * 2;
buf = REALLOC(0, STACK_ALLOC);
}
char* ptr = buf + (cur *= (cur+reqlen) < (STACK_ALLOC - 1), (cur += reqlen) - reqlen);
/*stbsp_*/vsnprintf( ptr, sz, fmt, vl );
return (char *)ptr;
@ -1430,10 +1440,17 @@ float audio_volume_master(float gain) {
mixer.gain = volume_master;
return sqrt( volume_master );
}
int audio_mute(int mute) {
static bool muted = 0; do_once muted = flag("--mute") || flag("--muted");
if( mute >= 0 && mute <= 1 ) muted = mute;
return muted;
}
int audio_muted() {
return audio_mute(-1);
}
int audio_play_gain_pitch_pan( audio_t a, int flags, float gain, float pitch, float pan ) {
static bool muted = 0; do_once muted = flag("--mute") || flag("--muted");
if(muted) return 1;
if(audio_muted()) return 1;
if( flags & AUDIO_IGNORE_MIXER_GAIN ) {
// do nothing, gain used as-is
@ -4179,7 +4196,7 @@ array(struct fs) zipscan_filter(int threadid, int numthreads) {
// skip if list item does not belong to this thread bucket
uint64_t hash = hash_str(fname);
unsigned bucket = (hash >> 32) % numthreads;
unsigned bucket = (hash /*>> 32*/) % numthreads;
if(bucket != threadid) continue;
array_push(fs, fs_now[i]);
@ -4436,10 +4453,8 @@ bool cook_start( const char *cook_ini, const char *masks, int flags ) {
char *s = strchr( ART, ';' ); if(s) *s = 0;
char *w = strchr( ART, ' ' ); if(w) *w = 0;
char *out = 0; const char *sep = "";
const char *v4k_title = getenv("V4K_TITLE");
for each_substring(ART, ",", t) {
char *tmp = file_pathabs(va("%s%s", HOME, t)) + ART_LEN;
PRINTF("ART mount+=%s\n", tmp);
for(int i = 0; tmp[i]; ++i) if(tmp[i]=='\\') tmp[i] = '/';
strcatf(&out, "%s%s%s", sep, tmp, strendi(tmp, "/") ? "" : "/");
assert( out[strlen(out) - 1] == '/' );
@ -5226,7 +5241,7 @@ bool file_delete(const char *pathfile) {
}
bool file_copy(const char *src, const char *dst) {
int ok = 0, BUFSIZE = 1 << 20; // 1 MiB
static __thread char *buffer = 0; do_once buffer = REALLOC(0, BUFSIZE);
static __thread char *buffer = 0; do_once buffer = REALLOC(0, BUFSIZE); // @leak
for( FILE *in = fopen(src, "rb"); in; fclose(in), in = 0) {
for( FILE *out = fopen(dst, "wb"); out; fclose(out), out = 0, ok = 1) {
for( int n; !!(n = fread( buffer, 1, BUFSIZE, in )); ){
@ -5551,6 +5566,8 @@ void vfs_reload() {
#if defined(EMSCRIPTEN)
vfs_mount("index.zip");
#else
// mount fused executables
vfs_mount(va("%s%s%s", app_path(), app_name(), ifdef(win32, ".exe", "")));
/* // old way
for( int i = 0; i < JOBS_MAX; ++i) {
if( vfs_mount(va(".art[%02x].zip", i)) ) continue;
@ -5570,6 +5587,34 @@ void vfs_reload() {
}
}
#define ARK1 'ArK\x1'
#define ARK1_PADDING (512 - 40) // 472
#define ARK_PRINTF(f,...) 0 // printf(f,__VA_ARGS__)
#define ARK_SWAP32(x) (x)
#define ARK_SWAP64(x) (x)
#define ARK_REALLOC REALLOC
static uint64_t ark_fget64( FILE *in ) { uint64_t v; fread( &v, 1, 8, in ); return ARK_SWAP64(v); }
void ark_list( const char *infile, zip **z ) {
for( FILE *in = fopen(infile, "rb"); in; fclose(in), in = 0 )
while(!feof(in)) {
if( 0 != (ftell(in) % ARK1_PADDING) ) fseek(in, ARK1_PADDING - (ftell(in) % ARK1_PADDING), SEEK_CUR);
ARK_PRINTF("Reading at #%d\n", (int)ftell(in));
uint64_t mark = ark_fget64(in);
if( mark != ARK1 ) continue;
uint64_t stamp = ark_fget64(in);
uint64_t datalen = ark_fget64(in);
uint64_t datahash = ark_fget64(in);
uint64_t namelen = ark_fget64(in);
*z = zip_open_handle(in, "rb");
return;
}
}
static
bool vfs_mount_(const char *path, array(struct vfs_entry) *entries) {
zip *z = NULL; tar *t = NULL; pak *p = NULL; dir *d = NULL;
@ -5579,6 +5624,7 @@ bool vfs_mount_(const char *path, array(struct vfs_entry) *entries) {
if( !is_folder ) z = zip_open(path, "rb");
if( !is_folder && !z ) t = tar_open(path, "rb");
if( !is_folder && !z && !t ) p = pak_open(path, "rb");
if( !is_folder && !z && !t && !p ) ark_list(path, &z); // last resort. try as .ark
if( !is_folder && !z && !t && !p ) return 0;
// normalize input -> "././" to ""
@ -5763,9 +5809,9 @@ if( found && *found == 0 ) {
const char *lookup_id = /*file_normalize_with_folder*/(pathfile);
// search (last item)
static char last_item[256] = { 0 };
static void *last_ptr = 0;
static int last_size = 0;
static __thread char last_item[256] = { 0 };
static __thread void *last_ptr = 0;
static __thread int last_size = 0;
if( !strcmpi(lookup_id, last_item)) {
ptr = last_ptr;
size = last_size;
@ -5922,6 +5968,10 @@ void* cache_insert(const char *pathfile, void *ptr, int size) { // append key/va
if( !MAX_CACHED_FILES ) return 0;
if( !ptr || !size ) return 0;
// keep cached files within limits
static thread_mutex_t mutex, *init = 0; if(!init) thread_mutex_init(init = &mutex);
thread_mutex_lock(&mutex);
// append to cache
archive_dir zero = {0}, *old = dir_cache;
*(dir_cache = REALLOC(0, sizeof(archive_dir))) = zero;
@ -5931,7 +5981,8 @@ void* cache_insert(const char *pathfile, void *ptr, int size) { // append key/va
dir_cache->data = REALLOC(0, size+1);
memcpy(dir_cache->data, ptr, size); size[(char*)dir_cache->data] = 0; // copy+terminator
// keep cached files within limits
void *found = 0;
static int added = 0;
if( added < MAX_CACHED_FILES ) {
++added;
@ -5940,15 +5991,18 @@ void* cache_insert(const char *pathfile, void *ptr, int size) { // append key/va
for( archive_dir *prev = dir_cache, *dir = prev; dir ; prev = dir, dir = dir->next ) {
if( !dir->next ) {
prev->next = 0; // break link
void *data = dir->data;
found = dir->data;
dir->path = REALLOC(dir->path, 0);
dir->data = REALLOC(dir->data, 0);
dir = REALLOC(dir, 0);
return data;
break;
}
}
}
return 0;
thread_mutex_unlock(&mutex);
return found;
}
// ----------------------------------------------------------------------------
@ -12721,8 +12775,8 @@ static map(unsigned, reflected_t) reflects;
static map(unsigned, array(reflected_t)) members;
void reflected_printf(reflected_t *r) {
printf("id:%u objtype:%u sz:%u name:%s info:%s addr:%p parent:%u type:%s",
r->id, r->objtype, r->sz, r->name ? r->name : "", r->info ? r->info : "", r->addr, r->parent, r->type ? r->type : "");
printf("name:%s info:'%s' id:%u objtype:%u sz:%u addr:%p parent:%u type:%s",
r->name ? r->name : "", r->info ? r->info : "", r->id, r->objtype, r->sz, r->addr, r->parent, r->type ? r->type : "");
}
void reflected_printf_all() {
for each_map_ptr(reflects, unsigned, k, reflected_t, p) {
@ -20295,6 +20349,26 @@ bool app_open(const char *link) {
return app_open_url(link);
}
const char* app_loadfile() {
const char *windowTitle = NULL;
const char *defaultPathFile = NULL;
const char *filterHints = NULL; // "image files"
const char *filters[] = { "*.*" };
int allowMultipleSelections = 0;
tinyfd_assumeGraphicDisplay = 1;
return tinyfd_openFileDialog( windowTitle, defaultPathFile, countof(filters), filters, filterHints, allowMultipleSelections );
}
const char* app_savefile() {
const char *windowTitle = NULL;
const char *defaultPathFile = NULL;
const char *filterHints = NULL; // "image files"
const char *filters[] = { "*.*" };
tinyfd_assumeGraphicDisplay = 1;
return tinyfd_saveFileDialog( windowTitle, defaultPathFile, countof(filters), filters, filterHints );
}
// ----------------------------------------------------------------------------
// tests
@ -21881,11 +21955,12 @@ int ui_collapse_end() {
int ui_contextual() {
struct nk_rect bounds = nk_widget_bounds(ui_ctx);
struct nk_rect bounds = nk_widget_bounds(ui_ctx); // = nk_window_get_bounds(ui_ctx);
bounds.y -= 25;
return ui_popups() ? 0 : nk_contextual_begin(ui_ctx, 0, nk_vec2(150, 300), bounds);
}
int ui_contextual_end() {
int ui_contextual_end(int close) {
if(close) nk_contextual_close(ui_ctx);
nk_contextual_end(ui_ctx);
return 1;
}
@ -21896,7 +21971,7 @@ int ui_submenu(const char *options) {
for( int i = 0; i < array_count(tokens) ; ++i ) {
if( ui_button_transparent(tokens[i]) ) choice = i + 1;
}
ui_contextual_end();
ui_contextual_end(0);
}
return choice;
}
@ -22030,8 +22105,10 @@ int ui_label(const char *text) {
int ui_label2(const char *label, const char *text_) {
nk_layout_row_dynamic(ui_ctx, 0, 2);
int align1 = label[0] == '>' ? (label++, NK_TEXT_RIGHT) : label[0] == '=' ? (label++, NK_TEXT_CENTERED) : label[0] == '<' ? (label++, NK_TEXT_LEFT) : NK_TEXT_LEFT;
int align2 = text_[0] == '>' ? (text_++, NK_TEXT_RIGHT) : text_[0] == '=' ? (text_++, NK_TEXT_CENTERED) : text_[0] == '<' ? (text_++, NK_TEXT_LEFT) : NK_TEXT_LEFT;
int align1 = NK_TEXT_LEFT;
int align2 = NK_TEXT_LEFT;
if( label ) align1 = label[0] == '>' ? (label++, NK_TEXT_RIGHT) : label[0] == '=' ? (label++, NK_TEXT_CENTERED) : label[0] == '<' ? (label++, NK_TEXT_LEFT) : NK_TEXT_LEFT;
if( text_ ) align2 = text_[0] == '>' ? (text_++, NK_TEXT_RIGHT) : text_[0] == '=' ? (text_++, NK_TEXT_CENTERED) : text_[0] == '<' ? (text_++, NK_TEXT_LEFT) : NK_TEXT_LEFT;
ui_label_(label, align1);
const struct nk_input *input = &ui_ctx->input;
@ -23766,12 +23843,38 @@ int window_frame_begin() {
if( may_render_stats ) {
if( has_menu ? ui_window("Debug " ICON_MD_SETTINGS, 0) : ui_panel("Debug " ICON_MD_SETTINGS, 0) ) {
#if 1
static char *filter = 0;
static int time_factor = 0;
static int playing = 0;
static int paused = 0;
int advance_frame = 0;
static int do_filter = 0;
static int do_profile = 0;
static int do_extra = 0;
char *EDITOR_TOOLBAR_ICONS = va("%s;%s;%s;%s;%s;%s;%s;%s",
do_filter ? ICON_MD_CLOSE : ICON_MD_SEARCH,
ICON_MD_PLAY_ARROW,
paused ? ICON_MD_SKIP_NEXT : ICON_MD_PAUSE,
ICON_MD_FAST_FORWARD,
ICON_MD_STOP,
ICON_MD_REPLAY,
ICON_MD_FACE,
ICON_MD_MENU
);
if( input_down(KEY_F) ) if( input(KEY_LCTRL) || input(KEY_RCTRL) ) do_filter ^= 1;
int choice = ui_toolbar(ICON_MD_SEARCH ";");
if( choice == 1 ) do_filter = 1;
int choice = ui_toolbar(EDITOR_TOOLBAR_ICONS);
if( choice == 1 ) do_filter ^= 1, do_profile = 0, do_extra = 0;
if( choice == 2 ) playing = 1, paused = 0;
if( choice == 3 ) advance_frame = !!paused, paused = 1;
if( choice == 4 ) paused = 0, time_factor = (++time_factor) % 4;
if( choice == 5 ) playing = 0, paused = 0, advance_frame = 0, time_factor = 0;
if( choice == 6 ) window_reload();
if( choice == 7 ) do_filter = 0, do_profile ^= 1, do_extra = 0;
if( choice == 8 ) do_filter = 0, do_profile = 0, do_extra ^= 1;
static char *filter = 0;
if( do_filter ) {
ui_string(ICON_MD_CLOSE " Filter " ICON_MD_SEARCH, &filter);
if( ui_label_icon_clicked_L.x > 0 && ui_label_icon_clicked_L.x <= 24 ) { // if clicked on CANCEL icon (1st icon)
@ -23781,13 +23884,44 @@ int window_frame_begin() {
if( filter ) filter[0] = '\0';
}
char *filter_mask = filter && filter[0] ? va("*%s*", filter) : "*";
#endif
static char *username = 0;
static char *userpass = 0;
if( do_profile ) {
ui_string(ICON_MD_FACE " Username", &username);
ui_string(ICON_MD_FACE " Password", &userpass);
}
if( do_extra ) {
int choice2 = ui_label2_toolbar(NULL,
ICON_MD_VIEW_IN_AR
ICON_MD_MESSAGE
ICON_MD_TIPS_AND_UPDATES ICON_MD_LIGHTBULB ICON_MD_LIGHTBULB_OUTLINE
ICON_MD_IMAGE_SEARCH ICON_MD_INSERT_PHOTO
ICON_MD_VIDEOGAME_ASSET ICON_MD_VIDEOGAME_ASSET_OFF
ICON_MD_VOLUME_UP ICON_MD_VOLUME_OFF // audio_volume_master(-1) > 0
ICON_MD_TROUBLESHOOT ICON_MD_SCHEMA ICON_MD_MENU
);
}
int open = 0, clicked_or_toggled = 0;
#define ui_collapse_filtered(lbl,id) (strmatchi(lbl,filter_mask) && ui_collapse(lbl,id))
for( int p = (open = ui_collapse_filtered(ICON_MD_FOLDER_SPECIAL " Art", "Debug.Art")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
#define EDITOR_UI_COLLAPSE(f,...) \
for( int macro(p) = (open = ui_collapse_filtered(f,__VA_ARGS__)), macro(dummy) = (clicked_or_toggled = ui_collapse_clicked()); macro(p); ui_collapse_end(), macro(p) = 0)
EDITOR_UI_COLLAPSE(ICON_MD_BUG_REPORT " Bugs 0", "Debug.Bugs") {
// @todo. parse /bugs.ini, includes saved screenshots & videos.
// @todo. screenshot include parseable level, position screen markers (same info as /bugs.ini)
}
// Art and bookmarks
EDITOR_UI_COLLAPSE(ICON_MD_FOLDER_SPECIAL " Art", "Debug.Art") {
bool inlined = true;
const char *file = 0;
if( ui_browse(&file, &inlined) ) {
@ -23795,47 +23929,78 @@ int window_frame_begin() {
app_exec(va("%s %s%s%s", ifdef(win32, "start \"\"", ifdef(osx, "open", "xdg-open")), sep, file, sep));
}
}
for( int p = (open = ui_collapse_filtered(ICON_MD_ROCKET_LAUNCH " AI", "Debug.AI")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_BOOKMARK " Bookmarks", "Debug.Bookmarks") { /* @todo */ }
// E,C,S,W
EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Scene", "Debug.Scene") {
EDITOR_UI_COLLAPSE(ICON_MD_BUBBLE_CHART/*ICON_MD_SCATTER_PLOT*/ " Entities", "Debug.Entities") { /* @todo */ }
EDITOR_UI_COLLAPSE(ICON_MD_TUNE " Components", "Debug.Components") { /* @todo */ }
EDITOR_UI_COLLAPSE(ICON_MD_PRECISION_MANUFACTURING " Systems", "Debug.Systems") { /* @todo */ }
EDITOR_UI_COLLAPSE(ICON_MD_PUBLIC " Levels", "Debug.Levels") {
//node_edit(editor.edit.down,&editor.edit);
}
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Init", "Debug.HierarchyInit") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Draw", "Debug.HierarchyDraw") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Tick", "Debug.HierarchyTick") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Edit", "Debug.HierarchyEdit") { /* @todo */ }
//EDITOR_UI_COLLAPSE(ICON_MD_ACCOUNT_TREE " Quit", "Debug.HierarchyQuit") { /* @todo */ }
// node_edit(&editor.init,&editor.init);
// node_edit(&editor.draw,&editor.draw);
// node_edit(&editor.tick,&editor.tick);
// node_edit(&editor.edit,&editor.edit);
// node_edit(&editor.quit,&editor.quit);
}
EDITOR_UI_COLLAPSE(ICON_MD_ROCKET_LAUNCH " AI", "Debug.AI") {
// @todo
}
for( int p = (open = ui_collapse_filtered(ICON_MD_VOLUME_UP " Audio", "Debug.Audio")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_VOLUME_UP " Audio", "Debug.Audio") {
ui_audio();
}
for( int p = (open = ui_collapse_filtered(ICON_MD_VIDEOCAM " Camera", "Debug.Camera")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_VIDEOCAM " Camera", "Debug.Camera") {
ui_camera( camera_get_active() );
}
for( int p = (open = ui_collapse_filtered(ICON_MD_BUILD " Cook", "Debug.Cook")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
// @todo
EDITOR_UI_COLLAPSE(ICON_MD_MONITOR " Display", "Debug.Display") {
// @todo: fps lock, fps target, aspect ratio, fullscreen
char *text = va("%s;%s;%s",
window_has_fullscreen() ? ICON_MD_FULLSCREEN_EXIT : ICON_MD_FULLSCREEN,
ICON_MD_PHOTO_CAMERA,
record_active() ? ICON_MD_VIDEOCAM_OFF : ICON_MD_VIDEOCAM
);
int choice = ui_toolbar(text);
if( choice == 1 ) editor_send("key_fullscreen",0);
if( choice == 2 ) editor_send("key_screenshot",0);
if( choice == 3 ) editor_send("key_record",0);
}
for( int p = (open = ui_collapse_filtered(ICON_MD_SIGNAL_CELLULAR_ALT " Network", "Debug.Network")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
// @todo
}
for( int p = (open = ui_collapse_filtered(ICON_MD_CONTENT_PASTE " Scripts", "Debug.Scripts")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
// @todo
}
for( int p = (open = ui_collapse_filtered(ICON_MD_MOVIE " FXs", "Debug.FXs")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
ui_fxs();
}
for( int p = (open = ui_collapse_filtered(ICON_MD_SPEED " Profiler", "Debug.Profiler")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
ui_profiler();
}
for( int p = (open = ui_collapse_filtered(ICON_MD_STAR_HALF " Shaders", "Debug.Shaders")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
ui_shaders();
}
for( int p = (open = ui_collapse_filtered(ICON_MD_KEYBOARD " Keyboard", "Debug.Keyboard")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_KEYBOARD " Keyboard", "Debug.Keyboard") {
ui_keyboard();
}
for( int p = (open = ui_collapse_filtered(ICON_MD_MOUSE " Mouse", "Debug.Mouse")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_MOUSE " Mouse", "Debug.Mouse") {
ui_mouse();
}
for( int p = (open = ui_collapse_filtered(ICON_MD_GAMEPAD " Gamepads", "Debug.Gamepads")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_GAMEPAD " Gamepads", "Debug.Gamepads") {
for( int q = 0; q < 4; ++q ) {
for( int r = (open = ui_collapse(va("Gamepad #%d",q+1), va("Debug.Gamepads%d",q))), dummy = (clicked_or_toggled = ui_collapse_clicked()); r; ui_collapse_end(), r = 0) {
ui_gamepad(q);
}
}
}
for( int p = (open = ui_collapse_filtered(ICON_MD_VIEW_QUILT " UI", "Debug.UI")), dummy = (clicked_or_toggled = ui_collapse_clicked()); p; ui_collapse_end(), p = 0) {
EDITOR_UI_COLLAPSE(ICON_MD_CONTENT_PASTE " Scripts", "Debug.Scripts") {
// @todo
}
EDITOR_UI_COLLAPSE(ICON_MD_STAR_HALF " Shaders", "Debug.Shaders") {
ui_shaders();
}
EDITOR_UI_COLLAPSE(ICON_MD_MOVIE " FXs", "Debug.FXs") {
ui_fxs();
}
EDITOR_UI_COLLAPSE(ICON_MD_VIEW_QUILT " UI", "Debug.UI") {
int choice = ui_toolbar(ICON_MD_RECYCLING " Reset layout;" ICON_MD_SAVE_AS " Save layout");
if( choice == 1 ) ui_layout_all_reset("*");
if( choice == 2 ) file_delete(WINDOWS_INI), ui_layout_all_save_disk("*");
@ -23848,8 +24013,61 @@ int window_frame_begin() {
}
}
EDITOR_UI_COLLAPSE(ICON_MD_SAVINGS " Budgets", "Debug.Budgets") {
// @todo. // mem,fps,gfx,net,hdd,... also logging
}
EDITOR_UI_COLLAPSE(ICON_MD_WIFI/*ICON_MD_SIGNAL_CELLULAR_ALT*/ " Network 0/0 KiB", "Debug.Network") {
// @todo
// SIGNAL_CELLULAR_1_BAR SIGNAL_CELLULAR_2_BAR
}
EDITOR_UI_COLLAPSE(va(ICON_MD_SPEED " Profiler %5.2f/%d", window_fps(), (int)window_fps_target()), "Debug.Profiler") {
ui_profiler();
}
EDITOR_UI_COLLAPSE(va(ICON_MD_STORAGE " Storage %s", xstats()), "Debug.Storage") {
// @todo
}
// logic: either plug icon (power saving off) or one of the following ones (power saving on):
// if 0% batt (no batt): battery alert
// if discharging: battery levels [alert,0..6,full]
// if charging: battery charging
int battery_read = app_battery();
int battery_level = abs(battery_read);
int battery_discharging = battery_read < 0 && battery_level < 100;
const char *power_icon_label = ICON_MD_POWER " Power";
if( battery_level ) {
const char *battery_levels[9] = { // @todo: remap [7%..100%] -> [0..1] ?
ICON_MD_BATTERY_ALERT,ICON_MD_BATTERY_0_BAR,ICON_MD_BATTERY_1_BAR,
ICON_MD_BATTERY_2_BAR,ICON_MD_BATTERY_3_BAR,ICON_MD_BATTERY_4_BAR,
ICON_MD_BATTERY_5_BAR,ICON_MD_BATTERY_6_BAR,ICON_MD_BATTERY_FULL,
};
power_icon_label = (const char*)va("%s Power %d%%",
battery_discharging ? battery_levels[(int)((9-1)*clampf(battery_level/100.f,0,1))] : ICON_MD_BATTERY_CHARGING_FULL,
battery_level);
}
EDITOR_UI_COLLAPSE(power_icon_label, "Debug.Power") {
int choice = ui_toolbar( ICON_MD_POWER ";" ICON_MD_BOLT );
if( choice == 1 ) editor_send("key_battery","0");
if( choice == 2 ) editor_send("key_battery","1");
}
EDITOR_UI_COLLAPSE(ICON_MD_EXTENSION " Plugins", "Debug.Plugins") {
// @todo. include VCS
EDITOR_UI_COLLAPSE(ICON_MD_BUILD " Cook", "Debug.Cook") {
// @todo
}
}
(has_menu ? ui_window_end : ui_panel_end)();
}
API int editor_tick();
editor_tick();
}
#if 0 // deprecated
@ -25250,6 +25468,61 @@ int ui_bt(bt_t *b) {
// editing:
// nope > functions: add/rem property
#define ICON_PLAY ICON_MD_PLAY_ARROW
#define ICON_PAUSE ICON_MD_PAUSE
#define ICON_STOP ICON_MD_STOP
#define ICON_CANCEL ICON_MD_CLOSE
#define ICON_WARNING ICON_MD_WARNING
#define ICON_BROWSER ICON_MD_FOLDER_SPECIAL
#define ICON_OUTLINER ICON_MD_VIEW_IN_AR
#define ICON_BUILD ICON_MD_BUILD
#define ICON_SCREENSHOT ICON_MD_PHOTO_CAMERA
#define ICON_CAMERA_ON ICON_MD_VIDEOCAM
#define ICON_CAMERA_OFF ICON_MD_VIDEOCAM_OFF
#define ICON_GAMEPAD_ON ICON_MD_VIDEOGAME_ASSET
#define ICON_GAMEPAD_OFF ICON_MD_VIDEOGAME_ASSET_OFF
#define ICON_AUDIO_ON ICON_MD_VOLUME_UP
#define ICON_AUDIO_OFF ICON_MD_VOLUME_OFF
#define ICON_WINDOWED ICON_MD_FULLSCREEN_EXIT
#define ICON_FULLSCREEN ICON_MD_FULLSCREEN
#define ICON_LIGHTS_ON ICON_MD_LIGHTBULB
#define ICON_LIGHTS_OFF ICON_MD_LIGHTBULB_OUTLINE
#define ICON_RENDER_BASIC ICON_MD_IMAGE_SEARCH
#define ICON_RENDER_FULL ICON_MD_INSERT_PHOTO
#define ICON_SIGNAL ICON_MD_SIGNAL_CELLULAR_ALT
#define ICON_DISK ICON_MD_STORAGE
#define ICON_RATE ICON_MD_SPEED
#define ICON_CLOCK ICON_MD_TODAY
#define ICON_CHRONO ICON_MD_TIMELAPSE
#define ICON_SETTINGS ICON_MD_SETTINGS
#define ICON_LANGUAGE ICON_MD_G_TRANSLATE
#define ICON_PERSONA ICON_MD_FACE
#define ICON_SOCIAL ICON_MD_MESSAGE
#define ICON_GAME ICON_MD_ROCKET_LAUNCH
#define ICON_KEYBOARD ICON_MD_KEYBOARD
#define ICON_MOUSE ICON_MD_MOUSE
#define ICON_GAMEPAD ICON_MD_GAMEPAD
#define ICON_MONITOR ICON_MD_MONITOR
#define ICON_WIFI ICON_MD_WIFI
#define ICON_BUDGET ICON_MD_SAVINGS
#define ICON_NEW_FOLDER ICON_MD_CREATE_NEW_FOLDER
#define ICON_PLUGIN ICON_MD_EXTENSION
#define ICON_RESTART ICON_MD_REPLAY
#define ICON_QUIT ICON_MD_CLOSE
#define ICON_POWER ICON_MD_BOLT // ICON_MD_POWER
#define ICON_BATTERY_CHARGING ICON_MD_BATTERY_CHARGING_FULL
#define ICON_BATTERY_LEVELS \
ICON_MD_BATTERY_ALERT, \
ICON_MD_BATTERY_0_BAR,ICON_MD_BATTERY_1_BAR, \
ICON_MD_BATTERY_2_BAR,ICON_MD_BATTERY_3_BAR, \
ICON_MD_BATTERY_4_BAR,ICON_MD_BATTERY_5_BAR, \
ICON_MD_BATTERY_6_BAR,ICON_MD_BATTERY_FULL
char *editor_path(const char *path) {
return va("%s/%s", EDITOR, path);
}
@ -25314,6 +25587,75 @@ int editor_ui_bits8(const char *label, uint8_t *enabled) { // @to deprecate
return clicked | (copy ^ *enabled);
}
typedef union editor_var {
int i;
float f;
char *s;
} editor_var;
static map(char*,editor_var) editor_vars;
float *editor_getf(const char *key) {
if(!editor_vars) map_init_str(editor_vars);
editor_var *found = map_find_or_add(editor_vars, (char*)key, ((editor_var){0}) );
return &found->f;
}
int *editor_geti(const char *key) {
if(!editor_vars) map_init_str(editor_vars);
editor_var *found = map_find_or_add(editor_vars, (char*)key, ((editor_var){0}) );
return &found->i;
}
char **editor_gets(const char *key) {
if(!editor_vars) map_init_str(editor_vars);
editor_var *found = map_find_or_add(editor_vars, (char*)key, ((editor_var){0}) );
if(!found->s) found->s = stringf("%s","");
return &found->s;
}
int editor_send(const char *cmd, const char *optional_value) {
unsigned *gamepads = editor_geti("gamepads"); // 0 off, mask gamepad1(1), gamepad2(2), gamepad3(4), gamepad4(8)...
unsigned *renders = editor_geti("renders"); // 0 off, mask: 1=lit, 2=ddraw, 3=whiteboxes
float *speed = editor_getf("speed"); // <0 num of frames to advance, 0 paused, [0..1] slomo, 1 play regular speed, >1 fast-forward (x2/x4/x8)
unsigned *powersave = editor_geti("powersave");
char *name;
/**/ if( !strcmp(cmd, "key_quit" )) record_stop(), exit(0);
else if( !strcmp(cmd, "key_stop" )) window_pause(1);
else if( !strcmp(cmd, "key_mute" )) audio_volume_master( 1 ^ !!audio_volume_master(-1) );
else if( !strcmp(cmd, "key_pause" )) window_pause( window_has_pause() ^ 1 );
else if( !strcmp(cmd, "key_reload" )) window_reload();
else if( !strcmp(cmd, "key_battery" )) *powersave = optional_value ? !!atoi(optional_value) : *powersave ^ 1;
else if( !strcmp(cmd, "key_browser" )) ui_show("File Browser", ui_visible("File Browser") ^ true);
else if( !strcmp(cmd, "key_outliner" )) ui_show("Outliner", ui_visible("Outliner") ^ true);
else if( !strcmp(cmd, "key_record" )) if(record_active()) record_stop(); else
name = file_counter(va("%s.mp4",app_name())), window_record(name), ui_notify(va("Video capturing: %s", name), date_string());
else if( !strcmp(cmd, "key_screenshot" )) name = file_counter(va("%s.png",app_name())), window_screenshot(name), ui_notify(va("Screenshot: %s", name), date_string());
else if( !strcmp(cmd, "key_profiler" )) ui_show("Profiler", profiler_enable(ui_visible("Profiler") ^ true));
else if( !strcmp(cmd, "key_fullscreen" )) record_stop(), window_fullscreen( window_has_fullscreen() ^ 1 ); // framebuffer resizing corrupts video stream, so stop any recording beforehand
else if( !strcmp(cmd, "key_gamepad" )) *gamepads = (*gamepads & ~1u) | ((*gamepads & 1) ^ 1);
else if( !strcmp(cmd, "key_lit" )) *renders = (*renders & ~1u) | ((*renders & 1) ^ 1);
else if( !strcmp(cmd, "key_ddraw" )) *renders = (*renders & ~2u) | ((*renders & 2) ^ 2);
else alert(va("editor could not handle `%s` command.", cmd));
return 0;
}
int editor_tick() {
enum { editor_hz = 60 };
enum { editor_hz_mid = 18 };
enum { editor_hz_low = 5 };
if( *editor_geti("powersave") ) {
// adaptive framerate
int app_on_background = !window_has_focus();
int hz = app_on_background ? editor_hz_low : editor_hz_mid;
window_fps_lock( hz < 5 ? 5 : hz );
} else {
// window_fps_lock( editor_hz );
}
return 0;
}
static int gizmo__mode;
static int gizmo__active;
static int gizmo__hover;
@ -25425,26 +25767,6 @@ int gizmo(vec3 *pos, vec3 *rot, vec3 *sca) {
return modified;
}
char* dialog_load() {
const char *windowTitle = NULL;
const char *defaultPathFile = NULL;
const char *filterHints = NULL; // "image files"
const char *filters[] = { "*.*" };
int allowMultipleSelections = 0;
tinyfd_assumeGraphicDisplay = 1;
return tinyfd_openFileDialog( windowTitle, defaultPathFile, countof(filters), filters, filterHints, allowMultipleSelections );
}
char* dialog_save() {
const char *windowTitle = NULL;
const char *defaultPathFile = NULL;
const char *filterHints = NULL; // "image files"
const char *filters[] = { "*.*" };
tinyfd_assumeGraphicDisplay = 1;
return tinyfd_saveFileDialog( windowTitle, defaultPathFile, countof(filters), filters, filterHints );
}
// -- localization kit
static const char *kit_lang = "enUS", *kit_langs =
@ -25618,18 +25940,20 @@ static void v4k_pre_init() {
// window_swap();
}
static void v4k_post_init(float refresh_rate) {
int i;
// cook cleanup
cook_stop();
vfs_reload();
// init subsystems that depend on cooked assets now. ui_init() is special case and needs to be safely in single thread
ui_init();
// init more subsystems; beware of VFS mounting, as some of these may need cooked assets at this point
int i;
#if 1 // #ifdef PARALLEL_INIT
#pragma omp parallel for
#endif
for( i = 0; i <= 3; ++i) {
/**/ if( i == 0 ) ui_init(), scene_init(); // init these on thread #0, since both will be compiling shaders, and shaders need to be compiled from the very same thread than glfwMakeContextCurrent() was set up
/**/ if( i == 0 ) scene_init(); // init these on thread #0, since both will be compiling shaders, and shaders need to be compiled from the very same thread than glfwMakeContextCurrent() was set up
else if( i == 1 ) audio_init(0); // initialize audio after cooking // reasoning for this: do not launch audio threads while cooks are in progress, so there is more cpu for cooking actually
else if( i == 2 ) script_init(), kit_init(), midi_init();
else if( i == 3 ) input_init(), network_init();

View File

@ -1359,9 +1359,12 @@ API int audio_play_gain_pitch( audio_t a, int flags, float gain, float pitch
API int audio_play_gain_pitch_pan( audio_t a, int flags, float gain, float pitch, float pan/*0*/ );
API int audio_stop( audio_t a );
API float audio_volume_clip(float gain); // set fx volume if gain is in [0..1] range. return current fx volume in any case
API float audio_volume_stream(float gain); // set bgm volume if gain is in [0..1] range. return current bgm volume in any case
API float audio_volume_master(float gain); // set master volume if gain is in [0..1] range. return current master volume in any case
API float audio_volume_clip(float gain); // set fx volume if gain is in [0..1] range. returns current fx volume in any case
API float audio_volume_stream(float gain); // set bgm volume if gain is in [0..1] range. returns current bgm volume in any case
API float audio_volume_master(float gain); // set master volume if gain is in [0..1] range. returns current master volume in any case
API int audio_mute(int mute);
API int audio_muted();
API int ui_audio();
@ -1750,6 +1753,11 @@ API void* dll(const char *filename, const char *symbol);
API vec3 editor_pick(float mouse_x, float mouse_y);
API char* editor_path(const char *path);
API float* editor_getf(const char *key);
API int* editor_geti(const char *key);
API char** editor_gets(const char *key);
API int editor_send(const char *cmd, const char *optional_value);
// open file dialog
API char* dialog_load();
@ -2587,7 +2595,9 @@ extern API int profiler_enabled; ///-
// @todo: nested structs? pointers in members?
// @todo: declare TYPEDEF(vec3, float[3]), TYPEDEF(mat4, vec4[4]/*float[16]*/)
#ifndef ifdef_objapi
#define ifdef_objapi(T,...) __VA_ARGS__
#endif
typedef struct reflected_t {
unsigned id, objtype;
@ -3586,9 +3596,10 @@ char* strtok_s(char* str,const char* delimiters,char** context); // tcc misses t
#if 1
#define each_substring(str, delims, keyname) \
( int len_ = strlen(str) + 1; len_; len_ = 0 ) \
for( char buf_[1024], *ptr_ = len_ < 1024 ? buf_ : REALLOC(0, len_), *lit_ = (char*)(str), *_bak = (snprintf(ptr_, len_, "%s", lit_), ptr_); _bak; _bak = 0, (ptr_ == buf_ ? 0 : REALLOC(ptr_, 0)) ) \
for( char *next_token = 0, *keyname = strtok_r(_bak, delims, &next_token); keyname; keyname = strtok_r(NULL, delims, &next_token) )
( char *str_ = (char*)(str); str_; str_ = 0 ) \
for( int len_ = strlen(str_) + 1, heap_ = len_ < 1024; len_ > 1; len_ = 0 ) \
for( char *ptr_ = (heap_ ? REALLOC(0, len_) : ALLOCA(len_)), *cpy_ = (snprintf(ptr_, len_, "%s", str_), ptr_); ptr_; (heap_ ? REALLOC(ptr_, 0) : 0), ptr_ = 0 ) \
for( char *next_token = 0, *keyname = strtok_r(cpy_, delims, &next_token); keyname; keyname = strtok_r(NULL, delims, &next_token) )
#else
#define each_substring(str, delims, keyname) \
( char** tokens_ = strsplit((str), (delims)), *keyname = 0; tokens_; tokens_ = 0) \
@ -3743,6 +3754,10 @@ API void app_crash();
API void app_singleton(const char *guid);
API bool app_open(const char *folder_file_or_url);
API const char* app_loadfile();
API const char* app_savefile();
API char* callstack( int traces ); // write callstack into a temporary string. <0 traces to invert order. do not free().
API int callstackf( FILE *fp, int traces ); // write callstack to file. <0 traces to invert order.
@ -3842,7 +3857,7 @@ API int ui_label2_float(const char *label, float value);
API int ui_label2_toolbar(const char *label, const char *icons);
API int ui_slider(const char *label, float *value);
API int ui_slider2(const char *label, float *value, const char *caption);
API int ui_contextual_end();
API int ui_contextual_end(int close);
API int ui_collapse_clicked();
API int ui_collapse_end();
API int ui_panel_end();

138
tools/ark.c 100644
View File

@ -0,0 +1,138 @@
// ARK: lightweight, append-only, header-less journaling file format specification.
// - rlyeh, public domain.
// Features:
// - [x] Journaling support: data can be rolled back to an earlier state to retrieve older versions of files.
// - [x] Append-only format: create or update new entries just by appending stuff to the journal file.
// - [x] Compaction support: compact archives by keeping, for each duplicated file, its latest revision only.
// - [x] Concat friendly: journals can be glued together, and the result will still be a valid journey file.
// - [x] Foreign support: append random data to a foreign file and result will still be a valid journey file.
// - [x] Always aligned: file data is always 512-byte aligned for safe/fast memory access.
// - [x] Simple, tiny, portable, cross-platform, header-only.
// - [x] Public domain, CC0, 0-BSD, unlicensed (pick one).
// Extension:
// .ark
// File format:
// [foreign data]
// [...]
// [archive-entry #1]
// [archive-entry #2]
// [...]
// [archive-entry #N]
// [EOF]
//
// Where, each archive-entry is {
// [zero] 472-byte aligned zero padding
// [mark] 64-bit magic id 'Ark\x1' (if \1krA is found, swap endianness)
// [time] 64-bit time stamp in seconds (unix epoch)
// [dlen] 64-bit data length
// [hash] 64-bit data hash
// [nlen] 64-bit name length+1
// [data] file data (512-byte aligned)
// [name] file name+NUL
// }
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define ARK1 'ArK\x1'
#define ARK1_PADDING (512 - 40) // 472
#ifndef ARK_PRINTF
#define ARK_PRINTF printf
#endif
#ifndef ARK_SWAP32
#define ARK_SWAP32(x) x
#define ARK_SWAP64(x) x
#endif
#ifndef ARK_REALLOC
#define ARK_REALLOC realloc
#endif
static int ark_fput64( FILE *out, uint64_t v ) { return fwrite( (v = ARK_SWAP64(v), &v), 1, 8, out); }
static uint64_t ark_fget64( FILE *in ) { uint64_t v; fread( &v, 1, 8, in ); return ARK_SWAP64(v); }
static const char ark1_zero[ARK1_PADDING] = {0};
// `outfile` must be fopen("file.ark", "a+b") beforehand
int ark_append_mem( FILE *out, const char *name, const void *data, int datalen, uint64_t stamp ) {
fseek( out, 0L, SEEK_END );
while( 0 != (ftell(out) % ARK1_PADDING) ) fwrite(ark1_zero, 1, ARK1_PADDING - (ftell(out) % ARK1_PADDING), out);
ARK_PRINTF("Writing %s at #%d\n", name, (int)ftell(out));
int namelen = strlen(name) + 1;
uint64_t mark = ARK1;
ark_fput64(out, mark);
ark_fput64(out, stamp);
ark_fput64(out, datalen);
ark_fput64(out, 0ULL/*hash*/);
ark_fput64(out, namelen);
fwrite(data, 1, datalen, out);
fwrite(name, 1, namelen, out);
return 1;
}
// `outfile` must be fopen("file.ark", "a+b") beforehand
int ark_append_file( FILE *out, const char *name ) {
FILE *in = fopen(name, "rb");
if( in ) {
fseek(in, 0L, SEEK_END);
size_t sz = ftell(in);
fseek(in, 0L, SEEK_SET);
char *buffer = ARK_REALLOC(0, sz);
if( !buffer ) return fclose(in), 0;
fread(buffer, 1, sz, in);
fclose(in);
int rc = ark_append_mem(out, name, buffer, sz, 0ULL);
ARK_REALLOC(buffer, 0);
return rc;
}
return 0;
}
void ark_list( FILE *in, void *yield_fn ) {
int (*ark_yield_fn)() = yield_fn;
while( !feof(in) ) {
while( 0 != (ftell(in) % ARK1_PADDING) && !feof(in) ) fseek(in, ARK1_PADDING - (ftell(in) % ARK1_PADDING), SEEK_CUR);
ARK_PRINTF("Reading at #%d\n", (int)ftell(in));
uint64_t mark = ark_fget64(in);
if( mark != ARK1 ) continue;
uint64_t stamp = ark_fget64(in);
uint64_t datalen = ark_fget64(in);
uint64_t datahash = ark_fget64(in);
uint64_t namelen = ark_fget64(in);
char *data = ARK_REALLOC(0, datalen);
fread(data, 1, datalen, in);
char *name = ARK_REALLOC(0, namelen);
fread(name, 1, namelen, in);
if( yield_fn == printf ) {
printf("Found %s (%d bytes)\n", name, (int)datalen );
ARK_REALLOC(name, 0);
ARK_REALLOC(data, 0);
}
else {
if( !ark_yield_fn(name, data, datalen, datahash, stamp) )
return;
}
}
}
int main(int argc, char **argv) {
if( argc > 2 ) {
FILE *ark = fopen(argv[1], "a+b");
if( ark ) for( int i = 2; i < argc; ++i) ark_append_file( ark, argv[i] );
if( ark ) fclose(ark);
}
else if( argc == 2 ) {
FILE *ark = fopen(argv[1], "rb");
if( ark ) ark_list(ark, printf), fclose(ark);
}
else printf("%s infile.ark\n%s outfile.ark infile1 [infile2...]\n", argv[0], argv[0]);
}
// cl ark.c /MT /O2 /DNDEBUG /link setargv.obj

BIN
tools/ark.exe 100644

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,20 @@
Pictogrammers Free License
--------------------------
This icon collection is released as free, open source, and GPL friendly by
the [Pictogrammers](http://pictogrammers.com/) icon group. You may use it
for commercial projects, open source projects, or anything really.
# Icons: Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
Some of the icons are redistributed under the Apache 2.0 license. All other
icons are either redistributed under their respective licenses or are
distributed under the Apache 2.0 license.
# Fonts: Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
All web and desktop fonts are distributed under the Apache 2.0 license. Web
and desktop fonts contain some icons that are redistributed under the Apache
2.0 license. All other icons are either redistributed under their respective
licenses or are distributed under the Apache 2.0 license.
# Code: MIT (https://opensource.org/licenses/MIT)
The MIT license applies to all non-font and non-icon files.

View File

@ -61,7 +61,7 @@
// ### editor (v4)
// Bring in remote datas into the editor.
// Go social & marketplace. Allow others to expand, share, publish, subscribe, discuss their sub-editors within a small community.
// I really like the way the way OpenFrameworks.cc does their addons, and I think we should do same: just discover and monitor github repos, and list everything on a website (v4k- prefix?).
// I really like the way the way OpenFrameworks.cc does their addons, and I think we should do same: just discover and monitor github repos, and list everything on a website (fwk- prefix?).
// Wishlist for a github-based community flow: discovery, transparent installs, publish on github, star there, watch commits & releases, track issues+discussions, etc
//
// We should have a generic, extensible, script/plugin-driven, working editor at this point (hopefully) that does not require maintenance.
@ -153,7 +153,7 @@
// - ecs: sys are modules, ecs: char *messaging, ecs: filesystem (e/dir,c/files,s/dll)
// - world: streaming, migration
#include "v4k.h"
#include "fwk.h"
// #include "labs.vm/ecs.c"
@ -839,10 +839,10 @@ void editor_obj_render_max_properties(void *obj, const char *mask) { // headless
// main editor interface
void editor_render_menubar() {
int alts = input(KEY_LALT) || input(KEY_RALT); // @todo: move to v4k.c
int ctrls = input(KEY_LCTRL) || input(KEY_RCTRL); // @todo: move to v4k.c
int shifts = input(KEY_LSHIFT) || input(KEY_RSHIFT); // @todo: move to v4k.c
int mods = alts || ctrls || shifts; // @todo: move to v4k.c
int alts = input(KEY_LALT) || input(KEY_RALT); // @todo: move to fwk.c
int ctrls = input(KEY_LCTRL) || input(KEY_RCTRL); // @todo: move to fwk.c
int shifts = input(KEY_LSHIFT) || input(KEY_RSHIFT); // @todo: move to fwk.c
int mods = alts || ctrls || shifts; // @todo: move to fwk.c
if( input_down(KEY_F5) ) editor_key = key_reload;
if( input_down(KEY_F11) ) editor_key = key_fullscreen;
if( input_down(KEY_PAUSE) ) editor_key = key_pause;
@ -1046,7 +1046,7 @@ void editor_obj_render_properties_recursively(void *obj, const char *mask) {
if( ui_button_transparent("<Cut" ) ) do_context_obj = obj, do_context_cmd = cc3(c,u,t);
if( ui_button_transparent("<Copy" ) ) do_context_obj = obj, do_context_cmd = cc4(c,o,p,y);
if( ui_button_transparent("<Paste") ) do_context_obj = obj, do_context_cmd = cc5(p,a,s,t,e);
ui_contextual_end();
ui_contextual_end(0);
}
for( int i = 0; i < num_subobjects; ++i ) {
@ -1067,7 +1067,7 @@ void editor_obj_render_properties_recursively(void *obj, const char *mask) {
if( ui_button_transparent("<Cut" ) ) do_context_obj = obj, do_context_cmd = cc3(c,u,t);
if( ui_button_transparent("<Copy" ) ) do_context_obj = obj, do_context_cmd = cc4(c,o,p,y);
if( ui_button_transparent("<Paste") ) do_context_obj = obj, do_context_cmd = cc5(p,a,s,t,e);
ui_contextual_end();
ui_contextual_end(0);
}
if( clicked_or_toggled & 1 ) {

View File

@ -13,7 +13,7 @@
- [ ] Editor: GUI pass: timeline and data tracks, node graphs. <!-- worthy: will be reused into materials, animgraphs and blueprints -->
*/
#include "v4k.c"
#include "fwk.c"
#include "editor2.h" // old editor interface
#define ui_push_hspace(px) \
@ -28,8 +28,6 @@
// ----------------------------------------------------------------------------------------
#include "labs.meta/meta_reflect.c"
int *meta_changed(void *value) {
static map(void*,int) changes = 0;
do_once map_init_ptr(changes);
@ -37,20 +35,20 @@ int *meta_changed(void *value) {
return map_find_or_add(changes, value, 0);
}
void reflect_ui( const reflect *r, void *value, void *userdata ) {
void reflect_ui( const char *type, const char *name, const char *info, void *value ) {
ui_label_icon_highlight = *meta_changed(value); // @hack: remove ui_label_icon_highlight hack
char *title = va(ICON_MD_UNDO "%s", r->info);
char *title = va(ICON_MD_UNDO "%s", info);
int changed = 0;
/**/ if( !strcmp(r->type, "int") ) changed = ui_int(title, (int*)value);
else if( !strcmp(r->type, "char") && r->is_ptr ) changed = ui_buffer(title, (char*)value, strlen((char*)value)+1);
else if( !strcmp(r->type, "string") ) changed = ui_string(title, (char**)value);
else if( !strcmp(r->type, "float") ) changed = ui_float(title, (float*)value);
else if( !strcmp(r->type, "double") ) changed = ui_double(title, (double*)value);
else if( !strcmp(r->type, "unsigned") ) changed = ui_unsigned(title, (unsigned*)value);
else if( !strcmp(r->type, "color") ) changed = ui_color4(va("%s #%02X%02X%02X%02X", title, (int)(0[(float*)value]),(int)(1[(float*)value]),(int)(2[(float*)value]),(int)(3[(float*)value])), (float*)value);
/**/ if( !strcmp(type, "int") ) changed = ui_int(title, (int*)value);
else if( !strcmp(type, "char*") ) changed = ui_buffer(title, (char*)value, strlen((char*)value)+1);
else if( !strcmp(type, "string") ) changed = ui_string(title, (char**)value);
else if( !strcmp(type, "float") ) changed = ui_float(title, (float*)value);
else if( !strcmp(type, "double") ) changed = ui_double(title, (double*)value);
else if( !strcmp(type, "unsigned") ) changed = ui_unsigned(title, (unsigned*)value);
else if( !strcmp(type, "color") ) changed = ui_color4(va("%s #%02X%02X%02X%02X", title, (int)(0[(float*)value]),(int)(1[(float*)value]),(int)(2[(float*)value]),(int)(3[(float*)value])), (float*)value);
// else if( !strcmp(type, "vec3") ) ; // not supported. decays to 3 floats
else ui_label2(title, va("(%s)%s", r->type, r->name));
else ui_label2(title, va("(%s)%s", type, name));
if( changed ) {
*meta_changed(value) = 1;
@ -62,7 +60,7 @@ void reflect_ui( const reflect *r, void *value, void *userdata ) {
}
bool reflect_parse(void *obj, const char *type, const char *val) {
/**/ if( !strcmp(type, "int") ) *((int*)obj) = eval(val);
// else if( !strcmp(r->type, "char") && r->is_ptr ) ; // @fixme: not supported, unless we do strncpy() or similar.
// else if( !strcmp(type, "char*") ) ; // @fixme: not supported, unless we do strncpy() or similar.
else if( !strcmp(type, "string") ) *((char**)obj) = stringf("%s", val);
else if( !strcmp(type, "float") ) *((float*)obj) = eval(val); // = v[0] == '~' ? (float)~atoi(val+1) : atof(val); // = atof(val);
else if( !strcmp(type, "double") ) *((double*)obj) = eval(val); // = v[0] == '~' ? (float)~atoi(val+1) : atof(val); // = atof(val);
@ -73,42 +71,6 @@ bool reflect_parse(void *obj, const char *type, const char *val) {
return 1;
}
// ----------------------------------------------------------------------------------------
typedef void(*obj_ctor)(void*);
static map(char*, obj_ctor) obj_ctors;
#define STRUCT_CTOR(type, ctor) STRUCT_CTOR(#type, (obj_ctor)ctor)
void (STRUCT_CTOR)( const char *type, obj_ctor ctor ) {
do_once map_init_str(obj_ctors);
map_find_or_add(obj_ctors, STRDUP(type), ctor);
}
bool obj_make(void *obj, const char *ini_data) { // initialize object from ini datas
char *hint = 0;
for( ini_t read = ini_from_mem(ini_data); !!read; map_free(read), read = 0) {
for each_map(read, char*, k, char*, v) {
array(char*) tokens = strsplit(k, ".");
if( array_count(tokens) != 2 ) continue;
const char *type = 0;
void *found = reflect_field( tokens[0], obj, tokens[1], &type );
if( !found ) continue;
if( reflect_parse(found, type, v) ) {
hint = tokens[0];
}
}
// constructor (post-init call)
obj_ctor *ctor = map_find(obj_ctors, hint);
if( ctor ) (*ctor)( obj );
}
return hint != 0;
}
// ----------------------------------------------------------------------------
#define POD_TYPES \
@ -223,9 +185,9 @@ void node_edit(node *n, node *root) {
if( ui_collapse(va("%s %s (%u)", n->down ? ICON_MD_SOURCE : ICON_MD_FOLDER, node_name(n), node_children(n)), va("%p%p",root,n->v.ptr)) ) { // @fixme v.ptr
if( n->down ) node_edit(n->down,root);
if( reflect_has_fields( node_type(n), n->v.ptr ) ) {
if( 1 ) { // reflect_has_fields( node_type(n), n->v.ptr ) ) {
for ui_push_hspace( 4 ) {
#define ICON_DOT ICON_CANCEL // ICON_MD_WIFI_1_BAR // ICON_MD_RADIO_BUTTON_UNCHECKED // ICON_MD_LENS_BLUR
#define ICON_DOT " · " // ICON_CANCEL // ICON_MD_WIFI_1_BAR // ICON_MD_RADIO_BUTTON_UNCHECKED // ICON_MD_LENS_BLUR
static int flags[4] = {0};
char *toolbar = va("%s%s%s%s",
flags[3] ? ICON_MD_STAR : ICON_MD_STAR_OUTLINE, // ICON_MD_BOOKMARK : ICON_MD_BOOKMARK_BORDER, // flags[3] == 0 ? ICON_MD_STAR_OUTLINE : flags[3] == 1 ? ICON_MD_STAR_HALF : ICON_MD_STAR,
@ -239,7 +201,10 @@ void node_edit(node *n, node *root) {
int choice = ui_label2_toolbar(section, toolbar);
if( choice ) flags[ choice - 1 ] = (flags[ choice - 1 ] + 1 ) % ( choice == 4 ? 2/*3*/ : 2);
reflect_iterate_fields( node_type(n), n->v.ptr, reflect_ui, NULL ); // @fixme v.ptr
for each_member( node_type(n), R ) {
reflect_ui(R->type, R->name, R->info, n->v.ptr); // @fixme v.ptr
}
}
}
@ -269,7 +234,7 @@ void editor_reset() {
}
void editor_frame() {
editor_init(); // old editor interface
editor_tick(); // old editor interface
editor_tick_(); // old editor interface
editor_menubar(); // old editor interface
if( input_down(KEY_F5) ) {
@ -422,42 +387,42 @@ int main() {
STRUCT( my_sprite, vec3, position, "Position" );
STRUCT( my_sprite, float, tilt, "Tilt degrees" );
STRUCT( my_sprite, color, tint, "Tint color" );
STRUCT_CTOR( my_sprite, my_sprite_ctor );
PRINTF("pod:%d, var:%d, node:%d warn\n", (int)sizeof(pod), (int)sizeof(var), (int)sizeof(node));
PRINTF("reflected:%d bytes vs real:%d bytes warn\n", reflect_sizeof("my_sprite"), (int)sizeof(my_sprite));
// PRINTF("reflected:%d bytes vs real:%d bytes warn\n", reflect_sizeof("my_sprite"), (int)sizeof(my_sprite));
// cook_config("../../tools/cook.ini");
window_create(0.80, 0);
struct my_sprite spr1 = {0}, spr2 = {0}, spr3 = {0};
obj_make(&spr1,
"[my_sprite]\n"
"filename=cat.png\n"
"position=5 2 100\n"
"tilt=45 + 45 -90\n"
"tint=255 255 0\n"
);
obj_make(&spr2,
"[my_sprite]\n"
"filename=cat.png\n"
"position=1 2 100\n"
"tilt=45 + 45 -90\n"
"tint=255 0 0\n"
);
obj_make(&spr3,
"[my_sprite]\n"
"filename=cat.png\n"
"position=1 2 100\n"
"tilt=45\n"
"tint=0 0 255\n"
);
struct my_sprite
spr1 = {
.filename="cat.png",
.position = vec3(5, 2, 100),
.tilt=45 + 45 -90,
.tint=vec4(255, 255, 0, 255)
},
spr2 = {
.filename="cat.png",
.position = vec3(1, 2, 100),
.tilt=45 + 45 -90,
.tint=vec4(255, 0, 0, 255)
},
spr3 = {
.filename="cat.png",
.position = vec3(1, 2, 100),
.tilt=45,
.tint=vec4(0, 0, 255, 255)
};
my_sprite_ctor(&spr1);
my_sprite_ctor(&spr2);
my_sprite_ctor(&spr3);
int hero1 = editor_spawn("/hero1", "my_sprite", &spr1);
int hero2 = editor_spawn("/hero2", "my_sprite", &spr2);
int hero3 = editor_spawn("/hero1/heroB", "my_sprite", &spr3);
camera_t cam = camera();
camera_enable(&cam);
while( window_swap() ) {
editor_frame();

View File

@ -102,12 +102,12 @@ void editor_init() {
map_init_ptr(editor_dicts);
set_init_ptr(editor_world);
set_init_ptr(editor_selection);
profile_enable( false );
profiler_enable( false );
window_pause( true );
}
}
void editor_tick() {
void editor_tick_() {
// timing
editor_dt = window_delta() * !window_has_pause(); if(editor_dt > 1/60.f) editor_dt = 1/60.f;
}
@ -155,10 +155,10 @@ enum editor_keys {
void editor_menubar() {
do_once editor_init();
int alts = input(KEY_LALT) || input(KEY_RALT); // @todo: move to v4k.c
int ctrls = input(KEY_LCTRL) || input(KEY_RCTRL); // @todo: move to v4k.c
int shifts = input(KEY_LSHIFT) || input(KEY_RSHIFT); // @todo: move to v4k.c
int mods = alts || ctrls || shifts; // @todo: move to v4k.c
int alts = input(KEY_LALT) || input(KEY_RALT); // @todo: move to fwk.c
int ctrls = input(KEY_LCTRL) || input(KEY_RCTRL); // @todo: move to fwk.c
int shifts = input(KEY_LSHIFT) || input(KEY_RSHIFT); // @todo: move to fwk.c
int mods = alts || ctrls || shifts; // @todo: move to fwk.c
if( input_down(KEY_F5) ) editor_key = key_reload;
if( input_down(KEY_F11) ) editor_key = key_fullscreen;
if( input_down(KEY_PAUSE) ) editor_key = key_pause;
@ -332,7 +332,7 @@ void editor_menubar() {
break; case key_outliner: ui_show("Outliner", ui_visible("Outliner") ^ true);
break; case key_recording: name = file_counter(va("%s.mp4",app_name())), window_record(name), ui_notify(va("Video capturing: %s", name), date_string());
break; case key_screenshot: name = file_counter(va("%s.png",app_name())), window_screenshot(name), ui_notify(va("Screenshot: %s", name), date_string());
break; case key_profiler: ui_show("Profiler", profile_enable(ui_visible("Profiler") ^ true));
break; case key_profiler: ui_show("Profiler", profiler_enable(ui_visible("Profiler") ^ true));
break; case key_fullscreen: record_stop(), window_fullscreen( window_has_fullscreen() ^ 1 ); // framebuffer resizing corrupts video stream, so stop any recording beforehand
break; case key_gamepad: editor_gamepad ^= 1;
break; case key_lit: editor_lit ^= 1;

View File

@ -1,6 +1,6 @@
//#define META_DEMO
#include "v4k.h"
#include "fwk.h"
#include <string.h>
#include <stdint.h>

View File

@ -1,5 +1,5 @@
#define V4K_C
#include "v4k.h"
#define FWK_C
#include "fwk.h"
bool parse_struct(const char *line) {
return strstr(line, "s""truct ");

View File

@ -21,9 +21,9 @@ cl ..\editor2.c -I ..\..\tools -DCOOK_ON_DEMAND
pushd ..\.. && call make amalgamation && popd
taskkill /im "oscedit.exe" > nul 2> nul
call ..\..\tools\tcc oscgame.c -I ..\.. -DV4K_IMPLEMENTATION -DCOOK_ON_DEMAND %*
call ..\..\tools\tcc oscsend.c -I ..\.. -DV4K_IMPLEMENTATION -DCOOK_ON_DEMAND %*
call ..\..\tools\tcc oscedit.c -I ..\.. -DV4K_IMPLEMENTATION -DCOOK_ON_DEMAND %* && start oscedit.exe
call ..\..\tools\tcc oscgame.c -I ..\.. -DFWK_IMPLEMENTATION -DCOOK_ON_DEMAND %*
call ..\..\tools\tcc oscsend.c -I ..\.. -DFWK_IMPLEMENTATION -DCOOK_ON_DEMAND %*
call ..\..\tools\tcc oscedit.c -I ..\.. -DFWK_IMPLEMENTATION -DCOOK_ON_DEMAND %* && start oscedit.exe
timeout 3

View File

@ -1,4 +1,4 @@
#include "v4k.h"
#include "fwk.h"
#include "oscedit.h"
// demo

View File

@ -1,4 +1,4 @@
#include "v4k.h"
#include "fwk.h"
#include "oscedit.h"
// game

View File

@ -1,4 +1,4 @@
#include "v4k.h"
#include "fwk.h"
#define OSCPACK_C
#define OSCRECV_C

View File

@ -1,7 +1,7 @@
// networked gui demo
// - rlyeh, public domain
#include "v4k.h"
#include "fwk.h"
#define OSCPACK_C
#define OSCRECV_C
@ -46,7 +46,6 @@ int main() {
// camera
camera_t cam = camera();
cam.speed = 0.2f;
// demo loop
while (window_swap())
@ -61,7 +60,7 @@ int main() {
if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f);
vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active);
vec3 wasdecq = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-(input(KEY_C)||input(KEY_Q)),input(KEY_W)-input(KEY_S)), cam.speed);
camera_move(&cam, wasdecq.x,wasdecq.y,wasdecq.z);
camera_moveby(&cam, wasdecq);
camera_fps(&cam, mouse.x,mouse.y);
// queue model scale bounces

View File

@ -1,4 +1,4 @@
#include "v4k.h"
#include "fwk.h"
#include "oscsend.h"
#include "oscedit.h"
int main(int argc, char **argv) {

View File

@ -0,0 +1,86 @@
given a string A, we want it to be B.
A: hello world and thanks for the fish.
B: hello cruel o_o world and thanks for the fish!
however, the instructions to reconstruct B must be as small as possible, to minimize transmission costs. this is why we dont send B entirely.
different algorithms as follow:
## ALGORITHM 1
- identify the first mismatching character (S).
v-- 6(S)
A: hello world and thanks for the fish.
B: hello cruel o_o world and thanks for the fish!
- identify the last mismatch character (E).
v-- 0(E)
A: hello world and thanks for the fish.
B: hello cruel o_o world and thanks for the fish!
- we construct the patch now with 3 numbers:
- number of bytes to copy from beginning(A) till S(A)
- number of bytes to copy from S(B) till E(B). plus the substring that is get copied.
- number of bytes to copy from E(A) till end of string.
6 40 "cruel o_o world and thanks for the fish!" 0
- total patch size is 3 control bytes + 40 (string) = 43 bytes
## ALGORITHM 2
- We delta every character in both strings, from beginning to end, and from end to beginning.
A: hello world and thanks for the fish.0000000000
B: hello cruel o_o world and thanks for the fish!
C: 000000XXXXX0XXX0XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX (-)
A: 0000000000hello world and thanks for the fish.
B: hello cruel o_o world and thanks for the fish!
C: XXXXXXXXXXXXXX0000000000000000000000000000000X (-)
- Pick the option with most zeros (2nd). Aka, select the option with less Xs.
do { Encode every XXXX island in C as a positive operation indicating how many bytes to copy from.
Run-length the number of zeros into a negative operation.
} repeat till lane is exhausted.
XXXXXXXXXXXXXXX: +14 "hello cruel o_"
0000000000000000000000000000000: -29
X: +1 "!"
- Patch size is 3 control bytes + 14 (string) + 1 (string) = 18 bytes
## ALGORITHM 3
- start source (A) and target (B) strings. start with A:
- select how many bytes to copy (positive), how many to skip (negative), or switch A<-->B lanes (zero).
- repeat previous step over and over until both lanes do reach their respective string ends.
A: hello world and thanks for the fish.
B: hello cruel o_o world and thanks for the fish!
A: +6 0 >> hello
B: -6 +10 0 >> hello cruel o_o
A: +24 -1 0 >> hello cruel o_o world and thanks for the fish
B: -29 +1 >> hello cruel o_o world and thanks for the fish!
(10 bytes)
what if we always specify a dual +/- operation per lane?
lanes changed automatically after every tuple. in this case a 0 shall indicate a no op.
A: +6 0 >> hello
B: -6 +10 >> hello cruel o_o
A: +24 -1 >> hello cruel o_o world and thanks for the fish
B: -29 +1 >> hello cruel o_o world and thanks for the fish!
(8 bytes)
- we construct the patch now. a substring excerpt must come after any positive operations on lane B.
+6 0 -6 +10 "cruel o_o " +24 -1 -29 +1 "!"
- total patch size is 8 control bytes + 10 (string) + 1 (string) = 19 bytes

View File

@ -0,0 +1,193 @@
// object: method dispatch tables
#define ctor(obj) obj_method0(obj, ctor) // ctor[obj_typeid(obj)](obj)
#define dtor(obj) obj_method0(obj, dtor) // dtor[obj_typeid(obj)](obj)
API extern void (*ctor[256])(); ///-
API extern void (*dtor[256])(); ///-
const char *obj_typeof( const void *obj ) {
int obj_typeeq(a,b)
// ---
// ---
void *obj_copy(void **dst, const void *src) {
if(!*dst) return *dst = obj_clone(src);
if( obj_typeeq(*dst, src) ) {
return memcpy(*dst, src, obj_sizeof(src));
}
return NULL;
}
void *obj_mutate(void **dst_, const void *src) {
// mutate a class. ie, convert a given object class into a different one,
// while preserving the original metas and references as much as possible.
//
// @fixme: systems might be tracking objects in the future. the fact that we
// can reallocate a pointer (and hence, change its address) seems way too dangerous,
// as the tracking systems could crash when referencing a mutated object.
// solutions: do not reallocate if sizeof(new_class) > sizeof(old_class) maybe? good enough?
// also, optimization hint: no need to reallocate if both sizes matches, just copy contents.
if(!*dst_) return *dst_ = obj_clone(src);
void *dst = *dst_;
dtor(dst);
unsigned src_sz = obj_sizeof(src);
unsigned src_id = obj_typeid(src);
void *dst_ptr = *((void**)dst - 1);
unsigned payload = (OBJPAYLOAD16(dst_ptr) & 255) | src_id << 8;
FREE( OBJUNBOX(dst_ptr) );
*((void**)dst - 1) = OBJBOX( STRDUP( OBJUNBOX(*((void**)src - 1)) ), payload);
void *base = (void*)((void**)dst - 1);
base = REALLOC(base, src_sz + sizeof(void*));
*dst_ = (char*)base + sizeof(void*);
dst = (char*)base + sizeof(void*);
memcpy(dst, src, src_sz);
ctor(dst);
return dst;
}
#ifdef OBJ_DEMO
typedef struct MyObject {
char* id;
int x,y;
float rotation;
struct MyObject *next;
} MyObject;
void tests1() {
// Construct two objects
MyObject *root = obj_new(MyObject, 0);
MyObject *obj = obj_new(MyObject, "An identifier!", 0x11, 0x22, 3.1415f, root );
// Dump contents of our objects
obj_hexdump(root);
obj_hexdump(obj);
// Save to mem
char* buffer = obj_save(obj);
printf("%d bytes\n", (int)strlen(buffer));
// Clear
obj_zero( obj );
obj_hexdump( obj );
// Reload
obj_load( obj, buffer );
obj_hexdump( obj );
// Copy tests
{
MyObject *clone = obj_clone(obj);
obj_hexdump(clone);
obj_del(clone);
}
{
MyObject *copy = 0;
obj_copy(&copy, obj);
obj_hexdump(copy);
obj_del(copy);
}
{
MyObject *copy = obj_new(MyObject, "A different identifier!", 0x33, 0x44, 0.0f, root );
obj_copy(&copy, obj);
obj_hexdump(copy);
obj_del(copy);
}
{
void *copy = obj_malloc(100, "an untyped class" );
obj_mutate(&copy, obj);
obj_hexdump(copy);
obj_copy(&copy, obj);
obj_hexdump(copy);
obj_del(copy);
}
// Benchmarking call overhead.
// We're here using dtor as a method to test. Since there is actually no
// destructor associated to this class, it will be safe to call it extensively (no double frees).
//
// results:
// 427 million calls/s @ old i5-4300/1.90Ghz laptop. compiled with "cl /Ox /Os /MT /DNDEBUG /GL /GF /arch:AVX2"
#ifndef N
#define N (INT32_MAX-1)
#endif
double t = (puts("benchmarking..."), -clock() / (double)CLOCKS_PER_SEC);
for( int i = 0; i < N; ++i ) {
dtor(root);
}
t += clock() / (double)CLOCKS_PER_SEC;
printf("Benchmark: %5.2f objcalls/s %5.2fM objcalls/s\n", N/(t), (N/1000)/(t*1000)); // ((N+N)*5) / (t) );
}
void tests2() {
REGISTER_BOX
REGISTER_RECT
box *b = obj_new(box, 100);
rect *r = obj_new(rect, 100, 200);
dump(b);
dump(r);
printf("%f\n", area(b));
printf("%f\n", area(r));
obj_del(b);
obj_ref(r); obj_unref(r); //obj_del(r);
int *untyped = obj_malloc( sizeof(int) );
int *my_number = obj_malloc( sizeof(int), "a comment about my_number" );
char *my_text = obj_malloc( 32, "some debug info here" );
*untyped = 100;
*my_number = 123;
sprintf( my_text, "hello world" );
struct my_bitmap { int w, h, bpp; const char *pixels; };
struct my_bitmap *my_bitmap = obj_new(struct my_bitmap, 2,2,8, "\1\2\3\4");
printf( "%p(%s,%u)\n", my_bitmap, obj_typeof(my_bitmap), obj_typeid(my_bitmap) );
printf( "%d(%s,%d)\n", *untyped, obj_typeof(untyped), obj_typeid(untyped) );
printf( "%d(%s,%d)\n", *my_number, obj_typeof(my_number), obj_typeid(my_number) );
printf( "%s(%s,%d)\n", my_text, obj_typeof(my_text), obj_typeid(my_text) );
obj_printf(my_text, "hello world #1\n");
obj_printf(my_text, "hello world #2\n");
puts(obj_output(my_text));
printf( "%s(%s,%d)\n", my_text, obj_typeof(my_text), obj_typeid(my_text) );
printf( "equal?:%d\n", obj_typeeq(my_number, untyped) );
printf( "equal?:%d\n", obj_typeeq(my_number, my_number) );
printf( "equal?:%d\n", obj_typeeq(my_number, my_text) );
printf( "equal?:%d\n", obj_typeeq(my_number, my_bitmap) );
obj_free( untyped );
obj_free( my_text );
obj_free( my_bitmap );
obj_del( my_number ); // should not crash, even if allocated with obj_malloc()
}

View File

@ -0,0 +1,18 @@
[myWindow]
x=0.166667
y=0.194444
w=0.666667
h=0.666667
visible=1
[Outliner]
x=-0.000000
y=0.107495
w=0.249929
h=0.333728
visible=1
[Properties]
x=0.749973
y=0.052117
w=0.250028
h=0.333860
visible=1