Index: apps/plugins/m3u_cleaner.c =================================================================== --- apps/plugins/m3u_cleaner.c (revision 0) +++ apps/plugins/m3u_cleaner.c (revision 0) @@ -0,0 +1,210 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: m3u_cleaner.c 12807 2007-11-11 $ + * + * 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" +PLUGIN_HEADER + +static const struct plugin_api* rb; +MEM_FUNCTION_WRAPPERS(rb); + +#define MAX_OUT_ENTRIES 100 +struct{ + int num; + char* entries[MAX_OUT_ENTRIES]; +} out; + +/* Code copied from firmware_flash plugin. */ + +/* Tool function to calculate a CRC32 across some buffer */ +/* third argument is either 0xFFFFFFFF to start or value from last piece */ +unsigned crc_32(const void *src, unsigned len, unsigned crc32) +{ + const unsigned char *buf = (const unsigned char *)src; + + /* CCITT standard polynomial 0x04C11DB7 */ + static const unsigned crc32_lookup[16] = + { /* lookup table for 4 bits at a time is affordable */ + 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, + 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005, + 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61, + 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD + }; + + unsigned char byte; + unsigned t; + + while (len--) + { + byte = *buf++; /* get one byte of data */ + + /* upper nibble of our data */ + t = crc32 >> 28; /* extract the 4 most significant bits */ + t ^= byte >> 4; /* XOR in 4 bits of data into the extracted bits */ + crc32 <<= 4; /* shift the CRC register left 4 bits */ + crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */ + + /* lower nibble of our data */ + t = crc32 >> 28; /* extract the 4 most significant bits */ + t ^= byte & 0x0F; /* XOR in 4 bits of data into the extracted bits */ + crc32 <<= 4; /* shift the CRC register left 4 bits */ + crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */ + } + + return crc32; +} + +bool is_duplicate(const char* entry, int len, bool* reset_list){ + #define MAX_PLAYLIST_ENTRIES 500 + static unsigned entries_crc[MAX_PLAYLIST_ENTRIES]; + static int num_entries=0; + int i=0; + unsigned entry_crc=crc_32(entry, len, 0xffffffff); + if(*reset_list){ + num_entries=0; + *reset_list=false; + } + while(i add to list */ + entries_crc[num_entries]=entry_crc; + num_entries=(num_entries+1)%MAX_PLAYLIST_ENTRIES; + return false; + } + return true; +} + +void output_entry(const char* const output, const char* const param, int line){ + char* string = out.entries[out.num++]; + if(out.num >= MAX_OUT_ENTRIES){ + out.num = 1; + string = out.entries[out.num++]; + rb->splash(HZ,"Number of output entries exceeds buffer!"); + } + rb->snprintf(string,MAX_PATH,output,param); + out.entries[out.num] = string + rb->strlen(string) + 1; + + int i; + FOR_NB_SCREENS(i){ + rb->screens[i]->puts(0,line,string); + rb->screens[i]->update(); + } +} + +void clean_playlist(const unsigned char* playlist_name){ + output_entry("Cleaning '%s' ...",playlist_name, 0); + size_t size; + int entries_size=0; + bool have_bad_entries=false; + char* entry=rb->plugin_get_buffer(&size); + char* first=entry; + char* str; + int fd=rb->open(playlist_name, O_RDONLY); + bool reset_list=true; + if(fd>=0){ + /* check playlist entries */ + while(rb->read_line(fd, entry, MAX_PATH)>0){ + if(entry[1]==':') /* skip drive letter */ + rb->strcpy(entry,entry+2); + str=entry; + while (*str!=0){ /* replace \ with / */ + if ( *str == '\\' ) *str = '/'; + str++; + } + if(rb->file_exists(entry)){ /* entry o.k. */ + if(!is_duplicate(entry, str-entry, &reset_list)){ + /* cannot use length returned by read_line, because of \r\n */ + entries_size+=str-entry+1; + entry=str+1; /* next entry points to the end of the current entry */ + *str='\n'; /* replace 0 with \n */ + } + else{ + output_entry("-> Duplicate: '%s'",entry,1); + have_bad_entries=true; + } + } + else { + output_entry("-> Invalid: '%s'",entry,1); + have_bad_entries=true; + } + if(rb->button_get(false)!=BUTTON_NONE){ /* press any button to abort */ + have_bad_entries=false; + break; + } + } + rb->close(fd); + if(have_bad_entries){ /* only modify file if it has invalid entries */ + if(entries_size > 0){ + rb->open(playlist_name, O_WRONLY|O_CREAT|O_TRUNC); + rb->write(fd,first,entries_size); + rb->close(fd); + } + else{ + rb->remove(playlist_name); + output_entry("-> Removed: '%s'",playlist_name,1); + } + } + else{ + out.num--; /* overwrite string "Cleaning ..." */ + output_entry("ok: '%s'",playlist_name,1); + } + } +} +void clean_playlist_dir(const unsigned char* playlist_dir){ + struct dirent *entry; + DIR* dir; + unsigned char* ext; + char fullpath[MAX_PATH]; + dir = rb->opendir(playlist_dir); + if (dir) { + while((entry=rb->readdir(dir))!=0 && /* looking for playlists in dir */ + rb->button_get(false)==BUTTON_NONE){ /* press any button to abort */ + ext=rb->strrchr(entry->d_name,'.')+1; + if(rb->strncasecmp(ext,"m3u",3)==0){ /* check if it's a playlist */ + rb->snprintf(fullpath,MAX_PATH,"%s/%s",playlist_dir,entry->d_name); + clean_playlist(fullpath); + } + } + rb->closedir(dir); + } + else + rb->splash(HZ,"Directory '%s' not found!",playlist_dir); +} + +/* this is the plugin entry point */ +enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter) +{ + static char output_buffer[PLUGIN_BUFFER_SIZE>>2]; + /* init */ + rb = api; + out.entries[0] = output_buffer; + out.num=0; + + /* cleaning */ + parameter==NULL ? + clean_playlist_dir(rb->global_settings->playlist_catalog_dir): + clean_playlist((const unsigned char*) parameter); + + /* report status */ + struct menu_callback_with_desc entry_desc = + {NULL,"m3u Cleaning Report", Icon_NOICON}; + struct menu_item_ex entry_list = { + MT_RETURN_ID|MENU_HAS_DESC|MENU_ITEM_COUNT(out.num), + {.strings = (const char **) out.entries}, + {.callback_and_desc = &entry_desc} + }; + rb->do_menu(&entry_list, NULL,NULL,false); + return PLUGIN_OK; +} Property changes on: apps/plugins/m3u_cleaner.c ___________________________________________________________________ Name: svn:executable + * Index: apps/plugins/SOURCES =================================================================== --- apps/plugins/SOURCES (revision 17565) +++ apps/plugins/SOURCES (working copy) @@ -19,6 +19,7 @@ stopwatch.c vbrfix.c viewer.c +m3u_cleaner.c #ifdef HAVE_BACKLIGHT lamp.c #endif /* HAVE_BACKLIGHT */ Index: apps/plugins/CATEGORIES =================================================================== --- apps/plugins/CATEGORIES (revision 17576) +++ apps/plugins/CATEGORIES (working copy) @@ -99,3 +99,4 @@ xobox,games zxbox,viewers lamp,apps +m3u_cleaner,apps Index: apps/plugins/viewers.config =================================================================== --- apps/plugins/viewers.config (revision 17576) +++ apps/plugins/viewers.config (working copy) @@ -41,3 +41,5 @@ ssg,games/superdom,- link,viewers/shortcuts_view,- *,viewers/shortcuts_append,- +m3u,apps/m3u_cleaner,1 +m3u8,apps/m3u_cleaner,1