2023-07-30 19:18:50 +00:00
// editing:
// nope > functions: add/rem property
2023-10-13 10:59:44 +00:00
# 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
2023-07-30 19:18:50 +00:00
char * editor_path ( const char * path ) {
return va ( " %s/%s " , EDITOR , path ) ;
}
vec3 editor_pick ( float mouse_x , float mouse_y ) {
2023-10-10 08:52:29 +00:00
#if 0
// unproject 2d coord as 3d coord
camera_t * camera = camera_get_active ( ) ;
vec3 out , xyd = vec3 ( mouse_x , window_height ( ) - mouse_y , 1 ) ; // usually x:mouse_x,y:window_height()-mouse_y,d:0=znear/1=zfar
mat44 mvp , model ; identity44 ( model ) ; multiply44x3 ( mvp , camera - > proj , camera - > view , model ) ;
bool ok = unproject44 ( & out , xyd , vec4 ( 0 , 0 , window_width ( ) , window_height ( ) ) , mvp ) ;
return out ;
# else
2023-07-30 19:18:50 +00:00
// unproject 2d coord as 3d coord
2023-10-11 19:01:06 +00:00
vec2 dpi = ifdef ( osx , window_dpi ( ) , vec2 ( 1 , 1 ) ) ;
2023-07-30 19:18:50 +00:00
camera_t * camera = camera_get_active ( ) ;
2023-10-10 19:10:22 +00:00
float x = ( 2.0f * mouse_x ) / ( dpi . x * window_width ( ) ) - 1.0f ;
float y = 1.0f - ( 2.0f * mouse_y ) / ( dpi . y * window_height ( ) ) ;
2023-10-09 21:47:23 +00:00
float z = 1.0f ;
vec3 ray_nds = vec3 ( x , y , z ) ;
vec4 ray_clip = vec4 ( ray_nds . x , ray_nds . y , - 1.0 , 1.0 ) ;
mat44 inv_proj ; invert44 ( inv_proj , camera - > proj ) ;
mat44 inv_view ; invert44 ( inv_view , camera - > view ) ;
vec4 p = transform444 ( inv_proj , ray_clip ) ;
vec4 eye = vec4 ( p . x , p . y , - 1.0 , 0.0 ) ;
vec4 wld = norm4 ( transform444 ( inv_view , eye ) ) ;
return vec3 ( wld . x , wld . y , wld . z ) ;
2023-10-10 08:52:29 +00:00
# endif
2023-07-30 19:18:50 +00:00
}
2023-10-26 07:27:46 +00:00
#if 0
2023-07-30 19:18:50 +00:00
int editor_ui_bits8 ( const char * label , uint8_t * enabled ) { // @to deprecate
int clicked = 0 ;
uint8_t copy = * enabled ;
// @fixme: better way to retrieve widget width? nk_layout_row_dynamic() seems excessive
nk_layout_row_dynamic ( ui_ctx , 1 , 1 ) ;
struct nk_rect bounds = nk_widget_bounds ( ui_ctx ) ;
// actual widget: label + 8 checkboxes
enum { HEIGHT = 18 , BITS = 8 , SPAN = 118 } ; // bits widget below needs at least 118px wide
nk_layout_row_begin ( ui_ctx , NK_STATIC , HEIGHT , 1 + BITS ) ;
int offset = bounds . w > SPAN ? bounds . w - SPAN : 0 ;
nk_layout_row_push ( ui_ctx , offset ) ;
if ( ui_label_ ( label , NK_TEXT_LEFT ) ) clicked = 1 < < 31 ;
for ( int i = 0 ; i < BITS ; + + i ) {
nk_layout_row_push ( ui_ctx , 10 ) ;
// bit
int val = ( * enabled > > i ) & 1 ;
int chg = nk_checkbox_label ( ui_ctx , " " , & val ) ;
* enabled = ( * enabled & ~ ( 1 < < i ) ) | ( ( ! ! val ) < < i ) ;
// tooltip
struct nk_rect bb = { offset + 10 + i * 14 , bounds . y , 14 , HEIGHT } ; // 10:padding,14:width
if ( nk_input_is_mouse_hovering_rect ( & ui_ctx - > input , bb ) & & ! ui_popups ( ) ) {
const char * tips [ BITS ] = { " Init " , " Tick " , " Draw " , " Quit " , " " , " " , " " , " " } ;
if ( tips [ i ] [ 0 ] ) nk_tooltipf ( ui_ctx , " %s " , tips [ i ] ) ;
}
}
nk_layout_row_end ( ui_ctx ) ;
return clicked | ( copy ^ * enabled ) ;
}
2023-10-26 07:27:46 +00:00
# endif
2023-07-30 19:18:50 +00:00
2023-10-13 10:59:44 +00:00
2023-10-26 07:27:46 +00:00
typedef union engine_var {
2023-10-13 10:59:44 +00:00
int i ;
float f ;
char * s ;
2023-10-26 07:27:46 +00:00
} engine_var ;
static map ( char * , engine_var ) engine_vars ;
float * engine_getf ( const char * key ) {
if ( ! engine_vars ) map_init_str ( engine_vars ) ;
engine_var * found = map_find_or_add ( engine_vars , ( char * ) key , ( ( engine_var ) { 0 } ) ) ;
2023-10-13 10:59:44 +00:00
return & found - > f ;
}
2023-10-26 07:27:46 +00:00
int * engine_geti ( const char * key ) {
if ( ! engine_vars ) map_init_str ( engine_vars ) ;
engine_var * found = map_find_or_add ( engine_vars , ( char * ) key , ( ( engine_var ) { 0 } ) ) ;
2023-10-13 10:59:44 +00:00
return & found - > i ;
}
2023-10-26 07:27:46 +00:00
char * * engine_gets ( const char * key ) {
if ( ! engine_vars ) map_init_str ( engine_vars ) ;
engine_var * found = map_find_or_add ( engine_vars , ( char * ) key , ( ( engine_var ) { 0 } ) ) ;
2023-10-13 10:59:44 +00:00
if ( ! found - > s ) found - > s = stringf ( " %s " , " " ) ;
return & found - > s ;
}
2023-10-26 07:27:46 +00:00
int engine_send ( const char * cmd , const char * optional_value ) {
unsigned * gamepads = engine_geti ( " gamepads " ) ; // 0 off, mask gamepad1(1), gamepad2(2), gamepad3(4), gamepad4(8)...
unsigned * renders = engine_geti ( " renders " ) ; // 0 off, mask: 1=lit, 2=ddraw, 3=whiteboxes
float * speed = engine_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 = engine_geti ( " powersave " ) ;
2023-10-13 10:59:44 +00:00
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 ) ;
2023-10-20 17:55:43 +00:00
else if ( ! strcmp ( cmd , " key_record " ) ) if ( record_active ( ) ) record_stop ( ) , ui_notify ( va ( " Video recorded " ) , date_string ( ) ) ; else
app_beep ( ) , name = file_counter ( va ( " %s.mp4 " , app_name ( ) ) ) , window_record ( name ) ;
2023-10-13 10:59:44 +00:00
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 ;
}
2023-10-26 07:27:46 +00:00
int engine_tick ( ) {
enum { engine_hz = 60 } ;
2023-10-26 07:38:50 +00:00
enum { engine_hz_mid = 30 } ;
2023-10-26 07:27:46 +00:00
enum { engine_hz_low = 5 } ;
if ( * engine_geti ( " powersave " ) ) {
2023-10-13 10:59:44 +00:00
// adaptive framerate
int app_on_background = ! window_has_focus ( ) ;
2023-10-26 07:27:46 +00:00
int hz = app_on_background ? engine_hz_low : engine_hz_mid ;
2023-10-13 10:59:44 +00:00
window_fps_lock ( hz < 5 ? 5 : hz ) ;
} else {
// window_fps_lock( editor_hz );
2023-10-26 07:38:50 +00:00
window_fps_unlock ( ) ;
2023-10-13 10:59:44 +00:00
}
return 0 ;
}
2023-07-30 19:18:50 +00:00
static int gizmo__mode ;
static int gizmo__active ;
static int gizmo__hover ;
bool gizmo_active ( ) {
return gizmo__active ;
}
bool gizmo_hover ( ) {
return gizmo__hover ;
}
int gizmo ( vec3 * pos , vec3 * rot , vec3 * sca ) {
#if 0
ddraw_flush ( ) ;
mat44 copy ; copy44 ( copy , camera_get_active ( ) - > view ) ;
if ( 1 ) {
float * mv = camera_get_active ( ) - > view ;
float d = sqrt ( mv [ 4 * 0 + 0 ] * mv [ 4 * 0 + 0 ] + mv [ 4 * 1 + 1 ] * mv [ 4 * 1 + 1 ] + mv [ 4 * 2 + 2 ] * mv [ 4 * 2 + 2 ] ) ;
if ( 4 ) mv [ 4 * 0 + 0 ] = d , mv [ 4 * 0 + 1 ] = 0 , mv [ 4 * 0 + 2 ] = 0 ;
if ( 2 ) mv [ 4 * 1 + 0 ] = 0 , mv [ 4 * 1 + 1 ] = d , mv [ 4 * 1 + 2 ] = 0 ;
if ( 1 ) mv [ 4 * 2 + 0 ] = 0 , mv [ 4 * 2 + 1 ] = 0 , mv [ 4 * 2 + 2 ] = d ;
}
# endif
ddraw_color_push ( dd_color ) ;
ddraw_ontop_push ( 1 ) ;
int enabled = ! ui_active ( ) & & ! ui_hover ( ) ;
vec3 mouse = enabled ? vec3 ( input ( MOUSE_X ) , input ( MOUSE_Y ) , input_down ( MOUSE_L ) ) : vec3 ( 0 , 0 , 0 ) ; // x,y,l
vec3 from = camera_get_active ( ) - > position ;
vec3 to = editor_pick ( mouse . x , mouse . y ) ;
ray r = ray ( from , to ) ;
static vec3 src3 , hit3 , off3 ; static vec2 src2 ;
# define on_gizmo_dragged(X,Y,Z,COLOR,DRAWCMD, ...) do { \
vec3 dir = vec3 ( X , Y , Z ) ; \
line axis = { add3 ( * pos , scale3 ( dir , 100 ) ) , add3 ( * pos , scale3 ( dir , - 100 ) ) } ; \
plane ground = { vec3 ( 0 , 0 , 0 ) , vec3 ( Y ? 1 : 0 , Y ? 0 : 1 , 0 ) } ; \
vec3 unit = vec3 ( X + ( 1.0 - X ) * 0.3 , Y + ( 1.0 - Y ) * 0.3 , Z + ( 1.0 - Z ) * 0.3 ) ; \
aabb arrow = { sub3 ( * pos , unit ) , add3 ( * pos , unit ) } ; \
hit * hit_arrow = ray_hit_aabb ( r , arrow ) , * hit_ground = ray_hit_plane ( r , ground ) ; \
ddraw_color ( hit_arrow | | gizmo__active = = ( X * 4 + Y * 2 + Z ) ? gizmo__hover = 1 , YELLOW : COLOR ) ; \
DRAWCMD ; \
if ( ! gizmo__active & & hit_arrow & & mouse . z ) src2 = vec2 ( mouse . x , mouse . y ) , src3 = * pos , hit3 = hit_ground - > p , off3 = mul3 ( sub3 ( src3 , hit3 ) , vec3 ( X , Y , Z ) ) , gizmo__active = X * 4 + Y * 2 + Z ; \
if ( ( gizmo__active & & gizmo__active = = ( X * 4 + Y * 2 + Z ) ) | | ( ! gizmo__active & & hit_arrow ) ) { ddraw_color ( COLOR ) ; ( 1 ? ddraw_line : ddraw_line_dashed ) ( axis . a , axis . b ) ; } \
if ( gizmo__active = = ( X * 4 + Y * 2 + Z ) & & hit_ground ) { { __VA_ARGS__ } ; modified = 1 ; gizmo__active * = ! ! input ( MOUSE_L ) ; } \
} while ( 0 )
# define gizmo_translate(X,Y,Z,COLOR) \
on_gizmo_dragged ( X , Y , Z , COLOR , ddraw_arrow ( * pos , add3 ( * pos , vec3 ( X , Y , Z ) ) ) , { \
* pos = add3 ( line_closest_point ( axis , hit_ground - > p ) , off3 ) ; \
} )
# define gizmo_scale(X,Y,Z,COLOR) \
on_gizmo_dragged ( X , Y , Z , COLOR , ( ddraw_line ( * pos , add3 ( * pos , vec3 ( X , Y , Z ) ) ) , ddraw_sphere ( add3 ( * pos , vec3 ( X - 0.1 * X , Y - 0.1 * Y , Z - 0.1 * Z ) ) , 0.1 ) ) , { /*ddraw_aabb(arrow.min,arrow.max)*/ \
int component = ( X * 1 + Y * 2 + Z * 3 ) - 1 ; \
float mag = len2 ( sub2 ( vec2 ( mouse . x , mouse . y ) , src2 ) ) ; \
float magx = ( mouse . x - src2 . x ) * ( mouse . x - src2 . x ) ; \
float magy = ( mouse . y - src2 . y ) * ( mouse . y - src2 . y ) ; \
float sgn = ( magx > magy ? mouse . x > src2 . x : mouse . y > src2 . y ) ? 1 : - 1 ; \
sca - > v3 [ component ] - = sgn * mag * 0.01 ; \
src2 = vec2 ( mouse . x , mouse . y ) ; \
} )
# define gizmo_rotate(X,Y,Z,COLOR) do { \
vec3 dir = vec3 ( X , Y , Z ) ; \
line axis = { add3 ( * pos , scale3 ( dir , 100 ) ) , add3 ( * pos , scale3 ( dir , - 100 ) ) } ; \
plane ground = { vec3 ( 0 , 0 , 0 ) , vec3 ( 0 , 1 , 0 ) } ; \
vec3 unit = vec3 ( X + ( 1.0 - X ) * 0.3 , Y + ( 1.0 - Y ) * 0.3 , Z + ( 1.0 - Z ) * 0.3 ) ; \
aabb arrow = { sub3 ( * pos , unit ) , add3 ( * pos , unit ) } ; \
hit * hit_arrow = ray_hit_aabb ( r , arrow ) , * hit_ground = ray_hit_plane ( r , ground ) ; \
int hover = ( hit_arrow ? ( X * 4 + Y * 2 + Z ) : 0 ) ; \
if ( gizmo__active = = ( X * 4 + Y * 2 + Z ) ) { ddraw_color ( gizmo__active ? gizmo__hover = 1 , YELLOW : WHITE ) ; ddraw_circle ( * pos , vec3 ( X , Y , Z ) , 1 ) ; } \
else if ( ! gizmo__active & & hover = = ( X * 4 + Y * 2 + Z ) ) { gizmo__hover = 1 ; ddraw_color ( COLOR ) ; ddraw_circle ( * pos , vec3 ( X , Y , Z ) , 1 ) ; } \
else if ( ! gizmo__active ) { ddraw_color ( WHITE ) ; ddraw_circle ( * pos , vec3 ( X , Y , Z ) , 1 ) ; } \
if ( ! gizmo__active & & hit_arrow & & mouse . z ) src2 = vec2 ( mouse . x , mouse . y ) , gizmo__active = hover ; \
if ( ( ! gizmo__active & & hover = = ( X * 4 + Y * 2 + Z ) ) | | gizmo__active = = ( X * 4 + Y * 2 + Z ) ) { gizmo__hover = 1 ; ddraw_color ( COLOR ) ; ( 1 ? ddraw_line_thin : ddraw_line_dashed ) ( axis . a , axis . b ) ; } \
if ( gizmo__active & & gizmo__active = = ( X * 4 + Y * 2 + Z ) & & hit_ground & & enabled ) { \
int component = ( Y * 1 + X * 2 + Z * 3 ) - 1 ; /*pitch,yaw,roll*/ \
float mag = len2 ( sub2 ( vec2 ( mouse . x , mouse . y ) , src2 ) ) ; \
float magx = ( mouse . x - src2 . x ) * ( mouse . x - src2 . x ) ; \
float magy = ( mouse . y - src2 . y ) * ( mouse . y - src2 . y ) ; \
float sgn = ( magx > magy ? mouse . x > src2 . x : mouse . y > src2 . y ) ? 1 : - 1 ; \
rot - > v3 [ component ] + = sgn * mag ; \
/*rot->v3[component] = clampf(rot->v3[component], -360, +360);*/ \
src2 = vec2 ( mouse . x , mouse . y ) ; \
\
} \
gizmo__active * = enabled & & ! ! input ( MOUSE_L ) ; \
} while ( 0 )
gizmo__hover = 0 ;
int modified = 0 ;
if ( enabled & & input_down ( KEY_SPACE ) ) gizmo__active = 0 , gizmo__mode = ( gizmo__mode + 1 ) % 3 ;
if ( gizmo__mode = = 0 ) gizmo_translate ( 1 , 0 , 0 , RED ) ;
if ( gizmo__mode = = 0 ) gizmo_translate ( 0 , 1 , 0 , GREEN ) ;
if ( gizmo__mode = = 0 ) gizmo_translate ( 0 , 0 , 1 , BLUE ) ;
if ( gizmo__mode = = 1 ) gizmo_scale ( 1 , 0 , 0 , RED ) ;
if ( gizmo__mode = = 1 ) gizmo_scale ( 0 , 1 , 0 , GREEN ) ;
if ( gizmo__mode = = 1 ) gizmo_scale ( 0 , 0 , 1 , BLUE ) ;
if ( gizmo__mode = = 2 ) gizmo_rotate ( 1 , 0 , 0 , RED ) ;
if ( gizmo__mode = = 2 ) gizmo_rotate ( 0 , 1 , 0 , GREEN ) ;
if ( gizmo__mode = = 2 ) gizmo_rotate ( 0 , 0 , 1 , BLUE ) ;
#if 0
ddraw_flush ( ) ;
copy44 ( camera_get_active ( ) - > view , copy ) ;
# endif
ddraw_ontop_pop ( ) ;
ddraw_color_pop ( ) ;
return modified ;
}
// -- localization kit
static const char * kit_lang = " enUS " , * kit_langs =
" enUS,enGB, "
" frFR, "
" esES,esAR,esMX, "
" deDE,deCH,deAT, "
" itIT,itCH, "
" ptBR,ptPT, "
" zhCN,zhSG,zhTW,zhHK,zhMO, "
" ruRU,elGR,trTR,daDK,noNB,svSE,nlNL,plPL,fiFI,jaJP, "
" koKR,csCZ,huHU,roRO,thTH,bgBG,heIL "
;
static map ( char * , char * ) kit_ids ;
static map ( char * , char * ) kit_vars ;
# ifndef KIT_FMT_ID2
# define KIT_FMT_ID2 "%s.%s"
# endif
void kit_init ( ) {
do_once map_init ( kit_ids , less_str , hash_str ) ;
do_once map_init ( kit_vars , less_str , hash_str ) ;
}
void kit_insert ( const char * id , const char * translation ) {
char * id2 = va ( KIT_FMT_ID2 , kit_lang , id ) ;
char * * found = map_find_or_add_allocated_key ( kit_ids , STRDUP ( id2 ) , NULL ) ;
if ( * found ) FREE ( * found ) ;
* found = STRDUP ( translation ) ;
}
bool kit_merge ( const char * filename ) {
// @todo: xlsx2ini
return false ;
}
void kit_clear ( ) {
map_clear ( kit_ids ) ;
}
bool kit_load ( const char * filename ) {
return kit_clear ( ) , kit_merge ( filename ) ;
}
void kit_set ( const char * key , const char * value ) {
value = value & & value [ 0 ] ? value : " " ;
char * * found = map_find_or_add_allocated_key ( kit_vars , STRDUP ( key ) , NULL ) ;
if ( * found ) FREE ( * found ) ;
* found = STRDUP ( value ) ;
}
void kit_reset ( ) {
map_clear ( kit_vars ) ;
}
char * kit_translate2 ( const char * id , const char * lang ) {
char * id2 = va ( KIT_FMT_ID2 , lang , id ) ;
char * * found = map_find ( kit_ids , id2 ) ;
// return original [[ID]] if no translation is found
if ( ! found ) return va ( " [[%s]] " , id ) ;
// return translation if no {{moustaches}} are found
if ( ! strstr ( * found , " {{ " ) ) return * found ;
// else replace all found {{moustaches}} with context vars
{
// make room
static __thread char * results [ 16 ] = { 0 } ;
static __thread unsigned counter = 0 ; counter = ( counter + 1 ) % 16 ;
char * buffer = results [ counter ] ;
if ( buffer ) FREE ( buffer ) , buffer = 0 ;
// iterate moustaches
const char * begin , * end , * text = * found ;
while ( NULL ! = ( begin = strstr ( text , " {{ " ) ) ) {
end = strstr ( begin + 2 , " }} " ) ;
if ( end ) {
char * var = va ( " %.*s " , ( int ) ( end - ( begin + 2 ) ) , begin + 2 ) ;
char * * found_var = map_find ( kit_vars , var ) ;
if ( found_var & & 0 [ * found_var ] ) {
strcatf ( & buffer , " %.*s%s " , ( int ) ( begin - text ) , text , * found_var ) ;
} else {
strcatf ( & buffer , " %.*s{{%s}} " , ( int ) ( begin - text ) , text , var ) ;
}
text = end + 2 ;
} else {
strcatf ( & buffer , " %.*s " , ( int ) ( begin - text ) , text ) ;
text = begin + 2 ;
}
}
strcatf ( & buffer , " %s " , text ) ;
return buffer ;
}
}
char * kit_translate ( const char * id ) {
return kit_translate2 ( id , kit_lang ) ;
}
void kit_locale ( const char * lang ) {
kit_lang = STRDUP ( lang ) ; // @leak
}
void kit_dump_state ( FILE * fp ) {
for each_map ( kit_ids , char * , k , char * , v ) {
fprintf ( fp , " [ID ] %s=%s \n " , k , v ) ;
}
for each_map ( kit_vars , char * , k , char * , v ) {
fprintf ( fp , " [VAR] %s=%s \n " , k , v ) ;
}
}
/*
int main ( ) {
kit_init ( ) ;
kit_locale ( " enUS " ) ;
kit_insert ( " HELLO_PLAYERS " , " Hi {{PLAYER1}} and {{PLAYER2}}! " ) ;
kit_insert ( " GREET_PLAYERS " , " Nice to meet you. " ) ;
kit_locale ( " esES " ) ;
kit_insert ( " HELLO_PLAYERS " , " Hola {{PLAYER1}} y {{PLAYER2}}! " ) ;
kit_insert ( " GREET_PLAYERS " , " Un placer conoceros. " ) ;
kit_locale ( " enUS " ) ;
printf ( " %s %s \n " , kit_translate ( " HELLO_PLAYERS " ) , kit_translate ( " GREET_PLAYERS " ) ) ; // Hi {{PLAYER1}} and {{PLAYER2}}! Nice to meet you.
kit_locale ( " esES " ) ;
kit_set ( " PLAYER1 " , " John " ) ;
kit_set ( " PLAYER2 " , " Karl " ) ;
printf ( " %s %s \n " , kit_translate ( " HELLO_PLAYERS " ) , kit_translate ( " GREET_PLAYERS " ) ) ; // Hola John y Karl! Un placer conoceros.
assert ( 0 = = strcmp ( kit_translate ( " NON_EXISTING " ) , " [[NON_EXISTING]] " ) ) ; // [[NON_EXISTING]]
assert ( ~ puts ( " Ok " ) ) ;
}
*/