From b0fee17d6e1a463dcd84568e5997663b69488998 Mon Sep 17 00:00:00 2001 From: Marcoen Hirschberg Date: Tue, 6 Dec 2005 13:27:15 +0000 Subject: waiting is over: initial unicode commit git-svn-id: svn://svn.rockbox.org/rockbox/trunk@8169 a1c6a512-1295-4272-9138-f99709370657 --- firmware/SOURCES | 3 + firmware/arabjoin.h | 205 +++++++++++++++++++ firmware/bidi.c | 186 +++++++++++++----- firmware/common/unicode.c | 307 +++++++++++++++++++++++++++++ firmware/drivers/fat.c | 165 +++------------- firmware/drivers/lcd-16bit.c | 26 +-- firmware/drivers/lcd-h100-remote.c | 26 +-- firmware/drivers/lcd-h100.c | 18 +- firmware/drivers/lcd-player.c | 40 +++- firmware/drivers/lcd-recorder.c | 18 +- firmware/export/bidi.h | 4 +- firmware/export/font.h | 13 +- firmware/export/id3.h | 2 +- firmware/font.c | 392 ++++++++++++++++++++++++++++++++----- firmware/font_cache.c | 224 +++++++++++++++++++++ firmware/id3.c | 211 ++++++++++---------- firmware/include/font_cache.h | 48 +++++ firmware/include/lru.h | 46 +++++ firmware/include/rbunicode.h | 29 +++ firmware/lru.c | 119 +++++++++++ firmware/mpeg.c | 9 + firmware/powermgmt.c | 8 +- 22 files changed, 1706 insertions(+), 393 deletions(-) create mode 100644 firmware/arabjoin.h create mode 100644 firmware/common/unicode.c create mode 100644 firmware/font_cache.c create mode 100644 firmware/include/font_cache.h create mode 100644 firmware/include/lru.h create mode 100644 firmware/include/rbunicode.h create mode 100644 firmware/lru.c (limited to 'firmware') diff --git a/firmware/SOURCES b/firmware/SOURCES index e2e0e44e5d..5a1c9adb7a 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -1,3 +1,4 @@ +common/unicode.c #ifdef ROCKBOX_HAS_LOGF logf.c #endif @@ -103,6 +104,8 @@ drivers/rtc.c drivers/serial.c #endif /* !SIMULATOR */ #ifdef HAVE_LCD_BITMAP +lru.c +font_cache.c font.c #endif id3.c diff --git a/firmware/arabjoin.h b/firmware/arabjoin.h new file mode 100644 index 0000000000..cba498660c --- /dev/null +++ b/firmware/arabjoin.h @@ -0,0 +1,205 @@ +typedef struct { + unsigned short isolated; + unsigned short final; + unsigned short medial; + unsigned short initial; +} arab_t; + +//static arab_t dummy = {0,0,0,0}; +static arab_t zwj = {0x20,0x20,0x20,0x20}; //zero-width joiner + +static arab_t lamaleph[] = { + {0xfefb,0xfefc,0,0}, + {0xfef9,0xfefa,0,0}, + {0xfef7,0xfef8,0,0}, + {0xfef5,0xfef6,0,0} +}; + +static arab_t jointable[] = { +{0xfe80,0x0000,0x0000,0x0000}, // 0x0621 +{0xfe81,0xfe82,0x0000,0x0000}, // 0x0622 +{0xfe83,0xfe84,0x0000,0x0000}, // 0x0623 +{0xfe85,0xfe86,0x0000,0x0000}, // 0x0624 +{0xfe87,0xfe88,0x0000,0x0000}, // 0x0625 +{0xfe89,0xfe8a,0xfe8c,0xfe8b}, // 0x0626 +{0xfe8d,0xfe8e,0x0000,0x0000}, // 0x0627 +{0xfe8f,0xfe90,0xfe92,0xfe91}, // 0x0628 +{0xfe93,0xfe94,0x0000,0x0000}, // 0x0629 +{0xfe95,0xfe96,0xfe98,0xfe97}, // 0x062a +{0xfe99,0xfe9a,0xfe9c,0xfe9b}, // 0x062b +{0xfe9d,0xfe9e,0xfea0,0xfe9f}, // 0x062c +{0xfea1,0xfea2,0xfea4,0xfea3}, // 0x062d +{0xfea5,0xfea6,0xfea8,0xfea7}, // 0x062e +{0xfea9,0xfeaa,0x0000,0x0000}, // 0x062f +{0xfeab,0xfeac,0x0000,0x0000}, // 0x0630 +{0xfead,0xfeae,0x0000,0x0000}, // 0x0631 +{0xfeaf,0xfeb0,0x0000,0x0000}, // 0x0632 +{0xfeb1,0xfeb2,0xfeb4,0xfeb3}, // 0x0633 +{0xfeb5,0xfeb6,0xfeb8,0xfeb7}, // 0x0634 +{0xfeb9,0xfeba,0xfebc,0xfebb}, // 0x0635 +{0xfebd,0xfebe,0xfec0,0xfebf}, // 0x0636 +{0xfec1,0xfec2,0xfec4,0xfec3}, // 0x0637 +{0xfec5,0xfec6,0xfec8,0xfec7}, // 0x0638 +{0xfec9,0xfeca,0xfecc,0xfecb}, // 0x0639 +{0xfecd,0xfece,0xfed0,0xfecf}, // 0x063a +#if 0 +{0x0000,0x0000,0x0000,0x0000}, // 0x063b +{0x0000,0x0000,0x0000,0x0000}, // 0x063c +{0x0000,0x0000,0x0000,0x0000}, // 0x063d +{0x0000,0x0000,0x0000,0x0000}, // 0x063e +{0x0000,0x0000,0x0000,0x0000}, // 0x063f +#endif +{0x0640,0x0640,0x0640,0x0640}, // 0x0640 +{0xfed1,0xfed2,0xfed4,0xfed3}, // 0x0641 +{0xfed5,0xfed6,0xfed8,0xfed7}, // 0x0642 +{0xfed9,0xfeda,0xfedc,0xfedb}, // 0x0643 +{0xfedd,0xfede,0xfee0,0xfedf}, // 0x0644 +{0xfee1,0xfee2,0xfee4,0xfee3}, // 0x0645 +{0xfee5,0xfee6,0xfee8,0xfee7}, // 0x0646 +{0xfee9,0xfeea,0xfeec,0xfeeb}, // 0x0647 +{0xfeed,0xfeee,0x0000,0x0000}, // 0x0648 +{0xfeef,0xfef0,0x0000,0x0000}, // 0x0649 +{0xfef1,0xfef2,0xfef4,0xfef3}, // 0x064a +# if 0 +{0x0000,0x0000,0x0000,0x0000}, // 0x064b +{0x0000,0x0000,0x0000,0x0000}, // 0x064c +{0x0000,0x0000,0x0000,0x0000}, // 0x064d +{0x0000,0x0000,0x0000,0x0000}, // 0x064e +{0x0000,0x0000,0x0000,0x0000}, // 0x064f +{0x0000,0x0000,0x0000,0x0000}, // 0x0650 +{0x0000,0x0000,0x0000,0x0000}, // 0x0651 +{0x0000,0x0000,0x0000,0x0000}, // 0x0652 +{0x0000,0x0000,0x0000,0x0000}, // 0x0653 +{0x0000,0x0000,0x0000,0x0000}, // 0x0654 +{0x0000,0x0000,0x0000,0x0000}, // 0x0655 +{0x0000,0x0000,0x0000,0x0000}, // 0x0656 +{0x0000,0x0000,0x0000,0x0000}, // 0x0657 +{0x0000,0x0000,0x0000,0x0000}, // 0x0658 +{0x0000,0x0000,0x0000,0x0000}, // 0x0659 +{0x0000,0x0000,0x0000,0x0000}, // 0x065a +{0x0000,0x0000,0x0000,0x0000}, // 0x065b +{0x0000,0x0000,0x0000,0x0000}, // 0x065c +{0x0000,0x0000,0x0000,0x0000}, // 0x065d +{0x0000,0x0000,0x0000,0x0000}, // 0x065e +{0x0000,0x0000,0x0000,0x0000}, // 0x065f +{0x0000,0x0000,0x0000,0x0000}, // 0x0660 +{0x0000,0x0000,0x0000,0x0000}, // 0x0661 +{0x0000,0x0000,0x0000,0x0000}, // 0x0662 +{0x0000,0x0000,0x0000,0x0000}, // 0x0663 +{0x0000,0x0000,0x0000,0x0000}, // 0x0664 +{0x0000,0x0000,0x0000,0x0000}, // 0x0665 +{0x0000,0x0000,0x0000,0x0000}, // 0x0666 +{0x0000,0x0000,0x0000,0x0000}, // 0x0667 +{0x0000,0x0000,0x0000,0x0000}, // 0x0668 +{0x0000,0x0000,0x0000,0x0000}, // 0x0669 +{0x0000,0x0000,0x0000,0x0000}, // 0x066a +{0x0000,0x0000,0x0000,0x0000}, // 0x066b +{0x0000,0x0000,0x0000,0x0000}, // 0x066c +{0x0000,0x0000,0x0000,0x0000}, // 0x066d +{0x0000,0x0000,0x0000,0x0000}, // 0x066e +{0x0000,0x0000,0x0000,0x0000}, // 0x066f +{0x0000,0x0000,0x0000,0x0000}, // 0x0670 +#endif +{0xfb50,0x0000,0x0000,0x0000}, // 0x0671 +{0x0672,0x0672,0x0000,0x0000}, // 0x0672 +{0x0673,0x0673,0x0000,0x0000}, // 0x0673 +{0x0674,0x0000,0x0000,0x0000}, // 0x0674 +{0x0675,0x0675,0x0000,0x0000}, // 0x0675 +{0x0676,0x0676,0x0000,0x0000}, // 0x0676 +{0xfbdd,0x0677,0x0000,0x0000}, // 0x0677 +{0x0678,0x0678,0x0678,0x0678}, // 0x0678 +{0xfb66,0xfb67,0xfb69,0xfb68}, // 0x0679 +{0xfb5e,0xfb5f,0xfb61,0xfb60}, // 0x067a +{0xfb52,0xfb53,0xfb55,0xfb54}, // 0x067b +{0x067c,0x067c,0x067c,0x067c}, // 0x067c +{0x067d,0x067d,0x067d,0x067d}, // 0x067d +{0xfb56,0xfb57,0xfb59,0xfb58}, // 0x067e +{0xfb62,0xfb63,0xfb65,0xfb64}, // 0x067f +{0xfb5a,0xfb5b,0xfb5d,0xfb5c}, // 0x0680 +{0x0681,0x0681,0x0681,0x0681}, // 0x0681 +{0x0682,0x0682,0x0682,0x0682}, // 0x0682 +{0xfb76,0xfb77,0xfb79,0xfb78}, // 0x0683 +{0xfb72,0xfb73,0xfb75,0xfb74}, // 0x0684 +{0x0685,0x0685,0x0685,0x0685}, // 0x0685 +{0xfb7a,0xfb7b,0xfb7d,0xfb7c}, // 0x0686 +{0xfb7e,0xfb7f,0xfb81,0xfb80}, // 0x0687 +{0xfb88,0xfb89,0x0000,0x0000}, // 0x0688 +{0x0689,0x0689,0x0000,0x0000}, // 0x0689 +{0x068a,0x068a,0x0000,0x0000}, // 0x068a +{0x068b,0x068b,0x0000,0x0000}, // 0x068b +{0xfb84,0xfb85,0x0000,0x0000}, // 0x068c +{0xfb82,0xfb83,0x0000,0x0000}, // 0x068d +{0xfb86,0xfb87,0x0000,0x0000}, // 0x068e +{0x068f,0x068f,0x0000,0x0000}, // 0x068f +{0x0690,0x0690,0x0000,0x0000}, // 0x0690 +{0xfb8c,0xfb8d,0x0000,0x0000}, // 0x0691 +{0x0692,0x0692,0x0000,0x0000}, // 0x0692 +{0x0693,0x0693,0x0000,0x0000}, // 0x0693 +{0x0694,0x0694,0x0000,0x0000}, // 0x0694 +{0x0695,0x0695,0x0000,0x0000}, // 0x0695 +{0x0695,0x0696,0x0000,0x0000}, // 0x0696 +{0x0697,0x0697,0x0000,0x0000}, // 0x0697 +{0xfb8a,0xfb8b,0x0000,0x0000}, // 0x0698 +{0x0699,0x0699,0x0000,0x0000}, // 0x0699 +{0x069a,0x069a,0x069a,0x069a}, // 0x069a +{0x069b,0x069b,0x069b,0x069b}, // 0x069b +{0x069c,0x069c,0x069c,0x069c}, // 0x069c +{0x069d,0x069d,0x069d,0x069d}, // 0x069d +{0x069e,0x069e,0x069e,0x069e}, // 0x069e +{0x069f,0x069f,0x069f,0x069f}, // 0x069f +{0x06a0,0x06a0,0x06a0,0x06a0}, // 0x06a0 +{0x06a1,0x06a1,0x06a1,0x06a1}, // 0x06a1 +{0x06a2,0x06a2,0x06a2,0x06a2}, // 0x06a2 +{0x06a3,0x06a3,0x06a3,0x06a3}, // 0x06a3 +{0xfb6a,0xfb6b,0xfb6d,0xfb6c}, // 0x06a4 +{0x06a5,0x06a5,0x06a5,0x06a5}, // 0x06a5 +{0xfb6e,0xfb6f,0xfb71,0xfb70}, // 0x06a6 +{0x06a7,0x06a7,0x06a7,0x06a7}, // 0x06a7 +{0x06a8,0x06a8,0x06a8,0x06a8}, // 0x06a8 +{0xfb8e,0xfb8f,0xfb91,0xfb90}, // 0x06a9 +{0x06aa,0x06aa,0x06aa,0x06aa}, // 0x06aa +{0x06ab,0x06ab,0x06ab,0x06ab}, // 0x06ab +{0x06ac,0x06ac,0x06ac,0x06ac}, // 0x06ac +{0xfbd3,0xfbd4,0xfbd6,0xfbd5}, // 0x06ad +{0x06ae,0x06ae,0x06ae,0x06ae}, // 0x06ae +{0xfb92,0xfb93,0xfb95,0xfb94}, // 0x06af +{0x06b0,0x06b0,0x06b0,0x06b0}, // 0x06b0 +{0xfb9a,0xfb9b,0xfb9d,0xfb9c}, // 0x06b1 +{0x06b2,0x06b2,0x06b2,0x06b2}, // 0x06b2 +{0xfb96,0xfb97,0xfb99,0xfb98}, // 0x06b3 +{0x06b4,0x06b4,0x06b4,0x06b4}, // 0x06b4 +{0x06b5,0x06b5,0x06b5,0x06b5}, // 0x06b5 +{0x06b6,0x06b6,0x06b6,0x06b6}, // 0x06b6 +{0x06b7,0x06b7,0x06b7,0x06b7}, // 0x06b7 +{0x0000,0x0000,0x0000,0x0000}, // 0x06b8 +{0x0000,0x0000,0x0000,0x0000}, // 0x06b9 +{0xfb9e,0xfb9f,0x06ba,0x06ba}, // 0x06ba +{0xfba0,0xfba1,0xfba3,0xfba2}, // 0x06bb +{0x06bc,0x06bc,0x06bc,0x06bc}, // 0x06bc +{0x06bd,0x06bd,0x06bd,0x06bd}, // 0x06bd +{0xfbaa,0xfbab,0xfbad,0xfbac}, // 0x06be +{0x0000,0x0000,0x0000,0x0000}, // 0x06bf +{0xfba4,0xfba5,0x0000,0x0000}, // 0x06c0 +{0x06c1,0x06c1,0x06c1,0x06c1}, // 0x06c1 +{0x06c2,0x06c2,0x0000,0x0000}, // 0x06c2 +{0x06c3,0x06c3,0x0000,0x0000}, // 0x06c3 +{0x06c4,0x06c4,0x0000,0x0000}, // 0x06c4 +{0x06c5,0x06c5,0x0000,0x0000}, // 0x06c5 +{0x06c6,0x06c6,0x0000,0x0000}, // 0x06c6 +{0x06c7,0x06c7,0x0000,0x0000}, // 0x06c7 +{0x06c8,0x06c8,0x0000,0x0000}, // 0x06c8 +{0x06c9,0x06c9,0x0000,0x0000}, // 0x06c9 +{0x06ca,0x06ca,0x0000,0x0000}, // 0x06ca +{0x06cb,0x06cb,0x0000,0x0000}, // 0x06cb +{0x06cc,0x06cc,0x06cc,0x06cc}, // 0x06cc +{0x06cd,0x06cd,0x0000,0x0000}, // 0x06cd +{0x06ce,0x06ce,0x06ce,0x06ce}, // 0x06ce +{0x0000,0x0000,0x0000,0x0000}, // 0x06cf +{0x06d0,0x06d0,0x06d0,0x06d0}, // 0x06d0 +{0x06d1,0x06d1,0x06d1,0x06d1}, // 0x06d1 +{0xfbae,0xfbaf,0x0000,0x0000}, // 0x06d2 +{0xfbb0,0xfbb1,0x0000,0x0000}, // 0x06d3 +{0x0000,0x0000,0x0000,0x0000}, // 0x06d4 +{0x06d5,0x0000,0x0000,0x0000} // 0x06d5 +}; + diff --git a/firmware/bidi.c b/firmware/bidi.c index dcb44999ec..93c1dd0951 100644 --- a/firmware/bidi.c +++ b/firmware/bidi.c @@ -24,75 +24,168 @@ #include #include "file.h" #include "lcd.h" +#include "rbunicode.h" +#include "arabjoin.h" -#define _HEB_BUFFER_LENGTH MAX_PATH + LCD_WIDTH/2 + 3 + 2 + 2 +//#define _HEB_BUFFER_LENGTH (MAX_PATH + LCD_WIDTH/2 + 3 + 2 + 2) * 2 #define _HEB_BLOCK_TYPE_ENG 1 #define _HEB_BLOCK_TYPE_HEB 0 #define _HEB_ORIENTATION_LTR 1 #define _HEB_ORIENTATION_RTL 0 -#define ischar(c) (((((unsigned char) c)>=193) && (((unsigned char) c)<=250)) ? 1 : 0) -#define _isblank(c) (((((unsigned char) c)==' ' || ((unsigned char) c)=='\t')) ? 1 : 0) -#define _isnewline(c) (((((unsigned char) c)=='\n' || ((unsigned char) c)=='\r')) ? 1 : 0) +#define ischar(c) ((c > 0x0589 && c < 0x0700) || (c >= 0xfb50 && c <= 0xfefc) ? 1 : 0) +#define _isblank(c) ((c==' ' || c=='\t') ? 1 : 0) +#define _isnewline(c) ((c=='\n' || c=='\r') ? 1 : 0) #define XOR(a,b) ((a||b) && !(a&&b)) -bool bidi_support_enabled = false; +arab_t * arab_lookup(unsigned short uchar) +{ + if (uchar >= 0x621 && uchar <= 0x63a) + return &(jointable[uchar - 0x621]); + if (uchar >= 0x640 && uchar <= 0x64a) + return &(jointable[uchar - 0x621 - 5]); + if (uchar >= 0x671 && uchar <= 0x6d5) + return &(jointable[uchar - 0x621 - 5 - 38]); + if (uchar == 0x200D) /* Support for the zero-width joiner */ + return ‍ + return 0; +} + +void arabjoin(unsigned short * stringprt, int length){ + + bool connected = false; + unsigned short * writeprt = stringprt; + + arab_t * prev = 0; + arab_t * cur; + arab_t * ligature = 0; + short uchar; -unsigned char *bidi_l2v(const char *str, int orientation) + int i; + for (i = 0; i <= length; i++) { + cur = arab_lookup(uchar = *stringprt++); + + /* Skip non-arabic chars */ + if (cur == 0) { + if (prev) { + /* Finish the last char */ + if (connected) { + *writeprt++ = prev->final; + connected = false; + } else + *writeprt++ = prev->isolated; + prev = 0; + *writeprt++ = uchar; + } else { + *writeprt++ = uchar; + } + continue; + } + + /* nothing to do for arabic char if the previous was non-arabic */ + if (prev == 0) { + prev = cur; + continue; + } + + /* if it's LAM, check for LAM+ALEPH ligatures */ + if (prev->isolated == 0xfedd) { + switch (cur->isolated) { + case 0xfe8d: + ligature = &(lamaleph[0]); + break; + case 0xfe87: + ligature = &(lamaleph[1]); + break; + case 0xfe83: + ligature = &(lamaleph[2]); + break; + case 0xfe81: + ligature = &(lamaleph[3]); + } + } + + if (ligature) { /* replace the 2 glyphs by their ligature */ + prev = ligature; + ligature = 0; + } else { + if (connected) { /* previous char has something connected to it */ + if (prev->medial && cur->final) /* Can we connect to it? */ + *writeprt++ = prev->medial; + else { + *writeprt++ = prev->final; + connected = false; + } + } else { + if (prev->initial && cur->final) { /* Can we connect to it? */ + *writeprt++ = prev->initial; + connected = true; + } else + *writeprt++ = prev->isolated; + } + prev = cur; + } + } +} + +unsigned short *bidi_l2v(const unsigned char *str, int orientation) { - static unsigned char buf_heb_str[_HEB_BUFFER_LENGTH]; - static unsigned char buf_broken_str[_HEB_BUFFER_LENGTH]; - const unsigned char *tmp; - unsigned char *heb_str, *target, *opposite_target, *broken_str; + int length = utf8length(str); + static unsigned short utf16_buf[MAX_PATH+1]; + static unsigned short bidi_buf[MAX_PATH+1]; + unsigned short *heb_str, *target, *tmp; // *broken_str int block_start, block_end, block_type, block_length, i; - int block_ended; - long max_chars=0; - int begin, end, char_count, orig_begin; + //long max_chars=0; + //int begin, end, char_count, orig_begin; if (!str || !*str) - return (unsigned char *)""; + return &(unsigned short){0}; + + //tmp = str; + target = tmp = utf16_buf; + while (*str) + str = utf8decode(str, target++); + *target = 0; - tmp = (unsigned char *)str; - block_start=block_end=0; - block_ended=0; + /* properly join any arabic chars */ + arabjoin(utf16_buf, length); - heb_str = buf_heb_str; + block_start=block_end=block_length=0; + + heb_str = bidi_buf; if (orientation) { target = heb_str; - opposite_target = heb_str + strlen(str); } else { - target = heb_str + strlen(str); - opposite_target = heb_str; + target = heb_str + length; *target = 0; target--; } - - block_length=0; + if (ischar(*tmp)) block_type = _HEB_BLOCK_TYPE_HEB; else block_type = _HEB_BLOCK_TYPE_ENG; - + do { - while((XOR(ischar((int)*(tmp+1)),block_type) - || _isblank((int)*(tmp+1)) || ispunct((int)*(tmp+1)) - || (int)*(tmp+1)=='\n') - && block_end<(int)strlen(str)-1) { - tmp++; - block_end++; - block_length++; + while((XOR(ischar(*(tmp+1)),block_type) + || _isblank(*(tmp+1)) || ispunct((int)*(tmp+1)) + || *(tmp+1)=='\n') + && block_end < length-1) { + tmp++; + block_end++; + block_length++; } - + if (block_type != orientation) { - while ((_isblank((int)*tmp) || ispunct((int)*tmp)) + while ((_isblank(*tmp) || ispunct((int)*tmp)) && *tmp!='/' && *tmp!='-' && block_end>block_start) { tmp--; block_end--; } } - + for (i=block_start; i<=block_end; i++) { - *target = (block_type == orientation) ? *(str+i) : *(str+block_end-i+block_start); + *target = (block_type == orientation) ? *(utf16_buf+i) : *(utf16_buf+block_end-i+block_start); if (block_type!=orientation) { switch (*target) { case '(': @@ -109,12 +202,15 @@ unsigned char *bidi_l2v(const char *str, int orientation) } block_type = !block_type; block_start=block_end+1; - } while(block_end<(int)strlen(str)-1); - - broken_str = buf_broken_str; - begin=end=strlen(str)-1; + } while(block_end0) { @@ -147,9 +243,9 @@ unsigned char *bidi_l2v(const char *str, int orientation) } orig_begin=begin; - if (_isblank(heb_str[begin])) { + /* if (_isblank(heb_str[begin])) { heb_str[begin]='\n'; - } + } */ /* skip leading newlines */ while (begin<=end && _isnewline(heb_str[begin])) { @@ -176,9 +272,7 @@ unsigned char *bidi_l2v(const char *str, int orientation) end=begin; } return broken_str; +#endif + return heb_str; } -void set_bidi_support(bool setting) -{ - bidi_support_enabled = setting; -} diff --git a/firmware/common/unicode.c b/firmware/common/unicode.c new file mode 100644 index 0000000000..a82327e1b1 --- /dev/null +++ b/firmware/common/unicode.c @@ -0,0 +1,307 @@ +/* Some conversion functions for handling UTF-8 + * + * copyright Marcoen Hirschberg (2004,2005) + * + * I got all the info from: + * http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + * and + * http://en.wikipedia.org/wiki/Unicode + */ + +#include +#include "file.h" +#include "debug.h" +#include "rbunicode.h" + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#define NUM_TABLES 5 +#define NUM_CODEPAGES 13 + +static int default_codepage = 0; +static unsigned short codepage_table[MAX_CP_TABLE_SIZE]; +static int loaded_cp_table = 0; + + +static const unsigned char utf8comp[6] = +{ + 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC +}; + +static const char *filename[NUM_TABLES] = +{ + CODEPAGE_DIR"/iso.cp", + CODEPAGE_DIR"/932.cp", /* SJIS */ + CODEPAGE_DIR"/936.cp", /* GB2312 */ + CODEPAGE_DIR"/949.cp", /* KSX1001 */ + CODEPAGE_DIR"/950.cp" /* BIG5 */ +}; + +static const char cp_2_table[NUM_CODEPAGES] = +{ + 0, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5 +}; + +/* Load codepage file into memory */ +int load_cp_table(int cp) +{ + int i=0; + int table = cp_2_table[cp]; + int file, tablesize; + unsigned char tmp[2]; + + if (cp == 0 || table == loaded_cp_table) + return 1; + + file = open(filename[table-1], O_RDONLY|O_BINARY); + + if (file < 0) { + DEBUGF("Can't open codepage file: %s.cp\n", filename[table-1]); + return 0; + } + + tablesize = lseek(file, 0, SEEK_END) / 2; + lseek(file, 0, SEEK_SET); + + if (tablesize > MAX_CP_TABLE_SIZE) { + DEBUGF("Invalid codepage file: %s.cp\n", filename[table-1]); + close(file); + return 0; + } + + while (i < tablesize) { + if (!read(file, tmp, 2)) { + DEBUGF("Can't read from codepage file: %s.cp\n", filename[table-1]); + loaded_cp_table = 0; + return 0; + } + codepage_table[i++] = (tmp[1] << 8) | tmp[0]; + } + + loaded_cp_table = table; + close(file); + return 1; +} + +/* Encode a UCS value as UTF-8 and return a pointer after this UTF-8 char. */ +unsigned char* utf8encode(unsigned long ucs, unsigned char *utf8) +{ + int tail = 0; + + if (ucs > 0x7F) + while (ucs >> (6*tail + 2)) + tail++; + + *utf8++ = (ucs >> (6*tail)) | utf8comp[tail]; + while (tail--) + *utf8++ = ((ucs >> (6*tail)) & (MASK ^ 0xFF)) | COMP; + + return utf8; +} + +/* Recode an iso encoded string to UTF-8 */ +unsigned char* iso_decode(const unsigned char *iso, unsigned char *utf8, + int cp, int count) +{ + unsigned short ucs, tmp; + + if (cp == -1) /* use default codepage */ + cp = default_codepage; + + if (!load_cp_table(cp)) cp = 0; + + while (count--) { + if (*iso < 128) + *utf8++ = *iso++; + + else { + + /* cp tells us which codepage to convert from */ + switch (cp) { + case 0x01: /* Greek (ISO-8859-7) */ + case 0x02: /* Hebrew (ISO-8859-8) */ + case 0x03: /* Russian (CP1251) */ + case 0x04: /* Thai (ISO-8859-11) */ + case 0x05: /* Arabic (ISO-8859-6) */ + case 0x06: /* Turkish (ISO-8859-9) */ + case 0x07: /* Latin Extended (ISO-8859-2) */ + tmp = ((cp-1)*128) + (*iso++ - 128); + ucs = codepage_table[tmp]; + break; + + case 0x08: /* Japanese (SJIS) */ + if (*iso > 0xA0 && *iso < 0xE0) { + tmp = *iso | 0xA100; + ucs = codepage_table[tmp]; + break; + } + + case 0x09: /* Simplified Chinese (GB2312) */ + case 0x0A: /* Korean (KSX1001) */ + case 0x0B: /* Traditional Chinese (BIG5) */ + if (count < 1 || !iso[1]) { + ucs = *iso++; + break; + } + + /* we assume all cjk strings are written + in big endian order */ + tmp = *iso++ << 8; + tmp |= *iso++; + tmp -= 0x8000; + ucs = codepage_table[tmp]; + count--; + break; + + case 0x0C: /* UTF-8, do nothing */ + default: + ucs = *iso++; + break; + } + + if (ucs == 0) /* unknown char, assume invalid encoding */ + ucs = 0xffff; + utf8 = utf8encode(ucs, utf8); + } + } + return utf8; +} + +/* Recode a UTF-16 string with little-endian byte ordering to UTF-8 */ +unsigned char* utf16LEdecode(const unsigned char *utf16, unsigned char *utf8, unsigned int count) +{ + unsigned long ucs; + + while (count != 0) { + if (utf16[1] >= 0xD8 && utf16[1] < 0xE0) { /* Check for a surrogate pair */ + ucs = 0x10000 + ((utf16[0] << 10) | ((utf16[1] - 0xD8) << 18) | utf16[2] | ((utf16[3] - 0xDC) << 8)); + utf16 += 4; + count -= 2; + } else { + ucs = (utf16[0] | (utf16[1] << 8)); + utf16 += 2; + count -= 1; + } + utf8 = utf8encode(ucs, utf8); + } + return utf8; +} + +/* Recode a UTF-16 string with big-endian byte ordering to UTF-8 */ +unsigned char* utf16BEdecode(const unsigned char *utf16, unsigned char *utf8, unsigned int count) +{ + unsigned long ucs; + + while (count != 0) { + if (*utf16 >= 0xD8 && *utf16 < 0xE0) { /* Check for a surrogate pair */ + ucs = 0x10000 + (((utf16[0] - 0xD8) << 18) | (utf16[1] << 10) | ((utf16[2] - 0xDC) << 8) | utf16[3]); + utf16 += 4; + count -= 2; + } else { + ucs = (utf16[0] << 8) | utf16[1]; + utf16 += 2; + count -= 1; + } + utf8 = utf8encode(ucs, utf8); + } + return utf8; +} + +/* Recode any UTF-16 string to UTF-8 */ +//unsigned char* utf16decode(unsigned const char *utf16, unsigned char *utf8, unsigned int count) +unsigned char* utf16decode(const unsigned char *utf16, unsigned char *utf8, unsigned int count) +{ + unsigned long ucs; + + ucs = *(utf16++) << 8; + ucs |= *(utf16++); + + if (ucs == 0xFEFF) /* Check for BOM */ + return utf16BEdecode(utf16, utf8, count-1); + else if (ucs == 0xFFFE) + return utf16LEdecode(utf16, utf8, count-1); + else { /* ADDME: Should default be LE or BE? */ + utf16 -= 2; + return utf16BEdecode(utf16, utf8, count); + } +} + +/* Return the number of UTF-8 chars in a string */ +unsigned long utf8length(const unsigned char *utf8) +{ + unsigned long l = 0; + + while (*utf8 != 0) + if ((*utf8++ & MASK) != COMP) + l++; + + return l; +} + +/* Decode 1 UTF-8 char and return a pointer to the next char. */ +const unsigned char* utf8decode(const unsigned char *utf8, unsigned short *ucs) +{ + unsigned char c = *utf8++; + unsigned long code; + int tail = 0; + + if ((c <= 0x7f) || (c >= 0xc2)) { + /* Start of new character. */ + if (c < 0x80) { /* U-00000000 - U-0000007F, 1 byte */ + code = c; + } else if (c < 0xe0) { /* U-00000080 - U-000007FF, 2 bytes */ + tail = 1; + code = c & 0x1f; + } else if (c < 0xf0) { /* U-00000800 - U-0000FFFF, 3 bytes */ + tail = 2; + code = c & 0x0f; + } else if (c < 0xf5) { /* U-00010000 - U-001FFFFF, 4 bytes */ + tail = 3; + code = c & 0x07; + } else { + /* Invalid size. */ + code = 0xffff; + } + + while (tail-- && ((c = *utf8++) != 0)) { + if ((c & 0xc0) == 0x80) { + /* Valid continuation character. */ + code = (code << 6) | (c & 0x3f); + + } else { + /* Invalid continuation char */ + code = 0xffff; + utf8--; + break; + } + } + } else { + /* Invalid UTF-8 char */ + code = 0xffff; + } + /* currently we don't support chars above U-FFFF */ + *ucs = (code < 0x10000) ? code : 0xffff; + return utf8; +} + +void set_codepage(int cp) +{ + default_codepage = cp; + return; +} + +/* seek to a given char in a utf8 string and + return its start position in the string */ +int utf8seek(const unsigned char* utf8, int offset) +{ + int pos = 0; + + while (offset--) { + pos++; + while ((utf8[pos] & MASK) == COMP) + pos++; + } + return pos; +} diff --git a/firmware/drivers/fat.c b/firmware/drivers/fat.c index fd09bff0da..559d00b2b9 100644 --- a/firmware/drivers/fat.c +++ b/firmware/drivers/fat.c @@ -28,6 +28,7 @@ #include "system.h" #include "timefuncs.h" #include "kernel.h" +#include "rbunicode.h" #define BYTES2INT16(array,pos) \ (array[pos] | (array[pos+1] << 8 )) @@ -115,74 +116,6 @@ #define FAT_BAD_MARK 0x0ffffff7 #define FAT_EOF_MARK 0x0ffffff8 -/* filename charset conversion table */ -static const unsigned char unicode2iso8859_2[] = { - 0x00, 0x00, 0xc3, 0xe3, 0xa1, 0xb1, 0xc6, 0xe6, /* 0x0100 */ - 0x00, 0x00, 0x00, 0x00, 0xc8, 0xe8, 0xcf, 0xef, /* 0x0108 */ - 0xd0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0110 */ - 0xca, 0xea, 0xcc, 0xec, 0x00, 0x00, 0x00, 0x00, /* 0x0118 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0120 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0128 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0130 */ - 0x00, 0xc5, 0xe5, 0x00, 0x00, 0xa5, 0xb5, 0x00, /* 0x0138 */ - 0x00, 0xa3, 0xb3, 0xd1, 0xf1, 0x00, 0x00, 0xd2, /* 0x0140 */ - 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0148 */ - 0xd5, 0xf5, 0x00, 0x00, 0xc0, 0xe0, 0x00, 0x00, /* 0x0150 */ - 0xd8, 0xf8, 0xa6, 0xb6, 0x00, 0x00, 0xaa, 0xba, /* 0x0158 */ - 0xa9, 0xb9, 0xde, 0xfe, 0xab, 0xbb, 0x00, 0x00, /* 0x0160 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0xf9, /* 0x0168 */ - 0xdb, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0170 */ - 0x00, 0xac, 0xbc, 0xaf, 0xbf, 0xae, 0xbe, 0x00, /* 0x0178 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0180 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0188 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0190 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0198 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x01a0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x01a8 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x01b0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x01b8 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x01c0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x01c8 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x01d0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x01d8 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x01e0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x01e8 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x01f0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x01f8 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0200 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0208 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0210 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0218 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0220 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0228 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0230 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0238 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0240 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0248 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0250 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0258 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0260 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0268 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0270 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0278 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0280 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0288 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0290 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0298 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x02a0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x02a8 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x02b0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x02b8 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, /* 0x02c0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x02c8 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x02d0 */ - 0xa2, 0xff, 0x00, 0xb2, 0x00, 0xbd, 0x00, 0x00, /* 0x02d8 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x02e0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x02e8 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x02f0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* 0x02f8 */ -}; - struct fsinfo { unsigned long freecount; /* last known free cluster count */ unsigned long nextfree; /* first cluster to start looking for free @@ -1106,10 +1039,11 @@ static int write_long_name(struct fat_file* file, unsigned char* entry; unsigned int idx = firstentry % DIR_ENTRIES_PER_SECTOR; unsigned int sector = firstentry / DIR_ENTRIES_PER_SECTOR; - unsigned int i, j=0; unsigned char chksum = 0; - int nameidx=0, namelen = strlen(name); + unsigned int i, j=0; + unsigned int nameidx=0, namelen = utf8length(name); int rc; + unsigned short name_utf16[namelen + 1]; LDEBUGF("write_long_name(file:%lx, first:%d, num:%d, name:%s)\n", file->firstcluster, firstentry, numentries, name); @@ -1132,6 +1066,11 @@ static int write_long_name(struct fat_file* file, nameidx < (namelen - NAME_BYTES_PER_ENTRY); nameidx += NAME_BYTES_PER_ENTRY); + /* we need to convert the name first */ + /* since it is written in reverse order */ + for (i = 0; i <= namelen; i++) + name = utf8decode(name, &name_utf16[i]); + for (i=0; i < numentries; i++) { /* new sector? */ if ( idx >= DIR_ENTRIES_PER_SECTOR ) { @@ -1170,7 +1109,7 @@ static int write_long_name(struct fat_file* file, memset(entry, 0, DIR_ENTRY_SIZE); if ( i+1 < numentries ) { /* longname entry */ - int k, l = nameidx; + unsigned int k, l = nameidx; entry[FATLONG_ORDER] = numentries-i-1; if (i==0) { @@ -1178,22 +1117,22 @@ static int write_long_name(struct fat_file* file, entry[FATLONG_ORDER] |= 0x40; /* pad name with 0xffff */ - for (k=1; k<12; k++) entry[k] = 0xff; + for (k=1; k<11; k++) entry[k] = 0xff; for (k=14; k<26; k++) entry[k] = 0xff; for (k=28; k<32; k++) entry[k] = 0xff; }; /* set name */ for (k=0; k<5 && l <= namelen; k++) { - entry[k*2 + 1] = name[l++]; - entry[k*2 + 2] = 0; + entry[k*2 + 1] = (unsigned char)(name_utf16[l] & 0xff); + entry[k*2 + 2] = (unsigned char)(name_utf16[l++] >> 8); } for (k=0; k<6 && l <= namelen; k++) { - entry[k*2 + 14] = name[l++]; - entry[k*2 + 15] = 0; + entry[k*2 + 14] = (unsigned char)(name_utf16[l] & 0xff); + entry[k*2 + 15] = (unsigned char)(name_utf16[l++] >> 8); } for (k=0; k<2 && l <= namelen; k++) { - entry[k*2 + 28] = name[l++]; - entry[k*2 + 29] = 0; + entry[k*2 + 28] = (unsigned char)(name_utf16[l] & 0xff); + entry[k*2 + 29] = (unsigned char)(name_utf16[l++] >> 8); } entry[FATDIR_ATTR] = FAT_ATTR_LONG_NAME; @@ -1291,7 +1230,7 @@ static int add_dir_entry(struct fat_dir* dir, /* one dir entry needed for every 13 bytes of filename, plus one entry for the short name */ - entries_needed = (strlen(name) + (NAME_BYTES_PER_ENTRY-1)) + entries_needed = (utf8length(name) + (NAME_BYTES_PER_ENTRY-1)) / NAME_BYTES_PER_ENTRY + 1; } @@ -2226,58 +2165,6 @@ int fat_opendir(IF_MV2(int volume,) return 0; } -/* convert from unicode to a single-byte charset */ -static void unicode2iso(const unsigned char* unicode, unsigned char* iso, - int count) -{ - int i; - - for (i=0; iname; /* iterate backwards through the dir entries */ for (j=longs-1; j>=0; j--) { unsigned char* ptr = cached_buf; @@ -2370,16 +2258,11 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) index &= SECTOR_SIZE-1; } - /* names are stored in unicode, but we - only grab the low byte (iso8859-1). */ - unicode2iso(ptr + index + 1, entry->name + l, 5); - l+= 5; - unicode2iso(ptr + index + 14, entry->name + l, 6); - l+= 6; - unicode2iso(ptr + index + 28, entry->name + l, 2); - l+= 2; + utf8 = utf16LEdecode(ptr + index + 1, utf8, 5); + utf8 = utf16LEdecode(ptr + index + 14, utf8, 6); + utf8 = utf16LEdecode(ptr + index + 28, utf8, 2); } - entry->name[l]=0; + *utf8 = 0; } done = true; sectoridx = 0; diff --git a/firmware/drivers/lcd-16bit.c b/firmware/drivers/lcd-16bit.c index a4ae6cd553..582050c878 100644 --- a/firmware/drivers/lcd-16bit.c +++ b/firmware/drivers/lcd-16bit.c @@ -30,6 +30,7 @@ #include "debug.h" #include "system.h" #include "font.h" +#include "rbunicode.h" #include "bidi.h" #define SCROLLABLE_LINES 26 @@ -542,13 +543,13 @@ void lcd_bitmap(const fb_data *src, int x, int y, int width, int height) /* put a string at a given pixel position, skipping first ofs pixel columns */ static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str) { - int ch; + unsigned short ch; + unsigned short *ucs; struct font* pf = font_get(curfont); - if (bidi_support_enabled) - str = bidi_l2v(str, 1); + ucs = bidi_l2v(str, 1); - while ((ch = *str++) != '\0' && x < LCD_WIDTH) + while ((ch = *ucs++) != 0 && x < LCD_WIDTH) { int width; const unsigned char *bits; @@ -559,7 +560,7 @@ static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str) ch -= pf->firstchar; /* get proportional width and glyph bits */ - width = pf->width ? pf->width[ch] : pf->maxwidth; + width = font_get_width(pf,ch); if (ofs > width) { @@ -567,8 +568,7 @@ static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str) continue; } - bits = pf->bits + (pf->offset ? - pf->offset[ch] : ((pf->height + 7) / 8 * pf->maxwidth * ch)); + bits = font_get_bits(pf, ch); lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs, pf->height); @@ -597,7 +597,7 @@ void lcd_puts_style(int x, int y, const unsigned char *str, int style) return; lcd_getstringsize(str, &w, &h); - xpos = xmargin + x*w / strlen(str); + xpos = xmargin + x*w / utf8length(str); ypos = ymargin + y*h; lcd_putsxy(xpos, ypos, str); drawmode = (DRMODE_SOLID|DRMODE_INVERSEVID); @@ -707,7 +707,7 @@ void lcd_puts_scroll_style(int x, int y, const unsigned char *string, int style) end = strchr(s->line, '\0'); strncpy(end, string, LCD_WIDTH/2); - s->len = strlen(string); + s->len = utf8length(string); s->offset = 0; s->startx = x; s->backward = false; @@ -765,9 +765,11 @@ static void scroll_thread(void) } } else { - /* scroll forward the whole time */ - if (s->offset >= s->width) - s->offset %= s->width; + /* pause at beginning of line */ + if (s->offset >= s->width) { + s->offset = 0; + s->start_tick = current_tick + scroll_delay * 2; + } } lastmode = drawmode; diff --git a/firmware/drivers/lcd-h100-remote.c b/firmware/drivers/lcd-h100-remote.c index 33a8b3790d..a2fe63ee97 100644 --- a/firmware/drivers/lcd-h100-remote.c +++ b/firmware/drivers/lcd-h100-remote.c @@ -29,6 +29,7 @@ #include "debug.h" #include "system.h" #include "font.h" +#include "rbunicode.h" #include "bidi.h" /*** definitions ***/ @@ -1091,13 +1092,13 @@ void lcd_remote_bitmap(const unsigned char *src, int x, int y, int width, /* put a string at a given pixel position, skipping first ofs pixel columns */ static void lcd_remote_putsxyofs(int x, int y, int ofs, const unsigned char *str) { - int ch; + unsigned short ch; + unsigned short *ucs; struct font* pf = font_get(curfont); - if (bidi_support_enabled) - str = bidi_l2v(str, 1); + ucs = bidi_l2v(str, 1); - while ((ch = *str++) != '\0' && x < LCD_REMOTE_WIDTH) + while ((ch = *ucs++) != 0 && x < LCD_REMOTE_WIDTH) { int width; const unsigned char *bits; @@ -1108,7 +1109,7 @@ static void lcd_remote_putsxyofs(int x, int y, int ofs, const unsigned char *str ch -= pf->firstchar; /* get proportional width and glyph bits */ - width = pf->width ? pf->width[ch] : pf->maxwidth; + width = font_get_width(pf, ch); if (ofs > width) { @@ -1116,8 +1117,7 @@ static void lcd_remote_putsxyofs(int x, int y, int ofs, const unsigned char *str continue; } - bits = pf->bits + (pf->offset ? - pf->offset[ch] : ((pf->height + 7) / 8 * pf->maxwidth * ch)); + bits = font_get_bits(pf, ch); lcd_remote_bitmap_part(bits, ofs, 0, width, x, y, width - ofs, pf->height); @@ -1153,7 +1153,7 @@ void lcd_remote_puts_style(int x, int y, const unsigned char *str, int style) return; lcd_remote_getstringsize(str, &w, &h); - xpos = xmargin + x*w / strlen((char *)str); + xpos = xmargin + x*w / utf8length((char *)str); ypos = ymargin + y*h; lcd_remote_putsxy(xpos, ypos, str); drawmode = (DRMODE_SOLID|DRMODE_INVERSEVID); @@ -1256,7 +1256,7 @@ void lcd_remote_puts_scroll_style(int x, int y, const unsigned char *string, int end = strchr(s->line, '\0'); strncpy(end, (char *)string, LCD_REMOTE_WIDTH/2); - s->len = strlen((char *)string); + s->len = utf8length((char *)string); s->offset = 0; s->startx = x; s->backward = false; @@ -1323,9 +1323,11 @@ static void scroll_thread(void) } } else { - /* scroll forward the whole time */ - if (s->offset >= s->width) - s->offset %= s->width; + /* pause at beginning of line */ + if (s->offset >= s->width) { + s->offset = 0; + s->start_tick = current_tick + scroll_delay * 2; + } } lastmode = drawmode; diff --git a/firmware/drivers/lcd-h100.c b/firmware/drivers/lcd-h100.c index ba959d23de..1199bf04a1 100644 --- a/firmware/drivers/lcd-h100.c +++ b/firmware/drivers/lcd-h100.c @@ -28,6 +28,7 @@ #include "debug.h" #include "system.h" #include "font.h" +#include "rbunicode.h" #include "bidi.h" /*** definitions ***/ @@ -1014,13 +1015,13 @@ void lcd_bitmap(const unsigned char *src, int x, int y, int width, int height) /* put a string at a given pixel position, skipping first ofs pixel columns */ static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str) { - int ch; + unsigned short ch; + unsigned short *ucs; struct font* pf = font_get(curfont); - if (bidi_support_enabled) - str = bidi_l2v(str, 1); + ucs = bidi_l2v(str, 1); - while ((ch = *str++) != '\0' && x < LCD_WIDTH) + while ((ch = *ucs++) != 0 && x < LCD_WIDTH) { int width; const unsigned char *bits; @@ -1031,7 +1032,7 @@ static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str) ch -= pf->firstchar; /* get proportional width and glyph bits */ - width = pf->width ? pf->width[ch] : pf->maxwidth; + width = font_get_width(pf,ch); if (ofs > width) { @@ -1039,8 +1040,7 @@ static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str) continue; } - bits = pf->bits + (pf->offset ? - pf->offset[ch] : ((pf->height + 7) / 8 * pf->maxwidth * ch)); + bits = font_get_bits(pf, ch); lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs, pf->height); @@ -1069,7 +1069,7 @@ void lcd_puts_style(int x, int y, const unsigned char *str, int style) return; lcd_getstringsize(str, &w, &h); - xpos = xmargin + x*w / strlen((char *)str); + xpos = xmargin + x*w / utf8length((char *)str); ypos = ymargin + y*h; lcd_putsxy(xpos, ypos, str); drawmode = (DRMODE_SOLID|DRMODE_INVERSEVID); @@ -1178,7 +1178,7 @@ void lcd_puts_scroll_style(int x, int y, const unsigned char *string, int style) end = strchr(s->line, '\0'); strncpy(end, (char *)string, LCD_WIDTH/2); - s->len = strlen((char *)string); + s->len = utf8length((char *)string); s->offset = 0; s->startx = x; s->backward = false; diff --git a/firmware/drivers/lcd-player.c b/firmware/drivers/lcd-player.c index 566c868d7f..d01ec3447f 100644 --- a/firmware/drivers/lcd-player.c +++ b/firmware/drivers/lcd-player.c @@ -31,6 +31,7 @@ #include "system.h" #include "font.h" #include "lcd-player-charset.h" +#include "rbunicode.h" /*** definitions ***/ @@ -323,9 +324,24 @@ static void lcd_puts_cont_scroll(int x, int y, const unsigned char *string) } void lcd_puts(int x, int y, const unsigned char *string) { + int i=0; + unsigned short ucs; + const unsigned char *utf8 = string; + unsigned char tmp[12]; + + while (*utf8 && i<11) { + utf8 = utf8decode(utf8, &ucs); + if (ucs < 256) + tmp[i++] = ucs; + else + tmp[i++] = '?'; + } + + tmp[i] = 0; + DEBUGF("lcd_puts(%d, %d) -> ", x, y); scroll[y].mode=SCROLL_MODE_OFF; - return lcd_puts_cont_scroll(x, y, string); + return lcd_puts_cont_scroll(x, y, tmp); } void lcd_put_cursor(int x, int y, char cursor_char) @@ -627,14 +643,28 @@ void lcd_bidir_scroll(int percent) void lcd_puts_scroll(int x, int y, const unsigned char* string ) { struct scrollinfo* s; - int i; + int i=0; + unsigned short ucs; + const unsigned char *utf8 = string; + unsigned char tmp[utf8length(string)+1]; + + while (*utf8) { + utf8 = utf8decode(utf8, &ucs); + if (ucs < 256) + tmp[i++] = ucs; + else + tmp[i++] = '?'; + } + + tmp[i] = 0; + DEBUGF("lcd_puts_scroll(%d, %d, %s)\n", x, y, string); s = &scroll[y]; - lcd_puts_cont_scroll(x,y,string); - s->textlen = strlen(string); + lcd_puts_cont_scroll(x,y,tmp); + s->textlen = strlen(tmp); if ( s->textlen > 11-x ) { s->mode = SCROLL_MODE_RUN; @@ -649,7 +679,7 @@ void lcd_puts_scroll(int x, int y, const unsigned char* string ) s->jump_scroll_steps=11-x; s->jump_scroll=jump_scroll; } - strncpy(s->text,string,sizeof s->text); + strncpy(s->text,tmp,sizeof s->text); s->turn_offset=-1; if (bidir_limit && (s->textlen < ((11-x)*(100+bidir_limit))/100)) { s->turn_offset=s->textlen+x-11; diff --git a/firmware/drivers/lcd-recorder.c b/firmware/drivers/lcd-recorder.c index 7e064b8001..081a7a5267 100644 --- a/firmware/drivers/lcd-recorder.c +++ b/firmware/drivers/lcd-recorder.c @@ -28,6 +28,7 @@ #include "system.h" #include "font.h" #include "hwcompat.h" +#include "rbunicode.h" #include "bidi.h" /*** definitions ***/ @@ -844,13 +845,13 @@ void lcd_bitmap(const unsigned char *src, int x, int y, int width, int height) /* put a string at a given pixel position, skipping first ofs pixel columns */ static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str) { - int ch; + unsigned short ch; + unsigned short *ucs; struct font* pf = font_get(curfont); - if (bidi_support_enabled) - str = bidi_l2v(str, 1); + ucs = bidi_l2v(str, 1); - while ((ch = *str++) != '\0' && x < LCD_WIDTH) + while ((ch = *ucs++) != 0 && x < LCD_WIDTH) { int width; const unsigned char *bits; @@ -861,7 +862,7 @@ static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str) ch -= pf->firstchar; /* get proportional width and glyph bits */ - width = pf->width ? pf->width[ch] : pf->maxwidth; + width = font_get_width(pf,ch); if (ofs > width) { @@ -869,8 +870,7 @@ static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str) continue; } - bits = pf->bits + (pf->offset ? - pf->offset[ch] : ((pf->height + 7) / 8 * pf->maxwidth * ch)); + bits = font_get_bits(pf, ch); lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs, pf->height); @@ -898,7 +898,7 @@ void lcd_puts_style(int x, int y, const unsigned char *str, int style) return; lcd_getstringsize(str, &w, &h); - xpos = xmargin + x*w / strlen(str); + xpos = xmargin + x*w / utf8length(str); ypos = ymargin + y*h; lcd_putsxy(xpos, ypos, str); drawmode = (DRMODE_SOLID|DRMODE_INVERSEVID); @@ -1007,7 +1007,7 @@ void lcd_puts_scroll_style(int x, int y, const unsigned char *string, int style) end = strchr(s->line, '\0'); strncpy(end, string, LCD_WIDTH/2); - s->len = strlen(string); + s->len = utf8length(string); s->offset = 0; s->startx = x; s->backward = false; diff --git a/firmware/export/bidi.h b/firmware/export/bidi.h index 88e2eeb07e..36ff46c6c0 100644 --- a/firmware/export/bidi.h +++ b/firmware/export/bidi.h @@ -17,7 +17,5 @@ * ****************************************************************************/ #ifndef BIDI_H -extern unsigned char *bidi_l2v(const unsigned char *str, int orientation); -extern bool bidi_support_enabled; -extern void set_bidi_support(bool setting); +extern unsigned short *bidi_l2v(const unsigned char *str, int orientation); #endif diff --git a/firmware/export/font.h b/firmware/export/font.h index beb63470bf..ccdb30ff00 100644 --- a/firmware/export/font.h +++ b/firmware/export/font.h @@ -31,10 +31,16 @@ #if LCD_HEIGHT > 64 #define MAX_FONT_SIZE 10000 #else -#define MAX_FONT_SIZE 4000 +#define MAX_FONT_SIZE 4000 #endif #endif +#ifndef FONT_HEADER_SIZE +#define FONT_HEADER_SIZE 36 +#endif + +#define GLYPH_CACHE_FILE "/.rockbox/.glyphcache" + /* * Fonts are specified by number, and used for display * of menu information as well as mp3 filename data. @@ -88,6 +94,7 @@ struct font { const unsigned short *offset; /* offsets into bitmap data*/ const unsigned char *width; /* character widths or NULL if fixed*/ int defaultchar; /* default char (not glyph index)*/ + long bits_size; /* # bytes of glyph bits*/ }; /* font routines*/ @@ -96,6 +103,10 @@ struct font* font_load(const char *path); struct font* font_get(int font); void font_reset(void); int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber); +int font_get_width(struct font* ft, unsigned short ch); +const unsigned char * font_get_bits(struct font* ft, unsigned short ch); +void glyph_cache_save(void); +void glyph_cache_load(void); #else /* HAVE_LCD_BITMAP */ diff --git a/firmware/export/id3.h b/firmware/export/id3.h index df0c61f473..5090534e99 100644 --- a/firmware/export/id3.h +++ b/firmware/export/id3.h @@ -94,7 +94,7 @@ struct mp3entry { /* these following two fields are used for local buffering */ char id3v2buf[300]; - char id3v1buf[3][32]; + char id3v1buf[3][92]; /* resume related */ unsigned long offset; /* bytes played */ diff --git a/firmware/font.c b/firmware/font.c index 303887701d..e8d5db46b9 100644 --- a/firmware/font.c +++ b/firmware/font.c @@ -33,6 +33,10 @@ #include "file.h" #include "debug.h" #include "panic.h" +#include "rbunicode.h" +/* Font cache includes */ +#include "font_cache.h" +#include "lru.h" #ifndef O_BINARY #define O_BINARY 0 @@ -53,6 +57,16 @@ static unsigned char *freeptr = mbuf; static unsigned char *fileptr; static unsigned char *eofptr; +/* Font cache structures */ +static struct font_cache font_cache_ui; +static int fnt_file = -1; /* >=0 if font is cached */ +unsigned long file_width_offset; /* offset to file width data */ +unsigned long file_offset_offset; /* offset to file offset data */ +static void cache_create(int maxwidth, int height); +static int long_offset = 0; +static int glyph_file; +/* End Font cache structures */ + void font_init(void) { memset(&font_ui, 0, sizeof(struct font)); @@ -94,41 +108,12 @@ void font_reset(void) memset(&font_ui, 0, sizeof(struct font)); } -/* read and load font into incore font structure*/ -struct font* font_load(const char *path) +static struct font* font_load_header(struct font *pf) { - int fd, filesize; + char version[4+1]; unsigned short maxwidth, height, ascent, pad; unsigned long firstchar, defaultchar, size; - unsigned long i, nbits, noffset, nwidth; - char version[4+1]; - struct font* pf = &font_ui; - - /* open and read entire font file*/ - fd = open(path, O_RDONLY|O_BINARY); - if (fd < 0) { - DEBUGF("Can't open font: %s\n", path); - return NULL; - } - - font_reset(); - - /* currently, font loading replaces earlier font allocation*/ - freeptr = (unsigned char *)(((int)mbuf + 3) & ~3); - - fileptr = freeptr; - filesize = read(fd, fileptr, MAX_FONT_SIZE); - eofptr = fileptr + filesize; - - /* no need for multiple font loads currently*/ - /*freeptr += filesize;*/ - /*freeptr = (unsigned char *)(freeptr + 3) & ~3;*/ /* pad freeptr*/ - - close(fd); - if (filesize == MAX_FONT_SIZE) { - DEBUGF("Font %s too large: %d\n", path, filesize); - return NULL; - } + unsigned long nbits; /* read magic and version #*/ memset(version, 0, sizeof(version)); @@ -163,6 +148,14 @@ struct font* font_load(const char *path) /* # words of bitmap_t*/ if (!readlong(&nbits)) return NULL; + pf->bits_size = nbits; + + return pf; +} +/* Load memory font */ +struct font* font_load_in_memory(struct font* pf) +{ + long i, noffset, nwidth; /* # longs of offset*/ if (!readlong(&noffset)) @@ -174,19 +167,44 @@ struct font* font_load(const char *path) /* variable font data*/ pf->bits = (unsigned char *)fileptr; - fileptr += nbits*sizeof(unsigned char); + fileptr += pf->bits_size*sizeof(unsigned char); - /* pad to 16 bit boundary*/ - fileptr = (unsigned char *)(((long)fileptr + 1) & ~1); + if ( pf->bits_size < 0xFFDB ) + { + /* pad to 16-bit boundary */ + fileptr = (unsigned char *)(((int)fileptr + 1) & ~1); + } + else + { + /* pad to 32-bit boundary*/ + fileptr = (unsigned char *)(((int)fileptr + 3) & ~3); + } - if (noffset) { - pf->offset = (unsigned short *)fileptr; - for (i=0; ibits_size < 0xFFDB ) + { + long_offset = 0; + pf->offset = (unsigned short *)fileptr; + for (i=0; ioffset))[i] = (unsigned short)offset; + } + } + else { - unsigned short offset; - if (!readshort(&offset)) - return NULL; - ((unsigned short*)(pf->offset))[i] = (unsigned short)offset; + long_offset = 1; + pf->offset = (unsigned short *)fileptr; + for (i=0; ioffset))[i] = (unsigned long)offset; + } } } else @@ -205,6 +223,141 @@ struct font* font_load(const char *path) return pf; /* success!*/ } +/* Load cached font */ +struct font* font_load_cached(struct font* pf) +{ + unsigned long noffset, nwidth; + unsigned char* oldfileptr = fileptr; + + /* # longs of offset*/ + if (!readlong(&noffset)) + return NULL; + + /* # bytes of width*/ + if (!readlong(&nwidth)) + return NULL; + + /* We are now at the bitmap data, this is fixed at 36.. */ + pf->bits = NULL; + + /* Calculate offset to offset data */ + fileptr += pf->bits_size * sizeof(unsigned char); + + if ( pf->bits_size < 0xFFDB ) + { + long_offset = 0; + /* pad to 16-bit boundary */ + fileptr = (unsigned char *)(((int)fileptr + 1) & ~1); + } + else + { + long_offset = 1; + /* pad to 32-bit boundary*/ + fileptr = (unsigned char *)(((int)fileptr + 3) & ~3); + } + + if (noffset) + file_offset_offset = (unsigned long)(fileptr - freeptr); + else + file_offset_offset = 0; + + /* Calculate offset to widths data */ + if ( pf->bits_size < 0xFFDB ) + fileptr += noffset * sizeof(unsigned short); + else + fileptr += noffset * sizeof(unsigned long); + + if (nwidth) + file_width_offset = (unsigned long)(fileptr - freeptr); + else + file_width_offset = 0; + + fileptr = oldfileptr; + + /* Create the cache */ + cache_create(pf->maxwidth, pf->height); + + return pf; +} + +/* read and load font into incore font structure*/ +struct font* font_load(const char *path) +{ + int filesize; + struct font* pf = &font_ui; + + /* save loaded glyphs */ + glyph_cache_save(); + + /* Close font file handle */ + if (fnt_file >= 0) + close(fnt_file); + + /* open and read entire font file*/ + fnt_file = open(path, O_RDONLY|O_BINARY); + + if (fnt_file < 0) { + DEBUGF("Can't open font: %s\n", path); + return NULL; + } + + /* Check file size */ + filesize = lseek(fnt_file, 0, SEEK_END); + lseek(fnt_file, 0, SEEK_SET); + + font_reset(); + + /* currently, font loading replaces earlier font allocation*/ + freeptr = (unsigned char *)(((int)mbuf + 3) & ~3); + fileptr = freeptr; + + + if (filesize > MAX_FONT_SIZE) + { + read(fnt_file, fileptr, FONT_HEADER_SIZE); + eofptr = fileptr + FONT_HEADER_SIZE; + + if (!font_load_header(pf)) + { + DEBUGF("Failed font header load"); + return NULL; + } + + if (!font_load_cached(pf)) + { + DEBUGF("Failed font cache load"); + return NULL; + } + + glyph_cache_load(); + } + else + { + read(fnt_file, fileptr, MAX_FONT_SIZE); + eofptr = fileptr + filesize; + close(fnt_file); + fnt_file = -1; + + if (!font_load_header(pf)) + { + DEBUGF("Failed font header load"); + return NULL; + } + + if (!font_load_in_memory(pf)) + { + DEBUGF("Failed mem load"); + return NULL; + } + } + + /* no need for multiple font loads currently*/ + /*freeptr += filesize;*/ + /*freeptr = (unsigned char *)(freeptr + 3) & ~3;*/ /* pad freeptr*/ + + return pf; /* success!*/ +} + /* * Return a pointer to an incore font structure. * If the requested font isn't loaded/compiled-in, @@ -231,17 +384,18 @@ struct font* font_get(int font) int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber) { struct font* pf = font_get(fontnumber); - int ch; + unsigned short ch; int width = 0; - while((ch = *str++)) { + for (str = utf8decode(str, &ch); ch != 0 ; str = utf8decode(str, &ch)) + { /* check input range*/ if (ch < pf->firstchar || ch >= pf->firstchar+pf->size) ch = pf->defaultchar; ch -= pf->firstchar; /* get proportional width and glyph bits*/ - width += pf->width? pf->width[ch]: pf->maxwidth; + width += font_get_width(pf,ch); } if ( w ) *w = width; @@ -250,6 +404,154 @@ int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber) return width; } +/* + * Reads an entry into cache entry + */ +static void +load_cache_entry(struct font_cache_entry* p, void* callback_data) +{ + struct font* pf = callback_data; + unsigned short char_code = p->_char_code; + unsigned char tmp[2]; + + if (file_width_offset) + { + int width_offset = file_width_offset + char_code; + lseek(fnt_file, width_offset, SEEK_SET); + read(fnt_file, &(p->width), 1); + } + else + { + p->width = pf->maxwidth; + } + + long bitmap_offset = 0; + + if (file_offset_offset) + { + long offset = file_offset_offset + char_code * (long_offset ? sizeof(long) : sizeof(short)); + lseek(fnt_file, offset, SEEK_SET); + read (fnt_file, tmp, 2); + bitmap_offset = tmp[0] | (tmp[1] << 8); + if (long_offset) { + read (fnt_file, tmp, 2); + bitmap_offset |= (tmp[0] << 16) | (tmp[1] << 24); + } + } + else + { + bitmap_offset = ((pf->height + 7) / 8) * p->width * char_code; + } + + long file_offset = FONT_HEADER_SIZE + bitmap_offset; + lseek(fnt_file, file_offset, SEEK_SET); + + int src_bytes = p->width * ((pf->height + 7) / 8); + read(fnt_file, p->bitmap, src_bytes); +} + +/* + * Converts cbuf into a font cache + */ +static void cache_create(int maxwidth, int height) +{ + /* maximum size of rotated bitmap */ + int bitmap_size = maxwidth * ((height + 7) / 8); + + /* Initialise cache */ + font_cache_create(&font_cache_ui, mbuf, MAX_FONT_SIZE, bitmap_size); +} + +/* + * Returns width of character + */ +int font_get_width(struct font* pf, unsigned short char_code) +{ + return (fnt_file >= 0 && pf != &sysfont)? + font_cache_get(&font_cache_ui,char_code,load_cache_entry,pf)->width: + pf->width? pf->width[char_code]: pf->maxwidth; +} + +const unsigned char* font_get_bits(struct font* pf, unsigned short char_code) +{ + const unsigned char* bits; + if (fnt_file >= 0 && pf != &sysfont) + { + bits = + (unsigned char*)font_cache_get(&font_cache_ui,char_code,load_cache_entry,pf)->bitmap; + } + else + { + bits = pf->bits + (pf->offset? + pf->offset[char_code]: + (((pf->height + 7) / 8) * pf->maxwidth * char_code)); + } + + return bits; +} + +void glyph_file_write(void* data) +{ + struct font_cache_entry* p = data; + unsigned char tmp[2]; + + if (p->_char_code != 0xffff && glyph_file >= 0) { + tmp[0] = p->_char_code >> 8; + tmp[1] = p->_char_code & 0xff; + if (write(glyph_file, tmp, 2) != 2) { + close(glyph_file); + glyph_file = -1; + } + } + return; +} + +/* save the char codes of the loaded glyphs to a file */ +void glyph_cache_save(void) +{ + + if (fnt_file >= 0) { + + glyph_file = creat(GLYPH_CACHE_FILE, 0); + + if (glyph_file < 0) return; + + lru_traverse(&font_cache_ui._lru, glyph_file_write); + + if (glyph_file >= 0) + close(glyph_file); + } + return; +} + +void glyph_cache_load(void) +{ + if (fnt_file >= 0) { + + int fd; + unsigned char tmp[2]; + unsigned short ch; + struct font* pf = &font_ui; + + fd = open(GLYPH_CACHE_FILE, O_RDONLY|O_BINARY); + + if (fd >= 0) { + + while (read(fd, tmp, 2) == 2) { + ch = (tmp[0] << 8) | tmp[1]; + font_get_bits(pf, ch); + } + + close(fd); + } else { + /* load latin1 chars into cache */ + ch = 255 - pf->firstchar; + while (ch--) + font_get_bits(pf, ch); + } + } + return; +} #endif /* HAVE_LCD_BITMAP */ diff --git a/firmware/font_cache.c b/firmware/font_cache.c new file mode 100644 index 0000000000..34a14e0551 --- /dev/null +++ b/firmware/font_cache.c @@ -0,0 +1,224 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2003 Tat Tang + * + * 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 +#include "font_cache.h" +#include "debug.h" + +/******************************************************************************* + * font_cache_lru_init + ******************************************************************************/ +void font_cache_lru_init(void* data) +{ + struct font_cache_entry* p = data; + p->_char_code = 0xffff; /* assume invalid char */ +} + +/******************************************************************************* + * font_cache_create + ******************************************************************************/ +void font_cache_create( + struct font_cache* fcache, + void *buf, + int buf_size, + int bitmap_bytes_size) +{ + int font_cache_entry_size = + sizeof(struct font_cache_entry) + bitmap_bytes_size; + + /* make sure font cache entries are a multiple of 16 bits */ + if (font_cache_entry_size % 2 != 0) + font_cache_entry_size++; + + int cache_size = buf_size / + (font_cache_entry_size + LRU_SLOT_OVERHEAD + sizeof(short)); + + fcache->_size = 1; + fcache->_capacity = cache_size; + + /* set up index */ + fcache->_index = buf; + + /* set up lru list */ + unsigned char* lru_buf = buf; + lru_buf += sizeof(short) * cache_size; + lru_create(&fcache->_lru, lru_buf, cache_size, font_cache_entry_size); + + /* initialise cache */ + lru_traverse(&fcache->_lru, font_cache_lru_init); + short i; + for (i = 0; i < cache_size; i++) + fcache->_index[i] = i; /* small cheat here */ +} + +/******************************************************************************* + * font_cache_index_of + ******************************************************************************/ +int font_cache_index_of( + struct font_cache* fcache, + unsigned short char_code) +{ + struct font_cache_entry* p; + int left, right, mid, c; + left = 0; + right = fcache->_size - 1; + + do + { + mid = (left + right) / 2; + + p = lru_data(&fcache->_lru, fcache->_index[mid]); + c = p->_char_code - char_code; + + if (c == 0) + return mid; + + if (c < 0) + left = mid + 1; + else + right = mid - 1; + } + while (left <= right); + + return -1; +} + +/******************************************************************************* + * font_cache_insertion_point + ******************************************************************************/ +int font_cache_insertion_point( + struct font_cache* fcache, + unsigned short char_code) +{ + struct font_cache_entry* p; + short *index = fcache->_index; + + p = lru_data(&fcache->_lru, index[0]); + if (char_code < p->_char_code) + return -1; + + p = lru_data(&fcache->_lru, index[fcache->_capacity - 1]); + if (char_code > p->_char_code) + return fcache->_capacity - 1; + + int left, right, mid, c; + + left = 0; + right = fcache->_capacity - 1; + + do + { + mid = (left + right) / 2; + + p = lru_data(&fcache->_lru, index[mid]); + c = char_code - p->_char_code; + + if (c >= 0) + { + p = lru_data(&fcache->_lru, index[mid+1]); + int z = char_code - p->_char_code; + + if (z < 0) + return mid; + + if (z == 0) + return mid + 1; + } + + + if (c > 0) + left = mid + 1; + else + right = mid - 1; + } + while (left <= right); + + /* not found */ + return -2; +} + +/******************************************************************************* + * font_cache_get + ******************************************************************************/ +struct font_cache_entry* font_cache_get( + struct font_cache* fcache, + unsigned short char_code, + void (*callback) (struct font_cache_entry* p, void *callback_data), + void *callback_data) +{ + int insertion_point = font_cache_insertion_point(fcache, char_code); + if (insertion_point >= 0) + { + short lru_handle = fcache->_index[insertion_point]; + struct font_cache_entry* p = lru_data(&fcache->_lru, lru_handle); + if (p->_char_code == char_code) + { + lru_touch(&fcache->_lru, lru_handle); + return lru_data(&fcache->_lru, lru_handle); + } + } + + /* not found */ + short lru_handle_to_replace = fcache->_lru._head; + struct font_cache_entry* p = + lru_data(&fcache->_lru, lru_handle_to_replace); + int index_to_replace = font_cache_index_of(fcache, p->_char_code); + + if (insertion_point < index_to_replace) + { + /* shift memory down */ + int dest = insertion_point+2; + int src = insertion_point+1; + int len = index_to_replace - insertion_point - 1; + + int desti = dest + len - 1; + int srci = src + len - 1; + + int i; + for (i = 0; i < len; i++) + fcache->_index[desti--] = fcache->_index[srci--]; + + /* add to index */ + fcache->_index[insertion_point + 1] = lru_handle_to_replace; + } + else if (insertion_point > index_to_replace) + { + /* shift memory up */ + int dest = index_to_replace; + int src = index_to_replace + 1; + int len = insertion_point - index_to_replace; + + int i; + for (i=0; i < len; i++) + fcache->_index[dest++] = fcache->_index[src++]; + + /* add to index */ + fcache->_index[insertion_point] = lru_handle_to_replace; + } + + /* load new entry into cache */ + lru_touch(&fcache->_lru, lru_handle_to_replace); + + if (fcache->_size < fcache->_capacity) + fcache->_size++; + + p->_char_code = char_code; + callback(p, callback_data); + + return p; +} diff --git a/firmware/id3.c b/firmware/id3.c index 13e00c3941..ad7c8280d0 100644 --- a/firmware/id3.c +++ b/firmware/id3.c @@ -42,6 +42,7 @@ #include "mp3data.h" #include "system.h" #include "replaygain.h" +#include "rbunicode.h" #define UNSYNC(b0,b1,b2,b3) (((long)(b0 & 0x7F) << (3*7)) | \ ((long)(b1 & 0x7F) << (2*7)) | \ @@ -359,100 +360,83 @@ static const struct tag_resolver taglist[] = { #define TAGLIST_SIZE ((int)(sizeof(taglist) / sizeof(taglist[0]))) /* Checks to see if the passed in string is a 16-bit wide Unicode v2 - string. If it is, we attempt to convert it to a 8-bit ASCII string - (for valid 8-bit ASCII characters). If it's not unicode, we leave - it alone. At some point we should fully support unicode strings */ -static int unicode_munge(char* string, int *len) { - long tmp; - bool le = false; - int i; - char *str = string; - char *outstr = string; - bool bom = false; - int outlen; - - if(str[0] > 0x03) { - /* Plain old string */ - return 0; - } - - /* Type 0x00 is ordinary ISO 8859-1 */ - if(str[0] == 0x00) { - int i = --(*len); - - /* We must move the string to the left */ - while (i--) { - string[0] = string[1]; - string++; - } - - return 0; - } - - /* Unicode with or without BOM */ - if(str[0] == 0x01 || str[0] == 0x02) { - (*len)--; - str++; - i = 0; - - /* Handle frames with more than one string (needed for TXXX frames). - */ - do { - tmp = BYTES2INT(0, 0, str[0], str[1]); - - /* Now check if there is a BOM (zero-width non-breaking space, 0xfeff) - and if it is in little or big endian format */ - if(tmp == 0xfffe) { /* Little endian? */ - bom = true; - le = true; - str += 2; - (*len)-=2; - } - - if(tmp == 0xfeff) { /* Big endian? */ - bom = true; - str += 2; - (*len)-=2; - } - - /* If there is no BOM (which is a specification violation), - let's try to guess it. If one of the bytes is 0x00, it is - probably the most significant one. */ - if(!bom) { - if(str[1] == 0) - le = true; - } - - outlen = *len / 2; - - do { - if(le) { - if(str[1]) - outstr[i++] = '.'; - else - outstr[i++] = str[0]; - } else { - if(str[0]) - outstr[i++] = '.'; - else - outstr[i++] = str[1]; - } - str += 2; - } while((str[0] || str[1]) && (i < outlen)); - - str += 2; - outstr[i++] = 0; /* Terminate the string */ - } while(i < outlen); + string. If it is, we convert it to a UTF-8 string. If it's not unicode, + we convert from the default codepage */ +static int unicode_munge(char* string, char* utf8buf, int *len) { + long tmp; + bool le = false; + int i = 0; + char *str = string; + int templen = 0; + char* utf8 = utf8buf; + + switch (str[0]) { + case 0x00: /* Type 0x00 is ordinary ISO 8859-1 */ + str++; + (*len)--; + utf8 = iso_decode(str, utf8, -1, *len); + *utf8 = 0; + *len = strlen(utf8buf); + break; - *len = i - 1; + case 0x01: /* Unicode with or without BOM */ + case 0x02: + (*len)--; + str++; + + /* Handle frames with more than one string + (needed for TXXX frames).*/ + do { + tmp = BYTES2INT(0, 0, str[0], str[1]); + + /* Now check if there is a BOM + (zero-width non-breaking space, 0xfeff) + and if it is in little or big endian format */ + if(tmp == 0xfffe) { /* Little endian? */ + le = true; + str += 2; + (*len)-=2; + } else if(tmp == 0xfeff) { /* Big endian? */ + str += 2; + (*len)-=2; + } else + /* If there is no BOM (which is a specification violation), + let's try to guess it. If one of the bytes is 0x00, it is + probably the most significant one. */ + if(str[1] == 0) + le = true; + + do { + if(le) + utf8 = utf16LEdecode(str, utf8, 1); + else + utf8 = utf16BEdecode(str, utf8, 1); + + str+=2; + i += 2; + } while((str[0] || str[1]) && (i < *len)); + + *utf8++ = 0; /* Terminate the string */ + templen += (strlen(&utf8buf[templen]) + 1); + str += 2; + i+=2; + } while(i < *len); + *len = templen - 1; + break; - return 0; - } + case 0x03: /* UTF-8 encoded string */ + for(i=0; i < *len; i++) + utf8[i] = str[i+1]; + *len = strlen(utf8buf); + break; - /* If we come here, the string was of an unsupported type */ - *len = 1; - outstr[0] = 0; - return -1; + default: /* Plain old string */ + utf8 = iso_decode(str, utf8, -1, *len); + *utf8 = 0; + *len = strlen(utf8buf); + break; + } + return 0; } /* @@ -468,6 +452,7 @@ static bool setid3v1title(int fd, struct mp3entry *entry) unsigned char buffer[128]; static const char offsets[] = {3, 33, 63, 93, 125, 127}; int i, j; + unsigned char* utf8; if (-1 == lseek(fd, -128, SEEK_END)) return false; @@ -482,8 +467,8 @@ static bool setid3v1title(int fd, struct mp3entry *entry) entry->id3version = ID3_VER_1_0; for (i=0; i < (int)sizeof offsets; i++) { - char* ptr = (char *)buffer + offsets[i]; - + unsigned char* ptr = (unsigned char *)buffer + offsets[i]; + if (i<3) { /* kill trailing space in strings */ for (j=29; j && ptr[j]==' '; j--) @@ -492,18 +477,13 @@ static bool setid3v1title(int fd, struct mp3entry *entry) switch(i) { case 0: - strncpy(entry->id3v1buf[2], ptr, 30); - entry->title = entry->id3v1buf[2]; - break; - case 1: - strncpy(entry->id3v1buf[0], ptr, 30); - entry->artist = entry->id3v1buf[0]; - break; - case 2: - strncpy(entry->id3v1buf[1], ptr, 30); - entry->album = entry->id3v1buf[1]; + /* convert string to utf8 */ + utf8 = entry->id3v1buf[i]; + utf8 = iso_decode(ptr, utf8, -1, 30); + /* make sure string is terminated */ + *utf8 = 0; break; case 3: @@ -527,6 +507,10 @@ static bool setid3v1title(int fd, struct mp3entry *entry) } } + entry->title = entry->id3v1buf[0]; + entry->artist = entry->id3v1buf[1]; + entry->album = entry->id3v1buf[2]; + return true; } @@ -556,7 +540,7 @@ static void setid3v2title(int fd, struct mp3entry *entry) bool global_unsynch = false; bool unsynch = false; int data_length_ind; - int i; + int i, j; int rc; global_ff_found = false; @@ -789,7 +773,18 @@ static void setid3v2title(int fd, struct mp3entry *entry) if(unsynch || (global_unsynch && version >= ID3_VER_2_4)) bytesread = unsynchronize_frame(tag, bytesread); - unicode_munge( tag, &bytesread ); + + /* UTF-8 could potentially be 3 times larger */ + /* so we need to create a new buffer */ + char utf8buf[(3 * bytesread) + 1]; + + unicode_munge( tag, utf8buf, &bytesread ); + + if(bytesread >= buffersize - bufferpos) + bytesread = buffersize - bufferpos - 1; + + for (j = 0; j < bytesread; j++) + tag[j] = utf8buf[j]; if (ptag) *ptag = tag; @@ -809,7 +804,7 @@ static void setid3v2title(int fd, struct mp3entry *entry) break; } } - + if( i == TAGLIST_SIZE ) { /* no tag in tagList was found, or it was a repeat. skip it using the total size */ @@ -839,7 +834,7 @@ int getid3v2len(int fd) { char buf[6]; int offset; - + /* Make sure file has a ID3 tag */ if((-1 == lseek(fd, 0, SEEK_SET)) || (read(fd, buf, 6) != 6) || @@ -955,7 +950,7 @@ bool mp3info(struct mp3entry *entry, const char *filename, bool v1first) #if CONFIG_CODEC != SWCODEC memset(entry, 0, sizeof(struct mp3entry)); #endif - + strncpy(entry->path, filename, sizeof(entry->path)); entry->title = NULL; diff --git a/firmware/include/font_cache.h b/firmware/include/font_cache.h new file mode 100644 index 0000000000..813cd18987 --- /dev/null +++ b/firmware/include/font_cache.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2003 Tat Tang + * + * 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 "lru.h" + +/******************************************************************************* + * + ******************************************************************************/ +struct font_cache +{ + struct lru _lru; + int _size; + int _capacity; + short *_index; /* index of lru handles in char_code order */ +}; + +struct font_cache_entry +{ + unsigned short _char_code; + unsigned char width; + unsigned char bitmap[1]; /* place holder */ +}; + +/* void (*f) (void*, struct font_cache_entry*); */ +/* Create an auto sized font cache from buf */ +void font_cache_create( + struct font_cache* fcache, void* buf, int buf_size, int bitmap_bytes_size); +/* Get font cache entry */ +struct font_cache_entry* font_cache_get( + struct font_cache* fcache, + unsigned short char_code, + void (*callback) (struct font_cache_entry* p, void *callback_data), + void *callback_data); diff --git a/firmware/include/lru.h b/firmware/include/lru.h new file mode 100644 index 0000000000..8c74aa64ef --- /dev/null +++ b/firmware/include/lru.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2003 Tat Tang + * + * 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. + * + ****************************************************************************/ + +#ifndef LRU_H +#define LRU_H + +/******************************************************************************* + * LRU manager + ******************************************************************************/ +struct lru +{ + short _head; + short _tail; + short _size; + short _slot_size; + void *_base; +}; + +#define LRU_SLOT_OVERHEAD (2 * sizeof(short)) + +/* Create LRU list with specified size from buf. */ +void lru_create(struct lru* pl, void *buf, short size, short data_size); +/* Touch an entry. Moves handle to back of LRU list */ +void lru_touch(struct lru* pl, short handle); +/* Data */ +void *lru_data(struct lru* pl, short handle); +/* Traverse lru-wise */ +void lru_traverse(struct lru* pl, void (*callback)(void* data)); + +#endif /* LRU_H */ + diff --git a/firmware/include/rbunicode.h b/firmware/include/rbunicode.h new file mode 100644 index 0000000000..1d4bc43096 --- /dev/null +++ b/firmware/include/rbunicode.h @@ -0,0 +1,29 @@ +/* Some conversion functions for handling UTF-8 + * + * copyright Marcoen Hirschberg (2004,2005) + * + * I got all the info from: + * http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + * and + * http://en.wikipedia.org/wiki/Unicode + */ + +#define CODEPAGE_DIR "/.rockbox/codepages" + +#define MAX_CP_TABLE_SIZE 32768 + +#define MASK 0xC0 /* 11000000 */ +#define COMP 0x80 /* 10x */ + +extern int codepage; + +/* Encode a UCS value as UTF-8 and return a pointer after this UTF-8 char. */ +unsigned char* utf8encode(unsigned long ucs, unsigned char *utf8); +unsigned char* iso_decode(const unsigned char *latin1, unsigned char *utf8, int cp, int count); +unsigned char* utf16LEdecode(const unsigned char *utf16, unsigned char *utf8, unsigned int count); +unsigned char* utf16BEdecode(const unsigned char *utf16, unsigned char *utf8, unsigned int count); +unsigned char* utf16decode(const unsigned char *utf16, unsigned char *utf8, unsigned int count); +unsigned long utf8length(const unsigned char *utf8); +const unsigned char* utf8decode(const unsigned char *utf8, unsigned short *ucs); +void set_codepage(int cp); +int utf8seek(const unsigned char* utf8, int offset); diff --git a/firmware/lru.c b/firmware/lru.c new file mode 100644 index 0000000000..6834c3da1b --- /dev/null +++ b/firmware/lru.c @@ -0,0 +1,119 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2003 Tat Tang + * + * 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 "lru.h" + +struct lru_node +{ + short _next; + short _prev; + unsigned char data[1]; /* place holder */ +}; + +#define lru_node_p(pl, x) \ + ((struct lru_node*)(pl->_base + pl->_slot_size * x)) + +/******************************************************************************* + * lru_create + ******************************************************************************/ +void lru_create(struct lru* pl, void *buf, short size, short data_size) +{ + pl->_base = (unsigned char*) buf; + pl->_slot_size = data_size + LRU_SLOT_OVERHEAD; + + pl->_size = size; + + pl->_head = 0; + pl->_tail = pl->_size - 1; + + /* Initialise slots */ + int i; + for (i=0; i_size; i++) + { + lru_node_p(pl, i)->_next = i + 1; + lru_node_p(pl, i)->_prev = i - 1; + } + + /* Fix up head and tail to form circular buffer */ + lru_node_p(pl, 0)->_prev = pl->_tail; + lru_node_p(pl, pl->_tail)->_next = pl->_head; +} + +/******************************************************************************* + * lru_traverse + ******************************************************************************/ +void lru_traverse(struct lru* pl, void (*callback)(void* data)) +{ + short i; + struct lru_node* slot; + short loc = pl->_head; + + for (i = 0; i < pl->_size; i++) + { + slot = lru_node_p(pl, loc); + callback(slot->data); + loc = slot->_next; + } +} + +/******************************************************************************* + * lru_touch + ******************************************************************************/ +void lru_touch(struct lru* pl, short handle) +{ + if (handle == pl->_head) + { + pl->_head = lru_node_p(pl, pl->_head)->_next; + pl->_tail = lru_node_p(pl, pl->_tail)->_next; + return; + } + + if (handle == pl->_tail) + { + return; + } + + /* Remove current node from linked list */ + struct lru_node* curr_node = lru_node_p(pl, handle); + struct lru_node* prev_node = lru_node_p(pl, curr_node->_prev); + struct lru_node* next_node = lru_node_p(pl, curr_node->_next); + + prev_node->_next = curr_node->_next; + next_node->_prev = curr_node->_prev; + + /* insert current node at tail */ + struct lru_node* tail_node = lru_node_p(pl, pl->_tail); + short tail_node_next_handle = tail_node->_next; /* Bug fix */ + struct lru_node* tail_node_next = lru_node_p(pl, tail_node_next_handle); /* Bug fix */ + + curr_node->_next = tail_node->_next; + curr_node->_prev = pl->_tail; + tail_node_next->_prev = handle; /* Bug fix */ + tail_node->_next = handle; + + pl->_tail = handle; +} + +/******************************************************************************* + * lru_data + ******************************************************************************/ +void *lru_data(struct lru* pl, short handle) +{ + return lru_node_p(pl, handle)->data; +} + diff --git a/firmware/mpeg.c b/firmware/mpeg.c index a162887fd9..b5f1ef83c7 100644 --- a/firmware/mpeg.c +++ b/firmware/mpeg.c @@ -46,6 +46,9 @@ #include "file.h" #include "hwcompat.h" #endif /* !SIMULATOR */ +#ifdef HAVE_LCD_BITMAP +#include "lcd.h" +#endif #ifndef SIMULATOR extern unsigned long mas_version_code; @@ -916,6 +919,12 @@ static struct trackdata *add_track_to_tag_list(const char *filename) } track->mempos = audiobuf_write; track->id3.elapsed = 0; +#ifdef HAVE_LCD_BITMAP + int w, h; + lcd_getstringsize(track->id3.title, &w, &h); + lcd_getstringsize(track->id3.artist, &w, &h); + lcd_getstringsize(track->id3.album, &w, &h); +#endif track_write_idx = (track_write_idx+1) & MAX_TRACK_ENTRIES_MASK; debug_tags(); diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c index e061f7043b..a60a35c8cb 100644 --- a/firmware/powermgmt.c +++ b/firmware/powermgmt.c @@ -46,6 +46,9 @@ #elif HAVE_TLV320 #include "tlv320.h" #endif +#ifdef HAVE_LCD_BITMAP +#include "font.h" +#endif #include "logf.h" #include "lcd-remote.h" @@ -378,7 +381,7 @@ static void handle_auto_poweroff(void) * Inhibit shutdown as long as the charger is plugged in. If it is * unplugged, wait for a timeout period and then shut down. */ - if(charger_input_state == CHARGER) { + if(charger_input_state == CHARGER || audio_stat == AUDIO_STATUS_PLAY) { last_event_tick = current_tick; } #endif @@ -899,6 +902,9 @@ void shutdown_hw(void) } #endif audio_stop(); +#ifdef HAVE_LCD_BITMAP + glyph_cache_save(); +#endif ata_flush(); ata_spindown(1); while(ata_disk_is_active()) -- cgit v1.2.3