Index: apps/plugins/viewers.config
===================================================================
--- apps/plugins/viewers.config	(Revision 12180)
+++ apps/plugins/viewers.config	(Arbeitskopie)
@@ -2,6 +2,7 @@
 txt,viewers/viewer,55 55 55 55 55 55
 nfo,viewers/viewer,55 55 55 55 55 55
 txt,rocks/text_editor, 55 55 55 55 55 55
+idx,viewers/stardict, 55 55 55 55 55 55
 jpg,viewers/jpeg,18 24 3C 3C 24 18
 jpe,viewers/jpeg,18 24 3C 3C 24 18
 jpeg,viewers/jpeg,18 24 3C 3C 24 18
Index: apps/plugins/stardict.c
===================================================================
--- apps/plugins/stardict.c	(Revision 0)
+++ apps/plugins/stardict.c	(Revision 0)
@@ -0,0 +1,619 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id: dict.c,v 1.15 2006-10-26 13:37:58 barrywardell Exp $
+ *
+ * Copyright (C) 2005 Tomas Salfischberger, 2006 Timo Horstschäfer
+ *
+ * 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
+
+/* save the plugin api pointer. */
+static struct plugin_api* rb;
+/* screen info */
+static int display_columns, display_lines;
+
+/* Some lenghts */
+#define WORDLEN 256 /* length of word_str in stardict's DICT format */
+#define FILENAME_LEN FAT_FILENAME_BYTES
+#define MAX_FILESIZE 2147483647
+
+#define MAX_FIND 255 /* max articles to be found */
+
+#define LINES_SCROLL display_lines /* how many lines to scroll */
+
+struct WordData
+{
+    uint32_t offset;
+    uint32_t size;
+};
+
+#ifdef ROCKBOX_BIG_ENDIAN
+#define ntohl(A)  (A)
+#define htohn(A)  (A)
+#else
+#define ntohl(A)  ((((uint32_t)(A) & 0xff000000) >> 24) | \
+                   (((uint32_t)(A) & 0x00ff0000) >> 8)  | \
+                   (((uint32_t)(A) & 0x0000ff00) << 8)  | \
+                   (((uint32_t)(A) & 0x000000ff) << 24))
+#define htonl   ntohl
+#endif
+
+/* A funtion to get width and height etc (from viewer.c) */
+void init_screen(void)
+{
+#ifdef HAVE_LCD_BITMAP
+    int w,h;
+
+    rb->lcd_getstringsize("o", &w, &h);
+    display_lines = LCD_HEIGHT / h;
+    display_columns = LCD_WIDTH / w;
+#else
+
+    display_lines = 2;
+    display_columns = 11;
+#endif
+}
+
+/* global vars for pl_malloc() */
+void *bufptr;
+int bufleft;
+
+/* simple function to "allocate" memory in pluginbuffer. */
+void *pl_malloc(int size)
+{
+    void *ptr;
+    ptr = bufptr;
+
+    if (bufleft < size)
+    {
+        return NULL;
+    }
+    else
+    {
+        bufptr += size;
+        return ptr;
+    }
+}
+
+/* init function for pl_malloc() */
+void pl_malloc_init(void)
+{
+    bufptr = rb->plugin_get_buffer(&bufleft);
+}
+
+size_t read_word(int fd, char *buf, size_t count)
+{
+    size_t i;
+    for (i=0; i<count; i++)
+    {
+        if (!rb->read(fd, buf, 1) || ! *buf++)
+            break;
+    }
+    return i;
+}
+
+/* returns a pointer to the result name followed by a WordData struct */
+char *select_result(int fIndex, const char *searchword)
+{
+    struct menu_item items[MAX_FIND];
+    char word[WORDLEN];
+    char *ptr;
+    int statusbar_setting, searchword_len, len;
+    int n; /* menu size */
+    int m;
+
+    searchword_len = rb->strlen(searchword);
+
+    for (n = 0; n < MAX_FIND; n++)
+    {
+        len = read_word(fIndex, word, WORDLEN) + 1;
+        DEBUGF("art: %s\n", word);
+
+        /* find articles starting with... */
+        if (rb->strncasecmp(searchword, word, searchword_len) != 0)
+            break;
+
+        ptr = (char *)pl_malloc(len + sizeof(struct WordData) );
+        if (ptr == NULL)
+            break;
+
+        rb->strcpy(ptr, word);
+        rb->read(fIndex, ptr+len, sizeof(struct WordData));
+
+        items[n].desc = ptr;
+        items[n].function = NULL;
+    }
+
+    if (n == 0)
+        return NULL;
+
+    if (n == 1)
+        return items[0].desc;
+
+    /* disable status bar */
+    statusbar_setting = rb->global_settings->statusbar;
+    rb->global_settings->statusbar = false;
+
+    m = rb->menu_init(items, n, NULL, NULL, NULL, NULL);
+    n = rb->menu_show(m);
+    rb->menu_exit(m);
+
+    /* restore status bar */
+    rb->global_settings->statusbar = statusbar_setting;
+
+#ifdef HAVE_LCD_BITMAP
+        rb->lcd_setmargins(0, 0);
+#endif
+
+    if (n < 0)
+        return NULL;
+
+    return items[n].desc;
+}
+
+/* Button definitions */
+#if CONFIG_KEYPAD == PLAYER_PAD
+#define LP_QUIT BUTTON_STOP
+#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
+      (CONFIG_KEYPAD == IPOD_3G_PAD)
+#define LP_QUIT BUTTON_MENU
+#define LP_UP BUTTON_LEFT
+#define LP_DOWN BUTTON_RIGHT
+
+#elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
+#define LP_QUIT BUTTON_PLAY
+#define LP_UP BUTTON_UP
+#define LP_DOWN BUTTON_DOWN
+
+#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
+      (CONFIG_KEYPAD == IPOD_3G_PAD)
+#define LP_QUIT BUTTON_OFF
+#define LP_UP BUTTON_SCROLL_BACK
+#define LP_DOWN BUTTON_SCROLL_FWD
+
+#elif CONFIG_KEYPAD == IAUDIO_X5_PAD
+#define LP_QUIT BUTTON_POWER
+#define LP_UP   BUTTON_UP
+#define LP_DOWN BUTTON_DOWN
+
+#elif CONFIG_KEYPAD == GIGABEAT_PAD
+#define LP_QUIT BUTTON_A
+#define LP_UP   BUTTON_UP
+#define LP_DOWN BUTTON_DOWN
+
+#elif CONFIG_KEYPAD == SANSA_E200_PAD
+#define LP_QUIT BUTTON_POWER
+#define LP_UP   BUTTON_UP
+#define LP_DOWN BUTTON_DOWN
+
+#elif CONFIG_KEYPAD == IRIVER_H10_PAD
+#define LP_QUIT BUTTON_POWER
+#define LP_UP BUTTON_SCROLL_UP
+#define LP_DOWN BUTTON_SCROLL_DOWN
+
+#else
+#define LP_QUIT BUTTON_OFF
+#define LP_UP   BUTTON_UP
+#define LP_DOWN BUTTON_DOWN
+#endif
+
+/* the main plugin function */
+enum plugin_status plugin_start(struct plugin_api* api, void* file)
+{
+    char name[255], dir[255]; /* name of the dictionary (filename without extension) */
+    char filename[FILENAME_LEN]; /* temporary filename */
+    char searchword[WORDLEN], word[WORDLEN]; /* word to search for */
+    char *result, *description; /* pointer to description buffer */
+    char *output; /* pointer to output buffer */
+    char *ptr, *space, *lineptr;
+    struct WordData *word_data; /* the struct to read into */
+    int fIndex, fData, fCache; /* files */
+    int filesize, high, low, probe;
+    int lines, line, len;
+    int column;
+    bool quit, update;
+    uint32_t offset;
+
+    /* plugin stuff */
+    rb = api;
+
+    /* get screen info */
+    init_screen();
+
+    /* get pl_malloc() buffer ready. */
+    pl_malloc_init();
+
+    /* don't let the disk spin down */
+    rb->ata_spindown(254);
+
+    /* init output buffer */
+    output = (char *)pl_malloc(display_columns*2); /* leave enough space for unicode chars */
+    if (output == NULL)
+    {
+        DEBUGF("Err: failed to allocate output buffer.");
+        return PLUGIN_ERROR;
+    }
+
+    /* extract dir name */
+    len = rb->strrchr(file, '/') - (char*) file + 1;
+    rb->strncpy(dir, file, len);
+    dir[len] = '\0';
+    
+    /* extract name */
+    file = rb->strrchr(file, '/') + 1;
+    len = rb->strrchr(file, '.') - (char*) file;
+    rb->strncpy(name, file, len);
+    name[len] = '\0';
+
+    /* "clear" input buffer */
+    searchword[0] = '\0';
+
+    rb->kbd_input(searchword, sizeof(searchword)); /* get the word to search */
+
+    if (rb->strlen(searchword) < 1)
+    {
+        rb->ata_spindown(rb->global_settings->disk_spindown);
+        return PLUGIN_OK;
+    }
+
+    rb->snprintf(filename, FILENAME_LEN, "%s/%s.idx", dir, name);
+    fIndex = rb->open(filename, O_RDONLY); /* index file */
+    if (fIndex < 0)
+    {
+        DEBUGF("Err: Failed to open index file.");
+        rb->splash(HZ*2, true, "Failed to open index.");
+        rb->ata_spindown(rb->global_settings->disk_spindown);
+        return PLUGIN_ERROR;
+    }
+
+    /* try to open the cache file */
+    rb->snprintf(filename, FILENAME_LEN, "%s/%s.oft", ROCKBOX_DIR, name);
+    fCache = rb->open(filename, O_RDONLY);
+    if (fCache < 0)
+    {
+        /* create cache */
+        uint32_t offset;
+        int size;
+        char c;
+        fCache = rb->creat(filename);
+        if (fCache < 0)
+        {
+            DEBUGF("Err: Failed to create cache file.\n");
+            rb->splash(HZ*2, true, "Failed to create cache.");
+            rb->ata_spindown(rb->global_settings->disk_spindown);
+            return PLUGIN_ERROR;
+        }
+
+        rb->splash(0, true, "Creating cache file...");
+
+        /* now create the cache */
+        offset = 0;
+        while (true)
+        {
+            /* get name length */
+            size = 0;
+            while (true)
+            {
+                if (rb->read(fIndex, &c, 1) < 1)
+                    break;
+                size++;
+                if (c == '\0')
+                    break;
+            }
+            if (size < 1)
+                break;
+
+            /* write current position to file */
+            rb->write(fCache, &offset, sizeof(offset));
+
+            rb->lseek(fIndex, sizeof(struct WordData), SEEK_CUR);
+            offset += sizeof(struct WordData) + size;
+
+            rb->yield();
+        }
+        rb->close(fCache);
+
+        /* reopen the cache file for normal use */
+        fCache = rb->open(filename, O_RDONLY);
+        if (fCache < 0)
+        {
+            DEBUGF("Err: Failed to open cache file.\n");
+            rb->splash(HZ*2, true, "Failed to open cache.");
+            rb->ata_spindown(rb->global_settings->disk_spindown);
+            return PLUGIN_ERROR;
+        }
+
+    }
+
+    filesize = rb->filesize(fCache); /* get filesize */
+
+    DEBUGF("Filesize: %d bytes = %d words \n", filesize,
+           (filesize / sizeof( uint32_t )));
+
+    /* for the searching algorithm */
+    high = filesize / sizeof( uint32_t );
+    low = -1;
+
+    while (high - low > 1)
+    {
+        probe = (high + low) / 2;
+
+        /* read offset */
+        rb->lseek(fCache, sizeof(uint32_t) * probe, SEEK_SET);
+        rb->read(fCache, &offset, sizeof(uint32_t));
+
+        /* Jump to word pointed by probe offset, and read it. */
+        rb->lseek(fIndex, offset, SEEK_SET);
+        read_word(fIndex, word, WORDLEN);
+
+        /* jump according to the found word. */
+        if (rb->strcasecmp(searchword, word) < 0)
+        {
+            high = probe;
+        }
+        else
+        {
+            low = probe;
+        }
+    }
+
+    /* find all previous words with the same strcasecmp size */
+    while (true)
+    {
+        /* get word offset */
+        rb->lseek(fCache, sizeof(uint32_t) * low, SEEK_SET);
+        rb->read(fCache, &offset, sizeof(uint32_t));
+
+        /* get word */
+        rb->lseek(fIndex, offset, SEEK_SET);
+        read_word(fIndex, word, WORDLEN);
+
+        if (rb->strcasecmp(searchword, word) != 0)
+        {
+            rb->lseek(fIndex, sizeof(struct WordData), SEEK_CUR);
+            break;
+        }
+        else if (low <= 1)
+        {
+            rb->lseek(fIndex, 0, SEEK_SET);
+            break;
+        }
+
+        low--;
+    }
+
+    /* find articles starting with searchword and show the selection screen */
+    result = select_result(fIndex, searchword);
+
+    /* Check if we found something */
+    if (result != NULL)
+    {
+        word_data = (struct WordData*) (result + rb->strlen(result) + 1);
+
+        /* convert to host endianess */
+        word_data->offset = ntohl(word_data->offset);
+        word_data->size = ntohl(word_data->size);
+
+        DEBUGF("Found %s at offset %d with size %d\n",
+               result, word_data->offset, word_data->size);
+    }
+    else
+    {
+        DEBUGF("Not found.\n");
+        rb->splash(HZ*2, true, "Not found.");
+
+        rb->close(fIndex);
+        rb->close(fCache);
+        rb->ata_spindown(rb->global_settings->disk_spindown);
+        return PLUGIN_OK;
+    }
+
+    /* init description buffer*/
+    description = (char *)pl_malloc(word_data->size);
+    if (description == NULL)
+    {
+        rb->splash(HZ, true, "Not enough memory left.");
+        DEBUGF("Err: failed to allocate description buffer.");
+        rb->ata_spindown(rb->global_settings->disk_spindown);
+        return PLUGIN_ERROR;
+    }
+
+    /* now open the description file */
+    if (word_data->offset <= MAX_FILESIZE)
+    {
+        rb->snprintf(filename, FILENAME_LEN, "%s/%s.dict", dir, name);
+        offset = word_data->offset;
+    }
+    else
+    {
+        rb->snprintf(filename, FILENAME_LEN, "%s/%s.dict.1", dir, name);
+        offset = word_data->offset - MAX_FILESIZE;
+    }    
+    fData = rb->open(filename, O_RDONLY); /* description file */
+    if (fData < 0)
+    {
+        DEBUGF("Err: Failed to open description file.\n");
+        rb->splash(HZ*2, true, "Failed to open descriptions.");
+        rb->close(fIndex);
+        rb->close(fCache);
+        rb->ata_spindown(rb->global_settings->disk_spindown);
+        return PLUGIN_ERROR;
+    }
+
+    /* seek to the right offset */
+    rb->lseek(fData, offset, SEEK_SET);
+
+    /* Read in the description */
+    len = rb->read(fData, description, word_data->size);
+    /* see if article is splitted between two files */
+    if ((unsigned) len < word_data->size && word_data->size <= MAX_FILESIZE)
+    {
+        rb->snprintf(filename, FILENAME_LEN, "%s/%s.dict.1", dir, name);
+        /* close the first file and open the second */
+        rb->close(fData);
+        fData = rb->open(filename, O_RDONLY); /* description file */
+        if (fData < 0)
+        {
+            DEBUGF("Err: Failed to open description file.\n");
+            rb->splash(HZ*2, true, "Failed to open descriptions.");
+            rb->close(fIndex);
+            rb->close(fCache);
+            rb->ata_spindown(rb->global_settings->disk_spindown);
+            return PLUGIN_ERROR;
+        }
+    }
+    rb->read(fData, description+len, word_data->size-len);
+    description[word_data->size] = '\0';
+
+    /* And print it to debug. */
+    DEBUGF("Description: %s\n", description);
+
+    /* now spin down the HDD */
+    rb->ata_spindown(rb->global_settings->disk_spindown);
+
+
+    /* get pointer to first char */
+    column = 0;
+    ptr = description;
+    space = NULL;
+
+    /* format description to fit on display */
+    while (*ptr)
+    {
+        /* remember last space */
+        if (*ptr == ' ')
+            space = ptr;
+        if (*ptr == '\n')
+            column = 0;
+        else
+        {
+            column++;
+            /* break lines */
+            if (column > display_columns && space != NULL)
+            {
+                ptr = space;
+                *ptr = '\n';
+                space = NULL;
+                column = 0;
+            }
+        }
+        ptr += rb->utf8seek(ptr, 1);
+    }
+
+    lineptr = description;
+    line = 0;
+    quit = false;
+    update = true;
+
+    while (!quit)
+    {
+        if (update)
+        {
+            /* clear screen */
+            rb->lcd_clear_display();
+            
+            ptr = lineptr;
+            lines = 0;
+
+            while (lines < display_lines)
+            {
+                /* set space to end of the lcd line */
+                space = rb->strchr(ptr, '\n');
+                if (space == NULL)
+                {
+                    rb->lcd_puts(0, lines, ptr);
+                    break;
+                }
+                len = (space-ptr>display_columns*2-1) ? display_columns*2-1 : space-ptr;
+
+                rb->strncpy(output, ptr, len);
+                output[len] = '\0';
+                DEBUGF("%s\n", output);
+
+                rb->lcd_puts(0, lines, output);
+                lines++;
+
+                ptr = space+1;
+            }
+
+#ifdef HAVE_LCD_BITMAP
+            rb->lcd_update();
+#endif
+            update = false;
+        }
+
+        switch (rb->button_get(true))
+        {
+            case LP_QUIT:
+                quit = true;
+                break;
+            case LP_DOWN: case LP_DOWN|BUTTON_REPEAT:
+                /* check if there are enough lines left to scroll */
+                lines = 0;
+                ptr = lineptr;
+                while ((ptr = rb->strchr(ptr, '\n')) != NULL)
+                {
+                    lines++;
+                    /* must be increased because it points to the last '\n' */
+                    ptr++;
+                }
+                /* last line is null-terminated and wasn't not recognized */
+                lines++;
+
+                /* set lineptr to start of the next line */
+                if (lines >= display_lines)
+                {
+                    int scroll;
+                    if (lines-display_lines<LINES_SCROLL)
+                        scroll = lines-display_lines;
+                    else
+                        scroll = LINES_SCROLL;
+
+                    for (lines=0; lines<scroll; lines++)
+                        lineptr = rb->strchr(lineptr, '\n') + 1;
+                    line += scroll;
+                    update = true;
+                }
+                break;
+            case LP_UP: case LP_UP|BUTTON_REPEAT:
+                ptr = lineptr;
+                lines = -1;
+                while (lines < LINES_SCROLL && ptr > description)
+                {
+                    if (*--ptr == '\n')
+                        lines++;
+                }
+                if (ptr == description)
+                    lines++;
+                else
+                    ptr++;
+
+                line -= lines;
+                lineptr = ptr;
+                update = true;
+            default:
+                rb->yield();
+                break;
+        }
+    }
+
+
+    rb->close(fIndex);
+    rb->close(fCache);
+    rb->close(fData);
+    return PLUGIN_OK;
+}
Index: apps/plugins/SOURCES
===================================================================
--- apps/plugins/SOURCES	(Revision 12180)
+++ apps/plugins/SOURCES	(Arbeitskopie)
@@ -4,6 +4,7 @@
 credits.c
 cube.c
 dict.c
+stardict.c
 firmware_flash.c
 logo.c
 mosaique.c
