Replace limiter with dynamic range compressor
[kugel-rb.git] / apps / gui / skin_engine / skin_tokens.c
blob94d8eb4dc6d5991249e93d10439750df4565d1ef
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002-2007 Björn Stenberg
11 * Copyright (C) 2007-2008 Nicolas Pennequin
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
22 #include "font.h"
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include "action.h"
27 #include "system.h"
28 #include "settings.h"
29 #include "settings_list.h"
30 #include "rbunicode.h"
31 #include "timefuncs.h"
32 #include "audio.h"
33 #include "status.h"
34 #include "power.h"
35 #include "powermgmt.h"
36 #include "sound.h"
37 #include "debug.h"
38 #ifdef HAVE_LCD_CHARCELLS
39 #include "hwcompat.h"
40 #endif
41 #include "abrepeat.h"
42 #include "mp3_playback.h"
43 #include "lang.h"
44 #include "misc.h"
45 #include "led.h"
46 #ifdef HAVE_LCD_BITMAP
47 /* Image stuff */
48 #include "albumart.h"
49 #endif
50 #include "dsp.h"
51 #include "playlist.h"
52 #if CONFIG_CODEC == SWCODEC
53 #include "playback.h"
54 #endif
55 #include "viewport.h"
57 #include "wps_internals.h"
58 #include "wps.h"
60 static char* get_codectype(const struct mp3entry* id3)
62 if (id3->codectype < AFMT_NUM_CODECS) {
63 return (char*)audio_formats[id3->codectype].label;
64 } else {
65 return NULL;
69 /* Extract a part from a path.
71 * buf - buffer extract part to.
72 * buf_size - size of buffer.
73 * path - path to extract from.
74 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
75 * parent of parent, etc.
77 * Returns buf if the desired level was found, NULL otherwise.
79 static char* get_dir(char* buf, int buf_size, const char* path, int level)
81 const char* sep;
82 const char* last_sep;
83 int len;
85 sep = path + strlen(path);
86 last_sep = sep;
88 while (sep > path)
90 if ('/' == *(--sep))
92 if (!level)
93 break;
95 level--;
96 last_sep = sep - 1;
100 if (level || (last_sep <= sep))
101 return NULL;
103 len = MIN(last_sep - sep, buf_size - 1);
104 strlcpy(buf, sep + 1, len + 1);
105 return buf;
108 /* Return the tag found at index i and write its value in buf.
109 The return value is buf if the tag had a value, or NULL if not.
111 intval is used with conditionals/enums: when this function is called,
112 intval should contain the number of options in the conditional/enum.
113 When this function returns, intval is -1 if the tag is non numeric or,
114 if the tag is numeric, *intval is the enum case we want to go to (between 1
115 and the original value of *intval, inclusive).
116 When not treating a conditional/enum, intval should be NULL.
118 const char *get_token_value(struct gui_wps *gwps,
119 struct wps_token *token,
120 char *buf, int buf_size,
121 int *intval)
123 if (!gwps)
124 return NULL;
126 struct wps_data *data = gwps->data;
127 struct wps_state *state = gwps->state;
129 if (!data || !state)
130 return NULL;
132 struct mp3entry *id3;
134 if (token->next)
135 id3 = state->nid3;
136 else
137 id3 = state->id3;
139 if (!id3)
140 return NULL;
142 #if CONFIG_RTC
143 struct tm* tm = NULL;
145 /* if the token is an RTC one, update the time
146 and do the necessary checks */
147 if (token->type >= WPS_TOKENS_RTC_BEGIN
148 && token->type <= WPS_TOKENS_RTC_END)
150 tm = get_time();
152 if (!valid_time(tm))
153 return NULL;
155 #endif
157 int limit = 1;
158 if (intval)
160 limit = *intval;
161 *intval = -1;
164 switch (token->type)
166 case WPS_TOKEN_CHARACTER:
167 if (token->value.c == '\n')
168 return NULL;
169 return &(token->value.c);
171 case WPS_TOKEN_STRING:
172 return (char*)token->value.data;
174 case WPS_TOKEN_TRACK_TIME_ELAPSED:
175 format_time(buf, buf_size,
176 id3->elapsed + state->ff_rewind_count);
177 return buf;
179 case WPS_TOKEN_TRACK_TIME_REMAINING:
180 format_time(buf, buf_size,
181 id3->length - id3->elapsed -
182 state->ff_rewind_count);
183 return buf;
185 case WPS_TOKEN_TRACK_LENGTH:
186 format_time(buf, buf_size, id3->length);
187 return buf;
189 case WPS_TOKEN_PLAYLIST_ENTRIES:
190 snprintf(buf, buf_size, "%d", playlist_amount());
191 return buf;
193 case WPS_TOKEN_PLAYLIST_NAME:
194 return playlist_name(NULL, buf, buf_size);
196 case WPS_TOKEN_PLAYLIST_POSITION:
197 snprintf(buf, buf_size, "%d", playlist_get_display_index());
198 return buf;
200 case WPS_TOKEN_PLAYLIST_SHUFFLE:
201 if ( global_settings.playlist_shuffle )
202 return "s";
203 else
204 return NULL;
205 break;
207 case WPS_TOKEN_VOLUME:
208 snprintf(buf, buf_size, "%d", global_settings.volume);
209 if (intval)
211 if (global_settings.volume == sound_min(SOUND_VOLUME))
213 *intval = 1;
215 else if (global_settings.volume == 0)
217 *intval = limit - 1;
219 else if (global_settings.volume > 0)
221 *intval = limit;
223 else
225 *intval = (limit - 3) * (global_settings.volume
226 - sound_min(SOUND_VOLUME) - 1)
227 / (-1 - sound_min(SOUND_VOLUME)) + 2;
230 return buf;
232 case WPS_TOKEN_TRACK_ELAPSED_PERCENT:
233 if (id3->length <= 0)
234 return NULL;
236 if (intval)
238 *intval = limit * (id3->elapsed + state->ff_rewind_count)
239 / id3->length + 1;
241 snprintf(buf, buf_size, "%d",
242 100*(id3->elapsed + state->ff_rewind_count) / id3->length);
243 return buf;
245 case WPS_TOKEN_METADATA_ARTIST:
246 return id3->artist;
248 case WPS_TOKEN_METADATA_COMPOSER:
249 return id3->composer;
251 case WPS_TOKEN_METADATA_ALBUM:
252 return id3->album;
254 case WPS_TOKEN_METADATA_ALBUM_ARTIST:
255 return id3->albumartist;
257 case WPS_TOKEN_METADATA_GROUPING:
258 return id3->grouping;
260 case WPS_TOKEN_METADATA_GENRE:
261 return id3->genre_string;
263 case WPS_TOKEN_METADATA_DISC_NUMBER:
264 if (id3->disc_string)
265 return id3->disc_string;
266 if (id3->discnum) {
267 snprintf(buf, buf_size, "%d", id3->discnum);
268 return buf;
270 return NULL;
272 case WPS_TOKEN_METADATA_TRACK_NUMBER:
273 if (id3->track_string)
274 return id3->track_string;
276 if (id3->tracknum) {
277 snprintf(buf, buf_size, "%d", id3->tracknum);
278 return buf;
280 return NULL;
282 case WPS_TOKEN_METADATA_TRACK_TITLE:
283 return id3->title;
285 case WPS_TOKEN_METADATA_VERSION:
286 switch (id3->id3version)
288 case ID3_VER_1_0:
289 return "1";
291 case ID3_VER_1_1:
292 return "1.1";
294 case ID3_VER_2_2:
295 return "2.2";
297 case ID3_VER_2_3:
298 return "2.3";
300 case ID3_VER_2_4:
301 return "2.4";
303 default:
304 return NULL;
307 case WPS_TOKEN_METADATA_YEAR:
308 if( id3->year_string )
309 return id3->year_string;
311 if (id3->year) {
312 snprintf(buf, buf_size, "%d", id3->year);
313 return buf;
315 return NULL;
317 case WPS_TOKEN_METADATA_COMMENT:
318 return id3->comment;
320 #ifdef HAVE_ALBUMART
321 case WPS_TOKEN_ALBUMART_FOUND:
322 if (data->albumart && audio_current_aa_hid() >= 0) {
323 return "C";
325 return NULL;
327 case WPS_TOKEN_ALBUMART_DISPLAY:
328 if (!data->albumart)
329 return NULL;
330 data->albumart->draw = true;
331 return NULL;
332 #endif
334 case WPS_TOKEN_FILE_BITRATE:
335 if(id3->bitrate)
336 snprintf(buf, buf_size, "%d", id3->bitrate);
337 else
338 return "?";
339 return buf;
341 case WPS_TOKEN_FILE_CODEC:
342 if (intval)
344 if(id3->codectype == AFMT_UNKNOWN)
345 *intval = AFMT_NUM_CODECS;
346 else
347 *intval = id3->codectype;
349 return get_codectype(id3);
351 case WPS_TOKEN_FILE_FREQUENCY:
352 snprintf(buf, buf_size, "%ld", id3->frequency);
353 return buf;
355 case WPS_TOKEN_FILE_FREQUENCY_KHZ:
356 /* ignore remainders < 100, so 22050 Hz becomes just 22k */
357 if ((id3->frequency % 1000) < 100)
358 snprintf(buf, buf_size, "%ld", id3->frequency / 1000);
359 else
360 snprintf(buf, buf_size, "%ld.%d",
361 id3->frequency / 1000,
362 (id3->frequency % 1000) / 100);
363 return buf;
365 case WPS_TOKEN_FILE_NAME:
366 if (get_dir(buf, buf_size, id3->path, 0)) {
367 /* Remove extension */
368 char* sep = strrchr(buf, '.');
369 if (NULL != sep) {
370 *sep = 0;
372 return buf;
374 else {
375 return NULL;
378 case WPS_TOKEN_FILE_NAME_WITH_EXTENSION:
379 return get_dir(buf, buf_size, id3->path, 0);
381 case WPS_TOKEN_FILE_PATH:
382 return id3->path;
384 case WPS_TOKEN_FILE_SIZE:
385 snprintf(buf, buf_size, "%ld", id3->filesize / 1024);
386 return buf;
388 case WPS_TOKEN_FILE_VBR:
389 return id3->vbr ? "(avg)" : NULL;
391 case WPS_TOKEN_FILE_DIRECTORY:
392 return get_dir(buf, buf_size, id3->path, token->value.i);
394 case WPS_TOKEN_BATTERY_PERCENT:
396 int l = battery_level();
398 if (intval)
400 limit = MAX(limit, 2);
401 if (l > -1) {
402 /* First enum is used for "unknown level". */
403 *intval = (limit - 1) * l / 100 + 2;
404 } else {
405 *intval = 1;
409 if (l > -1) {
410 snprintf(buf, buf_size, "%d", l);
411 return buf;
412 } else {
413 return "?";
417 case WPS_TOKEN_BATTERY_VOLTS:
419 unsigned int v = battery_voltage();
420 snprintf(buf, buf_size, "%d.%02d", v / 1000, (v % 1000) / 10);
421 return buf;
424 case WPS_TOKEN_BATTERY_TIME:
426 int t = battery_time();
427 if (t >= 0)
428 snprintf(buf, buf_size, "%dh %dm", t / 60, t % 60);
429 else
430 return "?h ?m";
431 return buf;
434 #if CONFIG_CHARGING
435 case WPS_TOKEN_BATTERY_CHARGER_CONNECTED:
437 if(charger_input_state==CHARGER)
438 return "p";
439 else
440 return NULL;
442 #endif
443 #if CONFIG_CHARGING >= CHARGING_MONITOR
444 case WPS_TOKEN_BATTERY_CHARGING:
446 if (charge_state == CHARGING || charge_state == TOPOFF) {
447 return "c";
448 } else {
449 return NULL;
452 #endif
453 case WPS_TOKEN_BATTERY_SLEEPTIME:
455 if (get_sleep_timer() == 0)
456 return NULL;
457 else
459 format_time(buf, buf_size, get_sleep_timer() * 1000);
460 return buf;
464 case WPS_TOKEN_PLAYBACK_STATUS:
466 int status = audio_status();
467 int mode = 1;
468 if (status == AUDIO_STATUS_PLAY)
469 mode = 2;
470 if (is_wps_fading() ||
471 (status & AUDIO_STATUS_PAUSE && !status_get_ffmode()))
472 mode = 3;
473 if (status_get_ffmode() == STATUS_FASTFORWARD)
474 mode = 4;
475 if (status_get_ffmode() == STATUS_FASTBACKWARD)
476 mode = 5;
478 if (intval) {
479 *intval = mode;
482 snprintf(buf, buf_size, "%d", mode-1);
483 return buf;
486 case WPS_TOKEN_REPEAT_MODE:
487 if (intval)
488 *intval = global_settings.repeat_mode + 1;
489 snprintf(buf, buf_size, "%d", global_settings.repeat_mode);
490 return buf;
492 case WPS_TOKEN_RTC_PRESENT:
493 #if CONFIG_RTC
494 return "c";
495 #else
496 return NULL;
497 #endif
499 #if CONFIG_RTC
500 case WPS_TOKEN_RTC_12HOUR_CFG:
501 if (intval)
502 *intval = global_settings.timeformat + 1;
503 snprintf(buf, buf_size, "%d", global_settings.timeformat);
504 return buf;
506 case WPS_TOKEN_RTC_DAY_OF_MONTH:
507 /* d: day of month (01..31) */
508 snprintf(buf, buf_size, "%02d", tm->tm_mday);
509 return buf;
511 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED:
512 /* e: day of month, blank padded ( 1..31) */
513 snprintf(buf, buf_size, "%2d", tm->tm_mday);
514 return buf;
516 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED:
517 /* H: hour (00..23) */
518 snprintf(buf, buf_size, "%02d", tm->tm_hour);
519 return buf;
521 case WPS_TOKEN_RTC_HOUR_24:
522 /* k: hour ( 0..23) */
523 snprintf(buf, buf_size, "%2d", tm->tm_hour);
524 return buf;
526 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED:
527 /* I: hour (01..12) */
528 snprintf(buf, buf_size, "%02d",
529 (tm->tm_hour % 12 == 0) ? 12 : tm->tm_hour % 12);
530 return buf;
532 case WPS_TOKEN_RTC_HOUR_12:
533 /* l: hour ( 1..12) */
534 snprintf(buf, buf_size, "%2d",
535 (tm->tm_hour % 12 == 0) ? 12 : tm->tm_hour % 12);
536 return buf;
538 case WPS_TOKEN_RTC_MONTH:
539 /* m: month (01..12) */
540 if (intval)
541 *intval = tm->tm_mon + 1;
542 snprintf(buf, buf_size, "%02d", tm->tm_mon + 1);
543 return buf;
545 case WPS_TOKEN_RTC_MINUTE:
546 /* M: minute (00..59) */
547 snprintf(buf, buf_size, "%02d", tm->tm_min);
548 return buf;
550 case WPS_TOKEN_RTC_SECOND:
551 /* S: second (00..59) */
552 snprintf(buf, buf_size, "%02d", tm->tm_sec);
553 return buf;
555 case WPS_TOKEN_RTC_YEAR_2_DIGITS:
556 /* y: last two digits of year (00..99) */
557 snprintf(buf, buf_size, "%02d", tm->tm_year % 100);
558 return buf;
560 case WPS_TOKEN_RTC_YEAR_4_DIGITS:
561 /* Y: year (1970...) */
562 snprintf(buf, buf_size, "%04d", tm->tm_year + 1900);
563 return buf;
565 case WPS_TOKEN_RTC_AM_PM_UPPER:
566 /* p: upper case AM or PM indicator */
567 return tm->tm_hour/12 == 0 ? "AM" : "PM";
569 case WPS_TOKEN_RTC_AM_PM_LOWER:
570 /* P: lower case am or pm indicator */
571 return tm->tm_hour/12 == 0 ? "am" : "pm";
573 case WPS_TOKEN_RTC_WEEKDAY_NAME:
574 /* a: abbreviated weekday name (Sun..Sat) */
575 return str(LANG_WEEKDAY_SUNDAY + tm->tm_wday);
577 case WPS_TOKEN_RTC_MONTH_NAME:
578 /* b: abbreviated month name (Jan..Dec) */
579 return str(LANG_MONTH_JANUARY + tm->tm_mon);
581 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON:
582 /* u: day of week (1..7); 1 is Monday */
583 if (intval)
584 *intval = (tm->tm_wday == 0) ? 7 : tm->tm_wday;
585 snprintf(buf, buf_size, "%1d", tm->tm_wday + 1);
586 return buf;
588 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN:
589 /* w: day of week (0..6); 0 is Sunday */
590 if (intval)
591 *intval = tm->tm_wday + 1;
592 snprintf(buf, buf_size, "%1d", tm->tm_wday);
593 return buf;
594 #else
595 case WPS_TOKEN_RTC_DAY_OF_MONTH:
596 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED:
597 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED:
598 case WPS_TOKEN_RTC_HOUR_24:
599 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED:
600 case WPS_TOKEN_RTC_HOUR_12:
601 case WPS_TOKEN_RTC_MONTH:
602 case WPS_TOKEN_RTC_MINUTE:
603 case WPS_TOKEN_RTC_SECOND:
604 case WPS_TOKEN_RTC_AM_PM_UPPER:
605 case WPS_TOKEN_RTC_AM_PM_LOWER:
606 case WPS_TOKEN_RTC_YEAR_2_DIGITS:
607 return "--";
608 case WPS_TOKEN_RTC_YEAR_4_DIGITS:
609 return "----";
610 case WPS_TOKEN_RTC_WEEKDAY_NAME:
611 case WPS_TOKEN_RTC_MONTH_NAME:
612 return "---";
613 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON:
614 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN:
615 return "-";
616 #endif
618 #ifdef HAVE_LCD_CHARCELLS
619 case WPS_TOKEN_PROGRESSBAR:
621 char *end = utf8encode(data->wps_progress_pat[0], buf);
622 *end = '\0';
623 return buf;
626 case WPS_TOKEN_PLAYER_PROGRESSBAR:
627 if(is_new_player())
629 /* we need 11 characters (full line) for
630 progress-bar */
631 strlcpy(buf, " ", buf_size);
633 else
635 /* Tell the user if we have an OldPlayer */
636 strlcpy(buf, " <Old LCD> ", buf_size);
638 return buf;
639 #endif
641 #ifdef HAVE_TAGCACHE
642 case WPS_TOKEN_DATABASE_PLAYCOUNT:
643 if (intval) {
644 *intval = id3->playcount + 1;
646 snprintf(buf, buf_size, "%ld", id3->playcount);
647 return buf;
649 case WPS_TOKEN_DATABASE_RATING:
650 if (intval) {
651 *intval = id3->rating + 1;
653 snprintf(buf, buf_size, "%d", id3->rating);
654 return buf;
656 case WPS_TOKEN_DATABASE_AUTOSCORE:
657 if (intval)
658 *intval = id3->score + 1;
660 snprintf(buf, buf_size, "%d", id3->score);
661 return buf;
662 #endif
664 #if (CONFIG_CODEC == SWCODEC)
665 case WPS_TOKEN_CROSSFADE:
666 if (intval)
667 *intval = global_settings.crossfade + 1;
668 snprintf(buf, buf_size, "%d", global_settings.crossfade);
669 return buf;
671 case WPS_TOKEN_REPLAYGAIN:
673 int val;
675 if (global_settings.replaygain_type == REPLAYGAIN_OFF)
676 val = 1; /* off */
677 else
679 int type =
680 get_replaygain_mode(id3->track_gain_string != NULL,
681 id3->album_gain_string != NULL);
682 if (type < 0)
683 val = 6; /* no tag */
684 else
685 val = type + 2;
687 if (global_settings.replaygain_type == REPLAYGAIN_SHUFFLE)
688 val += 2;
691 if (intval)
692 *intval = val;
694 switch (val)
696 case 1:
697 case 6:
698 return "+0.00 dB";
699 break;
700 case 2:
701 case 4:
702 strlcpy(buf, id3->track_gain_string, buf_size);
703 break;
704 case 3:
705 case 5:
706 strlcpy(buf, id3->album_gain_string, buf_size);
707 break;
709 return buf;
711 #endif /* (CONFIG_CODEC == SWCODEC) */
713 #if (CONFIG_CODEC != MAS3507D)
714 case WPS_TOKEN_SOUND_PITCH:
716 int val = sound_get_pitch();
717 snprintf(buf, buf_size, "%d.%d",
718 val / 10, val % 10);
719 return buf;
721 #endif
723 case WPS_TOKEN_MAIN_HOLD:
724 #ifdef HAS_BUTTON_HOLD
725 if (button_hold())
726 #else
727 if (is_keys_locked())
728 #endif /*hold switch or softlock*/
729 return "h";
730 else
731 return NULL;
733 #ifdef HAS_REMOTE_BUTTON_HOLD
734 case WPS_TOKEN_REMOTE_HOLD:
735 if (remote_button_hold())
736 return "r";
737 else
738 return NULL;
739 #endif
741 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
742 case WPS_TOKEN_VLED_HDD:
743 if(led_read(HZ/2))
744 return "h";
745 else
746 return NULL;
747 #endif
748 case WPS_TOKEN_BUTTON_VOLUME:
749 if (data->button_time_volume &&
750 TIME_BEFORE(current_tick, data->button_time_volume +
751 token->value.i * TIMEOUT_UNIT))
752 return "v";
753 return NULL;
754 case WPS_TOKEN_LASTTOUCH:
755 #ifdef HAVE_TOUCHSCREEN
756 if (TIME_BEFORE(current_tick, token->value.i * TIMEOUT_UNIT +
757 touchscreen_last_touch()))
758 return "t";
759 #endif
760 return NULL;
762 case WPS_TOKEN_SETTING:
764 if (intval)
766 /* Handle contionals */
767 const struct settings_list *s = settings+token->value.i;
768 switch (s->flags&F_T_MASK)
770 case F_T_INT:
771 case F_T_UINT:
772 if (s->flags&F_RGB)
773 /* %?St|name|<#000000|#000001|...|#FFFFFF> */
774 /* shouldn't overflow since colors are stored
775 * on 16 bits ...
776 * but this is pretty useless anyway */
777 *intval = *(int*)s->setting + 1;
778 else if (s->cfg_vals == NULL)
779 /* %?St|name|<1st choice|2nd choice|...> */
780 *intval = (*(int*)s->setting-s->int_setting->min)
781 /s->int_setting->step + 1;
782 else
783 /* %?St|name|<1st choice|2nd choice|...> */
784 /* Not sure about this one. cfg_name/vals are
785 * indexed from 0 right? */
786 *intval = *(int*)s->setting + 1;
787 break;
788 case F_T_BOOL:
789 /* %?St|name|<if true|if false> */
790 *intval = *(bool*)s->setting?1:2;
791 break;
792 case F_T_CHARPTR:
793 /* %?St|name|<if non empty string|if empty>
794 * The string's emptyness discards the setting's
795 * prefix and suffix */
796 *intval = ((char*)s->setting)[0]?1:2;
797 break;
798 default:
799 /* This shouldn't happen ... but you never know */
800 *intval = -1;
801 break;
804 cfg_to_string(token->value.i,buf,buf_size);
805 return buf;
808 default:
809 return NULL;