--- orig/apps/filetypes.c 2005-08-30 11:02:48.000000000 +0200 +++ dev/apps/filetypes.c 2005-09-02 11:33:00.194149500 +0200 @@ -208,6 +208,24 @@ return false; } +/* return plugin if plugin exists */ +char* filetype_find_plugin(const char* plugin) +{ + int i; + + for (i=first_soft_filetype; i < cnt_filetypes; i++) + { + if (filetypes[i].plugin) + { + if (!strcasecmp(plugin, filetypes[i].plugin)) + { + return filetypes[i].plugin; + } + } + } + return NULL; +} + /* get the "dynamic" attribute for an extension */ int filetype_get_attr(const char* name) { --- orig/apps/filetypes.h 2005-03-06 19:26:34.000000000 +0100 +++ dev/apps/filetypes.h 2005-09-02 11:33:00.209774500 +0200 @@ -34,6 +34,7 @@ bool filetype_supported(int); int filetype_load_menu(struct menu_item*, int); int filetype_load_plugin(const char*, char*); +char* filetype_find_plugin(const char*); struct file_type { #ifdef HAVE_LCD_BITMAP --- orig/apps/onplay.c 2005-09-02 11:36:25.303524500 +0200 +++ dev/apps/onplay.c 2005-09-02 11:36:25.287899500 +0200 @@ -66,6 +66,14 @@ bool queue; }; +bool browse_id3(void) +{ + if (filetype_load_plugin(filetype_find_plugin("edit_id3"),selected_file) + == PLUGIN_USB_CONNECTED) + onplay_result = ONPLAY_RELOAD_DIR; + return false; +} + /* ----------------------------------------------------------------------- */ /* Displays the bookmark menu options for the user to decide. This is an */ /* interface function. */ @@ -187,6 +195,7 @@ lcd_puts(0,3,str(LANG_CONFIRM_WITH_PLAY_RECORDER)); lcd_puts(0,4,str(LANG_CANCEL_WITH_ANY_RECORDER)); #endif +#include "plugin.h" lcd_update(); @@ -553,7 +562,8 @@ if (file) { - if (context == CONTEXT_WPS) + if ((selected_file_attr & TREE_ATTR_MASK) == TREE_ATTR_MPA && + filetype_find_plugin("edit_id3")) { items[i].desc = ID2P(LANG_MENU_SHOW_ID3_INFO); items[i].function = browse_id3; --- orig/apps/plugin.c 2005-09-02 11:36:25.412899500 +0200 +++ dev/apps/plugin.c 2005-09-02 11:36:25.381649500 +0200 @@ -51,6 +51,7 @@ #if (CONFIG_CODEC == SWCODEC) #include "pcm_playback.h" #endif +#include "buffer.h" #ifdef HAVE_LCD_BITMAP #include "peakmeter.h" @@ -341,6 +342,15 @@ /* new stuff at the end, sort into place next time the API gets incompatible */ + +#ifdef HAVE_LCD_BITMAP + lcd_setmargins, +#else + put_cursorxy, +#endif + set_bool, + set_int, + id3_get_genre, }; int plugin_load(const char* plugin, void* parameter) --- orig/apps/plugin.h 2005-09-02 11:36:25.506649500 +0200 +++ dev/apps/plugin.h 2005-09-02 11:36:25.491024500 +0200 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include "config.h" #include "dir.h" @@ -52,6 +53,7 @@ #include "timer.h" #include "thread.h" #include "playlist.h" +#include "menu.h" #ifdef HAVE_LCD_BITMAP #include "widgets.h" #endif @@ -89,7 +91,7 @@ #endif /* increase this every time the api struct changes */ -#define PLUGIN_API_VERSION 50 +#define PLUGIN_API_VERSION 51 /* update this to latest version if a change to the api struct breaks backwards compatibility (and please take the opportunity to sort in any @@ -427,6 +429,16 @@ /* new stuff at the end, sort into place next time the API gets incompatible */ +#ifdef HAVE_LCD_BITMAP + void (*lcd_setmargins)(int xmargin, int ymargin); +#else + void (*put_cursorxy)(int x, int y, bool on); +#endif + bool (*set_bool)(const char* string, bool* variable ); + bool (*set_int)(const 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*) ); + char* (*id3_get_genre)(unsigned int genre); }; int plugin_load(const char* plugin, void* parameter); --- orig/apps/plugins/SOURCES 2005-09-02 11:26:37.000000000 +0200 +++ dev/apps/plugins/SOURCES 2005-09-02 11:33:00.319149500 +0200 @@ -3,6 +3,7 @@ chessclock.c credits.c cube.c +edit_id3.c favorites.c firmware_flash.c logo.c --- orig/apps/plugins/edit_id3.c 1970-01-01 01:00:00.000000000 +0100 +++ dev/apps/plugins/edit_id3.c 2005-09-02 11:33:00.350399500 +0200 @@ -0,0 +1,1297 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: editID3.c,v 1.1 2003/06/29 16:33:04 zagor Exp $ + * + * Copyright (C) 2002 Jeremy Zoss + * + * 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 "plugin.h" +#include "time.h" +static struct plugin_api* rb; + +/* function declarations (for convenience) */ +static bool edit_id3_tags(struct mp3entry*, bool); +static bool set_genre(char*, int*); +static int write_id3_tags(struct mp3entry*); +static int write_id3v1(struct mp3entry*); +static int write_id3v2(struct mp3entry*); +static int write_id3v2_frame(int, const char*, char*); +static int id3v2_tagsize(struct mp3entry*); +static void replace_tag(struct mp3entry*, unsigned int, char*); +static int save_unsup_tags(char*, char*); +static int file_insert_data(char*, unsigned int, char*, unsigned int); +static int file_copy_data(int, int, unsigned int, char*, unsigned int); + +static char unsuptag_file[MAX_PATH]; + +#define UNSYNC(b0,b1,b2,b3) (((b0 & 0x7F) << (3*7)) | \ + ((b1 & 0x7F) << (2*7)) | \ + ((b2 & 0x7F) << (1*7)) | \ + ((b3 & 0x7F) << (0*7))) + +#define SYNC(num, b0,b1,b2,b3) ( (b0=((num) >> (3*7) ) & 0x7F ) | \ + (b1=((num) >> (2*7) ) & 0x7F ) | \ + (b2=((num) >> (1*7) ) & 0x7F ) | \ + (b3=((num) >> (0*7) ) & 0x7F ) ) + +#define BYTES2INT(b0,b1,b2,b3) (((b0 & 0xFF) << (3*8)) | \ + ((b1 & 0xFF) << (2*8)) | \ + ((b2 & 0xFF) << (1*8)) | \ + ((b3 & 0xFF) << (0*8))) + +#define INT2BYTES(num,b0,b1,b2,b3) ( (b0=((num) >> (3*8) ) & 0xFF ) | \ + (b1=((num) >> (2*8) ) & 0xFF ) | \ + (b2=((num) >> (1*8) ) & 0xFF ) | \ + (b3=((num) >> (0*8) ) & 0xFF ) ) + +/* the following functions and structures are analogous to the + * tag_resolver struct in id3.c. Rather than export all those functions + * through the plugin API, I copied them (modified) here. Of course, + * this means that changes to the tag_resolver struct capabilities + * (e.g. due to new supported tag types) should be mirrored here too + */ + +/* Structure for ID3 Tag extraction information */ +struct tag_struct { + const char* tag; + int tag_length; + size_t v2_offset; + size_t v1_offset; + const char * prompt; + void (*v2_to_v1)(struct mp3entry *entry, struct tag_struct *tr); + void (*v1_to_v2)(struct mp3entry *entry, struct tag_struct *tr); +}; + +/* str : v2->v1 */ +static void str_v2v1( struct mp3entry *entry, struct tag_struct *tr) +{ + char *v1str = (char*)( (char*)entry + tr->v1_offset ); + char *v2str = *(char**)( (char*)entry + tr->v2_offset ); + + if (v2str) + rb->memcpy(v1str, v2str, 30); + else + v1str[0]=0; +} + +/* str : v1->v2 */ +static void str_v1v2( struct mp3entry *entry, struct tag_struct *tr) +{ + char *v1str = (char*)( (char*)entry + tr->v1_offset ); + + replace_tag(entry, tr->v2_offset, v1str); +} + +/* int : v2->v1 */ +static void int_v2v1( struct mp3entry *entry, struct tag_struct *tr) +{ + char *str = *(char**)( (char*)entry + tr->v2_offset ); + unsigned int *iptr = (unsigned int*)( (char*)entry + tr->v1_offset ); + + if (str) + *iptr = rb->atoi(str); + else + *iptr = 0; +} + +/* int : v1->v2 */ +static void int_v1v2( struct mp3entry *entry, struct tag_struct *tr) +{ + unsigned int num = *(unsigned int*)( (char*)entry + tr->v1_offset ); + char str[5]; + + rb->snprintf(str, sizeof(str), "%d", num); + replace_tag(entry, tr->v2_offset, str); +} + +/* genre : v2->v1 */ +static void genre_v2v1( struct mp3entry *entry, struct tag_struct *tr) +{ + char *str = *(char**)( (char*)entry + tr->v2_offset ); + unsigned int *genre = (unsigned int*)( (char*)entry + tr->v1_offset ); + + if ( str && str[0] == '(' && str[1] != '(' ) + *genre = rb->atoi(str+1); + else + *genre = 0xff; +} + +/* genre : v1->v2 */ +static void genre_v1v2( struct mp3entry *entry, struct tag_struct *tr) +{ + unsigned int genre = *(unsigned char*)( (char*)entry + tr->v1_offset ); + char str[30]; + + rb->snprintf(str, sizeof(str), "(%d) %s", genre, rb->id3_get_genre(genre)); + replace_tag(entry, tr->v2_offset, str); +} + +static struct tag_struct taglist[] = { + { "TPE1", 4, offsetof(struct mp3entry, artist), + offsetof(struct mp3entry, v1_artist), + "Artist", &str_v2v1, &str_v1v2 }, + { "TP1", 3, offsetof(struct mp3entry, artist), + offsetof(struct mp3entry, v1_artist), + "Artist", &str_v2v1, &str_v1v2 }, + { "TIT2", 4, offsetof(struct mp3entry, title), + offsetof(struct mp3entry, v1_title), + "Title", &str_v2v1, &str_v1v2 }, + { "TT2", 3, offsetof(struct mp3entry, title), + offsetof(struct mp3entry, v1_title), + "Title", &str_v2v1, &str_v1v2 }, + { "TALB", 4, offsetof(struct mp3entry, album), + offsetof(struct mp3entry, v1_album), + "Album", &str_v2v1, &str_v1v2 }, + { "TRCK", 4, offsetof(struct mp3entry, track_string), + offsetof(struct mp3entry, v1_tracknum), + "Track", &int_v2v1, &int_v1v2 }, + { "TYER", 4, offsetof(struct mp3entry, year_string), + offsetof(struct mp3entry, v1_year), + "Year", &int_v2v1, &int_v1v2 }, + { "TYR", 3, offsetof(struct mp3entry, year_string), + offsetof(struct mp3entry, v1_year), + "Year", &int_v2v1, &int_v1v2 }, + { "TCON", 4, offsetof(struct mp3entry, genre_string), + offsetof(struct mp3entry, v1_genre), + "Genre", &genre_v2v1, &genre_v1v2 }, + { "TCOM", 4, offsetof(struct mp3entry, composer), + 0, "Composer", NULL, NULL} +}; + +static struct tag_struct *id3v2list[] = { + taglist+2, taglist, taglist+4, taglist+5, taglist+6, taglist+8, + taglist+9 }; +static const unsigned short id3v2list_len = (sizeof(id3v2list) / sizeof(id3v2list[0])); + +static struct tag_struct *id3v1list[] = { + taglist+2, taglist+0, taglist+4, taglist+6, taglist+5, taglist+8 }; + +static const unsigned short TAGLIST_SIZE = ((int)(sizeof(taglist) / sizeof(taglist[0]))); + +#define ID3_DEF_PADDING 2000 /* default id3v2 padding (bytes) */ + +#define MAX_TAG_STRLEN 256 + +#define NUM_EDITABLE_ID3_TAGS id3v2list_len +#define NUM_KNOWN_ID3_TAGS NUM_EDITABLE_ID3_TAGS + 3 + +#ifdef HAVE_LCD_BITMAP +#define MARGIN_X (NUM_KNOWN_ID3_TAGS > max_on_screen ? \ + SCROLLBAR_WIDTH : 0) + CURSOR_WIDTH +#define MARGIN_Y 0 +#define LINE_X 0 +#define LINE_Y 0 +#define CURSOR_X (NUM_KNOWN_ID3_TAGS > max_on_screen ? 1 : 0) +#define CURSOR_Y 0 +#define CURSOR_WIDTH 0 +#define SCROLLBAR_X 0 +#define SCROLLBAR_Y 0 +#define SCROLLBAR_WIDTH 6 +#else /* HAVE_LCD_BITMAP */ +#define LINE_X 2 +#define LINE_Y 0 +#define CURSOR_X 0 +#define CURSOR_Y 0 +#define MAX_ON_SCREEN 2 +#endif /* HAVE_LCD_BITMAP */ + +#if CONFIG_KEYPAD == RECORDER_PAD +#define TAG_PREV BUTTON_UP +#define TAG_NEXT BUTTON_DOWN +#define TAG_EDIT BUTTON_RIGHT +#define TAG_EDITSTR BUTTON_PLAY +#define TAG_REVERT BUTTON_LEFT +#define TAG_CLEAR BUTTON_LEFT | BUTTON_REPEAT +#define TAG_ACCEPT BUTTON_ON +#define TAG_CANCEL BUTTON_OFF + +#elif CONFIG_KEYPAD == PLAYER_PAD +#define TAG_PREV BUTTON_LEFT +#define TAG_NEXT BUTTON_RIGHT +#define TAG_EDIT 0xff /* Not enough keys!! */ +#define TAG_EDITSTR BUTTON_PLAY +#define TAG_REVERT BUTTON_STOP +#define TAG_CLEAR BUTTON_STOP | BUTTON_REPEAT +#define TAG_ACCEPT BUTTON_ON +#define TAG_CANCEL BUTTON_MENU + +#elif CONFIG_KEYPAD == ONDIO_PAD +#define TAG_PREV BUTTON_LEFT +#define TAG_NEXT BUTTON_RIGHT +#define TAG_EDIT 0xff /* Not enough keys!! */ +#define TAG_EDITSTR BUTTON_PLAY +#define TAG_REVERT BUTTON_STOP +#define TAG_CLEAR BUTTON_STOP | BUTTON_REPEAT +#define TAG_ACCEPT BUTTON_ON +#define TAG_CANCEL BUTTON_MENU +#endif + +/* + * UI for displaying/editing ID3-tag values + * + * Arguments: entry - id3 struct to display/edit + * edit - flag to allow editing (or restrict to display only) + * + * Returns: nonzero if error + */ +static bool edit_id3_tags(struct mp3entry *entry, bool edit) +{ + unsigned short i; + unsigned short max_on_screen; + bool done = false; + unsigned short scnline=0; + unsigned short startidx=0; + char linestr[MAX_PATH]; + char tmptag[MAX_TAG_STRLEN]; + int button; + struct mp3entry lcl_entry; + struct tm *tm; + +#ifdef HAVE_LCD_BITMAP + int line_height; + int fw, fh; + rb->lcd_getstringsize("A", &fw, &fh); + max_on_screen = (LCD_HEIGHT - MARGIN_Y) / fh; + line_height = fh; +#else + max_on_screen = MAX_ON_SCREEN; +#endif + + /* copy tag data to local buffers */ + rb->memcpy(lcl_entry.id3v2buf, entry->id3v2buf, sizeof(lcl_entry.id3v2buf)); + for (i=0; iv2_offset ); + char** t2 = (char**)( (char*)&lcl_entry + tr->v2_offset ); + char* b1 = entry->id3v2buf; + char* b2 = lcl_entry.id3v2buf; + + *t2 = (*t1) ? (*t1-b1)+b2 : 0; + if (tr->v2_to_v1) + tr->v2_to_v1(&lcl_entry, tr); + } + lcl_entry.length = entry->length; + lcl_entry.frequency = entry->frequency; + lcl_entry.bitrate = entry->bitrate; + + + /* MAIN LOOP */ + /*----------------------------------------------------*/ + while (!done) + { + /* clear display */ +#ifdef HAVE_LCD_CHARCELLS + rb->lcd_stop_scroll(); +#endif + rb->lcd_clear_display(); +#ifdef HAVE_LCD_BITMAP + rb->lcd_setmargins(MARGIN_X, MARGIN_Y); +#endif + + /* DISPLAY TAG DATA */ + /*----------------------------------------------------*/ + for (i=0; i= NUM_KNOWN_ID3_TAGS) + break; + + if (disptagidx < NUM_EDITABLE_ID3_TAGS) + { + struct tag_struct *tr = id3v2list[disptagidx]; + char *tag = *(char**)( (char*)&lcl_entry+tr->v2_offset ); + + rb->snprintf(linestr, sizeof(linestr), "[%s]: %s", + tr->prompt, (tag && tag[0]) ? tag : "-----"); + } + else + { + switch (disptagidx-NUM_EDITABLE_ID3_TAGS) + { + case 0: + rb->snprintf(linestr, sizeof(linestr), "[%s]: %d:%02d", + "Length", entry->length / 60000, + entry->length % 60000 / 1000 ); + break; + + case 1: + rb->snprintf(linestr, sizeof(linestr), "[%s]: %d kbps", + "Bitrate", entry->bitrate); + break; + + case 2: + rb->snprintf(linestr, sizeof(linestr), "[%s]: %d Hz", + "Frequency", entry->frequency); + break; + } + } + + if (i == scnline) + { +#ifdef HAVE_LCD_BITMAP + rb->lcd_puts_scroll_style(LINE_X, i, linestr, STYLE_INVERT); +#else + rb->lcd_puts_scroll(LINE_X, i, linestr); + rb->put_cursorxy(CURSOR_X, CURSOR_Y + i, true); +#endif + } + else + rb->lcd_puts(LINE_X, i, linestr); + } /* loop over scn display lines */ + +#ifdef HAVE_LCD_BITMAP + if ((NUM_KNOWN_ID3_TAGS > max_on_screen) ) + rb->scrollbar(SCROLLBAR_X, SCROLLBAR_Y, SCROLLBAR_WIDTH - 1, + LCD_HEIGHT - SCROLLBAR_Y, NUM_KNOWN_ID3_TAGS, + startidx, startidx + max_on_screen, 0); + rb->lcd_update(); +#endif + + /* HANDLE BUTTON ACTIONS */ + /*----------------------------------------------------*/ + button = rb->button_get(true); + switch (button) + { + /*----------------------------------------------------*/ + case TAG_PREV: + case TAG_PREV | BUTTON_REPEAT: + if (scnline) + scnline--; + else if (startidx) + startidx--; + else + if (NUM_KNOWN_ID3_TAGS < max_on_screen) + { + scnline = NUM_KNOWN_ID3_TAGS-1; + startidx = 0; + } + else + { + scnline = max_on_screen-1; + startidx = NUM_KNOWN_ID3_TAGS-max_on_screen; + } + break; + + /*----------------------------------------------------*/ + case TAG_NEXT: + case TAG_NEXT | BUTTON_REPEAT: + if (scnline + startidx + 1 < NUM_KNOWN_ID3_TAGS) + { + if (scnline+1 < max_on_screen) + scnline++; + else + startidx++; + } + else + { + if (NUM_KNOWN_ID3_TAGS < max_on_screen) + scnline = 0; + else + startidx = 0; + scnline = 0; + } + break; + + /*----------------------------------------------------*/ + case TAG_EDIT: /* "simple" edit-method */ + if (!edit) + { + rb->splash(HZ*2, true, "Can't edit currently playing file"); + break; + } + else if ( (startidx+scnline) >= NUM_EDITABLE_ID3_TAGS ) + break; + else + { + struct tag_struct *tr = id3v2list[startidx+scnline]; + bool matched=false; + + if (tr->v1_offset==offsetof(struct mp3entry, v1_tracknum)) + { + rb->snprintf(tmptag, sizeof(tmptag), "ID3 %s", + "Tracknum"); + if (!rb->set_int(tmptag, "", 0, &lcl_entry.v1_tracknum, + NULL, 1, 1, 100, NULL)) + matched=true; + } + else if (tr->v1_offset==offsetof(struct mp3entry, v1_year)) + { + rb->snprintf(tmptag, sizeof(tmptag), "ID3 %s", + "Year"); + + /* default year to current, if not specified */ + tm = rb->get_time(); + if ( !lcl_entry.v1_year ) + lcl_entry.v1_year = tm->tm_year%100 + 2000; + rb->snprintf(tmptag, sizeof(tmptag), "ID3 %s", + "Year"); + if (!rb->set_int(tmptag, "", 0, &lcl_entry.v1_year, + NULL, 1, 1900, 2100, NULL)) + matched=true; + } + else if (tr->v1_offset==offsetof(struct mp3entry, v1_genre)) + { + rb->snprintf(tmptag, sizeof(tmptag), "ID3 %s", + "Genre"); + if (!set_genre(tmptag, &lcl_entry.v1_genre)) + matched=true; + } + + if (matched) + { + if (tr->v1_to_v2) + tr->v1_to_v2(&lcl_entry, tr); + break; + } + } + /* NOTE -- non-matched tags fallthrough to EDITSTR */ + + case TAG_EDITSTR: + if (!edit) + { + rb->splash(HZ*2, true, "Can't edit currently playing file"); + break; + } + else if ( (startidx+scnline) >= NUM_EDITABLE_ID3_TAGS ) + break; + else { + struct tag_struct *tr = id3v2list[startidx + scnline]; + char *str = *(char**)( (char*)&lcl_entry + tr->v2_offset ); + + if (str) + rb->memcpy(tmptag, str, sizeof(tmptag)); + else + tmptag[0]=0; + if (!rb->kbd_input(tmptag, sizeof(tmptag))) + { + replace_tag(&lcl_entry, tr->v2_offset, tmptag); + if (tr->v2_to_v1) + tr->v2_to_v1(&lcl_entry, tr); + } + } + break; + + /*----------------------------------------------------*/ + case TAG_REVERT: + if (!edit || ((startidx+scnline) >= NUM_EDITABLE_ID3_TAGS)) + break; + else + { + struct tag_struct *tr=id3v2list[startidx+scnline]; + char *str = *(char**)( (char*)entry + tr->v2_offset); + + replace_tag(&lcl_entry, tr->v2_offset, str); + if (tr->v2_to_v1) + tr->v2_to_v1(&lcl_entry, tr); + } + break; + + /*----------------------------------------------------*/ + case TAG_CLEAR: + if (!edit || ((startidx+scnline) >= NUM_EDITABLE_ID3_TAGS)) + break; + else + { + struct tag_struct *tr=id3v2list[startidx+scnline]; + replace_tag(&lcl_entry, tr->v2_offset, NULL); + if (tr->v2_to_v1) + tr->v2_to_v1(&lcl_entry, tr); + } + break; + + /*----------------------------------------------------*/ + case TAG_ACCEPT: + if (!edit) return true; + + /* copy tag data back to "real" buffers */ + rb->memcpy(entry->id3v2buf, lcl_entry.id3v2buf, + sizeof(entry->id3v2buf)); + + /* reassign tag pointers to locations in id3v2buf */ + for (i=0; iv2_offset ); + char** t2 = (char**)( (char*)&lcl_entry + tr->v2_offset ); + char* b1 = entry->id3v2buf; + char* b2 = lcl_entry.id3v2buf; + + *t1 = (*t2) ? (*t2-b2)+b1 : 0; + if (tr->v2_to_v1) + tr->v2_to_v1(entry, tr); + } + done = true; + break; + + case TAG_CANCEL: + return true; + break; + } /* switch (button) */ + } /* while (!done) */ + + return false; +} /* edit_id3_tags() */ + + +/* + * UI for selecting a pre-defined ID3v1-style genre from list + * + * Arguments: string - title string to show in UI screen + * variable - initial/output genre value + * + * Returns: true if no error, flase otherwise + */ +static bool set_genre(char* string, int* variable) +{ + bool done = false; + int button; + int new_value = *variable; + int id3_num_genres; + + /* count number of available genres */ + id3_num_genres=0; + while(rb->id3_get_genre(id3_num_genres)) + id3_num_genres++; + + if (NULL == rb->id3_get_genre(new_value)) + new_value = 0; + + rb->lcd_clear_display(); + rb->lcd_puts_scroll(0, 0, string); + + while (!done) { + char str[32]; + rb->snprintf(str, sizeof str, "%03d) %s", new_value, + rb->id3_get_genre(new_value)); + rb->lcd_puts(0, 1, str); +#ifdef HAVE_LCD_BITMAP + rb->lcd_update(); +#endif + + button = rb->button_get_w_tmo(HZ/2); + switch(button) { +#ifdef HAVE_RECORDER_KEYPAD + case BUTTON_UP: + case BUTTON_UP | BUTTON_REPEAT: +#else + case BUTTON_RIGHT: + case BUTTON_RIGHT | BUTTON_REPEAT: +#endif + new_value = (new_value < id3_num_genres-1) ? + new_value+1 : 0; + break; + +#ifdef HAVE_RECORDER_KEYPAD + case BUTTON_DOWN: + case BUTTON_DOWN | BUTTON_REPEAT: +#else + case BUTTON_LEFT: + case BUTTON_LEFT | BUTTON_REPEAT: +#endif + new_value = (new_value > 0) ? + new_value-1 : id3_num_genres-1; + break; + +#ifdef HAVE_RECORDER_KEYPAD + case BUTTON_LEFT: + case BUTTON_PLAY: +#else + case BUTTON_PLAY: +#endif + *variable = new_value; + done = true; + break; + +#ifdef HAVE_RECORDER_KEYPAD + case BUTTON_OFF: +#else +#ifdef BUTTON_STOP + case BUTTON_STOP: +#endif +#ifdef BUTTON_MENU + case BUTTON_MENU: +#endif +#endif + if (new_value != *variable) { + rb->lcd_stop_scroll(); + rb->lcd_puts(0, 0, "Cancelled"); +#ifdef HAVE_LCD_BITMAP + rb->lcd_update(); +#endif + rb->sleep(HZ/2); + } + done = true; + break; + + default: + if (rb->default_event_handler(button)==SYS_USB_CONNECTED) { + return true; + } + } + + } + rb->lcd_stop_scroll(); + + return false; +} + + +/* + * Writes id3v1 and id3v2 tags to file + * + * Arguments: entry - id3 info (including filename) + * + * Returns: true if no error, flase otherwise + */ +static int write_id3_tags(struct mp3entry *entry) +{ + int rc; + if ( (rc=write_id3v1(entry)) ) + return 10*rc-1; + + if ( (rc=write_id3v2(entry)) ) + return 10*rc-2; + + return 0; +} + + +/* + * Writes an id3v1 tag to the file, expanding the file if necessary + * + * Arguments: entry - id3 info (including filename) + * + * Returns: nonzero if error + */ +static int write_id3v1(struct mp3entry *entry) +{ + unsigned char buf[60]; + static char sizes[] = {30, 30, 30, 4+29, 1, 1}; + int fd; + int i; + + fd = rb->open(entry->path, O_RDWR); + if (fd < 0) + return 10*fd-1; + + if (-1 == rb->lseek(fd, -128, SEEK_END)) + return -2; + + if (rb->read(fd, buf, 3) != 3) + return -3; + + if (rb->memcmp(buf, "TAG", 3)) { + rb->lseek(fd, 0, SEEK_END); + if (rb->write(fd, "TAG", 3) != 3) + return -4; + } + + for (i=0; i < (int)sizeof sizes; i++) { + struct tag_struct *tr = id3v1list[i]; + void *ptr = (char*)entry+tr->v1_offset; + + rb->memset(buf, 0, sizeof buf); + switch (i) { + case 0: + case 1: + case 2: + rb->snprintf(buf, sizes[i]+1, "%s", (char*)ptr); + break; + case 3: + rb->snprintf(buf, sizes[i]+1, "%d", *(unsigned int*)ptr); + break; + case 4: + case 5: + rb->snprintf(buf, sizes[i]+1, "%c",(char)*(unsigned int*)ptr); + break; + } + + if ( rb->write(fd, buf, sizes[i]) != sizes[i] ) + return -5; + } + + rb->close(fd); + return 0; +} + + +/* + * Writes an id3v2 tag at head of file, expanding the file if necessary + * - uses id3v2.3 format, since v2.4 writes YEAR in odd format + * + * Arguments: entry - id3 info + * + * Returns: nonzero if error + */ +static int write_id3v2(struct mp3entry *entry) +{ + char *fname; + char buf[1500]; + unsigned long newlen; + int rc=0; + int fd; + int dataleft, buflen, copylen; + unsigned int i; + + fname = entry->path; + + /* resize file, if necessary, for new tag length */ + newlen = id3v2_tagsize(entry); + + if (entry->id3v2len < newlen) { + int addlen = newlen - entry->id3v2len; + + addlen = (addlen < ID3_DEF_PADDING) ? ID3_DEF_PADDING : addlen; + rc = file_insert_data(fname, entry->id3v2len, 0, addlen); + if (rc < 0) + return 10*rc-1; + + entry->id3v2len = entry->id3v2len + addlen; + } + + /* open file */ + fd = rb->open(fname, O_WRONLY); + if (fd < 0) + return 10*rc-2; + + /* write header */ + rb->snprintf(buf, 10, "ID3%c%c%c", 3, 0, 0); + SYNC(entry->id3v2len-10, buf[6], buf[7], buf[8], buf[9]); + rb->write(fd, buf, 10); + + /* write known tags */ + for (i=0; iv2_offset); + + if (tag) write_id3v2_frame(fd, tr->tag, tag); + } + + /* write unsupported tags */ + if (unsuptag_file[0]) { + int tmpfd = rb->open(unsuptag_file, O_RDONLY); + if (tmpfd < 0) + return 10*tmpfd-3; + + file_copy_data(tmpfd, fd, rb->filesize(tmpfd), buf, sizeof(buf)); + rb->close(tmpfd); + } + + /* write zeros for remainder */ + rb->memset(buf, 0, sizeof(buf)); + dataleft = entry->id3v2len - rb->lseek(fd, 0, SEEK_CUR); + buflen = sizeof(buf); + while (dataleft > 0) { + int written; + copylen = (dataleft < buflen) ? dataleft : buflen; + + written = rb->write(fd, buf, copylen); + if (written != copylen) { + rb->close(fd); + return -4; + } + dataleft -= written; + } + + rb->close(fd); + + /* reread mp3info, to sync up all entry fields */ + if (rb->mp3info(entry, fname, false)) + return -5; + + return 0; +} + +/* + * Writes a text-type id3v2 frame at current file location + * + * Arguments: fd - file pointer at write location + * id - ID string of tag to write + * str - tag string to write + * + * Returns: true if no error, false otherwise + */ +static int write_id3v2_frame(int fd, const char *id, char *str) +{ + char hdr[11]; + int written = 0; + + rb->memset(hdr, 0, sizeof hdr); + rb->memcpy(hdr, id, 4); /* frame ID */ + INT2BYTES(rb->strlen(str) + 1, hdr[4], hdr[5], /* frame size */ + hdr[6], hdr[7]); + + hdr[8] = hdr[9] = 0; /* flags */ + hdr[10] = 0; /* text encoding */ + written += rb->write(fd, hdr, sizeof hdr); + written += rb->write(fd, str, rb->strlen(str)); + + return (written < (int)(sizeof(hdr) + rb->strlen(str))) ? false : true; +} + + +/* + * Calculate in-file size of id3v2 tag + * + * Arguments: entry - tag info + * unsuptag_file - tmpfile containing unsupported tags + * + * Returns: in-file tag size + */ +static int id3v2_tagsize(struct mp3entry *entry) +{ + int len=0; + unsigned int i; + + len += 10; /* tag hdr */ + + /* scan id3v2 tags */ + for (i=0; i< id3v2list_len; i++) { + struct tag_struct *tr = id3v2list[i]; + char* tag = *(char**)(((char*)entry) + tr->v2_offset); + + if ( (tag != NULL) && (rb->strlen(tag) > 0) ) + len += rb->strlen(tag)+11; + } + + /* add in length of unsupported tags */ + if (unsuptag_file[0]) { + int fd=rb->open(unsuptag_file, O_RDONLY); + len += rb->filesize(fd); + rb->close(fd); + } + + return len; +} + + + +/* + * Replaces an id3v1/2 tag in the struct, shifting other tags to make room + * + * Arguments : entry - id3 struct + * offset - offset of struct element to update + * str - new string value to assign to struct element + */ +static void replace_tag(struct mp3entry *entry, unsigned int offset, char *str) +{ + struct mp3entry tmpentry; + int buflen = sizeof(tmpentry.id3v2buf); + int bufpos = 0; + unsigned int i; + + rb->memset(tmpentry.id3v2buf, 0, buflen); + + /* scan id3 tags, copying over string to tmpentry */ + for (i=0; i< id3v2list_len; i++) { + struct tag_struct *tr = id3v2list[i]; + char** srctag = (char**) (((char*)entry) + tr->v2_offset); + char** tmptag = (char**) (((char*)&tmpentry) + tr->v2_offset); + char *s = NULL; + + /* if offset matches specified offset, copy specified str instead */ + if (tr->v2_offset == offset) + s = str; + else if (*srctag) + s = *srctag; + + if (s) { + rb->memcpy(tmpentry.id3v2buf+bufpos, s, buflen-bufpos-1); + *tmptag = tmpentry.id3v2buf + bufpos; + *srctag = entry->id3v2buf + bufpos; + bufpos += rb->strlen(tmpentry.id3v2buf+bufpos)+1; + } + else + *tmptag = *srctag = 0; + } + + /* now copy all tags back to original entry */ + rb->memcpy(entry->id3v2buf, tmpentry.id3v2buf, buflen); +} + + +/* + * Scans mp3 file, writing unsupported tags to a tmp file + * - yes, this duplicates much of mp3info(), but I didn't want to + * modify the behavior of mp3info just for this plugin. + * + * Arguments: mp3file - filename of input mp3 file + * tmpfile - tmp file to store unsupported tags + * + * Returns: 0 if no error + */ +static int save_unsup_tags(char *mp3file, char *tmpfile) +{ + int src_fd, tmp_fd; + int id3v2len, hdrlen; + char buf[10]; + int minframesize, size; + int totframelen, framelen; + int flags, skip; + unsigned int i; + unsigned char version; + unsigned char unsup_tag; + char copybuf[1024]; + + /* open MP3 file */ + src_fd = rb->open(mp3file, O_RDONLY); + if (-1 == src_fd) + return 10*src_fd - 1; + + /* open unsuptag file */ + tmp_fd = rb->creat(tmpfile, O_WRONLY); + if (tmp_fd < 0) { + rb->close(src_fd); + return 0; + } + + /* make sure file has an ID3 tag */ + if((-1 == rb->lseek(src_fd, 0, SEEK_SET)) || + (rb->read(src_fd, buf, 6) != 6) || + (rb->memcmp(buf, "ID3", rb->strlen("ID3")) != 0)) + id3v2len = 0; + else + if(rb->read(src_fd, buf, 4) != 4) + id3v2len = 0; + else + id3v2len = UNSYNC(buf[0], buf[1], buf[2], buf[3]) + 10; + + if ( (0 == id3v2len) || (id3v2len < 10) ) { + rb->close(src_fd); + return 0; + } + + /* Read the ID3 tag version from the header */ + rb->lseek(src_fd, 0, SEEK_SET); + if(10 != rb->read(src_fd, buf, 10)) { + rb->close(src_fd); + return 0; + } + + /* Get the total ID3 tag size */ + size = id3v2len - 10; + + version = buf[3]; + switch (version ) { + case 2: + minframesize = 8; + hdrlen = 6; + break; + + case 3: + minframesize = 12; + hdrlen = 10; + break; + + case 4: + minframesize = 12; + hdrlen = 10; + break; + + default: + /* unsupported id3 version */ + rb->close(src_fd); + return 0; + } + + /* Skip the extended header if it is present */ + if(version >= 4) { + if (buf[5] & 0x40) { + if(4 != rb->read(src_fd, buf, 4)) { + rb->close(src_fd); + return 0; + } + + framelen = UNSYNC(buf[0], buf[1], buf[2], buf[3]); + rb->lseek(src_fd, framelen - 4, SEEK_CUR); + } + } + + /* We must have at least minframesize bytes left for the + * remaining frames to be interesting + */ + while(size > minframesize ) { + flags = 0; + unsup_tag = 0; + + /* Read frame header and check length */ + if(version >= 3) { + if(hdrlen != rb->read(src_fd, buf, hdrlen)) { + rb->close(src_fd); rb->close(tmp_fd); + return 0; + } + /* Adjust for the 10 bytes we read */ + size -= hdrlen; + + flags = BYTES2INT(0, 0, buf[8], buf[9]); + + if (version >= 4) { + framelen = UNSYNC(buf[4], buf[5], buf[6], buf[7]); + } else { + /* version .3 files don't use synchsafe ints for size */ + framelen = BYTES2INT(buf[4], buf[5], buf[6], buf[7]); + } + } else { + if(hdrlen != rb->read(src_fd, buf, hdrlen)) { + rb->close(src_fd); rb->close(tmp_fd); + return 0; + } + /* Adjust for the 6 bytes we read */ + size -= hdrlen; + + framelen = BYTES2INT(0, buf[3], buf[4], buf[5]); + } + + /* Keep track of the total size */ + totframelen = framelen; + + if(framelen == 0) { + rb->close(src_fd); rb->close(tmp_fd); + return 0; + } + + if(flags) { + skip = 0; + + if(flags & 0x0040) /* Grouping identity */ + skip++; + + if(flags & 0x000c) /* Compression or encryption */ + unsup_tag = 1; + + if(flags & 0x0001) /* Data length indicator */ + skip += 4; + + if(skip) { + rb->lseek(src_fd, skip, SEEK_CUR); + framelen -= skip; + } + } + + /* check frame against known list */ + for (i=0; (imemcmp(buf, tr->tag, tr->tag_length ) ) + break; + } + + /* if unsupported tag, write out to tmpfile */ + if ( unsup_tag || (i == TAGLIST_SIZE) ) { + size -= totframelen; + rb->write(tmp_fd, buf, hdrlen); + file_copy_data(src_fd, tmp_fd, framelen, + copybuf, sizeof(copybuf)); + } + else + rb->lseek(src_fd, framelen, SEEK_CUR); + } + + return 0; +} /* end write_unsup_tag() */ + +/* + * Nondestructively inserts data in the middle of a given file + * (uses mpeg_buffer, so can't be playing!!) + * + * Arguments: fname - filename to insert data in + * fpos - position to insert data BEFORE + * src_buf - data to insert (if NULL, insert zeros) + * num_bytes - # of bytes of data + * + * Returns: 0 if no error + */ +static int file_insert_data(char *fname, unsigned int fpos, char *src_buf, + unsigned int num_bytes) +{ + int writelen; + int rc; + int fd, tmpfd; + char tmpname[MAX_PATH]; + unsigned char *copybuf, *mpegbuf; + int copybuf_len, mpegbuf_len; + + mpegbuf = (unsigned char *)rb->plugin_get_buffer(&mpegbuf_len); + + fd = rb->open(fname, O_RDONLY); + if (fd < 0) + return 10*fd - 2; + + rb->snprintf(tmpname, MAX_PATH, "%s.tmp", fname); + tmpfd = rb->creat(tmpname, O_WRONLY); + if (tmpfd < 0) + return 10*tmpfd - 3; + + /* First, copy the initial portion */ + if(fpos) { + rc = file_copy_data(fd, tmpfd, fpos, mpegbuf, mpegbuf_len); + if (rc < 0) + return 10*rc - 4; + } + + /* Now insert the data into the file */ + if (src_buf) { + copybuf = src_buf; + copybuf_len = num_bytes; + } + else { /* no data specified. fill w/ zeros */ + copybuf = mpegbuf; + copybuf_len = mpegbuf_len; + rb->memset(copybuf, 0, copybuf_len); + } + + writelen = num_bytes; + while (writelen > 0) { + rc = rb->write(tmpfd, copybuf, + (copybuf_len < writelen) ? copybuf_len : writelen); + if (rc < 0) { + rb->close(fd); + return 10*rc - 5; + } + writelen -= rc; + } + + /* Copy the rest of the file */ + rc = file_copy_data(fd, tmpfd, rb->filesize(fd)-fpos, mpegbuf, mpegbuf_len); + rb->close(fd); + rb->close(tmpfd); + + /* Remove the old file */ + rc = rb->remove(fname); + if(rc < 0) { + return 10*rc - 6; + } + + /* Replace the old file with the new */ + rc = rb->rename(tmpname, fname); + if(rc < 0) { + return 10*rc - 7; + } + + return 0; +} + + + +/* + * Copy bytes from one file to another (overwriting data or expanding file) + * + * Arguments: src_fd - file to copy data FROM + * dst_fd - file to copy data TO + * num_bytes - # of bytes of data to copy + * copybuf - buffer to use for copying data + * buflen - length of copy buffer + * + * Returns: 0 if no error + */ +static int file_copy_data(int src_fd, int dst_fd, unsigned int num_bytes, + char *copybuf, unsigned int buflen) +{ + int readlen, rc; + unsigned int nread=0; + bool doPrint = (num_bytes > 10000); + + do { + if (doPrint) + rb->splash(0,true,"Copying File: %d%%", nread*100/num_bytes); + readlen = (num_bytes < buflen) ? num_bytes : buflen; + readlen = rb->read(src_fd, copybuf, readlen); + if(readlen < 0) + return 10*readlen - 1; + + rc = rb->write(dst_fd, copybuf, readlen); + if(rc < 0) + return 10*rc - 2; + + nread += rc; + } while (nread < num_bytes); + + return 0; +} + +/* this is the plugin entry point */ +enum plugin_status plugin_start(struct plugin_api* api, void* parameter) +{ + char filename[MAX_PATH]; + bool editable=true; + struct mp3entry entry, *currPlaying; + unsigned int tagsize; + int rc; + + TEST_PLUGIN_API(api); + rb = api; + + /* grab mp3 filename */ + rb->memcpy(filename, (char*)parameter, sizeof(filename)); + + /* not allowed to edit currently-playing file */ + currPlaying = rb->audio_current_track(); + if ( (rb->audio_status() & AUDIO_STATUS_PLAY) && + !rb->memcmp(filename, currPlaying->path, rb->strlen(filename)) ) + editable=false; + + /* only edit tags in mp3 files */ + if (rb->mp3info(&entry, filename, false)) { + rb->splash(HZ*2, true, "Not an MP3 file! (%s)", filename); + return PLUGIN_OK; + } + + /* copy off unsupported tags to a temporary file */ + if (editable) { + rb->snprintf(unsuptag_file, sizeof(unsuptag_file), "%s.tag", filename); + rc = save_unsup_tags(filename, unsuptag_file); + if (rc<0) { + rb->remove(unsuptag_file); + return PLUGIN_ERROR; + } + } + + /* browse/edit tags */ + rc = edit_id3_tags(&entry, editable); + if (rc) { + rb->remove(unsuptag_file); + return PLUGIN_OK; + } + + /* check if file needs to be resized (prompt if necessary) */ + tagsize = id3v2_tagsize(&entry); + if (tagsize > entry.id3v2len) { + bool do_resize; + rb->set_bool("Resize File for ID3?", &do_resize); + + if (do_resize==false) { + rb->remove(unsuptag_file); + return PLUGIN_OK; + } + else if ( rb->audio_status() & AUDIO_STATUS_PLAY ) { + rb->splash(HZ*2, true, "Can't resize while playing!"); + rb->remove(unsuptag_file); + return PLUGIN_ERROR; + } + } + + rc = write_id3_tags(&entry); + if (rc<0) { + rb->splash(HZ*2, true, + "Error during write_id3(%d). File may be corrupt", rc); + rb->remove(unsuptag_file); + return PLUGIN_ERROR; + } + + rb->remove(unsuptag_file); + if (editable) rb->splash(HZ*2, true, "Edit Completed"); + + return PLUGIN_OK; +} --- orig/apps/plugins/metronome.c 2005-09-02 11:36:25.819149500 +0200 +++ dev/apps/plugins/metronome.c 2005-09-02 11:36:25.787899500 +0200 @@ -148,7 +148,7 @@ void callback(unsigned char** start, int* size){ (void)start; /* unused parameter, avoid warning */ - *size = NULL; /* end of data */ + *size = 0; /* end of data */ sound_active = false; led(0); } --- orig/apps/plugins/viewers.config 2005-09-02 11:36:25.912899500 +0200 +++ dev/apps/plugins/viewers.config 2005-09-02 11:36:25.881649500 +0200 @@ -4,7 +4,7 @@ jpg,jpeg.rock,18 24 3C 3C 24 18 ucl,rockbox_flash.rock,2A 7F 41 41 7F 2A rvf,video.rock,5D 7F 5D 7F 5D 7F -mp3,vbrfix.rock,10 08 58 38 04 02 +mp3,vbrfix.rock,00 00 00 00 00 00 m3u,search.rock,00 00 00 00 00 00 txt,sort.rock, 00 00 00 00 00 00 gb,rockboy.rock, 0C 2A 59 7A 2E 0C @@ -21,4 +21,5 @@ mid,midi2wav.rock, 20 70 70 3F 00 00 rsp,searchengine.rock, 0e 11 11 31 7e 60 wav,wav2wv.rock, 00 00 00 00 00 00 +mp3,edit_id3.rock, 00 00 00 00 00 00 --- orig/apps/screens.c 2005-09-02 11:36:26.006649500 +0200 +++ dev/apps/screens.c 2005-09-02 11:33:00.381649500 +0200 @@ -1307,187 +1307,6 @@ return line + 2; } -#if CONFIG_CODEC == SWCODEC -#define ID3_ITEMS 13 -#else -#define ID3_ITEMS 11 -#endif - -bool browse_id3(void) -{ - char buf[64]; - const struct mp3entry* id3 = audio_current_track(); -#if defined(HAVE_LCD_BITMAP) - const int y_margin = lcd_getymargin(); - const int line_height = font_get(FONT_UI)->height; - const int rows = (LCD_HEIGHT - y_margin) / line_height; - const bool show_scrollbar = global_settings.scrollbar - && (ID3_ITEMS * 2 > rows); -#else - const int rows = 2; -#endif - const int top_max = (ID3_ITEMS * 2) - (rows & ~1); - int top = 0; - int button; - bool exit = false; - - if (!id3 || (!(audio_status() & AUDIO_STATUS_PLAY))) - { - return false; - } - -#if defined(HAVE_LCD_BITMAP) - lcd_setmargins(show_scrollbar ? SCROLLBAR_WIDTH : 0, y_margin); -#endif - - while (!exit) - { - int line = 0; - int old_top = top; - char* body; - - lcd_clear_display(); - status_draw(true); - line = draw_id3_item(line, top, LANG_ID3_TITLE, id3->title); - line = draw_id3_item(line, top, LANG_ID3_ARTIST, id3->artist); - line = draw_id3_item(line, top, LANG_ID3_ALBUM, id3->album); - - if (id3->track_string) - { - body = id3->track_string; - } - else if (id3->tracknum) - { - snprintf(buf, sizeof(buf), "%d", id3->tracknum); - body = buf; - } - else - { - body = NULL; - } - - line = draw_id3_item(line, top, LANG_ID3_TRACKNUM, body); - - body = id3->genre_string ? id3->genre_string : id3_get_genre(id3); - line = draw_id3_item(line, top, LANG_ID3_GENRE, body); - - if (id3->year_string) - { - body = id3->year_string; - } - else if (id3->year) - { - snprintf(buf, sizeof(buf), "%d", id3->year); - body = buf; - } - else - { - body = NULL; - } - - line = draw_id3_item(line, top, LANG_ID3_YEAR, body); - - wps_format_time(buf, sizeof(buf), id3->length); - line = draw_id3_item(line, top, LANG_ID3_LENGHT, buf); - - snprintf(buf, sizeof(buf), "%d/%d", playlist_get_display_index(), - playlist_amount()); - line = draw_id3_item(line, top, LANG_ID3_PLAYLIST, buf); - - snprintf(buf, sizeof(buf), "%d kbps%s", id3->bitrate, - id3->vbr ? str(LANG_ID3_VBR) : (const unsigned char*) ""); - line = draw_id3_item(line, top, LANG_ID3_BITRATE, buf); - - snprintf(buf, sizeof(buf), "%d Hz", id3->frequency); - line = draw_id3_item(line, top, LANG_ID3_FRECUENCY, buf); - -#if CONFIG_CODEC == SWCODEC - line = draw_id3_item(line, top, LANG_ID3_TRACK_GAIN, - id3->track_gain_string); - - line = draw_id3_item(line, top, LANG_ID3_ALBUM_GAIN, - id3->album_gain_string); -#endif - - line = draw_id3_item(line, top, LANG_ID3_PATH, id3->path); - -#if defined(HAVE_LCD_BITMAP) - if (show_scrollbar) - { - scrollbar(0, y_margin, SCROLLBAR_WIDTH - 1, rows * line_height, - ID3_ITEMS * 2 + (rows & 1), top, top + rows, VERTICAL); - } -#endif - - while (!exit && (top == old_top)) - { - status_draw(false); - lcd_update(); - button = button_get_w_tmo(HZ / 2); - - switch(button) - { - /* It makes more sense to have the keys mapped "backwards" when - * scrolling a list. - */ -#if defined(HAVE_LCD_BITMAP) - case SETTINGS_INC: - case SETTINGS_INC | BUTTON_REPEAT: -#else - case SETTINGS_DEC: -#endif - if (top > 0) - { - top -= 2; - } - else if (!(button & BUTTON_REPEAT)) - { - top = top_max; - } - - break; - -#if defined(HAVE_LCD_BITMAP) - case SETTINGS_DEC: - case SETTINGS_DEC | BUTTON_REPEAT: -#else - case SETTINGS_INC: -#endif - if (top < top_max) - { - top += 2; - } - else if (!(button & BUTTON_REPEAT)) - { - top = 0; - } - - break; - -#ifdef SETTINGS_OK2 - case SETTINGS_OK2: -#endif - case SETTINGS_CANCEL: - lcd_stop_scroll(); - /* Eat release event */ - button_get(true); - exit = true; - break; - - default: - if (default_event_handler(button) == SYS_USB_CONNECTED) - { - return true; - } - - break; - } - } - } - - return false; -} - bool set_rating(void) { struct mp3entry* id3 = audio_current_track(); --- orig/apps/screens.h 2005-09-02 11:36:26.116024500 +0200 +++ dev/apps/screens.h 2005-09-02 11:36:26.084774500 +0200 @@ -54,7 +54,6 @@ #endif bool shutdown_screen(void); -bool browse_id3(void); bool set_rating(void); #endif --- orig/apps/tree.c 2005-09-02 11:36:26.209774500 +0200 +++ dev/apps/tree.c 2005-09-02 11:36:26.178524500 +0200 @@ -1635,3 +1635,23 @@ } } +/* gets currently selected file for external (non-tree.c) code to access */ +/* appends "/" to directories, so can distinguish dirs from file */ +char* get_current_file(void) +{ + static char fullname[MAX_PATH]; + struct entry *dircache = tc.dircache; + char* file = dircache[tc.dircursor+tc.dirstart].name; + struct entry* f = &dircache[tc.dirstart + tc.dircursor]; + bool isdir = f->attr & ATTR_DIRECTORY; + + if ((tc.currdir[0]=='/') && (tc.currdir[1]==0)) + snprintf(fullname, sizeof fullname, "%s%s", tc.currdir, file); + else + snprintf(fullname, sizeof fullname, "%s/%s", tc.currdir, file); + + if (isdir) + strcat(fullname, "/"); + + return fullname; +} --- orig/apps/tree.h 2005-09-02 11:36:26.319149500 +0200 +++ dev/apps/tree.h 2005-09-02 11:36:26.287899500 +0200 @@ -193,5 +193,6 @@ void reload_directory(void); bool check_rockboxdir(void); struct tree_context* tree_get_context(void); +char* get_current_file(void); #endif --- orig/apps/wps-display.c 2005-09-02 11:36:26.428524500 +0200 +++ dev/apps/wps-display.c 2005-09-02 11:36:26.397274500 +0200 @@ -515,8 +515,8 @@ if (id3->track_string) return id3->track_string; - if (id3->tracknum) { - snprintf(buf, buf_size, "%d", id3->tracknum); + if (id3->v1_tracknum) { + snprintf(buf, buf_size, "%d", id3->v1_tracknum); return buf; } return NULL; @@ -531,14 +531,17 @@ if( id3->year_string ) return id3->year_string; - if (id3->year) { - snprintf(buf, buf_size, "%d", id3->year); + if (id3->v1_year) { + snprintf(buf, buf_size, "%d", id3->v1_year); return buf; } return NULL; case 'g': /* genre */ - return id3_get_genre(id3); + if( id3->genre_string ) + return id3->genre_string ; + return id3_get_genre(id3->v1_genre); + case 'v': /* id3 version */ switch (id3->id3version) { --- orig/apps/wps.c 2005-09-02 11:36:26.537899500 +0200 +++ dev/apps/wps.c 2005-09-02 11:36:26.506649500 +0200 @@ -742,14 +742,6 @@ exit = true; break; #endif - -#ifdef WPS_ID3 - case WPS_ID3: - browse_id3(); - restore = true; - break; -#endif - case BUTTON_NONE: /* Timeout */ update_track = true; break; --- orig/apps/wps.h 2005-09-02 11:36:26.631649500 +0200 +++ dev/apps/wps.h 2005-09-02 11:36:26.616024500 +0200 @@ -39,7 +39,6 @@ #define WPS_BROWSE (BUTTON_SELECT | BUTTON_REL) #define WPS_BROWSE_PRE BUTTON_SELECT #define WPS_EXIT BUTTON_OFF -#define WPS_ID3 (BUTTON_MODE | BUTTON_ON) #define WPS_CONTEXT (BUTTON_SELECT | BUTTON_REPEAT) #define WPS_QUICK (BUTTON_MODE | BUTTON_REPEAT) @@ -75,7 +74,6 @@ #define WPS_BROWSE_PRE BUTTON_ON #define WPS_EXIT BUTTON_OFF #define WPS_KEYLOCK (BUTTON_F1 | BUTTON_DOWN) -#define WPS_ID3 (BUTTON_F1 | BUTTON_ON) #define WPS_CONTEXT (BUTTON_PLAY | BUTTON_REPEAT) #define WPS_QUICK BUTTON_F2 @@ -109,7 +107,6 @@ #define WPS_BROWSE_PRE BUTTON_ON #define WPS_EXIT BUTTON_STOP #define WPS_KEYLOCK (BUTTON_MENU | BUTTON_STOP) -#define WPS_ID3 (BUTTON_MENU | BUTTON_ON) #define WPS_CONTEXT (BUTTON_PLAY | BUTTON_REPEAT) #ifdef AB_REPEAT_ENABLE @@ -158,7 +155,6 @@ #define WPS_BROWSE_PRE BUTTON_ON #define WPS_EXIT BUTTON_OFF #define WPS_KEYLOCK (BUTTON_MENU | BUTTON_DOWN) -#define WPS_ID3 (BUTTON_MENU | BUTTON_ON) #endif --- orig/firmware/export/id3.h 2005-09-02 11:36:26.741024500 +0200 +++ dev/firmware/export/id3.h 2005-09-02 11:36:26.709774500 +0200 @@ -88,7 +88,14 @@ /* these following two fields are used for local buffering */ char id3v2buf[300]; - char id3v1buf[3][32]; + + /* id3v1 fields */ + unsigned int v1_genre; + unsigned int v1_tracknum; + unsigned int v1_year; + char v1_title[30]; + char v1_artist[30]; + char v1_album[30]; /* resume related */ int offset; /* bytes played */ @@ -128,7 +135,7 @@ }; bool mp3info(struct mp3entry *entry, const char *filename, bool v1first); -char* id3_get_genre(const struct mp3entry* id3); +char* id3_get_genre(unsigned int genre); char* id3_get_codec(const struct mp3entry* id3); #endif --- orig/firmware/id3.c 2005-09-02 11:36:26.850399500 +0200 +++ dev/firmware/id3.c 2005-09-02 11:36:26.819149500 +0200 @@ -96,13 +96,10 @@ #endif }; -char* id3_get_genre(const struct mp3entry* id3) +char* id3_get_genre(unsigned int genre) { - if( id3->genre_string ) - return id3->genre_string ; - - if (id3->genre < sizeof(genres)/sizeof(char*)) - return (char*)genres[id3->genre]; + if (genre < sizeof(genres)/sizeof(char*)) + return (char*)genres[genre]; return NULL; } @@ -259,14 +256,14 @@ /* parse numeric value from string */ static int parsetracknum( struct mp3entry* entry, char* tag, int bufferpos ) { - entry->tracknum = atoi( tag ); + entry->v1_tracknum = atoi( tag ); return bufferpos; } /* parse numeric value from string */ static int parseyearnum( struct mp3entry* entry, char* tag, int bufferpos ) { - entry->year = atoi( tag ); + entry->v1_year = atoi( tag ); return bufferpos; } @@ -279,23 +276,23 @@ /* Is it a number? */ if(isdigit(tag[0])) { - entry->genre = atoi( tag ); + entry->v1_genre = atoi( tag ); entry->genre_string = 0; return tag - entry->id3v2buf; } else { entry->genre_string = tag; - entry->genre = 0xff; + entry->v1_genre = 0xff; return bufferpos; } } else { - if( tag[0] == '(' && tag[1] != '(' ) { - entry->genre = atoi( tag + 1 ); + if( tag[0] == '(' && isdigit(tag[1]) ) { + entry->v1_genre = atoi( tag + 1 ); entry->genre_string = 0; return tag - entry->id3v2buf; } else { entry->genre_string = tag; - entry->genre = 0xff; + entry->v1_genre = 0xff; return bufferpos; } } @@ -484,37 +481,37 @@ switch(i) { case 0: - strncpy(entry->id3v1buf[2], ptr, 30); - entry->title = entry->id3v1buf[2]; + strncpy(entry->v1_title, ptr, 30); + entry->title = entry->v1_title; break; case 1: - strncpy(entry->id3v1buf[0], ptr, 30); - entry->artist = entry->id3v1buf[0]; + strncpy(entry->v1_artist, ptr, 30); + entry->artist = entry->v1_artist; break; case 2: - strncpy(entry->id3v1buf[1], ptr, 30); - entry->album = entry->id3v1buf[1]; + strncpy(entry->v1_album, ptr, 30); + entry->album = entry->v1_album; break; case 3: ptr[4] = 0; - entry->year = atoi(ptr); + entry->v1_year = atoi(ptr); break; case 4: /* id3v1.1 uses last two bytes of comment field for track number: first must be 0 and second is track num */ if (!ptr[0] && ptr[1]) { - entry->tracknum = ptr[1]; + entry->v1_tracknum = ptr[1]; entry->id3version = ID3_VER_1_1; } break; case 5: /* genre */ - entry->genre = ptr[0]; + entry->v1_genre = ptr[0]; break; } } @@ -587,8 +584,8 @@ return; } entry->id3version = version; - entry->tracknum = entry->year = 0; - entry->genre = 0xff; + entry->v1_tracknum = entry->v1_year = 0; + entry->v1_genre = 0xff; entry->title = entry->artist = entry->album = NULL; global_flags = header[5]; @@ -952,8 +949,8 @@ entry->title = NULL; entry->filesize = filesize(fd); entry->id3v2len = getid3v2len(fd); - entry->tracknum = 0; - entry->genre = 0xff; + entry->v1_tracknum = 0; + entry->v1_genre = 0xff; if(v1first) v1found = setid3v1title(fd, entry);