rockbox/apps/settings.c
Peter D'Hoye ab90b1d55e Added bad filename protection when user enters an fm radio preset filename. Fixes bugreport 5137.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9795 a1c6a512-1295-4272-9138-f99709370657
2006-04-25 00:14:21 +00:00

1962 lines
64 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2002 by wavey@wavey.org
* RTC config saving code (C) 2002 by hessu@hes.iki.fi
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdio.h>
#include <stddef.h>
#include <limits.h>
#include "inttypes.h"
#include "config.h"
#include "kernel.h"
#include "thread.h"
#include "settings.h"
#include "disk.h"
#include "panic.h"
#include "debug.h"
#include "button.h"
#include "usb.h"
#include "backlight.h"
#include "lcd.h"
#include "audio.h"
#include "mp3_playback.h"
#include "mpeg.h"
#include "talk.h"
#include "string.h"
#include "ata.h"
#include "fat.h"
#include "power.h"
#include "backlight.h"
#include "powermgmt.h"
#include "status.h"
#include "atoi.h"
#include "screens.h"
#include "ctype.h"
#include "file.h"
#include "errno.h"
#include "system.h"
#include "misc.h"
#include "timefuncs.h"
#ifdef HAVE_LCD_BITMAP
#include "icons.h"
#include "font.h"
#include "peakmeter.h"
#include "hwcompat.h"
#endif
#include "lang.h"
#include "language.h"
#include "gwps.h"
#include "powermgmt.h"
#include "bookmark.h"
#include "sprintf.h"
#include "keyboard.h"
#include "version.h"
#include "rtc.h"
#include "sound.h"
#include "rbunicode.h"
#include "dircache.h"
#include "select.h"
#include "statusbar.h"
#include "splash.h"
#include "list.h"
#ifdef HAVE_LCD_COLOR
#include "backdrop.h"
#endif
#ifdef CONFIG_TUNER
#include "radio.h"
#endif
#if CONFIG_CODEC == MAS3507D
void dac_line_in(bool enable);
#endif
struct user_settings global_settings;
#ifdef HAVE_RECORDING
const char rec_base_directory[] = REC_BASE_DIR;
#endif
#if CONFIG_CODEC == SWCODEC
#include "pcmbuf.h"
#include "pcm_playback.h"
#include "dsp.h"
#endif
#define CONFIG_BLOCK_VERSION 40
#define CONFIG_BLOCK_SIZE 512
#define RTC_BLOCK_SIZE 44
#ifdef HAVE_LCD_BITMAP
#define MAX_LINES 10
#else
#define MAX_LINES 2
#endif
#ifdef HAVE_REMOTE_LCD
#include "lcd-remote.h"
#endif
long lasttime = 0;
static long config_sector = 0; /* mark uninitialized */
static unsigned char config_block[CONFIG_BLOCK_SIZE];
/* descriptor for a configuration value */
/* (watch the struct packing and member sizes to keep this small) */
struct bit_entry
{
/* how many bits within the bitfield (1-32), MSB set if value is signed */
unsigned char bit_size; /* min 6+1 bit */
/* how many bytes in the global_settings struct (1,2,4) */
unsigned char byte_size; /* min 3 bits */
/* store position in global_settings struct */
short settings_offset; /* min 9 bit, better 10 */
/* default value */
int default_val; /* min 15 bit */
/* variable name in a .cfg file, NULL if not to be saved */
const char* cfg_name;
/* set of values, "rgb" for a color, or NULL for a numerical value */
const char* cfg_val;
};
/********************************************
Config block as saved on the battery-packed RTC user RAM memory block
of 44 bytes, starting at offset 0x14 of the RTC memory space.
offset abs
0x00 0x14 "Roc" header signature: 0x52 0x6f 0x63
0x03 0x17 <version byte: 0x0>
0x04 0x18 start of bit-table
...
0x28,0x29 unused, not reachable by set_bits() without disturbing the next 2
0x2A,0x2B <checksum 2 bytes: xor of 0x00-0x29>
Config memory is reset to 0xff and initialized with 'factory defaults' if
a valid header & checksum is not found. Config version number is only
increased when information is _relocated_ or space is _reused_ so that old
versions can read and modify configuration changed by new versions.
Memory locations not used by a given version should not be
modified unless the header & checksum test fails.
Rest of config block, only saved to disk:
0x2C start of 2nd bit-table
...
0xA4 (char[20]) FMR Preset file
0xB8 (char[20]) WPS file
0xCC (char[20]) Lang file
0xE0 (char[20]) Font file
... (char[20]) RWPS file (on targets supporting a Remote WPS)
... (char[20]) Main backdrop file (on color LCD targets)
... to 0x200 <unused>
*************************************/
/* The persistence of the global_settings members is now controlled by
the two tables below, rtc_bits and hd_bits.
New values can just be added to the end, it will be backwards
compatible. If you however change order, bitsize, etc. of existing
entries, you need to bump CONFIG_BLOCK_VERSION to break compatibility.
*/
/* convenience macro for both size and offset of global_settings member */
#define S_O(val) sizeof(global_settings.val), offsetof(struct user_settings, val)
#define SIGNED 0x80 /* for bitsize value with signed attribute */
/* some sets of values which are used more than once, to save memory */
static const char off_on[] = "off,on";
static const char off_on_ask[] = "off,on,ask";
static const char off_number_spell_hover[] = "off,number,spell,hover";
#ifdef HAVE_LCD_BITMAP
static const char graphic_numeric[] = "graphic,numeric";
#endif
#ifdef HAVE_RECORDING
/* keep synchronous to trig_durations and
trigger_times in settings_apply_trigger */
static const char trig_durations_conf [] =
"0s,1s,2s,5s,10s,15s,20s,25s,30s,1min,2min,5min,10min";
#endif
#if defined(CONFIG_BACKLIGHT)
static const char backlight_times_conf [] =
"off,on,1,2,3,4,5,6,7,8,9,10,15,20,25,30,45,60,90";
#endif
/* the part of the settings which ends up in the RTC RAM, where available
(those we either need early, save frequently, or without spinup) */
static const struct bit_entry rtc_bits[] =
{
/* placeholder, containing the size information */
{9, 0, 0, 0, NULL, NULL }, /* 9 bit to tell how far this is populated */
/* # of bits, offset+size, default, .cfg name, .cfg values */
/* sound */
#if CONFIG_CODEC == MAS3507D
{8 | SIGNED, S_O(volume), -18, "volume", NULL }, /* -78...+18 */
#else
{8 | SIGNED, S_O(volume), -25, "volume", NULL }, /* -100...+12 / -84...0 */
#endif
{8 | SIGNED, S_O(balance), 0, "balance", NULL }, /* -100...100 */
#if CONFIG_CODEC != SWCODEC /* any MAS */
{5 | SIGNED, S_O(bass), 0, "bass", NULL }, /* -15..+15 / -12..+12 */
{5 | SIGNED, S_O(treble), 0, "treble", NULL }, /* -15..+15 / -12..+12 */
#elif defined HAVE_UDA1380
{5, S_O(bass), 0, "bass", NULL }, /* 0..+24 */
{3, S_O(treble), 0, "treble", NULL }, /* 0..+6 */
#elif defined(HAVE_WM8975) || defined(HAVE_WM8758) \
|| defined(HAVE_WM8731) || defined(HAVE_WM8721)
{4 | SIGNED, S_O(bass), 0, "bass", NULL }, /* -6..+9 */
{4 | SIGNED, S_O(treble), 0, "treble", NULL }, /* -6..+9 */
#endif
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
{5, S_O(loudness), 0, "loudness", NULL }, /* 0...17 */
{3, S_O(avc), 0, "auto volume", "off,20ms,2,4,8" },
{1, S_O(superbass), false, "superbass", off_on },
#endif
{3, S_O(channel_config), 0, "channels",
"stereo,mono,custom,mono left,mono right,karaoke" },
{8, S_O(stereo_width), 100, "stereo width", NULL},
/* playback */
{1, S_O(resume), false, "resume", off_on },
{1, S_O(playlist_shuffle), false, "shuffle", off_on },
{16 | SIGNED, S_O(resume_index), -1, NULL, NULL },
{16 | SIGNED, S_O(resume_first_index), 0, NULL, NULL },
{32 | SIGNED, S_O(resume_offset), -1, NULL, NULL },
{32 | SIGNED, S_O(resume_seed), -1, NULL, NULL },
{3, S_O(repeat_mode), REPEAT_ALL, "repeat", "off,all,one,shuffle,ab" },
/* LCD */
{6, S_O(contrast), 40, "contrast", NULL },
#ifdef CONFIG_BACKLIGHT
{5, S_O(backlight_timeout), 5, "backlight timeout", backlight_times_conf },
#ifdef HAVE_CHARGING
{5, S_O(backlight_timeout_plugged), 11, "backlight timeout plugged",
backlight_times_conf },
#endif
#endif /* CONFIG_BACKLIGHT */
#ifdef HAVE_LCD_BITMAP
{1, S_O(invert), false, "invert", off_on },
{1, S_O(flip_display), false, "flip display", off_on },
/* display */
{1, S_O(invert_cursor), true, "invert cursor", off_on },
{1, S_O(statusbar), true, "statusbar", off_on },
{1, S_O(scrollbar), true, "scrollbar", off_on },
#if CONFIG_KEYPAD == RECORDER_PAD
{1, S_O(buttonbar), true, "buttonbar", off_on },
#endif
{1, S_O(volume_type), 0, "volume display", graphic_numeric },
{1, S_O(battery_display), 0, "battery display", graphic_numeric },
{1, S_O(timeformat), 0, "time format", "24hour,12hour" },
#endif /* HAVE_LCD_BITMAP */
{1, S_O(show_icons), true, "show icons", off_on },
/* system */
{4, S_O(poweroff), 10,
"idle poweroff", "off,1,2,3,4,5,6,7,8,9,10,15,30,45,60" },
{18, S_O(runtime), 0, NULL, NULL },
{18, S_O(topruntime), 0, NULL, NULL },
{15, S_O(max_files_in_playlist), 10000,
"max files in playlist", NULL }, /* 1000...20000 */
{14, S_O(max_files_in_dir), 400,
"max files in dir", NULL }, /* 50...10000 */
/* battery */
{12, S_O(battery_capacity), BATTERY_CAPACITY_DEFAULT, "battery capacity",
NULL }, /* 1500...3200 for NiMH, 2200...3200 for LiIon,
500...1500 for Alkaline */
#ifdef HAVE_CHARGING
{1, S_O(car_adapter_mode), false, "car adapter mode", off_on },
#endif
/* tuner */
#ifdef CONFIG_TUNER
{1, S_O(fm_force_mono), false, "force fm mono", off_on },
{8, S_O(last_frequency), 0, NULL, NULL }, /* Default: MIN_FREQ */
#endif
#if BATTERY_TYPES_COUNT > 1
{1, S_O(battery_type), 0, "battery type", "alkaline,nimh" },
#endif
#ifdef HAVE_REMOTE_LCD
/* remote lcd */
{6, S_O(remote_contrast), 42, "remote contrast", NULL },
{1, S_O(remote_invert), false, "remote invert", off_on },
{1, S_O(remote_flip_display), false, "remote flip display", off_on },
{5, S_O(remote_backlight_timeout), 5, "remote backlight timeout",
backlight_times_conf },
#ifdef HAVE_CHARGING
{5, S_O(remote_backlight_timeout_plugged), 11,
"remote backlight timeout plugged", backlight_times_conf },
#endif
#ifdef HAVE_REMOTE_LCD_TICKING
{1, S_O(remote_reduce_ticking), false, "remote reduce ticking", off_on },
#endif
#endif
#ifdef CONFIG_BACKLIGHT
{1, S_O(bl_filter_first_keypress),
#ifdef HAVE_LCD_COLOR
true,
#else
false,
#endif
"backlight filters first keypress", off_on },
#ifdef HAVE_REMOTE_LCD
{1, S_O(remote_bl_filter_first_keypress), false,
"backlight filters first remote keypress", off_on },
#endif
#endif
/* new stuff to be added here */
/* If values are just added to the end, no need to bump the version. */
/* Current sum of bits: 277 (worst case, but w/o remote lcd) */
/* Sum of all bit sizes must not grow beyond 288! */
};
/* the part of the settings which ends up in HD sector only */
static const struct bit_entry hd_bits[] =
{
/* This table starts after the 44 RTC bytes = 352 bits. */
/* Here we need 11 bits to tell how far this is populated. */
/* placeholder, containing the size information */
{11, 0, 0, 0, NULL, NULL }, /* 11 bit to tell how far this is populated */
/* # of bits, offset+size, default, .cfg name, .cfg values */
/* more display */
#ifdef CONFIG_BACKLIGHT
{1, S_O(caption_backlight), false, "caption backlight", off_on },
#endif
{4, S_O(scroll_speed), 9, "scroll speed", NULL }, /* 0...15 */
#ifdef HAVE_LCD_BITMAP
#if LCD_WIDTH > 127
{8, S_O(scroll_step), 6, "scroll step", NULL }, /* 1...160 */
#else
{7, S_O(scroll_step), 6, "scroll step", NULL }, /* 1...112 */
#endif
#endif /* HAVE_LCD_BITMAP */
{8, S_O(scroll_delay), 100, "scroll delay", NULL }, /* 0...250 */
{8, S_O(bidir_limit), 50, "bidir limit", NULL }, /* 0...200 */
#ifdef HAVE_LCD_CHARCELLS
{3, S_O(jump_scroll), 0, "jump scroll", NULL }, /* 0...5 */
{8, S_O(jump_scroll_delay), 50, "jump scroll delay", NULL }, /* 0...250 */
#endif
/* more playback */
{1, S_O(play_selected), true, "play selected", off_on },
{1, S_O(fade_on_stop), true, "volume fade", off_on },
{4, S_O(ff_rewind_min_step), FF_REWIND_1000,
"scan min step", "1,2,3,4,5,6,8,10,15,20,25,30,45,60" },
{4, S_O(ff_rewind_accel), 3, "scan accel", NULL },
#if CONFIG_CODEC == SWCODEC
{3, S_O(buffer_margin), 0, "antiskip",
"5s,15s,30s,1min,2min,3min,5min,10min" },
#else
{3, S_O(buffer_margin), 0, "antiskip", NULL },
#endif
/* disk */
#ifndef HAVE_MMC
#ifdef HAVE_ATA_POWER_OFF
{1, S_O(disk_poweroff), false, "disk poweroff", off_on },
#endif
{8, S_O(disk_spindown), 5, "disk spindown", NULL },
#endif /* HAVE_MMC */
/* browser */
{3, S_O(dirfilter), SHOW_SUPPORTED,
"show files", "all,supported,music,playlists,id3 database" },
{1, S_O(sort_case), false, "sort case", off_on },
{1, S_O(browse_current), false, "follow playlist", off_on },
/* playlist */
{1, S_O(playlist_viewer_icons), true, "playlist viewer icons", off_on },
{1, S_O(playlist_viewer_indices), true,
"playlist viewer indices", off_on },
{1, S_O(playlist_viewer_track_display), 0,
"playlist viewer track display", "track name,full path" },
{2, S_O(recursive_dir_insert), RECURSE_OFF,
"recursive directory insert", off_on_ask },
/* bookmarks */
{3, S_O(autocreatebookmark), BOOKMARK_NO, "autocreate bookmarks",
"off,on,ask,recent only - on,recent only - ask" },
{2, S_O(autoloadbookmark), BOOKMARK_NO,
"autoload bookmarks", off_on_ask },
{2, S_O(usemrb), BOOKMARK_NO,
"use most-recent-bookmarks", "off,on,unique only" },
#ifdef HAVE_LCD_BITMAP
/* peak meter */
{5, S_O(peak_meter_clip_hold), 16, "peak meter clip hold", /* 0...25 */
"on,1,2,3,4,5,6,7,8,9,10,15,20,25,30,45,60,90,2min,3min,5min,10min,20min,45min,90min" },
{5, S_O(peak_meter_hold), 3, "peak meter hold",
"off,200ms,300ms,500ms,1,2,3,4,5,6,7,8,9,10,15,20,30,1min" },
{7, S_O(peak_meter_release), 8, "peak meter release", NULL }, /* 0...126 */
{1, S_O(peak_meter_dbfs), true, "peak meter dbfs", off_on },
{7, S_O(peak_meter_min), 60, "peak meter min", NULL }, /* 0...100 */
{7, S_O(peak_meter_max), 0, "peak meter max", NULL }, /* 0...100 */
#endif
#if CONFIG_CODEC == MAS3587F
/* recording */
{1, S_O(rec_editable), false, "editable recordings", off_on },
{4, S_O(rec_timesplit), 0, "rec timesplit", /* 0...15 */
"off,00:05,00:10,00:15,00:30,01:00,01:14,01:20,02:00,04:00,06:00,08:00,10:00,12:00,18:00,24:00" },
{1, S_O(rec_channels), 0, "rec channels", "stereo,mono" },
{4, S_O(rec_mic_gain), 8, "rec mic gain", NULL },
{3, S_O(rec_quality), 5, "rec quality", NULL },
#ifdef HAVE_SPDIF_IN
{2, S_O(rec_source), 0 /* 0=mic */, "rec source", "mic,line,spdif" },
#else
{1, S_O(rec_source), 0 /* 0=mic */, "rec source", "mic,line" },
#endif
{3, S_O(rec_frequency), 0, /* 0=44.1kHz */
"rec frequency", "44,48,32,22,24,16" },
{4, S_O(rec_left_gain), 2, /* 0dB */
"rec left gain", NULL }, /* 0...15 */
{4, S_O(rec_right_gain), 2, /* 0dB */
"rec right gain", NULL }, /* 0...15 */
{5, S_O(rec_prerecord_time), 0, "prerecording time", NULL }, /* 0...30 */
{1, S_O(rec_directory), 0, /* rec_base_directory */
"rec directory", REC_BASE_DIR ",current" },
#endif
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
{7, S_O(mdb_strength), 0, "mdb strength", NULL},
{7, S_O(mdb_harmonics), 0, "mdb harmonics", NULL},
{9, S_O(mdb_center), 0, "mdb center", NULL},
{9, S_O(mdb_shape), 0, "mdb shape", NULL},
{1, S_O(mdb_enable), 0, "mdb enable", off_on},
#endif
#if CONFIG_CODEC == MAS3507D
{1, S_O(line_in), false, "line in", off_on },
#endif
/* voice */
{2, S_O(talk_dir), 0, "talk dir", off_number_spell_hover },
{2, S_O(talk_file), 0, "talk file", off_number_spell_hover },
{1, S_O(talk_menu), true, "talk menu", off_on },
{2, S_O(sort_file), 0, "sort files", "alpha,oldest,newest,type" },
{2, S_O(sort_dir), 0, "sort dirs", "alpha,oldest,newest" },
{1, S_O(id3_v1_first), 0, "id3 tag priority", "v2-v1,v1-v2"},
#ifdef HAVE_RECORDING
{1, S_O(rec_startup), false, "rec screen on startup", off_on },
/* values for the trigger */
{8 | SIGNED, S_O(rec_start_thres), -35, "trigger start threshold", NULL},
{8 | SIGNED, S_O(rec_stop_thres), -45, "trigger stop threshold", NULL},
{4, S_O(rec_start_duration), 0, "trigger start duration", trig_durations_conf},
{4, S_O(rec_stop_postrec), 2, "trigger stop postrec", trig_durations_conf},
{4, S_O(rec_stop_gap), 1, "trigger min gap", trig_durations_conf},
{4, S_O(rec_trigger_mode ), 0, "trigger mode", "off,once,repeat"},
#endif
#ifdef HAVE_BACKLIGHT_PWM_FADING
/* backlight fading */
{2, S_O(backlight_fade_in), 1, "backlight fade in", "off,500ms,1s,2s"},
{3, S_O(backlight_fade_out), 3, "backlight fade out",
"off,500ms,1s,2s,3s,4s,5s,10s"},
#endif
#ifdef HAVE_SPDIF_POWER
{1, S_O(spdif_enable), false, "spdif enable", off_on},
#endif
{1, S_O(next_folder), false, "folder navigation", off_on },
{1, S_O(runtimedb), false, "gather runtime data", off_on },
#if CONFIG_CODEC == SWCODEC
{1, S_O(replaygain), false, "replaygain", off_on },
{2, S_O(replaygain_type), REPLAYGAIN_ALBUM, "replaygain type",
"track,album,track shuffle" },
{1, S_O(replaygain_noclip), false, "replaygain noclip", off_on },
{8 | SIGNED, S_O(replaygain_preamp), 0, "replaygain preamp", NULL },
{2, S_O(beep), 0, "beep", "off,weak,moderate,strong" },
{2, S_O(crossfade), 0, "crossfade", "off,shuffle,track skip,always"},
{3, S_O(crossfade_fade_in_delay), 0, "crossfade fade in delay", NULL},
{3, S_O(crossfade_fade_out_delay), 0, "crossfade fade out delay", NULL},
{4, S_O(crossfade_fade_in_duration), 0, "crossfade fade in duration", NULL},
{4, S_O(crossfade_fade_out_duration), 0, "crossfade fade out duration", NULL},
{1, S_O(crossfade_fade_out_mixmode), 0, "crossfade fade out mode", "crossfade,mix"},
{1, S_O(crossfeed), false, "crossfeed", off_on },
{6, S_O(crossfeed_direct_gain), 15, "crossfeed direct gain", NULL },
{7, S_O(crossfeed_cross_gain), 60, "crossfeed cross gain", NULL },
{8, S_O(crossfeed_hf_attenuation), 160, "crossfeed hf attenuation", NULL },
{11, S_O(crossfeed_hf_cutoff), 700, "crossfeed hf cutoff", NULL },
#endif
#ifdef HAVE_DIRCACHE
{1, S_O(dircache), false, "dircache", off_on },
{22, S_O(dircache_size), 0, NULL, NULL },
#endif
#if defined(HAVE_UDA1380)
/* recording settings for iriver */
{4, S_O(rec_timesplit), 0, "rec timesplit", /* 0...15 */
"off,00:05,00:10,00:15,00:30,01:00,01:14,01:20,02:00,04:00,06:00,08:00,10:00,12:00,18:00,24:00" },
{1, S_O(rec_channels), 0, "rec channels", "stereo,mono" },
{4, S_O(rec_mic_gain), 4, "rec mic gain", NULL },
{1, S_O(rec_source), 0 /* 0=mic */, "rec source", "mic,line" },
{3, S_O(rec_frequency), 0, /* 0=44.1kHz */
"rec frequency", "44,48,32,22,24,16" },
{4, S_O(rec_left_gain), 2, /* 0dB */
"rec left gain", NULL }, /* 0...15 */
{4, S_O(rec_right_gain), 2, /* 0dB */
"rec right gain", NULL }, /* 0...15 */
{5, S_O(rec_prerecord_time), 0, "prerecording time", NULL }, /* 0...30 */
{1, S_O(rec_directory), 0, /* rec_base_directory */
"rec directory", REC_BASE_DIR ",current" },
{8|SIGNED, S_O(rec_linein_decimator_left_gain), 0, /* 0dB */
"line in decimator left gain", NULL }, /* -128...48 */
{8|SIGNED, S_O(rec_linein_decimator_right_gain), 0, /* 0dB */
"line in decimator right gain", NULL }, /* -128...48 */
#endif
#ifdef HAVE_REMOTE_LCD
{1, S_O(remote_caption_backlight), false,
"remote caption backlight", off_on },
#endif
{4, S_O(default_codepage), 0, "default codepage", "iso8859-1,iso8859-7,iso8859-8,cp1251,iso8859-11,cp1256,iso8859-9,iso8859-2,sjis,gb2312,ksx1001,big5,utf-8,cp1256" },
#ifdef HAVE_BACKLIGHT_BRIGHTNESS
{4, S_O(brightness), 9, "brightness", NULL },
#endif
#ifdef HAVE_LCD_BITMAP
{1, S_O(offset_out_of_view), false, "Screen Scrolls Out Of View", off_on },
#if LCD_WIDTH > 127
{8, S_O(screen_scroll_step), 16, "screen scroll step", NULL }, /* 1...160 */
#else
{7, S_O(screen_scroll_step), 16, "screen scroll step", NULL }, /* 1...112 */
#endif
#endif /* HAVE_LCD_BITMAP */
{1, S_O(warnon_erase_dynplaylist), false,
"warn when erasing dynamic playlist", off_on },
#if CONFIG_CODEC == SWCODEC
{1, S_O(eq_enabled), false, "eq enabled", off_on },
{8, S_O(eq_precut), 0, "eq precut", NULL },
/* 0..32768 Hz */
{15, S_O(eq_band0_cutoff), 60, "eq band 0 cutoff", NULL },
{15, S_O(eq_band1_cutoff), 200, "eq band 1 cutoff", NULL },
{15, S_O(eq_band2_cutoff), 800, "eq band 2 cutoff", NULL },
{15, S_O(eq_band3_cutoff), 4000, "eq band 3 cutoff", NULL },
{15, S_O(eq_band4_cutoff), 12000, "eq band 4 cutoff", NULL },
/* 0..64 (or 0.0 to 6.4) */
{6, S_O(eq_band0_q), 7, "eq band 0 q", NULL },
{6, S_O(eq_band1_q), 10, "eq band 1 q", NULL },
{6, S_O(eq_band2_q), 10, "eq band 2 q", NULL },
{6, S_O(eq_band3_q), 10, "eq band 3 q", NULL },
{6, S_O(eq_band4_q), 7, "eq band 4 q", NULL },
/* -240..240 (or -24db to +24db) */
{9|SIGNED, S_O(eq_band0_gain), 0, "eq band 0 gain", NULL },
{9|SIGNED, S_O(eq_band1_gain), 0, "eq band 1 gain", NULL },
{9|SIGNED, S_O(eq_band2_gain), 0, "eq band 2 gain", NULL },
{9|SIGNED, S_O(eq_band3_gain), 0, "eq band 3 gain", NULL },
{9|SIGNED, S_O(eq_band4_gain), 0, "eq band 4 gain", NULL },
#endif
#if defined(HAVE_UDA1380) /* PLEASE merge this with the other UDA1380 define
when bumping the settings version number PLEASE */
{8|SIGNED, S_O(rec_mic_decimator_left_gain), 0, /* 0dB */
"mic decimator left gain", NULL }, /* -128...48 */
{8|SIGNED, S_O(rec_mic_decimator_right_gain), 0, /* 0dB */
"mic decimator right gain", NULL }, /* -128...48 */
#endif
{1, S_O(scroll_paginated), false, "scroll paginated", off_on },
#ifdef HAVE_RECORDING
#ifdef CONFIG_BACKLIGHT
{2, S_O(cliplight), 0, "cliplight", "off,main,both,remote" },
#endif /* CONFIG_BACKLIGHT */
#endif /*HAVE_RECORDING*/
#ifdef HAVE_LCD_COLOR
{LCD_DEPTH,S_O(fg_color),LCD_DEFAULT_FG,"foreground color","rgb"},
{LCD_DEPTH,S_O(bg_color),LCD_DEFAULT_BG,"background color","rgb"},
#endif
#ifdef HAVE_DIRCACHE
{1, S_O(tagcache_ram), 0, "tagcache_ram", off_on },
#endif
/* If values are just added to the end, no need to bump the version. */
/* new stuff to be added at the end */
/* Sum of all bit sizes must not grow beyond 0xB8*8 = 1472 */
};
/* helper function to extract n (<=32) bits from an arbitrary position
* counting from LSB to MSB */
static uint32_t get_bits(
const uint32_t *p, /* the start of the bitfield array */
unsigned int from, /* bit no. to start reading from */
unsigned int size) /* how many bits to read */
{
unsigned int long_index = from / 32;
unsigned int bit_index = from % 32;
uint32_t result;
result = p[long_index] >> bit_index;
if (bit_index + size > 32) /* crossing longword boundary */
result |= p[long_index+1] << (32 - bit_index);
result &= 0xFFFFFFFF >> (32 - size);
return result;
}
/* helper function to set n (<=32) bits to an arbitrary position,
* counting from LSB to MSB */
static void set_bits(
uint32_t *p, /* the start of the bitfield array */
unsigned int from, /* bit no. to start writing into */
unsigned int size, /* how many bits to change */
uint32_t value) /* content (LSBs will be taken) */
{
unsigned int long_index = from / 32;
unsigned int bit_index = from % 32;
uint32_t mask;
mask = 0xFFFFFFFF >> (32 - size);
value &= mask;
mask <<= bit_index;
if (bit_index + size > 32)
p[long_index+1] =
(p[long_index+1] & (0xFFFFFFFF << (bit_index + size - 32)))
| (value >> (32 - bit_index));
p[long_index] = (p[long_index] & ~mask) | (value << bit_index);
}
#ifdef HAVE_LCD_COLOR
/*
* Helper function to convert a string of 6 hex digits to a native colour
*/
#define hex2dec(c) (((c) >= '0' && ((c) <= '9')) ? (toupper(c)) - '0' : \
(toupper(c)) - 'A' + 10)
int hex_to_rgb(const char* hex)
{ int ok = 1;
int i;
int red, green, blue;
if (strlen(hex) == 6) {
for (i=0; i < 6; i++ ) {
if (!isxdigit(hex[i])) {
ok=0;
break;
}
}
if (ok) {
red = (hex2dec(hex[0]) << 4) | hex2dec(hex[1]);
green = (hex2dec(hex[2]) << 4) | hex2dec(hex[3]);
blue = (hex2dec(hex[4]) << 4) | hex2dec(hex[5]);
return LCD_RGBPACK(red,green,blue);
}
}
return 0;
}
#endif
/*
* Calculates the checksum for the config block and returns it
*/
static unsigned short calculate_config_checksum(const unsigned char* buf)
{
unsigned int i;
unsigned char cksum[2];
cksum[0] = cksum[1] = 0;
for (i=0; i < RTC_BLOCK_SIZE - 2; i+=2 ) {
cksum[0] ^= buf[i];
cksum[1] ^= buf[i+1];
}
return (cksum[0] << 8) | cksum[1];
}
/*
* initialize the config block buffer
*/
static void init_config_buffer( void )
{
DEBUGF( "init_config_buffer()\n" );
/* reset to 0 - all unused */
memset(config_block, 0, CONFIG_BLOCK_SIZE);
/* insert header */
config_block[0] = 'R';
config_block[1] = 'o';
config_block[2] = 'c';
config_block[3] = CONFIG_BLOCK_VERSION;
}
/*
* save the config block buffer to disk or RTC RAM
*/
static int save_config_buffer( void )
{
unsigned short chksum;
#ifdef HAVE_RTC_RAM
unsigned int i;
#endif
/* update the checksum in the end of the block before saving */
chksum = calculate_config_checksum(config_block);
config_block[ RTC_BLOCK_SIZE - 2 ] = chksum >> 8;
config_block[ RTC_BLOCK_SIZE - 1 ] = chksum & 0xff;
#ifdef HAVE_RTC_RAM
/* FIXME: okay, it _would_ be cleaner and faster to implement rtc_write so
that it would write a number of bytes at a time since the RTC chip
supports that, but this will have to do for now 8-) */
for (i=0; i < RTC_BLOCK_SIZE; i++ ) {
int r = rtc_write(0x14+i, config_block[i]);
if (r) {
DEBUGF( "save_config_buffer: rtc_write failed at addr 0x%02x: %d\n",
14+i, r );
return r;
}
}
#endif
if (config_sector != 0)
ata_delayed_write( config_sector, config_block);
else
return -1;
return 0;
}
/*
* load the config block buffer from disk or RTC RAM
*/
static int load_config_buffer(int which)
{
unsigned short chksum;
bool correct = false;
DEBUGF( "load_config_buffer()\n" );
if (which & SETTINGS_HD)
{
if (config_sector != 0) {
ata_read_sectors(IF_MV2(0,) config_sector, 1, config_block);
/* calculate the checksum, check it and the header */
chksum = calculate_config_checksum(config_block);
if (config_block[0] == 'R' &&
config_block[1] == 'o' &&
config_block[2] == 'c' &&
config_block[3] == CONFIG_BLOCK_VERSION &&
(chksum >> 8) == config_block[RTC_BLOCK_SIZE - 2] &&
(chksum & 0xff) == config_block[RTC_BLOCK_SIZE - 1])
{
DEBUGF( "load_config_buffer: header & checksum test ok\n" );
correct = true;
}
}
}
#ifdef HAVE_RTC_RAM
if(!correct)
{
/* If the disk sector was incorrect, reinit the buffer */
memset(config_block, 0, CONFIG_BLOCK_SIZE);
}
if (which & SETTINGS_RTC)
{
unsigned int i;
unsigned char rtc_block[RTC_BLOCK_SIZE];
/* read rtc block */
for (i=0; i < RTC_BLOCK_SIZE; i++ )
rtc_block[i] = rtc_read(0x14+i);
chksum = calculate_config_checksum(rtc_block);
/* if rtc block is ok, use that */
if (rtc_block[0] == 'R' &&
rtc_block[1] == 'o' &&
rtc_block[2] == 'c' &&
rtc_block[3] == CONFIG_BLOCK_VERSION &&
(chksum >> 8) == rtc_block[RTC_BLOCK_SIZE - 2] &&
(chksum & 0xff) == rtc_block[RTC_BLOCK_SIZE - 1])
{
memcpy(config_block, rtc_block, RTC_BLOCK_SIZE);
correct = true;
}
}
#endif
if ( !correct ) {
/* if checksum is not valid, clear the config buffer */
DEBUGF( "load_config_buffer: header & checksum test failed\n" );
init_config_buffer();
return -1;
}
return 0;
}
/* helper to save content of global_settings into a bitfield,
as described per table */
static void save_bit_table(const struct bit_entry* p_table, int count, int bitstart)
{
uint32_t *p_bitfield = (uint32_t *)config_block; /* 32 bit addr. */
uint32_t value; /* 32 bit content */
int i;
const struct bit_entry* p_run = p_table; /* start after the size info */
int curr_bit = bitstart + p_table->bit_size;
count--; /* first is excluded from loop */
for (i=0; i<count; i++)
{
p_run++;
/* could do a memcpy, but that would be endian-dependent */
switch(p_run->byte_size)
{
case 1:
value = ((uint8_t *)&global_settings)[p_run->settings_offset];
break;
case 2:
value = ((uint16_t *)&global_settings)[p_run->settings_offset/2];
break;
case 4:
value = ((uint32_t *)&global_settings)[p_run->settings_offset/4];
break;
default:
DEBUGF( "save_bit_table: illegal size!\n" );
continue;
}
set_bits(p_bitfield, curr_bit, p_run->bit_size & 0x3F, value);
curr_bit += p_run->bit_size & 0x3F;
}
set_bits(p_bitfield, bitstart, p_table->bit_size, /* write size */
curr_bit); /* = position after last element */
}
/*
* figure out the config sector from the partition table and the
* mounted file system
*/
void settings_calc_config_sector(void)
{
#ifdef SIMULATOR
config_sector = 61;
#else
int i;
long partition_start;
long sector = 0;
if (fat_startsector(IF_MV(0)) != 0) /* There is a partition table */
{
sector = 61;
for (i = 0; i < 4; i++)
{
partition_start = disk_partinfo(i)->start;
if (partition_start != 0 && (partition_start - 2) < sector)
sector = partition_start - 2;
}
if (sector < 0)
sector = 0;
}
config_sector = sector;
#endif
}
/*
* persist all runtime user settings to disk or RTC RAM
*/
int settings_save( void )
{
int i;
{
int elapsed_secs;
elapsed_secs = (current_tick - lasttime) / HZ;
global_settings.runtime += elapsed_secs;
lasttime += (elapsed_secs * HZ);
if ( global_settings.runtime > global_settings.topruntime )
global_settings.topruntime = global_settings.runtime;
}
/* serialize scalar values into RTC and HD sector, specified via table */
save_bit_table(rtc_bits, sizeof(rtc_bits)/sizeof(rtc_bits[0]), 4*8);
save_bit_table(hd_bits, sizeof(hd_bits)/sizeof(hd_bits[0]), RTC_BLOCK_SIZE*8);
i = 0xb8;
strncpy((char *)&config_block[i], (char *)global_settings.wps_file,
MAX_FILENAME);
i+= MAX_FILENAME;
strncpy((char *)&config_block[i], (char *)global_settings.lang_file,
MAX_FILENAME);
i+= MAX_FILENAME;
strncpy((char *)&config_block[i], (char *)global_settings.font_file,
MAX_FILENAME);
i+= MAX_FILENAME;
#ifdef HAVE_REMOTE_LCD
strncpy((char *)&config_block[i], (char *)global_settings.rwps_file,
MAX_FILENAME);
i+= MAX_FILENAME;
#endif
#ifdef CONFIG_TUNER
strncpy((char *)&config_block[i], (char *)global_settings.fmr_file,
MAX_FILENAME);
i+= MAX_FILENAME;
#endif
#ifdef HAVE_LCD_COLOR
strncpy((char *)&config_block[i], (char *)global_settings.backdrop_file,
MAX_FILENAME);
i+= MAX_FILENAME;
#endif
#ifdef HAVE_LCD_BITMAP
strncpy((char *)&config_block[i], (char *)global_settings.kbd_file,
MAX_FILENAME);
i+= MAX_FILENAME;
#endif
if(save_config_buffer())
{
lcd_clear_display();
#ifdef HAVE_REMOTE_LCD
lcd_remote_clear_display();
#endif
#ifdef HAVE_LCD_CHARCELLS
lcd_puts(0, 0, str(LANG_SETTINGS_SAVE_PLAYER));
lcd_puts(0, 1, str(LANG_SETTINGS_BATTERY_PLAYER));
#else
lcd_puts(4, 2, str(LANG_SETTINGS_SAVE_RECORDER));
lcd_puts(2, 4, str(LANG_SETTINGS_BATTERY_RECORDER));
lcd_update();
#ifdef HAVE_REMOTE_LCD
lcd_remote_puts(4, 2, str(LANG_SETTINGS_SAVE_RECORDER));
lcd_remote_puts(2, 4, str(LANG_SETTINGS_BATTERY_RECORDER));
lcd_remote_update();
#endif
#endif
sleep(HZ*2);
return -1;
}
return 0;
}
#ifdef HAVE_LCD_BITMAP
/**
* Applies the range infos stored in global_settings to
* the peak meter.
*/
void settings_apply_pm_range(void)
{
int pm_min, pm_max;
/* depending on the scale mode (dBfs or percent) the values
of global_settings.peak_meter_dbfs have different meanings */
if (global_settings.peak_meter_dbfs)
{
/* convert to dBfs * 100 */
pm_min = -(((int)global_settings.peak_meter_min) * 100);
pm_max = -(((int)global_settings.peak_meter_max) * 100);
}
else
{
/* percent is stored directly -> no conversion */
pm_min = global_settings.peak_meter_min;
pm_max = global_settings.peak_meter_max;
}
/* apply the range */
peak_meter_init_range(global_settings.peak_meter_dbfs, pm_min, pm_max);
}
#endif /* HAVE_LCD_BITMAP */
void sound_settings_apply(void)
{
sound_set(SOUND_BASS, global_settings.bass);
sound_set(SOUND_TREBLE, global_settings.treble);
sound_set(SOUND_BALANCE, global_settings.balance);
sound_set(SOUND_VOLUME, global_settings.volume);
#if CONFIG_CODEC == SWCODEC
channels_set(global_settings.channel_config);
stereo_width_set(global_settings.stereo_width);
#else
sound_set(SOUND_CHANNELS, global_settings.channel_config);
sound_set(SOUND_STEREO_WIDTH, global_settings.stereo_width);
#endif
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
sound_set(SOUND_LOUDNESS, global_settings.loudness);
sound_set(SOUND_AVC, global_settings.avc);
sound_set(SOUND_MDB_STRENGTH, global_settings.mdb_strength);
sound_set(SOUND_MDB_HARMONICS, global_settings.mdb_harmonics);
sound_set(SOUND_MDB_CENTER, global_settings.mdb_center);
sound_set(SOUND_MDB_SHAPE, global_settings.mdb_shape);
sound_set(SOUND_MDB_ENABLE, global_settings.mdb_enable);
sound_set(SOUND_SUPERBASS, global_settings.superbass);
#endif
}
void settings_apply(void)
{
char buf[64];
#if CONFIG_CODEC == SWCODEC
int i;
#endif
sound_settings_apply();
audio_set_buffer_margin(global_settings.buffer_margin);
lcd_set_contrast(global_settings.contrast);
lcd_scroll_speed(global_settings.scroll_speed);
#ifdef HAVE_REMOTE_LCD
lcd_remote_set_contrast(global_settings.remote_contrast);
lcd_remote_set_invert_display(global_settings.remote_invert);
lcd_remote_set_flip(global_settings.remote_flip_display);
lcd_remote_scroll_speed(global_settings.scroll_speed);
lcd_remote_scroll_step(global_settings.scroll_step);
lcd_remote_scroll_delay(global_settings.scroll_delay * (HZ/10));
#ifdef HAVE_REMOTE_LCD_TICKING
lcd_remote_emireduce(global_settings.remote_reduce_ticking);
#endif
remote_backlight_set_timeout(global_settings.remote_backlight_timeout);
#ifdef HAVE_CHARGING
remote_backlight_set_timeout_plugged(global_settings.remote_backlight_timeout_plugged);
#endif
#endif
#ifdef CONFIG_BACKLIGHT
backlight_set_timeout(global_settings.backlight_timeout);
#ifdef HAVE_CHARGING
backlight_set_timeout_plugged(global_settings.backlight_timeout_plugged);
#endif
#if defined(HAVE_BACKLIGHT_PWM_FADING) && !defined(SIMULATOR)
backlight_set_fade_in(global_settings.backlight_fade_in);
backlight_set_fade_out(global_settings.backlight_fade_out);
#endif
#endif
#ifdef HAVE_BACKLIGHT_BRIGHTNESS
backlight_set_brightness(global_settings.brightness);
#endif
ata_spindown(global_settings.disk_spindown);
#if (CONFIG_CODEC == MAS3507D) && !defined(SIMULATOR)
dac_line_in(global_settings.line_in);
#endif
mpeg_id3_options(global_settings.id3_v1_first);
#ifdef HAVE_ATA_POWER_OFF
ata_poweroff(global_settings.disk_poweroff);
#endif
set_poweroff_timeout(global_settings.poweroff);
set_battery_capacity(global_settings.battery_capacity);
#if BATTERY_TYPES_COUNT > 1
set_battery_type(global_settings.battery_type);
#endif
#ifdef HAVE_LCD_BITMAP
lcd_set_invert_display(global_settings.invert);
lcd_set_flip(global_settings.flip_display);
button_set_flip(global_settings.flip_display);
lcd_update(); /* refresh after flipping the screen */
settings_apply_pm_range();
peak_meter_init_times(
global_settings.peak_meter_release, global_settings.peak_meter_hold,
global_settings.peak_meter_clip_hold);
#endif
if ( global_settings.wps_file[0] &&
global_settings.wps_file[0] != 0xff ) {
snprintf(buf, sizeof buf, WPS_DIR "/%s.wps",
global_settings.wps_file);
wps_data_load(gui_wps[0].data, buf, true);
}
else
wps_data_init(gui_wps[0].data);
#ifdef HAVE_LCD_COLOR
if ( global_settings.backdrop_file[0] &&
global_settings.backdrop_file[0] != 0xff ) {
snprintf(buf, sizeof buf, BACKDROP_DIR "/%s.bmp",
global_settings.backdrop_file);
load_main_backdrop(buf);
} else {
lcd_set_backdrop(NULL);
}
screens[SCREEN_MAIN].set_foreground(global_settings.fg_color);
screens[SCREEN_MAIN].set_background(global_settings.bg_color);
#endif
#if defined(HAVE_REMOTE_LCD) && (NB_SCREENS > 1)
if ( global_settings.rwps_file[0] &&
global_settings.rwps_file[0] != 0xff ) {
snprintf(buf, sizeof buf, WPS_DIR "/%s.rwps",
global_settings.rwps_file);
wps_data_load(gui_wps[1].data, buf, true);
}
else
wps_data_init(gui_wps[1].data);
#endif
#ifdef HAVE_LCD_BITMAP
if ( global_settings.font_file[0] &&
global_settings.font_file[0] != 0xff ) {
snprintf(buf, sizeof buf, ROCKBOX_DIR FONT_DIR "/%s.fnt",
global_settings.font_file);
font_load(buf);
}
else
font_reset();
if ( global_settings.kbd_file[0] &&
global_settings.kbd_file[0] != 0xff ) {
snprintf(buf, sizeof buf, ROCKBOX_DIR "/%s.kbd",
global_settings.kbd_file);
load_kbd(buf);
}
else
load_kbd(NULL);
lcd_scroll_step(global_settings.scroll_step);
gui_list_screen_scroll_step(global_settings.screen_scroll_step);
gui_list_screen_scroll_out_of_view(global_settings.offset_out_of_view);
#else
lcd_jump_scroll(global_settings.jump_scroll);
lcd_jump_scroll_delay(global_settings.jump_scroll_delay * (HZ/10));
#endif
lcd_bidir_scroll(global_settings.bidir_limit);
lcd_scroll_delay(global_settings.scroll_delay * (HZ/10));
if ( global_settings.lang_file[0] &&
global_settings.lang_file[0] != 0xff ) {
snprintf(buf, sizeof buf, ROCKBOX_DIR LANG_DIR "/%s.lng",
global_settings.lang_file);
lang_load(buf);
talk_init(); /* use voice of same language */
}
set_codepage(global_settings.default_codepage);
#if CONFIG_CODEC == SWCODEC
audio_set_crossfade(global_settings.crossfade);
dsp_set_replaygain(true);
dsp_set_crossfeed(global_settings.crossfeed);
dsp_set_crossfeed_direct_gain(global_settings.crossfeed_direct_gain);
dsp_set_crossfeed_cross_params(global_settings.crossfeed_cross_gain,
global_settings.crossfeed_cross_gain
+ global_settings.crossfeed_hf_attenuation,
global_settings.crossfeed_hf_cutoff);
dsp_set_eq(global_settings.eq_enabled);
dsp_set_eq_precut(global_settings.eq_precut);
/* Update all EQ bands */
for(i = 0; i < 5; i++) {
dsp_set_eq_coefs(i);
}
#endif
#ifdef HAVE_SPDIF_POWER
spdif_power_enable(global_settings.spdif_enable);
#endif
#ifdef CONFIG_BACKLIGHT
set_backlight_filter_keypress(global_settings.bl_filter_first_keypress);
#ifdef HAVE_REMOTE_LCD
set_remote_backlight_filter_keypress(global_settings.remote_bl_filter_first_keypress);
#endif
#endif
}
/* helper to load global_settings from a bitfield, as described per table */
static void load_bit_table(const struct bit_entry* p_table, int count, int bitstart)
{
uint32_t *p_bitfield = (uint32_t *)config_block; /* 32 bit addr. */
uint32_t value; /* 32 bit content */
int i;
int maxbit; /* how many bits are valid in the saved part */
const struct bit_entry* p_run = p_table; /* start after the size info */
count--; /* first is excluded from loop */
maxbit = get_bits(p_bitfield, bitstart, p_table->bit_size);
bitstart += p_table->bit_size;
for (i=0; i<count; i++)
{
int size;
p_run++;
size = p_run->bit_size & 0x3F; /* mask off abused bits */
if (bitstart + size > maxbit)
break; /* exit if this is not valid any more in bitfield */
value = get_bits(p_bitfield, bitstart, size);
bitstart += size;
if (p_run->bit_size & SIGNED)
{ // sign extend the read value
unsigned long mask = 0xFFFFFFFF << (size - 1);
if (value & mask) /* true if MSB of value is set */
value |= mask;
}
/* could do a memcpy, but that would be endian-dependent */
switch(p_run->byte_size)
{
case 1:
((uint8_t *)&global_settings)[p_run->settings_offset] =
(unsigned char)value;
break;
case 2:
((uint16_t *)&global_settings)[p_run->settings_offset/2] =
(unsigned short)value;
break;
case 4:
((uint32_t *)&global_settings)[p_run->settings_offset/4] =
(unsigned int)value;
break;
default:
DEBUGF( "load_bit_table: illegal size!\n" );
continue;
}
}
}
/*
* load settings from disk or RTC RAM
*/
void settings_load(int which)
{
int i;
DEBUGF( "reload_all_settings()\n" );
/* load the buffer from the RTC (resets it to all-unused if the block
is invalid) and decode the settings which are set in the block */
if (!load_config_buffer(which))
{
/* load scalar values from RTC and HD sector, specified via table */
if (which & SETTINGS_RTC)
{
load_bit_table(rtc_bits, sizeof(rtc_bits)/sizeof(rtc_bits[0]), 4*8);
}
if (which & SETTINGS_HD)
{
load_bit_table(hd_bits, sizeof(hd_bits)/sizeof(hd_bits[0]),
RTC_BLOCK_SIZE*8);
}
if ( global_settings.contrast < MIN_CONTRAST_SETTING )
global_settings.contrast = lcd_default_contrast();
i = 0xb8;
strncpy((char *)global_settings.wps_file, (char *)&config_block[i],
MAX_FILENAME);
i+= MAX_FILENAME;
strncpy((char *)global_settings.lang_file, (char *)&config_block[i],
MAX_FILENAME);
i+= MAX_FILENAME;
strncpy((char *)global_settings.font_file, (char *)&config_block[i],
MAX_FILENAME);
i+= MAX_FILENAME;
#ifdef HAVE_REMOTE_LCD
strncpy((char *)global_settings.rwps_file, (char *)&config_block[i],
MAX_FILENAME);
i+= MAX_FILENAME;
#endif
#ifdef CONFIG_TUNER
strncpy((char *)global_settings.fmr_file, (char *)&config_block[i],
MAX_FILENAME);
i+= MAX_FILENAME;
#endif
#ifdef HAVE_LCD_COLOR
strncpy((char *)global_settings.backdrop_file, (char *)&config_block[i],
MAX_FILENAME);
i+= MAX_FILENAME;
#endif
#ifdef HAVE_LCD_BITMAP
strncpy((char *)global_settings.kbd_file, (char *)&config_block[i],
MAX_FILENAME);
i+= MAX_FILENAME;
#endif
}
}
void set_file(char* filename, char* setting, int maxlen)
{
char* fptr = strrchr(filename,'/');
int len;
int extlen = 0;
char* ptr;
if (!fptr)
return;
*fptr = 0;
fptr++;
len = strlen(fptr);
ptr = fptr + len;
while ((*ptr != '.') && (ptr != fptr)) {
extlen++;
ptr--;
}
if(ptr == fptr) extlen = 0;
if (strncasecmp(ROCKBOX_DIR, filename ,strlen(ROCKBOX_DIR)) ||
(len-extlen > maxlen))
return;
strncpy(setting, fptr, len-extlen);
setting[len-extlen]=0;
settings_save();
}
/* helper to sort a .cfg file entry into a global_settings member,
as described per table. Returns the position if found, else 0. */
static int load_cfg_table(
const struct bit_entry* p_table, /* the table which describes the entries */
int count, /* number of entries in the table, including the first */
const char* name, /* the item to be searched */
const char* value, /* the value which got loaded for that item */
int hint) /* position to start looking */
{
int i = hint;
do
{
if (p_table[i].cfg_name != NULL && !strcasecmp(name, p_table[i].cfg_name))
{ /* found */
int val = 0;
if (p_table[i].cfg_val == NULL)
{ /* numerical value, just convert the string */
val = atoi(value);
}
#if HAVE_LCD_COLOR
else if (!strncasecmp(p_table[i].cfg_val,"rgb",4))
{
val = hex_to_rgb(value);
}
#endif
else
{ /* set of string values, find the index */
const char* item;
const char* run;
int len = strlen(value);
item = run = p_table[i].cfg_val;
while(1)
{
/* count the length of the field */
while (*run != ',' && *run != '\0')
run++;
if (!strncasecmp(value, item, MAX(run-item, len)))
break; /* match, exit the search */
if (*run == '\0') /* reached the end of the choices */
return i; /* return the position, but don't update */
val++; /* count the item up */
run++; /* behind the ',' */
item = run;
}
}
/* could do a memcpy, but that would be endian-dependent */
switch(p_table[i].byte_size)
{
case 1:
((unsigned char*)&global_settings)[p_table[i].settings_offset] =
(unsigned char)val;
break;
case 2:
((unsigned short*)&global_settings)[p_table[i].settings_offset/2] =
(unsigned short)val;
break;
case 4:
((unsigned int*)&global_settings)[p_table[i].settings_offset/4] =
(unsigned int)val;
break;
default:
DEBUGF( "illegal size!" );
continue;
}
return i; /* return the position */
}
i++;
if (i==count)
i=1; /* wraparound */
} while (i != hint); /* back where we started, all searched */
return 0; /* indicate not found */
}
bool settings_load_config(const char* file)
{
int fd;
char line[128];
fd = open(file, O_RDONLY);
if (fd < 0)
return false;
while (read_line(fd, line, sizeof line) > 0)
{
char* name;
char* value;
const struct bit_entry* table[2] = { rtc_bits, hd_bits };
const int ta_size[2] = {
sizeof(rtc_bits)/sizeof(rtc_bits[0]),
sizeof(hd_bits)/sizeof(hd_bits[0])
};
int last_table = 0; /* which table was used last round */
int last_pos = 1; /* at which position did we succeed */
int pos; /* currently returned position */
if (!settings_parseline(line, &name, &value))
continue;
/* check for the string values */
if (!strcasecmp(name, "wps")) {
if (wps_data_load(gui_wps[0].data, value, true))
set_file(value, (char *)global_settings.wps_file, MAX_FILENAME);
}
#if defined(HAVE_REMOTE_LCD) && (NB_SCREENS > 1)
else if (!strcasecmp(name, "rwps")) {
if (wps_data_load(gui_wps[1].data, value, true))
set_file(value, (char *)global_settings.rwps_file, MAX_FILENAME);
}
#endif
else if (!strcasecmp(name, "lang")) {
if (!lang_load(value))
{
set_file(value, (char *)global_settings.lang_file, MAX_FILENAME);
talk_init(); /* use voice of same language */
}
}
#ifdef CONFIG_TUNER
else if (!strcasecmp(name, "fmr")) {
set_file(value, global_settings.fmr_file, MAX_FILENAME);
}
#endif
#ifdef HAVE_LCD_BITMAP
else if (!strcasecmp(name, "font")) {
if (font_load(value))
set_file(value, (char *)global_settings.font_file, MAX_FILENAME);
}
#endif
#ifdef HAVE_LCD_COLOR
else if (!strcasecmp(name, "backdrop")) {
if (load_main_backdrop(value))
set_file(value, (char *)global_settings.backdrop_file, MAX_FILENAME);
}
#endif
#ifdef HAVE_LCD_BITMAP
else if (!strcasecmp(name, "keyboard")) {
if (!load_kbd(value))
set_file(value, (char *)global_settings.kbd_file, MAX_FILENAME);
}
#endif
/* check for scalar values, using the two tables */
pos = load_cfg_table(table[last_table], ta_size[last_table],
name, value, last_pos);
if (pos) /* success */
{
last_pos = pos; /* remember as a position hint for next round */
continue;
}
last_table = 1-last_table; /* try other table */
last_pos = 1; /* search from start */
pos = load_cfg_table(table[last_table], ta_size[last_table],
name, value, last_pos);
if (pos) /* success */
{
last_pos = pos; /* remember as a position hint for next round */
continue;
}
}
close(fd);
settings_apply();
settings_save();
return true;
}
/* helper to save content of global_settings into a file,
as described per table */
static void save_cfg_table(const struct bit_entry* p_table, int count, int fd)
{
long value; /* 32 bit content */
int i;
const struct bit_entry* p_run = p_table; /* start after the size info */
count--; /* first is excluded from loop */
for (i=0; i<count; i++)
{
p_run++;
if (p_run->cfg_name == NULL)
continue; /* this value is not to be saved */
/* could do a memcpy, but that would be endian-dependent */
switch(p_run->byte_size)
{
case 1:
if (p_run->bit_size & SIGNED) /* signed? */
value = ((char*)&global_settings)[p_run->settings_offset];
else
value = ((unsigned char*)&global_settings)[p_run->settings_offset];
break;
case 2:
if (p_run->bit_size & SIGNED) /* signed? */
value = ((short*)&global_settings)[p_run->settings_offset/2];
else
value = ((unsigned short*)&global_settings)[p_run->settings_offset/2];
break;
case 4:
value = ((unsigned int*)&global_settings)[p_run->settings_offset/4];
break;
default:
DEBUGF( "illegal size!" );
continue;
}
if (p_run->cfg_val == NULL) /* write as number */
{
fdprintf(fd, "%s: %ld\r\n", p_run->cfg_name, value);
}
#ifdef HAVE_LCD_COLOR
else if (!strcasecmp(p_run->cfg_val, "rgb"))
{
fdprintf(fd, "%s: %02x%02x%02x\r\n", p_run->cfg_name,
(int)RGB_UNPACK_RED(value),
(int)RGB_UNPACK_GREEN(value),
(int)RGB_UNPACK_BLUE(value));
}
#endif
else /* write as item */
{
const char* p = p_run->cfg_val;
fdprintf(fd, "%s: ", p_run->cfg_name);
while(value >= 0)
{
char c = *p++; /* currently processed char */
if (c == ',') /* separator */
value--;
else if (c == '\0') /* end of string */
break; /* not found */
else if (value == 0) /* the right place */
write(fd, &c, 1); /* char by char, this is lame, OK */
}
fdprintf(fd, "\r\n");
if (p_run->cfg_val != off_on) /* explaination for non-bool */
fdprintf(fd, "# (possible values: %s)\r\n", p_run->cfg_val);
}
}
}
bool settings_save_config(void)
{
int fd;
char filename[MAX_PATH];
create_numbered_filename(filename, ROCKBOX_DIR, "config", ".cfg", 2);
/* allow user to modify filename */
while (true) {
if (!kbd_input(filename, sizeof filename)) {
fd = creat(filename,0);
if (fd < 0)
gui_syncsplash(HZ, true, str(LANG_FAILED));
else
break;
}
else {
gui_syncsplash(HZ, true, str(LANG_MENU_SETTING_CANCEL));
return false;
}
}
fdprintf(fd, "# .cfg file created by rockbox %s - "
"http://www.rockbox.org\r\n#\r\n#\r\n# wps / rwps / language"
" / font / fmpreset / backdrop \r\n#\r\n", appsversion);
if (global_settings.wps_file[0] != 0)
fdprintf(fd, "wps: %s/%s.wps\r\n", WPS_DIR,
global_settings.wps_file);
#ifdef HAVE_REMOTE_LCD
if (global_settings.rwps_file[0] != 0)
fdprintf(fd, "rwps: %s/%s.rwps\r\n", WPS_DIR,
global_settings.rwps_file);
#endif
if (global_settings.lang_file[0] != 0)
fdprintf(fd, "lang: %s/%s.lng\r\n", ROCKBOX_DIR LANG_DIR,
global_settings.lang_file);
#ifdef HAVE_LCD_BITMAP
if (global_settings.font_file[0] != 0)
fdprintf(fd, "font: %s/%s.fnt\r\n", ROCKBOX_DIR FONT_DIR,
global_settings.font_file);
#endif
#ifdef HAVE_LCD_COLOR
if (global_settings.backdrop_file[0] != 0)
fdprintf(fd, "backdrop: %s/%s.bmp\r\n", BACKDROP_DIR,
global_settings.backdrop_file);
#endif
#ifdef CONFIG_TUNER
if (global_settings.fmr_file[0] != 0)
fdprintf(fd, "fmr: %s/%s.fmr\r\n", FMPRESET_PATH,
global_settings.fmr_file);
#endif
#ifdef HAVE_LCD_BITMAP
if (global_settings.kbd_file[0] != 0)
fdprintf(fd, "keyboard: %s/%s.kbd\r\n", ROCKBOX_DIR,
global_settings.kbd_file);
#endif
/* here's the action: write values to file, specified via table */
save_cfg_table(rtc_bits, sizeof(rtc_bits)/sizeof(rtc_bits[0]), fd);
save_cfg_table(hd_bits, sizeof(hd_bits)/sizeof(hd_bits[0]), fd);
close(fd);
gui_syncsplash(HZ, true, str(LANG_SETTINGS_SAVED));
return true;
}
/* helper to load defaults from table into global_settings members */
static void default_table(const struct bit_entry* p_table, int count)
{
int i;
for (i=1; i<count; i++) /* exclude the first, the size placeholder */
{
/* could do a memcpy, but that would be endian-dependent */
switch(p_table[i].byte_size)
{
case 1:
((unsigned char*)&global_settings)[p_table[i].settings_offset] =
(unsigned char)p_table[i].default_val;
break;
case 2:
((unsigned short*)&global_settings)[p_table[i].settings_offset/2] =
(unsigned short)p_table[i].default_val;
break;
case 4:
((unsigned int*)&global_settings)[p_table[i].settings_offset/4] =
(unsigned int)p_table[i].default_val;
break;
default:
DEBUGF( "illegal size!" );
continue;
}
}
}
/*
* reset all settings to their default value
*/
void settings_reset(void) {
DEBUGF( "settings_reset()\n" );
/* read defaults from table(s) into global_settings */
default_table(rtc_bits, sizeof(rtc_bits)/sizeof(rtc_bits[0]));
default_table(hd_bits, sizeof(hd_bits)/sizeof(hd_bits[0]));
/* do some special cases not covered by table */
global_settings.volume = sound_default(SOUND_VOLUME);
global_settings.balance = sound_default(SOUND_BALANCE);
global_settings.bass = sound_default(SOUND_BASS);
global_settings.treble = sound_default(SOUND_TREBLE);
global_settings.channel_config = sound_default(SOUND_CHANNELS);
global_settings.stereo_width = sound_default(SOUND_STEREO_WIDTH);
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
global_settings.loudness = sound_default(SOUND_LOUDNESS);
global_settings.avc = sound_default(SOUND_AVC);
global_settings.mdb_strength = sound_default(SOUND_MDB_STRENGTH);
global_settings.mdb_harmonics = sound_default(SOUND_MDB_HARMONICS);
global_settings.mdb_center = sound_default(SOUND_MDB_CENTER);
global_settings.mdb_shape = sound_default(SOUND_MDB_SHAPE);
global_settings.mdb_enable = sound_default(SOUND_MDB_ENABLE);
global_settings.superbass = sound_default(SOUND_SUPERBASS);
#endif
global_settings.contrast = lcd_default_contrast();
#ifdef CONFIG_TUNER
global_settings.fmr_file[0] = '\0';
#endif
global_settings.wps_file[0] = '\0';
#ifdef HAVE_REMOTE_LCD
global_settings.rwps_file[0] = '\0';
#endif
global_settings.font_file[0] = '\0';
global_settings.lang_file[0] = '\0';
#ifdef HAVE_LCD_COLOR
global_settings.backdrop_file[0] = '\0';
global_settings.fg_color = LCD_DEFAULT_FG;
global_settings.bg_color = LCD_DEFAULT_BG;
#endif
#ifdef HAVE_LCD_BITMAP
global_settings.kbd_file[0] = '\0';
#endif
}
bool set_bool(const char* string, bool* variable )
{
return set_bool_options(string, variable,
(char *)STR(LANG_SET_BOOL_YES),
(char *)STR(LANG_SET_BOOL_NO),
NULL);
}
/* wrapper to convert from int param to bool param in set_option */
static void (*boolfunction)(bool);
void bool_funcwrapper(int value)
{
if (value)
boolfunction(true);
else
boolfunction(false);
}
bool set_bool_options(const char* string, bool* variable,
const char* yes_str, int yes_voice,
const char* no_str, int no_voice,
void (*function)(bool))
{
struct opt_items names[] = {
{(unsigned char *)no_str, no_voice},
{(unsigned char *)yes_str, yes_voice}
};
bool result;
boolfunction = function;
result = set_option(string, variable, BOOL, names, 2,
function ? bool_funcwrapper : NULL);
return result;
}
void talk_unit(int unit, int value)
{
if (global_settings.talk_menu)
{
if (unit < UNIT_LAST)
{ /* use the available unit definition */
talk_value(value, unit, false);
}
else
{ /* say the number, followed by an arbitrary voice ID */
talk_number(value, false);
talk_id(unit, true);
}
}
}
bool set_int(const unsigned char* string,
const char* unit,
int voice_unit,
int* variable,
void (*function)(int),
int step,
int min,
int max,
void (*formatter)(char*, int, int, const char*) )
{
int button;
int oldvalue=*variable;
struct gui_select select;
gui_select_init_numeric(&select, (char *)string, *variable, min, max, step, unit,
formatter);
gui_syncselect_draw(&select);
talk_unit(voice_unit, *variable);
while (!select.validated)
{
button = button_get_w_tmo(HZ/2);
if(gui_syncselect_do_button(&select, button))
{
*variable=select.options.option;
gui_syncselect_draw(&select);
talk_unit(voice_unit, *variable);
if ( function )
function(*variable);
}
gui_syncstatusbar_draw(&statusbars, false);
if(select.canceled)
{
*variable=oldvalue;
if ( function )
function(*variable);
return false;
}
if(default_event_handler(button) == SYS_USB_CONNECTED)
return true;
}
return false;
}
/* NOTE: the 'type' parameter specifies the actual type of the variable
that 'variable' points to. not the value within. Only variables with
type 'bool' should use parameter BOOL.
The type separation is necessary since int and bool are fundamentally
different and bit-incompatible types and can not share the same access
code. */
#define set_type_fromint(type, dest, value) \
if (type == INT) \
*(int *)dest=value; \
else \
*(bool *)dest=value?true:false
#define type_fromvoidptr(type, value) \
(type == INT)? \
(int)(*(int*)(value)) \
: \
(bool)(*(bool*)(value))
#define get_int_fromtype(type, var) \
(type == INT)?*(int *)var:(*(bool *)var?1:0)
bool set_option(const char* string, void* variable, enum optiontype type,
const struct opt_items* options, int numoptions, void (*function)(int))
{
int button;
int oldvalue;
/* oldvalue=*variable; */
oldvalue=get_int_fromtype(type, variable);
struct gui_select select;
gui_select_init_items(&select, string, oldvalue, options, numoptions);
gui_syncselect_draw(&select);
if (global_settings.talk_menu)
talk_id(options[select.options.option].voice_id, true);
while ( !select.validated )
{
gui_syncstatusbar_draw(&statusbars, true);
button = button_get_w_tmo(HZ/2);
select.options.limit_loop = false;
if(gui_syncselect_do_button(&select, button))
{
/* *variable = gui_select_get_selected(&select) */
set_type_fromint(type, variable, select.options.option);
gui_syncselect_draw(&select);
if (global_settings.talk_menu)
talk_id(options[select.options.option].voice_id, false);
if ( function )
function(type_fromvoidptr(type, variable));
}
gui_syncstatusbar_draw(&statusbars, false);
if(select.canceled)
{
/* *variable=oldvalue; */
set_type_fromint(type, variable, oldvalue);
if ( function )
function(type_fromvoidptr(type, variable));
return false;
}
if(default_event_handler(button) == SYS_USB_CONNECTED)
return true;
}
return false;
}
#ifdef HAVE_RECORDING
/* This array holds the record timer interval lengths, in seconds */
static const unsigned long rec_timer_seconds[] =
{
0, /* 0 means OFF */
5*60, /* 00:05 */
10*60, /* 00:10 */
15*60, /* 00:15 */
30*60, /* 00:30 */
60*60, /* 01:00 */
74*60, /* 74:00 */
80*60, /* 80:00 */
2*60*60, /* 02:00 */
4*60*60, /* 04:00 */
6*60*60, /* 06:00 */
8*60*60, /* 08:00 */
10L*60*60, /* 10:00 */
12L*60*60, /* 12:00 */
18L*60*60, /* 18:00 */
24L*60*60 /* 24:00 */
};
unsigned int rec_timesplit_seconds(void)
{
return rec_timer_seconds[global_settings.rec_timesplit];
}
/*
* Time strings used for the trigger durations.
* Keep synchronous to trigger_times in settings_apply_trigger
*/
const char * const trig_durations[TRIG_DURATION_COUNT] =
{
"0s", "1s", "2s", "5s",
"10s", "15s", "20s", "25s", "30s",
"1min", "2min", "5min", "10min"
};
void settings_apply_trigger(void)
{
/* Keep synchronous to trig_durations and trig_durations_conf*/
static const long trigger_times[TRIG_DURATION_COUNT] = {
0, HZ, 2*HZ, 5*HZ,
10*HZ, 15*HZ, 20*HZ, 25*HZ, 30*HZ,
60*HZ, 2*60*HZ, 5*60*HZ, 10*60*HZ
};
peak_meter_define_trigger(
global_settings.rec_start_thres,
trigger_times[global_settings.rec_start_duration],
MIN(trigger_times[global_settings.rec_start_duration] / 2, 2*HZ),
global_settings.rec_stop_thres,
trigger_times[global_settings.rec_stop_postrec],
trigger_times[global_settings.rec_stop_gap]
);
}
#endif