summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSuren A. Chilingaryan <csa@suren.me>2021-03-12 03:55:34 +0100
committerSuren A. Chilingaryan <csa@suren.me>2021-03-12 03:55:34 +0100
commit60bd665e74cfeeaf70882173a0dd56c883e2014a (patch)
tree8a0066bb9f0259becac5605641e8e1eed1ae0e89
downloadrusxmms2-60bd665e74cfeeaf70882173a0dd56c883e2014a.tar.gz
rusxmms2-60bd665e74cfeeaf70882173a0dd56c883e2014a.tar.bz2
rusxmms2-60bd665e74cfeeaf70882173a0dd56c883e2014a.tar.xz
rusxmms2-60bd665e74cfeeaf70882173a0dd56c883e2014a.zip
Added to git tree
-rw-r--r--3rdparty/mpg123/0050_all_libxmms-charset.patch191
-rw-r--r--3rdparty/mpg123/2011_all_mpg123-http-seek.patch201
-rw-r--r--3rdparty/mpg123/2012_all_mpg123-id3convert.patch41
-rw-r--r--3rdparty/mpg123/2013_all_mpg123-latin-id3.patch128
-rw-r--r--3rdparty/mpg123/2014_all_mpg123-encode-override.patch130
-rw-r--r--3rdparty/mpg123/2015_all_mpg123-id3v2edit.patch2247
-rw-r--r--3rdparty/mpg123/2020_all_mpg123-vorbis-ssl.patch766
-rw-r--r--ReadMe86
-rwxr-xr-xapply.sh50
-rw-r--r--configs/libtranslate/services.xml258
-rw-r--r--configs/xmms/config5
-rw-r--r--docs/Notes.txt27
-rw-r--r--docs/RusXMMS2.sxwbin0 -> 9047 bytes
-rw-r--r--docs/RusXMMS2.sxw.pdfbin0 -> 25988 bytes
-rw-r--r--docs/patches/amarok.txt27
-rw-r--r--fixes/libguess-fixes/README1
-rw-r--r--fixes/libguess-fixes/libguess-ds-cn.patch62
-rw-r--r--fixes/libtranslate-fixes/README38
-rw-r--r--fixes/libtranslate-fixes/libtranslate-0.99-charsetparse.diff47
-rw-r--r--fixes/libtranslate-fixes/libtranslate-0.99-condfix.diff38
-rw-r--r--fixes/libtranslate-fixes/libtranslate-ds-empty.patch17
-rw-r--r--fixes/libtranslate-fixes/libtranslate-ds-fixcharset.patch93
-rw-r--r--fixes/libtranslate-fixes/libtranslate-ds-memory.patch12
-rw-r--r--fixes/libtranslate-fixes/libtranslate-ds-promt.patch27
-rw-r--r--fixes/libtranslate-fixes/libtranslate-ds-timed.patch352
-rw-r--r--fixes/libtranslate-fixes/services.xml260
-rw-r--r--fixes/mac-fixes/README41
-rw-r--r--fixes/mac-fixes/mac-3.99-u4-b5-s4-ds-fix.patch12
-rw-r--r--fixes/taglib-fixes/taglib-1.4_wchar.diff31
-rw-r--r--fixes/xmms-fixes/gcc41/xmms-1.2.10-gcc41.patch10
-rw-r--r--fixes/xmms-fixes/gcc41/xmms-1.2.10-locale_fix.patch11
-rw-r--r--fixes/zinf-fixes/zinf-ds-noutfrecoding.patch20
-rw-r--r--fixes/zinf-fixes/zinf-ds.patch229
-rw-r--r--licenses.txt25
-rw-r--r--misc/brokentags.c16
-rw-r--r--patches/gftp/gftp-ds-rcc.patch822
-rw-r--r--patches/gstreamer/gst-plugins-good-0.10.16-rusxmms.diff224
-rw-r--r--patches/gstreamer/gstreamer14
-rw-r--r--patches/gstreamer/libid3_problems3
-rw-r--r--patches/id3lib/id3lib-ds-rcc.patch169
-rw-r--r--patches/libid3tag/README2
-rw-r--r--patches/libid3tag/libid3tag-0.15.1b-ds-rcc.patch300
-rw-r--r--patches/libtranslate/README.txt5
-rw-r--r--patches/libtranslate/libtranslate-ds2-memory.patch12
-rw-r--r--patches/libtranslate/libtranslate-ds3-charsetparse.patch47
-rw-r--r--patches/libtranslate/libtranslate-ds4-condfix.patch38
-rw-r--r--patches/libtranslate/libtranslate-ds5-empty.patch17
-rw-r--r--patches/libtranslate/libtranslate-ds6-promt.patch27
-rw-r--r--patches/libtranslate/libtranslate-ds7-fixcharset.patch93
-rw-r--r--patches/libtranslate/services.xml258
-rw-r--r--patches/libtranslate/soup22/libtranslate-ds1-timed.patch352
-rw-r--r--patches/libtranslate/soup24/libtranslate-ds1-timed24.patch360
-rw-r--r--patches/libtranslate/soup24/libtranslate-ds8-soup24inc.patch533
-rw-r--r--patches/mpg123/mpg123-ds-rcc.patch151
-rw-r--r--patches/mpg123/mpg123-ds-rcc1121.patch247
-rw-r--r--patches/mpg123/mpg123-ds-rcc173.patch271
-rw-r--r--patches/mpg123/mpg123-ds-rcc65.patch240
-rw-r--r--patches/p7zip/README13
-rw-r--r--patches/p7zip/p7zip_4.44-ds-rusxmms-full.patch314
-rw-r--r--patches/p7zip/p7zip_4.57-ds-rusxmms-full.patch419
-rw-r--r--patches/p7zip/p7zip_4.57-ds-rusxmms.patch336
-rw-r--r--patches/p7zip/p7zip_4.65-ds-rusxmms-full.patch425
-rw-r--r--patches/p7zip/p7zip_4.65-ds-rusxmms.patch305
-rw-r--r--patches/p7zip/p7zip_9.04-ds-rusxmms-full.patch414
-rw-r--r--patches/p7zip/p7zip_9.04-ds-rusxmms.patch294
-rw-r--r--patches/taglib/README36
-rw-r--r--patches/taglib/taglib-1.10-ds-rusxmms.patch1339
-rw-r--r--patches/taglib/taglib-1.11-ds-rusxmms.patch684
-rw-r--r--patches/taglib/taglib-1.4-ds-rusxmms.patch504
-rw-r--r--patches/taglib/taglib-1.5-ds-rusxmms.patch584
-rw-r--r--patches/taglib/taglib-1.6-ds-rusxmms.patch587
-rw-r--r--patches/taglib/taglib-1.7-ds-rusxmms-r2.patch506
-rw-r--r--patches/taglib/taglib-1.7-ds-rusxmms.patch515
-rw-r--r--patches/taglib/taglib-1.8-ds-rusxmms-r2.patch507
-rw-r--r--patches/taglib/taglib-1.8-ds-rusxmms-r9.patch609
-rw-r--r--patches/taglib/taglib-1.8-ds-rusxmms.patch519
-rw-r--r--patches/taglib/taglib-1.9.1-ds-rusxmms-enforce.patch20
-rw-r--r--patches/taglib/taglib-1.9.1-ds-rusxmms.patch676
-rw-r--r--patches/unzip/README30
-rw-r--r--patches/unzip/unzip-5.52-ds-rusxmms.patch159
-rw-r--r--patches/unzip/unzip-5.52-ds-rusxmms2.patch149
-rw-r--r--patches/unzip/unzip-ds-unixenc.patch9
-rw-r--r--patches/unzip/unzip60-ds-isprint.patch12
-rwxr-xr-xpatches/unzip/update_lazy9
-rwxr-xr-xpatches/unzip/update_shared9
-rw-r--r--patches/xmms-plugins/xmms-wma-csa1.tar.bz2bin0 -> 20480 bytes
-rw-r--r--patches/xmms/extra/xmms-ds-mpg123-wrongencoding.patch157
-rw-r--r--patches/xmms/plugins/xmms-ds-mpg123-editor-keys.patch164
-rw-r--r--patches/xmms/plugins/xmms-ds-mpg123-editor.patch202
-rw-r--r--patches/xmms/plugins/xmms-ds-mpg123.patch20
-rw-r--r--patches/xmms/plugins/xmms-ds-vorbis-editor-keys.patch78
-rw-r--r--patches/xmms/plugins/xmms-ds-vorbis-editor.patch51
-rw-r--r--patches/xmms/xmms-ds-playlist.patch526
-rw-r--r--patches/xmms/xmms-ds-rusxmms-charset.patch87
-rw-r--r--patches/xmms/xmms-ds-rusxmms.patch172
-rw-r--r--patches/xmms/xmms-ds-shade.patch194
-rw-r--r--patches/xmms/xmms-ds-textbox.patch31
-rw-r--r--source/rcc.c185
-rw-r--r--source/rcc.h32
-rw-r--r--source/rcc_langs.h120
100 files changed, 21686 insertions, 0 deletions
diff --git a/3rdparty/mpg123/0050_all_libxmms-charset.patch b/3rdparty/mpg123/0050_all_libxmms-charset.patch
new file mode 100644
index 0000000..94416dd
--- /dev/null
+++ b/3rdparty/mpg123/0050_all_libxmms-charset.patch
@@ -0,0 +1,191 @@
+diff -Naur xmms-1.2.10-20041012/libxmms/charset.c xmms-1.2.10-20041012.convert/libxmms/charset.c
+--- xmms-1.2.10-20041012/libxmms/charset.c 2004-10-13 01:03:03.258234924 -0700
++++ xmms-1.2.10-20041012.convert/libxmms/charset.c 2004-10-24 23:49:42.083591275 -0700
+@@ -22,7 +22,6 @@
+
+ #include "charset.h"
+
+-
+ char* xmms_charset_get_current(void)
+ {
+ char *charset = getenv("CHARSET");
+@@ -38,6 +37,18 @@
+ return charset;
+ }
+
++static size_t utf16_strlen(const char *string)
++{
++ size_t len = 0;
++
++ if (!string)
++ return 0;
++
++ while (*(string + len) != 0 || *(string + len + 1) != 0)
++ len += 2;
++
++ return len;
++}
+
+ #ifdef HAVE_ICONV
+ char* xmms_charset_convert(const char *string, size_t insize, char *from, char *to)
+@@ -108,15 +119,55 @@
+ {
+ if (!string)
+ return NULL;
++
+ return xmms_charset_convert(string, strlen(string), "UTF-8", NULL);
+ }
+
++char *xmms_charset_from_utf16(const unsigned char *string)
++{
++ if (!string)
++ return NULL;
++
++ return xmms_charset_convert(string, utf16_strlen(string), "UTF-16", NULL);
++}
++
++char *xmms_charset_from_utf16be(const unsigned char *string)
++{
++ if (!string)
++ return NULL;
++
++ return xmms_charset_convert(string, utf16_strlen(string), "UTF-16BE", NULL);
++}
++
++char* xmms_charset_from_latin1(const char *string)
++{
++ char *to = xmms_charset_get_current();
++
++ if (!string)
++ return NULL;
++
++ if (!strcmp(to, "UTF-8"))
++ return xmms_charset_convert(string, strlen(string), "ISO-8859-1", to);
++ else
++ return g_strdup(string);
++}
++
+ #else
+
+ char* xmms_charset_convert(const char *string, size_t insize, char *from, char *to)
+ {
++ if (!string)
++ return NULL;
++
+ if (!strcmp(from, "UTF-8") && !to)
+ return xmms_charset_from_utf8(string);
++
++ if (!strcmp(from, "UTF-16") && !to)
++ return xmms_charset_from_utf16(string);
++
++ if (!strcmp(from, "UTF-16BE") && !to)
++ return xmms_charset_from_utf16be(string);
++
+ return g_strdup(string);
+ }
+
+@@ -155,11 +206,83 @@
+ return ascii;
+ }
+
++static char* utf16_to_ascii(const unsigned char *utf16, int le)
++{
++ char *ascii;
++ unsigned int i, len, c;
++
++ if (!utf16)
++ return NULL;
++
++ len = utf16_strlen(utf16) / 2 + 1;
++
++ ascii = g_malloc(len + 1);
++
++ for (i = 0, c = 0; i < len; i++)
++ {
++ guint16 uc;
++ int o = i << 1;
++
++ if (le)
++ uc = *(utf16 + o) | *(utf16 + o + 1) << 8;
++ else
++ uc = *(utf16 + o) << 8 | *(utf16 + o + 1);
++
++ /* Skip BOM and surrogate pairs */
++ if (uc == 0xfeff || (uc >= 0xd800 && uc <= 0xdfff))
++ continue;
++
++ if (uc < 0x80)
++ ascii[c] = uc;
++ else
++ ascii[c] = '?';
++ c++;
++ }
++
++ ascii[c] = 0;
++ return ascii;
++}
++
++char *xmms_charset_from_utf16(const unsigned char *string)
++{
++ int le = FALSE;
++ guint16 bom;
++
++ if (!string)
++ return NULL;
++
++ bom = *string << 8 | *(string + 1);
++
++ if (bom == 0xfffe)
++ le = TRUE;
++ else if (bom != 0xfeff)
++ return g_strdup("");
++
++ return utf16_to_ascii(string, le);
++}
++
++char *xmms_charset_from_utf16be(const unsigned char *string)
++{
++ if (!string)
++ return NULL;
++
++ return utf16_to_ascii(string, FALSE);
++}
++
++char* xmms_charset_from_latin1(const char *string)
++{
++ if (!string)
++ return NULL;
++
++ return g_strdup(string);
++}
++
+ #endif
+
+ char* xmms_charset_to_utf8(const char *string)
+ {
+ if (!string)
+ return NULL;
++
+ return xmms_charset_convert(string, strlen(string), NULL, "UTF-8");
+ }
+diff -Naur xmms-1.2.10-20041012/libxmms/charset.h xmms-1.2.10-20041012.convert/libxmms/charset.h
+--- xmms-1.2.10-20041012/libxmms/charset.h 2004-10-13 01:03:03.260234595 -0700
++++ xmms-1.2.10-20041012.convert/libxmms/charset.h 2004-10-23 08:54:11.421220833 -0700
+@@ -5,11 +5,16 @@
+ *
+ */
+
++#ifndef XMMS_CHARSET_H
++#define XMMS_CHARSET_H
+
+ char* xmms_charset_get_current(void);
+ char* xmms_charset_convert(const char *string, size_t insize, char *from, char *to);
+ char* xmms_charset_to_utf8(const char *string);
+ char* xmms_charset_from_utf8(const char *string);
++char* xmms_charset_from_utf16(const unsigned char *string);
++char* xmms_charset_from_utf16be(const unsigned char *string);
++char* xmms_charset_from_latin1(const char *string);
+
+-
++#endif /* XMMS_CHARSET_H */
+
diff --git a/3rdparty/mpg123/2011_all_mpg123-http-seek.patch b/3rdparty/mpg123/2011_all_mpg123-http-seek.patch
new file mode 100644
index 0000000..67ab4f5
--- /dev/null
+++ b/3rdparty/mpg123/2011_all_mpg123-http-seek.patch
@@ -0,0 +1,201 @@
+diff -dPNur xmms-1.2.11/Input/mpg123/common.c xmms-1.2.11-new/Input/mpg123/common.c
+--- xmms-1.2.11/Input/mpg123/common.c 2005-05-15 02:01:19.000000000 +0200
++++ xmms-1.2.11-new/Input/mpg123/common.c 2007-11-24 23:52:01.000000000 +0100
+@@ -148,19 +148,34 @@
+ int mpg123_stream_jump_to_frame(struct frame *fr, int frame)
+ {
+ if (!filept)
+- return -1;
+- mpg123_read_frame_init();
+- fseek(filept, frame * (fr->framesize + 4), SEEK_SET);
+- mpg123_read_frame(fr);
++ {
++ unsigned long r;
++
++ r = frame * (fr->framesize + 4);
++ mpg123_stream_close();
++ mpg123_open_stream(mpg123_filename, -1, r);
++ }
++ else
++ {
++ mpg123_read_frame_init();
++ fseek(filept, frame * (fr->framesize + 4), SEEK_SET);
++ mpg123_read_frame(fr);
++ }
+ return 0;
+ }
+
+ int mpg123_stream_jump_to_byte(struct frame *fr, int byte)
+ {
+ if (!filept)
+- return -1;
+- fseek(filept, byte, SEEK_SET);
+- mpg123_read_frame(fr);
++ {
++ mpg123_stream_close();
++ mpg123_open_stream(mpg123_filename, -1, (unsigned long)byte);
++ }
++ else
++ {
++ fseek(filept, byte, SEEK_SET);
++ mpg123_read_frame(fr);
++ }
+ return 0;
+ }
+
+@@ -446,14 +461,14 @@
+ return 1;
+ }
+
+-void mpg123_open_stream(char *bs_filenam, int fd)
++void mpg123_open_stream(char *bs_filenam, int fd, unsigned long range)
+ {
+ filept_opened = 1;
+ if (!strncasecmp(bs_filenam, "http://", 7))
+ {
+ filept = NULL;
+- mpg123_http_open(bs_filenam);
+ mpg123_info->filesize = 0;
++ mpg123_http_open(bs_filenam, range);
+ mpg123_info->network_stream = TRUE;
+ }
+ else
+diff -dPNur xmms-1.2.11/Input/mpg123/http.c xmms-1.2.11-new/Input/mpg123/http.c
+--- xmms-1.2.11/Input/mpg123/http.c 2007-11-16 22:51:24.000000000 +0100
++++ xmms-1.2.11-new/Input/mpg123/http.c 2007-11-24 23:52:01.000000000 +0100
+@@ -50,6 +50,7 @@
+ extern gboolean mpg123_stereo;
+
+ static gboolean prebuffering, going, eof = FALSE;
++static unsigned long range;
+ static gint sock, rd_index, wr_index, buffer_length, prebuffer_length;
+ static guint64 buffer_read = 0;
+ static gchar *buffer;
+@@ -326,7 +327,7 @@
+ static void *http_buffer_loop(void *arg)
+ {
+ gchar line[1024], *user, *pass, *host, *filename,
+- *status, *url, *temp, *file;
++ *status, *url, *temp, *temp2, *file;
+ gchar *chost;
+ gint cnt, written, error, port, cport;
+ socklen_t err_len;
+@@ -495,15 +496,22 @@
+ }
+ else
+ file = g_strconcat("/", filename, NULL);
+- temp = g_strdup_printf("GET %s HTTP/1.0\r\n"
++ if (range)
++ {
++ temp2 = g_strdup_printf("Range: bytes=%lu-\r\n", range);
++ } else
++ temp2 = NULL;
++ temp = g_strdup_printf("GET %s HTTP/1.1\r\n"
+ "Host: %s\r\n"
+ "User-Agent: %s/%s\r\n"
+- "%s%s%s%s\r\n",
++ "%s%s%s%s%s\r\n",
+ file, host, PACKAGE, VERSION,
+ proxy_auth ? proxy_auth : "", auth ? auth : "",
+ mpg123_cfg.cast_title_streaming ? "Icy-MetaData:1\r\n" : "",
+- mpg123_cfg.use_udp_channel ? udpspace : "");
+-
++ mpg123_cfg.use_udp_channel ? udpspace : "",
++ range ? temp2 : "");
++
++ g_free(temp2);
+ g_free(file);
+ if(proxy_auth)
+ g_free(proxy_auth);
+@@ -587,7 +595,9 @@
+ #endif
+ /* udp_serverport = atoi (line + 20); */
+ }
+-
++ if (!strncasecmp(line, "content-length:", 15)) {
++ mpg123_info->filesize = atoi(line + 15);
++ }
+ }
+ else
+ {
+@@ -719,7 +729,7 @@
+ pthread_exit(NULL);
+ }
+
+-int mpg123_http_open(gchar * _url)
++int mpg123_http_open(gchar * _url, unsigned long rng)
+ {
+ gchar *url;
+
+@@ -735,6 +745,7 @@
+ going = TRUE;
+ eof = FALSE;
+ buffer = g_malloc(buffer_length);
++ range = rng;
+
+ pthread_create(&thread, NULL, http_buffer_loop, url);
+
+diff -dPNur xmms-1.2.11/Input/mpg123/mpg123.c xmms-1.2.11-new/Input/mpg123/mpg123.c
+--- xmms-1.2.11/Input/mpg123/mpg123.c 2006-07-25 05:18:51.000000000 +0200
++++ xmms-1.2.11-new/Input/mpg123/mpg123.c 2007-11-24 23:52:01.000000000 +0100
+@@ -857,7 +857,7 @@
+
+ mpg123_read_frame_init();
+
+- mpg123_open_stream(filename, -1);
++ mpg123_open_stream(filename, -1, 0);
+ if (mpg123_info->eof || !mpg123_read_frame(&fr))
+ mpg123_info->eof = TRUE;
+ if (!mpg123_info->eof && mpg123_info->going)
+@@ -906,7 +906,7 @@
+ break;
+ }
+
+- if (!have_xing_header && strncasecmp(filename, "http://", 7))
++ if(!have_xing_header && mpg123_info->filesize != 0)
+ mpg123_info->num_frames = mpg123_calc_numframes(&fr);
+
+ memcpy(&fr, &temp_fr, sizeof(struct frame));
+@@ -918,11 +918,10 @@
+ mpg123_lsf = fr.lsf;
+ mpg123_mpeg25 = fr.mpeg25;
+ mpg123_mode = fr.mode;
+-
++ mpg123_length = mpg123_info->num_frames * mpg123_info->tpf * 1000;
++
+ if (strncasecmp(filename, "http://", 7))
+ {
+- mpg123_length =
+- mpg123_info->num_frames * mpg123_info->tpf * 1000;
+ if (!mpg123_title)
+ mpg123_title = get_song_title(NULL,filename);
+ }
+@@ -930,7 +929,6 @@
+ {
+ if (!mpg123_title)
+ mpg123_title = mpg123_http_get_title(filename);
+- mpg123_length = -1;
+ }
+ mpg123_ip.set_info(mpg123_title, mpg123_length,
+ mpg123_bitrate * 1000,
+diff -dPNur xmms-1.2.11/Input/mpg123/mpg123.h xmms-1.2.11-new/Input/mpg123/mpg123.h
+--- xmms-1.2.11/Input/mpg123/mpg123.h 2006-07-24 00:32:44.000000000 +0200
++++ xmms-1.2.11-new/Input/mpg123/mpg123.h 2007-11-24 23:52:01.000000000 +0100
+@@ -176,7 +176,7 @@
+
+ /* ------ Declarations from "http.c" ------ */
+
+-extern int mpg123_http_open(char *url);
++extern int mpg123_http_open(char *url, unsigned long rng);
+ int mpg123_http_read(gpointer data, gint length);
+ void mpg123_http_close(void);
+ char *mpg123_http_get_title(char * url);
+@@ -188,7 +188,7 @@
+ extern unsigned int mpg123_getbits(int);
+ extern unsigned int mpg123_getbits_fast(int);
+
+-extern void mpg123_open_stream(char *bs_filenam, int fd);
++extern void mpg123_open_stream(char *bs_filenam, int fd, unsigned long range);
+ extern int mpg123_head_check(unsigned long);
+ extern void mpg123_stream_close(void);
+
diff --git a/3rdparty/mpg123/2012_all_mpg123-id3convert.patch b/3rdparty/mpg123/2012_all_mpg123-id3convert.patch
new file mode 100644
index 0000000..8c1b76b
--- /dev/null
+++ b/3rdparty/mpg123/2012_all_mpg123-id3convert.patch
@@ -0,0 +1,41 @@
+diff -dPNur xmms-1.2.11/Input/mpg123/mpg123.c xmms-1.2.11-new/Input/mpg123/mpg123.c
+--- xmms-1.2.11/Input/mpg123/mpg123.c 2007-11-24 23:53:33.000000000 +0100
++++ xmms-1.2.11-new/Input/mpg123/mpg123.c 2007-11-24 23:55:43.000000000 +0100
+@@ -2,6 +2,7 @@
+ #include "id3_header.h"
+ #include "libxmms/configfile.h"
+ #include "libxmms/titlestring.h"
++#include "libxmms/charset.h"
+ #include <string.h>
+ #include <stdlib.h>
+ #include <pthread.h>
+@@ -400,6 +401,14 @@
+ return ext;
+ }
+
++static char *convert_id3v1_field(const char *v1field, unsigned len)
++{
++ char *tmp = g_strndup(v1field, len);
++ char *v2field = xmms_charset_from_latin1(g_strstrip(tmp));
++ g_free(tmp);
++ return v2field;
++}
++
+ /*
+ * Function id3v1_to_id3v2 (v1, v2)
+ *
+@@ -411,10 +420,10 @@
+ char *year;
+ struct id3v2tag_t *v2 = g_malloc0(sizeof (struct id3v2tag_t));
+
+- v2->title = g_strstrip(g_strndup(v1->title, 30));
+- v2->artist = g_strstrip(g_strndup(v1->artist, 30));
+- v2->album = g_strstrip(g_strndup(v1->album, 30));
+- v2->comment = g_strstrip(g_strndup(v1->u.v1_0.comment, 30));
++ v2->title = convert_id3v1_field(v1->title, 30);
++ v2->artist = convert_id3v1_field(v1->artist, 30);
++ v2->album = convert_id3v1_field(v1->album, 30);
++ v2->comment = convert_id3v1_field(v1->u.v1_0.comment, 30);
+ v2->genre = g_strstrip(g_strdup(mpg123_get_id3_genre(v1->genre)));
+
+ year = g_strndup(v1->year, 4);
diff --git a/3rdparty/mpg123/2013_all_mpg123-latin-id3.patch b/3rdparty/mpg123/2013_all_mpg123-latin-id3.patch
new file mode 100644
index 0000000..b1d33cd
--- /dev/null
+++ b/3rdparty/mpg123/2013_all_mpg123-latin-id3.patch
@@ -0,0 +1,128 @@
+diff -Naur xmms-1.2.10-20041012/Input/mpg123/Makefile.am xmms-1.2.10-20041012.id3latin/Input/mpg123/Makefile.am
+--- xmms-1.2.10-20041012/Input/mpg123/Makefile.am 2004-10-13 01:03:03.186246768 -0700
++++ xmms-1.2.10-20041012.id3latin/Input/mpg123/Makefile.am 2004-10-23 08:52:23.370409147 -0700
+@@ -15,7 +15,7 @@
+ dxhead.c dxhead.h \
+ id3.c id3.h \
+ id3_frame.c id3_frame_content.c id3_frame_text.c id3_frame_url.c \
+-id3_header.h id3_tag.c unicode.c
++id3_header.h id3_tag.c
+
+ if ARCH_X86
+
+diff -Naur xmms-1.2.10-20041012/Input/mpg123/id3_frame_text.c xmms-1.2.10-20041012.id3latin/Input/mpg123/id3_frame_text.c
+--- xmms-1.2.10-20041012/Input/mpg123/id3_frame_text.c 2004-10-13 01:03:03.201244300 -0700
++++ xmms-1.2.10-20041012.id3latin/Input/mpg123/id3_frame_text.c 2004-10-23 09:15:29.117825260 -0700
+@@ -60,13 +60,13 @@
+ switch (encoding)
+ {
+ case ID3_ENCODING_ISO_8859_1:
+- return g_strdup(text);
++ return xmms_charset_from_latin1(text);
+ case ID3_ENCODING_UTF8:
+ return xmms_charset_from_utf8(text);
+ case ID3_ENCODING_UTF16:
+- return convert_from_utf16(text);
++ return xmms_charset_from_utf16(text);
+ case ID3_ENCODING_UTF16BE:
+- return convert_from_utf16be(text);
++ return xmms_charset_from_utf16be(text);
+ default:
+ return NULL;
+ }
+diff -Naur xmms-1.2.10-20041012/Input/mpg123/unicode.c xmms-1.2.10-20041012.id3latin/Input/mpg123/unicode.c
+--- xmms-1.2.10-20041012/Input/mpg123/unicode.c 2004-10-13 01:03:03.215241997 -0700
++++ xmms-1.2.10-20041012.id3latin/Input/mpg123/unicode.c 1969-12-31 16:00:00.000000000 -0800
+@@ -1,92 +0,0 @@
+-/*
+- * Copyright (C) 2004 Haavard Kvaalen <havardk@xmms.org>
+- *
+- * Licensed under GNU GPL version 2.
+- *
+- */
+-#include "config.h"
+-
+-#include <stdlib.h>
+-#include <glib.h>
+-
+-#include "libxmms/charset.h"
+-
+-size_t utf16_strlen(const char *string)
+-{
+- size_t len = 0;
+-
+- while (*(string + len) != 0 || *(string + len + 1) != 0)
+- len += 2;
+-
+- return len;
+-}
+-
+-#ifdef HAVE_ICONV
+-
+-char *convert_from_utf16(const unsigned char *utf16)
+-{
+- return xmms_charset_convert(utf16, utf16_strlen(utf16), "UTF-16", NULL);
+-}
+-
+-char *convert_from_utf16be(const unsigned char *utf16)
+-{
+- return xmms_charset_convert(utf16, utf16_strlen(utf16), "UTF-16BE", NULL);
+-}
+-
+-
+-#else
+-
+-
+-static char* to_ascii(const unsigned char *utf16, int le)
+-{
+- char *ascii;
+- unsigned int i, len, c;
+-
+- len = utf16_strlen(utf16) / 2 + 1;
+-
+- ascii = g_malloc(len + 1);
+-
+- for (i = 0, c = 0; i < len; i++)
+- {
+- guint16 uc;
+- int o = i << 1;
+-
+- if (le)
+- uc = *(utf16 + o) | *(utf16 + o + 1) << 8;
+- else
+- uc = *(utf16 + o) << 8 | *(utf16 + o + 1);
+-
+- /* Skip BOM and surrogate pairs */
+- if (uc == 0xfeff || (uc >= 0xd800 && uc <= 0xdfff))
+- continue;
+-
+- if (uc < 0x80)
+- ascii[c] = uc;
+- else
+- ascii[c] = '?';
+- c++;
+- }
+-
+- ascii[c] = 0;
+- return ascii;
+-}
+-
+-char *convert_from_utf16(const unsigned char *utf16)
+-{
+- int le = FALSE;
+- guint16 bom = *utf16 << 8 | *(utf16 + 1);
+-
+- if (bom == 0xfffe)
+- le = TRUE;
+- else if (bom != 0xfeff)
+- return g_strdup("");
+-
+- return to_ascii(utf16, le);
+-}
+-
+-char *convert_from_utf16be(const unsigned char *utf16)
+-{
+- return to_ascii(utf16, FALSE);
+-}
+-
+-#endif
diff --git a/3rdparty/mpg123/2014_all_mpg123-encode-override.patch b/3rdparty/mpg123/2014_all_mpg123-encode-override.patch
new file mode 100644
index 0000000..5c27f66
--- /dev/null
+++ b/3rdparty/mpg123/2014_all_mpg123-encode-override.patch
@@ -0,0 +1,130 @@
+diff -dPNur xmms-1.2.11/Input/mpg123/configure.c xmms-1.2.11-new/Input/mpg123/configure.c
+--- xmms-1.2.11/Input/mpg123/configure.c 2005-05-15 02:01:19.000000000 +0200
++++ xmms-1.2.11-new/Input/mpg123/configure.c 2007-11-24 23:57:29.000000000 +0100
+@@ -27,6 +27,7 @@
+ static GtkWidget *streaming_proxy_hbox, *streaming_proxy_auth_hbox, *streaming_save_dirbrowser;
+ static GtkWidget *streaming_save_hbox, *title_id3_box, *title_tag_desc;
+ static GtkWidget *title_override, *title_id3_entry, *title_id3v2_disable;
++static GtkWidget *id3v2_encoding_override, *id3v2_encoding_box, *encoding_entry;
+
+ MPG123Config mpg123_cfg;
+
+@@ -105,6 +106,8 @@
+ mpg123_cfg.disable_id3v2 = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(title_id3v2_disable));
+ g_free(mpg123_cfg.id3_format);
+ mpg123_cfg.id3_format = g_strdup(gtk_entry_get_text(GTK_ENTRY(title_id3_entry)));
++ mpg123_cfg.id3v2_encoding_override = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(id3v2_encoding_override));
++ mpg123_cfg.id3v2_default_encoding = g_strdup(gtk_entry_get_text(GTK_ENTRY(encoding_entry)));
+
+ filename = g_strconcat(g_get_home_dir(), "/.xmms/config", NULL);
+ cfg = xmms_cfg_open_file(filename);
+@@ -134,6 +137,8 @@
+ xmms_cfg_write_boolean(cfg, "MPG123", "title_override", mpg123_cfg.title_override);
+ xmms_cfg_write_boolean(cfg, "MPG123", "disable_id3v2", mpg123_cfg.disable_id3v2);
+ xmms_cfg_write_string(cfg, "MPG123", "id3_format", mpg123_cfg.id3_format);
++ xmms_cfg_write_boolean(cfg, "MPG123", "id3v2_encoding_override", mpg123_cfg.id3v2_encoding_override);
++ xmms_cfg_write_string(cfg, "MPG123", "id3v2_default_encoding", mpg123_cfg.id3v2_default_encoding);
+ xmms_cfg_write_int(cfg, "MPG123", "detect_by", mpg123_cfg.detect_by);
+ #ifdef USE_SIMD
+ xmms_cfg_write_int(cfg, "MPG123", "default_synth", mpg123_cfg.default_synth);
+@@ -212,6 +217,13 @@
+ gtk_widget_set_sensitive(title_tag_desc, override);
+ }
+
++static void id3v2_encoding_override_cb(GtkWidget * w, gpointer data)
++{
++ gboolean override;
++ override = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(id3v2_encoding_override));
++ gtk_widget_set_sensitive(id3v2_encoding_box, override);
++}
++
+ static void configure_destroy(GtkWidget * w, gpointer data)
+ {
+ if (streaming_save_dirbrowser)
+@@ -230,6 +242,7 @@
+ GtkWidget *streaming_save_label, *streaming_save_browse;
+ GtkWidget *streaming_cast_frame, *streaming_cast_vbox;
+ GtkWidget *title_frame, *title_id3_vbox, *title_id3_label;
++ GtkWidget *title_id3_label2;
+ GtkWidget *bbox, *ok, *cancel;
+
+ char *temp;
+@@ -576,6 +589,23 @@
+ title_tag_desc = xmms_titlestring_descriptions("pafFetnygc", 2);
+ gtk_widget_set_sensitive(title_tag_desc, mpg123_cfg.title_override);
+ gtk_box_pack_start(GTK_BOX(title_id3_vbox), title_tag_desc, FALSE, FALSE, 0);
++
++ id3v2_encoding_override = gtk_check_button_new_with_label(_("Override default ID3V2 encoding"));
++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(id3v2_encoding_override), mpg123_cfg.id3v2_encoding_override);
++ gtk_signal_connect(GTK_OBJECT(id3v2_encoding_override), "clicked", id3v2_encoding_override_cb, NULL);
++ gtk_box_pack_start(GTK_BOX(title_id3_vbox), id3v2_encoding_override, FALSE, FALSE, 0);
++
++ id3v2_encoding_box = gtk_hbox_new(FALSE, 5);
++ gtk_widget_set_sensitive(id3v2_encoding_box, mpg123_cfg.id3v2_encoding_override);
++ gtk_box_pack_start(GTK_BOX(title_id3_vbox), id3v2_encoding_box, FALSE, FALSE, 0);
++
++ title_id3_label2 = gtk_label_new(_("Encoding name:"));
++ gtk_box_pack_start(GTK_BOX(id3v2_encoding_box), title_id3_label2, FALSE, FALSE, 0);
++
++ encoding_entry = gtk_entry_new();
++ gtk_entry_set_text(GTK_ENTRY(encoding_entry), mpg123_cfg.id3v2_default_encoding);
++ gtk_box_pack_start(GTK_BOX(id3v2_encoding_box), encoding_entry, TRUE, TRUE, 0);
++
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), title_frame, gtk_label_new(_("Title")));
+
+ bbox = gtk_hbutton_box_new();
+diff -dPNur xmms-1.2.11/Input/mpg123/id3_frame_text.c xmms-1.2.11-new/Input/mpg123/id3_frame_text.c
+--- xmms-1.2.11/Input/mpg123/id3_frame_text.c 2007-11-24 23:57:15.000000000 +0100
++++ xmms-1.2.11-new/Input/mpg123/id3_frame_text.c 2007-11-24 23:57:29.000000000 +0100
+@@ -29,6 +29,8 @@
+
+ #include "libxmms/charset.h"
+
++/* For extern mpg123_cfg */
++#include "mpg123.h"
+
+ /* Get size of string in bytes including null. */
+ guint id3_string_size(guint8 encoding, const char* text)
+@@ -60,6 +62,9 @@
+ switch (encoding)
+ {
+ case ID3_ENCODING_ISO_8859_1:
++ if (mpg123_cfg.id3v2_encoding_override) {
++ return xmms_charset_convert(text, strlen(text), mpg123_cfg.id3v2_default_encoding, NULL);
++ }
+ return xmms_charset_from_latin1(text);
+ case ID3_ENCODING_UTF8:
+ return xmms_charset_from_utf8(text);
+diff -dPNur xmms-1.2.11/Input/mpg123/mpg123.c xmms-1.2.11-new/Input/mpg123/mpg123.c
+--- xmms-1.2.11/Input/mpg123/mpg123.c 2007-11-24 23:56:41.000000000 +0100
++++ xmms-1.2.11-new/Input/mpg123/mpg123.c 2007-11-24 23:57:29.000000000 +0100
+@@ -171,6 +171,7 @@
+ mpg123_cfg.use_udp_channel = FALSE;
+ mpg123_cfg.title_override = FALSE;
+ mpg123_cfg.disable_id3v2 = FALSE;
++ mpg123_cfg.id3v2_encoding_override = FALSE;
+ mpg123_cfg.detect_by = DETECT_EXTENSION;
+ mpg123_cfg.default_synth = SYNTH_AUTO;
+
+@@ -199,6 +200,9 @@
+ xmms_cfg_read_boolean(cfg, "MPG123", "disable_id3v2", &mpg123_cfg.disable_id3v2);
+ if (!xmms_cfg_read_string(cfg, "MPG123", "id3_format", &mpg123_cfg.id3_format))
+ mpg123_cfg.id3_format = g_strdup("%p - %t");
++ xmms_cfg_read_boolean(cfg, "MPG123", "id3v2_encoding_override", &mpg123_cfg.id3v2_encoding_override);
++ if (!xmms_cfg_read_string(cfg, "MPG123", "id3v2_default_encoding", &mpg123_cfg.id3v2_default_encoding))
++ mpg123_cfg.id3_format = g_strdup("ISO-8859-1");
+ xmms_cfg_read_int(cfg, "MPG123", "detect_by", &mpg123_cfg.detect_by);
+ xmms_cfg_read_int(cfg, "MPG123", "default_synth", &mpg123_cfg.default_synth);
+
+diff -dPNur xmms-1.2.11/Input/mpg123/mpg123.h xmms-1.2.11-new/Input/mpg123/mpg123.h
+--- xmms-1.2.11/Input/mpg123/mpg123.h 2007-11-24 23:53:33.000000000 +0100
++++ xmms-1.2.11-new/Input/mpg123/mpg123.h 2007-11-24 23:57:29.000000000 +0100
+@@ -159,6 +159,8 @@
+ gboolean use_udp_channel;
+ gchar *id3_format;
+ gboolean title_override, disable_id3v2;
++ gboolean id3v2_encoding_override;
++ gchar *id3v2_default_encoding;
+ int detect_by;
+ int default_synth;
+ }
diff --git a/3rdparty/mpg123/2015_all_mpg123-id3v2edit.patch b/3rdparty/mpg123/2015_all_mpg123-id3v2edit.patch
new file mode 100644
index 0000000..b1d47a2
--- /dev/null
+++ b/3rdparty/mpg123/2015_all_mpg123-id3v2edit.patch
@@ -0,0 +1,2247 @@
+diff -dPNur xmms-1.2.11/Input/mpg123/common.c xmms-1.2.11-new/Input/mpg123/common.c
+--- xmms-1.2.11/Input/mpg123/common.c 2007-11-24 23:53:33.000000000 +0100
++++ xmms-1.2.11-new/Input/mpg123/common.c 2007-11-24 23:58:41.000000000 +0100
+@@ -303,8 +303,8 @@
+ mpg123_id3v2_destroy(tag);
+ id3_close(id3d);
+ }
+- g_free(id3buf);
+
++ g_free(id3buf);
+ return TRUE;
+ }
+
+diff -dPNur xmms-1.2.11/Input/mpg123/fileinfo.c xmms-1.2.11-new/Input/mpg123/fileinfo.c
+--- xmms-1.2.11/Input/mpg123/fileinfo.c 2005-05-21 20:05:56.000000000 +0200
++++ xmms-1.2.11-new/Input/mpg123/fileinfo.c 2007-11-24 23:58:41.000000000 +0100
+@@ -27,42 +27,96 @@
+ #include <gdk/gdkkeysyms.h>
+ #include "mpg123.h"
+
++#define MAX_STR_LEN 100
++#define MAX_ENTRY_LEN2 1023
++
+ static GtkWidget *window = NULL;
+-static GtkWidget *filename_entry, *id3_frame;
+-static GtkWidget *title_entry, *artist_entry, *album_entry, *year_entry;
+-static GtkWidget *tracknum_entry, *comment_entry, *genre_combo;
++static GtkWidget *notebook = NULL;
++static GtkWidget *filename_entry, *id3v1_frame, *id3v2_frame;
++static GtkWidget *v1_checkbox, *v2_checkbox;
++static GtkWidget *v1_title_entry, *v1_artist_entry, *v1_album_entry, *v1_year_entry, *v1_tracknum_entry, *v1_comment_entry;
++static GtkWidget *v2_title_entry, *v2_artist_entry, *v2_album_entry, *v2_year_entry, *v2_tracknum_entry, *v2_comment_entry,
++ *v2_composer_entry, *v2_orig_artist_entry, *v2_url_entry, *v2_encoded_by_entry;
++static GtkWidget *v1_genre_combo, *v2_genre_combo;
+ static GtkWidget *mpeg_level, *mpeg_bitrate, *mpeg_samplerate, *mpeg_flags;
+ static GtkWidget *mpeg_fileinfo;
+
++static GPtrArray *v1_labels_list = NULL, *v2_labels_list = NULL; // TODO: Where will these be freed?
+ static GList *genre_list;
+ struct genre_item {
+ const char *name;
+ int id;
+ };
+-static int current_genre;
++static int v1_current_genre;
++static int v2_current_genre;
+ static char *current_filename;
+
+ extern char *mpg123_filename;
+ extern int mpg123_bitrate, mpg123_frequency, mpg123_layer, mpg123_lsf, mpg123_mode;
+ extern gboolean mpg123_stereo, mpg123_mpeg25;
+
+-#define MAX_STR_LEN 100
+-
+-static void label_set_text(GtkWidget * label, char *str, ...)
++static void label_set_text(GtkWidget * label, const char *str, ...)
+ G_GNUC_PRINTF(2, 3);
+
+-static void set_entry_tag(GtkEntry * entry, char * tag, int length)
++static void set_entry_tag_v1(GtkEntry * entry, const char * tag, int length)
+ {
+ char *text = g_strchomp(g_strndup(tag, length));
+ gtk_entry_set_text(entry, text);
+ g_free(text);
+ }
+
+-static void get_entry_tag(GtkEntry * entry, char * tag, int length)
++static void get_entry_tag_v1(GtkEntry * entry, char * tag, int length)
+ {
+ strncpy(tag, gtk_entry_get_text(entry), length);
+ }
+
++void copy_entry_tag_v1(GtkEntry * src, GtkEntry * dest, int length)
++{
++ set_entry_tag_v1(dest, gtk_entry_get_text(src), length);
++ return;
++}
++
++static void set_entry_tag_v2(GtkEntry * entry, struct id3_tag* id3, guint32 frame_type)
++{
++ struct id3_frame* frame;
++
++ frame = id3_get_frame(id3, frame_type, 1);
++ if (frame != NULL)
++ {
++ char *text, *url, *comment;
++
++ text = id3_get_text(frame);
++ if (text != NULL)
++ {
++ gtk_entry_set_text(entry, text);
++ g_free(text);
++ return;
++ }
++
++ url = id3_get_url(frame);
++ if (url != NULL)
++ {
++ gtk_entry_set_text(entry, url);
++ g_free(url);
++ return;
++ }
++
++ comment = id3_get_comment(frame);
++ if (comment != NULL)
++ {
++ gtk_entry_set_text(entry, comment);
++ g_free(url);
++ return;
++ }
++ }
++}
++
++void copy_entry_tag_v2(GtkEntry * src, GtkEntry * dest)
++{
++ gtk_entry_set_text(dest, gtk_entry_get_text(src));
++ return;
++}
++
+ static int genre_find_index(GList *genre_list, int id)
+ {
+ int idx = 0;
+@@ -74,6 +128,24 @@
+ idx++;
+ genre_list = genre_list->next;
+ }
++ if (!genre_list)
++ return 0;
++ return idx;
++}
++
++static int genre_find_index_str(GList *genre_list, const char* str)
++{
++ int idx = 0;
++ while (genre_list)
++ {
++ struct genre_item *item = genre_list->data;
++ if (strcmp(item->name, str) == 0)
++ break;
++ idx++;
++ genre_list = genre_list->next;
++ }
++ if (!genre_list)
++ return 0;
+ return idx;
+ }
+
+@@ -83,9 +155,9 @@
+ return strcasecmp(ga->name, gb->name);
+ }
+
+-static void save_cb(GtkWidget * w, gpointer data)
++static void remove_id3v1(void)
+ {
+- int fd;
++ int fd, len;
+ struct id3v1tag_t tag;
+ char *msg = NULL;
+
+@@ -94,71 +166,36 @@
+
+ if ((fd = open(current_filename, O_RDWR)) != -1)
+ {
+- int tracknum;
+-
+- lseek(fd, -128, SEEK_END);
++ len = lseek(fd, -128, SEEK_END);
+ read(fd, &tag, sizeof (struct id3v1tag_t));
+
+ if (!strncmp(tag.tag, "TAG", 3))
+- lseek(fd, -128, SEEK_END);
+- else
+- lseek(fd, 0, SEEK_END);
+- tag.tag[0] = 'T';
+- tag.tag[1] = 'A';
+- tag.tag[2] = 'G';
+- get_entry_tag(GTK_ENTRY(title_entry), tag.title, 30);
+- get_entry_tag(GTK_ENTRY(artist_entry), tag.artist, 30);
+- get_entry_tag(GTK_ENTRY(album_entry), tag.album, 30);
+- get_entry_tag(GTK_ENTRY(year_entry), tag.year, 4);
+- tracknum = atoi(gtk_entry_get_text(GTK_ENTRY(tracknum_entry)));
+- if (tracknum > 0)
+ {
+- get_entry_tag(GTK_ENTRY(comment_entry),
+- tag.u.v1_1.comment, 28);
+- tag.u.v1_1.__zero = 0;
+- tag.u.v1_1.track_number = MIN(tracknum, 255);
++ if (ftruncate(fd, len))
++ msg = g_strdup_printf(_("%s\n"
++ "Unable to truncate file: %s"),
++ _("Couldn't remove tag!"),
++ strerror(errno));
+ }
+- else
+- get_entry_tag(GTK_ENTRY(comment_entry),
+- tag.u.v1_0.comment, 30);
+- tag.genre = current_genre;
+- if (write(fd, &tag, sizeof (tag)) != sizeof (tag))
+- msg = g_strdup_printf(_("%s\nUnable to write to file: %s"),
+- _("Couldn't write tag!"),
+- strerror(errno));
+ close(fd);
+ }
+ else
+ msg = g_strdup_printf(_("%s\nUnable to open file: %s"),
+- _("Couldn't write tag!"),
++ _("Couldn't remove tag!"),
+ strerror(errno));
+ if (msg)
+ {
+- GtkWidget *mwin = xmms_show_message(_("File Info"), msg,
+- _("OK"), FALSE, NULL, NULL);
++ GtkWidget *mwin = xmms_show_message(_("File Info"), msg, _("OK"),
++ FALSE, NULL, NULL);
+ gtk_window_set_transient_for(GTK_WINDOW(mwin),
+ GTK_WINDOW(window));
+ g_free(msg);
+ }
+- else
+- gtk_widget_destroy(window);
+-}
+-
+-static void label_set_text(GtkWidget * label, char *str, ...)
+-{
+- va_list args;
+- char tempstr[MAX_STR_LEN];
+-
+- va_start(args, str);
+- g_vsnprintf(tempstr, MAX_STR_LEN, str, args);
+- va_end(args);
+-
+- gtk_label_set_text(GTK_LABEL(label), tempstr);
+ }
+
+-static void remove_id3_cb(GtkWidget * w, gpointer data)
++static void save_cb(GtkWidget * w, gpointer data)
+ {
+- int fd, len;
++ int fd;
+ struct id3v1tag_t tag;
+ char *msg = NULL;
+
+@@ -167,36 +204,112 @@
+
+ if ((fd = open(current_filename, O_RDWR)) != -1)
+ {
+- len = lseek(fd, -128, SEEK_END);
+- read(fd, &tag, sizeof (struct id3v1tag_t));
++ if (!GTK_TOGGLE_BUTTON(v1_checkbox)->active) {
++ /* Try to save id3v1 tag */
++ int tracknum;
+
+- if (!strncmp(tag.tag, "TAG", 3))
+- {
+- if (ftruncate(fd, len))
+- msg = g_strdup_printf(
+- _("%s\n"
+- "Unable to truncate file: %s"),
+- _("Couldn't remove tag!"),
+- strerror(errno));
++ lseek(fd, -128, SEEK_END);
++ read(fd, &tag, sizeof (struct id3v1tag_t));
++
++ if (!strncmp(tag.tag, "TAG", 3))
++ lseek(fd, -128, SEEK_END);
++ else
++ lseek(fd, 0, SEEK_END);
++ tag.tag[0] = 'T';
++ tag.tag[1] = 'A';
++ tag.tag[2] = 'G';
++ get_entry_tag_v1(GTK_ENTRY(v1_title_entry), tag.title, 30);
++ get_entry_tag_v1(GTK_ENTRY(v1_artist_entry), tag.artist, 30);
++ get_entry_tag_v1(GTK_ENTRY(v1_album_entry), tag.album, 30);
++ get_entry_tag_v1(GTK_ENTRY(v1_year_entry), tag.year, 4);
++ tracknum = atoi(gtk_entry_get_text(GTK_ENTRY(v1_tracknum_entry)));
++ if (tracknum > 0)
++ {
++ get_entry_tag_v1(GTK_ENTRY(v1_comment_entry), tag.u.v1_1.comment, 28);
++ tag.u.v1_1.__zero = 0;
++ tag.u.v1_1.track_number = MIN(tracknum, 255);
++ }
++ else
++ get_entry_tag_v1(GTK_ENTRY(v1_comment_entry),
++ tag.u.v1_0.comment, 30);
++ tag.genre = v1_current_genre;
++ if (write(fd, &tag, sizeof (tag)) != sizeof (tag))
++ msg = g_strdup_printf(_("%s\nUnable to write to file: %s"),
++ _("Couldn't write tag!"),
++ strerror(errno));
++ } else {
++ /* Remove the id3v1 tag from the file */
++ remove_id3v1();
+ }
+- else
+- msg = strdup(_("No tag to remove!"));
+- close(fd);
++
++ if (!GTK_TOGGLE_BUTTON(v2_checkbox)->active) {
++ struct id3_tag* id3;
++
++ lseek(fd, SEEK_SET, 0);
++ id3 = id3_open_fd(fd, 0);
++ if (id3 == NULL)
++ id3 = id3_new();
++
++ if (id3 != NULL)
++ {
++ id3_set_text(id3_get_or_add_frame(id3, ID3_TIT2), gtk_entry_get_text(GTK_ENTRY(v2_title_entry)));
++ id3_set_text(id3_get_or_add_frame(id3, ID3_TPE1), gtk_entry_get_text(GTK_ENTRY(v2_artist_entry)));
++ id3_set_text(id3_get_or_add_frame(id3, ID3_TALB), gtk_entry_get_text(GTK_ENTRY(v2_album_entry)));
++ id3_set_text(id3_get_or_add_frame(id3, ID3_TYER), gtk_entry_get_text(GTK_ENTRY(v2_year_entry)));
++ id3_set_comment(id3_get_or_add_frame(id3, ID3_COMM), gtk_entry_get_text(GTK_ENTRY(v2_comment_entry)));
++ id3_set_text(id3_get_or_add_frame(id3, ID3_TRCK), gtk_entry_get_text(GTK_ENTRY(v2_tracknum_entry)));
++ if (v2_current_genre != 0xff)
++ {
++ /* Essentially the same behavior as Winamp2's ID3v2 tagger */
++ char genre[255];
++ snprintf(genre, sizeof(genre), "(%d)%s",
++ v2_current_genre,
++ gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(v2_genre_combo)->entry)));
++ id3_set_text(id3_get_or_add_frame(id3, ID3_TCON), genre);
++ }
++ else
++ {
++ id3_set_text(id3_get_or_add_frame(id3, ID3_TCON), gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(v2_genre_combo)->entry)));
++ }
++ id3_set_text(id3_get_or_add_frame(id3, ID3_TCOM), gtk_entry_get_text(GTK_ENTRY(v2_composer_entry)));
++ id3_set_url(id3_get_or_add_frame(id3, ID3_WCOM), gtk_entry_get_text(GTK_ENTRY(v2_url_entry)));
++ id3_set_text(id3_get_or_add_frame(id3, ID3_TOPE), gtk_entry_get_text(GTK_ENTRY(v2_orig_artist_entry)));
++ id3_set_text(id3_get_or_add_frame(id3, ID3_TENC), gtk_entry_get_text(GTK_ENTRY(v2_encoded_by_entry)));
++
++ id3_write_tag_filename(id3, current_filename);
++ }
++
++ } else {
++ id3_remove_tag_filename(current_filename);
++ }
++
++ if (fd)
++ close(fd);
+ }
+ else
+ msg = g_strdup_printf(_("%s\nUnable to open file: %s"),
+- _("Couldn't remove tag!"),
++ _("Couldn't write tag!"),
+ strerror(errno));
+ if (msg)
+ {
+- GtkWidget *mwin = xmms_show_message(_("File Info"), msg,
+- _("OK"), FALSE, NULL, NULL);
++ GtkWidget *mwin = xmms_show_message(_("File Info"), msg, _("OK"),
++ FALSE, NULL, NULL);
+ gtk_window_set_transient_for(GTK_WINDOW(mwin),
+ GTK_WINDOW(window));
+ g_free(msg);
+ }
+- else
+- gtk_widget_destroy(window);
++}
++
++static void label_set_text(GtkWidget * label, const char *str, ...)
++{
++ va_list args;
++ char tempstr[MAX_STR_LEN];
++
++ va_start(args, str);
++ g_vsnprintf(tempstr, MAX_STR_LEN, str, args);
++ va_end(args);
++
++ gtk_label_set_text(GTK_LABEL(label), tempstr);
+ }
+
+ static void set_mpeg_level_label(gboolean mpeg25, int lsf, int layer)
+@@ -219,28 +332,141 @@
+
+ static void file_info_http(char *filename)
+ {
+- gtk_widget_set_sensitive(id3_frame, FALSE);
++ gtk_widget_set_sensitive(id3v1_frame, FALSE);
++ gtk_widget_set_sensitive(id3v2_frame, FALSE);
+ if (mpg123_filename && !strcmp(filename, mpg123_filename) &&
+ mpg123_bitrate != 0)
+ {
+- set_mpeg_level_label(mpg123_mpeg25, mpg123_lsf, mpg123_layer);
++ set_mpeg_level_label(mpg123_mpeg25, mpg123_lsf,
++ mpg123_layer);
+ label_set_text(mpeg_bitrate, _("Bitrate: %d kb/s"),
+- mpg123_bitrate);
++ mpg123_bitrate);
+ label_set_text(mpeg_samplerate, _("Samplerate: %d Hz"),
+- mpg123_frequency);
++ mpg123_frequency);
+ label_set_text(mpeg_flags, "%s",
+- channel_mode_name(mpg123_mode));
++ channel_mode_name(mpg123_mode));
+ }
+ }
+
+-static void genre_selected(GtkList *list, GtkWidget *w, gpointer data)
++void copy_v2_to_v1_cb(GtkButton *button, gpointer user_data)
++{
++ copy_entry_tag_v1(GTK_ENTRY(v2_title_entry), GTK_ENTRY(v1_title_entry), 30);
++ copy_entry_tag_v1(GTK_ENTRY(v2_artist_entry), GTK_ENTRY(v1_artist_entry), 30);
++ copy_entry_tag_v1(GTK_ENTRY(v2_album_entry), GTK_ENTRY(v1_album_entry), 30);
++ copy_entry_tag_v1(GTK_ENTRY(v2_year_entry), GTK_ENTRY(v1_year_entry), 4);
++ copy_entry_tag_v1(GTK_ENTRY(v2_comment_entry), GTK_ENTRY(v1_comment_entry), 30);
++ copy_entry_tag_v1(GTK_ENTRY(v2_tracknum_entry), GTK_ENTRY(v1_tracknum_entry), 3);
++
++ gtk_list_select_item(GTK_LIST(GTK_COMBO(v1_genre_combo)->list), genre_find_index(genre_list, v2_current_genre));
++ return;
++}
++
++void copy_v1_to_v2_cb(GtkButton *button, gpointer user_data)
++{
++ copy_entry_tag_v2(GTK_ENTRY(v1_title_entry), GTK_ENTRY(v2_title_entry));
++ copy_entry_tag_v2(GTK_ENTRY(v1_artist_entry), GTK_ENTRY(v2_artist_entry));
++ copy_entry_tag_v2(GTK_ENTRY(v1_album_entry), GTK_ENTRY(v2_album_entry));
++ copy_entry_tag_v2(GTK_ENTRY(v1_year_entry), GTK_ENTRY(v2_year_entry));
++ copy_entry_tag_v2(GTK_ENTRY(v1_comment_entry), GTK_ENTRY(v2_comment_entry));
++ copy_entry_tag_v2(GTK_ENTRY(v1_tracknum_entry), GTK_ENTRY(v2_tracknum_entry));
++
++ gtk_list_select_item(GTK_LIST(GTK_COMBO(v2_genre_combo)->list), genre_find_index(genre_list, v1_current_genre));
++ return;
++}
++
++void v1_toggle_cb (GtkWidget *widget, gpointer data)
++{
++ int i = 0;
++ if (GTK_TOGGLE_BUTTON (widget)->active)
++ {
++ // If control reaches here, the toggle button is down
++ // Gray out labels
++ for (i = 0; i < v1_labels_list->len; i++) {
++ gtk_widget_set_sensitive(GTK_WIDGET( g_ptr_array_index(v1_labels_list, i) ), FALSE);
++ }
++ gtk_widget_set_sensitive(v1_title_entry, FALSE);
++ gtk_widget_set_sensitive(v1_artist_entry, FALSE);
++ gtk_widget_set_sensitive(v1_album_entry, FALSE);
++ gtk_widget_set_sensitive(v1_year_entry, FALSE);
++ gtk_widget_set_sensitive(v1_tracknum_entry, FALSE);
++ gtk_widget_set_sensitive(v1_comment_entry, FALSE);
++ gtk_widget_set_sensitive(v1_genre_combo, FALSE);
++ } else {
++
++ // If control reaches here, the toggle button is up
++ // Enable labels
++ for (i = 0; i < v1_labels_list->len; i++) {
++ gtk_widget_set_sensitive(GTK_WIDGET( g_ptr_array_index(v1_labels_list, i) ), TRUE);
++ }
++ gtk_widget_set_sensitive(v1_title_entry, TRUE);
++ gtk_widget_set_sensitive(v1_artist_entry, TRUE);
++ gtk_widget_set_sensitive(v1_album_entry, TRUE);
++ gtk_widget_set_sensitive(v1_year_entry, TRUE);
++ gtk_widget_set_sensitive(v1_tracknum_entry, TRUE);
++ gtk_widget_set_sensitive(v1_comment_entry, TRUE);
++ gtk_widget_set_sensitive(v1_genre_combo, TRUE);
++ }
++}
++
++void v2_toggle_cb (GtkWidget *widget, gpointer data)
++{
++ int i = 0;
++ if (GTK_TOGGLE_BUTTON (widget)->active)
++ {
++ // If control reaches here, the toggle button is down
++ // Gray out labels
++ for (i = 0; i < v2_labels_list->len; i++) {
++ gtk_widget_set_sensitive(GTK_WIDGET( g_ptr_array_index(v2_labels_list, i) ), FALSE);
++ }
++ gtk_widget_set_sensitive(v2_title_entry, FALSE);
++ gtk_widget_set_sensitive(v2_artist_entry, FALSE);
++ gtk_widget_set_sensitive(v2_album_entry, FALSE);
++ gtk_widget_set_sensitive(v2_year_entry, FALSE);
++ gtk_widget_set_sensitive(v2_tracknum_entry, FALSE);
++ gtk_widget_set_sensitive(v2_comment_entry, FALSE);
++ gtk_widget_set_sensitive(v2_composer_entry, FALSE);
++ gtk_widget_set_sensitive(v2_orig_artist_entry, FALSE);
++ gtk_widget_set_sensitive(v2_url_entry, FALSE);
++ gtk_widget_set_sensitive(v2_encoded_by_entry, FALSE);
++ gtk_widget_set_sensitive(v2_genre_combo, FALSE);
++ } else {
++
++ // If control reaches here, the toggle button is up
++ // Enable labels
++ for (i = 0; i < v2_labels_list->len; i++) {
++ gtk_widget_set_sensitive(GTK_WIDGET( g_ptr_array_index(v2_labels_list, i) ), TRUE);
++ }
++ gtk_widget_set_sensitive(v2_title_entry, TRUE);
++ gtk_widget_set_sensitive(v2_artist_entry, TRUE);
++ gtk_widget_set_sensitive(v2_album_entry, TRUE);
++ gtk_widget_set_sensitive(v2_year_entry, TRUE);
++ gtk_widget_set_sensitive(v2_tracknum_entry, TRUE);
++ gtk_widget_set_sensitive(v2_comment_entry, TRUE);
++ gtk_widget_set_sensitive(v2_composer_entry, TRUE);
++ gtk_widget_set_sensitive(v2_orig_artist_entry, TRUE);
++ gtk_widget_set_sensitive(v2_url_entry, TRUE);
++ gtk_widget_set_sensitive(v2_encoded_by_entry, TRUE);
++ gtk_widget_set_sensitive(v2_genre_combo, TRUE);
++ }
++}
++static void v1_genre_selected(GtkList *list, GtkWidget *w, gpointer data)
+ {
+ void * p;
+ p = gtk_object_get_data(GTK_OBJECT(w), "genre_id");
+ if (p != NULL)
+- current_genre = GPOINTER_TO_INT(p);
++ v1_current_genre = GPOINTER_TO_INT(p);
+ else
+- current_genre = 0;
++ v1_current_genre = 0;
++}
++
++static void v2_genre_selected(GtkList *list, GtkWidget *w, gpointer data)
++{
++ void * p;
++ p = gtk_object_get_data(GTK_OBJECT(w), "genre_id");
++ if (p != NULL)
++ v2_current_genre = GPOINTER_TO_INT(p);
++ else
++ v2_current_genre = 0;
+ }
+
+ static void genre_set_popdown(GtkWidget *combo, GList *genres)
+@@ -269,7 +495,7 @@
+ void mpg123_file_info_box(char *filename)
+ {
+ int i;
+- struct id3v1tag_t tag;
++ struct id3v1tag_t id3v1tag;
+ FILE *fh;
+ char *tmp, *title;
+ const char *emphasis[4];
+@@ -284,205 +510,397 @@
+
+ if (!window)
+ {
+- GtkWidget *vbox, *hbox, *left_vbox, *table;
+- GtkWidget *mpeg_frame, *mpeg_box;
+- GtkWidget *label, *filename_hbox;
+- GtkWidget *bbox, *save, *remove_id3, *cancel;
+-
++ GtkWidget *window_vbox,
++ *id3v1_vbox, *id3v2_vbox, *id3v1_frame_vbox, *id3v2_frame_vbox,
++ *mpeg_lvbox, *mpeg_rvbox, *mpeg_hbox, *mpeg_box, *mpeg_frame,
++ *bbox, *save, *close, *copy_to, *copy_from,
++ *table, *label, *filename_hbox;
++
++ v1_labels_list = g_ptr_array_new();
++ v2_labels_list = g_ptr_array_new();
++
+ window = gtk_window_new(GTK_WINDOW_DIALOG);
+- gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE);
+- gtk_signal_connect(GTK_OBJECT(window), "destroy",
+- gtk_widget_destroyed, &window);
+- gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
+- file_info_box_keypress_cb, NULL);
++ gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &window);
++ gtk_signal_connect(GTK_OBJECT(window), "key_press_event", file_info_box_keypress_cb, NULL);
+ gtk_container_set_border_width(GTK_CONTAINER(window), 10);
+
+- vbox = gtk_vbox_new(FALSE, 10);
+- gtk_container_add(GTK_CONTAINER(window), vbox);
+-
++ window_vbox = gtk_vbox_new(FALSE,10);
+ filename_hbox = gtk_hbox_new(FALSE, 5);
+- gtk_box_pack_start(GTK_BOX(vbox), filename_hbox,
+- FALSE, TRUE, 0);
++ gtk_box_pack_start(GTK_BOX(window_vbox), filename_hbox, FALSE, TRUE, 0);
+
+ label = gtk_label_new(_("Filename:"));
+- gtk_box_pack_start(GTK_BOX(filename_hbox), label,
+- FALSE, TRUE, 0);
++ gtk_box_pack_start(GTK_BOX(filename_hbox), label, FALSE, TRUE, 0);
+ filename_entry = xmms_entry_new();
+ gtk_editable_set_editable(GTK_EDITABLE(filename_entry), FALSE);
+- gtk_box_pack_start(GTK_BOX(filename_hbox),
+- filename_entry, TRUE, TRUE, 0);
+-
+- hbox = gtk_hbox_new(FALSE, 10);
+- gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
+-
+- left_vbox = gtk_vbox_new(FALSE, 10);
+- gtk_box_pack_start(GTK_BOX(hbox), left_vbox, FALSE, FALSE, 0);
++ gtk_box_pack_start(GTK_BOX(filename_hbox), filename_entry, TRUE, TRUE, 0);
+
+- id3_frame = gtk_frame_new(_("ID3 Tag:"));
+- gtk_box_pack_start(GTK_BOX(left_vbox), id3_frame,
+- FALSE, FALSE, 0);
++ /* Set up the genres list */
+
+- table = gtk_table_new(5, 5, FALSE);
++ if (!genre_list)
++ {
++ struct genre_item *item;
++
++ for (i = 0; i < GENRE_MAX; i++)
++ {
++ item = g_malloc(sizeof (*item));
++ item->name = gettext(mpg123_id3_genres[i]);
++ item->id = i;
++ genre_list = g_list_prepend(genre_list, item);
++ }
++ item = g_malloc(sizeof (*item));
++ item->name = "";
++ item->id = 0xff;
++ genre_list = g_list_prepend(genre_list, item);
++ genre_list = g_list_sort(genre_list, genre_comp_func);
++ }
++
++ notebook = gtk_notebook_new ();
++ gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
++
++ /* ID3v2 page */
++
++ id3v2_vbox = gtk_vbox_new(FALSE, 0);
++
++ id3v2_frame = gtk_frame_new("ID3v2 Information");
++ gtk_box_pack_start(GTK_BOX(id3v2_vbox), id3v2_frame, FALSE, FALSE, 0);
++
++ id3v2_frame_vbox = gtk_vbox_new(FALSE, 0);
++ gtk_container_add(GTK_CONTAINER(id3v2_frame), id3v2_frame_vbox);
++
++ table = gtk_table_new(6, 6, FALSE);
+ gtk_container_set_border_width(GTK_CONTAINER(table), 5);
+- gtk_container_add(GTK_CONTAINER(id3_frame), table);
++ gtk_widget_set_usize(GTK_WIDGET(table), 400, -1);
++ gtk_box_pack_start(GTK_BOX(id3v2_frame_vbox), table, FALSE, FALSE, 0);
+
+- label = gtk_label_new(_("Title:"));
++ v2_checkbox = gtk_check_button_new_with_label ("Disable ID3v2 Tag");
++ gtk_signal_connect(GTK_OBJECT(v2_checkbox), "toggled", GTK_SIGNAL_FUNC(v2_toggle_cb), NULL);
++ gtk_table_attach(GTK_TABLE(table), v2_checkbox, 1, 3, 0, 1, GTK_FILL, GTK_FILL, 0, 2);
++
++ label = gtk_label_new("Track number:");
++ g_ptr_array_add(v2_labels_list, (gpointer)label);
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+- gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,
+- GTK_FILL, GTK_FILL, 5, 5);
++ gtk_table_attach(GTK_TABLE(table), label, 3, 4, 0, 1,
++ GTK_FILL, GTK_FILL, 5,5);
+
+- title_entry = gtk_entry_new_with_max_length(30);
+- gtk_table_attach(GTK_TABLE(table), title_entry, 1, 4, 0, 1,
+- GTK_FILL | GTK_EXPAND | GTK_SHRINK,
+- GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5);
++ v2_tracknum_entry = gtk_entry_new_with_max_length(3);
++ gtk_widget_set_usize(v2_tracknum_entry, 20, -1);
++ gtk_table_attach(GTK_TABLE(table), v2_tracknum_entry, 4, 5, 0, 1,
++ GTK_FILL, GTK_FILL, 0, 2);
+
+- label = gtk_label_new(_("Artist:"));
++ label = gtk_label_new("Title:");
++ g_ptr_array_add(v2_labels_list, (gpointer)label);
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+- gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
+- GTK_FILL, GTK_FILL, 5, 5);
++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 5, 5);
+
+- artist_entry = gtk_entry_new_with_max_length(30);
+- gtk_table_attach(GTK_TABLE(table), artist_entry, 1, 4, 1, 2,
+- GTK_FILL | GTK_EXPAND | GTK_SHRINK,
+- GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5);
++ v2_title_entry = gtk_entry_new_with_max_length(MAX_ENTRY_LEN2);
++ gtk_table_attach(GTK_TABLE(table), v2_title_entry, 1, 5, 1, 2,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2);
+
+- label = gtk_label_new(_("Album:"));
++ label = gtk_label_new("Artist:");
++ g_ptr_array_add(v2_labels_list, (gpointer)label);
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3,
+- GTK_FILL, GTK_FILL, 5, 5);
++ GTK_FILL, GTK_FILL, 5, 5);
+
+- album_entry = gtk_entry_new_with_max_length(30);
+- gtk_table_attach(GTK_TABLE(table), album_entry, 1, 4, 2, 3,
+- GTK_FILL | GTK_EXPAND | GTK_SHRINK,
+- GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5);
++ v2_artist_entry = gtk_entry_new_with_max_length(MAX_ENTRY_LEN2);
++ gtk_table_attach(GTK_TABLE(table), v2_artist_entry, 1, 5, 2, 3,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2);
+
+- label = gtk_label_new(_("Comment:"));
++ label = gtk_label_new("Album:");
++ g_ptr_array_add(v2_labels_list, (gpointer)label);
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 3, 4,
+- GTK_FILL, GTK_FILL, 5, 5);
++ GTK_FILL, GTK_FILL, 5, 5);
+
+- comment_entry = gtk_entry_new_with_max_length(30);
+- gtk_table_attach(GTK_TABLE(table), comment_entry, 1, 4, 3, 4,
+- GTK_FILL | GTK_EXPAND | GTK_SHRINK,
+- GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5);
++ v2_album_entry = gtk_entry_new_with_max_length(MAX_ENTRY_LEN2);
++ gtk_table_attach(GTK_TABLE(table), v2_album_entry, 1, 5, 3, 4,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2);
+
+- label = gtk_label_new(_("Year:"));
++ label = gtk_label_new("Comment:");
++ g_ptr_array_add(v2_labels_list, (gpointer)label);
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 4, 5,
+- GTK_FILL, GTK_FILL, 5, 5);
++ GTK_FILL, GTK_FILL, 5, 5);
+
+- year_entry = gtk_entry_new_with_max_length(4);
+- gtk_widget_set_usize(year_entry, 40, -1);
+- gtk_table_attach(GTK_TABLE(table), year_entry, 1, 2, 4, 5,
+- GTK_FILL | GTK_EXPAND | GTK_SHRINK,
+- GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5);
++ v2_comment_entry = gtk_entry_new_with_max_length(MAX_ENTRY_LEN2);
++ gtk_table_attach(GTK_TABLE(table), v2_comment_entry, 1, 5, 4, 5,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2);
+
+- label = gtk_label_new(_("Track number:"));
++ label = gtk_label_new("Year:");
++ g_ptr_array_add(v2_labels_list, (gpointer)label);
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+- gtk_table_attach(GTK_TABLE(table), label, 2, 3, 4, 5,
+- GTK_FILL, GTK_FILL, 5, 5);
++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 5, 6,
++ GTK_FILL, GTK_FILL, 5, 5);
+
+- tracknum_entry = gtk_entry_new_with_max_length(3);
+- gtk_widget_set_usize(tracknum_entry, 40, -1);
+- gtk_table_attach(GTK_TABLE(table), tracknum_entry, 3, 4, 4, 5,
+- GTK_FILL | GTK_EXPAND | GTK_SHRINK,
+- GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5);
++ v2_year_entry = gtk_entry_new_with_max_length(4);
++ gtk_widget_set_usize(v2_year_entry, 45, -1);
++ gtk_table_attach(GTK_TABLE(table), v2_year_entry, 1, 2, 5, 6,
++ GTK_FILL, GTK_FILL, 0, 2);
+
+- label = gtk_label_new(_("Genre:"));
++ label = gtk_label_new("Genre:");
++ g_ptr_array_add(v2_labels_list, (gpointer)label);
++ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
++ gtk_table_attach(GTK_TABLE(table), label, 2, 3, 5, 6,
++ GTK_FILL, GTK_FILL, 5, 5);
++
++ v2_genre_combo = gtk_combo_new();
++ gtk_combo_set_value_in_list(GTK_COMBO(v2_genre_combo), FALSE, TRUE);
++
++ genre_set_popdown(v2_genre_combo, genre_list);
++ gtk_signal_connect(GTK_OBJECT(GTK_COMBO(v2_genre_combo)->list),
++ "select-child", v2_genre_selected, NULL);
++
++ gtk_table_attach(GTK_TABLE(table), v2_genre_combo, 3, 5, 5, 6,
++ GTK_FILL | GTK_SHRINK, GTK_FILL |
++ GTK_SHRINK, 0, 2);
++
++ label = gtk_label_new("Composer:");
++ g_ptr_array_add(v2_labels_list, (gpointer)label);
++ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 6, 7,
++ GTK_FILL, GTK_FILL, 5, 5);
++
++ v2_composer_entry = gtk_entry_new_with_max_length(MAX_ENTRY_LEN2);
++ gtk_table_attach(GTK_TABLE(table), v2_composer_entry, 1, 5, 6, 7,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2);
++
++ label = gtk_label_new("Orig. Artist:");
++ g_ptr_array_add(v2_labels_list, (gpointer)label);
++ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 7, 8,
++ GTK_FILL, GTK_FILL, 5, 5);
++
++ v2_orig_artist_entry = gtk_entry_new_with_max_length(MAX_ENTRY_LEN2);
++ gtk_table_attach(GTK_TABLE(table), v2_orig_artist_entry, 1, 5, 7, 8,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2);
++
++ label = gtk_label_new("URL:");
++ g_ptr_array_add(v2_labels_list, (gpointer)label);
++ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 8, 9,
++ GTK_FILL, GTK_FILL, 5, 5);
++
++ v2_url_entry = gtk_entry_new_with_max_length(MAX_ENTRY_LEN2);
++ gtk_table_attach(GTK_TABLE(table), v2_url_entry, 1, 5, 8, 9,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2);
++ label = gtk_label_new("Encoded By:");
++ g_ptr_array_add(v2_labels_list, (gpointer)label);
++ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 9, 10,
++ GTK_FILL, GTK_FILL, 5, 5);
++
++ v2_encoded_by_entry = gtk_entry_new_with_max_length(MAX_ENTRY_LEN2);
++ gtk_table_attach(GTK_TABLE(table), v2_encoded_by_entry, 1, 5, 9, 10,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2);
++
++ bbox = gtk_hbutton_box_new();
++ gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_START);
++ gtk_button_box_set_spacing (GTK_BUTTON_BOX (bbox), 0);
++ gtk_container_set_border_width(GTK_CONTAINER(bbox), 5);
++ gtk_box_pack_start(GTK_BOX(id3v2_frame_vbox), bbox, FALSE, FALSE, 0);
++
++ copy_to = gtk_button_new_with_label("ID3v2 to ID3v1");
++ gtk_signal_connect(GTK_OBJECT(copy_to), "clicked", GTK_SIGNAL_FUNC(copy_v2_to_v1_cb), NULL);
++ /* remove the next line to thicken the button width */
++ GTK_WIDGET_SET_FLAGS(copy_to, GTK_CAN_DEFAULT);
++ gtk_box_pack_start(GTK_BOX(bbox), copy_to, FALSE, TRUE, 0);
++
++ label = gtk_label_new ("ID3v2");
++ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), id3v2_vbox, label);
++
++ /* ID3v1 page */
++
++ id3v1_vbox = gtk_vbox_new(FALSE, 10);
++ id3v1_frame = gtk_frame_new("ID3v1 Information");
++ gtk_box_pack_start(GTK_BOX(id3v1_vbox), id3v1_frame, TRUE, TRUE, 0);
++
++ id3v1_frame_vbox = gtk_vbox_new(FALSE,10);
++ gtk_container_add(GTK_CONTAINER(id3v1_frame), id3v1_frame_vbox);
++
++ table = gtk_table_new(6, 6, FALSE);
++ gtk_container_set_border_width(GTK_CONTAINER(table), 5);
++ //gtk_widget_set_usize(GTK_WIDGET(table), 325, -1);
++ //gtk_container_add(GTK_CONTAINER(id3v1_frame), table);
++ gtk_box_pack_start(GTK_BOX(id3v1_frame_vbox), table, FALSE, FALSE, 0);
++
++ v1_checkbox = gtk_check_button_new_with_label ("Disable ID3v1 Tag");
++ gtk_signal_connect(GTK_OBJECT(v1_checkbox), "toggled", GTK_SIGNAL_FUNC(v1_toggle_cb), NULL);
++ gtk_table_attach(GTK_TABLE(table), v1_checkbox, 1, 3, 0, 1,
++ GTK_FILL, GTK_FILL, 0, 2);
++
++ label = gtk_label_new("Track number:");
++ g_ptr_array_add(v1_labels_list, (gpointer)label);
++ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
++ gtk_table_attach(GTK_TABLE(table), label, 3, 4, 0, 1,
++ GTK_FILL, GTK_FILL, 5,5);
++
++ v1_tracknum_entry = gtk_entry_new_with_max_length(3);
++ gtk_widget_set_usize(v1_tracknum_entry, 20, -1);
++ gtk_table_attach(GTK_TABLE(table), v1_tracknum_entry, 4, 5, 0, 1,
++ GTK_FILL, GTK_FILL, 0, 2);
++
++ label = gtk_label_new("Title:");
++ g_ptr_array_add(v1_labels_list, (gpointer)label);
++ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
++ GTK_FILL, GTK_FILL, 5, 5);
++
++ v1_title_entry = gtk_entry_new_with_max_length(30);
++ gtk_table_attach(GTK_TABLE(table), v1_title_entry, 1, 5, 1, 2,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2);
++
++ label = gtk_label_new("Artist:");
++ g_ptr_array_add(v1_labels_list, (gpointer)label);
++ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3,
++ GTK_FILL, GTK_FILL, 5, 5);
++
++ v1_artist_entry = gtk_entry_new_with_max_length(30);
++ gtk_table_attach(GTK_TABLE(table), v1_artist_entry, 1, 5, 2, 3,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2);
++
++ label = gtk_label_new("Album:");
++ g_ptr_array_add(v1_labels_list, (gpointer)label);
++ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 3, 4,
++ GTK_FILL, GTK_FILL, 5, 5);
++
++ v1_album_entry = gtk_entry_new_with_max_length(30);
++ gtk_table_attach(GTK_TABLE(table), v1_album_entry, 1, 5, 3, 4,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2);
++
++ label = gtk_label_new("Comment:");
++ g_ptr_array_add(v1_labels_list, (gpointer)label);
++ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
++ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 4, 5,
++ GTK_FILL, GTK_FILL, 5, 5);
++
++ v1_comment_entry = gtk_entry_new_with_max_length(30);
++ gtk_table_attach(GTK_TABLE(table), v1_comment_entry, 1, 5, 4, 5,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK,
++ GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2);
++
++ label = gtk_label_new("Year:");
++ g_ptr_array_add(v1_labels_list, (gpointer)label);
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 5, 6,
+- GTK_FILL, GTK_FILL, 5, 5);
++ GTK_FILL, GTK_FILL, 5, 5);
+
+- genre_combo = gtk_combo_new();
+- gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(genre_combo)->entry),
+- FALSE);
+- if (!genre_list)
+- {
+- struct genre_item *item;
++ v1_year_entry = gtk_entry_new_with_max_length(4);
++ gtk_widget_set_usize(v1_year_entry, 45, -1);
++ gtk_table_attach(GTK_TABLE(table), v1_year_entry, 1, 2, 5, 6,
++ GTK_FILL, GTK_FILL, 0, 2);
+
+- for (i = 0; i < GENRE_MAX; i++)
+- {
+- item = g_malloc(sizeof (*item));
+- item->name = gettext(mpg123_id3_genres[i]);
+- item->id = i;
+- genre_list = g_list_prepend(genre_list, item);
+- }
+- item = g_malloc(sizeof (*item));
+- item->name = "";
+- item->id = 0xff;
+- genre_list = g_list_prepend(genre_list, item);
+- genre_list = g_list_sort(genre_list, genre_comp_func);
+- }
+- genre_set_popdown(genre_combo, genre_list);
+- gtk_signal_connect(GTK_OBJECT(GTK_COMBO(genre_combo)->list),
+- "select-child", genre_selected, NULL);
++ label = gtk_label_new("Genre:");
++ g_ptr_array_add(v1_labels_list, (gpointer)label);
++ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
++ gtk_table_attach(GTK_TABLE(table), label, 2, 3, 5, 6,
++ GTK_FILL, GTK_FILL, 5, 5);
+
+- gtk_table_attach(GTK_TABLE(table), genre_combo, 1, 4, 5, 6,
+- GTK_FILL | GTK_EXPAND | GTK_SHRINK,
+- GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5);
++ v1_genre_combo = gtk_combo_new();
++ gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(v1_genre_combo)->entry), FALSE);
++ genre_set_popdown(v1_genre_combo, genre_list);
++ gtk_signal_connect(GTK_OBJECT(GTK_COMBO(v1_genre_combo)->list),
++ "select-child", v1_genre_selected, NULL);
++
++ gtk_table_attach(GTK_TABLE(table), v1_genre_combo, 3, 5, 5, 6,
++ GTK_FILL | GTK_SHRINK, GTK_FILL |
++ GTK_SHRINK, 0, 2);
+
+ bbox = gtk_hbutton_box_new();
+- gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox),
+- GTK_BUTTONBOX_END);
+- gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
+- gtk_box_pack_start(GTK_BOX(left_vbox), bbox, FALSE, FALSE, 0);
++ gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_START);
++ gtk_button_box_set_spacing (GTK_BUTTON_BOX (bbox), 0);
++ gtk_container_set_border_width(GTK_CONTAINER(bbox), 5);
++ gtk_box_pack_start(GTK_BOX(id3v1_frame_vbox), bbox, FALSE, FALSE, 0);
+
+- save = gtk_button_new_with_label(_("Save"));
+- gtk_signal_connect(GTK_OBJECT(save), "clicked", save_cb, NULL);
+- GTK_WIDGET_SET_FLAGS(save, GTK_CAN_DEFAULT);
+- gtk_box_pack_start(GTK_BOX(bbox), save, TRUE, TRUE, 0);
+- gtk_widget_grab_default(save);
++ copy_from = gtk_button_new_with_label("ID3v1 to ID3v2");
++ gtk_signal_connect(GTK_OBJECT(copy_from), "clicked", GTK_SIGNAL_FUNC(copy_v1_to_v2_cb), NULL);
++ // remove the next line to thicken the button width
++ GTK_WIDGET_SET_FLAGS(copy_from, GTK_CAN_DEFAULT);
++ gtk_box_pack_start(GTK_BOX(bbox), copy_from, FALSE, TRUE, 0);
+
+- remove_id3 = gtk_button_new_with_label(_("Remove ID3"));
+- gtk_signal_connect(GTK_OBJECT(remove_id3), "clicked",
+- remove_id3_cb, NULL);
+- GTK_WIDGET_SET_FLAGS(remove_id3, GTK_CAN_DEFAULT);
+- gtk_box_pack_start(GTK_BOX(bbox), remove_id3, TRUE, TRUE, 0);
++ label = gtk_label_new ("ID3v1");
++ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), id3v1_vbox, label);
+
+- cancel = gtk_button_new_with_label(_("Cancel"));
+- gtk_signal_connect_object(GTK_OBJECT(cancel), "clicked",
+- gtk_widget_destroy, GTK_OBJECT(window));
+- GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT);
+- gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0);
++ /* MPEG info page */
+
+- mpeg_frame = gtk_frame_new(_("MPEG Info:"));
+- gtk_box_pack_start(GTK_BOX(hbox), mpeg_frame, FALSE, FALSE, 0);
++ mpeg_frame = gtk_frame_new("MPEG Information");
++ mpeg_hbox = gtk_hbox_new(FALSE,50);
++ gtk_container_add(GTK_CONTAINER(mpeg_frame), mpeg_hbox);
++
++ mpeg_lvbox = gtk_vbox_new(FALSE, 5);
++ gtk_container_set_border_width(GTK_CONTAINER(mpeg_lvbox), 10);
++ gtk_box_pack_start(GTK_BOX(mpeg_hbox), mpeg_lvbox, FALSE, FALSE, 0);
+
+ mpeg_box = gtk_vbox_new(FALSE, 5);
+- gtk_container_add(GTK_CONTAINER(mpeg_frame), mpeg_box);
++ gtk_box_pack_start(GTK_BOX(mpeg_hbox), mpeg_box, FALSE, FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(mpeg_box), 10);
+ gtk_box_set_spacing(GTK_BOX(mpeg_box), 0);
+
+ mpeg_level = gtk_label_new("");
+- gtk_widget_set_usize(mpeg_level, 120, -2);
+- gtk_misc_set_alignment(GTK_MISC(mpeg_level), 0, 0);
+- gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_level, FALSE, FALSE, 0);
+-
+- mpeg_bitrate = gtk_label_new("");
+- gtk_misc_set_alignment(GTK_MISC(mpeg_bitrate), 0, 0);
+- gtk_label_set_justify(GTK_LABEL(mpeg_bitrate),
+- GTK_JUSTIFY_LEFT);
+- gtk_box_pack_start(GTK_BOX(mpeg_box),
+- mpeg_bitrate, FALSE, FALSE, 0);
++ //gtk_widget_set_usize(mpeg_level, 120, -2);
++ gtk_label_set_justify (GTK_LABEL(mpeg_level), GTK_JUSTIFY_LEFT);
++ gtk_misc_set_alignment(GTK_MISC(mpeg_level), 0, 0.5);
++ gtk_box_pack_start(GTK_BOX(mpeg_lvbox), mpeg_level, FALSE, FALSE, 0);
+
+ mpeg_samplerate = gtk_label_new("");
+- gtk_misc_set_alignment(GTK_MISC(mpeg_samplerate), 0, 0);
+- gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_samplerate,
+- FALSE, FALSE, 0);
++ gtk_label_set_justify (GTK_LABEL(mpeg_samplerate), GTK_JUSTIFY_LEFT);
++ gtk_misc_set_alignment(GTK_MISC(mpeg_samplerate), 0, 0.5);
++ gtk_box_pack_start(GTK_BOX(mpeg_lvbox), mpeg_samplerate, FALSE, FALSE, 0);
++
++ mpeg_fileinfo = gtk_label_new("");
++ gtk_label_set_justify (GTK_LABEL(mpeg_fileinfo), GTK_JUSTIFY_LEFT);
++ gtk_misc_set_alignment(GTK_MISC(mpeg_fileinfo), 0, 0.5);
++ gtk_box_pack_start(GTK_BOX(mpeg_lvbox), mpeg_fileinfo, FALSE, FALSE, 0);
++
++ mpeg_rvbox = gtk_vbox_new(FALSE, 5);
++ gtk_box_pack_start(GTK_BOX(mpeg_hbox), mpeg_rvbox, FALSE, FALSE, 0);
++ gtk_container_set_border_width(GTK_CONTAINER(mpeg_rvbox), 10);
++
++ mpeg_bitrate = gtk_label_new("");
++ gtk_label_set_justify (GTK_LABEL(mpeg_bitrate), GTK_JUSTIFY_LEFT);
++ gtk_misc_set_alignment(GTK_MISC(mpeg_bitrate), 0, 0.5);
++ gtk_box_pack_start(GTK_BOX(mpeg_rvbox), mpeg_bitrate, FALSE, FALSE, 0);
+
+ mpeg_flags = gtk_label_new("");
+- gtk_misc_set_alignment(GTK_MISC(mpeg_flags), 0, 0);
+- gtk_label_set_justify(GTK_LABEL(mpeg_flags), GTK_JUSTIFY_LEFT);
+- gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_flags,
+- FALSE, FALSE, 0);
++ gtk_label_set_justify (GTK_LABEL(mpeg_flags), GTK_JUSTIFY_LEFT);
++ gtk_misc_set_alignment(GTK_MISC(mpeg_flags), 0, 0.5);
++ gtk_box_pack_start(GTK_BOX(mpeg_rvbox), mpeg_flags, FALSE, FALSE, 0);
+
+- mpeg_fileinfo = gtk_label_new("");
+- gtk_misc_set_alignment(GTK_MISC(mpeg_fileinfo), 0, 0);
+- gtk_label_set_justify(GTK_LABEL(mpeg_fileinfo),
+- GTK_JUSTIFY_LEFT);
+- gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_fileinfo,
+- FALSE, FALSE, 0);
++ label = gtk_label_new ("MPEG");
++ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), mpeg_frame, label);
++
++ /* add notebook to window vbox */
++ gtk_box_pack_start(GTK_BOX(window_vbox), notebook, FALSE, FALSE, 0);
++
++ /* add button box to window vbox */
++ bbox = gtk_hbutton_box_new();
++ gtk_box_pack_start(GTK_BOX(window_vbox), bbox, FALSE, FALSE, 0);
+
++ gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
++ gtk_button_box_set_spacing (GTK_BUTTON_BOX (bbox), 5);
++
++ save = gtk_button_new_with_label("Save");
++ gtk_signal_connect(GTK_OBJECT(save), "clicked", GTK_SIGNAL_FUNC(save_cb), NULL);
++ gtk_box_pack_start(GTK_BOX(bbox), save, TRUE, TRUE, 5);
++
++ close = gtk_button_new_with_label("Close");
++ gtk_signal_connect_object(GTK_OBJECT(close), "clicked",
++ GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(window));
++ gtk_box_pack_start(GTK_BOX(bbox), close, TRUE, TRUE, 5);
++
++ gtk_container_add(GTK_CONTAINER(window), window_vbox);
+ gtk_widget_show_all(window);
+ }
+
+@@ -500,73 +918,128 @@
+ title = g_strdup(g_basename(filename));
+ if ((tmp = strrchr(title, '.')) != NULL)
+ *tmp = '\0';
+- gtk_entry_set_text(GTK_ENTRY(title_entry), title);
++ gtk_entry_set_text(GTK_ENTRY(v1_title_entry), title);
++ gtk_entry_set_text(GTK_ENTRY(v2_title_entry), title);
+ g_free(title);
+
+- gtk_entry_set_text(GTK_ENTRY(artist_entry), "");
+- gtk_entry_set_text(GTK_ENTRY(album_entry), "");
+- gtk_entry_set_text(GTK_ENTRY(year_entry), "");
+- gtk_entry_set_text(GTK_ENTRY(tracknum_entry), "");
+- gtk_entry_set_text(GTK_ENTRY(comment_entry), "");
+- gtk_list_select_item(GTK_LIST(GTK_COMBO(genre_combo)->list),
++ gtk_entry_set_text(GTK_ENTRY(v1_artist_entry), "");
++ gtk_entry_set_text(GTK_ENTRY(v1_album_entry), "");
++ gtk_entry_set_text(GTK_ENTRY(v1_year_entry), "");
++ gtk_entry_set_text(GTK_ENTRY(v1_tracknum_entry), "");
++ gtk_entry_set_text(GTK_ENTRY(v1_comment_entry), "");
++
++ gtk_list_select_item(GTK_LIST(GTK_COMBO(v1_genre_combo)->list),
+ genre_find_index(genre_list, 0xff));
++
++ gtk_entry_set_text(GTK_ENTRY(v2_artist_entry), "");
++ gtk_entry_set_text(GTK_ENTRY(v2_album_entry), "");
++ gtk_entry_set_text(GTK_ENTRY(v2_year_entry), "");
++ gtk_entry_set_text(GTK_ENTRY(v2_tracknum_entry), "");
++ gtk_entry_set_text(GTK_ENTRY(v2_comment_entry), "");
++ gtk_entry_set_text(GTK_ENTRY(v2_composer_entry), "");
++ gtk_entry_set_text(GTK_ENTRY(v2_orig_artist_entry), "");
++ gtk_entry_set_text(GTK_ENTRY(v2_url_entry), "");
++ gtk_entry_set_text(GTK_ENTRY(v2_encoded_by_entry), "");
++ gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(v2_genre_combo)->entry), "");
++
+ gtk_label_set_text(GTK_LABEL(mpeg_level), "MPEG ?, layer ?");
+ gtk_label_set_text(GTK_LABEL(mpeg_bitrate), "");
+ gtk_label_set_text(GTK_LABEL(mpeg_samplerate), "");
+ gtk_label_set_text(GTK_LABEL(mpeg_flags), "");
+ gtk_label_set_text(GTK_LABEL(mpeg_fileinfo), "");
+-
+ if (!strncasecmp(filename, "http://", 7))
+ {
+ file_info_http(filename);
+ return;
+ }
+
+- gtk_widget_set_sensitive(id3_frame, TRUE);
++ gtk_widget_set_sensitive(id3v1_frame, TRUE);
++ gtk_widget_set_sensitive(id3v2_frame, TRUE);
+
+ if ((fh = fopen(current_filename, "rb")) != NULL)
+ {
+ struct frame frm;
+ gboolean id3_found = FALSE;
++ struct id3_tag *id3 = NULL;
+ guint8 *buf;
+ double tpf;
+ int pos;
+ xing_header_t xing_header;
+ guint32 num_frames;
+-
+- fseek(fh, -sizeof (tag), SEEK_END);
+- if (fread(&tag, 1, sizeof (tag), fh) == sizeof (tag))
++ /*
++ * Try reading ID3v2 tag.
++ */
++ fseek(fh, 0, SEEK_SET);
++ id3 = id3_open_fp(fh, 0);
++ if (id3 != NULL)
+ {
+- if (!strncmp(tag.tag, "TAG", 3))
++ struct id3_frame* frame;
++
++ set_entry_tag_v2(GTK_ENTRY(v2_title_entry), id3, ID3_TIT2);
++ set_entry_tag_v2(GTK_ENTRY(v2_artist_entry), id3, ID3_TPE1);
++ set_entry_tag_v2(GTK_ENTRY(v2_album_entry), id3, ID3_TALB);
++ set_entry_tag_v2(GTK_ENTRY(v2_comment_entry), id3, ID3_COMM);
++ set_entry_tag_v2(GTK_ENTRY(v2_composer_entry), id3, ID3_TCOM);
++ set_entry_tag_v2(GTK_ENTRY(v2_orig_artist_entry), id3, ID3_TOPE);
++ set_entry_tag_v2(GTK_ENTRY(v2_url_entry), id3, ID3_WCOM);
++ set_entry_tag_v2(GTK_ENTRY(v2_encoded_by_entry), id3, ID3_TENC);
++ set_entry_tag_v2(GTK_ENTRY(v2_tracknum_entry), id3, ID3_TRCK);
++ set_entry_tag_v2(GTK_ENTRY(v2_year_entry), id3, ID3_TYER);
++
++ frame = id3_get_frame(id3, ID3_TCON, 1);
++ if (frame != NULL)
+ {
+- id3_found = TRUE;
+- set_entry_tag(GTK_ENTRY(title_entry),
+- tag.title, 30);
+- set_entry_tag(GTK_ENTRY(artist_entry),
+- tag.artist, 30);
+- set_entry_tag(GTK_ENTRY(album_entry),
+- tag.album, 30);
+- set_entry_tag(GTK_ENTRY(year_entry),
+- tag.year, 4);
+- /* Check for v1.1 tags */
+- if (tag.u.v1_1.__zero == 0 && tag.u.v1_1.track_number > 0)
+- {
+- char *temp = g_strdup_printf("%d", tag.u.v1_1.track_number);
+- set_entry_tag(GTK_ENTRY(comment_entry),
+- tag.u.v1_1.comment, 28);
+- gtk_entry_set_text(GTK_ENTRY(tracknum_entry), temp);
+- g_free(temp);
+- }
+- else
++ char* genre = id3_get_content(frame);
++ if (genre != NULL)
+ {
+- set_entry_tag(GTK_ENTRY(comment_entry),
+- tag.u.v1_0.comment, 30);
+- gtk_entry_set_text(GTK_ENTRY(tracknum_entry), "");
++ int genre_idx = genre_find_index_str(genre_list, genre);
++ if (genre_idx > 0)
++ gtk_list_select_item(GTK_LIST(GTK_COMBO(v2_genre_combo)->list), genre_idx);
++ else
++ gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(v2_genre_combo)->entry), genre);
+ }
+-
+- gtk_list_select_item(GTK_LIST(GTK_COMBO(genre_combo)->list), genre_find_index(genre_list, tag.genre));
+ }
++
++ id3_close(id3);
++ }
++ else
++ {
++ /* Grey out the id3v2 tab */
++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(v2_checkbox), TRUE);
++ }
++
++ /*
++ * Try reading ID3v1 tag.
++ */
++ fseek(fh, -sizeof (id3v1tag), SEEK_END);
++ if ( (fread(&id3v1tag, 1, sizeof (id3v1tag), fh) == sizeof (id3v1tag)) && !strncmp(id3v1tag.tag, "TAG", 3))
++ {
++ id3_found = TRUE;
++ set_entry_tag_v1(GTK_ENTRY(v1_title_entry), id3v1tag.title, 30);
++ set_entry_tag_v1(GTK_ENTRY(v1_artist_entry), id3v1tag.artist, 30);
++ set_entry_tag_v1(GTK_ENTRY(v1_album_entry), id3v1tag.album, 30);
++ set_entry_tag_v1(GTK_ENTRY(v1_year_entry), id3v1tag.year, 4);
++ /* Check for v1.1 tags */
++ if (id3v1tag.u.v1_1.__zero == 0 && id3v1tag.u.v1_1.track_number > 0)
++ {
++ char *temp = g_strdup_printf("%d", id3v1tag.u.v1_1.track_number);
++ set_entry_tag_v1(GTK_ENTRY(v1_comment_entry), id3v1tag.u.v1_1.comment, 28);
++ gtk_entry_set_text(GTK_ENTRY(v1_tracknum_entry), temp);
++ g_free(temp);
++ }
++ else
++ {
++ set_entry_tag_v1(GTK_ENTRY(v1_comment_entry), id3v1tag.u.v1_0.comment, 30);
++ gtk_entry_set_text(GTK_ENTRY(v1_tracknum_entry), "");
+ }
++ gtk_list_select_item(GTK_LIST(GTK_COMBO(v1_genre_combo)->list), genre_find_index(genre_list, id3v1tag.genre));
++ }
++ else
++ {
++ // Grey out id3v1 tab
++ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(v1_checkbox), TRUE);
++ }
++
+ rewind(fh);
+
+ if (!mpg123_get_first_frame(fh, &frm, &buf))
+diff -dPNur xmms-1.2.11/Input/mpg123/id3.c xmms-1.2.11-new/Input/mpg123/id3.c
+--- xmms-1.2.11/Input/mpg123/id3.c 2005-05-21 20:05:56.000000000 +0200
++++ xmms-1.2.11-new/Input/mpg123/id3.c 2007-11-24 23:58:41.000000000 +0100
+@@ -22,6 +22,7 @@
+
+ #include <sys/types.h>
+ #include <sys/uio.h>
++#include <sys/stat.h>
+ #include <glib.h>
+ #include <fcntl.h>
+ #include <unistd.h>
+@@ -49,7 +50,7 @@
+ */
+ static int id3_seek_mem(struct id3_tag *id3, int offset)
+ {
+- if (id3->id3_pos + offset > id3->id3_tagsize ||
++ if (id3->id3_pos + offset > id3->id3_totalsize ||
+ id3->id3_pos + offset < 0)
+ {
+ id3_error(id3, "seeking beyond tag boundary");
+@@ -77,7 +78,7 @@
+ /*
+ * Check boundary.
+ */
+- if (id3->id3_pos + size > id3->id3_tagsize)
++ if (id3->id3_pos + size > id3->id3_totalsize)
+ return NULL;
+
+ /*
+@@ -118,7 +119,7 @@
+ /*
+ * Check boundary.
+ */
+- if (id3->id3_pos + offset > id3->id3_tagsize ||
++ if (id3->id3_pos + offset > id3->id3_totalsize ||
+ id3->id3_pos + offset < 0)
+ return -1;
+
+@@ -148,8 +149,8 @@
+ /*
+ * Check boundary.
+ */
+- if (id3->id3_pos + size > id3->id3_tagsize)
+- return NULL;
++ if (id3->id3_pos + size > id3->id3_totalsize)
++ size = id3->id3_totalsize - id3->id3_pos;
+
+ /*
+ * If buffer is NULL, we use the default buffer.
+@@ -205,7 +206,7 @@
+ /*
+ * Check boundary.
+ */
+- if (id3->id3_pos + offset > id3->id3_tagsize ||
++ if (id3->id3_pos + offset > id3->id3_totalsize ||
+ id3->id3_pos + offset < 0)
+ return -1;
+
+@@ -263,8 +264,8 @@
+ /*
+ * Check boundary.
+ */
+- if (id3->id3_pos + size > id3->id3_tagsize)
+- size = id3->id3_tagsize - id3->id3_pos;
++ if (id3->id3_pos + size > id3->id3_totalsize)
++ size = id3->id3_totalsize - id3->id3_pos;
+
+ /*
+ * If buffer is NULL, we use the default buffer.
+@@ -338,6 +339,32 @@
+ return NULL;
+ }
+
++/*
++ * Function id3_new()
++ *
++ * Creates a new ID3 tag structure. Useful for creating
++ * a new tag.
++ *
++ */
++struct id3_tag *id3_new()
++{
++ struct id3_tag *id3;
++
++ /*
++ * Allocate ID3 structure.
++ */
++ id3 = g_malloc0( sizeof(struct id3_tag) );
++
++ if (id3 != NULL)
++ {
++ id3_init_tag ( id3 );
++ id3->id3_type = ID3_TYPE_NONE;
++ id3->id3_seek = NULL;
++ id3->id3_read = NULL;
++ }
++
++ return id3;
++}
+
+ /*
+ * Function id3_open_fd (fd, flags)
+@@ -493,7 +520,7 @@
+ if (id3->id3_newtag)
+ return 0;
+ else
+- return id3->id3_tagsize + 3 + sizeof(id3_taghdr_t);
++ return id3->id3_totalsize + 3 + sizeof(id3_taghdr_t);
+ }
+ #endif
+
+@@ -560,6 +587,81 @@
+ return 0;
+ }
+
++/*
++ * Function id3_remove_tag_filename(filename)
++ *
++ * Remove the ID3v2 tag from the file indicated by filename. Takes care of resizing
++ * the file, if needed. Returns 0 upon success, or -1 if an error occured.
++ *
++ */
++int id3_remove_tag_filename(const char* filename)
++{
++ struct id3_tag *current_id3;
++ int fd;
++ int current_totalsize;
++ struct stat stat_buf;
++
++ fd = open(filename, O_RDWR);
++ if (fd == -1)
++ return -1;
++
++ /*
++ * Figure out how large the current tag is.
++ */
++ current_id3 = id3_open_fd(fd, 0);
++ if (current_id3 != NULL)
++ {
++ /* We use MAX to make sure an erroneous tag doesn't confuse us */
++ current_totalsize = MAX(current_id3->id3_totalsize, 0);
++ id3_close(current_id3);
++ }
++ else
++ {
++ current_totalsize = 0;
++ }
++
++ if (current_totalsize <= 0)
++ return 0;
++
++ /*
++ * Rewrite the file.
++ */
++
++ stat(filename, &stat_buf);
++
++ /* Re-position all the data current_totalsize bytes backwards */
++ {
++ int read_pos, write_pos;
++ size_t read_size;
++ char buf[4096];
++
++ /* TODO: Add error handling to IO operations */
++
++ /* We reposition the data by going forwards, each time reading
++ a block and copying it backward. That way, we never write
++ over a block which we intend to read later. */
++ write_pos = 0;
++ read_pos = write_pos + current_totalsize;
++
++ do
++ {
++ lseek(fd, read_pos, SEEK_SET);
++ read_size = read(fd, buf, sizeof(buf));
++ read_pos += read_size;
++
++ /* Write whatever block we've got */
++ lseek(fd, write_pos, SEEK_SET);
++ write(fd, buf, read_size);
++ write_pos += read_size;
++ }
++ while (read_size > 0);
++
++ ftruncate(fd, stat_buf.st_size - current_totalsize);
++ }
++
++ close(fd);
++ return 0;
++}
+
+ /*
+ * Function id3_write_tag (id3, fd)
+@@ -567,30 +669,25 @@
+ * Wrtite the ID3 tag to the indicated file descriptor. Return 0
+ * upon success, or -1 if an error occured.
+ *
++ * Warning: This function is called by id3_write_tag_filename and should
++ * not be called standalone, as it doesn't perform seeking
++ * and doesn't enlarge the file as necessary, and thus might
++ * OVERWRITE THE AUDIO DATA.
++ *
+ */
+ int id3_write_tag(struct id3_tag *id3, int fd)
+ {
+ struct id3_frame *fr;
+ GList *node;
+- int size = 0;
+ char buf[ID3_TAGHDR_SIZE];
+
+ /*
+- * Calculate size of ID3 tag.
+- */
+- for (node = id3->id3_frame; node != NULL; node = node->next)
+- {
+- fr = node->data;
+- size += fr->fr_size + ID3_FRAMEHDR_SIZE;
+- }
+-
+- /*
+ * Write tag header.
+ */
+ buf[0] = id3->id3_version;
+ buf[1] = id3->id3_revision;
+ buf[2] = id3->id3_flags;
+- ID3_SET_SIZE28(size, buf[3], buf[4], buf[5], buf[6]);
++ ID3_SET_SIZE28(id3->id3_size, buf[3], buf[4], buf[5], buf[6]);
+
+ if (safe_write(fd, "ID3", 3) == -1)
+ return -1;
+@@ -617,19 +714,203 @@
+ * TODO: Support compressed headers, encoded
+ * headers, and grouping info.
+ */
+- /* fhdr.fh_id = fr->fr_desc ? g_htonl(fr->fr_desc->fd_id) : 0; */
+- fhdr[3] = (fr->fr_size >> 24) & 0xff;
+- fhdr[4] = (fr->fr_size >> 16) & 0xff;
+- fhdr[5] = (fr->fr_size >> 8) & 0xff;
+- fhdr[6] = fr->fr_size & 0xff;
+- fhdr[7] = (fr->fr_flags >> 8) & 0xff;
+- fhdr[8] = fr->fr_flags & 0xff;
++ fhdr[0] = fr->fr_desc->fd_idstr[0];
++ fhdr[1] = fr->fr_desc->fd_idstr[1];
++ fhdr[2] = fr->fr_desc->fd_idstr[2];
++ fhdr[3] = fr->fr_desc->fd_idstr[3];
++ fhdr[4] = (fr->fr_raw_size >> 24) & 0xff;
++ fhdr[5] = (fr->fr_raw_size >> 16) & 0xff;
++ fhdr[6] = (fr->fr_raw_size >> 8) & 0xff;
++ fhdr[7] = fr->fr_raw_size & 0xff;
++ fhdr[8] = (fr->fr_flags >> 8) & 0xff;
++ fhdr[9] = fr->fr_flags & 0xff;
+
+ if (safe_write(fd, fhdr, sizeof(fhdr)) == -1)
+ return -1;
+
+- if (safe_write(fd, fr->fr_data, fr->fr_size) == -1)
++ if (safe_write(fd, fr->fr_raw_data, fr->fr_raw_size) == -1)
+ return -1;
+ }
+ return 0;
+ }
++
++
++/*
++ * Function id3_write_tag_file(id3, filename)
++ *
++ * Write the ID3 tag to the file indicated by filename. Takes care of enlarging
++ * the file, if needed. Returns 0 upon success, or -1 if an error occured.
++ *
++ */
++int id3_write_tag_filename(struct id3_tag *id3, const char* filename)
++{
++ struct id3_tag *current_id3;
++ int fd;
++ int current_totalsize, new_totalsize;
++ GList* node;
++
++ fd = open(filename, O_RDWR);
++ if (fd == -1)
++ return -1;
++
++ /*
++ * Figure out how large the current tag is.
++ */
++ current_id3 = id3_open_fd(fd, 0);
++ if (current_id3 != NULL)
++ {
++ /* We use MAX to make sure an erroneous tag doesn't confuse us */
++ current_totalsize = MAX(current_id3->id3_totalsize, 0);
++ id3_close(current_id3);
++ }
++ else
++ {
++ current_totalsize = 0;
++ }
++
++ /*
++ * Figure out how large the new tag will be.
++ */
++ new_totalsize = 10;
++ node = id3->id3_frame;
++ while (node != NULL)
++ {
++ struct id3_frame* fr = node->data;
++
++ {
++ char* text = id3_get_text(fr);
++ if (text != NULL)
++ {
++ int len = strlen(text);
++ g_free(text);
++
++ if (len == 0)
++ {
++ /* We skip over frames with empty text */
++ node = node->next;
++ id3_delete_frame(fr);
++ continue;
++ }
++ }
++ }
++
++ {
++ char* url = id3_get_url(fr);
++ if (url != NULL)
++ {
++ int len = strlen(url);
++ g_free(url);
++
++ if (len == 0)
++ {
++ /* We skip over frames with empty URLs */
++ node = node->next;
++ id3_delete_frame(fr);
++ continue;
++ }
++ }
++ }
++
++ new_totalsize += fr->fr_raw_size + ID3_FRAMEHDR_SIZE;
++ node = node->next;
++ }
++ new_totalsize += 0; /* no extended header, no footer, no padding */
++ id3->id3_flags = 0;
++
++ /*
++ * Determine whether we need to rewrite the file to make place for the new tag.
++ */
++ if (new_totalsize > current_totalsize)
++ {
++ struct stat stat_buf;
++ int grow_size;
++
++ stat(filename, &stat_buf);
++ grow_size = new_totalsize - current_totalsize;
++ ftruncate(fd, stat_buf.st_size + grow_size);
++
++ /* truncate adds "sparse" zeros; we'll turn them into real ones,
++ which occupy space on disk, to make sure we actually have
++ the disk space before we start enlarging the file */
++ {
++ int remaining = grow_size;
++ char buf[1024] = { 0 };
++ lseek(fd, stat_buf.st_size, SEEK_SET);
++ while (remaining > 0)
++ {
++ int ret = write(fd, buf, MIN(sizeof(buf), remaining));
++ if (ret >= 0)
++ remaining -= ret;
++ else
++ {
++ id3_error(id3, "Unable to enlarge file for the new tag");
++ ftruncate(fd, stat_buf.st_size);
++ close(fd);
++ return -1;
++ }
++ }
++ }
++
++ /* and now, re-position all the data grow_size bytes forward */
++ {
++ int area_to_move_size = stat_buf.st_size - current_totalsize;
++ int read_pos, write_pos;
++ size_t read_size, read_size_desired;
++ char buf[4096];
++
++ /* TODO: Add error handling to IO operations */
++
++ /* We reposition the data by going backwards, each time reading
++ a block and copying it forward. That way, we never write
++ over a block which we intend to read later. */
++ write_pos = lseek(fd, 0, SEEK_END);
++ read_pos = write_pos - grow_size;
++
++ /* While we still have bytes to move... */
++ while(area_to_move_size > 0)
++ {
++ /* Get 1 block or the remaining bytes -- the smallest of the two */
++ read_size_desired = MIN(area_to_move_size, sizeof(buf));
++ read_pos -= read_size_desired;
++ lseek(fd, read_pos, SEEK_SET);
++ read_size = read(fd, buf, read_size_desired);
++
++ /* Write whatever block we've got */
++ write_pos -= read_size;
++ lseek(fd, write_pos, SEEK_SET);
++ write(fd, buf, read_size);
++
++ area_to_move_size -= read_size;
++ }
++ }
++ }
++ else
++ {
++ new_totalsize = current_totalsize;
++ }
++
++ id3->id3_size = new_totalsize - 10;
++
++ /* Zero-out the ID3v2 tag area */
++ {
++ char buf[1024] = {0};
++ size_t remaining;
++
++ lseek(fd, 0, SEEK_SET);
++ for(remaining = new_totalsize; remaining > 0; remaining -= MIN(remaining,sizeof(buf)))
++ {
++ write(fd, buf, MIN(remaining,sizeof(buf)));
++ }
++ }
++
++ /* Write the new tag */
++ lseek(fd, 0, SEEK_SET);
++ if (id3_write_tag(id3, fd) == -1)
++ {
++ close(fd);
++ return -1;
++ }
++
++ close(fd);
++ return 0;
++}
+diff -dPNur xmms-1.2.11/Input/mpg123/id3_frame.c xmms-1.2.11-new/Input/mpg123/id3_frame.c
+--- xmms-1.2.11/Input/mpg123/id3_frame.c 2005-05-21 20:05:56.000000000 +0200
++++ xmms-1.2.11-new/Input/mpg123/id3_frame.c 2007-11-24 23:58:41.000000000 +0100
+@@ -283,7 +283,7 @@
+ */
+ if (!((buf[0] >= '0' && buf[0] <= '9') || (buf[0] >= 'A' && buf[0] <= 'Z')))
+ {
+- id3->id3_seek(id3, id3->id3_tagsize - id3->id3_pos);
++ id3->id3_seek(id3, id3->id3_totalsize - id3->id3_pos);
+ return 0;
+ }
+ id = ID3_FRAME_ID(buf[0], buf[1], buf[2], buf[3]);
+@@ -308,7 +308,7 @@
+ */
+
+ frame->fr_desc = find_frame_description(id);
+-
++
+ /*
+ * Check if frame had a valid id.
+ */
+@@ -385,6 +385,29 @@
+ }
+
+ /*
++ * Function id3_get_or_add_frame (id3, type)
++ *
++ * Search in the list of frames for the ID3-tag, and return the first frame
++ * of the indicated type. If no frame of the indicated type exists yet,
++ * it will add one and return it.
++ *
++ */
++struct id3_frame *id3_get_or_add_frame(struct id3_tag *id3, guint32 type)
++{
++ struct id3_frame* fr;
++
++ fr = id3_get_frame(id3, type, 1);
++ if (fr != NULL)
++ {
++ return fr;
++ }
++ else
++ {
++ return id3_add_frame(id3, type);
++ }
++}
++
++/*
+ * Function decompress_frame(frame)
+ *
+ * Uncompress the indicated frame. Return 0 upon success, or -1 if
+@@ -517,27 +540,26 @@
+ */
+ int id3_delete_frame(struct id3_frame *frame)
+ {
+- GList *list = frame->fr_owner->id3_frame;
++ struct id3_tag* id3 = frame->fr_owner;
++ GList *list = id3->id3_frame;
+ int ret;
+
+ /*
+ * Search for frame in list.
+ */
+- if (g_list_find(list, frame) != NULL)
+- {
+- /*
+- * Frame does not exist in frame list.
+- */
+- ret = -1;
+- }
+- else
+- {
++ if (g_list_find(list, frame) != NULL) {
+ /*
+ * Remove frame from frame list.
+ */
+ list = g_list_remove(list, frame);
+- frame->fr_owner->id3_altered = 1;
+- ret = 0;
++ id3->id3_frame = list;
++ id3->id3_altered = 1;
++ ret = 0;
++ } else {
++ /*
++ * Frame does not exist in frame list.
++ */
++ ret = -1;
+ }
+
+ /*
+@@ -704,7 +726,7 @@
+ */
+ if (!((buf[0] >= '0' && buf[0] <= '9') || (buf[0] >= 'A' && buf[0] <= 'Z')))
+ {
+- id3->id3_seek(id3, id3->id3_tagsize - id3->id3_pos);
++ id3->id3_seek(id3, id3->id3_size - id3->id3_pos);
+ return 0;
+ }
+
+diff -dPNur xmms-1.2.11/Input/mpg123/id3_frame_text.c xmms-1.2.11-new/Input/mpg123/id3_frame_text.c
+--- xmms-1.2.11/Input/mpg123/id3_frame_text.c 2007-11-24 23:58:20.000000000 +0100
++++ xmms-1.2.11-new/Input/mpg123/id3_frame_text.c 2007-11-24 23:58:41.000000000 +0100
+@@ -155,6 +155,11 @@
+ char *id3_get_text(struct id3_frame *frame)
+ {
+ int offset = 0;
++
++ /* Do we even have data for this frame */
++ if (!frame->fr_data)
++ return NULL;
++
+ /* Type check */
+ if (frame->fr_desc->fd_idstr[0] != 'T')
+ return NULL;
+@@ -374,3 +379,54 @@
+ return id3_string_decode(ID3_TEXT_FRAME_ENCODING(frame),
+ ID3_TEXT_FRAME_PTR(frame) + offset);
+ }
++
++/*
++ * Function id3_set_comment (frame, text)
++ *
++ * Set text for the indicated frame. Return 0 upon
++ * success, or -1 if an error occured.
++ *
++ */
++int id3_set_comment(struct id3_frame *frame, char *text)
++{
++ int *intp;
++
++ /* Type check */
++ if (frame->fr_desc->fd_id != ID3_COMM)
++ return -1;
++
++ /*
++ * Release memory occupied by previous data.
++ */
++ id3_frame_clear_data(frame);
++
++ /*
++ * Allocate memory for new data.
++ */
++ frame->fr_raw_size = 13 + strlen(text);
++ frame->fr_raw_data = g_malloc(frame->fr_raw_size + 1); /* <encode>XXXComments\0<comment><\0>
++
++ /* Save time... we just need to zero out the start, not the whole
++ * block, so don't waste time with a calloc()
++ */
++
++ ((guint8 *)frame->fr_raw_data)[0] = ID3_ENCODING_ISO_8859_1;
++ ((guint8 *)frame->fr_raw_data)[1] = 0x58;
++ ((guint8 *)frame->fr_raw_data)[2] = 0x58;
++ ((guint8 *)frame->fr_raw_data)[3] = 0x58;
++
++ memcpy((char *) frame->fr_raw_data + 4, "Comments", 9);
++
++ /*
++ * Copy contents.
++ */
++ memcpy((char *) frame->fr_raw_data + 13, text, strlen(text) + 1);
++
++ frame->fr_altered = 1;
++ frame->fr_owner->id3_altered = 1;
++
++ frame->fr_data = frame->fr_raw_data;
++ frame->fr_size = frame->fr_raw_size;
++
++ return 0;
++}
+diff -dPNur xmms-1.2.11/Input/mpg123/id3_frame_url.c xmms-1.2.11-new/Input/mpg123/id3_frame_url.c
+--- xmms-1.2.11/Input/mpg123/id3_frame_url.c 2005-05-21 20:05:56.000000000 +0200
++++ xmms-1.2.11-new/Input/mpg123/id3_frame_url.c 2007-11-24 23:58:41.000000000 +0100
+@@ -34,6 +34,11 @@
+ char *id3_get_url(struct id3_frame *frame)
+ {
+ int offset = 0;
++
++ /* Do we even have data for this frame */
++ if (!frame->fr_data)
++ return NULL;
++
+ /* Type check */
+ if (frame->fr_desc->fd_idstr[0] != 'W')
+ return NULL;
+@@ -80,3 +85,32 @@
+ return id3_string_decode(ID3_TEXT_FRAME_ENCODING(frame),
+ ID3_TEXT_FRAME_PTR(frame));
+ }
++
++/*
++ * Function id3_set_url (frame)
++ *
++ * Sets URL of frame.
++ *
++ */
++void id3_set_url(struct id3_frame *frame, const char* url)
++{
++ /* Type check */
++ if ( frame->fr_desc->fd_idstr[0] != 'W' )
++ return;
++
++ /* Check if frame is compressed */
++ if (id3_decompress_frame(frame) == -1)
++ return;
++
++ /*
++ * Allocate memory for new data.
++ */
++ frame->fr_raw_size = strlen(url) + 1;
++ frame->fr_raw_data = g_malloc(frame->fr_raw_size + 1);
++
++ /*
++ * Copy contents.
++ */
++ *(gint8 *) frame->fr_raw_data = ID3_ENCODING_ISO_8859_1;
++ memcpy((char *) frame->fr_raw_data + 1, url, frame->fr_raw_size);
++}
+diff -dPNur xmms-1.2.11/Input/mpg123/id3.h xmms-1.2.11-new/Input/mpg123/id3.h
+--- xmms-1.2.11/Input/mpg123/id3.h 2005-05-21 20:05:56.000000000 +0200
++++ xmms-1.2.11-new/Input/mpg123/id3.h 2007-11-24 23:58:41.000000000 +0100
+@@ -22,6 +22,7 @@
+ #define ID3_H
+
+ #include <glib.h>
++#include <string.h>
+
+ /*
+ * Option flags to id3_open_*().
+@@ -49,8 +50,9 @@
+
+ int id3_version; /* Major ID3 version number */
+ int id3_revision; /* ID3 revision number */
++ int id3_size; /* Size of ID3 tag (as dictated by header) */
+
+- int id3_tagsize; /* Total size of ID3 tag */
++ int id3_totalsize; /* Total size of ID3 tag (including header, footer and padding) */
+ int id3_pos; /* Current position within tag */
+
+ char *id3_error_msg; /* Last error message */
+@@ -140,8 +142,6 @@
+ #define ID3_ENCODING_UTF16BE 0x02
+ #define ID3_ENCODING_UTF8 0x03
+
+-
+-
+ /*
+ * ID3 frame id numbers.
+ */
+@@ -322,50 +322,51 @@
+ */
+
+ /* From id3.c */
+-struct id3_tag *id3_open_mem(void *, int);
+-struct id3_tag *id3_open_fd(int, int);
+-struct id3_tag *id3_open_fp(FILE *, int);
+-int id3_set_output(struct id3_tag *, char *);
+-int id3_close(struct id3_tag *);
+-int id3_tell(struct id3_tag *);
+-int id3_alter_file(struct id3_tag *);
+-int id3_write_tag(struct id3_tag *, int);
++struct id3_tag *id3_new();
++struct id3_tag *id3_open_mem(void *ptr, int flags);
++struct id3_tag *id3_open_fd(int fd, int flags);
++struct id3_tag *id3_open_fp(FILE *fp, int flags);
++int id3_close(struct id3_tag *id3);
++int id3_tell(struct id3_tag *id3);
++int id3_alter_file(struct id3_tag *id3);
++int id3_write_tag(struct id3_tag *id3, int fd);
++int id3_write_tag_filename(struct id3_tag *id3, const char* filename);
++int id3_remove_tag_filename(const char* filename);
+
+ /* From id3_frame.c */
+ int id3_read_frame(struct id3_tag *id3);
+-struct id3_frame *id3_get_frame(struct id3_tag *, guint32, int);
++struct id3_frame *id3_get_frame(struct id3_tag *id3, guint32 type, int num);
+ int id3_delete_frame(struct id3_frame *frame);
+-struct id3_frame *id3_add_frame(struct id3_tag *, guint32);
+-int id3_decompress_frame(struct id3_frame *);
+-void id3_destroy_frames(struct id3_tag *id);
++struct id3_frame *id3_add_frame(struct id3_tag *id3, guint32 type);
++struct id3_frame *id3_get_or_add_frame(struct id3_tag *id3, guint32 type);
++int id3_decompress_frame(struct id3_frame *frame);
++void id3_destroy_frames(struct id3_tag *id3);
+ void id3_frame_clear_data(struct id3_frame *frame);
+
+ /* From id3_frame_text.c */
+ guint id3_string_size(guint8 encoding, const char* text);
+ char* id3_string_decode(guint8 encoding, const char* text);
+-gint8 id3_get_encoding(struct id3_frame *);
+-int id3_set_encoding(struct id3_frame *, gint8);
+-char *id3_get_text(struct id3_frame *);
+-char *id3_get_comment(struct id3_frame *);
+-char *id3_get_text_desc(struct id3_frame *);
+-int id3_get_text_number(struct id3_frame *);
+-int id3_set_text(struct id3_frame *, char *);
+-int id3_set_text_number(struct id3_frame *, int);
++gint8 id3_get_encoding(struct id3_frame *frame);
++int id3_set_encoding(struct id3_frame *frame, gint8 encoding);
++char *id3_get_text(struct id3_frame *frame);
++char *id3_get_comment(struct id3_frame *frame);
++char *id3_get_text_desc(struct id3_frame *frame);
++int id3_get_text_number(struct id3_frame *frame);
++int id3_set_text(struct id3_frame *frame, char *text);
++int id3_set_comment(struct id3_frame *frame, char *comment);
++int id3_set_text_number(struct id3_frame *frame, int number);
+ gboolean id3_frame_is_text(struct id3_frame *frame);
+
+ /* From id3_frame_content.c */
+-char *id3_get_content(struct id3_frame *);
++char *id3_get_content(struct id3_frame *frame);
+
+ /* From id3_frame_url.c */
+-char *id3_get_url(struct id3_frame *);
+-char *id3_get_url_desc(struct id3_frame *);
++char *id3_get_url(struct id3_frame *frame);
++char *id3_get_url_desc(struct id3_frame *frame);
++void id3_set_url(struct id3_frame *frame, const char* url);
+
+ /* From id3_tag.c */
+ void id3_init_tag(struct id3_tag *id3);
+ int id3_read_tag(struct id3_tag *id3);
+
+-char *convert_from_utf16(const unsigned char *utf16);
+-char *convert_from_utf16be(const unsigned char *utf16);
+-
+-
+ #endif /* ID3_H */
+diff -dPNur xmms-1.2.11/Input/mpg123/id3_header.h xmms-1.2.11-new/Input/mpg123/id3_header.h
+--- xmms-1.2.11/Input/mpg123/id3_header.h 2005-05-21 20:05:56.000000000 +0200
++++ xmms-1.2.11-new/Input/mpg123/id3_header.h 2007-11-24 23:58:41.000000000 +0100
+@@ -39,22 +39,21 @@
+ #define ID3_THFLAG_USYNC 0x80
+ #define ID3_THFLAG_EXT 0x40
+ #define ID3_THFLAG_EXP 0x20
++#define ID3_THFLAG_FOOTER 0x10
+
+ #define ID3_SET_SIZE28(size, a, b, c, d) \
+-do { \
+- a = (size >> (24 + 3)) & 0x7f; \
+- b = (size >> (16 + 2)) & 0x7f; \
+- c = (size >> ( 8 + 1)) & 0x7f; \
++{ \
++ a = (size >> (24 - 3)) & 0x7f; \
++ b = (size >> (16 - 2)) & 0x7f; \
++ c = (size >> ( 8 - 1)) & 0x7f; \
+ d = size & 0x7f; \
+-} while (0)
++}
+
+ #define ID3_GET_SIZE28(a, b, c, d) \
+-(((a & 0x7f) << (24 - 3)) | \
+- ((b & 0x7f) << (16 - 2)) | \
+- ((c & 0x7f) << ( 8 - 1)) | \
+- ((d & 0x7f)))
+-
+-
++ (((a & 0x7f) << (24 - 3)) | \
++ ((b & 0x7f) << (16 - 2)) | \
++ ((c & 0x7f) << ( 8 - 1)) | \
++ ((d & 0x7f))) \
+
+ /*
+ * Layout for the extended header.
+diff -dPNur xmms-1.2.11/Input/mpg123/id3_tag.c xmms-1.2.11-new/Input/mpg123/id3_tag.c
+--- xmms-1.2.11/Input/mpg123/id3_tag.c 2005-05-21 20:05:56.000000000 +0200
++++ xmms-1.2.11-new/Input/mpg123/id3_tag.c 2007-11-24 23:58:41.000000000 +0100
+@@ -39,7 +39,8 @@
+ id3->id3_version = 3;
+ id3->id3_revision = 0;
+ id3->id3_flags = ID3_THFLAG_USYNC | ID3_THFLAG_EXP;
+- id3->id3_tagsize = 0;
++ id3->id3_size = 0;
++ id3->id3_totalsize = 0;
+
+ id3->id3_altered = 1;
+ id3->id3_newtag = 1;
+@@ -63,13 +64,14 @@
+ int id3_read_tag(struct id3_tag *id3)
+ {
+ char *buf;
++ guint8 padding;
+
+ /*
+ * We know that the tag will be at least this big.
+ *
+ * tag header + "ID3"
+ */
+- id3->id3_tagsize = ID3_TAGHDR_SIZE + 3;
++ id3->id3_totalsize = ID3_TAGHDR_SIZE + 3;
+
+ if (!(id3->id3_oflags & ID3_OPENF_NOCHK))
+ {
+@@ -100,9 +102,11 @@
+ id3->id3_version = buf[0];
+ id3->id3_revision = buf[1];
+ id3->id3_flags = buf[2];
+- id3->id3_tagsize = ID3_GET_SIZE28(buf[3], buf[4], buf[5], buf[6]);
++ id3->id3_size = ID3_GET_SIZE28(buf[3], buf[4], buf[5], buf[6]);
++ id3->id3_totalsize += id3->id3_size;
++ if (id3->id3_flags & ID3_THFLAG_FOOTER)
++ id3->id3_totalsize += 10;
+ id3->id3_newtag = 0;
+- id3->id3_pos = 0;
+
+ if (id3->id3_version < 2 || id3->id3_version > 4)
+ return -1;
+@@ -120,14 +124,34 @@
+ /*
+ * Parse frames.
+ */
+- while (id3->id3_pos < id3->id3_tagsize)
++ while (id3->id3_pos < id3->id3_size)
+ {
+ if (id3_read_frame(id3) == -1)
+ return -1;
+ }
+
+- if (id3->id3_frame == NULL)
+- return -1;
++ /*
++ * Like id3lib, we try to find unstandard padding (not within
++ * the tag size). This is important to know when we strip
++ * the tag or replace it.
++ * Another option might be looking for an MPEG sync, but we don't do it.
++ */
++
++ id3->id3_seek(id3, id3->id3_totalsize - id3->id3_pos);
++
++ /* Temporarily increase totalsize, to try reading beyong the boundary */
++ ++id3->id3_totalsize;
++
++ while (id3->id3_read(id3, &padding, sizeof(padding)) != NULL)
++ {
++ if (padding == 0)
++ ++id3->id3_totalsize;
++ else
++ break;
++ }
++
++ /* Decrease totalsize after we temporarily increased it */
++ --id3->id3_totalsize;
+
+ return 0;
+ }
+diff -dPNur xmms-1.2.11/Input/mpg123/mpg123.c xmms-1.2.11-new/Input/mpg123/mpg123.c
+--- xmms-1.2.11/Input/mpg123/mpg123.c 2007-11-24 23:58:20.000000000 +0100
++++ xmms-1.2.11-new/Input/mpg123/mpg123.c 2007-11-24 23:58:41.000000000 +0100
+@@ -129,9 +129,9 @@
+
+ #ifdef USE_SIMD
+ fr->dct36 = funcs_dct36[0];
+-
++
+ if (CPU_HAS_3DNOW() && !p8 &&
+- (mpg123_cfg.default_synth == SYNTH_3DNOW ||
++ (mpg123_cfg.default_synth == SYNTH_3DNOW ||
+ mpg123_cfg.default_synth == SYNTH_AUTO))
+ {
+ fr->synth = funcs[3][ds]; /* 3DNow! optimized synth_1to1() */
+@@ -320,11 +320,11 @@
+ if (!strncasecmp(filename, "http://", 7))
+ { /* We assume all http:// (except those ending in .ogg) are mpeg -- why do we do that? */
+ ext = strrchr(filename, '.');
+- if (ext)
++ if (ext)
+ {
+- if (!strncasecmp(ext, ".ogg", 4))
++ if (!strncasecmp(ext, ".ogg", 4))
+ return FALSE;
+- if (!strncasecmp(ext, ".rm", 3) ||
++ if (!strncasecmp(ext, ".rm", 3) ||
+ !strncasecmp(ext, ".ra", 3) ||
+ !strncasecmp(ext, ".rpm", 4) ||
+ !strncasecmp(ext, ".fla", 4) ||
+@@ -534,7 +534,7 @@
+ * Function mpg123_get_id3v2 (id3d, tag)
+ *
+ * Get desired contents from the indicated id3tag and store it in
+- * `tag'.
++ * `tag'.
+ *
+ */
+ struct id3v2tag_t* mpg123_id3v2_get(struct id3_tag *id3d)
+@@ -550,6 +550,10 @@
+ tag->track_number = id3v2_get_num(id3d, ID3_TRCK);
+ tag->comment = id3v2_get_text(id3d, ID3_COMM);
+ tag->genre = id3v2_get_text(id3d, ID3_TCON);
++ tag->composer = id3v2_get_text(id3d, ID3_TCOM);
++ tag->orig_artist = id3v2_get_text(id3d, ID3_TOPE);
++ tag->url = id3v2_get_text(id3d, ID3_WCOM);
++ tag->encoded_by = id3v2_get_text(id3d, ID3_TENC);
+
+ return tag;
+ }
+@@ -829,7 +833,7 @@
+ static int mpg123_seek(struct frame *fr, xing_header_t *xh, gboolean vbr, int time)
+ {
+ int jumped = -1;
+-
++
+ if (xh)
+ {
+ int percent = ((double) time * 100.0) /
+@@ -995,7 +999,7 @@
+ mpg123_info->output_audio = FALSE;
+ continue;
+ }
+-
++
+ }
+ }
+ if(mpg123_freqs[fr.sampling_frequency] != mpg123_frequency || mpg123_stereo != fr.stereo)
+@@ -1033,12 +1037,12 @@
+ mpg123_info->output_audio = FALSE;
+ continue;
+ }
+- }
++ }
+ }
+-
++
+ if (tabsel_123[fr.lsf][fr.lay - 1][fr.bitrate_index] != mpg123_bitrate)
+ mpg123_bitrate = tabsel_123[fr.lsf][fr.lay - 1][fr.bitrate_index];
+-
++
+ if (!disp_count)
+ {
+ disp_count = 20;
+@@ -1154,7 +1158,7 @@
+
+ if (aboutbox != NULL)
+ return;
+-
++
+ aboutbox = xmms_show_message(
+ _("About MPEG Layer 1/2/3 plugin"),
+ _("mpg123 decoding engine by Michael Hipp <mh@mpg123.de>\n"
+diff -dPNur xmms-1.2.11/Input/mpg123/mpg123.h xmms-1.2.11-new/Input/mpg123/mpg123.h
+--- xmms-1.2.11/Input/mpg123/mpg123.h 2007-11-24 23:58:20.000000000 +0100
++++ xmms-1.2.11-new/Input/mpg123/mpg123.h 2007-11-24 23:58:41.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * mpg123 defines
++ * mpg123 defines
+ * used source: musicout.h from mpegaudio package
+ */
+
+@@ -79,6 +79,10 @@
+ char *album;
+ char *comment;
+ char *genre;
++ char *composer;
++ char *orig_artist;
++ char *url;
++ char *encoded_by;
+ int year;
+ int track_number;
+ };
+@@ -300,7 +304,6 @@
+ int mpg123_decode_header(struct frame *fr, unsigned long newhead);
+ double mpg123_compute_bpf(struct frame *fr);
+ double mpg123_compute_tpf(struct frame *fr);
+-guint mpg123_strip_spaces(char *src, size_t n);
+ struct id3v2tag_t* mpg123_id3v2_get(struct id3_tag *id3d);
+ void mpg123_id3v2_destroy(struct id3v2tag_t* tag);
+ char *mpg123_format_song_title(struct id3v2tag_t *tag, char *filename);
diff --git a/3rdparty/mpg123/2020_all_mpg123-vorbis-ssl.patch b/3rdparty/mpg123/2020_all_mpg123-vorbis-ssl.patch
new file mode 100644
index 0000000..453c91b
--- /dev/null
+++ b/3rdparty/mpg123/2020_all_mpg123-vorbis-ssl.patch
@@ -0,0 +1,766 @@
+diff -dPNur xmms-1.2.11/configure.in xmms-1.2.11-new/configure.in
+--- xmms-1.2.11/configure.in 2007-11-16 22:52:30.000000000 +0100
++++ xmms-1.2.11-new/configure.in 2007-11-25 00:00:09.000000000 +0100
+@@ -144,6 +144,22 @@
+ fi
+ AC_SUBST([PTHREAD_LIBS])
+
++dnl *** OpenSSL support
++AC_ARG_ENABLE( ssl,
++[ --disable-ssl Disable HTTP SSL in plugin(s) [default=enabled]],,
++ enable_ssl="yes")
++
++if test "x$enable_ssl" = xyes; then
++ SSL_LIBS=""
++ AC_CHECK_LIB(ssl, SSL_read, [SSL_LIBS="-lssl"
++ AC_DEFINE(HTTP_SSL,,[Define if OpenSSL is available])
++ AC_DEFINE(OPENSSL_NO_KRB5,,[Define if OpenSSL is available])],
++ echo "*** SSL support requires openssl and openssl-devel packages ***")
++ LIBS="$LIBS $SSL_LIBS"
++else
++ AC_MSG_RESULT([*** Disabling SSL in plugin(s) per user request ***])
++ have_ssl=no
++fi
+
+ dnl ***
+ dnl *** OpenGL
+diff -dPNur xmms-1.2.11/Input/mpg123/common.c xmms-1.2.11-new/Input/mpg123/common.c
+--- xmms-1.2.11/Input/mpg123/common.c 2007-11-24 23:59:35.000000000 +0100
++++ xmms-1.2.11-new/Input/mpg123/common.c 2007-11-25 00:00:09.000000000 +0100
+@@ -464,7 +464,11 @@
+ void mpg123_open_stream(char *bs_filenam, int fd, unsigned long range)
+ {
+ filept_opened = 1;
++#ifdef HTTP_SSL
++ if (!strncasecmp(bs_filenam, "http://", 7) || !strncasecmp(bs_filenam, "https://", 8))
++#else
+ if (!strncasecmp(bs_filenam, "http://", 7))
++#endif
+ {
+ filept = NULL;
+ mpg123_info->filesize = 0;
+diff -dPNur xmms-1.2.11/Input/mpg123/http.c xmms-1.2.11-new/Input/mpg123/http.c
+--- xmms-1.2.11/Input/mpg123/http.c 2007-11-24 23:53:33.000000000 +0100
++++ xmms-1.2.11-new/Input/mpg123/http.c 2007-11-25 00:00:09.000000000 +0100
+@@ -33,6 +33,14 @@
+ #include "mpg123.h"
+ #include "libxmms/util.h"
+
++#ifdef HTTP_SSL
++#include <openssl/ssl.h>
++#include <openssl/err.h>
++#include <openssl/x509.h>
++#include <openssl/pem.h>
++#include <openssl/crypto.h>
++#endif
++
+ #define min(x,y) ((x)<(y)?(x):(y))
+ #define min3(x,y,z) (min(x,y)<(z)?min(x,y):(z))
+ #define min4(x,y,z,w) (min3(x,y,z)<(w)?min3(x,y,z):(w))
+@@ -117,7 +125,11 @@
+ return res;
+ }
+
++#ifdef HTTP_SSL
++static void parse_url(const gchar * url, gchar ** user, gchar ** pass, gchar ** host, int *port, gchar ** filename, int *ssl)
++#else
+ static void parse_url(const gchar * url, gchar ** user, gchar ** pass, gchar ** host, int *port, gchar ** filename)
++#endif
+ {
+ gchar *h, *p, *pt, *f, *temp, *ptr;
+
+@@ -126,6 +138,14 @@
+
+ if (!strncasecmp("http://", ptr, 7))
+ ptr += 7;
++#ifdef HTTP_SSL
++ if (!strncasecmp("https://", ptr, 8)) {
++ ptr += 8;
++ *ssl = 1;
++ } else
++ *ssl = 0;
++#endif
++
+ h = strchr(ptr, '@');
+ f = strchr(ptr, '/');
+ if (h != NULL && (!f || h < f))
+@@ -160,7 +180,12 @@
+ {
+ if (f)
+ *f = '\0';
+- *port = 80;
++#ifdef HTTP_SSL
++ if (*ssl)
++ *port = 443;
++ else
++#endif
++ *port = 80;
+ }
+ *host = g_strdup(h);
+
+@@ -302,16 +327,27 @@
+ return FALSE;
+ }
+
++#ifdef HTTP_SSL
++gint mpg123_http_read_line(gchar * buf, gint size, SSL *ssl_c)
++#else
+ gint mpg123_http_read_line(gchar * buf, gint size)
++#endif
+ {
+- gint i = 0;
++ gint i = 0, rc;
+
+ while (going && i < size - 1)
+ {
+ if (http_check_for_data())
+ {
+- if (read(sock, buf + i, 1) <= 0)
+- return -1;
++#ifdef HTTP_SSL
++ if (ssl_c) {
++ while ((rc = SSL_read(ssl_c, buf + i, 1)) == -1);
++ if (rc <= 0)
++ return -1;
++ } else
++#endif
++ if (read(sock, buf + i, 1) <= 0)
++ return -1;
+ if (buf[i] == '\n')
+ break;
+ if (buf[i] != '\r')
+@@ -342,7 +378,13 @@
+ struct sockaddr_in address;
+ #endif
+ struct timeval tv;
+-
++#ifdef HTTP_SSL
++ SSL *ssl_c = NULL;
++ SSL_CTX *ssl_ctx = NULL;
++ BIO *b = NULL;
++ gint ssl=0;
++#endif
++
+ url = (gchar *) arg;
+ do
+ {
+@@ -350,7 +392,11 @@
+
+ g_strstrip(url);
+
++#ifdef HTTP_SSL
++ parse_url(url, &user, &pass, &host, &port, &filename, &ssl);
++#else
+ parse_url(url, &user, &pass, &host, &port, &filename);
++#endif
+
+ if ((!filename || !*filename) && url[strlen(url) - 1] != '/')
+ temp = g_strconcat(url, "/", NULL);
+@@ -362,6 +408,26 @@
+ chost = mpg123_cfg.use_proxy ? mpg123_cfg.proxy_host : host;
+ cport = mpg123_cfg.use_proxy ? mpg123_cfg.proxy_port : port;
+
++#ifdef HTTP_SSL
++ if (ssl) {
++ SSL_library_init();
++ OpenSSL_add_ssl_algorithms();
++ SSL_load_error_strings();
++
++ ssl_ctx = SSL_CTX_new(SSLv23_client_method());
++ if (ssl_ctx == NULL) {
++ fprintf(stderr, "SSL_CTX_new() failed.");
++ eof = TRUE;
++ }
++
++ ssl_c = SSL_new(ssl_ctx);
++ if (ssl_c == NULL) {
++ fprintf(stderr, "SSL_new() failed.\n");
++ eof = TRUE;
++ }
++ }
++#endif
++
+ #ifdef USE_IPV6
+ g_snprintf(service, 6, "%d", cport);
+ memset(&hints, 0, sizeof(hints));
+@@ -441,7 +507,20 @@
+ eof = TRUE;
+ }
+ }
++#ifdef HTTP_SSL
++ if (ssl) {
++ b = BIO_new_socket(sock, BIO_NOCLOSE);
++ if (b == NULL) {
++ printf("BIO_new_socket() failed.\n");
++ eof = TRUE;
++ }
++
++ // cannot fail
++ SSL_set_bio(ssl_c, b, b);
++ }
++#endif
+ #endif
++
+ while (going)
+ {
+ tv.tv_sec = 0;
+@@ -466,6 +545,24 @@
+ break;
+ }
+ }
++#ifdef HTTP_SSL
++ if (ssl) {
++ int rc;
++
++ SSL_set_connect_state(ssl_c);
++
++ while ((rc = SSL_connect(ssl_c)) == -1);
++ if (rc <= 0) {
++ fprintf(stderr, "SSL_connect() error. SSL error code: %d.\n",
++ SSL_get_error(ssl_c, rc));
++ }
++ while ((rc = SSL_do_handshake(ssl_c)) == -1);
++ if (rc <= 0) {
++ fprintf(stderr, "SSL_do_handshake() error. SSL error code: %d.\n",
++ SSL_get_error(ssl_c, rc));
++ }
++ }
++#endif
+ if (!eof)
+ {
+ gchar *auth = NULL, *proxy_auth = NULL;
+@@ -517,14 +614,30 @@
+ g_free(proxy_auth);
+ if(auth)
+ g_free(auth);
+- write(sock, temp, strlen(temp));
++#ifdef HTTP_SSL
++ if (ssl) {
++ int rc;
++ while ((rc = SSL_write(ssl_c, temp, strlen(temp))) == -1);
++ if (rc <= 0) {
++ fprintf(stderr, "SSL_write() error. SSL error code: %d.\n",
++ SSL_get_error(ssl_c, rc));
++ eof = TRUE;
++ }
++ } else
++#endif
++ write(sock, temp, strlen(temp));
++
+ g_free(temp);
+ mpg123_ip.set_info_text(_("CONNECTED: WAITING FOR REPLY"));
+ while (going && !eof)
+ {
+ if (http_check_for_data())
+ {
++#ifdef HTTP_SSL
++ if (mpg123_http_read_line(line, 1024, ssl_c))
++#else
+ if (mpg123_http_read_line(line, 1024))
++#endif
+ {
+ status = strchr(line, ' ');
+ if (status)
+@@ -537,7 +650,11 @@
+ {
+ if(http_check_for_data())
+ {
++#ifdef HTTP_SSL
++ if((cnt = mpg123_http_read_line(line, 1024, ssl_c)) != -1)
++#else
+ if((cnt = mpg123_http_read_line(line, 1024)) != -1)
++#endif
+ {
+ if(!cnt)
+ break;
+@@ -579,7 +696,11 @@
+ {
+ if (http_check_for_data())
+ {
++#ifdef HTTP_SSL
++ if ((cnt = mpg123_http_read_line(line, 1024, ssl_c)) != -1)
++#else
+ if ((cnt = mpg123_http_read_line(line, 1024)) != -1)
++#endif
+ {
+ if (!cnt)
+ break;
+@@ -617,6 +738,10 @@
+ fclose(output_file);
+ output_file = NULL;
+ }
++#ifdef HTTP_SSL
++ if (ssl)
++ SSL_shutdown(ssl_c);
++#endif
+ close(sock);
+ g_free(user);
+ g_free(pass);
+@@ -634,6 +759,10 @@
+ fname = file;
+ if (!strncasecmp(fname, "http://", 7))
+ fname += 7;
++#ifdef HTTP_SSL
++ if (!strncasecmp(fname, "https://", 8))
++ fname += 8;
++#endif
+ temp = strrchr(fname, '.');
+ if (temp && !strcasecmp(temp, ".mp3"))
+ *temp = '\0';
+@@ -668,7 +797,12 @@
+ cnt = min(http_free(), buffer_length - wr_index);
+ if (cnt > 1024)
+ cnt = 1024;
+- written = read(sock, buffer + wr_index, cnt);
++#ifdef HTTP_SSL
++ if (ssl)
++ while ((written = SSL_read(ssl_c, buffer + wr_index, cnt)) == -1);
++ else
++#endif
++ written = read(sock, buffer + wr_index, cnt);
+ if (written <= 0)
+ {
+ eof = TRUE;
+@@ -715,6 +849,10 @@
+ fclose(output_file);
+ output_file = NULL;
+ }
++#ifdef HTTP_SSL
++ if (ssl)
++ SSL_shutdown(ssl_c);
++#endif
+ close(sock);
+ if (udp_sock != 0)
+ close(udp_sock);
+diff -dPNur xmms-1.2.11/Input/mpg123/mpg123.c xmms-1.2.11-new/Input/mpg123/mpg123.c
+--- xmms-1.2.11/Input/mpg123/mpg123.c 2007-11-24 23:59:35.000000000 +0100
++++ xmms-1.2.11-new/Input/mpg123/mpg123.c 2007-11-25 00:00:09.000000000 +0100
+@@ -317,7 +317,11 @@
+ char *ext;
+ guint16 wavid;
+
++#ifdef HTTP_SSL
++ if (!strncasecmp(filename, "http://", 7) || !strncasecmp(filename, "https://", 8))
++#else
+ if (!strncasecmp(filename, "http://", 7))
++#endif
+ { /* We assume all http:// (except those ending in .ogg) are mpeg -- why do we do that? */
+ ext = strrchr(filename, '.');
+ if (ext)
+@@ -801,7 +805,11 @@
+ /*
+ * TODO: Getting song info from http streams.
+ */
++#ifdef HTTP_SSL
++ if (strncasecmp(filename, "http://", 7) && strncasecmp(filename, "https://", 8))
++#else
+ if (strncasecmp(filename, "http://", 7))
++#endif
+ {
+ if ((file = fopen(filename, "rb")) != NULL)
+ {
+@@ -890,7 +898,12 @@
+ mpg123_init_layer3(fr.down_sample_sblimit);
+
+ mpg123_info->tpf = mpg123_compute_tpf(&fr);
++
++#ifdef HTTP_SSL
++ if (strncasecmp(filename, "http://", 7) && strncasecmp(filename, "https://", 8))
++#else
+ if (strncasecmp(filename, "http://", 7))
++#endif
+ {
+ if (mpg123_stream_check_for_xing_header(&fr, &xing_header))
+ {
+@@ -937,7 +950,11 @@
+ mpg123_mode = fr.mode;
+ mpg123_length = mpg123_info->num_frames * mpg123_info->tpf * 1000;
+
++#ifdef HTTP_SSL
++ if (strncasecmp(filename, "http://", 7) && strncasecmp(filename, "https://", 8))
++#else
+ if (strncasecmp(filename, "http://", 7))
++#endif
+ {
+ if (!mpg123_title)
+ mpg123_title = get_song_title(NULL,filename);
+@@ -1050,7 +1067,11 @@
+ {
+ /* FIXME networks streams */
+ disp_bitrate = mpg123_bitrate;
++#ifdef HTTP_SSL
++ if(!have_xing_header && strncasecmp(filename,"http://",7) && strncasecmp(filename, "https://", 8))
++#else
+ if(!have_xing_header && strncasecmp(filename,"http://",7))
++#endif
+ {
+ double rel = mpg123_relative_pos();
+ if (rel)
+diff -dPNur xmms-1.2.11/Input/vorbis/fileinfo.c xmms-1.2.11-new/Input/vorbis/fileinfo.c
+--- xmms-1.2.11/Input/vorbis/fileinfo.c 2005-05-15 02:01:20.000000000 +0200
++++ xmms-1.2.11-new/Input/vorbis/fileinfo.c 2007-11-25 00:00:09.000000000 +0100
+@@ -227,7 +227,12 @@
+ vcedit_state *state;
+ vorbis_comment *comment;
+
++#ifdef HTTP_SSL
++ if (!g_strncasecmp(vte.filename, "http://", 7) ||
++ !g_strncasecmp(vte.filename, "https://", 8))
++#else
+ if (!g_strncasecmp(vte.filename, "http://", 7))
++#endif
+ return;
+
+ state = vcedit_new_state();
+@@ -303,7 +308,12 @@
+ vcedit_state *state;
+ vorbis_comment *comment;
+
++#ifdef HTTP_SSL
++ if (!g_strncasecmp(vte.filename, "http://", 7) ||
++ !g_strncasecmp(vte.filename, "https://", 8))
++#else
+ if (!g_strncasecmp(vte.filename, "http://", 7))
++#endif
+ return;
+
+ state = vcedit_new_state();
+@@ -800,7 +810,12 @@
+ } else
+ gdk_window_raise(window->window);
+
++#ifdef HTTP_SSL
++ if (!g_strncasecmp(vte.filename, "http://", 7) ||
++ !g_strncasecmp(vte.filename, "https://", 8))
++#else
+ if (!g_strncasecmp(vte.filename, "http://", 7))
++#endif
+ gtk_widget_set_sensitive(tag_frame, FALSE);
+ else
+ gtk_widget_set_sensitive(tag_frame, TRUE);
+diff -dPNur xmms-1.2.11/Input/vorbis/http.c xmms-1.2.11-new/Input/vorbis/http.c
+--- xmms-1.2.11/Input/vorbis/http.c 2007-11-16 22:51:24.000000000 +0100
++++ xmms-1.2.11-new/Input/vorbis/http.c 2007-11-25 00:03:11.000000000 +0100
+@@ -39,6 +39,10 @@
+ #include "xmms/plugin.h"
+ #include "xmms/i18n.h"
+
++#ifdef HTTP_SSL
++#include <openssl/ssl.h>
++#endif
++
+ #define min(x,y) ((x)<(y)?(x):(y))
+ #define min3(x,y,z) (min(x,y)<(z)?min(x,y):(z))
+ #define min4(x,y,z,w) (min3(x,y,z)<(w)?min3(x,y,z):(w))
+@@ -116,7 +120,11 @@
+ return res;
+ }
+
++#ifdef HTTP_SSL
++static void parse_url(const gchar * url, gchar ** user, gchar ** pass, gchar ** host, int *port, gchar ** filename, int *ssl)
++#else
+ static void parse_url(const gchar * url, gchar ** user, gchar ** pass, gchar ** host, int *port, gchar ** filename)
++#endif
+ {
+ gchar *h, *p, *pt, *f, *temp, *ptr;
+
+@@ -125,6 +133,14 @@
+
+ if (!strncasecmp("http://", ptr, 7))
+ ptr += 7;
++#ifdef HTTP_SSL
++ if (!strncasecmp("https://", ptr, 8)) {
++ ptr += 8;
++ *ssl = 1;
++ } else
++ *ssl = 0;
++#endif
++
+ h = strchr(ptr, '@');
+ f = strchr(ptr, '/');
+ if (h != NULL && (!f || h < f))
+@@ -159,7 +175,12 @@
+ {
+ if (f)
+ *f = '\0';
+- *port = 80;
++#ifdef HTTP_SSL
++ if (*ssl)
++ *port = 443;
++ else
++#endif
++ *port = 80;
+ }
+ *host = g_strdup(h);
+
+@@ -257,16 +278,27 @@
+ return FALSE;
+ }
+
++#ifdef HTTP_SSL
++gint vorbis_http_read_line(gchar * buf, gint size, SSL *ssl_c)
++#else
+ gint vorbis_http_read_line(gchar * buf, gint size)
++#endif
+ {
+- gint i = 0;
++ gint i = 0, rc;
+
+ while (going && i < size - 1)
+ {
+ if (http_check_for_data())
+ {
+- if (read(sock, buf + i, 1) <= 0)
+- return -1;
++#ifdef HTTP_SSL
++ if (ssl_c) {
++ while ((rc = SSL_read(ssl_c, buf + i, 1)) == -1);
++ if (rc <= 0)
++ return -1;
++ } else
++#endif
++ if (read(sock, buf + i, 1) <= 0)
++ return -1;
+ if (buf[i] == '\n')
+ break;
+ if (buf[i] != '\r')
+@@ -296,6 +328,12 @@
+ struct sockaddr_in address;
+ #endif
+ struct timeval tv;
++#ifdef HTTP_SSL
++ SSL *ssl_c = NULL;
++ SSL_CTX *ssl_ctx = NULL;
++ BIO *b = NULL;
++ gint ssl=0;
++#endif
+
+ url = (gchar *) arg;
+ do
+@@ -304,7 +342,11 @@
+
+ g_strstrip(url);
+
++#ifdef HTTP_SSL
++ parse_url(url, &user, &pass, &host, &port, &filename, &ssl);
++#else
+ parse_url(url, &user, &pass, &host, &port, &filename);
++#endif
+
+ if ((!filename || !*filename) && url[strlen(url) - 1] != '/')
+ temp = g_strconcat(url, "/", NULL);
+@@ -316,6 +358,26 @@
+ chost = vorbis_cfg.use_proxy ? vorbis_cfg.proxy_host : host;
+ cport = vorbis_cfg.use_proxy ? vorbis_cfg.proxy_port : port;
+
++#ifdef HTTP_SSL
++ if (ssl) {
++ SSL_library_init();
++ OpenSSL_add_ssl_algorithms();
++ SSL_load_error_strings();
++
++ ssl_ctx = SSL_CTX_new(SSLv23_client_method());
++ if (ssl_ctx == NULL) {
++ fprintf(stderr, "SSL_CTX_new() failed.");
++ eof = TRUE;
++ }
++
++ ssl_c = SSL_new(ssl_ctx);
++ if (ssl_c == NULL) {
++ fprintf(stderr, "SSL_new() failed.\n");
++ eof = TRUE;
++ }
++ }
++#endif
++
+ #ifdef USE_IPV6
+ g_snprintf(service, 6, "%d", cport);
+ memset(&hints, 0, sizeof(hints));
+@@ -394,6 +456,18 @@
+ eof = TRUE;
+ }
+ }
++#ifdef HTTP_SSL
++ if (ssl) {
++ b = BIO_new_socket(sock, BIO_NOCLOSE);
++ if (b == NULL) {
++ printf("BIO_new_socket() failed.\n");
++ eof = TRUE;
++ }
++
++ // cannot fail
++ SSL_set_bio(ssl_c, b, b);
++ }
++#endif
+ #endif
+ while (going)
+ {
+@@ -419,6 +493,24 @@
+ break;
+ }
+ }
++#ifdef HTTP_SSL
++ if (ssl) {
++ int rc;
++
++ SSL_set_connect_state(ssl_c);
++
++ while ((rc = SSL_connect(ssl_c)) == -1);
++ if (rc <= 0) {
++ fprintf(stderr, "SSL_connect() error. SSL error code: %d.\n",
++ SSL_get_error(ssl_c, rc));
++ }
++ while ((rc = SSL_do_handshake(ssl_c)) == -1);
++ if (rc <= 0) {
++ fprintf(stderr, "SSL_do_handshake() error. SSL error code: %d.\n",
++ SSL_get_error(ssl_c, rc));
++ }
++ }
++#endif
+ if (!eof)
+ {
+ gchar *auth = NULL, *proxy_auth = NULL;
+@@ -449,14 +541,30 @@
+ g_free(proxy_auth);
+ if(auth)
+ g_free(auth);
+- write(sock, temp, strlen(temp));
++#ifdef HTTP_SSL
++ if (ssl) {
++ int rc;
++ while ((rc = SSL_write(ssl_c, temp, strlen(temp))) == -1);
++ if (rc <= 0) {
++ fprintf(stderr, "SSL_write() error. SSL error code: %d.\n",
++ SSL_get_error(ssl_c, rc));
++ eof = TRUE;
++ }
++ } else
++#endif
++ write(sock, temp, strlen(temp));
++
+ g_free(temp);
+ vorbis_ip.set_info_text(_("CONNECTED: WAITING FOR REPLY"));
+ while (going && !eof)
+ {
+ if (http_check_for_data())
+ {
++#ifdef HTTP_SSL
++ if (vorbis_http_read_line(line, 1024, ssl_c))
++#else
+ if (vorbis_http_read_line(line, 1024))
++#endif
+ {
+ status = strchr(line, ' ');
+ if (status)
+@@ -469,7 +577,11 @@
+ {
+ if(http_check_for_data())
+ {
++#ifdef HTTP_SSL
++ if((cnt = vorbis_http_read_line(line, 1024, ssl_c)) != -1)
++#else
+ if((cnt = vorbis_http_read_line(line, 1024)) != -1)
++#endif
+ {
+ if(!cnt)
+ break;
+@@ -511,7 +623,11 @@
+ {
+ if (http_check_for_data())
+ {
++#ifdef HTTP_SSL
++ if ((cnt = vorbis_http_read_line(line, 1024, ssl_c)) != -1)
++#else
+ if ((cnt = vorbis_http_read_line(line, 1024)) != -1)
++#endif
+ {
+ if (!cnt)
+ break;
+@@ -537,6 +653,10 @@
+ fclose(output_file);
+ output_file = NULL;
+ }
++#ifdef HTTP_SSL
++ if (ssl)
++ SSL_shutdown(ssl_c);
++#endif
+ close(sock);
+ g_free(user);
+ g_free(pass);
+@@ -554,6 +674,9 @@
+ fname = file;
+ if (!strncasecmp(fname, "http://", 7))
+ fname += 7;
++ if (!strncasecmp(fname, "https://", 8))
++ fname += 8;
++
+ temp = strrchr(fname, '.');
+ if (temp && !strcasecmp(temp, ".ogg"))
+ *temp = '\0';
+@@ -588,7 +711,12 @@
+ cnt = min(http_free(), buffer_length - wr_index);
+ if (cnt > 1024)
+ cnt = 1024;
+- written = read(sock, buffer + wr_index, cnt);
++#ifdef HTTP_SSL
++ if (ssl)
++ while ((written = SSL_read(ssl_c, buffer + wr_index, cnt)) == -1);
++ else
++#endif
++ written = read(sock, buffer + wr_index, cnt);
+ if (written <= 0)
+ {
+ eof = TRUE;
+@@ -629,6 +757,10 @@
+ fclose(output_file);
+ output_file = NULL;
+ }
++#ifdef HTTP_SSL
++ if (ssl)
++ SSL_shutdown(ssl_c);
++#endif
+ close(sock);
+
+
+diff -dPNur xmms-1.2.11/Input/vorbis/vorbis.c xmms-1.2.11-new/Input/vorbis/vorbis.c
+--- xmms-1.2.11/Input/vorbis/vorbis.c 2006-07-16 15:40:04.000000000 +0200
++++ xmms-1.2.11-new/Input/vorbis/vorbis.c 2007-11-25 00:00:09.000000000 +0100
+@@ -138,7 +138,12 @@
+ char *ext;
+
+ /* is this our http resource? */
++#ifdef HTTP_SSL
++ if (strncasecmp(filename, "http://", 7) == 0 ||
++ strncasecmp(filename, "https://", 8) == 0) {
++#else
+ if (strncasecmp(filename, "http://", 7) == 0) {
++#endif
+ ext = strrchr(filename, '.');
+ if (ext) {
+ if (!strncasecmp(ext, ".ogg", 4)) {
+@@ -332,7 +337,12 @@
+
+ memset(&vf, 0, sizeof(vf));
+
++#ifdef HTTP_SSL
++ if (strncasecmp("http://", filename, 7) &&
++ strncasecmp("https://", filename, 8)) {
++#else
+ if (strncasecmp("http://", filename, 7) != 0) {
++#endif
+ /* file is a real file */
+ if ((stream = fopen(filename, "r")) == NULL) {
+ vorbis_eos = TRUE;
+@@ -536,7 +546,12 @@
+ FILE *stream;
+ OggVorbis_File vf; /* avoid thread interaction */
+
++#ifdef HTTP_SSL
++ if (strncasecmp(filename, "http://", 7) &&
++ strncasecmp(filename, "https://", 8)) {
++#else
+ if (strncasecmp(filename, "http://", 7)) {
++#endif
+ if ((stream = fopen(filename, "r")) == NULL)
+ return;
+
diff --git a/ReadMe b/ReadMe
new file mode 100644
index 0000000..93308cf
--- /dev/null
+++ b/ReadMe
@@ -0,0 +1,86 @@
+Prerequisites
+-------------
+ The patched XMMS can be compiled without any additional dependency,
+ but to get recoding on you will need LibRCC (http://rusxmms.sf.net).
+ The LibRCC (Russian Charset Conversion Library) supports several
+ ways of encoding autodetection. To get this feature on the LibRCD,
+ Enca, db4 libraries should be installed prior to LibRCC compilation.
+
+ Starting from version 0.2.0 LibRCC supports language autodetection using
+ aspell and language translation using libtranslate (the internet connection
+ is required).
+
+ So, to get fully functional system, with multi-language playlist recoding,
+ you will need following libraries:
+ 1. LibRCD (http://rusxmms.sf.net)
+ 2. Enca
+ 3. DB4 (Brekley DB)
+ 4. LibXML2
+ 5. LibRCC
+ To get language auto-detection:
+ 6. Aspell with dictionaries
+ To get language translation:
+ 7. Libtranslate + Internet Connection
+
+Compilation
+-----------
+1. Copy RusXMMS into the root of xmms source distribution
+ Run 'apply.sh' to get RusXMMS with ID3v2 enabled mpg123
+ * Patches from Gentoo XMMS Distribution is used to provide ID3v2
+ Custom: Just apply patches you need
+ * all files from source/*.[ch] should be copied into the
+ libxmms directory
+2. Run 'aclocal && automake && autoconf' in the root of XMMS source tree in
+ order to regenerate Makefiles.
+3. configure, compile and install as usual
+
+Patches
+-------
+ xmms-ds-rusxmms: Base RusXMMS patch (required!)
+ xmms-ds-rusxmms-charset: UTF16/RCC for libxmms/charset.c.
+ * Required by Id3v2 tag editor.
+ xmms-ds-playlist: PlayList Recoding (Save/Load/Display)
+ * Recodes file names from playlist files
+ * Recodes titles from playlist files
+ * Recodes titles from plugins if AutoEngine enabled
+ xmms-ds-shade: Enables font selection for Shade Windows
+ * After applying the non-English titles will be
+ displayed correctly in the shade window
+ xmms-ds-textbox: Corrects UTF-8 displaying in xmms 'TextBox'
+
+ xmms-ds-mpg123: Recodes mpg123 titles
+ editor: Enables editing of ID3 tags (v.1 and v.2)
+ keys: Enables fast keys
+ Esc, Enter: Cancel, Save and Exit
+ Ctrl+Left, Ctrl+Right: Navigate tabs
+ Ctrl+PgDn, Ctrl+PgUp: Navigate records
+ xmms-ds-vorbis-*: The same functionalities for vorbis plugin
+ ( No recoding patch is required )
+
+Extra Patches
+-------------
+ Some extra functionality, which is not included in default "apply.sh"
+ scripts and could be considered a bad or/and dangerous idea.
+
+ xmms-ds-mpg123-wrongencoding.patch:
+ The ID3 v.2 defines 4 types of encodings: Latin1, UTF16, UTF16BE, UTF8.
+ Original version of XMMS assumes the tag broken if other encoding is
+ specified. This patch, assumes Latin1 for such encodings.
+
+Minimal Configurations:
+ xmms-ds-rusxmms + xmms-ds-playlist:
+ In the case then the autoengine is available, this solution will
+ give completely recoded playlist window. The recoding of plugins
+ will work as well.
+ xmms-ds-rusxmms + xmms-ds-mpg123(no editor and keys):
+ This solution will give completely recoded playlist even if no
+ autoengine is available, but playlists are unsupported.
+
+Notes
+-----
+ * Edited ID3 tags and playlists will be saved in the corresponded
+ encodings selected using 'RusXMMS' preference page.
+
+
+Project Page: http://rusxmms.sf.net
+Author: Suren A. Chilingaryan <darksoft@dside.dyndns.org>
diff --git a/apply.sh b/apply.sh
new file mode 100755
index 0000000..69c4ea4
--- /dev/null
+++ b/apply.sh
@@ -0,0 +1,50 @@
+#! /bin/bash
+
+TOP_DIR=`pwd | sed -e s/\\\/RusXMMS2$//`
+if [ ! -d $TOP_DIR/RusXMMS2 ]; then
+ PATCH_DIR=$TOP_DIR/../
+ if [ ! -d $PATCH_DIR/RusXMMS2 ]; then
+ exit 1
+ fi
+else
+ PATCH_DIR=$TOP_DIR
+fi
+
+if [ ! -d $TOP_DIR/libxmms ]; then
+ exit 1
+fi
+
+echo "Patching: $TOP_DIR"
+
+cp $PATCH_DIR/RusXMMS2/source/* $TOP_DIR/libxmms/
+
+# ID3 fixes
+cat $PATCH_DIR/RusXMMS2/3rdparty/mpg123/*.patch | patch -d $TOP_DIR -p1
+if [ $? -ne 0 ]; then echo -e "\nFailed: *** gentoo mpg123 patches ***"; exit 1; fi
+
+# RusXMMS core
+cat $PATCH_DIR/RusXMMS2/patches/xmms-ds-rusxmms.patch | patch -d $TOP_DIR -p1
+if [ $? -ne 0 ]; then echo -e "\nFailed: *** xmms-ds-rusxmms ***"; exit 1; fi
+cat $PATCH_DIR/RusXMMS2/patches/xmms-ds-rusxmms-charset.patch | patch -d $TOP_DIR -p1
+if [ $? -ne 0 ]; then echo -e "\nFailed: *** xmms-ds-rusxmms-charset ***"; exit 1; fi
+
+# RusXMMS plugins
+cat $PATCH_DIR/RusXMMS2/patches/plugins/xmms-ds-mpg123.patch | patch -d $TOP_DIR -p1
+if [ $? -ne 0 ]; then echo -e "\nFailed: *** xmms-ds-mpg123 ***"; exit 1; fi
+cat $PATCH_DIR/RusXMMS2/patches/plugins/xmms-ds-mpg123-editor.patch | patch -d $TOP_DIR -p1
+if [ $? -ne 0 ]; then echo -e "\nFailed: *** xmms-ds-mpg123-editor ***"; exit 1; fi
+cat $PATCH_DIR/RusXMMS2/patches/plugins/xmms-ds-mpg123-editor-keys.patch | patch -d $TOP_DIR -p1
+if [ $? -ne 0 ]; then echo -e "\nFailed: *** xmms-ds-mpg123-editor-keys ***"; exit 1; fi
+cat $PATCH_DIR/RusXMMS2/patches/plugins/xmms-ds-vorbis-editor.patch | patch -d $TOP_DIR -p1
+if [ $? -ne 0 ]; then echo -e "\nFailed: *** xmms-ds-vorbis-editor ***"; exit 1; fi
+cat $PATCH_DIR/RusXMMS2/patches/plugins/xmms-ds-vorbis-editor-keys.patch | patch -d $TOP_DIR -p1
+if [ $? -ne 0 ]; then echo -e "\nFailed: *** xmms-ds-vorbis-editor-keys ***"; exit 1; fi
+
+# RusXMMS extra
+cat $PATCH_DIR/RusXMMS2/patches/xmms-ds-playlist.patch | patch -d $TOP_DIR -p1
+if [ $? -ne 0 ]; then echo -e "\nFailed: *** xmms-ds-playlist ***"; exit 1; fi
+cat $PATCH_DIR/RusXMMS2/patches/xmms-ds-shade.patch | patch -d $TOP_DIR -p1
+if [ $? -ne 0 ]; then echo -e "\nFailed: *** xmms-ds-shade ***"; exit 1; fi
+cat $PATCH_DIR/RusXMMS2/patches/xmms-ds-textbox.patch | patch -d $TOP_DIR -p1
+if [ $? -ne 0 ]; then echo -e "\nFailed: *** xmms-ds-textbox ***"; exit 1; fi
+
diff --git a/configs/libtranslate/services.xml b/configs/libtranslate/services.xml
new file mode 100644
index 0000000..14d0548
--- /dev/null
+++ b/configs/libtranslate/services.xml
@@ -0,0 +1,258 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE services SYSTEM "services.dtd">
+<services>
+ <custom-language tag="zh-TW" name="Chinese (Taiwan)"/>
+
+ <service nick="Google" name="google">
+ <group>
+ <language to="*" tag="en"/>
+ <language to="en,de" tag="fr"/>
+ <language to="en,fr" tag="de"/>
+ <language to="en" tag="it"/>
+ <language to="en" tag="pt"/>
+ <language to="en" tag="es"/>
+ <language to="en" tag="pt"/>
+ <language to="en" tag="ru"/>
+ <language to="en" tag="zh"/>
+ <language to="en" tag="zh-TW"/>
+ <language to="en" tag="ja"/>
+ <language to="en" tag="ko"/>
+ <language to="en" tag="ar"/>
+ <text-translation url="http://www.google.com/translate_t?text=${text:escape}&amp;langpair=${from}|${to}&amp;ie=utf8&amp;oe=utf8">
+ <pre-marker text="id=result_box"/>
+ <pre-marker text="mouseover"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;br&gt;"/>
+ </text-translation>
+ <web-page-translation url="http://www.google.com/translate_c?u=${url:escape}&amp;langpair=${from}|${to}"/>
+ </group>
+ </service>
+
+ <service nick="FreeTranslation" name="freetranslation" max-chunk-len="600">
+ <group>
+ <language to="en" tag="nl" service-tag="dutch"/>
+ <language to="*" tag="en" service-tag="english"/>
+ <language to="en" tag="fr" service-tag="french"/>
+ <language to="en" tag="de" service-tag="german"/>
+ <language to="en" tag="it" service-tag="italian"/>
+ <language tag="no" service-tag="norwegian"/>
+ <language to="en" tag="pt" service-tag="portuguese"/>
+ <language to="en" tag="es" service-tag="spanish"/>
+ <text-translation url="http://ets.freetranslation.com/?sequence=core&amp;srctext=${text:escape}&amp;language=${from}/${to}&amp;charset=utf-8"/>
+ <web-page-translation url="http://fets5.freetranslation.com/?sequence=core&amp;url=${url:escape}&amp;language=${from}/${to}"/>
+ </group>
+ <group>
+ <language tag="zh" service-tag="simplifiedchinese"/>
+ <language tag="zh-TW" service-tag="traditionalchinese"/>
+ <language to="*" tag="en" service-tag="english"/>
+ <language to="en" tag="ru" service-tag="russian"/>
+ <text-translation url="http://ets6.freetranslation.com/?sequence=core&amp;srctext=${text:escape}&amp;language=${from}/${to}&amp;charset=utf-8"/>
+ </group>
+ <group>
+ <language to="*" tag="en" service-tag="english"/>
+ <language to="en" tag="ja" service-tag="japanese"/>
+ <text-translation url="http://tets9.freetranslation.com/?sequence=core&amp;srctext=${text:escape}&amp;language=${from}/${to}&amp;charset=utf-8"/>
+ </group>
+ </service>
+
+ <service nick="Pereklad" name="pereklad">
+ <group>
+ <language to="*" tag="ru" service-tag="Rus"/>
+ <language to="*" tag="uk" service-tag="Ukr"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:escape}">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="*" tag="en" service-tag="Eng"/>
+ <language to="*" tag="de" service-tag="Ger"/>
+ <language to="*" tag="fr" service-tag="Fra"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:escape}"
+ response-charset="ISO-8859-1">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="en,de,fr" tag="ru" service-tag="Rus"/>
+ <language to="en,de,fr" tag="uk" service-tag="Ukr"/>
+ <language to="" tag="en" service-tag="Eng"/>
+ <language to="" tag="de" service-tag="Ger"/>
+ <language to="" tag="fr" service-tag="Fra"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:escape}"
+ response-charset="ISO-8859-1">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="" tag="ru" service-tag="Rus"/>
+ <language to="" tag="uk" service-tag="Ukr"/>
+ <language to="ru,uk" tag="en" service-tag="Eng"/>
+ <language to="ru,uk" tag="de" service-tag="Ger"/>
+ <language to="ru,uk" tag="fr" service-tag="Fra"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:escape}">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="" tag="ru" service-tag="Rus"/>
+ <language to="" tag="uk" service-tag="Ukr"/>
+ <language to="ru,uk" tag="lv" service-tag="Lat"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:escape}">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="*" tag="lv" service-tag="Lat"/>
+ <language to="" tag="en" service-tag="Eng"/>
+ <language to="" tag="de" service-tag="Ger"/>
+ <language to="" tag="fr" service-tag="Fra"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:escape}"
+ response-charset="ISO-8859-1">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="" tag="lv" service-tag="Lat"/>
+ <language to="lv" tag="ru" service-tag="Rus"/>
+ <language to="lv" tag="uk" service-tag="Ukr"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:escape}"
+ response-charset="ISO-8859-4">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="" tag="lv" service-tag="Lat"/>
+ <language to="lv" tag="en" service-tag="Eng"/>
+ <language to="lv" tag="de" service-tag="Ger"/>
+ <language to="lv" tag="fr" service-tag="Fra"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:escape}"
+ response-charset="ISO-8859-4">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ </service>
+
+ <service nick="Promt" name="promt">
+ <group>
+ <language to="*" tag="ru" service-tag="r"/>
+ <language to="ru,de,fr,es" tag="en" service-tag="e"/>
+ <language to="ru,en,fr,es" tag="de" service-tag="g"/>
+ <language to="ru,en,de,es" tag="fr" service-tag="f"/>
+ <language to="ru,en" tag="it" service-tag="i"/>
+ <language to="ru,en,de,fr" tag="es" service-tag="s"/>
+ <language to="en" tag="pt" service-tag="p"/>
+ <text-translation
+ url="http://www.translate.ru/Default.aspx/Text"
+ post="__EVENTTARGET=&amp;__EVENTTARGUMENT=&amp;ctl00$SiteContent$MA_trasnlform$bTranslate=Translate&amp;ctl00$SiteContent$MA_trasnlform$DropDownList2=&amp;ctl00$SiteContent$trasnlform$sLang=${from}&amp;ctl00$SiteContent$MA_trasnlform$rLang=${to}&amp;ctl00$SiteContent$MA_trasnlform$sourceText=${text:escape}&amp;ctl00$SiteContent$MA_trasnlform$rblTemplates=General">
+ <pre-marker text="ctl00$SiteContent$MA_trasnlform$tbPismo"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea"/>
+
+ </text-translation>
+ </group>
+ </service>
+
+ <service nick="SYSTRAN" name="systran">
+ <group>
+ <language to="en" tag="ar"/>
+ <language to="en" tag="zh"/>
+ <language to="en" tag="zh-TW" service-tag="zt"/>
+ <language to="en,fr" tag="nl"/>
+ <language to="*" tag="en"/>
+ <language to="nl,en,de,it,pt,es" tag="fr"/>
+ <language to="en,fr" tag="de"/>
+ <language to="en,fr" tag="it"/>
+ <language to="en" tag="ja"/>
+ <language to="en" tag="ko"/>
+ <language to="en,fr" tag="pt"/>
+ <language to="en" tag="ru"/>
+ <language to="en,fr" tag="es"/>
+ <language to="en" tag="sv"/>
+ <http-header value="http://www.systransoft.com/" name="Referer"/>
+ <text-translation
+ url="http://www.systranet.com/tt?lp=${from}_${to}&amp;service=translate"
+ post=". ${text}.">
+ <pre-marker text=". "/>
+ <post-marker text="."/>
+ </text-translation>
+ </group>
+ </service>
+
+ <service nick="Babel Fish" name="babelfish">
+ <group>
+ <language to="en" tag="zh"/>
+ <language to="en" tag="zh-TW" service-tag="zt"/>
+ <language to="*" tag="en"/>
+ <language to="en,fr" tag="nl"/>
+ <language to="en,de,el,it,pt,nl,es" tag="fr"/>
+ <language to="en,fr" tag="de"/>
+ <language to="en,fr" tag="el"/>
+ <language to="en,fr" tag="it"/>
+ <language to="en" tag="ja"/>
+ <language to="en" tag="ko"/>
+ <language to="en,fr" tag="pt"/>
+ <language to="en" tag="ru"/>
+ <language to="en,fr" tag="es"/>
+ <text-translation
+ url="http://babelfish.yahoo.com/translate_txt"
+ post="trtext=${text:escape}&amp;lp=${from}_${to}&amp;ei=UTF-8">
+ <pre-marker text="id=&quot;result&quot;"/>
+ <pre-marker text="&lt;div"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/div"/>
+ </text-translation>
+<!--
+ <web-page-translation url="http://babelfish.altavista.com/babelfish/trurl_load?url=${url:escape}&amp;lp=${from}_${to}&amp;enc=utf8"/>
+!-->
+ </group>
+ </service>
+
+<!--
+ <service nick="Kataku" name="kataku">
+ <group>
+ <language to="*" tag="en"/>
+ <language to="*" tag="id" service-tag="in"/>
+ <text-translation url="http://www.toggletext.com/kataku_trial.php" post="input_text=${text:charset=ISO8859-1,escape}&amp;langset_text=${from}_${to}">
+ <pre-marker text="Translation:"/>
+ <pre-marker text="&lt;pre"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&#10;&lt;/pre&gt;"/>
+ </text-translation>
+ <web-page-translation url="http://www.toggletext.com/kataku_webpage_translate.php?input=${url:escape}&amp;langset=${from}_${to}"/>
+ </group>
+ </service>
+-->
+
+</services> \ No newline at end of file
diff --git a/configs/xmms/config b/configs/xmms/config
new file mode 100644
index 0000000..f3094a0
--- /dev/null
+++ b/configs/xmms/config
@@ -0,0 +1,5 @@
+[xmms]
+playlist_font=-misc-ar pl shanheisun uni-medium-r-normal-*-14-*-*-*-p-*-iso10646-1
+use_fontsets=TRUE
+mainwin_use_xfont=TRUE
+mainwin_font=-misc-ar pl shanheisun uni-medium-r-normal-*-14-*-*-*-p-*-iso10646-1
diff --git a/docs/Notes.txt b/docs/Notes.txt
new file mode 100644
index 0000000..c0a998d
--- /dev/null
+++ b/docs/Notes.txt
@@ -0,0 +1,27 @@
+Fonts
+=====
+Some fonts used together with certain broken ID3 strings (for example having
+chinese tags in ru_RU.KOI8-R locale) causes my Xorg system to crash.
+For, example:
+ -misc-ar pl new sung-medium-r-normal-*-*-100-*-*-c-*-iso10646-1
+
+The tested font configuration for RusXMMS (KOI8 locale) is:
+ shade_font=-misc-fixed-medium-r-semicondensed-*-*-120-*-*-c-*-koi8-r
+ playlist_font=-adobe_koi8_1-helvetica-bold-r-*-*-10-*
+ mainwin_font=-adobe_koi8_1-helvetica-medium-r-*-*-8-*
+
+Fonts containing both cyrillic and chinese glyphs
+ Arphic family
+
+Autotools
+==========
+I got a problem with autotools 1.9 (complaining about invalid AM_PROG_LIBTOOL),
+with 1.10 everything is smooth.
+
+Checking Actual Tags
+====================
+ eyeD3 --debug -v
+ reports invalid ecnodings as latin1
+
+ eyeD3 --strict --debug -v
+ throws ValueError exception on invalid encoding
diff --git a/docs/RusXMMS2.sxw b/docs/RusXMMS2.sxw
new file mode 100644
index 0000000..834c24c
--- /dev/null
+++ b/docs/RusXMMS2.sxw
Binary files differ
diff --git a/docs/RusXMMS2.sxw.pdf b/docs/RusXMMS2.sxw.pdf
new file mode 100644
index 0000000..c3df222
--- /dev/null
+++ b/docs/RusXMMS2.sxw.pdf
Binary files differ
diff --git a/docs/patches/amarok.txt b/docs/patches/amarok.txt
new file mode 100644
index 0000000..e2f6b5d
--- /dev/null
+++ b/docs/patches/amarok.txt
@@ -0,0 +1,27 @@
+Amarok (1.4.8) ÕÓÔÒÏÅÎ ÐÒÉÍÅÒÎÏ ÓÌÅÄÕÀÝÉÍ ÏÂÒÁÚÏÍ. åÓÌÉ ÍÙ ÉÍÅÅÍ ÄÅÌÏ Ó
+ÌÏËÁÌØÎÙÍÉ ÆÁÊÌÁÍÉ, ÔÏ ×ÓÅ ÔÜÇÉ ÐÏÌÕÞÁÀÔÓÑ ÞÅÒÅÚ Taglib (× ÆÕÎËÃÉÉ
+MetaBundle::readTags ÉÚ metabundle.cpp).
+
+ïÄÎÁËÏ, taglib (1.5 É ÒÁÎØÛÅ) ÎÅ ÕÍÅÅÔ ÚÁ ÔÁÇÁÍÉ ÐÏ ÓÅÔÉ ÌÁÚÉÔØ. ðÏ-ÜÔÏÍÕ
+smb, nfs É Ô.Ð. ÏÂÒÁÂÁÔÙ×ÁÀÔÓÑ ÐÏ ÄÒÕÇÏÍÕ.
+1. ÷Ï-ÐÅÒ×ÙÈ × ÔÏÍÖÅ metabundle.cpp ÅÓÔØ:
+ MetaBundle::init( const KFileMetaInfo& info )
+ ËÏÔÏÒÙÊ ÍÏÖÅÔ ÐÒÉÎÉÍÁÔØ ÔÜÇÉ × ÎÅËÏÊ KDEÛÎÏÊ ÓÔÒÕËÔÕÒÕ. ðÏÄÏÚÒÅ×ÁÀ, ÞÔÏ
+ ÜÔÏ Ó×ÑÚÁÎÏ Ó KIO. ÷ÐÒÏÞÅÍ, ÎÅ×ÁÖÎÏ ÄÌÑ SMB ÆÁÊÌÏ× ÜÔÁ ÆÕÎËÃÉÑ ÎÅ ÚÁÐÕÓËÁÅÔÓÑ.
+2. òÅÁÌØÎÏ ÄÌÑ smb ÆÁÊÌÏ× ÔÜÇÉ ÌÏ×ÑÔÓÑ ÆÕÎËÃÉÅÊ slotEngineMetaData (ÉÚ
+ enginecontroller.cpp). ÷ÙÚÙ×ÁÅÔÓÑ ÄÁÎÎÁÑ ÆÕÎËÃÉÑ ÐÏ ÐÒÉÈÏÄÕ ÓÉÇÎÁÌÁ
+ "metaData".
+
+ îÁÓËÏÌØËÏ Ñ ÐÏÎÑÌ, ÄÁÎÎÙÊ ÓÉÇÎÁÌ ÐÏÓÙÌÁÀÔ ×ÓÑËÉÅ enginÙ, ËÏÔÏÒÙÅ ÐÒÏÉÇÒÙ×ÁÀÔ
+ ÍÕÚÙËÕ ÄÌÑ ÁÍÁÒÏË. ôÉÐÁ engine/yauap, engine/xine É Ô.Ð. ÷ÏÔ ×ÉÄÁÔØ ÏÎÉ
+ ÞÉÔÁÀÔ ÔÜÇÉ (ËÒÉ×Ï, ÐÏÓËÏÌØËÕ ÎÅ Ó ÌÉÎËÏ×ÁÎÙ Ó taglib ÉÌÉ ÅÝÅ ÞÅÍ Ó RusXMMS
+ ÐÁÔÞÅÍ).
+
+3. íÏÖÅÔ ÅÝÅ ÞÅÇÏ ÅÓÔØ ;)
+
+
+÷ÏÏÂÝÅÍ, ÒÅÚÀÍÅ. ðÁÔÞÉÔØ ÎÁÄÏ ÌÉÂÏ ÜÔÉ Ä×ÉÖËÉ, ÌÉÂÏ, ÞÔÏ ÍÎÅ (ÎÁ ÐÅÒ×ÙÊ ×ÚÇÌÑÄ)
+ËÁÖÅÔÓÑ ÐÒÏÝÅ, ÎÁÄÏ ÐÏÄÃÅÐÉÔÓÑ Ë slotEngineMetaData. ôÒÁÂÌÁ ÔÁÍ ÔÏÌØËÏ × ÔÏÍ,
+ÞÔÏ ÐÒÉÈÏÄÉÔ ÔÕÄÁ ÔÜÇÉ ÕÖÅ × UTF-8, ÎÅËÏÒÅËÔÎÏ ÐÅÒÅËÏÄÉÒÏ×ÁÎÎÏÍ. ðÏÜÔÏÍÕ,
+ÓÎÁÞÁÌÁ ÎÁÄÏ ËÏÎ×ÅÒÔÕÔØ UTF-8 ÏÂÒÁÔÎÏ × Latin1, Á ÕÖÅ ÐÏÔÏÍ ÎÁÔÒÁ×ÌÉ×ÁÔØ
+librcc. ÷ÐÒÏÞÅÍ, ÐÅÒÅËÏÄÉÒÏ×ËÕ UTF->Latin ÔÏÖÅ ÍÏÖÎÏ ÓÄÅÌÁÔØ ÞÅÒÅÚ librcc.
diff --git a/fixes/libguess-fixes/README b/fixes/libguess-fixes/README
new file mode 100644
index 0000000..bad7594
--- /dev/null
+++ b/fixes/libguess-fixes/README
@@ -0,0 +1 @@
+This patch adds BIG5 encoding detection into the Chinese detection engine.
diff --git a/fixes/libguess-fixes/libguess-ds-cn.patch b/fixes/libguess-fixes/libguess-ds-cn.patch
new file mode 100644
index 0000000..7c2384b
--- /dev/null
+++ b/fixes/libguess-fixes/libguess-ds-cn.patch
@@ -0,0 +1,62 @@
+diff -dPNur libguess-0.2.0-d7/guess.c libguess-0.2.0-d7-new/guess.c
+--- libguess-0.2.0-d7/guess.c 2006-12-05 17:59:32.000000000 +0100
++++ libguess-0.2.0-d7-new/guess.c 2007-06-26 19:56:59.000000000 +0200
+@@ -44,7 +44,7 @@
+ /* ORDER_** &highest, &second, ... &lowest */
+ #define ORDER_JP &utf8, &sjis, &eucj
+ #define ORDER_TW &utf8, &big5
+-#define ORDER_CN &utf8, &gb2312, &gb18030
++#define ORDER_CN &utf8, &gb2312, &gb18030, &big5
+ #define ORDER_KR &utf8, &euck, &johab
+
+ /* workaround for that glib's g_convert can't convert
+@@ -252,6 +252,8 @@
+ guess_dfa gb2312 = DFA_INIT(guess_gb2312_st, guess_gb2312_ar);
+ guess_dfa utf8 = DFA_INIT(guess_utf8_st, guess_utf8_ar);
+ guess_dfa gb18030 = DFA_INIT(guess_gb18030_st, guess_gb18030_ar);
++ guess_dfa big5 = DFA_INIT(guess_big5_st, guess_big5_ar);
++
+ guess_dfa *top = NULL;
+
+ guess_dfa *order[] = { ORDER_CN, NULL };
+@@ -287,22 +289,27 @@
+ }
+
+ if (DFA_ALIVE(gb2312)) {
+- if (!DFA_ALIVE(utf8) && !DFA_ALIVE(gb18030))
++ if (!DFA_ALIVE(utf8) && !DFA_ALIVE(gb18030) && !DFA_ALIVE(big5))
+ return "GB2312";
+ DFA_NEXT(gb2312, c);
+ }
+ if (DFA_ALIVE(utf8)) {
+- if (!DFA_ALIVE(gb2312) && !DFA_ALIVE(gb18030))
++ if (!DFA_ALIVE(gb2312) && !DFA_ALIVE(gb18030) && !DFA_ALIVE(big5))
+ return "UTF-8";
+ DFA_NEXT(utf8, c);
+ }
+ if (DFA_ALIVE(gb18030)) {
+- if (!DFA_ALIVE(utf8) && !DFA_ALIVE(gb2312))
++ if (!DFA_ALIVE(utf8) && !DFA_ALIVE(gb2312) && !DFA_ALIVE(big5))
+ return "GB18030";
+ DFA_NEXT(gb18030, c);
+ }
++ if (DFA_ALIVE(big5)) {
++ if (!DFA_ALIVE(utf8) && !DFA_ALIVE(gb2312) && !DFA_ALIVE(gb18030))
++ return "big5";
++ DFA_NEXT(big5, c);
++ }
+
+- if (!DFA_ALIVE(gb2312) && !DFA_ALIVE(utf8) && !DFA_ALIVE(gb18030)) {
++ if (!DFA_ALIVE(gb2312) && !DFA_ALIVE(utf8) && !DFA_ALIVE(gb18030) && !DFA_ALIVE(big5)) {
+ /* we ran out the possibilities */
+ return NULL;
+ }
+@@ -323,6 +330,8 @@
+ return "UTF-8";
+ if (top == &gb18030)
+ return "GB18030";
++ if (top == &big5)
++ return "BIG5";
+ return NULL;
+ }
+
diff --git a/fixes/libtranslate-fixes/README b/fixes/libtranslate-fixes/README
new file mode 100644
index 0000000..814a2ee
--- /dev/null
+++ b/fixes/libtranslate-fixes/README
@@ -0,0 +1,38 @@
+It is highly recommended to apply all attached patches to libtranslate. They
+are fixing several serious bugs and providing better support of various
+translation services. Additionaly, it is highly recommended to obtain latest
+valid service description file (services.xml) from http://RusXMMS.sf.net.
+
+*** IMPORTANT ***
+Please, not you should apply "libtranslate-ds-fixcharset.patch" patch after
+"libtranslate-ds-timed.patch" otherwise the process would fail.
+
+
+Official Patches
+================
+charsetparse: Patch fixing HTTP charset parsing
+condfix: Patch fixing occasional translate_session_translate_text()
+ lockup
+
+DarkSoft Patches
+================
+memory: Fixes memory exhaustion on 64bit platforms.
+empty: Fixes segmentation in the case if the Web Server closes
+ connection prior to sending any data.
+promt: Allows "entities" keyword to send ISO8859-1 source text in the
+ HTML Entities encoding as required by translate.ru service
+ (Look usage example in services.xml)
+fixcharset: This patch enables support for translation services returning
+ wrong charset in Content-Type header (pereklad.ua for example).
+ The "response-charset" attribute could be used to declare
+ correct charset.
+ (Look usage example in services.xml)
+timed: Provides time limited translation API functions.
+
+
+
+Configuration
+=============
+services.xml Example services.xml including translate.ru and pereklad.ua
+ services. This is an example only. Please, find the latest
+ version of the service.xml on the http://rusxmms.sf.net
diff --git a/fixes/libtranslate-fixes/libtranslate-0.99-charsetparse.diff b/fixes/libtranslate-fixes/libtranslate-0.99-charsetparse.diff
new file mode 100644
index 0000000..1626f25
--- /dev/null
+++ b/fixes/libtranslate-fixes/libtranslate-0.99-charsetparse.diff
@@ -0,0 +1,47 @@
+--- src/modules/translate-generic-service.c.orig Mon Apr 11 23:08:47 2005
++++ src/modules/translate-generic-service.c Mon Apr 11 23:15:54 2005
+@@ -484,7 +484,7 @@
+
+ if (SOUP_STATUS_IS_SUCCESSFUL(message->status_code))
+ {
+- const char *charset = NULL;
++ char *charset = NULL;
+
+ if (flags & TRANSFER_CONVERT)
+ {
+@@ -493,14 +493,31 @@
+ content_type = translate_generic_service_get_header(message, &info, "Content-Type");
+ if (content_type)
+ {
+- charset = translate_ascii_strcasestr(content_type, "charset=");
+- if (charset)
+- charset += 8;
++ const char *tmp;
++
++ tmp = translate_ascii_strcasestr(content_type, "charset=");
++ if (tmp)
++ {
++ int len;
++
++ tmp += 8;
++ if (*tmp == '\'' || *tmp == '"')
++ tmp++;
++
++ len = strlen(tmp);
++ if (len > 0 && (tmp[len - 1] == '\'' || tmp[len - 1] == '"'))
++ len--;
++
++ charset = g_strndup(tmp, len);
++ }
+ }
+ }
+
+ if (charset)
+- response = g_convert(message->response.body, message->response.length, "UTF-8", charset, NULL, NULL, err);
++ {
++ response = g_convert(message->response.body, message->response.length, "UTF-8", charset, NULL, NULL, err);
++ g_free(charset);
++ }
+ else
+ {
+ if ((flags & TRANSFER_CONVERT) && ! g_utf8_validate(message->response.body, message->response.length, NULL))
diff --git a/fixes/libtranslate-fixes/libtranslate-0.99-condfix.diff b/fixes/libtranslate-fixes/libtranslate-0.99-condfix.diff
new file mode 100644
index 0000000..4011dcd
--- /dev/null
+++ b/fixes/libtranslate-fixes/libtranslate-0.99-condfix.diff
@@ -0,0 +1,38 @@
+--- src/translate-session.c.orig Mon Apr 11 22:44:53 2005
++++ src/translate-session.c Mon Apr 11 22:51:48 2005
+@@ -703,7 +703,14 @@
+ GError *tmp_err = NULL;
+
+ g_mutex_lock(info->mutex);
+- ret = info->err != NULL;
++ if (info->err)
++ {
++ ret = TRUE;
++ if (info->progress_cond)
++ g_cond_signal(info->progress_cond);
++ }
++ else
++ ret = FALSE;
+ g_mutex_unlock(info->mutex);
+
+ if (ret)
+@@ -728,6 +735,9 @@
+ else
+ g_propagate_error(&info->err, tmp_err);
+
++ if (info->progress_cond)
++ g_cond_signal(info->progress_cond);
++
+ g_mutex_unlock(info->mutex);
+
+ return;
+@@ -759,6 +769,9 @@
+ info->err = g_error_new(TRANSLATE_SESSION_ERROR,
+ TRANSLATE_SESSION_ERROR_NO_SERVICE,
+ _("no service could translate chunk"));
++
++ if (info->progress_cond)
++ g_cond_signal(info->progress_cond);
+ }
+
+ g_mutex_unlock(info->mutex);
diff --git a/fixes/libtranslate-fixes/libtranslate-ds-empty.patch b/fixes/libtranslate-fixes/libtranslate-ds-empty.patch
new file mode 100644
index 0000000..1a6a25d
--- /dev/null
+++ b/fixes/libtranslate-fixes/libtranslate-ds-empty.patch
@@ -0,0 +1,17 @@
+diff -dPNur libtranslate-0.99/src/modules/translate-generic-service.c libtranslate-0.99-new/src/modules/translate-generic-service.c
+--- libtranslate-0.99/src/modules/translate-generic-service.c 2007-06-27 17:26:10.000000000 +0200
++++ libtranslate-0.99-new/src/modules/translate-generic-service.c 2007-06-27 17:23:55.000000000 +0200
+@@ -1042,6 +1042,13 @@
+
+ g_free(response);
+ }
++
++ if (!answer) {
++ g_set_error(err,
++ TRANSLATE_GENERIC_SERVICE_ERROR,
++ TRANSLATE_GENERIC_SERVICE_ERROR_PARSE,
++ _("empty server response"));
++ }
+
+ return answer ? g_string_free(answer, FALSE) : NULL;
+ }
diff --git a/fixes/libtranslate-fixes/libtranslate-ds-fixcharset.patch b/fixes/libtranslate-fixes/libtranslate-ds-fixcharset.patch
new file mode 100644
index 0000000..0665d2f
--- /dev/null
+++ b/fixes/libtranslate-fixes/libtranslate-ds-fixcharset.patch
@@ -0,0 +1,93 @@
+diff -dPNur libtranslate-0.99-new/src/modules/translate-generic-parser.c libtranslate-0.99-new-uk/src/modules/translate-generic-parser.c
+--- libtranslate-0.99-new/src/modules/translate-generic-parser.c 2005-01-17 17:46:24.000000000 +0100
++++ libtranslate-0.99-new-uk/src/modules/translate-generic-parser.c 2007-06-27 22:40:04.000000000 +0200
+@@ -726,6 +726,7 @@
+ {
+ const char *url;
+ const char *post;
++ const char *charset;
+ const char *content_type;
+
+ g_return_if_fail(info != NULL);
+@@ -740,6 +741,7 @@
+ "url", REQUIRED, &url,
+ "post", OPTIONAL, &post,
+ "content-type", OPTIONAL, &content_type,
++ "response-charset", OPTIONAL, &charset,
+ NULL);
+
+ if (! *err)
+@@ -748,6 +750,7 @@
+ (*location)->url = g_strdup(url);
+ (*location)->post = g_strdup(post);
+ (*location)->content_type = g_strdup(content_type ? content_type : "application/x-www-form-urlencoded");
++ (*location)->response_charset = g_strdup(charset);
+ }
+ }
+
+@@ -759,6 +762,7 @@
+ g_free(location->url);
+ g_free(location->post);
+ g_free(location->content_type);
++ g_free(location->response_charset);
+ g_slist_foreach(location->http_headers, (GFunc) translate_generic_http_header_free, NULL);
+ g_slist_free(location->http_headers);
+ g_free(location);
+diff -dPNur libtranslate-0.99-new/src/modules/translate-generic-parser.h libtranslate-0.99-new-uk/src/modules/translate-generic-parser.h
+--- libtranslate-0.99-new/src/modules/translate-generic-parser.h 2005-01-17 17:46:30.000000000 +0100
++++ libtranslate-0.99-new-uk/src/modules/translate-generic-parser.h 2007-06-27 22:34:13.000000000 +0200
+@@ -51,6 +51,7 @@
+ char *url;
+ char *post;
+ char *content_type;
++ char *response_charset;
+ GSList *http_headers;
+ } TranslateGenericLocation;
+
+diff -dPNur libtranslate-0.99-new/src/modules/translate-generic-service.c libtranslate-0.99-new-uk/src/modules/translate-generic-service.c
+--- libtranslate-0.99-new/src/modules/translate-generic-service.c 2007-06-27 17:23:55.000000000 +0200
++++ libtranslate-0.99-new-uk/src/modules/translate-generic-service.c 2007-06-27 22:40:29.000000000 +0200
+@@ -129,6 +129,7 @@
+ static char *translate_generic_service_get (const char *uri,
+ const char *post,
+ const char *post_content_type,
++ const char *response_charset,
+ const GSList *headers,
+ TransferFlags flags,
+ GTimeVal *deadline,
+@@ -407,6 +408,7 @@
+ translate_generic_service_get (const char *uri,
+ const char *post,
+ const char *post_content_type,
++ const char *response_charset,
+ const GSList *headers,
+ TransferFlags flags,
+ GTimeVal *deadline,
+@@ -550,9 +552,9 @@
+ }
+ }
+
+- if (charset)
++ if ((charset)||(response_charset))
+ {
+- response = g_convert(message->response.body, message->response.length, "UTF-8", charset, NULL, NULL, err);
++ response = g_convert(message->response.body, message->response.length, "UTF-8", response_charset?response_charset:charset, NULL, NULL, err);
+ g_free(charset);
+ }
+ else
+@@ -941,6 +943,7 @@
+ response = translate_generic_service_get(url,
+ post,
+ group->text_location->content_type,
++ group->text_location->response_charset,
+ headers,
+ TRANSFER_FOLLOW_REFRESH | TRANSFER_CONVERT,
+ deadline,
+@@ -1339,6 +1342,7 @@
+ response = translate_generic_service_get(translation_url,
+ post,
+ group->web_page_location->content_type,
++ group->web_page_location->response_charset,
+ headers,
+ 0,
+ NULL,
diff --git a/fixes/libtranslate-fixes/libtranslate-ds-memory.patch b/fixes/libtranslate-fixes/libtranslate-ds-memory.patch
new file mode 100644
index 0000000..e05c7b4
--- /dev/null
+++ b/fixes/libtranslate-fixes/libtranslate-ds-memory.patch
@@ -0,0 +1,12 @@
+diff -dPNur libtranslate-0.99/src/translate-util.c libtranslate-0.99-new/src/translate-util.c
+--- libtranslate-0.99/src/translate-util.c 2005-01-17 16:45:45.000000000 +0000
++++ libtranslate-0.99-new/src/translate-util.c 2005-12-29 18:35:04.000000000 +0000
+@@ -136,7 +136,7 @@
+ g_return_val_if_fail(big != NULL, NULL);
+ g_return_val_if_fail(little != NULL, NULL);
+
+- lower_big = g_ascii_strdown(big, big_len);
++ lower_big = g_ascii_strdown(big, (int)big_len);
+ lower_little = g_ascii_strdown(little, -1);
+
+ s = strstr(lower_big, lower_little);
diff --git a/fixes/libtranslate-fixes/libtranslate-ds-promt.patch b/fixes/libtranslate-fixes/libtranslate-ds-promt.patch
new file mode 100644
index 0000000..6b30145
--- /dev/null
+++ b/fixes/libtranslate-fixes/libtranslate-ds-promt.patch
@@ -0,0 +1,27 @@
+diff -dPNur libtranslate-0.99/src/modules/translate-generic-service.c libtranslate-0.99-new/src/modules/translate-generic-service.c
+--- libtranslate-0.99/src/modules/translate-generic-service.c 2007-06-27 17:26:10.000000000 +0200
++++ libtranslate-0.99-new/src/modules/translate-generic-service.c 2007-06-27 17:23:55.000000000 +0200
+@@ -1238,7 +1245,22 @@
+ {
+ if (modifier_value)
+ g_warning(_("%s: value specified for \"escape\" modifier"), warning_prefix);
+- modified = soup_uri_encode(value, NULL);
++ modified = soup_uri_encode(value, "&");
++ }
++ else if (! strcmp(modifier_name, "entities"))
++ {
++ int i;
++ char *ptr;
++
++ modified = g_malloc(strlen(value)*6 + 1);
++ for (i=0,ptr=modified;value[i];i++) {
++ if ((unsigned char)(value[i])<160) *(ptr++)=value[i];
++ else {
++ sprintf(ptr, "&#%u;", (unsigned char)(value[i]));
++ ptr+=6;
++ }
++ }
++ *ptr = 0;
+ }
+ else if (! strcmp(modifier_name, "charset"))
+ {
diff --git a/fixes/libtranslate-fixes/libtranslate-ds-timed.patch b/fixes/libtranslate-fixes/libtranslate-ds-timed.patch
new file mode 100644
index 0000000..c98868a
--- /dev/null
+++ b/fixes/libtranslate-fixes/libtranslate-ds-timed.patch
@@ -0,0 +1,352 @@
+diff -dPNur libtranslate-0.99/src/modules/translate-generic-service.c libtranslate-0.99-new/src/modules/translate-generic-service.c
+--- libtranslate-0.99/src/modules/translate-generic-service.c 2005-01-17 17:46:38.000000000 +0100
++++ libtranslate-0.99-new/src/modules/translate-generic-service.c 2005-07-27 22:13:33.000000000 +0200
+@@ -131,6 +131,7 @@
+ const char *post_content_type,
+ const GSList *headers,
+ TransferFlags flags,
++ GTimeVal *deadline,
+ TranslateProgressFunc progress_func,
+ gpointer user_data,
+ GError **err);
+@@ -181,6 +182,15 @@
+ gpointer user_data,
+ GError **err);
+
++static char *translate_generic_service_timed_translate_text (TranslateService *service,
++ const char *text,
++ const char *from,
++ const char *to,
++ GTimeVal *deadline,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err);
++
+ char *translate_generic_service_expand (const char *warning_prefix,
+ const char *str,
+ ...);
+@@ -248,6 +258,7 @@
+
+ service_class->get_pairs = translate_generic_service_get_pairs;
+ service_class->translate_text = translate_generic_service_translate_text;
++ service_class->timed_translate_text = translate_generic_service_timed_translate_text;
+ service_class->translate_web_page = translate_generic_service_translate_web_page;
+
+ g_object_class_install_property(object_class,
+@@ -387,12 +398,18 @@
+ return TRUE; /* continue */
+ }
+
++static void send_message_cb(SoupMessage *req, gpointer user_data) {
++ g_object_ref(req);
++ *(gboolean*)user_data = TRUE;
++}
++
+ static char *
+ translate_generic_service_get (const char *uri,
+ const char *post,
+ const char *post_content_type,
+ const GSList *headers,
+ TransferFlags flags,
++ GTimeVal *deadline,
+ TranslateProgressFunc progress_func,
+ gpointer user_data,
+ GError **err)
+@@ -400,6 +417,8 @@
+ TransferInfo info;
+ SoupMessage *message;
+ const GSList *l;
++ GTimeVal tv;
++ gboolean completed = 0, canceled = 0;
+ char *response = NULL;
+
+ g_return_val_if_fail(uri != NULL, FALSE);
+@@ -479,9 +498,27 @@
+ if (translate_generic_debug_flags & TRANSLATE_GENERIC_DEBUG_LOG_TRANSFERS)
+ translate_generic_service_log_connect(message);
+
++ if (deadline) {
++ soup_session_queue_message(info.session, message, send_message_cb, &completed);
++
++ do {
++ g_main_iteration (FALSE);
++ if (completed) break;
++
++ g_get_current_time(&tv);
++ } while ((tv.tv_sec < deadline->tv_sec)||((tv.tv_sec == deadline->tv_sec)&&(tv.tv_usec < deadline->tv_usec)));
++
++ if (!completed) {
++ soup_session_cancel_message(info.session, message);
++ canceled = 1;
++ }
++ } else
+ soup_session_send_message(info.session, message);
+ g_object_unref(info.session);
+
++ if (canceled)
++ g_set_error(err, TRANSLATE_ERROR, TRANSLATE_ERROR_CANCELLED, _("Timeout"));
++ else
+ if (SOUP_STATUS_IS_SUCCESSFUL(message->status_code))
+ {
+ const char *charset = NULL;
+@@ -833,10 +870,11 @@
+ }
+
+ static char *
+-translate_generic_service_translate_text (TranslateService *service,
++translate_generic_service_timed_translate_text (TranslateService *service,
+ const char *text,
+ const char *from,
+ const char *to,
++ GTimeVal *deadline,
+ TranslateProgressFunc progress_func,
+ gpointer user_data,
+ GError **err)
+@@ -882,16 +920,16 @@
+
+ headers = g_slist_copy(group->http_headers);
+ headers = g_slist_concat(headers, g_slist_copy(group->text_location->http_headers));
+-
++
+ response = translate_generic_service_get(url,
+ post,
+ group->text_location->content_type,
+ headers,
+ TRANSFER_FOLLOW_REFRESH | TRANSFER_CONVERT,
++ deadline,
+ progress_func,
+ user_data,
+ err);
+-
+ g_free(url);
+ g_free(post);
+ g_slist_free(headers);
+@@ -991,6 +1029,18 @@
+ return answer ? g_string_free(answer, FALSE) : NULL;
+ }
+
++static char *
++translate_generic_service_translate_text (TranslateService *service,
++ const char *text,
++ const char *from,
++ const char *to,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err)
++{
++ return translate_generic_service_timed_translate_text(service, text, from, to, NULL, progress_func, user_data, err);
++}
++
+ char *
+ translate_generic_service_expand (const char *warning_prefix,
+ const char *str,
+@@ -1252,6 +1302,7 @@
+ group->web_page_location->content_type,
+ headers,
+ 0,
++ NULL,
+ progress_func,
+ user_data,
+ err);
+@@ -1311,7 +1362,7 @@
+ g_free(proxy_text_uri);
+ }
+
+- session = soup_session_sync_new_with_options(SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
++ session = soup_session_async_new_with_options(SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
+
+ if (proxy_uri)
+ soup_uri_free(proxy_uri);
+diff -dPNur libtranslate-0.99/src/translate-service.c libtranslate-0.99-new/src/translate-service.c
+--- libtranslate-0.99/src/translate-service.c 2005-01-17 17:45:23.000000000 +0100
++++ libtranslate-0.99-new/src/translate-service.c 2005-07-27 17:18:07.000000000 +0200
+@@ -372,6 +372,28 @@
+ }
+
+ char *
++translate_service_timed_translate_text (TranslateService *service,
++ const char *text,
++ const char *from,
++ const char *to,
++ GTimeVal *deadline,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err)
++{
++ g_return_val_if_fail(TRANSLATE_IS_SERVICE(service), NULL);
++ g_return_val_if_fail(TRANSLATE_SERVICE_GET_CLASS(service)->translate_text != NULL, NULL);
++ g_return_val_if_fail(text != NULL, NULL);
++ g_return_val_if_fail(from != NULL, NULL);
++ g_return_val_if_fail(to != NULL, NULL);
++
++ if (TRANSLATE_SERVICE_GET_CLASS(service)->timed_translate_text)
++ return TRANSLATE_SERVICE_GET_CLASS(service)->timed_translate_text(service, text, from, to, deadline, progress_func, user_data, err);
++
++ return TRANSLATE_SERVICE_GET_CLASS(service)->translate_text(service, text, from, to, progress_func, user_data, err);
++}
++
++char *
+ translate_service_translate_web_page (TranslateService *service,
+ const char *url,
+ const char *from,
+diff -dPNur libtranslate-0.99/src/translate-service.h libtranslate-0.99-new/src/translate-service.h
+--- libtranslate-0.99/src/translate-service.h 2005-01-17 17:45:29.000000000 +0100
++++ libtranslate-0.99-new/src/translate-service.h 2005-07-27 16:54:46.000000000 +0200
+@@ -73,6 +73,14 @@
+ TranslateProgressFunc progress_func,
+ gpointer user_data,
+ GError **err);
++ char *(*timed_translate_text)(TranslateService *service,
++ const char *text,
++ const char *from,
++ const char *to,
++ GTimeVal *deadline,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err);
+ char *(*translate_web_page) (TranslateService *service,
+ const char *url,
+ const char *from,
+diff -dPNur libtranslate-0.99/src/translate-service-private.h libtranslate-0.99-new/src/translate-service-private.h
+--- libtranslate-0.99/src/translate-service-private.h 2005-01-17 17:45:17.000000000 +0100
++++ libtranslate-0.99-new/src/translate-service-private.h 2005-07-27 17:30:00.000000000 +0200
+@@ -41,6 +41,14 @@
+ TranslateProgressFunc progress_func,
+ gpointer user_data,
+ GError **err);
++char *translate_service_timed_translate_text (TranslateService *service,
++ const char *text,
++ const char *from,
++ const char *to,
++ GTimeVal *deadline,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err);
+ char *translate_service_translate_web_page (TranslateService *service,
+ const char *url,
+ const char *from,
+diff -dPNur libtranslate-0.99/src/translate-session.c libtranslate-0.99-new/src/translate-session.c
+--- libtranslate-0.99/src/translate-session.c 2005-01-17 17:45:35.000000000 +0100
++++ libtranslate-0.99-new/src/translate-session.c 2005-07-27 16:52:46.000000000 +0200
+@@ -62,6 +62,8 @@
+ {
+ GMutex *mutex;
+ GCond *progress_cond;
++
++ GTimeVal *deadline;
+
+ TranslateSession *session;
+ GSList *services;
+@@ -487,6 +489,7 @@
+ * @text: a nul-terminated string.
+ * @from: a RFC 3066 language tag.
+ * @to: a RFC 3066 language tag.
++ * @timeout: timeout in microseconds.
+ * @progress_func: a function to call when progressing, or %NULL.
+ * @user_data: data to pass to @progress_func, or %NULL.
+ * @err: a location to report errors, or %NULL. Any of the errors in
+@@ -505,10 +508,11 @@
+ * when no longer needed.
+ **/
+ char *
+-translate_session_translate_text (TranslateSession *session,
++translate_session_timed_translate_text (TranslateSession *session,
+ const char *text,
+ const char *from,
+ const char *to,
++ gulong timeout,
+ TranslateProgressFunc progress_func,
+ gpointer user_data,
+ GError **err)
+@@ -519,6 +523,7 @@
+ unsigned int max_threads;
+ GThreadPool *pool;
+ GSList *l;
++ GTimeVal deadline;
+ unsigned int max_chunk_len = 0;
+ char *translated = NULL;
+
+@@ -527,6 +532,11 @@
+ g_return_val_if_fail(from != NULL, NULL);
+ g_return_val_if_fail(to != NULL, NULL);
+
++ if (timeout) {
++ g_get_current_time(&deadline);
++ g_time_val_add(&deadline, timeout);
++ }
++
+ LOCK(session);
+ info.services = translate_session_get_services_for_translation(session,
+ TRANSLATE_PAIR_TEXT,
+@@ -560,7 +570,8 @@
+ chunks = translate_session_split(text, max_chunk_len);
+
+ info.mutex = g_mutex_new();
+- info.progress_cond = progress_func ? g_cond_new() : NULL;
++ info.progress_cond = (progress_func||timeout) ? g_cond_new() : NULL;
++ info.deadline = timeout ? &deadline : NULL;
+ info.session = session;
+ info.chunks = NULL;
+ info.from = from;
+@@ -614,6 +625,11 @@
+ GSList *l;
+ int n_chunks;
+
++ if (timeout) {
++ if (!g_cond_timed_wait(info.progress_cond, info.mutex, info.deadline))
++ info.err = g_error_new(TRANSLATE_ERROR,TRANSLATE_ERROR_CANCELLED,_("timeout"));
++ break;
++ } else
+ g_cond_wait(info.progress_cond, info.mutex);
+
+ if (info.err)
+@@ -680,6 +696,18 @@
+ return translated;
+ }
+
++char *
++translate_session_translate_text (TranslateSession *session,
++ const char *text,
++ const char *from,
++ const char *to,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err)
++{
++ return translate_session_timed_translate_text(session, text, from, to, 0, progress_func, user_data, err);
++}
++
+ static void
+ translate_session_translate_thread (gpointer data, gpointer user_data)
+ {
+@@ -709,10 +737,11 @@
+ if (ret)
+ return;
+
+- chunk_info->translated = translate_service_translate_text(service,
++ chunk_info->translated = translate_service_timed_translate_text(service,
+ chunk_info->chunk,
+ info->from,
+ info->to,
++ info->deadline,
+ info->progress_cond ? translate_session_translate_progress_cb : NULL,
+ info->progress_cond ? chunk_info : NULL,
+ &tmp_err);
+diff -dPNur libtranslate-0.99/src/translate-session.h libtranslate-0.99-new/src/translate-session.h
+--- libtranslate-0.99/src/translate-session.h 2005-01-17 17:45:40.000000000 +0100
++++ libtranslate-0.99-new/src/translate-session.h 2005-07-27 14:41:05.000000000 +0200
+@@ -93,6 +93,14 @@
+ unsigned int translate_session_get_max_threads (TranslateSession *session);
+ int translate_session_get_max_retries (TranslateSession *session);
+
++char *translate_session_timed_translate_text (TranslateSession *session,
++ const char *text,
++ const char *from,
++ const char *to,
++ gulong timeout,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err);
+ char *translate_session_translate_text (TranslateSession *session,
+ const char *text,
+ const char *from,
diff --git a/fixes/libtranslate-fixes/services.xml b/fixes/libtranslate-fixes/services.xml
new file mode 100644
index 0000000..c27dd2e
--- /dev/null
+++ b/fixes/libtranslate-fixes/services.xml
@@ -0,0 +1,260 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE services SYSTEM "services.dtd">
+<services>
+ <custom-language tag="zh-TW" name="Chinese (Taiwan)"/>
+
+ <service nick="Google" name="google">
+ <group>
+ <language to="*" tag="en"/>
+ <language to="en,de" tag="fr"/>
+ <language to="en,fr" tag="de"/>
+ <language to="en" tag="it"/>
+ <language to="en" tag="pt"/>
+ <language to="en" tag="es"/>
+ <language to="en" tag="pt"/>
+ <language to="en" tag="ru"/>
+ <language to="en" tag="zh"/>
+ <language to="en" tag="zh-TW"/>
+ <language to="en" tag="ja"/>
+ <language to="en" tag="ko"/>
+ <language to="en" tag="ar"/>
+ <text-translation url="http://www.google.com/translate_t?text=${text:escape}&amp;langpair=${from}|${to}&amp;ie=utf8&amp;oe=utf8">
+ <pre-marker text="id=result_box"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/div&gt;"/>
+ </text-translation>
+ <web-page-translation url="http://www.google.com/translate_c?u=${url:escape}&amp;langpair=${from}|${to}"/>
+ </group>
+ </service>
+
+ <service nick="FreeTranslation" name="freetranslation" max-chunk-len="600">
+ <group>
+ <language to="en" tag="nl" service-tag="dutch"/>
+ <language to="*" tag="en" service-tag="english"/>
+ <language to="en" tag="fr" service-tag="french"/>
+ <language to="en" tag="de" service-tag="german"/>
+ <language to="en" tag="it" service-tag="italian"/>
+ <language tag="no" service-tag="norwegian"/>
+ <language to="en" tag="pt" service-tag="portuguese"/>
+ <language to="en" tag="es" service-tag="spanish"/>
+ <text-translation url="http://ets.freetranslation.com/?sequence=core&amp;srctext=${text:escape}&amp;language=${from}/${to}&amp;charset=utf-8"/>
+ <web-page-translation url="http://fets5.freetranslation.com/?sequence=core&amp;url=${url:escape}&amp;language=${from}/${to}"/>
+ </group>
+ <group>
+ <language tag="zh" service-tag="simplifiedchinese"/>
+ <language tag="zh-TW" service-tag="traditionalchinese"/>
+ <language to="*" tag="en" service-tag="english"/>
+ <language to="en" tag="ru" service-tag="russian"/>
+ <text-translation url="http://ets6.freetranslation.com/?sequence=core&amp;srctext=${text:escape}&amp;language=${from}/${to}&amp;charset=utf-8"/>
+ </group>
+ <group>
+ <language to="*" tag="en" service-tag="english"/>
+ <language to="en" tag="ja" service-tag="japanese"/>
+ <text-translation url="http://tets9.freetranslation.com/?sequence=core&amp;srctext=${text:escape}&amp;language=${from}/${to}&amp;charset=utf-8"/>
+ </group>
+ </service>
+
+ <service nick="Pereklad" name="pereklad">
+ <group>
+ <language to="*" tag="ru" service-tag="Rus"/>
+ <language to="*" tag="uk" service-tag="Ukr"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:charset=CP1251,escape}">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="*" tag="en" service-tag="Eng"/>
+ <language to="*" tag="de" service-tag="Ger"/>
+ <language to="*" tag="fr" service-tag="Fra"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:charset=ISO-8859-1,escape}"
+ response-charset="ISO-8859-1">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="en,de,fr" tag="ru" service-tag="Rus"/>
+ <language to="en,de,fr" tag="uk" service-tag="Ukr"/>
+ <language to="" tag="en" service-tag="Eng"/>
+ <language to="" tag="de" service-tag="Ger"/>
+ <language to="" tag="fr" service-tag="Fra"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:charset=CP1251,escape}"
+ response-charset="ISO-8859-1">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="" tag="ru" service-tag="Rus"/>
+ <language to="" tag="uk" service-tag="Ukr"/>
+ <language to="ru,uk" tag="en" service-tag="Eng"/>
+ <language to="ru,uk" tag="de" service-tag="Ger"/>
+ <language to="ru,uk" tag="fr" service-tag="Fra"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:charset=ISO-8859-1,escape}">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="" tag="ru" service-tag="Rus"/>
+ <language to="" tag="uk" service-tag="Ukr"/>
+ <language to="ru,uk" tag="lv" service-tag="Lat"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:charset=ISO-8859-4,escape}">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="*" tag="lv" service-tag="Lat"/>
+ <language to="" tag="en" service-tag="Eng"/>
+ <language to="" tag="de" service-tag="Ger"/>
+ <language to="" tag="fr" service-tag="Fra"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:charset=ISO-8859-4,escape}"
+ response-charset="ISO-8859-1">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="" tag="lv" service-tag="Lat"/>
+ <language to="lv" tag="ru" service-tag="Rus"/>
+ <language to="lv" tag="uk" service-tag="Ukr"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:charset=CP1251,escape}"
+ response-charset="ISO-8859-4">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="" tag="lv" service-tag="Lat"/>
+ <language to="lv" tag="en" service-tag="Eng"/>
+ <language to="lv" tag="de" service-tag="Ger"/>
+ <language to="lv" tag="fr" service-tag="Fra"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:charset=ISO-8859-1,escape}"
+ response-charset="ISO-8859-4">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ </service>
+
+ <service nick="Promt" name="promt">
+ <group>
+ <language to="" tag="ru" service-tag="r"/>
+ <language to="ru,de,fr,es" tag="en" service-tag="e"/>
+ <language to="ru,en,fr,es" tag="de" service-tag="g"/>
+ <language to="ru,en,de,es" tag="fr" service-tag="f"/>
+ <language to="ru,en" tag="it" service-tag="i"/>
+ <language to="ru,en,de,fr" tag="es" service-tag="s"/>
+ <language to="en" tag="pt" service-tag="p"/>
+ <text-translation
+ url="http://www.translate.ru/text.asp"
+ post="lang=en&amp;transliterate=ON&amp;direction=${from}${to}&amp;source=${text:charset=ISO8859-1,entities,escape}&amp;status=translate">
+ <pre-marker text="id=&quot;r_text&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/span&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="*" tag="ru" service-tag="r"/>
+ <language to="" tag="de" service-tag="g"/>
+ <language to="" tag="fr" service-tag="f"/>
+ <language to="" tag="it" service-tag="i"/>
+ <language to="" tag="es" service-tag="s"/>
+ <text-translation url="http://www.translate.ru/text.asp?lang=ru&amp;transliterate=ON&amp;direction=r${to}&amp;source=${text:charset=CP1251,escape}&amp;status=translate">
+ <pre-marker text="id=&quot;r_text&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/span&gt;"/>
+ </text-translation>
+ </group>
+ </service>
+
+ <service nick="SYSTRAN" name="systran">
+ <group>
+ <language to="en" tag="ar"/>
+ <language to="en" tag="zh"/>
+ <language to="en" tag="zh-TW" service-tag="zt"/>
+ <language to="en,fr" tag="nl"/>
+ <language to="*" tag="en"/>
+ <language to="nl,en,de,it,pt,es" tag="fr"/>
+ <language to="en,fr" tag="de"/>
+ <language to="en,fr" tag="it"/>
+ <language to="en" tag="ja"/>
+ <language to="en" tag="ko"/>
+ <language to="en,fr" tag="pt"/>
+ <language to="en" tag="ru"/>
+ <language to="en,fr" tag="es"/>
+ <language to="en" tag="sv"/>
+ <http-header value="http://www.systransoft.com/" name="Referer"/>
+ <text-translation url="http://www.systranbox.com/systran/box?systran_charset=utf-8&amp;ttype=text&amp;systran_text=${text:escape}&amp;systran_lp=${from}_${to}">
+ <pre-marker text="&lt;textarea name=&quot;translation&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ <web-page-translation url="http://www.systranbox.com/systran/box?systran_id=SystranSoft-en&amp;systran_url=${url:escape}&amp;systran_lp=${from}_${to}&amp;systran_f=${time}"/>
+ </group>
+ </service>
+
+ <service nick="Babel Fish" name="babelfish">
+ <group>
+ <language to="en" tag="zh"/>
+ <language to="en" tag="zh-TW" service-tag="zt"/>
+ <language to="*" tag="en"/>
+ <language to="en,fr" tag="nl"/>
+ <language to="en,de,el,it,pt,nl,es" tag="fr"/>
+ <language to="en,fr" tag="de"/>
+ <language to="en,fr" tag="el"/>
+ <language to="en,fr" tag="it"/>
+ <language to="en" tag="ja"/>
+ <language to="en" tag="ko"/>
+ <language to="en,fr" tag="pt"/>
+ <language to="en" tag="ru"/>
+ <language to="en,fr" tag="es"/>
+ <text-translation url="http://babelfish.altavista.com/babelfish/tr?urltext=${text:escape}&amp;lp=${from}_${to}&amp;enc=utf8">
+ <pre-marker text="&lt;form action=&quot;http://www.altavista.com/web/results"/>
+ <pre-marker text="px;&gt;"/>
+ <post-marker text="&lt;/div&gt;"/>
+ </text-translation>
+ <web-page-translation url="http://babelfish.altavista.com/babelfish/trurl_load?url=${url:escape}&amp;lp=${from}_${to}&amp;enc=utf8"/>
+ </group>
+ </service>
+
+ <service nick="Kataku" name="kataku">
+ <group>
+ <language to="*" tag="en"/>
+ <language to="*" tag="id" service-tag="in"/>
+ <text-translation url="http://www.toggletext.com/kataku_trial.php" post="input_text=${text:charset=ISO8859-1,escape}&amp;langset_text=${from}_${to}">
+ <pre-marker text="Translation:"/>
+ <pre-marker text="&lt;pre"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&#10;&lt;/pre&gt;"/>
+ </text-translation>
+ <web-page-translation url="http://www.toggletext.com/kataku_webpage_translate.php?input=${url:escape}&amp;langset=${from}_${to}"/>
+ </group>
+ </service>
+</services> \ No newline at end of file
diff --git a/fixes/mac-fixes/README b/fixes/mac-fixes/README
new file mode 100644
index 0000000..072233e
--- /dev/null
+++ b/fixes/mac-fixes/README
@@ -0,0 +1,41 @@
+mac is unsupported port for Monkey Audio Loseless Codec (ape) Acutally, it is
+known for lot of problems on 64bit platforms. Here is patch fixing another one.
+
+
+Some description for future use
+===============================
+ The problems is actully related to converting pointers to/from int type.
+The portability requires intptr_t to be used for that purposes. However,
+in many places 'int' still specified. Therefore, if acutal pointer is
+above int size on AMD64, the crashes occurs.
+
+It looks new/malloc on my Gentoo system in the beginning allocates memory
+in the lower segments and everything goes smoothly. However, after a while
+the allocation from higher regions is started. This could be traced by
+monitoring the result of
+ mac_info = (PlayerInfo *)g_malloc0(sizeof(PlayerInfo));
+ decompress = CreateIAPEDecompress(pUTF16, &nRetVal);
+in function 'decompress_init' of mac.cpp (xmms-mac). After few iterations they
+would return high address ptr. Actually, the good method of accelerating
+crashes, is usage of following code (within decompress_init):
+
+ rep:
+ decompress = CreateIAPEDecompress(pUTF16, &nRetVal);
+ if (decompress < (void*)0x7FFFFFFF) {
+ void *a=malloc((int)1000*rand());
+ delete decompress;
+ free(a);
+ goto rep;
+ }
+
+Few notes on mac
+================
+MACLib.cpp - C(stdcall) functions for class construction
+APEDecompress - main working class which widly utilizes
+UnBitArray - classes to work with actual data (many int usages within)
+ pIO - after a lot of black magick is initialiozed by function
+ call:
+ CAPEDecompress::GetInfo
+ which in his order calls:
+ CAPEInfo::GetInfo
+
diff --git a/fixes/mac-fixes/mac-3.99-u4-b5-s4-ds-fix.patch b/fixes/mac-fixes/mac-3.99-u4-b5-s4-ds-fix.patch
new file mode 100644
index 0000000..aba0379
--- /dev/null
+++ b/fixes/mac-fixes/mac-3.99-u4-b5-s4-ds-fix.patch
@@ -0,0 +1,12 @@
+diff -dPNur mac-3.99-u4-b5-s4/src/MACLib/APEDecompress.cpp mac-3.99-u4-b5-s4-new/src/MACLib/APEDecompress.cpp
+--- mac-3.99-u4-b5-s4/src/MACLib/APEDecompress.cpp 2006-06-01 11:00:58.000000000 +0200
++++ mac-3.99-u4-b5-s4-new/src/MACLib/APEDecompress.cpp 2008-04-12 17:39:39.000000000 +0200
+@@ -369,7 +369,7 @@
+ *****************************************************************************************/
+ intptr_t CAPEDecompress::GetInfo(APE_DECOMPRESS_FIELDS Field, intptr_t nParam1, intptr_t nParam2)
+ {
+- int nRetVal = 0;
++ intptr_t nRetVal = 0;
+ BOOL bHandled = TRUE;
+
+ switch (Field)
diff --git a/fixes/taglib-fixes/taglib-1.4_wchar.diff b/fixes/taglib-fixes/taglib-1.4_wchar.diff
new file mode 100644
index 0000000..b84a614
--- /dev/null
+++ b/fixes/taglib-fixes/taglib-1.4_wchar.diff
@@ -0,0 +1,31 @@
+diff -ruN taglib-1.4.org/taglib/toolkit/tstring.cpp taglib-1.4/taglib/toolkit/tstring.cpp
+--- taglib-1.4.org/taglib/toolkit/tstring.cpp 2005-07-26 06:31:15.000000000 +0900
++++ taglib-1.4/taglib/toolkit/tstring.cpp 2006-05-26 12:02:55.000000000 +0900
+@@ -202,12 +202,22 @@
+ s.resize(d->data.size());
+
+ if(!unicode) {
+- std::string::iterator targetIt = s.begin();
+- for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
+- *targetIt = char(*it);
+- ++targetIt;
++ bool cjk = false;
++ //pre-scan: is there any cjk unicode character? if so, convert the string into utf-8.
++ for(unsigned int i=0; i< d->data.size(); i++){
++ if(d->data[i] > 0xff){
++ cjk = true;
++ break;
++ }
++ }
++ if(!cjk){
++ std::string::iterator targetIt = s.begin();
++ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
++ *targetIt = char(*it);
++ ++targetIt;
++ }
++ return s;
+ }
+- return s;
+ }
+
+ const int outputBufferSize = d->data.size() * 3 + 1;
diff --git a/fixes/xmms-fixes/gcc41/xmms-1.2.10-gcc41.patch b/fixes/xmms-fixes/gcc41/xmms-1.2.10-gcc41.patch
new file mode 100644
index 0000000..0041851
--- /dev/null
+++ b/fixes/xmms-fixes/gcc41/xmms-1.2.10-gcc41.patch
@@ -0,0 +1,10 @@
+--- xmms-1.2.10.orig/Output/esd/esdout.h 2005-11-22 19:53:38.000000000 +0600
++++ xmms-1.2.10/Output/esd/esdout.h 2005-11-22 19:56:04.197329014 +0600
+@@ -58,6 +58,7 @@
+ ESDConfig;
+
+ extern ESDConfig esd_cfg;
++extern void esdout_reset_playerid(void);
+
+ void esdout_init(void);
+ void esdout_about(void);
diff --git a/fixes/xmms-fixes/gcc41/xmms-1.2.10-locale_fix.patch b/fixes/xmms-fixes/gcc41/xmms-1.2.10-locale_fix.patch
new file mode 100644
index 0000000..cfb1cd1
--- /dev/null
+++ b/fixes/xmms-fixes/gcc41/xmms-1.2.10-locale_fix.patch
@@ -0,0 +1,11 @@
+diff -ruN xmms-1.2.10.orig/xmms/playlist.c xmms-1.2.10/xmms/playlist.c
+--- xmms-1.2.10.orig/xmms/playlist.c 2005-11-25 05:14:47.000000000 +0600
++++ xmms-1.2.10/xmms/playlist.c 2005-11-25 05:18:59.894581237 +0600
+@@ -23,6 +23,7 @@
+ #include "libxmms/rcc.h"
+ #include <sys/stat.h>
+ #include <unistd.h>
++#include <locale.h>
+
+ GList *playlist = NULL;
+ GList *shuffle_list = NULL;
diff --git a/fixes/zinf-fixes/zinf-ds-noutfrecoding.patch b/fixes/zinf-fixes/zinf-ds-noutfrecoding.patch
new file mode 100644
index 0000000..6100a1d
--- /dev/null
+++ b/fixes/zinf-fixes/zinf-ds-noutfrecoding.patch
@@ -0,0 +1,20 @@
+diff -dPNur zinf-2.2.5/plm/metadata/id3lib/id3lib.cpp zinf-2.2.5-new/plm/metadata/id3lib/id3lib.cpp
+--- zinf-2.2.5/plm/metadata/id3lib/id3lib.cpp 2004-01-30 13:06:02.000000000 +0100
++++ zinf-2.2.5-new/plm/metadata/id3lib/id3lib.cpp 2005-07-19 23:08:05.000000000 +0200
+@@ -321,16 +321,8 @@
+ if (field)
+ if (field->Get (buffer, sizeof buffer) > 0) {
+ #ifdef HAVE_GLIB
+- if (g_utf8_validate (buffer, -1 , NULL)){
+ result = buffer;
+ return true;
+- }
+- else {
+- utfbuffer=g_convert(buffer, sizeof buffer, "UTF-8", charset.c_str(), NULL, NULL, NULL);
+- result=utfbuffer;
+- g_free(utfbuffer);
+- return true;
+- }
+ #else
+ result=buffer;
+ return true;
diff --git a/fixes/zinf-fixes/zinf-ds.patch b/fixes/zinf-fixes/zinf-ds.patch
new file mode 100644
index 0000000..4e4b839
--- /dev/null
+++ b/fixes/zinf-fixes/zinf-ds.patch
@@ -0,0 +1,229 @@
+diff -dPNur zinf-2.2.5/plm/metadata/id3lib/id3lib.cpp zinf-2.2.5-new/plm/metadata/id3lib/id3lib.cpp
+--- zinf-2.2.5/plm/metadata/id3lib/id3lib.cpp 2004-01-30 13:06:02.000000000 +0100
++++ zinf-2.2.5-new/plm/metadata/id3lib/id3lib.cpp 2005-07-19 23:37:11.000000000 +0200
+@@ -304,10 +304,45 @@
+ }
+
+
++static int rccGetCurrentEncoding(char *result, unsigned int n) {
++ unsigned int i;
++ char *l;
++
++ if ((!result)||(!n)) return -1;
++
++ l = getenv("CHARSET");
++#ifdef HAVE_CODESET
++ if (!l) l = nl_langinfo(CODESET);
++#endif
++ if (l) {
++ if (strlen(l)>=n) return -1;
++ strcpy(result, l);
++ return 0;
++ }
++
++ l = setlocale(LC_CTYPE, NULL);
++ if (!l) return -1;
++
++ for (i=0;((l[i])&&(l[i]!='.')&&(l[i]!='_'));i++);
++ if (i>=n) return -1;
++
++ l = strrchr(l, '.');
++ if (!l) return -1;
++
++ for (i=0;((l[i])&&(l[i]!='@'));i++);
++ if (i>=n) return -1;
++
++ strncpy(result,l+1,i-1);
++ result[i]=0;
++
++ return 0;
++}
++
+ static
+ bool getTag(ID3_Tag&tag, ID3_FrameID frameid, string &result)
+ {
+- static char buffer[1024];
++ int err;
++ static char buffer[1024], locale[64];
+ #ifdef HAVE_GLIB
+ gchar *utfbuffer;
+ #endif
+@@ -326,7 +361,8 @@
+ return true;
+ }
+ else {
+- utfbuffer=g_convert(buffer, sizeof buffer, "UTF-8", charset.c_str(), NULL, NULL, NULL);
++ err = rccGetCurrentEncoding(locale, 64);
++ utfbuffer=g_convert(buffer, sizeof buffer, "UTF-8", err?charset.c_str():locale, NULL, NULL, NULL);
+ result=utfbuffer;
+ g_free(utfbuffer);
+ return true;
+diff -dPNur zinf-2.2.5/ui/ncurses/Makefile.am zinf-2.2.5-new/ui/ncurses/Makefile.am
+--- zinf-2.2.5/ui/ncurses/Makefile.am 2003-09-16 19:35:31.000000000 +0200
++++ zinf-2.2.5-new/ui/ncurses/Makefile.am 2005-07-19 23:51:02.000000000 +0200
+@@ -3,11 +3,11 @@
+ plugin_LTLIBRARIES = ncurses-ui.la
+
+ ncurses_ui_la_SOURCES = ncursesUI.cpp
+-ncurses_ui_la_LIBADD = $(NCURSES_LIBS)
++ncurses_ui_la_LIBADD = $(NCURSES_LIBS) $(GLIB_LIBS)
+ ncurses_ui_la_LDFLAGS = $(plugin_ldflags)
+
+ noinst_HEADERS = ncursesUI.h
+
+-AM_CPPFLAGS = $(THREAD_CFLAGS) $(base_includes)
++AM_CPPFLAGS = $(THREAD_CFLAGS) $(base_includes) $(GLIB_CFLAGS)
+
+ # arch-tag: 19b44c0b-0802-4cfc-9d47-519f9049c888
+diff -dPNur zinf-2.2.5/ui/ncurses/Makefile.in zinf-2.2.5-new/ui/ncurses/Makefile.in
+--- zinf-2.2.5/ui/ncurses/Makefile.in 2004-02-09 01:48:25.000000000 +0100
++++ zinf-2.2.5-new/ui/ncurses/Makefile.in 2005-07-20 00:23:03.823574339 +0200
+@@ -231,7 +231,7 @@
+
+ noinst_HEADERS = ncursesUI.h
+
+-AM_CPPFLAGS = $(THREAD_CFLAGS) $(base_includes)
++AM_CPPFLAGS = $(THREAD_CFLAGS) $(base_includes) $(GLIB_CFLAGS)
+ subdir = ui/ncurses
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs
+diff -dPNur zinf-2.2.5/ui/ncurses/ncursesUI.cpp zinf-2.2.5-new/ui/ncurses/ncursesUI.cpp
+--- zinf-2.2.5/ui/ncurses/ncursesUI.cpp 2003-09-16 19:35:31.000000000 +0200
++++ zinf-2.2.5-new/ui/ncurses/ncursesUI.cpp 2005-07-20 00:03:31.000000000 +0200
+@@ -32,6 +32,7 @@
+ #include <termios.h>
+ #include <signal.h>
+
++
+ using namespace std;
+ #include "config.h"
+ #include "i18n.h"
+@@ -41,6 +42,10 @@
+ #include "thread.h"
+ #include "eventdata.h"
+
++#ifdef HAVE_GLIB
++#include <glib.h>
++#endif
++
+ #define stdinfd 0
+
+ extern "C" {
+@@ -262,6 +267,40 @@
+
+ }
+
++static int rccGetCurrentEncoding(char *result, unsigned int n) {
++ unsigned int i;
++ char *l;
++
++ if ((!result)||(!n)) return -1;
++
++ l = getenv("CHARSET");
++#ifdef HAVE_CODESET
++ if (!l) l = nl_langinfo(CODESET);
++#endif
++ if (l) {
++ if (strlen(l)>=n) return -1;
++ strcpy(result, l);
++ return 0;
++ }
++
++ l = setlocale(LC_CTYPE, NULL);
++ if (!l) return -1;
++
++ for (i=0;((l[i])&&(l[i]!='.')&&(l[i]!='_'));i++);
++ if (i>=n) return -1;
++
++ l = strrchr(l, '.');
++ if (!l) return -1;
++
++ for (i=0;((l[i])&&(l[i]!='@'));i++);
++ if (i>=n) return -1;
++
++ strncpy(result,l+1,i-1);
++ result[i]=0;
++
++ return 0;
++}
++
+ Error ncursesUI::AcceptEvent(Event *e) {
+ if (e) {
+ switch (e->Type()) {
+@@ -342,36 +381,69 @@
+ break;
+ md = pItem->GetMetaData();
+
++ char *recoded;
++ char locale[64];
++#ifdef HAVE_GLIB
++ if (rccGetCurrentEncoding(locale, 64)) strcpy(locale, "UTF-8");
++#else
++ recoded = NULL;
++#endif
++
++
+ clear();
+ move(0,0);
+ showInfo();
+ move(2, 0);
+ addstr(_("Title : "));
+- if (md.Title().c_str()[0] != '\0')
+- addstr((char *)md.Title().c_str());
+- else
++ if (md.Title().c_str()[0] != '\0') {
++#ifdef HAVE_GLIB
++ recoded = g_convert(md.Title().c_str(), -1, locale, "UTF-8", NULL, NULL, NULL);
++#endif
++ addstr(recoded?recoded:(char *)md.Title().c_str());
++ } else
+ addstr(pmvi->m_filename.c_str());
++
++#ifdef HAVE_GLIB
++ if (recoded) free(recoded);
++ recoded = g_convert(md.Artist().c_str(), -1, locale, "UTF-8", NULL, NULL, NULL);
++#endif
+ addstr(_("\nArtist : "));
+- addstr((char *)md.Artist().c_str());
++ addstr(recoded?recoded:(char *)md.Artist().c_str());
++#ifdef HAVE_GLIB
++ if (recoded) free(recoded);
++ recoded = g_convert(md.Album().c_str(), -1, locale, "UTF-8", NULL, NULL, NULL);
++#endif
+ addstr(_("\nAlbum : "));
+- addstr((char *)md.Album().c_str());
++ addstr(recoded?recoded:(char *)md.Album().c_str());
+ addstr(_("\nYear : "));
+ if (md.Year() != 0)
+ {
+ sprintf(buf, "%d", md.Year());
+ addstr(buf);
+ }
++#ifdef HAVE_GLIB
++ if (recoded) free(recoded);
++ recoded = g_convert(md.Genre().c_str(), -1, locale, "UTF-8", NULL, NULL, NULL);
++#endif
+ addstr(_("\nGenre : "));
+- addstr((char *)md.Genre().c_str());
++ addstr(recoded?recoded:(char *)md.Genre().c_str());
+ addstr(_("\nTrack : "));
+ if (md.Track() != 0)
+ {
+ sprintf(buf, "%d", md.Track());
+ addstr(buf);
+ }
++#ifdef HAVE_GLIB
++ if (recoded) free(recoded);
++ recoded = g_convert(md.Comment().c_str(), -1, locale, "UTF-8", NULL, NULL, NULL);
++#endif
+ addstr(_("\nComment: "));
+- addstr((char *)md.Comment().c_str());
++ addstr(recoded?recoded:(char *)md.Comment().c_str());
+ addstr("\n");
++
++#ifdef HAVE_GLIB
++ if (recoded) free(recoded);
++#endif
+ refresh();
+
+ counter = 0;
diff --git a/licenses.txt b/licenses.txt
new file mode 100644
index 0000000..aaa10a1
--- /dev/null
+++ b/licenses.txt
@@ -0,0 +1,25 @@
+taglib - LGPL 2, MPL
+libid3tag - GPL 2 or later
+xmms - GPL 2
+xmms-wma - GPL 2
+7zip - LGPL
+unzip - Info-ZIP
+gftp - GPL 2
+moc - GPL 2
+mpg123 - LGPL 2.1
+id3lib - LGPL 2
+
+------------------------
+glib2 - LGPL 2
+gtk+2 - LGPL 2
+
+iconv - LGPL 2
+libxml - BSD like
+enca - GPL 2
+db4 - Open Source License for Oracle Berkeley DB
+ (GPL compatible, but not LGPL?)
+
+
+libtranslate - BSD like?
+libguess - BSD like?
+aspell - LGPL 2.1
diff --git a/misc/brokentags.c b/misc/brokentags.c
new file mode 100644
index 0000000..e850057
--- /dev/null
+++ b/misc/brokentags.c
@@ -0,0 +1,16 @@
+/* I tried to use it to fix broken tags (call id3_get_text), but
+it really didn't help much (half of title tag, instead of one fourth) */
+void id3_fix_text(struct id3_frame *frame, int offset) {
+ int i, size;
+ char *string;
+
+ size = frame->fr_size - offset - 1;
+ string = ID3_TEXT_FRAME_PTR(frame) + offset;
+
+ for (offset=0, i=0;i<size;i++) {
+ if (!string[i]) offset++;
+ else if (offset) string[i-offset] = string[i];
+ }
+
+ for (;offset>0;offset--) string[i-offset]=0;
+}
diff --git a/patches/gftp/gftp-ds-rcc.patch b/patches/gftp/gftp-ds-rcc.patch
new file mode 100644
index 0000000..a704deb
--- /dev/null
+++ b/patches/gftp/gftp-ds-rcc.patch
@@ -0,0 +1,822 @@
+diff -dPNur gftp-2.0.18-orig/configure.in gftp-2.0.18-new/configure.in
+--- gftp-2.0.18-orig/configure.in 2005-02-04 16:42:32.000000000 +0100
++++ gftp-2.0.18-new/configure.in 2005-07-23 18:54:53.000000000 +0200
+@@ -288,6 +288,20 @@
+ fi
+ AC_SUBST(SSL_LIBS)
+
++AC_CHECK_LIB(rccui, rccUiInit,[
++ AC_CHECK_HEADERS(librcc.h librccui.h,[
++ LIBRCC_LIBS="-lrccui"
++ LIBRCC_INCLUDES="-DHAVE_LIBRCC"
++ ],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])
++AC_SUBST(LIBRCC_LIBS)
++AC_SUBST(LIBRCC_INCLUDES)
++
+ AM_GNU_GETTEXT
+
+ AC_CHECK_PROG(DB2HTML, db2html, true, false)
+diff -dPNur gftp-2.0.18-orig/lib/gftp.h gftp-2.0.18-new/lib/gftp.h
+--- gftp-2.0.18-orig/lib/gftp.h 2005-01-19 00:09:58.000000000 +0100
++++ gftp-2.0.18-new/lib/gftp.h 2005-07-23 20:57:03.000000000 +0200
+@@ -362,6 +362,7 @@
+ {
+ int protonum; /* Current number of the protocol this is
+ set to */
++ int language, charset; /* Remote language and encoding */
+ char *hostname, /* Hostname we will connect to */
+ *username, /* Username for host*/
+ *password, /* Password for host */
+@@ -928,6 +929,12 @@
+ const char *filename,
+ mode_t * mode );
+
++void gftp_set_language ( gftp_request * request,
++ int language );
++
++void gftp_set_charset ( gftp_request * request,
++ int charset );
++
+ void gftp_set_hostname ( gftp_request * request,
+ const char *hostname );
+
+diff -dPNur gftp-2.0.18-orig/lib/Makefile.am gftp-2.0.18-new/lib/Makefile.am
+--- gftp-2.0.18-orig/lib/Makefile.am 2005-01-16 17:10:12.000000000 +0100
++++ gftp-2.0.18-new/lib/Makefile.am 2005-07-23 18:58:12.000000000 +0200
+@@ -4,6 +4,6 @@
+ noinst_LIBRARIES = libgftp.a
+ libgftp_a_SOURCES=bookmark.c cache.c config_file.c fsp.c ftps.c https.c \
+ local.c misc.c mkstemps.c protocols.c pty.c rfc959.c \
+- rfc2068.c sshv2.c sslcommon.c
+-INCLUDES=@GLIB_CFLAGS@ @PTHREAD_CFLAGS@ -I../intl -DSHARE_DIR=\"$(datadir)/gftp\" -DLOCALE_DIR=\"$(datadir)/locale\"
+-noinst_HEADERS=gftp.h ftpcommon.h httpcommon.h options.h
++ rfc2068.c sshv2.c sslcommon.c rcc.c
++INCLUDES=@LIBRCC_INCLUDES@ @GLIB_CFLAGS@ @PTHREAD_CFLAGS@ -I../intl -DSHARE_DIR=\"$(datadir)/gftp\" -DLOCALE_DIR=\"$(datadir)/locale\"
++noinst_HEADERS=gftp.h ftpcommon.h httpcommon.h options.h rcc.h
+diff -dPNur gftp-2.0.18-orig/lib/rcc.c gftp-2.0.18-new/lib/rcc.c
+--- gftp-2.0.18-orig/lib/rcc.c 1970-01-01 01:00:00.000000000 +0100
++++ gftp-2.0.18-new/lib/rcc.c 2005-07-24 01:53:25.000000000 +0200
+@@ -0,0 +1,289 @@
++#include <stdlib.h>
++
++#ifdef HAVE_LIBRCC
++# include <librcc.h>
++# include <librccui.h>
++#endif /* HAVE_LIBRCC */
++
++#include "rcc.h"
++
++#ifdef HAVE_LIBRCC
++static rcc_class classes[] = {
++ { "ftp", RCC_CLASS_STANDARD, NULL, NULL, "FTP Encoding", 0 },
++ { "http", RCC_CLASS_STANDARD, NULL, NULL, "HTTP Encoding", 0 },
++ { "ssh", RCC_CLASS_STANDARD, NULL, NULL, "SSH Encoding", 0 },
++ { "fs", RCC_CLASS_STANDARD, NULL, NULL, "FS Encoding", 0 },
++ { "out", RCC_CLASS_STANDARD, NULL, NULL, "Output Encoding", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++rcc_context ctx;
++rcc_ui_context uictx;
++static int rcc_initialized = 0;
++#endif /* HAVE_LIBRCC */
++
++
++void rccPatchFree() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) {
++ rccUiFreeContext(uictx);
++ rccFreeContext(ctx);
++ rccUiFree();
++ rccFree();
++ rcc_initialized = 0;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++void rccPatchInit() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) return;
++ rccInit();
++ ctx = rccCreateContext(NULL, 0, 0, classes, 0);
++ if (ctx) {
++ rccLoad(ctx, "ftp");
++ rccInitDb4(ctx, NULL, 0);
++ rccUiInit();
++ uictx = rccUiCreateContext(ctx);
++ if (uictx) rcc_initialized = 1;
++ else {
++ rccUiFree();
++ rccFreeContext(ctx);
++ rccFree();
++ }
++ } else rccFree();
++#endif /* HAVE_LIBRCC */
++}
++
++
++#ifdef HAVE_LIBRCC
++static char *rcc_languages[RCC_MAX_LANGUAGES+1];
++static char *rcc_charsets[RCC_MAX_CHARSETS+1];
++#endif /* HAVE_LIBRCC */
++
++char **rccPatchGetLanguageList() {
++#ifdef HAVE_LIBRCC
++ unsigned int i, num;
++
++ if (rcc_initialized) {
++ num = rccGetLanguageNumber(ctx);
++ for (i=0;i<(num?num:1);i++)
++ rcc_languages[i] = (char*)rccUiGetLanguageName(uictx, (rcc_language_id)i);
++
++ rcc_languages[i] = NULL;
++ return rcc_languages;
++ }
++#endif /* HAVE_LIBRCC */
++
++ return NULL;
++
++}
++
++char **rccPatchGetCharsetList(int lid) {
++#ifdef HAVE_LIBRCC
++ unsigned int i, num;
++ rcc_language_config config;
++
++ if (rcc_initialized) {
++ config = rccGetConfig(ctx, (rcc_language_id)lid);
++ num = rccConfigGetCharsetNumber(config);
++ for (i=0;i<(num?num:1);i++)
++ rcc_charsets[i] = (char*)rccUiGetCharsetName(uictx, (rcc_language_id)lid, (rcc_class_id)0 /* first class, they are equal*/, (rcc_charset_id)i);
++
++ rcc_charsets[i] = NULL;
++ return rcc_charsets;
++ }
++#endif /* HAVE_LIBRCC */
++
++ return NULL;
++}
++
++char *rccPatchFrom(gftp_request * request, const char *str) {
++#ifdef HAVE_LIBRCC
++ rcc_class_id cl;
++ if (rcc_initialized) {
++ switch (request->protonum) {
++ case GFTP_FTP_NUM:
++ case GFTP_FTPS_NUM:
++ cl = (rcc_class_id)RCC_CLASS_FTP;
++ break;
++ case GFTP_HTTP_NUM:
++ case GFTP_HTTPS_NUM:
++ cl = (rcc_class_id)RCC_CLASS_HTTP;
++ break;
++ case GFTP_SSHV2_NUM:
++ cl = (rcc_class_id)RCC_CLASS_SSH;
++ break;
++ case GFTP_LOCAL_NUM:
++ cl = (rcc_class_id)RCC_CLASS_FS;
++ break;
++ default:
++ return NULL;
++ }
++
++ if ((cl == RCC_CLASS_FTP)||(cl == RCC_CLASS_HTTP)||(cl == RCC_CLASS_SSH)) {
++ if (request->language) rccSetLanguage(ctx, (rcc_language_id)request->language);
++ if (request->charset) rccSetCharset(ctx, cl, (rcc_charset_id)request->charset);
++ }
++
++ return rccRecodeFromCharset(ctx, cl, "UTF-8", str);
++ }
++#endif /* HAVE_LIBRCC */
++ return NULL;
++}
++
++char *rccPatchTo(gftp_request * request, const char *str) {
++#ifdef HAVE_LIBRCC
++ rcc_class_id cl;
++ if (rcc_initialized) {
++ switch (request->protonum) {
++ case GFTP_FTP_NUM:
++ case GFTP_FTPS_NUM:
++ cl = (rcc_class_id)RCC_CLASS_FTP;
++ break;
++ case GFTP_HTTP_NUM:
++ case GFTP_HTTPS_NUM:
++ cl = (rcc_class_id)RCC_CLASS_HTTP;
++ break;
++ case GFTP_SSHV2_NUM:
++ cl = (rcc_class_id)RCC_CLASS_SSH;
++ break;
++ case GFTP_LOCAL_NUM:
++ cl = (rcc_class_id)RCC_CLASS_FS;
++ break;
++ default:
++ return NULL;
++ }
++
++ if ((cl == RCC_CLASS_FTP)||(cl == RCC_CLASS_HTTP)||(cl == RCC_CLASS_SSH)) {
++ if (request->language) rccSetLanguage(ctx, (rcc_language_id)request->language);
++ if (request->charset) rccSetCharset(ctx, cl, (rcc_charset_id)request->charset);
++ }
++
++ return rccRecodeToCharset(ctx, cl, "UTF-8", str);
++ }
++#endif /* HAVE_LIBRCC */
++ return NULL;
++}
++
++char *rccPatch(gftp_request *from, gftp_request *to, const char *str) {
++ char *res, *ret;
++ const char *tmp;
++
++ if (from->protonum == to->protonum) return NULL;
++ printf("%u %u\n", from->protonum, to->protonum);
++
++ tmp = strstr(str, to->directory);
++ if (!tmp) tmp = str;
++
++ res = rccPatchTo(from, tmp);
++ if (!res) res = (char*)tmp;
++
++ ret = rccPatchFrom(to, res);
++ if (ret) {
++ if (res!=tmp) free(res);
++ } else {
++ if (res!=tmp) ret = res;
++ else return NULL;
++ }
++
++ if (tmp != str) {
++ res = (char*)malloc((strlen(ret) + (tmp-str) + 1)*sizeof(char));
++ if (res) {
++ memcpy(res, str, (tmp-str));
++ strcpy(res+(tmp-str), ret);
++ }
++ free(ret);
++ if (res) puts(res);
++ return res;
++ }
++
++ return ret;
++}
++
++char *rccPatchFromClass(gftp_request * request, int from, const char *str) {
++#ifdef HAVE_LIBRCC
++ rcc_class_id cl;
++ if (rcc_initialized) {
++ switch (request->protonum) {
++ case GFTP_FTP_NUM:
++ case GFTP_FTPS_NUM:
++ cl = (rcc_class_id)RCC_CLASS_FTP;
++ break;
++ case GFTP_HTTP_NUM:
++ case GFTP_HTTPS_NUM:
++ cl = (rcc_class_id)RCC_CLASS_HTTP;
++ break;
++ case GFTP_SSHV2_NUM:
++ cl = (rcc_class_id)RCC_CLASS_SSH;
++ break;
++ case GFTP_LOCAL_NUM:
++ cl = (rcc_class_id)RCC_CLASS_FS;
++ break;
++ default:
++ return NULL;
++ }
++
++ if ((cl == RCC_CLASS_FTP)||(cl == RCC_CLASS_HTTP)||(cl == RCC_CLASS_SSH)) {
++ if (request->language) rccSetLanguage(ctx, (rcc_language_id)request->language);
++ if (request->charset) rccSetCharset(ctx, cl, (rcc_charset_id)request->charset);
++ }
++
++ return rccRecode(ctx, (rcc_class_id)from, cl, str);
++ }
++#endif /* HAVE_LIBRCC */
++ return NULL;
++}
++
++char *rccPatchToClass(gftp_request * request, int to, const char *str) {
++#ifdef HAVE_LIBRCC
++ rcc_class_id cl;
++ if (rcc_initialized) {
++ switch (request->protonum) {
++ case GFTP_FTP_NUM:
++ case GFTP_FTPS_NUM:
++ cl = (rcc_class_id)RCC_CLASS_FTP;
++ break;
++ case GFTP_HTTP_NUM:
++ case GFTP_HTTPS_NUM:
++ cl = (rcc_class_id)RCC_CLASS_HTTP;
++ break;
++ case GFTP_SSHV2_NUM:
++ cl = (rcc_class_id)RCC_CLASS_SSH;
++ break;
++ case GFTP_LOCAL_NUM:
++ cl = (rcc_class_id)RCC_CLASS_FS;
++ break;
++ default:
++ return NULL;
++ }
++
++ if ((cl == RCC_CLASS_FTP)||(cl == RCC_CLASS_HTTP)||(cl == RCC_CLASS_SSH)) {
++ if (request->language) rccSetLanguage(ctx, (rcc_language_id)request->language);
++ if (request->charset) rccSetCharset(ctx, cl, (rcc_charset_id)request->charset);
++ }
++
++ return rccRecode(ctx, cl, (rcc_class_id)to, str);
++ }
++#endif /* HAVE_LIBRCC */
++ return NULL;
++}
++
++
++char *rccPatchUTF2OUT(const char *str) {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) {
++ return rccRecodeFromCharset(ctx, RCC_CLASS_OUT, "UTF-8", str);
++ }
++#endif /* HAVE_LIBRCC */
++ return NULL;
++}
++
++char *rccPatchOUT2UTF(const char *str) {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) {
++ return rccRecodeToCharset(ctx, RCC_CLASS_OUT, "UTF-8", str);
++ }
++#endif /* HAVE_LIBRCC */
++ return NULL;
++}
+diff -dPNur gftp-2.0.18-orig/lib/rcc.h gftp-2.0.18-new/lib/rcc.h
+--- gftp-2.0.18-orig/lib/rcc.h 1970-01-01 01:00:00.000000000 +0100
++++ gftp-2.0.18-new/lib/rcc.h 2005-07-24 01:49:20.000000000 +0200
+@@ -0,0 +1,28 @@
++#ifndef _RCC_H
++#define _RCC_H
++
++#include "gftp.h"
++
++#define RCC_CLASS_FTP 0
++#define RCC_CLASS_HTTP 1
++#define RCC_CLASS_SSH 2
++#define RCC_CLASS_FS 3
++#define RCC_CLASS_OUT 4
++
++void rccPatchFree();
++void rccPatchInit();
++
++char **rccPatchGetLanguageList();
++char **rccPatchGetCharsetList(int lid);
++
++char *rccPatchFrom(gftp_request * request, const char *str);
++char *rccPatchTo(gftp_request * request, const char *str);
++char *rccPatch(gftp_request *from, gftp_request *to, const char *str);
++
++char *rccPatchFromClass(gftp_request * request, int from, const char *str);
++char *rccPatchToClass(gftp_request * request, int to, const char *str);
++
++char *rccPatchUTF2OUT(const char *str);
++char *rccPatchOUT2UTF(const char *str);
++
++#endif /* _RCC_H */
+diff -dPNur gftp-2.0.18-orig/src/gtk/gtkui.c gftp-2.0.18-new/src/gtk/gtkui.c
+--- gftp-2.0.18-orig/src/gtk/gtkui.c 2005-01-26 04:22:05.000000000 +0100
++++ gftp-2.0.18-new/src/gtk/gtkui.c 2005-07-24 00:40:47.000000000 +0200
+@@ -351,6 +351,8 @@
+ filelist = wdata->files;
+ templist = get_next_selection (templist, &filelist, &num);
+ curfle = filelist->data;
++ cdata->source_string = gftp_string_to_utf8(wdata->request, curfle->file);
++ if (!cdata->source_string)
+ cdata->source_string = g_strdup (curfle->file);
+
+ tempstr = g_strdup_printf (_("What would you like to rename %s to?"),
+diff -dPNur gftp-2.0.18-orig/src/gtk/Makefile.am gftp-2.0.18-new/src/gtk/Makefile.am
+--- gftp-2.0.18-orig/src/gtk/Makefile.am 2005-01-16 17:12:08.000000000 +0100
++++ gftp-2.0.18-new/src/gtk/Makefile.am 2005-07-23 19:16:26.000000000 +0200
+@@ -5,6 +5,6 @@
+ gftp_gtk_SOURCES = bookmarks.c chmod_dialog.c delete_dialog.c dnd.c \
+ gftp-gtk.c gtkui.c gtkui_transfer.c menu-items.c \
+ misc-gtk.c options_dialog.c transfer.c view_dialog.c
+-INCLUDES = @GTK_CFLAGS@ @PTHREAD_CFLAGS@ -I../../intl
+-LDADD = ../../lib/libgftp.a ../../lib/fsplib/libfsp.a ../uicommon/libgftpui.a @GTK_LIBS@ @PTHREAD_LIBS@ @EXTRA_LIBS@ @GTHREAD_LIBS@ @SSL_LIBS@ @LIBINTL@
++INCLUDES = @LIBRCC_INCLUDES@ @GTK_CFLAGS@ @PTHREAD_CFLAGS@ -I../../intl
++LDADD = ../../lib/libgftp.a ../../lib/fsplib/libfsp.a ../uicommon/libgftpui.a @GTK_LIBS@ @PTHREAD_LIBS@ @EXTRA_LIBS@ @GTHREAD_LIBS@ @SSL_LIBS@ @LIBINTL@ @LIBRCC_LIBS@
+ noinst_HEADERS = gftp-gtk.h
+diff -dPNur gftp-2.0.18-orig/src/text/gftp-text.c gftp-2.0.18-new/src/text/gftp-text.c
+--- gftp-2.0.18-orig/src/text/gftp-text.c 2005-01-25 02:11:00.000000000 +0100
++++ gftp-2.0.18-new/src/text/gftp-text.c 2005-07-24 01:54:25.000000000 +0200
+@@ -18,6 +18,7 @@
+ /*****************************************************************************/
+
+ #include "gftp-text.h"
++#include "../../lib/rcc.h"
+ static const char cvsid[] = "$Id: gftp-text.c,v 1.45 2005/01/25 01:11:00 masneyb Exp $";
+
+ unsigned int
+@@ -93,6 +94,7 @@
+ const char *string, ...)
+ {
+ char tempstr[512], *utf8_str = NULL, *outstr;
++ char *locale_str;
+ va_list argp;
+
+ g_return_if_fail (string != NULL);
+@@ -140,7 +142,14 @@
+ }
+
+ if (level == gftp_logging_misc_nolog)
++{
++ locale_str = rccPatchUTF2OUT(outstr);
++ if (locale_str) {
++ printf ("%s", locale_str);
++ free(locale_str);
++ } else
+ printf ("%s", outstr);
++}
+ else
+ gftp_text_write_string (request, outstr);
+
+@@ -158,6 +167,7 @@
+ gchar *locale_question;
+ sigset_t sig, sigsave;
+ char *pos, *termname;
++ char *tmp;
+ int singlechar;
+ FILE *infd;
+
+@@ -247,6 +257,9 @@
+ #else
+ char tempstr[512];
+ #endif
++ char *recoded = NULL;
++
++ rccPatchInit();
+
+ gftpui_common_init (&argc, &argv, gftp_text_log);
+
+@@ -298,9 +311,11 @@
+ g_snprintf (prompt, sizeof (prompt), "%sftp%s> ", GFTPUI_COMMON_COLOR_BLUE, GFTPUI_COMMON_COLOR_DEFAULT);
+ while ((tempstr = readline (prompt)))
+ {
++ if (recoded) free(recoded);
++ recoded = rccPatchFromClass(gftp_text_remreq, RCC_CLASS_OUT, tempstr);
+ if (gftpui_common_process_command (locuidata, gftp_text_locreq,
+ remuidata, gftp_text_remreq,
+- tempstr) == 0)
++ recoded?recoded:tempstr) == 0)
+ break;
+
+ add_history (tempstr);
+@@ -310,16 +325,20 @@
+ printf ("%sftp%s> ", GFTPUI_COMMON_COLOR_BLUE, GFTPUI_COMMON_COLOR_DEFAULT);
+ while (fgets (tempstr, sizeof (tempstr), stdin) != NULL)
+ {
++ if (recoded) free(recoded);
++ recoded = rccPatchFromClass(gftp_tetxt_remreq, RCC_CLASS_OUT, tempstr);
+ if (gftpui_common_process_command (locuidata, gftp_text_locreq,
+ remuidata, gftp_text_remreq,
+- tempstr) == 0)
++ recoded?recoded:tempstr) == 0)
+ break;
+
+ printf ("%sftp%s> ", GFTPUI_COMMON_COLOR_BLUE, GFTPUI_COMMON_COLOR_DEFAULT);
+ }
+ #endif
+-
++ if (recoded) free(recoded);
++
+ gftp_shutdown ();
++ rccPatchFree();
+ return (0);
+ }
+
+diff -dPNur gftp-2.0.18-orig/src/text/Makefile.am gftp-2.0.18-new/src/text/Makefile.am
+--- gftp-2.0.18-orig/src/text/Makefile.am 2005-01-16 17:12:00.000000000 +0100
++++ gftp-2.0.18-new/src/text/Makefile.am 2005-07-23 19:16:39.000000000 +0200
+@@ -3,7 +3,7 @@
+ bin_PROGRAMS = @GFTP_TEXT@
+ EXTRA_PROGRAMS = gftp-text
+ gftp_text_SOURCES=gftp-text.c textui.c
+-INCLUDES=@GLIB_CFLAGS@ -I../../intl
+-LDADD = ../../lib/libgftp.a ../../lib/fsplib/libfsp.a ../uicommon/libgftpui.a @GLIB_LIBS@ @EXTRA_LIBS@ @READLINE_LIBS@ @SSL_LIBS@ @LIBINTL@
++INCLUDES=@LIBRCC_INCLUDES@ @GLIB_CFLAGS@ -I../../intl
++LDADD = ../../lib/libgftp.a ../../lib/fsplib/libfsp.a ../uicommon/libgftpui.a @GLIB_LIBS@ @EXTRA_LIBS@ @READLINE_LIBS@ @SSL_LIBS@ @LIBINTL@ @LIBRCC_LIBS@
+ noinst_HEADERS=gftp-text.h
+ localedir=$(datadir)/locale
+diff -dPNur gftp-2.0.18-orig/lib/protocols.c gftp-2.0.18-new/lib/protocols.c
+--- gftp-2.0.18-orig/lib/protocols.c 2005-07-23 15:30:59.000000000 +0200
++++ gftp-2.0.18-new/lib/protocols.c 2005-07-24 00:56:12.000000000 +0200
+@@ -18,6 +18,8 @@
+ /*****************************************************************************/
+
+ #include "gftp.h"
++#include "rcc.h"
++
+ static const char cvsid[] = "$Id: protocols.c,v 1.125 2005/01/25 02:34:18 masneyb Exp $";
+
+ gftp_request *
+@@ -26,6 +28,8 @@
+ gftp_request *request;
+
+ request = g_malloc0 (sizeof (*request));
++ request->language = 0;
++ request->charset = 0;
+ request->datafd = -1;
+ request->cachefd = -1;
+ request->server_type = GFTP_DIRTYPE_OTHER;
+@@ -460,6 +469,9 @@
+ gftp_lookup_request_option (request, "remote_charsets", &tempstr);
+ if (*tempstr == '\0')
+ {
++ ret = rccPatchTo(request, str);
++ if (ret) return ret;
++
+ error = NULL;
+ if ((ret = g_locale_to_utf8 (str, -1, &bread, &bwrite, &error)) != NULL)
+ return (ret);
+@@ -531,6 +544,9 @@
+ gftp_lookup_request_option (request, "remote_charsets", &tempstr);
+ if (*tempstr == '\0')
+ {
++ ret = rccPatchFrom(request, str);
++ if (ret) return ret;
++
+ error = NULL;
+ if ((ret = g_locale_from_utf8 (str, -1, &bread, &bwrite, &error)) != NULL)
+ return (ret);
+@@ -919,6 +935,18 @@
+ }
+
+
++void
++gftp_set_language (gftp_request * request, int language) {
++ g_return_if_fail (request != NULL);
++ request->language = language;
++}
++
++void
++gftp_set_charset (gftp_request * request, int charset) {
++ g_return_if_fail (request != NULL);
++ request->charset = charset;
++}
++
+ void
+ gftp_set_hostname (gftp_request * request, const char *hostname)
+ {
+@@ -1056,6 +1084,7 @@
+ const char *newname)
+ {
+ char *utf8;
++ char *oldutf8;
+ int ret;
+
+ g_return_val_if_fail (request != NULL, GFTP_EFATAL);
+@@ -1064,13 +1093,13 @@
+ return (GFTP_EFATAL);
+
+ utf8 = gftp_string_from_utf8 (request, newname);
++ oldutf8 = gftp_string_from_utf8 (request, oldname);
++ ret = request->rename (request, oldutf8?oldutf8:oldname, utf8?utf8:newname);
++
+ if (utf8 != NULL)
+- {
+- ret = request->rename (request, oldname, utf8);
+ g_free (utf8);
+- }
+- else
+- ret = request->rename (request, oldname, newname);
++ if (oldutf8 != NULL)
++ g_free (oldutf8);
+
+ return (ret);
+ }
+@@ -1948,11 +1977,14 @@
+ (newsize = g_hash_table_lookup (dirhash, fle->file)) != NULL)
+ fle->startsize = *newsize;
+
+- if (transfer->toreq && fle->destfile == NULL)
++ if (transfer->toreq && fle->destfile == NULL) {
++ newname = rccPatch(transfer->fromreq, transfer->toreq, fle->file);
+ fle->destfile = gftp_build_path (transfer->toreq,
+ transfer->toreq->directory,
+- fle->file, NULL);
+-
++ newname?newname:fle->file, NULL);
++ if (newname) free(newname);
++ }
++
+ if (transfer->fromreq->directory != NULL &&
+ *transfer->fromreq->directory != '\0' &&
+ *fle->file != '/')
+@@ -2026,11 +2058,15 @@
+ return (curfle->size);
+ }
+
+- if (transfer->toreq && curfle->destfile == NULL)
++ if (transfer->toreq && curfle->destfile == NULL) {
++ newname = rccPatch(transfer->fromreq, transfer->toreq, curfle->file);
+ curfle->destfile = gftp_build_path (transfer->toreq,
+ transfer->toreq->directory,
+- curfle->file, NULL);
++ newname?newname:curfle->file, NULL);
++ if (newname) free(newname);
++ }
+
++
+ if (transfer->fromreq->directory != NULL &&
+ *transfer->fromreq->directory != '\0' && *curfle->file != '/')
+ {
+diff -dPNur gftp-2.0.18-orig/src/gtk/gftp-gtk.c gftp-2.0.18-new/src/gtk/gftp-gtk.c
+--- gftp-2.0.18-orig/src/gtk/gftp-gtk.c 2005-01-25 03:34:19.000000000 +0100
++++ gftp-2.0.18-new/src/gtk/gftp-gtk.c 2005-07-23 22:22:42.000000000 +0200
+@@ -18,6 +18,7 @@
+ /*****************************************************************************/
+
+ #include "gftp-gtk.h"
++#include "../../lib/rcc.h"
+ static const char cvsid[] = "$Id: gftp-gtk.c,v 1.66 2005/01/25 02:34:19 masneyb Exp $";
+
+ static GtkItemFactory *log_factory, *dl_factory;
+@@ -28,6 +29,7 @@
+ GtkWidget * stop_btn, * hostedit, * useredit, * passedit, * portedit, * logwdw,
+ * dlwdw, * protocol_menu, * optionmenu, * gftpui_command_widget,
+ * download_left_arrow, * upload_right_arrow, * openurl_btn;
++GtkWidget * language_menu, * charset_menu;
+ GtkTooltips * openurl_tooltip;
+ GtkAdjustment * logwdw_vadj;
+ #if GTK_MAJOR_VERSION > 1
+@@ -420,6 +422,42 @@
+ return (factory->widget);
+ }
+
++#ifdef HAVE_LIBRCC
++static int rcc_current_language = 0;
++static int rcc_current_charset = 0;
++
++static void rccCharsetCB(GtkWidget * w, gpointer item) {
++ rcc_current_charset = g_list_index(GTK_MENU_SHELL(charset_menu)->children, gtk_menu_get_active(GTK_MENU(charset_menu)));
++}
++
++static void rccLanguageCB(GtkWidget * w, gpointer item) {
++ GtkWidget *tempwid, *optionmenu;
++ unsigned int i;
++ char **langs;
++ gint cur;
++
++ cur = g_list_index(GTK_MENU_SHELL(language_menu)->children, gtk_menu_get_active(GTK_MENU(language_menu)));
++ optionmenu = gtk_menu_get_attach_widget(GTK_MENU(charset_menu));
++
++ rcc_current_language = cur;
++ rcc_current_charset = 0;
++
++ langs = rccPatchGetCharsetList(cur);
++ if (langs) {
++ charset_menu = gtk_menu_new ();
++ for (i=0;langs[i];i++) {
++ tempwid = gtk_menu_item_new_with_label (langs[i]);
++ gtk_object_set_user_data (GTK_OBJECT (tempwid), GINT_TO_POINTER(i));
++ gtk_menu_append (GTK_MENU (charset_menu), tempwid);
++ gtk_widget_show (tempwid);
++ }
++
++ gtk_option_menu_remove_menu (GTK_OPTION_MENU (optionmenu));
++ gtk_option_menu_set_menu (GTK_OPTION_MENU (optionmenu), charset_menu);
++ gtk_option_menu_set_history (GTK_OPTION_MENU (optionmenu), 0);
++ }
++}
++#endif /* HAVE_LIBRCC */
+
+ static GtkWidget *
+ CreateConnectToolbar (GtkWidget * parent)
+@@ -430,14 +468,19 @@
+ {"application/x-rootwin-drop", 0, 1}
+ };
+ GtkWidget *toolbar, *box, *tempwid;
++ GtkWidget *vbox;
++ GtkWidget *tmpoptionmenu;
+ gftp_config_list_vars * tmplistvar;
+ char *default_protocol, *tempstr;
++ char **langs;
+ int i, num;
+
+ toolbar = gtk_handle_box_new ();
+
++ vbox = gtk_vbox_new (FALSE, 0);
+ box = gtk_hbox_new (FALSE, 4);
+- gtk_container_add (GTK_CONTAINER (toolbar), box);
++ gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 0);
++ gtk_container_add (GTK_CONTAINER (toolbar), vbox);
+ gtk_container_border_width (GTK_CONTAINER (box), 5);
+
+ openurl_tooltip = gtk_tooltips_new ();
+@@ -548,6 +591,7 @@
+ optionmenu = gtk_option_menu_new ();
+ gtk_box_pack_start (GTK_BOX (tempwid), optionmenu, TRUE, FALSE, 0);
+
++
+ num = 0;
+ gftp_lookup_global_option ("default_protocol", &default_protocol);
+ protocol_menu = gtk_menu_new ();
+@@ -584,6 +628,61 @@
+ gtk_container_border_width (GTK_CONTAINER (stop_btn), 1);
+ gtk_box_pack_start (GTK_BOX (box), stop_btn, FALSE, FALSE, 0);
+
++#ifdef HAVE_LIBRCC
++ box = gtk_hbox_new (FALSE, 4);
++ gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 0);
++ gtk_container_border_width (GTK_CONTAINER (box), 5);
++
++ langs = rccPatchGetCharsetList(0);
++ if (langs) {
++ tempwid = gtk_vbox_new (FALSE, 0);
++ gtk_box_pack_end (GTK_BOX (box), tempwid, FALSE, FALSE, 0);
++
++ tmpoptionmenu = gtk_option_menu_new ();
++ gtk_box_pack_start (GTK_BOX (tempwid), tmpoptionmenu, TRUE, FALSE, 0);
++
++ charset_menu = gtk_menu_new ();
++ for (i=0;langs[i];i++) {
++ tempwid = gtk_menu_item_new_with_label (langs[i]);
++ gtk_signal_connect(GTK_OBJECT(tempwid), "activate", GTK_SIGNAL_FUNC(rccCharsetCB), NULL);
++ gtk_object_set_user_data (GTK_OBJECT (tempwid), GINT_TO_POINTER(i));
++ gtk_menu_append (GTK_MENU (charset_menu), tempwid);
++ gtk_widget_show (tempwid);
++ }
++
++ gtk_option_menu_set_menu (GTK_OPTION_MENU (tmpoptionmenu), charset_menu);
++ gtk_option_menu_set_history (GTK_OPTION_MENU (tmpoptionmenu), rcc_current_charset);
++
++ tempwid = gtk_label_new (_("Charset: "));
++ gtk_box_pack_end (GTK_BOX (box), tempwid, FALSE, FALSE, 0);
++ }
++
++ langs = rccPatchGetLanguageList();
++ if (langs) {
++ tempwid = gtk_vbox_new (FALSE, 0);
++ gtk_box_pack_end (GTK_BOX (box), tempwid, FALSE, FALSE, 0);
++
++ tmpoptionmenu = gtk_option_menu_new ();
++ gtk_box_pack_start (GTK_BOX (tempwid), tmpoptionmenu, TRUE, FALSE, 0);
++
++ language_menu = gtk_menu_new ();
++ for (i=0;langs[i];i++) {
++ tempwid = gtk_menu_item_new_with_label (langs[i]);
++ gtk_signal_connect(GTK_OBJECT(tempwid), "activate", GTK_SIGNAL_FUNC(rccLanguageCB), NULL);
++ gtk_object_set_user_data (GTK_OBJECT (tempwid), GINT_TO_POINTER(i));
++ gtk_menu_append (GTK_MENU (language_menu), tempwid);
++ gtk_widget_show (tempwid);
++ }
++
++ gtk_option_menu_set_menu (GTK_OPTION_MENU (tmpoptionmenu), language_menu);
++ gtk_option_menu_set_history (GTK_OPTION_MENU (tmpoptionmenu), rcc_current_language);
++
++ tempwid = gtk_label_new (_("Language: "));
++ gtk_box_pack_end (GTK_BOX (box), tempwid, FALSE, FALSE, 0);
++ }
++#endif /* HAVE_LIBRCC */
++
++
+ gtk_widget_grab_focus (GTK_COMBO (hostedit)->entry);
+
+ return (toolbar);
+@@ -1132,6 +1231,16 @@
+ add_history (current_wdata->combo, current_wdata->history,
+ current_wdata->histlen, current_wdata->request->directory);
+
++#ifdef HAVE_LIBRCC
++ tempwid = gtk_menu_get_active (GTK_MENU (language_menu));
++ num = GPOINTER_TO_INT (gtk_object_get_user_data (GTK_OBJECT (tempwid)));
++ gftp_set_language(current_wdata->request, num);
++
++ tempwid = gtk_menu_get_active (GTK_MENU (charset_menu));
++ num = GPOINTER_TO_INT (gtk_object_get_user_data (GTK_OBJECT (tempwid)));
++ gftp_set_charset(current_wdata->request, num);
++#endif /* HAVE_LIBRCC */
++
+ ftp_connect (current_wdata, current_wdata->request, 1);
+ }
+
+@@ -1270,6 +1379,8 @@
+ {
+ GtkWidget *window, *ui;
+
++ rccPatchInit();
++
+ /* We override the read color functions because we are using a GdkColor
+ structures to store the color. If I put this in lib/config_file.c, then
+ the core library would be dependant on Gtk+ being present */
+@@ -1336,6 +1447,8 @@
+ GDK_THREADS_ENTER ();
+ gtk_main ();
+ GDK_THREADS_LEAVE ();
++
++ rccPatchFree();
+
+ return (0);
+ }
diff --git a/patches/gstreamer/gst-plugins-good-0.10.16-rusxmms.diff b/patches/gstreamer/gst-plugins-good-0.10.16-rusxmms.diff
new file mode 100644
index 0000000..95aea19
--- /dev/null
+++ b/patches/gstreamer/gst-plugins-good-0.10.16-rusxmms.diff
@@ -0,0 +1,224 @@
+diff -dPNur gst-plugins-good-0.10.16/configure.ac gst-plugins-good-0.10.16-ds/configure.ac
+--- gst-plugins-good-0.10.16/configure.ac 2009-08-29 00:05:24.000000000 +0400
++++ gst-plugins-good-0.10.16-ds/configure.ac 2009-10-29 06:20:29.000000000 +0300
+@@ -939,6 +939,13 @@
+ AC_SUBST(BZ2_LIBS)
+ ])
+
++dnl *** id3demux to use LibRCC
++translit(dnm, m, l) AM_CONDITIONAL(USE_LIBRCC, true)
++AG_GST_CHECK_FEATURE(LIBRCC, [librcc support for id3demux],, [
++ AG_GST_CHECK_LIBHEADER(LIBRCC, rcc, rccInit, ,librcc.h, LIBRCC_LIBS="-lrcc")
++ AC_SUBST(LIBRCC_LIBS)
++])
++
+ else
+
+ dnl not building plugins with external dependencies,
+@@ -974,6 +981,7 @@
+ AM_CONDITIONAL(USE_XSHM, false)
+ AM_CONDITIONAL(USE_XVIDEO, false)
+ AM_CONDITIONAL(USE_ZLIB, false)
++AM_CONDITIONAL(USE_LIBRCC, false)
+
+ fi dnl of EXT plugins
+
+diff -dPNur gst-plugins-good-0.10.16/gst/id3demux/gstid3demux.c gst-plugins-good-0.10.16-ds/gst/id3demux/gstid3demux.c
+--- gst-plugins-good-0.10.16/gst/id3demux/gstid3demux.c 2009-08-11 02:15:55.000000000 +0400
++++ gst-plugins-good-0.10.16-ds/gst/id3demux/gstid3demux.c 2009-10-29 06:44:25.000000000 +0300
+@@ -56,6 +56,7 @@
+
+ #include "gstid3demux.h"
+ #include "id3tags.h"
++#include "rccpatch.h"
+
+ static const GstElementDetails gst_id3demux_details =
+ GST_ELEMENT_DETAILS ("ID3 tag demuxer",
+@@ -107,11 +108,22 @@
+ }
+
+ static void
++gst_id3demux_finalize (GObject * object)
++{
++ GstID3Demux *demux = GST_ID3DEMUX (object);
++
++ rcc_patch_unref();
++
++ G_OBJECT_CLASS (parent_class)->finalize (object);
++}
++
++static void
+ gst_id3demux_class_init (GstID3DemuxClass * klass)
+ {
+ GstTagDemuxClass *tagdemux_class = (GstTagDemuxClass *) klass;
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
++ gobject_class->finalize = gst_id3demux_finalize;
+ gobject_class->set_property = gst_id3demux_set_property;
+ gobject_class->get_property = gst_id3demux_get_property;
+
+@@ -133,6 +145,7 @@
+ gst_id3demux_init (GstID3Demux * id3demux, GstID3DemuxClass * klass)
+ {
+ id3demux->prefer_v1 = DEFAULT_PREFER_V1;
++ rcc_patch_ref();
+ }
+
+ static gboolean
+diff -dPNur gst-plugins-good-0.10.16/gst/id3demux/id3v2frames.c gst-plugins-good-0.10.16-ds/gst/id3demux/id3v2frames.c
+--- gst-plugins-good-0.10.16/gst/id3demux/id3v2frames.c 2009-08-11 02:15:55.000000000 +0400
++++ gst-plugins-good-0.10.16-ds/gst/id3demux/id3v2frames.c 2009-10-29 06:40:17.000000000 +0300
+@@ -33,6 +33,7 @@
+ #endif
+
+ #include "id3tags.h"
++#include "rccpatch.h"
+
+ GST_DEBUG_CATEGORY_EXTERN (id3demux_debug);
+ #define GST_CAT_DEFAULT (id3demux_debug)
+@@ -986,7 +987,12 @@
+ utf8 = NULL;
+ }
+ }
+- }
++ }
++
++ /* Try LibRCC library to detect encoding */
++ utf8 = rcc_patch_to_utf8(start, size);
++ if (utf8) goto beach;
++
+ /* Try current locale (if not UTF-8) */
+ if (!g_get_charset (&env)) {
+ if ((utf8 = g_locale_to_utf8 (start, size, &bytes_read, NULL, NULL))) {
+diff -dPNur gst-plugins-good-0.10.16/gst/id3demux/Makefile.am gst-plugins-good-0.10.16-ds/gst/id3demux/Makefile.am
+--- gst-plugins-good-0.10.16/gst/id3demux/Makefile.am 2009-08-11 02:15:55.000000000 +0400
++++ gst-plugins-good-0.10.16-ds/gst/id3demux/Makefile.am 2009-10-29 06:20:06.000000000 +0300
+@@ -1,10 +1,15 @@
+ plugin_LTLIBRARIES = libgstid3demux.la
+
+-libgstid3demux_la_SOURCES = gstid3demux.c id3tags.c id3v2frames.c
++libgstid3demux_la_SOURCES = gstid3demux.c id3tags.c id3v2frames.c rccpatch.c
+ libgstid3demux_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS)
+ libgstid3demux_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgsttag-@GST_MAJORMINOR@ \
+ -lgstpbutils-@GST_MAJORMINOR@ $(GST_BASE_LIBS) $(ZLIB_LIBS)
+ libgstid3demux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+ libgstid3demux_la_LIBTOOLFLAGS = --tag=disable-static
+
+-noinst_HEADERS = gstid3demux.h id3tags.h
++if USE_LIBRCC
++ libgstid3demux_la_LIBADD += @LIBRCC_LIBS@
++endif
++
++
++noinst_HEADERS = gstid3demux.h id3tags.h rccpatch.h
+diff -dPNur gst-plugins-good-0.10.16/gst/id3demux/rccpatch.c gst-plugins-good-0.10.16-ds/gst/id3demux/rccpatch.c
+--- gst-plugins-good-0.10.16/gst/id3demux/rccpatch.c 1970-01-01 03:00:00.000000000 +0300
++++ gst-plugins-good-0.10.16-ds/gst/id3demux/rccpatch.c 2009-10-29 07:05:20.000000000 +0300
+@@ -0,0 +1,91 @@
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include <stdlib.h>
++#include "rccpatch.h"
++
++#ifdef HAVE_LIBRCC
++# include <librcc.h>
++#endif /* HAVE_LIBRCC */
++
++
++#ifdef HAVE_LIBRCC
++# define ID3_CLASS 0
++# define ID3V2_CLASS 1
++# define UTF_CLASS 2
++# define OUT_CLASS 3
++static rcc_class classes[] = {
++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
++ { "id3v2", RCC_CLASS_STANDARD, "id3", NULL, "ID3 v.2 Encoding", 0 },
++ { "utf", RCC_CLASS_KNOWN, "UTF-8", NULL, "Unicode Encoding", 0},
++ { "out", RCC_CLASS_TRANSLATE_LOCALE, "LC_CTYPE", NULL, "Output Encoding", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int rcc_initialized = 0;
++
++static rcc_context ctx = NULL;
++#endif /* HAVE_LIBRCC */
++
++
++static void rcc_patch_free() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) {
++ puts("clean");
++ rccFree();
++ rcc_initialized = 0;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++static void rcc_patch_init() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) return;
++ puts("init");
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "xmms");
++ rccInitDb4(NULL, NULL, 0);
++ rcc_initialized = 1;
++#endif /* HAVE_LIBRCC */
++}
++
++static int registered = 0;
++static int ref_count = 0;
++static GStaticMutex lock = G_STATIC_MUTEX_INIT;
++
++void rcc_patch_ref() {
++ puts("ref");
++ g_static_mutex_lock(&lock);
++ if (!registered) {
++ g_atexit(rcc_patch_free);
++ registered = 1;
++ }
++ if (++ref_count==1) rcc_patch_init();
++ g_static_mutex_unlock(&lock);
++}
++
++void rcc_patch_unref() {
++ puts("unref");
++ g_static_mutex_lock(&lock);
++ if (--ref_count==0) rcc_patch_free();
++ g_static_mutex_unlock(&lock);
++}
++
++gchar *rcc_patch_to_utf8(const gchar *str, const guint size) {
++#ifdef HAVE_LIBRCC
++ gchar *res;
++
++ if (rcc_initialized) {
++ g_static_mutex_lock(&lock);
++ res = rccSizedRecode(ctx, ID3_CLASS, UTF_CLASS, str, size, NULL);
++ g_static_mutex_unlock(&lock);
++
++ return res;
++ }
++#endif /* HAVE_LIBRCC */
++
++ return NULL;
++}
++
+diff -dPNur gst-plugins-good-0.10.16/gst/id3demux/rccpatch.h gst-plugins-good-0.10.16-ds/gst/id3demux/rccpatch.h
+--- gst-plugins-good-0.10.16/gst/id3demux/rccpatch.h 1970-01-01 03:00:00.000000000 +0300
++++ gst-plugins-good-0.10.16-ds/gst/id3demux/rccpatch.h 2009-10-29 06:23:58.000000000 +0300
+@@ -0,0 +1,12 @@
++#ifndef _RCC_PATCH_H
++#define _RCC_PATCH_H
++
++#include <gst/gst.h>
++
++void rcc_patch_ref();
++void rcc_patch_unref();
++
++gchar *rcc_patch_to_utf8(const gchar *str, const guint size);
++
++
++#endif /* _RCC_PATCH_H */
diff --git a/patches/gstreamer/gstreamer b/patches/gstreamer/gstreamer
new file mode 100644
index 0000000..86a0b5f
--- /dev/null
+++ b/patches/gstreamer/gstreamer
@@ -0,0 +1,14 @@
+totem -> gstreamer ->
+ mp3 playback [mad,ffmpeg,flump3dec]
+ id3 id3demux (plugins-good)
+ it uses tag functions (from plugins-base/gst-libs/gst/tag) to
+ access id3 v.1 and maintains id3 v.2 itself.
+
+tag contains 'gst_tag_freeform_string_to_utf8' which should be rcc'ized and
+used from id3demux (and probably other places) instead of current patch.
+
+The question is initialization...
+
+
+GST_TAG_ENCODING=CP1251 totem 01_-_vpotmah_192_lame_cbr.mp3
+
diff --git a/patches/gstreamer/libid3_problems b/patches/gstreamer/libid3_problems
new file mode 100644
index 0000000..4d74a7f
--- /dev/null
+++ b/patches/gstreamer/libid3_problems
@@ -0,0 +1,3 @@
+problems with building of libid3 complaining due to id3_frametype_* undefined are due to
+ - missing gperf and as a result empty frametype.c and compat.c generated
+ - gperf should be installed and empty frametype.c, compat.c deleted
diff --git a/patches/id3lib/id3lib-ds-rcc.patch b/patches/id3lib/id3lib-ds-rcc.patch
new file mode 100644
index 0000000..22c7aa9
--- /dev/null
+++ b/patches/id3lib/id3lib-ds-rcc.patch
@@ -0,0 +1,169 @@
+diff -dPNur id3lib-3.8.3-orig/configure.in id3lib-3.8.3-new/configure.in
+--- id3lib-3.8.3-orig/configure.in 2003-03-02 01:23:00.000000000 +0100
++++ id3lib-3.8.3-new/configure.in 2005-07-19 22:03:12.000000000 +0200
+@@ -217,6 +217,20 @@
+ LIBS="$LIBS $ICONV_LIB"
+ fi
+
++AC_CHECK_LIB(rcc, rccInit,[
++ AC_CHECK_HEADERS(librcc.h,[
++ LIBRCC_LIBS="-lrcc"
++ LIBRCC_INCLUDES="-DHAVE_LIBRCC"
++ ],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])
++AC_SUBST(LIBRCC_LIBS)
++AC_SUBST(LIBRCC_INCLUDES)
++
+ dnl Check for c++ features
+ AC_LANG_SAVE
+ AC_LANG_CPLUSPLUS
+@@ -315,5 +329,5 @@
+ src/Makefile \
+ examples/Makefile \
+ prj/Makefile \
+- libprj/Makefile
++ libprj/Makefile
+ )
+diff -dPNur id3lib-3.8.3-orig/src/field_string_ascii.cpp id3lib-3.8.3-new/src/field_string_ascii.cpp
+--- id3lib-3.8.3-orig/src/field_string_ascii.cpp 2003-03-02 01:23:00.000000000 +0100
++++ id3lib-3.8.3-new/src/field_string_ascii.cpp 2005-07-19 21:34:12.000000000 +0200
+@@ -27,6 +27,7 @@
+ #include "field_impl.h"
+ #include "id3/utils.h" // has <config.h> "id3/id3lib_streams.h" "id3/globals.h" "id3/id3lib_strings.h"
+ #include "io_helpers.h"
++#include "rccpatch.h"
+
+ using namespace dami;
+
+@@ -92,8 +93,15 @@
+ buffer != NULL && maxLength > 0)
+ {
+ String data = this->GetText();
++ char *recoded = rccPatchRecode(data.data(), (size_t)data.size(), (size_t*)&size);
++ if (recoded) {
++ size = dami::min(maxLength, size);
++ ::memcpy(buffer, recoded, size);
++ free(recoded);
++ } else {
+ size = dami::min(maxLength, data.size());
+ ::memcpy(buffer, data.data(), size);
++ }
+ if (size < maxLength)
+ {
+ buffer[size] = '\0';
+diff -dPNur id3lib-3.8.3-orig/src/Makefile.am id3lib-3.8.3-new/src/Makefile.am
+--- id3lib-3.8.3-orig/src/Makefile.am 2003-03-02 01:23:00.000000000 +0100
++++ id3lib-3.8.3-new/src/Makefile.am 2005-07-19 21:38:31.000000000 +0200
+@@ -17,7 +17,7 @@
+ zlib_include =
+ endif
+
+-INCLUDES = \
++INCLUDES = @LIBRCC_INCLUDES@ \
+ @ID3LIB_DEBUG_FLAGS@ -I$(top_srcdir)/include/id3 -I$(top_srcdir)/include $(zlib_include)
+
+ noinst_HEADERS = \
+@@ -31,7 +31,8 @@
+ header_tag.h \
+ mp3_header.h \
+ tag_impl.h \
+- spec.h
++ spec.h \
++ rccpatch.h
+
+ id3lib_sources = \
+ c_wrapper.cpp \
+@@ -66,7 +67,8 @@
+ tag_parse_v1.cpp \
+ tag_render.cpp \
+ utils.cpp \
+- writers.cpp
++ writers.cpp \
++ rccpatch.cpp
+
+ lib_LTLIBRARIES = libid3.la
+
+@@ -77,6 +79,7 @@
+ endif
+
+ libid3_la_LDFLAGS = \
++ @LIBRCC_LIBS@ \
+ -version-info $(LT_VERSION) \
+ -release $(LT_RELEASE) \
+ -export-dynamic
+diff -dPNur id3lib-3.8.3-orig/src/rccpatch.cpp id3lib-3.8.3-new/src/rccpatch.cpp
+--- id3lib-3.8.3-orig/src/rccpatch.cpp 1970-01-01 01:00:00.000000000 +0100
++++ id3lib-3.8.3-new/src/rccpatch.cpp 2005-07-19 22:10:47.000000000 +0200
+@@ -0,0 +1,60 @@
++#include <stdlib.h>
++
++#include "../config.h"
++
++#ifdef HAVE_LIBRCC
++# include <librcc.h>
++#endif /* HAVE_LIBRCC */
++
++
++#ifdef HAVE_LIBRCC
++# define ID3_CLASS 0
++# define OUT_CLASS 1
++static rcc_class classes[] = {
++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
++ { "out", RCC_CLASS_STANDARD, NULL, NULL, "Output Encoding", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int rcc_initialized = 0;
++#endif /* HAVE_LIBRCC */
++
++
++void rccPatchFree() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) {
++ rccFree();
++ rcc_initialized = 0;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++void rccPatchInit() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) return;
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "xmms");
++ rccInitDb4(NULL, NULL, 0);
++ rcc_initialized = 1;
++#endif /* HAVE_LIBRCC */
++}
++
++static void rccPatchTryInit() {
++#ifdef HAVE_LIBRCC
++ if (!rcc_initialized) {
++ rccPatchInit();
++ if (rcc_initialized) atexit(rccPatchFree);
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++char *rccPatchRecode(const char *str, size_t len, size_t *rlen) {
++#ifdef HAVE_LIBRCC
++ char *res;
++ rccPatchTryInit();
++ return rccSizedRecode(NULL, ID3_CLASS, OUT_CLASS, str, len, rlen);
++#else
++ return NULL;
++#endif /* HAVE_LIBRCC */
++}
+diff -dPNur id3lib-3.8.3-orig/src/rccpatch.h id3lib-3.8.3-new/src/rccpatch.h
+--- id3lib-3.8.3-orig/src/rccpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ id3lib-3.8.3-new/src/rccpatch.h 2005-07-19 21:34:51.000000000 +0200
+@@ -0,0 +1,3 @@
++void rccPatchFree();
++void rccPatchInit();
++char *rccPatchRecode(const char *str, size_t len, size_t *rlen);
diff --git a/patches/libid3tag/README b/patches/libid3tag/README
new file mode 100644
index 0000000..f927e0d
--- /dev/null
+++ b/patches/libid3tag/README
@@ -0,0 +1,2 @@
+Read-only patch. The configuration in 'xmms' config. Both ID3 ID3 v.2 Latin1
+encoding is taken out of 'id3' class.
diff --git a/patches/libid3tag/libid3tag-0.15.1b-ds-rcc.patch b/patches/libid3tag/libid3tag-0.15.1b-ds-rcc.patch
new file mode 100644
index 0000000..4641f3d
--- /dev/null
+++ b/patches/libid3tag/libid3tag-0.15.1b-ds-rcc.patch
@@ -0,0 +1,300 @@
+diff -dPNur libid3tag-0.15.1b/configure.ac libid3tag-0.15.1b-ds/configure.ac
+--- libid3tag-0.15.1b/configure.ac 2004-01-24 00:22:46.000000000 +0100
++++ libid3tag-0.15.1b-ds/configure.ac 2008-04-12 21:36:35.000000000 +0200
+@@ -145,6 +145,22 @@
+ *** environment variable to specify its installed location, e.g. -L<dir>.])
+ ])
+
++
++AC_CHECK_LIB(rcc, rccInit,[
++ AC_CHECK_HEADERS(librcc.h,[
++ LIBRCC_LIBS="-lrcc"
++ LIBRCC_INCLUDES="-DHAVE_LIBRCC"
++ ],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])
++AC_SUBST(LIBRCC_LIBS)
++AC_SUBST(LIBRCC_INCLUDES)
++
++
+ dnl handle --enable and --disable options
+
+ AC_CACHE_SAVE
+diff -dPNur libid3tag-0.15.1b/latin1.c libid3tag-0.15.1b-ds/latin1.c
+--- libid3tag-0.15.1b/latin1.c 2004-01-23 10:41:32.000000000 +0100
++++ libid3tag-0.15.1b-ds/latin1.c 2008-04-12 21:36:35.000000000 +0200
+@@ -31,6 +31,9 @@
+ # include "latin1.h"
+ # include "ucs4.h"
+
++# include "utf8.h"
++# include "rccpatch.h"
++
+ /*
+ * NAME: latin1->length()
+ * DESCRIPTION: return the number of ucs4 chars represented by a latin1 string
+@@ -172,6 +175,11 @@
+ id3_length_t size = 0;
+ id3_latin1_t latin1[1], *out;
+
++/*
++ Theoretically, we should add here a code for converting ucs4 to
++ recoded latin1 string. However, using non-standard latin1 tags
++ in ID3v.2 tags is completely idiotic. So, I'll not do that.
++*/
+ while (*ucs4) {
+ switch (id3_latin1_encodechar(out = latin1, *ucs4++)) {
+ case 1: size += id3_latin1_put(ptr, *out++);
+@@ -193,6 +201,7 @@
+ {
+ id3_byte_t const *end;
+ id3_latin1_t *latin1ptr, *latin1;
++ id3_utf8_t *utf8;
+ id3_ucs4_t *ucs4;
+
+ end = *ptr + length;
+@@ -207,6 +216,19 @@
+
+ *latin1ptr = 0;
+
++
++ utf8 = rccPatchLatin2UTF(latin1);
++ if (utf8) {
++ ucs4 = malloc((id3_utf8_length(utf8) + 1) * sizeof(*ucs4));
++ if (ucs4)
++ id3_utf8_decode(utf8, ucs4);
++ free(utf8);
++ free(latin1);
++
++
++ return ucs4;
++ }
++
+ ucs4 = malloc((id3_latin1_length(latin1) + 1) * sizeof(*ucs4));
+ if (ucs4)
+ id3_latin1_decode(latin1, ucs4);
+diff -dPNur libid3tag-0.15.1b/Makefile.am libid3tag-0.15.1b-ds/Makefile.am
+--- libid3tag-0.15.1b/Makefile.am 2004-02-17 03:11:28.000000000 +0100
++++ libid3tag-0.15.1b-ds/Makefile.am 2008-04-12 21:36:35.000000000 +0200
+@@ -81,6 +81,7 @@
+ libid3tag_la_SOURCES = version.c ucs4.c latin1.c utf16.c utf8.c \
+ parse.c render.c field.c frametype.c compat.c \
+ genre.c frame.c crc.c util.c tag.c file.c \
++ rccpatch.c rccpatch.h \
+ version.h ucs4.h latin1.h utf16.h utf8.h \
+ parse.h render.h field.h frametype.h compat.h \
+ genre.h frame.h crc.h util.h tag.h file.h \
+@@ -90,7 +91,8 @@
+ frametype.gperf compat.gperf genre.dat.in \
+ debug.c debug.h
+
+-libid3tag_la_LDFLAGS = -version-info $(version_info)
++INCLUDES = @LIBRCC_INCLUDES@
++libid3tag_la_LDFLAGS = -version-info $(version_info) @LIBRCC_LIBS@
+
+ BUILT_SOURCES = frametype.c compat.c genre.dat
+
+diff -dPNur libid3tag-0.15.1b/rccpatch.c libid3tag-0.15.1b-ds/rccpatch.c
+--- libid3tag-0.15.1b/rccpatch.c 1970-01-01 01:00:00.000000000 +0100
++++ libid3tag-0.15.1b-ds/rccpatch.c 2008-04-12 21:36:35.000000000 +0200
+@@ -0,0 +1,96 @@
++#include <stdlib.h>
++#include "rccpatch.h"
++
++#ifdef HAVE_LIBRCC
++# include <librcc.h>
++#endif /* HAVE_LIBRCC */
++
++
++#ifdef HAVE_LIBRCC
++# define ID3_CLASS 0
++# define ID3V2_CLASS 1
++# define UTF_CLASS 2
++# define OUT_CLASS 3
++static rcc_class classes[] = {
++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
++ { "id3v2", RCC_CLASS_STANDARD, "id3", NULL, "ID3 v.2 Encoding", 0 },
++ { "utf", RCC_CLASS_KNOWN, "UTF-8", NULL, "Unicode Encoding", 0},
++ { "out", RCC_CLASS_TRANSLATE_LOCALE, "LC_CTYPE", NULL, "Output Encoding", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int rcc_initialized = 0;
++
++static rcc_context ctx = NULL;
++#endif /* HAVE_LIBRCC */
++
++
++void rccPatchFree() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) {
++ rccFree();
++ rcc_initialized = 0;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++void rccPatchInit() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) return;
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "xmms");
++ rccInitDb4(NULL, NULL, 0);
++ rcc_initialized = 1;
++#endif /* HAVE_LIBRCC */
++}
++
++void rccPatchSetContext(void *newctx) {
++#ifdef HAVE_LIBRCC
++ if (newctx) {
++ ctx = (rcc_context)newctx;
++ rcc_initialized = 1;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++static void rccPatchTryInit() {
++#ifdef HAVE_LIBRCC
++ if (!rcc_initialized) {
++ rccPatchInit();
++ if (rcc_initialized) atexit(rccPatchFree);
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++
++id3_utf8_t *rccPatchLatin2UTF(id3_latin1_t *str) {
++#ifdef HAVE_LIBRCC
++ rccPatchTryInit();
++
++ return rccRecode(ctx, ID3_CLASS, UTF_CLASS, str);
++#else
++ return NULL;
++#endif /* HAVE_LIBRCC */
++}
++
++id3_latin1_t *rccPatchUTF2Latin(id3_utf8_t *str) {
++#ifdef HAVE_LIBRCC
++ rccPatchTryInit();
++
++ return rccRecode(ctx, UTF_CLASS, ID3_CLASS, str);
++#else
++ return NULL;
++#endif /* HAVE_LIBRCC */
++}
++
++id3_latin1_t *rccPatchUTF2Out(id3_utf8_t *str) {
++#ifdef HAVE_LIBRCC
++ rccPatchTryInit();
++
++ return rccRecode(ctx, UTF_CLASS, OUT_CLASS, str);
++#else
++ return NULL;
++#endif /* HAVE_LIBRCC */
++}
++
+diff -dPNur libid3tag-0.15.1b/rccpatch.h libid3tag-0.15.1b-ds/rccpatch.h
+--- libid3tag-0.15.1b/rccpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ libid3tag-0.15.1b-ds/rccpatch.h 2008-04-12 21:36:35.000000000 +0200
+@@ -0,0 +1,15 @@
++#ifndef _RCC_PATCH_H
++#define _RCC_PATCH_H
++
++#include "id3tag.h"
++
++void rccPatchFree();
++void rccPatchInit();
++void rccPatchSetContext(void *newctx);
++
++id3_utf8_t *rccPatchLatin2UTF(id3_latin1_t *str);
++id3_latin1_t *rccPatchUTF2Latin(id3_utf8_t *str);
++id3_latin1_t *rccPatchUTF2Out(id3_utf8_t *str);
++
++
++#endif /* _RCC_PATCH_H */
+diff -dPNur libid3tag-0.15.1b/tag.c libid3tag-0.15.1b-ds/tag.c
+--- libid3tag-0.15.1b/tag.c 2004-02-17 03:04:10.000000000 +0100
++++ libid3tag-0.15.1b-ds/tag.c 2008-04-12 21:37:12.000000000 +0200
+@@ -45,6 +45,9 @@
+ # include "field.h"
+ # include "util.h"
+
++# include "utf8.h"
++# include "rccpatch.h"
++
+ /*
+ * NAME: tag->new()
+ * DESCRIPTION: allocate and return a new, empty tag
+@@ -335,6 +338,8 @@
+ {
+ struct id3_frame *frame;
+ id3_ucs4_t ucs4[31];
++
++ id3_utf8_t *utf8;
+
+ if (text) {
+ trim(text);
+@@ -350,9 +355,15 @@
+ ID3_FIELD_TEXTENCODING_ISO_8859_1) == -1)
+ goto fail;
+
+- if (text)
++ if (text) {
++ utf8 = rccPatchLatin2UTF(text);
++ if (utf8) {
++ if (strlen(utf8) > 30) utf8[30] = 0;
++ id3_utf8_decode(utf8, ucs4);
++ free(utf8);
++ } else
+ id3_latin1_decode(text, ucs4);
+- else
++ } else
+ id3_ucs4_putnumber(ucs4, number);
+
+ if (strcmp(id, ID3_FRAME_COMMENT) == 0) {
+diff -dPNur libid3tag-0.15.1b/ucs4.c libid3tag-0.15.1b-ds/ucs4.c
+--- libid3tag-0.15.1b/ucs4.c 2004-01-23 10:41:32.000000000 +0100
++++ libid3tag-0.15.1b-ds/ucs4.c 2008-04-12 21:36:35.000000000 +0200
+@@ -33,6 +33,9 @@
+ # include "utf16.h"
+ # include "utf8.h"
+
++# include <string.h>
++# include "rccpatch.h"
++
+ id3_ucs4_t const id3_ucs4_empty[] = { 0 };
+
+ /*
+@@ -125,6 +128,27 @@
+ {
+ id3_latin1_t *latin1;
+
++
++ id3_latin1_t *ltmp;
++ id3_utf8_t *utf8;
++
++ utf8 = malloc(id3_ucs4_utf8size(ucs4) * sizeof(*utf8));
++ if (utf8) {
++ id3_utf8_encode(utf8, ucs4);
++ ltmp = rccPatchUTF2Out(utf8);
++ free(utf8);
++
++ if (ltmp) {
++ latin1 = malloc((1+strlen(ltmp))*sizeof(char));
++ if (latin1) {
++ memcpy(latin1, ltmp, (1+strlen(ltmp)));
++ free(ltmp);
++ return release(latin1);
++ }
++ free(ltmp);
++ }
++ }
++
+ latin1 = malloc(id3_ucs4_latin1size(ucs4) * sizeof(*latin1));
+ if (latin1)
+ id3_latin1_encode(latin1, ucs4);
diff --git a/patches/libtranslate/README.txt b/patches/libtranslate/README.txt
new file mode 100644
index 0000000..e152812
--- /dev/null
+++ b/patches/libtranslate/README.txt
@@ -0,0 +1,5 @@
+Apply the patches in the order they listed. Use patches from 'soup22' directory
+to build against libsoup versions before 2.4, otherwise use patches from
+'soup24' (tested up to 2.8).
+
+Replace data/services.xml.in by provided services.xml with updated services.
diff --git a/patches/libtranslate/libtranslate-ds2-memory.patch b/patches/libtranslate/libtranslate-ds2-memory.patch
new file mode 100644
index 0000000..e05c7b4
--- /dev/null
+++ b/patches/libtranslate/libtranslate-ds2-memory.patch
@@ -0,0 +1,12 @@
+diff -dPNur libtranslate-0.99/src/translate-util.c libtranslate-0.99-new/src/translate-util.c
+--- libtranslate-0.99/src/translate-util.c 2005-01-17 16:45:45.000000000 +0000
++++ libtranslate-0.99-new/src/translate-util.c 2005-12-29 18:35:04.000000000 +0000
+@@ -136,7 +136,7 @@
+ g_return_val_if_fail(big != NULL, NULL);
+ g_return_val_if_fail(little != NULL, NULL);
+
+- lower_big = g_ascii_strdown(big, big_len);
++ lower_big = g_ascii_strdown(big, (int)big_len);
+ lower_little = g_ascii_strdown(little, -1);
+
+ s = strstr(lower_big, lower_little);
diff --git a/patches/libtranslate/libtranslate-ds3-charsetparse.patch b/patches/libtranslate/libtranslate-ds3-charsetparse.patch
new file mode 100644
index 0000000..2f578e9
--- /dev/null
+++ b/patches/libtranslate/libtranslate-ds3-charsetparse.patch
@@ -0,0 +1,47 @@
+--- libtranslate-0.99/src/modules/translate-generic-service.c.orig Mon Apr 11 23:08:47 2005
++++ libtranslate-0.99/src/modules/translate-generic-service.c Mon Apr 11 23:15:54 2005
+@@ -484,7 +484,7 @@
+
+ if (SOUP_STATUS_IS_SUCCESSFUL(message->status_code))
+ {
+- const char *charset = NULL;
++ char *charset = NULL;
+
+ if (flags & TRANSFER_CONVERT)
+ {
+@@ -493,14 +493,31 @@
+ content_type = translate_generic_service_get_header(message, &info, "Content-Type");
+ if (content_type)
+ {
+- charset = translate_ascii_strcasestr(content_type, "charset=");
+- if (charset)
+- charset += 8;
++ const char *tmp;
++
++ tmp = translate_ascii_strcasestr(content_type, "charset=");
++ if (tmp)
++ {
++ int len;
++
++ tmp += 8;
++ if (*tmp == '\'' || *tmp == '"')
++ tmp++;
++
++ len = strlen(tmp);
++ if (len > 0 && (tmp[len - 1] == '\'' || tmp[len - 1] == '"'))
++ len--;
++
++ charset = g_strndup(tmp, len);
++ }
+ }
+ }
+
+ if (charset)
+- response = g_convert(message->response.body, message->response.length, "UTF-8", charset, NULL, NULL, err);
++ {
++ response = g_convert(message->response.body, message->response.length, "UTF-8", charset, NULL, NULL, err);
++ g_free(charset);
++ }
+ else
+ {
+ if ((flags & TRANSFER_CONVERT) && ! g_utf8_validate(message->response.body, message->response.length, NULL))
diff --git a/patches/libtranslate/libtranslate-ds4-condfix.patch b/patches/libtranslate/libtranslate-ds4-condfix.patch
new file mode 100644
index 0000000..9915a80
--- /dev/null
+++ b/patches/libtranslate/libtranslate-ds4-condfix.patch
@@ -0,0 +1,38 @@
+--- libtranslate-0.99/src/translate-session.c.orig Mon Apr 11 22:44:53 2005
++++ libtranslate-0.99/src/translate-session.c Mon Apr 11 22:51:48 2005
+@@ -703,7 +703,14 @@
+ GError *tmp_err = NULL;
+
+ g_mutex_lock(info->mutex);
+- ret = info->err != NULL;
++ if (info->err)
++ {
++ ret = TRUE;
++ if (info->progress_cond)
++ g_cond_signal(info->progress_cond);
++ }
++ else
++ ret = FALSE;
+ g_mutex_unlock(info->mutex);
+
+ if (ret)
+@@ -728,6 +735,9 @@
+ else
+ g_propagate_error(&info->err, tmp_err);
+
++ if (info->progress_cond)
++ g_cond_signal(info->progress_cond);
++
+ g_mutex_unlock(info->mutex);
+
+ return;
+@@ -759,6 +769,9 @@
+ info->err = g_error_new(TRANSLATE_SESSION_ERROR,
+ TRANSLATE_SESSION_ERROR_NO_SERVICE,
+ _("no service could translate chunk"));
++
++ if (info->progress_cond)
++ g_cond_signal(info->progress_cond);
+ }
+
+ g_mutex_unlock(info->mutex);
diff --git a/patches/libtranslate/libtranslate-ds5-empty.patch b/patches/libtranslate/libtranslate-ds5-empty.patch
new file mode 100644
index 0000000..eb2e0ce
--- /dev/null
+++ b/patches/libtranslate/libtranslate-ds5-empty.patch
@@ -0,0 +1,17 @@
+diff -dPNur libtranslate-0.99/src/modules/translate-generic-service.c libtranslate-0.99-new/src/modules/translate-generic-service.c
+--- libtranslate-0.99/src/modules/translate-generic-service.c 2007-06-27 17:26:10.000000000 +0200
++++ libtranslate-0.99-new/src/modules/translate-generic-service.c 2007-06-27 17:23:55.000000000 +0200
+@@ -1042,6 +1042,13 @@
+
+ g_free(response);
+ }
++
++ if ((!answer)&&(!*err)) {
++ g_set_error(err,
++ TRANSLATE_GENERIC_SERVICE_ERROR,
++ TRANSLATE_GENERIC_SERVICE_ERROR_PARSE,
++ _("empty server response"));
++ }
+
+ return answer ? g_string_free(answer, FALSE) : NULL;
+ }
diff --git a/patches/libtranslate/libtranslate-ds6-promt.patch b/patches/libtranslate/libtranslate-ds6-promt.patch
new file mode 100644
index 0000000..6b30145
--- /dev/null
+++ b/patches/libtranslate/libtranslate-ds6-promt.patch
@@ -0,0 +1,27 @@
+diff -dPNur libtranslate-0.99/src/modules/translate-generic-service.c libtranslate-0.99-new/src/modules/translate-generic-service.c
+--- libtranslate-0.99/src/modules/translate-generic-service.c 2007-06-27 17:26:10.000000000 +0200
++++ libtranslate-0.99-new/src/modules/translate-generic-service.c 2007-06-27 17:23:55.000000000 +0200
+@@ -1238,7 +1245,22 @@
+ {
+ if (modifier_value)
+ g_warning(_("%s: value specified for \"escape\" modifier"), warning_prefix);
+- modified = soup_uri_encode(value, NULL);
++ modified = soup_uri_encode(value, "&");
++ }
++ else if (! strcmp(modifier_name, "entities"))
++ {
++ int i;
++ char *ptr;
++
++ modified = g_malloc(strlen(value)*6 + 1);
++ for (i=0,ptr=modified;value[i];i++) {
++ if ((unsigned char)(value[i])<160) *(ptr++)=value[i];
++ else {
++ sprintf(ptr, "&#%u;", (unsigned char)(value[i]));
++ ptr+=6;
++ }
++ }
++ *ptr = 0;
+ }
+ else if (! strcmp(modifier_name, "charset"))
+ {
diff --git a/patches/libtranslate/libtranslate-ds7-fixcharset.patch b/patches/libtranslate/libtranslate-ds7-fixcharset.patch
new file mode 100644
index 0000000..0665d2f
--- /dev/null
+++ b/patches/libtranslate/libtranslate-ds7-fixcharset.patch
@@ -0,0 +1,93 @@
+diff -dPNur libtranslate-0.99-new/src/modules/translate-generic-parser.c libtranslate-0.99-new-uk/src/modules/translate-generic-parser.c
+--- libtranslate-0.99-new/src/modules/translate-generic-parser.c 2005-01-17 17:46:24.000000000 +0100
++++ libtranslate-0.99-new-uk/src/modules/translate-generic-parser.c 2007-06-27 22:40:04.000000000 +0200
+@@ -726,6 +726,7 @@
+ {
+ const char *url;
+ const char *post;
++ const char *charset;
+ const char *content_type;
+
+ g_return_if_fail(info != NULL);
+@@ -740,6 +741,7 @@
+ "url", REQUIRED, &url,
+ "post", OPTIONAL, &post,
+ "content-type", OPTIONAL, &content_type,
++ "response-charset", OPTIONAL, &charset,
+ NULL);
+
+ if (! *err)
+@@ -748,6 +750,7 @@
+ (*location)->url = g_strdup(url);
+ (*location)->post = g_strdup(post);
+ (*location)->content_type = g_strdup(content_type ? content_type : "application/x-www-form-urlencoded");
++ (*location)->response_charset = g_strdup(charset);
+ }
+ }
+
+@@ -759,6 +762,7 @@
+ g_free(location->url);
+ g_free(location->post);
+ g_free(location->content_type);
++ g_free(location->response_charset);
+ g_slist_foreach(location->http_headers, (GFunc) translate_generic_http_header_free, NULL);
+ g_slist_free(location->http_headers);
+ g_free(location);
+diff -dPNur libtranslate-0.99-new/src/modules/translate-generic-parser.h libtranslate-0.99-new-uk/src/modules/translate-generic-parser.h
+--- libtranslate-0.99-new/src/modules/translate-generic-parser.h 2005-01-17 17:46:30.000000000 +0100
++++ libtranslate-0.99-new-uk/src/modules/translate-generic-parser.h 2007-06-27 22:34:13.000000000 +0200
+@@ -51,6 +51,7 @@
+ char *url;
+ char *post;
+ char *content_type;
++ char *response_charset;
+ GSList *http_headers;
+ } TranslateGenericLocation;
+
+diff -dPNur libtranslate-0.99-new/src/modules/translate-generic-service.c libtranslate-0.99-new-uk/src/modules/translate-generic-service.c
+--- libtranslate-0.99-new/src/modules/translate-generic-service.c 2007-06-27 17:23:55.000000000 +0200
++++ libtranslate-0.99-new-uk/src/modules/translate-generic-service.c 2007-06-27 22:40:29.000000000 +0200
+@@ -129,6 +129,7 @@
+ static char *translate_generic_service_get (const char *uri,
+ const char *post,
+ const char *post_content_type,
++ const char *response_charset,
+ const GSList *headers,
+ TransferFlags flags,
+ GTimeVal *deadline,
+@@ -407,6 +408,7 @@
+ translate_generic_service_get (const char *uri,
+ const char *post,
+ const char *post_content_type,
++ const char *response_charset,
+ const GSList *headers,
+ TransferFlags flags,
+ GTimeVal *deadline,
+@@ -550,9 +552,9 @@
+ }
+ }
+
+- if (charset)
++ if ((charset)||(response_charset))
+ {
+- response = g_convert(message->response.body, message->response.length, "UTF-8", charset, NULL, NULL, err);
++ response = g_convert(message->response.body, message->response.length, "UTF-8", response_charset?response_charset:charset, NULL, NULL, err);
+ g_free(charset);
+ }
+ else
+@@ -941,6 +943,7 @@
+ response = translate_generic_service_get(url,
+ post,
+ group->text_location->content_type,
++ group->text_location->response_charset,
+ headers,
+ TRANSFER_FOLLOW_REFRESH | TRANSFER_CONVERT,
+ deadline,
+@@ -1339,6 +1342,7 @@
+ response = translate_generic_service_get(translation_url,
+ post,
+ group->web_page_location->content_type,
++ group->web_page_location->response_charset,
+ headers,
+ 0,
+ NULL,
diff --git a/patches/libtranslate/services.xml b/patches/libtranslate/services.xml
new file mode 100644
index 0000000..14d0548
--- /dev/null
+++ b/patches/libtranslate/services.xml
@@ -0,0 +1,258 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE services SYSTEM "services.dtd">
+<services>
+ <custom-language tag="zh-TW" name="Chinese (Taiwan)"/>
+
+ <service nick="Google" name="google">
+ <group>
+ <language to="*" tag="en"/>
+ <language to="en,de" tag="fr"/>
+ <language to="en,fr" tag="de"/>
+ <language to="en" tag="it"/>
+ <language to="en" tag="pt"/>
+ <language to="en" tag="es"/>
+ <language to="en" tag="pt"/>
+ <language to="en" tag="ru"/>
+ <language to="en" tag="zh"/>
+ <language to="en" tag="zh-TW"/>
+ <language to="en" tag="ja"/>
+ <language to="en" tag="ko"/>
+ <language to="en" tag="ar"/>
+ <text-translation url="http://www.google.com/translate_t?text=${text:escape}&amp;langpair=${from}|${to}&amp;ie=utf8&amp;oe=utf8">
+ <pre-marker text="id=result_box"/>
+ <pre-marker text="mouseover"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;br&gt;"/>
+ </text-translation>
+ <web-page-translation url="http://www.google.com/translate_c?u=${url:escape}&amp;langpair=${from}|${to}"/>
+ </group>
+ </service>
+
+ <service nick="FreeTranslation" name="freetranslation" max-chunk-len="600">
+ <group>
+ <language to="en" tag="nl" service-tag="dutch"/>
+ <language to="*" tag="en" service-tag="english"/>
+ <language to="en" tag="fr" service-tag="french"/>
+ <language to="en" tag="de" service-tag="german"/>
+ <language to="en" tag="it" service-tag="italian"/>
+ <language tag="no" service-tag="norwegian"/>
+ <language to="en" tag="pt" service-tag="portuguese"/>
+ <language to="en" tag="es" service-tag="spanish"/>
+ <text-translation url="http://ets.freetranslation.com/?sequence=core&amp;srctext=${text:escape}&amp;language=${from}/${to}&amp;charset=utf-8"/>
+ <web-page-translation url="http://fets5.freetranslation.com/?sequence=core&amp;url=${url:escape}&amp;language=${from}/${to}"/>
+ </group>
+ <group>
+ <language tag="zh" service-tag="simplifiedchinese"/>
+ <language tag="zh-TW" service-tag="traditionalchinese"/>
+ <language to="*" tag="en" service-tag="english"/>
+ <language to="en" tag="ru" service-tag="russian"/>
+ <text-translation url="http://ets6.freetranslation.com/?sequence=core&amp;srctext=${text:escape}&amp;language=${from}/${to}&amp;charset=utf-8"/>
+ </group>
+ <group>
+ <language to="*" tag="en" service-tag="english"/>
+ <language to="en" tag="ja" service-tag="japanese"/>
+ <text-translation url="http://tets9.freetranslation.com/?sequence=core&amp;srctext=${text:escape}&amp;language=${from}/${to}&amp;charset=utf-8"/>
+ </group>
+ </service>
+
+ <service nick="Pereklad" name="pereklad">
+ <group>
+ <language to="*" tag="ru" service-tag="Rus"/>
+ <language to="*" tag="uk" service-tag="Ukr"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:escape}">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="*" tag="en" service-tag="Eng"/>
+ <language to="*" tag="de" service-tag="Ger"/>
+ <language to="*" tag="fr" service-tag="Fra"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:escape}"
+ response-charset="ISO-8859-1">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="en,de,fr" tag="ru" service-tag="Rus"/>
+ <language to="en,de,fr" tag="uk" service-tag="Ukr"/>
+ <language to="" tag="en" service-tag="Eng"/>
+ <language to="" tag="de" service-tag="Ger"/>
+ <language to="" tag="fr" service-tag="Fra"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:escape}"
+ response-charset="ISO-8859-1">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="" tag="ru" service-tag="Rus"/>
+ <language to="" tag="uk" service-tag="Ukr"/>
+ <language to="ru,uk" tag="en" service-tag="Eng"/>
+ <language to="ru,uk" tag="de" service-tag="Ger"/>
+ <language to="ru,uk" tag="fr" service-tag="Fra"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:escape}">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="" tag="ru" service-tag="Rus"/>
+ <language to="" tag="uk" service-tag="Ukr"/>
+ <language to="ru,uk" tag="lv" service-tag="Lat"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:escape}">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="*" tag="lv" service-tag="Lat"/>
+ <language to="" tag="en" service-tag="Eng"/>
+ <language to="" tag="de" service-tag="Ger"/>
+ <language to="" tag="fr" service-tag="Fra"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:escape}"
+ response-charset="ISO-8859-1">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="" tag="lv" service-tag="Lat"/>
+ <language to="lv" tag="ru" service-tag="Rus"/>
+ <language to="lv" tag="uk" service-tag="Ukr"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:escape}"
+ response-charset="ISO-8859-4">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ <group>
+ <language to="" tag="lv" service-tag="Lat"/>
+ <language to="lv" tag="en" service-tag="Eng"/>
+ <language to="lv" tag="de" service-tag="Ger"/>
+ <language to="lv" tag="fr" service-tag="Fra"/>
+ <text-translation
+ url="http://pereklad.online.ua/"
+ post="TranFrom=${from}&amp;TranTo=${to}&amp;SrcTxt=${text:escape}"
+ response-charset="ISO-8859-4">
+ <pre-marker text="id=&quot;DstTxt&quot;"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea&gt;"/>
+ </text-translation>
+ </group>
+ </service>
+
+ <service nick="Promt" name="promt">
+ <group>
+ <language to="*" tag="ru" service-tag="r"/>
+ <language to="ru,de,fr,es" tag="en" service-tag="e"/>
+ <language to="ru,en,fr,es" tag="de" service-tag="g"/>
+ <language to="ru,en,de,es" tag="fr" service-tag="f"/>
+ <language to="ru,en" tag="it" service-tag="i"/>
+ <language to="ru,en,de,fr" tag="es" service-tag="s"/>
+ <language to="en" tag="pt" service-tag="p"/>
+ <text-translation
+ url="http://www.translate.ru/Default.aspx/Text"
+ post="__EVENTTARGET=&amp;__EVENTTARGUMENT=&amp;ctl00$SiteContent$MA_trasnlform$bTranslate=Translate&amp;ctl00$SiteContent$MA_trasnlform$DropDownList2=&amp;ctl00$SiteContent$trasnlform$sLang=${from}&amp;ctl00$SiteContent$MA_trasnlform$rLang=${to}&amp;ctl00$SiteContent$MA_trasnlform$sourceText=${text:escape}&amp;ctl00$SiteContent$MA_trasnlform$rblTemplates=General">
+ <pre-marker text="ctl00$SiteContent$MA_trasnlform$tbPismo"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/textarea"/>
+
+ </text-translation>
+ </group>
+ </service>
+
+ <service nick="SYSTRAN" name="systran">
+ <group>
+ <language to="en" tag="ar"/>
+ <language to="en" tag="zh"/>
+ <language to="en" tag="zh-TW" service-tag="zt"/>
+ <language to="en,fr" tag="nl"/>
+ <language to="*" tag="en"/>
+ <language to="nl,en,de,it,pt,es" tag="fr"/>
+ <language to="en,fr" tag="de"/>
+ <language to="en,fr" tag="it"/>
+ <language to="en" tag="ja"/>
+ <language to="en" tag="ko"/>
+ <language to="en,fr" tag="pt"/>
+ <language to="en" tag="ru"/>
+ <language to="en,fr" tag="es"/>
+ <language to="en" tag="sv"/>
+ <http-header value="http://www.systransoft.com/" name="Referer"/>
+ <text-translation
+ url="http://www.systranet.com/tt?lp=${from}_${to}&amp;service=translate"
+ post=". ${text}.">
+ <pre-marker text=". "/>
+ <post-marker text="."/>
+ </text-translation>
+ </group>
+ </service>
+
+ <service nick="Babel Fish" name="babelfish">
+ <group>
+ <language to="en" tag="zh"/>
+ <language to="en" tag="zh-TW" service-tag="zt"/>
+ <language to="*" tag="en"/>
+ <language to="en,fr" tag="nl"/>
+ <language to="en,de,el,it,pt,nl,es" tag="fr"/>
+ <language to="en,fr" tag="de"/>
+ <language to="en,fr" tag="el"/>
+ <language to="en,fr" tag="it"/>
+ <language to="en" tag="ja"/>
+ <language to="en" tag="ko"/>
+ <language to="en,fr" tag="pt"/>
+ <language to="en" tag="ru"/>
+ <language to="en,fr" tag="es"/>
+ <text-translation
+ url="http://babelfish.yahoo.com/translate_txt"
+ post="trtext=${text:escape}&amp;lp=${from}_${to}&amp;ei=UTF-8">
+ <pre-marker text="id=&quot;result&quot;"/>
+ <pre-marker text="&lt;div"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&lt;/div"/>
+ </text-translation>
+<!--
+ <web-page-translation url="http://babelfish.altavista.com/babelfish/trurl_load?url=${url:escape}&amp;lp=${from}_${to}&amp;enc=utf8"/>
+!-->
+ </group>
+ </service>
+
+<!--
+ <service nick="Kataku" name="kataku">
+ <group>
+ <language to="*" tag="en"/>
+ <language to="*" tag="id" service-tag="in"/>
+ <text-translation url="http://www.toggletext.com/kataku_trial.php" post="input_text=${text:charset=ISO8859-1,escape}&amp;langset_text=${from}_${to}">
+ <pre-marker text="Translation:"/>
+ <pre-marker text="&lt;pre"/>
+ <pre-marker text="&gt;"/>
+ <post-marker text="&#10;&lt;/pre&gt;"/>
+ </text-translation>
+ <web-page-translation url="http://www.toggletext.com/kataku_webpage_translate.php?input=${url:escape}&amp;langset=${from}_${to}"/>
+ </group>
+ </service>
+-->
+
+</services> \ No newline at end of file
diff --git a/patches/libtranslate/soup22/libtranslate-ds1-timed.patch b/patches/libtranslate/soup22/libtranslate-ds1-timed.patch
new file mode 100644
index 0000000..c98868a
--- /dev/null
+++ b/patches/libtranslate/soup22/libtranslate-ds1-timed.patch
@@ -0,0 +1,352 @@
+diff -dPNur libtranslate-0.99/src/modules/translate-generic-service.c libtranslate-0.99-new/src/modules/translate-generic-service.c
+--- libtranslate-0.99/src/modules/translate-generic-service.c 2005-01-17 17:46:38.000000000 +0100
++++ libtranslate-0.99-new/src/modules/translate-generic-service.c 2005-07-27 22:13:33.000000000 +0200
+@@ -131,6 +131,7 @@
+ const char *post_content_type,
+ const GSList *headers,
+ TransferFlags flags,
++ GTimeVal *deadline,
+ TranslateProgressFunc progress_func,
+ gpointer user_data,
+ GError **err);
+@@ -181,6 +182,15 @@
+ gpointer user_data,
+ GError **err);
+
++static char *translate_generic_service_timed_translate_text (TranslateService *service,
++ const char *text,
++ const char *from,
++ const char *to,
++ GTimeVal *deadline,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err);
++
+ char *translate_generic_service_expand (const char *warning_prefix,
+ const char *str,
+ ...);
+@@ -248,6 +258,7 @@
+
+ service_class->get_pairs = translate_generic_service_get_pairs;
+ service_class->translate_text = translate_generic_service_translate_text;
++ service_class->timed_translate_text = translate_generic_service_timed_translate_text;
+ service_class->translate_web_page = translate_generic_service_translate_web_page;
+
+ g_object_class_install_property(object_class,
+@@ -387,12 +398,18 @@
+ return TRUE; /* continue */
+ }
+
++static void send_message_cb(SoupMessage *req, gpointer user_data) {
++ g_object_ref(req);
++ *(gboolean*)user_data = TRUE;
++}
++
+ static char *
+ translate_generic_service_get (const char *uri,
+ const char *post,
+ const char *post_content_type,
+ const GSList *headers,
+ TransferFlags flags,
++ GTimeVal *deadline,
+ TranslateProgressFunc progress_func,
+ gpointer user_data,
+ GError **err)
+@@ -400,6 +417,8 @@
+ TransferInfo info;
+ SoupMessage *message;
+ const GSList *l;
++ GTimeVal tv;
++ gboolean completed = 0, canceled = 0;
+ char *response = NULL;
+
+ g_return_val_if_fail(uri != NULL, FALSE);
+@@ -479,9 +498,27 @@
+ if (translate_generic_debug_flags & TRANSLATE_GENERIC_DEBUG_LOG_TRANSFERS)
+ translate_generic_service_log_connect(message);
+
++ if (deadline) {
++ soup_session_queue_message(info.session, message, send_message_cb, &completed);
++
++ do {
++ g_main_iteration (FALSE);
++ if (completed) break;
++
++ g_get_current_time(&tv);
++ } while ((tv.tv_sec < deadline->tv_sec)||((tv.tv_sec == deadline->tv_sec)&&(tv.tv_usec < deadline->tv_usec)));
++
++ if (!completed) {
++ soup_session_cancel_message(info.session, message);
++ canceled = 1;
++ }
++ } else
+ soup_session_send_message(info.session, message);
+ g_object_unref(info.session);
+
++ if (canceled)
++ g_set_error(err, TRANSLATE_ERROR, TRANSLATE_ERROR_CANCELLED, _("Timeout"));
++ else
+ if (SOUP_STATUS_IS_SUCCESSFUL(message->status_code))
+ {
+ const char *charset = NULL;
+@@ -833,10 +870,11 @@
+ }
+
+ static char *
+-translate_generic_service_translate_text (TranslateService *service,
++translate_generic_service_timed_translate_text (TranslateService *service,
+ const char *text,
+ const char *from,
+ const char *to,
++ GTimeVal *deadline,
+ TranslateProgressFunc progress_func,
+ gpointer user_data,
+ GError **err)
+@@ -882,16 +920,16 @@
+
+ headers = g_slist_copy(group->http_headers);
+ headers = g_slist_concat(headers, g_slist_copy(group->text_location->http_headers));
+-
++
+ response = translate_generic_service_get(url,
+ post,
+ group->text_location->content_type,
+ headers,
+ TRANSFER_FOLLOW_REFRESH | TRANSFER_CONVERT,
++ deadline,
+ progress_func,
+ user_data,
+ err);
+-
+ g_free(url);
+ g_free(post);
+ g_slist_free(headers);
+@@ -991,6 +1029,18 @@
+ return answer ? g_string_free(answer, FALSE) : NULL;
+ }
+
++static char *
++translate_generic_service_translate_text (TranslateService *service,
++ const char *text,
++ const char *from,
++ const char *to,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err)
++{
++ return translate_generic_service_timed_translate_text(service, text, from, to, NULL, progress_func, user_data, err);
++}
++
+ char *
+ translate_generic_service_expand (const char *warning_prefix,
+ const char *str,
+@@ -1252,6 +1302,7 @@
+ group->web_page_location->content_type,
+ headers,
+ 0,
++ NULL,
+ progress_func,
+ user_data,
+ err);
+@@ -1311,7 +1362,7 @@
+ g_free(proxy_text_uri);
+ }
+
+- session = soup_session_sync_new_with_options(SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
++ session = soup_session_async_new_with_options(SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
+
+ if (proxy_uri)
+ soup_uri_free(proxy_uri);
+diff -dPNur libtranslate-0.99/src/translate-service.c libtranslate-0.99-new/src/translate-service.c
+--- libtranslate-0.99/src/translate-service.c 2005-01-17 17:45:23.000000000 +0100
++++ libtranslate-0.99-new/src/translate-service.c 2005-07-27 17:18:07.000000000 +0200
+@@ -372,6 +372,28 @@
+ }
+
+ char *
++translate_service_timed_translate_text (TranslateService *service,
++ const char *text,
++ const char *from,
++ const char *to,
++ GTimeVal *deadline,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err)
++{
++ g_return_val_if_fail(TRANSLATE_IS_SERVICE(service), NULL);
++ g_return_val_if_fail(TRANSLATE_SERVICE_GET_CLASS(service)->translate_text != NULL, NULL);
++ g_return_val_if_fail(text != NULL, NULL);
++ g_return_val_if_fail(from != NULL, NULL);
++ g_return_val_if_fail(to != NULL, NULL);
++
++ if (TRANSLATE_SERVICE_GET_CLASS(service)->timed_translate_text)
++ return TRANSLATE_SERVICE_GET_CLASS(service)->timed_translate_text(service, text, from, to, deadline, progress_func, user_data, err);
++
++ return TRANSLATE_SERVICE_GET_CLASS(service)->translate_text(service, text, from, to, progress_func, user_data, err);
++}
++
++char *
+ translate_service_translate_web_page (TranslateService *service,
+ const char *url,
+ const char *from,
+diff -dPNur libtranslate-0.99/src/translate-service.h libtranslate-0.99-new/src/translate-service.h
+--- libtranslate-0.99/src/translate-service.h 2005-01-17 17:45:29.000000000 +0100
++++ libtranslate-0.99-new/src/translate-service.h 2005-07-27 16:54:46.000000000 +0200
+@@ -73,6 +73,14 @@
+ TranslateProgressFunc progress_func,
+ gpointer user_data,
+ GError **err);
++ char *(*timed_translate_text)(TranslateService *service,
++ const char *text,
++ const char *from,
++ const char *to,
++ GTimeVal *deadline,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err);
+ char *(*translate_web_page) (TranslateService *service,
+ const char *url,
+ const char *from,
+diff -dPNur libtranslate-0.99/src/translate-service-private.h libtranslate-0.99-new/src/translate-service-private.h
+--- libtranslate-0.99/src/translate-service-private.h 2005-01-17 17:45:17.000000000 +0100
++++ libtranslate-0.99-new/src/translate-service-private.h 2005-07-27 17:30:00.000000000 +0200
+@@ -41,6 +41,14 @@
+ TranslateProgressFunc progress_func,
+ gpointer user_data,
+ GError **err);
++char *translate_service_timed_translate_text (TranslateService *service,
++ const char *text,
++ const char *from,
++ const char *to,
++ GTimeVal *deadline,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err);
+ char *translate_service_translate_web_page (TranslateService *service,
+ const char *url,
+ const char *from,
+diff -dPNur libtranslate-0.99/src/translate-session.c libtranslate-0.99-new/src/translate-session.c
+--- libtranslate-0.99/src/translate-session.c 2005-01-17 17:45:35.000000000 +0100
++++ libtranslate-0.99-new/src/translate-session.c 2005-07-27 16:52:46.000000000 +0200
+@@ -62,6 +62,8 @@
+ {
+ GMutex *mutex;
+ GCond *progress_cond;
++
++ GTimeVal *deadline;
+
+ TranslateSession *session;
+ GSList *services;
+@@ -487,6 +489,7 @@
+ * @text: a nul-terminated string.
+ * @from: a RFC 3066 language tag.
+ * @to: a RFC 3066 language tag.
++ * @timeout: timeout in microseconds.
+ * @progress_func: a function to call when progressing, or %NULL.
+ * @user_data: data to pass to @progress_func, or %NULL.
+ * @err: a location to report errors, or %NULL. Any of the errors in
+@@ -505,10 +508,11 @@
+ * when no longer needed.
+ **/
+ char *
+-translate_session_translate_text (TranslateSession *session,
++translate_session_timed_translate_text (TranslateSession *session,
+ const char *text,
+ const char *from,
+ const char *to,
++ gulong timeout,
+ TranslateProgressFunc progress_func,
+ gpointer user_data,
+ GError **err)
+@@ -519,6 +523,7 @@
+ unsigned int max_threads;
+ GThreadPool *pool;
+ GSList *l;
++ GTimeVal deadline;
+ unsigned int max_chunk_len = 0;
+ char *translated = NULL;
+
+@@ -527,6 +532,11 @@
+ g_return_val_if_fail(from != NULL, NULL);
+ g_return_val_if_fail(to != NULL, NULL);
+
++ if (timeout) {
++ g_get_current_time(&deadline);
++ g_time_val_add(&deadline, timeout);
++ }
++
+ LOCK(session);
+ info.services = translate_session_get_services_for_translation(session,
+ TRANSLATE_PAIR_TEXT,
+@@ -560,7 +570,8 @@
+ chunks = translate_session_split(text, max_chunk_len);
+
+ info.mutex = g_mutex_new();
+- info.progress_cond = progress_func ? g_cond_new() : NULL;
++ info.progress_cond = (progress_func||timeout) ? g_cond_new() : NULL;
++ info.deadline = timeout ? &deadline : NULL;
+ info.session = session;
+ info.chunks = NULL;
+ info.from = from;
+@@ -614,6 +625,11 @@
+ GSList *l;
+ int n_chunks;
+
++ if (timeout) {
++ if (!g_cond_timed_wait(info.progress_cond, info.mutex, info.deadline))
++ info.err = g_error_new(TRANSLATE_ERROR,TRANSLATE_ERROR_CANCELLED,_("timeout"));
++ break;
++ } else
+ g_cond_wait(info.progress_cond, info.mutex);
+
+ if (info.err)
+@@ -680,6 +696,18 @@
+ return translated;
+ }
+
++char *
++translate_session_translate_text (TranslateSession *session,
++ const char *text,
++ const char *from,
++ const char *to,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err)
++{
++ return translate_session_timed_translate_text(session, text, from, to, 0, progress_func, user_data, err);
++}
++
+ static void
+ translate_session_translate_thread (gpointer data, gpointer user_data)
+ {
+@@ -709,10 +737,11 @@
+ if (ret)
+ return;
+
+- chunk_info->translated = translate_service_translate_text(service,
++ chunk_info->translated = translate_service_timed_translate_text(service,
+ chunk_info->chunk,
+ info->from,
+ info->to,
++ info->deadline,
+ info->progress_cond ? translate_session_translate_progress_cb : NULL,
+ info->progress_cond ? chunk_info : NULL,
+ &tmp_err);
+diff -dPNur libtranslate-0.99/src/translate-session.h libtranslate-0.99-new/src/translate-session.h
+--- libtranslate-0.99/src/translate-session.h 2005-01-17 17:45:40.000000000 +0100
++++ libtranslate-0.99-new/src/translate-session.h 2005-07-27 14:41:05.000000000 +0200
+@@ -93,6 +93,14 @@
+ unsigned int translate_session_get_max_threads (TranslateSession *session);
+ int translate_session_get_max_retries (TranslateSession *session);
+
++char *translate_session_timed_translate_text (TranslateSession *session,
++ const char *text,
++ const char *from,
++ const char *to,
++ gulong timeout,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err);
+ char *translate_session_translate_text (TranslateSession *session,
+ const char *text,
+ const char *from,
diff --git a/patches/libtranslate/soup24/libtranslate-ds1-timed24.patch b/patches/libtranslate/soup24/libtranslate-ds1-timed24.patch
new file mode 100644
index 0000000..daf3811
--- /dev/null
+++ b/patches/libtranslate/soup24/libtranslate-ds1-timed24.patch
@@ -0,0 +1,360 @@
+diff -dPNur libtranslate-0.99/src/modules/translate-generic-service.c libtranslate-0.99-new/src/modules/translate-generic-service.c
+--- libtranslate-0.99/src/modules/translate-generic-service.c 2005-01-17 17:46:38.000000000 +0100
++++ libtranslate-0.99-new/src/modules/translate-generic-service.c 2005-07-27 22:13:33.000000000 +0200
+@@ -131,6 +131,7 @@
+ const char *post_content_type,
+ const GSList *headers,
+ TransferFlags flags,
++ GTimeVal *deadline,
+ TranslateProgressFunc progress_func,
+ gpointer user_data,
+ GError **err);
+@@ -181,6 +182,15 @@
+ gpointer user_data,
+ GError **err);
+
++static char *translate_generic_service_timed_translate_text (TranslateService *service,
++ const char *text,
++ const char *from,
++ const char *to,
++ GTimeVal *deadline,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err);
++
+ char *translate_generic_service_expand (const char *warning_prefix,
+ const char *str,
+ ...);
+@@ -248,6 +258,7 @@
+
+ service_class->get_pairs = translate_generic_service_get_pairs;
+ service_class->translate_text = translate_generic_service_translate_text;
++ service_class->timed_translate_text = translate_generic_service_timed_translate_text;
+ service_class->translate_web_page = translate_generic_service_translate_web_page;
+
+ g_object_class_install_property(object_class,
+@@ -387,12 +398,22 @@
+ return TRUE; /* continue */
+ }
+
++#ifdef HAVE_LIBSOUP22
++static void send_message_cb(SoupMessage *req, gpointer user_data) {
++#else
++static void send_message_cb(SoupSession *session, SoupMessage *req, gpointer user_data) {
++#endif
++ g_object_ref(req);
++ *(gboolean*)user_data = TRUE;
++}
++
+ static char *
+ translate_generic_service_get (const char *uri,
+ const char *post,
+ const char *post_content_type,
+ const GSList *headers,
+ TransferFlags flags,
++ GTimeVal *deadline,
+ TranslateProgressFunc progress_func,
+ gpointer user_data,
+ GError **err)
+@@ -400,6 +417,8 @@
+ TransferInfo info;
+ SoupMessage *message;
+ const GSList *l;
++ GTimeVal tv;
++ gboolean completed = 0, canceled = 0;
+ char *response = NULL;
+
+ g_return_val_if_fail(uri != NULL, FALSE);
+@@ -479,9 +498,31 @@
+ if (translate_generic_debug_flags & TRANSLATE_GENERIC_DEBUG_LOG_TRANSFERS)
+ translate_generic_service_log_connect(message);
+
++ if (deadline) {
++ soup_session_queue_message(info.session, message, send_message_cb, &completed);
++
++ do {
++ g_main_iteration (FALSE);
++ if (completed) break;
++
++ g_get_current_time(&tv);
++ } while ((tv.tv_sec < deadline->tv_sec)||((tv.tv_sec == deadline->tv_sec)&&(tv.tv_usec < deadline->tv_usec)));
++
++ if (!completed) {
++#ifdef HAVE_LIBSOUP22
++ soup_session_cancel_message(info.session, message);
++#else
++ soup_session_cancel_message(info.session, message, SOUP_STATUS_CANCELLED);
++#endif
++ canceled = 1;
++ }
++ } else
+ soup_session_send_message(info.session, message);
+ g_object_unref(info.session);
+
++ if (canceled)
++ g_set_error(err, TRANSLATE_ERROR, TRANSLATE_ERROR_CANCELLED, _("Timeout"));
++ else
+ if (SOUP_STATUS_IS_SUCCESSFUL(message->status_code))
+ {
+ const char *charset = NULL;
+@@ -833,10 +870,11 @@
+ }
+
+ static char *
+-translate_generic_service_translate_text (TranslateService *service,
++translate_generic_service_timed_translate_text (TranslateService *service,
+ const char *text,
+ const char *from,
+ const char *to,
++ GTimeVal *deadline,
+ TranslateProgressFunc progress_func,
+ gpointer user_data,
+ GError **err)
+@@ -882,16 +920,16 @@
+
+ headers = g_slist_copy(group->http_headers);
+ headers = g_slist_concat(headers, g_slist_copy(group->text_location->http_headers));
+-
++
+ response = translate_generic_service_get(url,
+ post,
+ group->text_location->content_type,
+ headers,
+ TRANSFER_FOLLOW_REFRESH | TRANSFER_CONVERT,
++ deadline,
+ progress_func,
+ user_data,
+ err);
+-
+ g_free(url);
+ g_free(post);
+ g_slist_free(headers);
+@@ -991,6 +1029,18 @@
+ return answer ? g_string_free(answer, FALSE) : NULL;
+ }
+
++static char *
++translate_generic_service_translate_text (TranslateService *service,
++ const char *text,
++ const char *from,
++ const char *to,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err)
++{
++ return translate_generic_service_timed_translate_text(service, text, from, to, NULL, progress_func, user_data, err);
++}
++
+ char *
+ translate_generic_service_expand (const char *warning_prefix,
+ const char *str,
+@@ -1252,6 +1302,7 @@
+ group->web_page_location->content_type,
+ headers,
+ 0,
++ NULL,
+ progress_func,
+ user_data,
+ err);
+@@ -1311,7 +1362,7 @@
+ g_free(proxy_text_uri);
+ }
+
+- session = soup_session_sync_new_with_options(SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
++ session = soup_session_async_new_with_options(SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
+
+ if (proxy_uri)
+ soup_uri_free(proxy_uri);
+diff -dPNur libtranslate-0.99/src/translate-service.c libtranslate-0.99-new/src/translate-service.c
+--- libtranslate-0.99/src/translate-service.c 2005-01-17 17:45:23.000000000 +0100
++++ libtranslate-0.99-new/src/translate-service.c 2005-07-27 17:18:07.000000000 +0200
+@@ -372,6 +372,28 @@
+ }
+
+ char *
++translate_service_timed_translate_text (TranslateService *service,
++ const char *text,
++ const char *from,
++ const char *to,
++ GTimeVal *deadline,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err)
++{
++ g_return_val_if_fail(TRANSLATE_IS_SERVICE(service), NULL);
++ g_return_val_if_fail(TRANSLATE_SERVICE_GET_CLASS(service)->translate_text != NULL, NULL);
++ g_return_val_if_fail(text != NULL, NULL);
++ g_return_val_if_fail(from != NULL, NULL);
++ g_return_val_if_fail(to != NULL, NULL);
++
++ if (TRANSLATE_SERVICE_GET_CLASS(service)->timed_translate_text)
++ return TRANSLATE_SERVICE_GET_CLASS(service)->timed_translate_text(service, text, from, to, deadline, progress_func, user_data, err);
++
++ return TRANSLATE_SERVICE_GET_CLASS(service)->translate_text(service, text, from, to, progress_func, user_data, err);
++}
++
++char *
+ translate_service_translate_web_page (TranslateService *service,
+ const char *url,
+ const char *from,
+diff -dPNur libtranslate-0.99/src/translate-service.h libtranslate-0.99-new/src/translate-service.h
+--- libtranslate-0.99/src/translate-service.h 2005-01-17 17:45:29.000000000 +0100
++++ libtranslate-0.99-new/src/translate-service.h 2005-07-27 16:54:46.000000000 +0200
+@@ -73,6 +73,14 @@
+ TranslateProgressFunc progress_func,
+ gpointer user_data,
+ GError **err);
++ char *(*timed_translate_text)(TranslateService *service,
++ const char *text,
++ const char *from,
++ const char *to,
++ GTimeVal *deadline,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err);
+ char *(*translate_web_page) (TranslateService *service,
+ const char *url,
+ const char *from,
+diff -dPNur libtranslate-0.99/src/translate-service-private.h libtranslate-0.99-new/src/translate-service-private.h
+--- libtranslate-0.99/src/translate-service-private.h 2005-01-17 17:45:17.000000000 +0100
++++ libtranslate-0.99-new/src/translate-service-private.h 2005-07-27 17:30:00.000000000 +0200
+@@ -41,6 +41,14 @@
+ TranslateProgressFunc progress_func,
+ gpointer user_data,
+ GError **err);
++char *translate_service_timed_translate_text (TranslateService *service,
++ const char *text,
++ const char *from,
++ const char *to,
++ GTimeVal *deadline,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err);
+ char *translate_service_translate_web_page (TranslateService *service,
+ const char *url,
+ const char *from,
+diff -dPNur libtranslate-0.99/src/translate-session.c libtranslate-0.99-new/src/translate-session.c
+--- libtranslate-0.99/src/translate-session.c 2005-01-17 17:45:35.000000000 +0100
++++ libtranslate-0.99-new/src/translate-session.c 2005-07-27 16:52:46.000000000 +0200
+@@ -62,6 +62,8 @@
+ {
+ GMutex *mutex;
+ GCond *progress_cond;
++
++ GTimeVal *deadline;
+
+ TranslateSession *session;
+ GSList *services;
+@@ -487,6 +489,7 @@
+ * @text: a nul-terminated string.
+ * @from: a RFC 3066 language tag.
+ * @to: a RFC 3066 language tag.
++ * @timeout: timeout in microseconds.
+ * @progress_func: a function to call when progressing, or %NULL.
+ * @user_data: data to pass to @progress_func, or %NULL.
+ * @err: a location to report errors, or %NULL. Any of the errors in
+@@ -505,10 +508,11 @@
+ * when no longer needed.
+ **/
+ char *
+-translate_session_translate_text (TranslateSession *session,
++translate_session_timed_translate_text (TranslateSession *session,
+ const char *text,
+ const char *from,
+ const char *to,
++ gulong timeout,
+ TranslateProgressFunc progress_func,
+ gpointer user_data,
+ GError **err)
+@@ -519,6 +523,7 @@
+ unsigned int max_threads;
+ GThreadPool *pool;
+ GSList *l;
++ GTimeVal deadline;
+ unsigned int max_chunk_len = 0;
+ char *translated = NULL;
+
+@@ -527,6 +532,11 @@
+ g_return_val_if_fail(from != NULL, NULL);
+ g_return_val_if_fail(to != NULL, NULL);
+
++ if (timeout) {
++ g_get_current_time(&deadline);
++ g_time_val_add(&deadline, timeout);
++ }
++
+ LOCK(session);
+ info.services = translate_session_get_services_for_translation(session,
+ TRANSLATE_PAIR_TEXT,
+@@ -560,7 +570,8 @@
+ chunks = translate_session_split(text, max_chunk_len);
+
+ info.mutex = g_mutex_new();
+- info.progress_cond = progress_func ? g_cond_new() : NULL;
++ info.progress_cond = (progress_func||timeout) ? g_cond_new() : NULL;
++ info.deadline = timeout ? &deadline : NULL;
+ info.session = session;
+ info.chunks = NULL;
+ info.from = from;
+@@ -614,6 +625,11 @@
+ GSList *l;
+ int n_chunks;
+
++ if (timeout) {
++ if (!g_cond_timed_wait(info.progress_cond, info.mutex, info.deadline))
++ info.err = g_error_new(TRANSLATE_ERROR,TRANSLATE_ERROR_CANCELLED,_("timeout"));
++ break;
++ } else
+ g_cond_wait(info.progress_cond, info.mutex);
+
+ if (info.err)
+@@ -680,6 +696,18 @@
+ return translated;
+ }
+
++char *
++translate_session_translate_text (TranslateSession *session,
++ const char *text,
++ const char *from,
++ const char *to,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err)
++{
++ return translate_session_timed_translate_text(session, text, from, to, 0, progress_func, user_data, err);
++}
++
+ static void
+ translate_session_translate_thread (gpointer data, gpointer user_data)
+ {
+@@ -709,10 +737,11 @@
+ if (ret)
+ return;
+
+- chunk_info->translated = translate_service_translate_text(service,
++ chunk_info->translated = translate_service_timed_translate_text(service,
+ chunk_info->chunk,
+ info->from,
+ info->to,
++ info->deadline,
+ info->progress_cond ? translate_session_translate_progress_cb : NULL,
+ info->progress_cond ? chunk_info : NULL,
+ &tmp_err);
+diff -dPNur libtranslate-0.99/src/translate-session.h libtranslate-0.99-new/src/translate-session.h
+--- libtranslate-0.99/src/translate-session.h 2005-01-17 17:45:40.000000000 +0100
++++ libtranslate-0.99-new/src/translate-session.h 2005-07-27 14:41:05.000000000 +0200
+@@ -93,6 +93,14 @@
+ unsigned int translate_session_get_max_threads (TranslateSession *session);
+ int translate_session_get_max_retries (TranslateSession *session);
+
++char *translate_session_timed_translate_text (TranslateSession *session,
++ const char *text,
++ const char *from,
++ const char *to,
++ gulong timeout,
++ TranslateProgressFunc progress_func,
++ gpointer user_data,
++ GError **err);
+ char *translate_session_translate_text (TranslateSession *session,
+ const char *text,
+ const char *from,
diff --git a/patches/libtranslate/soup24/libtranslate-ds8-soup24inc.patch b/patches/libtranslate/soup24/libtranslate-ds8-soup24inc.patch
new file mode 100644
index 0000000..8de7c07
--- /dev/null
+++ b/patches/libtranslate/soup24/libtranslate-ds8-soup24inc.patch
@@ -0,0 +1,533 @@
+diff -dPNur libtranslate-0.99/config.h.in libtranslate-0.99-1/config.h.in
+--- libtranslate-0.99/config.h.in 2005-01-17 18:06:58.000000000 +0100
++++ libtranslate-0.99-1/config.h.in 2010-02-14 03:55:56.000000000 +0100
+@@ -24,6 +24,12 @@
+ /* Define if your <locale.h> file defines LC_MESSAGES. */
+ #undef HAVE_LC_MESSAGES
+
++/* Building with libsoup 2.2 */
++#undef HAVE_LIBSOUP22
++
++/* Building with libsoup 2.4 */
++#undef HAVE_LIBSOUP24
++
+ /* Define to 1 if you have the <locale.h> header file. */
+ #undef HAVE_LOCALE_H
+
+diff -dPNur libtranslate-0.99/configure.ac libtranslate-0.99-1/configure.ac
+--- libtranslate-0.99/configure.ac 2005-01-15 17:24:12.000000000 +0100
++++ libtranslate-0.99-1/configure.ac 2010-02-14 03:55:56.000000000 +0100
+@@ -50,7 +50,11 @@
+ ### optional libraries
+
+ if TRANSLATE_FEATURE_ENABLED(generic); then
+- PKG_CHECK_MODULES(SOUP, [libsoup-2.2],, [TRANSLATE_FEATURE_DISABLE(generic, [libsoup not found])])
++ PKG_CHECK_MODULES(SOUP, [libsoup-2.4],
++ [AC_DEFINE(HAVE_LIBSOUP24, 1, [Building with libsoup 2.4])],
++ [PKG_CHECK_MODULES(SOUP, [libsoup-2.2],
++ [AC_DEFINE(HAVE_LIBSOUP22, 1, [Building with libsoup 2.2])],
++ [TRANSLATE_FEATURE_DISABLE(generic, [libsoup not found])])])
+ fi
+ if TRANSLATE_FEATURE_ENABLED(generic); then
+ PKG_CHECK_MODULES(LIBXML, [libxml-2.0],, [TRANSLATE_FEATURE_DISABLE(generic, [libxml not found])])
+diff -dPNur libtranslate-0.99/src/modules/translate-generic-service.c libtranslate-0.99-1/src/modules/translate-generic-service.c
+--- libtranslate-0.99/src/modules/translate-generic-service.c 2010-02-14 03:55:34.000000000 +0100
++++ libtranslate-0.99-1/src/modules/translate-generic-service.c 2010-02-14 04:04:56.000000000 +0100
+@@ -35,7 +35,9 @@
+ #include <stdlib.h>
+ #include <glib/gi18n-lib.h>
+ #include <libsoup/soup.h>
++#ifdef HAVE_LIBSOUP22
+ #include <libsoup/soup-message-filter.h>
++#endif
+ #include <libxml/HTMLparser.h>
+ #include "translate.h"
+ #include "translate-generic-service.h"
+@@ -43,6 +45,17 @@
+ #include "translate-generic-parser.h"
+ #include "translate-generic-soup-cookie-jar.h"
+
++#ifdef HAVE_LIBSOUP22
++#define soup_message_headers_get soup_message_get_header
++#define soup_message_headers_append soup_message_add_header
++#define SoupURI SoupUri
++#define SOUP_MESSAGE_RESPONSE_BODY(msg) ((msg)->response.body)
++#define SOUP_MESSAGE_RESPONSE_LENGTH(msg) ((msg)->response.length)
++#else
++#define SOUP_MESSAGE_RESPONSE_BODY(msg) ((msg)->response_body->data)
++#define SOUP_MESSAGE_RESPONSE_LENGTH(msg) ((msg)->response_body->length)
++#endif
++
+ #define MAKE_WARNING_PREFIX(service, group_pos, attribute, element) \
+ g_strdup_printf(_("in %s, group %i, \"%s\" attribute of \"%s\" element"), \
+ translate_service_get_name((service)), \
+@@ -142,6 +155,7 @@
+ const char *name);
+
+ static void translate_generic_service_log_connect (SoupMessage *message);
++#ifdef HAVE_LIBSOUP22
+ static void translate_generic_service_log_wrote_headers_h (SoupMessage *message,
+ gpointer user_data);
+ static void translate_generic_service_log_wrote_body_h (SoupMessage *message,
+@@ -153,10 +167,20 @@
+ static void translate_generic_service_log_headers_cb (const char *key,
+ const char *value,
+ gpointer user_data);
++#else
++static void translate_generic_service_log_printer (SoupLogger *logger,
++ SoupLoggerLogLevel level,
++ char direction,
++ const char *data,
++ gpointer user_data);
++#endif
+
+ static void translate_generic_service_progress_got_headers_h (SoupMessage *message,
+ gpointer user_data);
+ static void translate_generic_service_progress_got_chunk_h (SoupMessage *message,
++#ifdef HAVE_LIBSOUP24
++ SoupBuffer *chunk,
++#endif
+ gpointer user_data);
+
+ static void translate_generic_service_html_got_headers_h (SoupMessage *message,
+@@ -172,8 +196,10 @@
+ static void translate_generic_service_refresh_got_body_h (SoupMessage *message,
+ gpointer user_data);
+
++#ifdef HAVE_LIBSOUP22
+ static void translate_generic_service_redirect_handler (SoupMessage *message,
+ gpointer user_data);
++#endif
+
+ static char *translate_generic_service_translate_text (TranslateService *service,
+ const char *text,
+@@ -440,7 +466,11 @@
+ g_return_val_if_fail(post_content_type != NULL, NULL);
+ soup_message_set_request(message,
+ post_content_type,
++#ifdef HAVE_LIBSOUP22
+ SOUP_BUFFER_USER_OWNED,
++#else
++ SOUP_MEMORY_TEMPORARY,
++#endif
+ (char *) post,
+ strlen(post));
+ }
+@@ -448,7 +478,7 @@
+ for (l = headers; l != NULL; l = l->next)
+ {
+ TranslateGenericHttpHeader *header = l->data;
+- soup_message_add_header(message->request_headers, header->name, header->value);
++ soup_message_headers_append(message->request_headers, header->name, header->value);
+ }
+
+ info.session = translate_generic_service_soup_session_sync_new();
+@@ -456,12 +486,21 @@
+ info.html_http_equiv = NULL;
+
+ if (translate_generic_debug_flags & TRANSLATE_GENERIC_DEBUG_LOG_TRANSFERS)
+- g_object_connect(message,
+- "signal::wrote-headers", translate_generic_service_log_wrote_headers_h, &info,
+- "signal::wrote-body", translate_generic_service_log_wrote_body_h, &info,
+- "signal::got-headers", translate_generic_service_log_got_headers_h, &info,
+- "signal::got-body", translate_generic_service_log_got_body_h, &info,
+- NULL);
++ {
++#ifdef HAVE_LIBSOUP22
++ g_object_connect(message,
++ "signal::wrote-headers", translate_generic_service_log_wrote_headers_h, &info,
++ "signal::wrote-body", translate_generic_service_log_wrote_body_h, &info,
++ "signal::got-headers", translate_generic_service_log_got_headers_h, &info,
++ "signal::got-body", translate_generic_service_log_got_body_h, &info,
++ NULL);
++#else
++ SoupLogger *logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1);
++ soup_logger_set_printer (logger, translate_generic_service_log_printer, NULL, NULL);
++ soup_logger_attach (logger, info.session);
++ g_object_unref (logger);
++#endif
++ }
+
+ if (progress_func)
+ {
+@@ -489,6 +528,7 @@
+ if (flags & TRANSFER_FOLLOW_REFRESH)
+ g_signal_connect(message, "got-body", G_CALLBACK(translate_generic_service_refresh_got_body_h), &info);
+
++#ifdef HAVE_LIBSOUP22
+ /* http://bugzilla.ximian.com/show_bug.cgi?id=70688 */
+ soup_message_set_flags(message, SOUP_MESSAGE_NO_REDIRECT);
+ soup_message_add_status_class_handler(message,
+@@ -496,6 +536,7 @@
+ SOUP_HANDLER_POST_BODY,
+ translate_generic_service_redirect_handler,
+ info.session);
++#endif
+
+ if (translate_generic_debug_flags & TRANSLATE_GENERIC_DEBUG_LOG_TRANSFERS)
+ translate_generic_service_log_connect(message);
+@@ -554,18 +595,18 @@
+
+ if ((charset)||(response_charset))
+ {
+- response = g_convert(message->response.body, message->response.length, "UTF-8", response_charset?response_charset:charset, NULL, NULL, err);
++ response = g_convert(SOUP_MESSAGE_RESPONSE_BODY (message), SOUP_MESSAGE_RESPONSE_LENGTH (message), "UTF-8", charset, NULL, NULL, err);
+ g_free(charset);
+ }
+ else
+ {
+- if ((flags & TRANSFER_CONVERT) && ! g_utf8_validate(message->response.body, message->response.length, NULL))
++ if ((flags & TRANSFER_CONVERT) && ! g_utf8_validate(SOUP_MESSAGE_RESPONSE_BODY (message), SOUP_MESSAGE_RESPONSE_LENGTH (message), NULL))
+ g_set_error(err,
+ TRANSLATE_GENERIC_SERVICE_ERROR,
+ TRANSLATE_GENERIC_SERVICE_ERROR_TRANSFER,
+ _("invalid UTF-8"));
+ else
+- response = g_strndup(message->response.body, message->response.length);
++ response = g_strndup(SOUP_MESSAGE_RESPONSE_BODY (message), SOUP_MESSAGE_RESPONSE_LENGTH (message));
+ }
+ }
+ else
+@@ -606,7 +647,7 @@
+ : NULL;
+
+ if (! value)
+- value = soup_message_get_header(message->response_headers, name);
++ value = soup_message_headers_get(message->response_headers, name);
+
+ return value;
+ }
+@@ -614,12 +655,14 @@
+ static void
+ translate_generic_service_log_connect (SoupMessage *message)
+ {
+- const SoupUri *uri;
++ const SoupURI *uri;
+
+ uri = soup_message_get_uri(message);
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, _("connecting to %s:%i"), uri->host, uri->port);
+ }
+
++#ifdef HAVE_LIBSOUP22
++
+ static void
+ translate_generic_service_log_wrote_headers_h (SoupMessage *message,
+ gpointer user_data)
+@@ -674,6 +717,20 @@
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "%s %s: %s", prefix, key, value);
+ }
+
++#else /* !HAVE_LIBSOUP22 */
++
++static void
++translate_generic_service_log_printer (SoupLogger *logger,
++ SoupLoggerLogLevel level,
++ char direction,
++ const char *data,
++ gpointer user_data)
++{
++ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "%c %s", direction, data);
++}
++
++#endif /* HAVE_LIBSOUP22 */
++
+ static void
+ translate_generic_service_progress_got_headers_h (SoupMessage *message,
+ gpointer user_data)
+@@ -681,7 +738,7 @@
+ TransferInfo *info = user_data;
+ const char *content_length;
+
+- content_length = soup_message_get_header(message->response_headers, "Content-Length");
++ content_length = soup_message_headers_get(message->response_headers, "Content-Length");
+ info->length = (content_length
+ && *content_length
+ && strspn(content_length, "0123456789") == strlen(content_length))
+@@ -691,6 +748,9 @@
+
+ static void
+ translate_generic_service_progress_got_chunk_h (SoupMessage *message,
++#ifdef HAVE_LIBSOUP24
++ SoupBuffer *chunk,
++#endif
+ gpointer user_data)
+ {
+ TransferInfo *info = user_data;
+@@ -700,7 +760,11 @@
+ progress = -1;
+ else
+ {
++#ifdef HAVE_LIBSOUP22
+ info->received += message->response.length;
++#else
++ info->received += chunk->length;
++#endif
+ progress = (double) info->received / info->length;
+ progress = CLAMP(progress, 0.0, 1.0);
+ }
+@@ -716,7 +780,7 @@
+ TransferInfo *info = user_data;
+ const char *content_type;
+
+- content_type = soup_message_get_header(message->response_headers, "Content-Type");
++ content_type = soup_message_headers_get(message->response_headers, "Content-Type");
+ info->parse_html = content_type
+ && (g_str_has_prefix(content_type, "text/html")
+ || g_str_has_prefix(content_type, "application/xhtml+xml")
+@@ -736,7 +800,7 @@
+ info->html_http_equiv = NULL;
+ }
+
+- if (info->parse_html && message->response.length > 0)
++ if (info->parse_html && SOUP_MESSAGE_RESPONSE_LENGTH (message) > 0)
+ {
+ char *body;
+ xmlSAXHandler sax_handler = { NULL };
+@@ -750,7 +814,7 @@
+ sax_handler.startElement = translate_generic_service_html_start_element_cb;
+ sax_handler.endElement = translate_generic_service_html_end_element_cb;
+
+- body = g_strndup(message->response.body, message->response.length);
++ body = g_strndup(SOUP_MESSAGE_RESPONSE_BODY (message), SOUP_MESSAGE_RESPONSE_LENGTH (message));
+ htmlSAXParseDoc(body, NULL, &sax_handler, user_data);
+ g_free(body);
+ }
+@@ -817,7 +881,7 @@
+ {
+ TransferInfo *info = user_data;
+ const char *refresh_uri;
+- SoupUri *new_uri = NULL;
++ SoupURI *new_uri = NULL;
+
+ refresh_uri = translate_generic_service_get_header(message, info, "Refresh");
+ if (refresh_uri)
+@@ -832,9 +896,9 @@
+ new_uri = soup_uri_new(refresh_uri);
+ if (! new_uri)
+ {
+- const SoupUri *base_uri;
++ SoupURI *base_uri;
+
+- base_uri = soup_message_get_uri(message);
++ base_uri = (SoupURI *)soup_message_get_uri(message);
+ new_uri = soup_uri_new_with_base(base_uri, refresh_uri);
+ }
+ }
+@@ -851,6 +915,7 @@
+ }
+ }
+
++#ifdef HAVE_LIBSOUP22
+ static void
+ translate_generic_service_redirect_handler (SoupMessage *message,
+ gpointer user_data)
+@@ -887,6 +952,7 @@
+ soup_session_requeue_message(session, message);
+ }
+ }
++#endif
+
+ static char *
+ translate_generic_service_timed_translate_text (TranslateService *service,
+@@ -1391,7 +1457,7 @@
+ translate_generic_service_soup_session_sync_new (void)
+ {
+ char *proxy_text_uri;
+- SoupUri *proxy_uri = NULL;
++ SoupURI *proxy_uri = NULL;
+ SoupSession *session;
+ TranslateGenericSoupCookieJar *cookie_jar;
+
+@@ -1411,7 +1477,7 @@
+ soup_uri_free(proxy_uri);
+
+ cookie_jar = translate_generic_soup_cookie_jar_new();
+- soup_session_add_filter(session, SOUP_MESSAGE_FILTER(cookie_jar));
++ translate_generic_soup_cookie_jar_attach(cookie_jar, session);
+ g_object_unref(cookie_jar);
+
+ return session;
+diff -dPNur libtranslate-0.99/src/modules/translate-generic-soup-cookie-jar.c libtranslate-0.99-1/src/modules/translate-generic-soup-cookie-jar.c
+--- libtranslate-0.99/src/modules/translate-generic-soup-cookie-jar.c 2005-01-17 17:46:53.000000000 +0100
++++ libtranslate-0.99-1/src/modules/translate-generic-soup-cookie-jar.c 2010-02-14 03:55:56.000000000 +0100
+@@ -29,9 +29,12 @@
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
++#include "config.h"
+ #include <string.h>
+ #include <libsoup/soup.h>
++#ifdef HAVE_LIBSOUP22
+ #include <libsoup/soup-message-filter.h>
++#endif
+ #include "translate-generic-soup-cookie-jar.h"
+
+ struct _TranslateGenericSoupCookieJarPrivate
+@@ -44,9 +47,12 @@
+ static void translate_generic_soup_cookie_jar_register_type (GType *type);
+ static void translate_generic_soup_cookie_jar_class_init (TranslateGenericSoupCookieJarClass *class);
+ static void translate_generic_soup_cookie_jar_init (TranslateGenericSoupCookieJar *jar);
++#ifdef HAVE_LIBSOUP22
+ static void translate_generic_soup_cookie_jar_filter_init (SoupMessageFilterClass *iface);
++#else
++#define SoupMessageFilter TranslateGenericSoupCookieJar
++#endif
+ static void translate_generic_soup_cookie_jar_finalize (GObject *object);
+-
+ static void translate_generic_soup_cookie_jar_setup_message (SoupMessageFilter *filter,
+ SoupMessage *message);
+
+@@ -75,17 +81,21 @@
+ 0,
+ (GInstanceInitFunc) translate_generic_soup_cookie_jar_init
+ };
++#ifdef HAVE_LIBSOUP22
+ static const GInterfaceInfo filter_info = {
+ (GInterfaceInitFunc) translate_generic_soup_cookie_jar_filter_init,
+ NULL,
+ NULL
+ };
++#endif
+
+ *type = g_type_register_static(G_TYPE_OBJECT,
+ "TranslateGenericSoupCookieJar",
+ &info,
+ 0);
++#ifdef HAVE_LIBSOUP22
+ g_type_add_interface_static(*type, SOUP_TYPE_MESSAGE_FILTER, &filter_info);
++#endif
+ }
+
+ static void
+@@ -107,11 +117,13 @@
+ TranslateGenericSoupCookieJarPrivate);
+ }
+
++#ifdef HAVE_LIBSOUP22
+ static void
+ translate_generic_soup_cookie_jar_filter_init (SoupMessageFilterClass *iface)
+ {
+ iface->setup_message = translate_generic_soup_cookie_jar_setup_message;
+ }
++#endif
+
+ static void
+ translate_generic_soup_cookie_jar_finalize (GObject *object)
+@@ -125,26 +137,46 @@
+ }
+
+ static void
++add_cookie_to_jar (TranslateGenericSoupCookieJar *jar, const char *cookie)
++{
++ char *s;
++
++ s = strchr(cookie, ';');
++ if (s)
++ jar->priv->cookies = g_slist_append(jar->priv->cookies, g_strndup(cookie, s - cookie));
++}
++
++#ifdef HAVE_LIBSOUP24
++static void
++maybe_add_cookie_to_jar (const char *header, const char *value, gpointer jar)
++{
++ if (!g_ascii_strcasecmp (header, "Set-Cookie"))
++ add_cookie_to_jar (jar, value);
++}
++#endif
++
++static void
+ translate_generic_soup_cookie_jar_setup_message (SoupMessageFilter *filter,
+- SoupMessage *message)
++ SoupMessage *message)
+ {
+ TranslateGenericSoupCookieJar *jar = TRANSLATE_GENERIC_SOUP_COOKIE_JAR(filter);
+- const GSList *cookies;
+ const GSList *l;
+
+ /* FIXME: add full RFC 2965 support */
+
++#ifdef HAVE_LIBSOUP22
++ const GSList *cookies;
++
+ cookies = soup_message_get_header_list(message->response_headers, "Set-Cookie");
+ for (l = cookies; l != NULL; l = l->next)
+ {
+ const char *cookie = l->data;
+- char *s;
+-
+- s = strchr(cookie, ';');
+- if (s)
+- jar->priv->cookies = g_slist_append(jar->priv->cookies, g_strndup(cookie, s - cookie));
++ add_cookie_to_jar(jar, cookie);
+ }
+-
++#else
++ soup_message_headers_foreach(message->response_headers, maybe_add_cookie_to_jar, jar);
++#endif
++
+ if (jar->priv->cookies)
+ {
+ GString *string;
+@@ -159,13 +191,44 @@
+ g_string_append(string, "; ");
+ }
+
++#ifdef HAVE_LIBSOUP22
+ soup_message_add_header(message->request_headers, "Cookie", string->str);
++#else
++ soup_message_headers_append(message->request_headers, "Cookie", string->str);
++#endif
+ g_string_free(string, TRUE);
+ }
+ }
+
++#ifdef HAVE_LIBSOUP24
++static void
++translate_generic_soup_cookie_jar_request_started (SoupSession *session,
++ SoupMessage *message,
++ SoupSocket *socket,
++ gpointer cookie_jar)
++{
++ translate_generic_soup_cookie_jar_setup_message (cookie_jar, message);
++}
++#endif
++
+ TranslateGenericSoupCookieJar *
+ translate_generic_soup_cookie_jar_new (void)
+ {
+ return g_object_new(TRANSLATE_GENERIC_TYPE_SOUP_COOKIE_JAR, NULL);
+ }
++
++void
++translate_generic_soup_cookie_jar_attach (TranslateGenericSoupCookieJar *cookie_jar,
++ SoupSession *session)
++{
++#ifdef HAVE_LIBSOUP22
++ soup_session_add_filter (session, SOUP_MESSAGE_FILTER(cookie_jar));
++#else
++ g_signal_connect (session, "request_started",
++ G_CALLBACK (translate_generic_soup_cookie_jar_request_started),
++ cookie_jar);
++ g_object_set_data_full (G_OBJECT (session), "TranslateGenericSoupCookieJar",
++ g_object_ref (cookie_jar), g_object_unref);
++#endif
++}
++
+diff -dPNur libtranslate-0.99/src/modules/translate-generic-soup-cookie-jar.h libtranslate-0.99-1/src/modules/translate-generic-soup-cookie-jar.h
+--- libtranslate-0.99/src/modules/translate-generic-soup-cookie-jar.h 2005-01-17 17:47:00.000000000 +0100
++++ libtranslate-0.99-1/src/modules/translate-generic-soup-cookie-jar.h 2010-02-14 03:55:56.000000000 +0100
+@@ -33,6 +33,7 @@
+ #define _TRANSLATE_GENERIC_SOUP_COOKIE_JAR_H
+
+ #include <glib-object.h>
++#include <libsoup/soup-session.h>
+
+ #define TRANSLATE_GENERIC_TYPE_SOUP_COOKIE_JAR (translate_generic_soup_cookie_jar_get_type())
+ #define TRANSLATE_GENERIC_SOUP_COOKIE_JAR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TRANSLATE_GENERIC_TYPE_SOUP_COOKIE_JAR, TranslateGenericSoupCookieJar))
+@@ -59,5 +60,6 @@
+
+ GType translate_generic_soup_cookie_jar_get_type (void);
+ TranslateGenericSoupCookieJar *translate_generic_soup_cookie_jar_new (void);
++void translate_generic_soup_cookie_jar_attach (TranslateGenericSoupCookieJar *cookie_jar, SoupSession *session);
+
+ #endif /* _TRANSLATE_GENERIC_SOUP_COOKIE_JAR_H */
diff --git a/patches/mpg123/mpg123-ds-rcc.patch b/patches/mpg123/mpg123-ds-rcc.patch
new file mode 100644
index 0000000..bbfd104
--- /dev/null
+++ b/patches/mpg123/mpg123-ds-rcc.patch
@@ -0,0 +1,151 @@
+diff -dPNur mpg123-0.59r/common.c mpg123-0.59r-new/common.c
+--- mpg123-0.59r/common.c 1999-06-15 23:24:19.000000000 +0200
++++ mpg123-0.59r-new/common.c 2005-09-16 02:48:11.000000000 +0200
+@@ -15,6 +15,7 @@
+ #endif
+ #endif
+
++#include "rccpatch.h"
+ #include "mpg123.h"
+ #include "genre.h"
+ #include "common.h"
+@@ -484,6 +485,8 @@
+ char comment[31]={0,};
+ char genre[31]={0,};
+
++ char *ctitle, *cartist, *calbum, *ccomment;
++
+ if(param.quiet)
+ return;
+
+@@ -492,16 +495,26 @@
+ strncpy(album,tag->album,30);
+ strncpy(year,tag->year,4);
+ strncpy(comment,tag->comment,30);
++
++ ctitle = rccPatchRecode(title);
++ cartist = rccPatchRecode(artist);
++ calbum = rccPatchRecode(album);
++ ccomment = rccPatchRecode(comment);
+
+- if ( tag->genre <= sizeof(genre_table)/sizeof(*genre_table) ) {
++ if ( tag->genre < sizeof(genre_table)/sizeof(*genre_table) ) {
+ strncpy(genre, genre_table[tag->genre], 30);
+ } else {
+ strncpy(genre,"Unknown",30);
+ }
+
+- fprintf(stderr,"Title : %-30s Artist: %s\n",title,artist);
+- fprintf(stderr,"Album : %-30s Year : %4s\n",album,year);
+- fprintf(stderr,"Comment: %-30s Genre : %s\n",comment,genre);
++ fprintf(stderr,"Title : %-30s Artist: %s\n",ctitle?ctitle:title,cartist?cartist:artist);
++ fprintf(stderr,"Album : %-30s Year : %4s\n",calbum?calbum:album,year);
++ fprintf(stderr,"Comment: %-30s Genre : %s\n",ccomment?ccomment:comment,genre);
++
++ if (ctitle) free(ctitle);
++ if (cartist) free(cartist);
++ if (calbum) free(calbum);
++ if (ccomment) free(ccomment);
+ }
+
+ #if 0
+diff -dPNur mpg123-0.59r/Makefile mpg123-0.59r-new/Makefile
+--- mpg123-0.59r/Makefile 1999-06-18 14:18:58.000000000 +0200
++++ mpg123-0.59r-new/Makefile 2005-09-16 02:47:47.000000000 +0200
+@@ -554,12 +554,12 @@
+ mpg123-make:
+ @ $(MAKE) CFLAGS='$(CFLAGS)' BINNAME=mpg123 mpg123
+
+-mpg123: mpg123.o common.o $(OBJECTS) decode_2to1.o decode_4to1.o \
++mpg123: mpg123.o rccpatch.o common.o $(OBJECTS) decode_2to1.o decode_4to1.o \
+ tabinit.o audio.o layer1.o layer2.o layer3.o buffer.o \
+ getlopt.o httpget.o xfermem.o equalizer.o \
+ decode_ntom.o Makefile wav.o readers.o getbits.o \
+ control_generic.o
+- $(CC) $(CFLAGS) $(LDFLAGS) mpg123.o tabinit.o common.o layer1.o \
++ $(CC) $(CFLAGS) $(LDFLAGS) -lrcc mpg123.o tabinit.o rccpatch.o common.o layer1.o \
+ layer2.o layer3.o audio.o buffer.o decode_2to1.o equalizer.o \
+ decode_4to1.o getlopt.o httpget.o xfermem.o decode_ntom.o \
+ wav.o readers.o getbits.o control_generic.o \
+diff -dPNur mpg123-0.59r/mpg123.c mpg123-0.59r-new/mpg123.c
+--- mpg123-0.59r/mpg123.c 1999-06-18 14:18:11.000000000 +0200
++++ mpg123-0.59r-new/mpg123.c 2005-09-16 02:47:47.000000000 +0200
+@@ -32,6 +32,8 @@
+ #include "buffer.h"
+ #include "term.h"
+
++#include "rccpatch.h"
++
+ #include "version.h"
+
+ static void usage(char *dummy);
+@@ -903,6 +905,8 @@
+ exit(0);
+ }
+ #endif
++
++ rccPatchInit();
+
+ while ((fname = get_next_file(argc, argv))) {
+ char *dirname, *filename;
+@@ -1068,6 +1072,8 @@
+ intflag = FALSE;
+ }
+ }
++ rccPatchFree();
++
+ #ifndef NOXFERMEM
+ if (param.usebuffer) {
+ buffer_end();
+diff -dPNur mpg123-0.59r/rccpatch.c mpg123-0.59r-new/rccpatch.c
+--- mpg123-0.59r/rccpatch.c 1970-01-01 01:00:00.000000000 +0100
++++ mpg123-0.59r-new/rccpatch.c 2005-09-16 02:47:47.000000000 +0200
+@@ -0,0 +1,40 @@
++#include <librcc.h>
++
++#define ID3_CLASS 0
++#define OUT_CLASS 1
++static rcc_class classes[] = {
++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
++ { "out", RCC_CLASS_STANDARD, NULL, NULL, "Output Encoding", 0 },
++ { NULL }
++};
++
++static int rcc_initialized = 0;
++
++void rccPatchFree() {
++ if (rcc_initialized) {
++ rccFree();
++ rcc_initialized = 0;
++ }
++}
++
++void rccPatchInit() {
++ if (rcc_initialized) return;
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "xmms");
++ rccInitDb4(NULL, NULL, 0);
++ rcc_initialized = 1;
++}
++
++static void rccPatchTryInit() {
++ if (!rcc_initialized) {
++ rccPatchInit();
++ if (rcc_initialized) atexit(rccPatchFree);
++ }
++}
++
++char *rccPatchRecode(const char *str) {
++ char *res;
++ rccPatchTryInit();
++ return rccRecode(NULL, ID3_CLASS, OUT_CLASS, str);
++}
+diff -dPNur mpg123-0.59r/rccpatch.h mpg123-0.59r-new/rccpatch.h
+--- mpg123-0.59r/rccpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ mpg123-0.59r-new/rccpatch.h 2005-09-16 02:47:47.000000000 +0200
+@@ -0,0 +1,4 @@
++void rccPatchFree();
++void rccPatchInit();
++char *rccPatchRecode(const char *str);
++
diff --git a/patches/mpg123/mpg123-ds-rcc1121.patch b/patches/mpg123/mpg123-ds-rcc1121.patch
new file mode 100644
index 0000000..1335795
--- /dev/null
+++ b/patches/mpg123/mpg123-ds-rcc1121.patch
@@ -0,0 +1,247 @@
+diff -dPNur mpg123-1.12.1/configure.ac mpg123-1.12.1-new/configure.ac
+--- mpg123-1.12.1/configure.ac 2010-03-31 10:27:37.000000000 +0200
++++ mpg123-1.12.1-new/configure.ac 2010-07-07 23:44:57.000000000 +0200
+@@ -998,6 +998,21 @@
+ AC_CHECK_LIB([m], [sqrt])
+ AC_CHECK_LIB([mx], [powf])
+
++# LibRCC
++AC_CHECK_LIB(rcc, rccInit,[
++ AC_CHECK_HEADERS(librcc.h,[
++ LIBRCC_LIBS="-lrcc"
++ LIBRCC_INCLUDES="-DHAVE_LIBRCC"
++ ],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])
++AC_SUBST(LIBRCC_LIBS)
++AC_SUBST(LIBRCC_INCLUDES)
++
+ # attempt to make the signal stuff work... also with GENERIC - later
+ #if test x"$ac_cv_header_sys_signal_h" = xyes; then
+ # AC_CHECK_FUNCS( sigemptyset sigaddset sigprocmask sigaction )
+diff -dPNur mpg123-1.12.1/src/libmpg123/id3.c mpg123-1.12.1-new/src/libmpg123/id3.c
+--- mpg123-1.12.1/src/libmpg123/id3.c 2010-03-31 10:27:35.000000000 +0200
++++ mpg123-1.12.1-new/src/libmpg123/id3.c 2010-07-07 23:51:50.000000000 +0200
+@@ -9,6 +9,8 @@
+ #include "mpg123lib_intern.h"
+ #include "id3.h"
+ #include "debug.h"
++#include "rccpatch.h"
++
+
+ #ifndef NO_ID3V2 /* Only the main parsing routine will always be there. */
+
+@@ -829,6 +831,15 @@
+ size_t length = l;
+ size_t i;
+ unsigned char *p;
++ char *ctitle;
++
++ ctitle = mpg123_rcc_recode(s, l, &i);
++ if (ctitle) {
++ convert_utf8(sb, ctitle, i, 0);
++ free(ctitle);
++ return;
++ }
++
+ /* determine real length, a latin1 character can at most take 2 in UTF8 */
+ for(i=0; i<l; ++i)
+ if(s[i] >= 0x80) ++length;
+diff -dPNur mpg123-1.12.1/src/libmpg123/Makefile.am mpg123-1.12.1-new/src/libmpg123/Makefile.am
+--- mpg123-1.12.1/src/libmpg123/Makefile.am 2010-03-31 10:27:35.000000000 +0200
++++ mpg123-1.12.1-new/src/libmpg123/Makefile.am 2010-07-07 23:44:57.000000000 +0200
+@@ -29,6 +29,8 @@
+ libmpg123_la_DEPENDENCIES = @DECODER_LOBJ@ @LFS_LOBJ@
+
+ libmpg123_la_SOURCES = \
++ rccpatch.c \
++ rccpatch.h \
+ compat.c \
+ compat.h \
+ parse.c \
+diff -dPNur mpg123-1.12.1/src/libmpg123/rccpatch.c mpg123-1.12.1-new/src/libmpg123/rccpatch.c
+--- mpg123-1.12.1/src/libmpg123/rccpatch.c 1970-01-01 01:00:00.000000000 +0100
++++ mpg123-1.12.1-new/src/libmpg123/rccpatch.c 2010-07-07 23:55:59.000000000 +0200
+@@ -0,0 +1,76 @@
++#include <string.h>
++#include <librcc.h>
++#include "mpg123.h"
++
++#define ID3_CLASS 0
++#define UTF_CLASS 1
++#define OUT_CLASS 2
++static rcc_class classes[] = {
++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
++ { "utf", RCC_CLASS_KNOWN, "UTF-8", NULL, "Unicode Encoding", 0 },
++ { "out", RCC_CLASS_STANDARD, NULL, NULL, "Output Encoding", 0 },
++ { NULL }
++};
++
++static int rcc_initialized = 0;
++
++void mpg123_rcc_free() {
++ if (rcc_initialized) {
++ rccFree();
++ rcc_initialized = 0;
++ }
++}
++
++void mpg123_rcc_init() {
++ if (rcc_initialized) return;
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "xmms");
++ rccInitDb4(NULL, NULL, 0);
++ rcc_initialized = 1;
++}
++
++static void mpg123_rcc_try_init() {
++ if (!rcc_initialized) {
++ mpg123_rcc_init();
++ if (rcc_initialized) atexit(mpg123_rcc_free);
++ }
++}
++
++
++char *mpg123_rcc_recode(const char *str, size_t len, size_t *rlen) {
++ char *res;
++ mpg123_rcc_try_init();
++ return rccSizedRecode(NULL, ID3_CLASS, UTF_CLASS, str, len, rlen);
++}
++
++static void mpg123_rcc_recode_string(mpg123_string *str, rcc_class_id from, rcc_class_id to) {
++ size_t size;
++ char *res;
++
++ if ((!str)||(str->fill<2)) return;
++
++ mpg123_rcc_try_init();
++
++ res = rccSizedRecode(NULL, from, to, str->p, str->fill - 1, &size);
++ if (res) {
++ if (size+1>str->size) {
++ if (!mpg123_resize_string(str, size + 1)) {
++ // allocation failed
++ free(res);
++ return;
++ }
++ }
++ strncpy(str->p, res, size + 1);
++ str->fill = size + 1;
++ free(res);
++ }
++}
++
++void mpg123_rcc_recode_utf_string(mpg123_string *str) {
++ mpg123_rcc_recode_string(str, UTF_CLASS, OUT_CLASS);
++}
++
++void mpg123_rcc_recode_latin_string(mpg123_string *str) {
++ mpg123_rcc_recode_string(str, ID3_CLASS, OUT_CLASS);
++}
+diff -dPNur mpg123-1.12.1/src/libmpg123/rccpatch.h mpg123-1.12.1-new/src/libmpg123/rccpatch.h
+--- mpg123-1.12.1/src/libmpg123/rccpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ mpg123-1.12.1-new/src/libmpg123/rccpatch.h 2010-07-07 23:44:57.000000000 +0200
+@@ -0,0 +1,5 @@
++void mpg123_rcc_free();
++void mpg123_rcc_init();
++char *mpg123_rcc_recode(const char *str, size_t len, size_t *rlen);
++void mpg123_rcc_recode_utf_string(mpg123_string *str);
++void mpg123_rcc_recode_latin_string(mpg123_string *str);
+diff -dPNur mpg123-1.12.1/src/Makefile.am mpg123-1.12.1-new/src/Makefile.am
+--- mpg123-1.12.1/src/Makefile.am 2010-03-31 10:27:36.000000000 +0200
++++ mpg123-1.12.1-new/src/Makefile.am 2010-07-07 23:46:10.000000000 +0200
+@@ -5,10 +5,10 @@
+ ## initially written by Nicholas J. Humfrey
+
+ AM_CPPFLAGS = -DPKGLIBDIR="\"$(pkglibdir)\""
+-mpg123_LDADD = $(LIBLTDL) libmpg123/libmpg123.la @MODULE_OBJ@ @OUTPUT_OBJ@ @OUTPUT_LIBS@
++mpg123_LDADD = $(LIBLTDL) libmpg123/libmpg123.la @MODULE_OBJ@ @OUTPUT_OBJ@ @OUTPUT_LIBS@ @LIBRCC_LIBS@
+ mpg123_LDFLAGS = @EXEC_LT_LDFLAGS@ @OUTPUT_LDFLAGS@
+ # Just mpg123_INCLUDES has no effect on build! Trying that before reverting to AM_CPPFLAGS.
+-INCLUDES = $(LTDLINCL) -I$(top_builddir)/src/libmpg123 -I$(top_srcdir)/src/libmpg123
++INCLUDES = $(LTDLINCL) -I$(top_builddir)/src/libmpg123 -I$(top_srcdir)/src/libmpg123 @LIBRCC_INCLUDES@
+ # libltdl is not mentioned here... it's not that trivial
+ mpg123_DEPENDENCIES = @OUTPUT_OBJ@ @MODULE_OBJ@ libmpg123/libmpg123.la
+
+diff -dPNur mpg123-1.12.1/src/metaprint.c mpg123-1.12.1-new/src/metaprint.c
+--- mpg123-1.12.1/src/metaprint.c 2010-03-31 10:27:36.000000000 +0200
++++ mpg123-1.12.1-new/src/metaprint.c 2010-07-07 23:54:21.000000000 +0200
+@@ -17,7 +17,11 @@
+ if(source == NULL) return;
+
+ if(utf8env) mpg123_copy_string(source, dest);
+- else utf8_ascii(dest, source);
++ else {
++ mpg123_copy_string(source, dest);
++ mpg123_rcc_recode_utf_string(dest);
++// utf8_ascii(dest, source);
++ }
+ }
+
+ /* print tags... limiting the UTF-8 to ASCII */
+@@ -53,6 +57,7 @@
+ strncpy(tag[TITLE].p,v1->title,30);
+ tag[TITLE].p[30] = 0;
+ tag[TITLE].fill = strlen(tag[TITLE].p) + 1;
++ mpg123_rcc_recode_latin_string(&tag[TITLE]);
+ }
+ }
+ if(!tag[ARTIST].fill)
+@@ -62,6 +67,7 @@
+ strncpy(tag[ARTIST].p,v1->artist,30);
+ tag[ARTIST].p[30] = 0;
+ tag[ARTIST].fill = strlen(tag[ARTIST].p) + 1;
++ mpg123_rcc_recode_latin_string(&tag[ARTIST]);
+ }
+ }
+ if(!tag[ALBUM].fill)
+@@ -71,6 +77,7 @@
+ strncpy(tag[ALBUM].p,v1->album,30);
+ tag[ALBUM].p[30] = 0;
+ tag[ALBUM].fill = strlen(tag[ALBUM].p) + 1;
++ mpg123_rcc_recode_latin_string(&tag[ALBUM]);
+ }
+ }
+ if(!tag[COMMENT].fill)
+@@ -80,6 +87,7 @@
+ strncpy(tag[COMMENT].p,v1->comment,30);
+ tag[COMMENT].p[30] = 0;
+ tag[COMMENT].fill = strlen(tag[COMMENT].p) + 1;
++ mpg123_rcc_recode_latin_string(&tag[COMMENT]);
+ }
+ }
+ if(!tag[YEAR].fill)
+diff -dPNur mpg123-1.12.1/src/mpg123.c mpg123-1.12.1-new/src/mpg123.c
+--- mpg123-1.12.1/src/mpg123.c 2010-03-31 10:27:36.000000000 +0200
++++ mpg123-1.12.1-new/src/mpg123.c 2010-07-07 23:52:58.000000000 +0200
+@@ -10,6 +10,7 @@
+ #include "mpg123app.h"
+ #include "mpg123.h"
+ #include "local.h"
++#include "rccpatch.h"
+
+ #ifdef HAVE_SYS_WAIT_H
+ #include <sys/wait.h>
+@@ -973,6 +974,8 @@
+ if(param.term_ctrl)
+ term_init();
+ #endif
++
++ mpg123_rcc_init();
+ while ((fname = get_next_file()))
+ {
+ char *dirname, *filename;
+@@ -1132,6 +1135,8 @@
+ #endif
+ }
+ } /* end of loop over input files */
++ mpg123_rcc_free();
++
+ /* Ensure we played everything. */
+ if(param.smooth && param.usebuffer)
+ {
diff --git a/patches/mpg123/mpg123-ds-rcc173.patch b/patches/mpg123/mpg123-ds-rcc173.patch
new file mode 100644
index 0000000..ae814ff
--- /dev/null
+++ b/patches/mpg123/mpg123-ds-rcc173.patch
@@ -0,0 +1,271 @@
+diff -dPNur mpg123-1.7.3/configure.ac mpg123-1.7.3-rusxmms/configure.ac
+--- mpg123-1.7.3/configure.ac 2009-04-27 09:02:40.000000000 +0200
++++ mpg123-1.7.3-rusxmms/configure.ac 2009-06-13 23:22:17.000000000 +0200
+@@ -759,6 +759,21 @@
+ AC_CHECK_LIB([m], [sqrt])
+ AC_CHECK_LIB([mx], [powf])
+
++# LibRCC
++AC_CHECK_LIB(rcc, rccInit,[
++ AC_CHECK_HEADERS(librcc.h,[
++ LIBRCC_LIBS="-lrcc"
++ LIBRCC_INCLUDES="-DHAVE_LIBRCC"
++ ],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])
++AC_SUBST(LIBRCC_LIBS)
++AC_SUBST(LIBRCC_INCLUDES)
++
+ # attempt to make the signal stuff work... also with GENERIC - later
+ #if test x"$ac_cv_header_sys_signal_h" = xyes; then
+ # AC_CHECK_FUNCS( sigemptyset sigaddset sigprocmask sigaction )
+diff -dPNur mpg123-1.7.3/src/libmpg123/id3.c mpg123-1.7.3-rusxmms/src/libmpg123/id3.c
+--- mpg123-1.7.3/src/libmpg123/id3.c 2009-04-27 09:02:12.000000000 +0200
++++ mpg123-1.7.3-rusxmms/src/libmpg123/id3.c 2009-06-14 00:34:23.000000000 +0200
+@@ -9,6 +9,8 @@
+ #include "mpg123lib_intern.h"
+ #include "id3.h"
+ #include "debug.h"
++#include "rccpatch.h"
++
+
+ #ifndef NO_ID3V2 /* Only the main parsing routine will always be there. */
+
+@@ -774,6 +776,15 @@
+ size_t length = l;
+ size_t i;
+ unsigned char *p;
++ char *ctitle;
++
++ ctitle = mpg123_rcc_recode(s, l, &i);
++ if (ctitle) {
++ convert_utf8(sb, ctitle, i);
++ free(ctitle);
++ return;
++ }
++
+ /* determine real length, a latin1 character can at most take 2 in UTF8 */
+ for(i=0; i<l; ++i)
+ if(s[i] >= 0x80) ++length;
+diff -dPNur mpg123-1.7.3/src/libmpg123/libmpg123.sym mpg123-1.7.3-rusxmms/src/libmpg123/libmpg123.sym
+--- mpg123-1.7.3/src/libmpg123/libmpg123.sym 2009-04-27 09:07:20.000000000 +0200
++++ mpg123-1.7.3-rusxmms/src/libmpg123/libmpg123.sym 2009-06-14 01:53:25.000000000 +0200
+@@ -74,3 +74,8 @@
+ mpg123_position
+ mpg123_length
+ mpg123_set_filesize
++mpg123_rcc_free
++mpg123_rcc_init
++mpg123_rcc_recode
++mpg123_rcc_recode_utf_string
++mpg123_rcc_recode_latin_string
+diff -dPNur mpg123-1.7.3/src/libmpg123/libmpg123.sym.in mpg123-1.7.3-rusxmms/src/libmpg123/libmpg123.sym.in
+--- mpg123-1.7.3/src/libmpg123/libmpg123.sym.in 2009-04-27 09:02:12.000000000 +0200
++++ mpg123-1.7.3-rusxmms/src/libmpg123/libmpg123.sym.in 2009-06-14 01:51:45.000000000 +0200
+@@ -74,3 +74,8 @@
+ mpg123_position@LARGEFILE_SUFFIX@
+ mpg123_length@LARGEFILE_SUFFIX@
+ mpg123_set_filesize@LARGEFILE_SUFFIX@
++mpg123_rcc_free
++mpg123_rcc_init
++mpg123_rcc_recode
++mpg123_rcc_recode_utf_string
++mpg123_rcc_recode_latin_string
+diff -dPNur mpg123-1.7.3/src/libmpg123/Makefile.am mpg123-1.7.3-rusxmms/src/libmpg123/Makefile.am
+--- mpg123-1.7.3/src/libmpg123/Makefile.am 2009-04-27 09:02:12.000000000 +0200
++++ mpg123-1.7.3-rusxmms/src/libmpg123/Makefile.am 2009-06-14 00:10:34.000000000 +0200
+@@ -31,6 +31,8 @@
+ libmpg123_la_DEPENDENCIES = @DECODER_LOBJ@ libmpg123.sym
+
+ libmpg123_la_SOURCES = \
++ rccpatch.c \
++ rccpatch.h \
+ compat.c \
+ compat.h \
+ parse.c \
+diff -dPNur mpg123-1.7.3/src/libmpg123/rccpatch.c mpg123-1.7.3-rusxmms/src/libmpg123/rccpatch.c
+--- mpg123-1.7.3/src/libmpg123/rccpatch.c 1970-01-01 01:00:00.000000000 +0100
++++ mpg123-1.7.3-rusxmms/src/libmpg123/rccpatch.c 2009-06-14 01:54:37.000000000 +0200
+@@ -0,0 +1,76 @@
++#include <string.h>
++#include <librcc.h>
++#include "mpg123.h"
++
++#define ID3_CLASS 0
++#define UTF_CLASS 1
++#define OUT_CLASS 2
++static rcc_class classes[] = {
++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
++ { "utf", RCC_CLASS_KNOWN, "UTF-8", NULL, "Unicode Encoding", 0 },
++ { "out", RCC_CLASS_STANDARD, NULL, NULL, "Output Encoding", 0 },
++ { NULL }
++};
++
++static int rcc_initialized = 0;
++
++void mpg123_rcc_free() {
++ if (rcc_initialized) {
++ rccFree();
++ rcc_initialized = 0;
++ }
++}
++
++void mpg123_rcc_init() {
++ if (rcc_initialized) return;
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "xmms");
++ rccInitDb4(NULL, NULL, 0);
++ rcc_initialized = 1;
++}
++
++static void mpg123_rcc_try_init() {
++ if (!rcc_initialized) {
++ mpg123_rcc_init();
++ if (rcc_initialized) atexit(mpg123_rcc_free);
++ }
++}
++
++
++char *mpg123_rcc_recode(const char *str, size_t len, size_t *rlen) {
++ char *res;
++ mpg123_rcc_try_init();
++ return rccSizedRecode(NULL, ID3_CLASS, UTF_CLASS, str, len, rlen);
++}
++
++static void mpg123_rcc_recode_string(mpg123_string *str, rcc_class_id from, rcc_class_id to) {
++ size_t size;
++ char *res;
++
++ if ((!str)||(str->fill<2)) return;
++
++ mpg123_rcc_try_init();
++
++ res = rccSizedRecode(NULL, from, to, str->p, str->fill - 1, &size);
++ if (res) {
++ if (size+1>str->size) {
++ if (!mpg123_resize_string(str, size + 1)) {
++ // allocation failed
++ free(res);
++ return;
++ }
++ }
++ strncpy(str->p, res, size + 1);
++ str->fill = size + 1;
++ free(res);
++ }
++}
++
++void mpg123_rcc_recode_utf_string(mpg123_string *str) {
++ mpg123_rcc_recode_string(str, UTF_CLASS, OUT_CLASS);
++}
++
++void mpg123_rcc_recode_latin_string(mpg123_string *str) {
++ mpg123_rcc_recode_string(str, ID3_CLASS, OUT_CLASS);
++}
+diff -dPNur mpg123-1.7.3/src/libmpg123/rccpatch.h mpg123-1.7.3-rusxmms/src/libmpg123/rccpatch.h
+--- mpg123-1.7.3/src/libmpg123/rccpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ mpg123-1.7.3-rusxmms/src/libmpg123/rccpatch.h 2009-06-14 01:51:21.000000000 +0200
+@@ -0,0 +1,5 @@
++void mpg123_rcc_free();
++void mpg123_rcc_init();
++char *mpg123_rcc_recode(const char *str, size_t len, size_t *rlen);
++void mpg123_rcc_recode_utf_string(mpg123_string *str);
++void mpg123_rcc_recode_latin_string(mpg123_string *str);
+diff -dPNur mpg123-1.7.3/src/Makefile.am mpg123-1.7.3-rusxmms/src/Makefile.am
+--- mpg123-1.7.3/src/Makefile.am 2009-04-27 09:02:12.000000000 +0200
++++ mpg123-1.7.3-rusxmms/src/Makefile.am 2009-06-14 00:10:22.000000000 +0200
+@@ -5,10 +5,10 @@
+ ## initially written by Nicholas J. Humfrey
+
+ AM_CPPFLAGS = -DPKGLIBDIR="\"$(pkglibdir)\""
+-mpg123_LDADD = $(LIBLTDL) libmpg123/libmpg123.la @MODULE_OBJ@ @OUTPUT_OBJ@ @OUTPUT_LIBS@
++mpg123_LDADD = $(LIBLTDL) libmpg123/libmpg123.la @MODULE_OBJ@ @OUTPUT_OBJ@ @OUTPUT_LIBS@ @LIBRCC_LIBS@
+ mpg123_LDFLAGS = @LT_LDFLAGS@ @OUTPUT_LDFLAGS@
+ # Just mpg123_INCLUDES has no effect on build! Trying that before reverting to AM_CPPFLAGS.
+-INCLUDES = $(LTDLINCL) -I$(top_builddir)/src/libmpg123 -I$(top_srcdir)/src/libmpg123
++INCLUDES = $(LTDLINCL) -I$(top_builddir)/src/libmpg123 -I$(top_srcdir)/src/libmpg123 @LIBRCC_INCLUDES@
+ # libltdl is not mentioned here... it's not that trivial
+ mpg123_DEPENDENCIES = @OUTPUT_OBJ@ @MODULE_OBJ@ libmpg123/libmpg123.la
+
+diff -dPNur mpg123-1.7.3/src/metaprint.c mpg123-1.7.3-rusxmms/src/metaprint.c
+--- mpg123-1.7.3/src/metaprint.c 2009-04-27 09:02:12.000000000 +0200
++++ mpg123-1.7.3-rusxmms/src/metaprint.c 2009-06-14 01:59:09.000000000 +0200
+@@ -17,7 +17,11 @@
+ if(source == NULL) return;
+
+ if(utf8env) mpg123_copy_string(source, dest);
+- else utf8_ascii(dest, source);
++ else {
++ mpg123_copy_string(source, dest);
++ mpg123_rcc_recode_utf_string(dest);
++// utf8_ascii(dest, source);
++ }
+ }
+
+ /* print tags... limiting the UTF-8 to ASCII */
+@@ -53,6 +57,7 @@
+ strncpy(tag[TITLE].p,v1->title,30);
+ tag[TITLE].p[30] = 0;
+ tag[TITLE].fill = strlen(tag[TITLE].p) + 1;
++ mpg123_rcc_recode_latin_string(&tag[TITLE]);
+ }
+ }
+ if(!tag[ARTIST].fill)
+@@ -62,6 +67,7 @@
+ strncpy(tag[ARTIST].p,v1->artist,30);
+ tag[ARTIST].p[30] = 0;
+ tag[ARTIST].fill = strlen(tag[ARTIST].p) + 1;
++ mpg123_rcc_recode_latin_string(&tag[ARTIST]);
+ }
+ }
+ if(!tag[ALBUM].fill)
+@@ -71,6 +77,7 @@
+ strncpy(tag[ALBUM].p,v1->album,30);
+ tag[ALBUM].p[30] = 0;
+ tag[ALBUM].fill = strlen(tag[ALBUM].p) + 1;
++ mpg123_rcc_recode_latin_string(&tag[ALBUM]);
+ }
+ }
+ if(!tag[COMMENT].fill)
+@@ -80,6 +87,7 @@
+ strncpy(tag[COMMENT].p,v1->comment,30);
+ tag[COMMENT].p[30] = 0;
+ tag[COMMENT].fill = strlen(tag[COMMENT].p) + 1;
++ mpg123_rcc_recode_latin_string(&tag[COMMENT]);
+ }
+ }
+ if(!tag[YEAR].fill)
+diff -dPNur mpg123-1.7.3/src/mpg123.c mpg123-1.7.3-rusxmms/src/mpg123.c
+--- mpg123-1.7.3/src/mpg123.c 2009-04-27 09:02:12.000000000 +0200
++++ mpg123-1.7.3-rusxmms/src/mpg123.c 2009-06-14 00:35:14.000000000 +0200
+@@ -10,6 +10,7 @@
+ #include "mpg123app.h"
+ #include "mpg123.h"
+ #include "local.h"
++#include "rccpatch.h"
+
+ #ifdef HAVE_SYS_WAIT_H
+ #include <sys/wait.h>
+@@ -893,6 +894,8 @@
+ if(param.term_ctrl)
+ term_init();
+ #endif
++
++ mpg123_rcc_init();
+ while ((fname = get_next_file()))
+ {
+ char *dirname, *filename;
+@@ -1038,6 +1041,8 @@
+ #endif
+ }
+ } /* end of loop over input files */
++ mpg123_rcc_free();
++
+ /* Ensure we played everything. */
+ if(param.smooth && param.usebuffer)
+ {
diff --git a/patches/mpg123/mpg123-ds-rcc65.patch b/patches/mpg123/mpg123-ds-rcc65.patch
new file mode 100644
index 0000000..41a4d33
--- /dev/null
+++ b/patches/mpg123/mpg123-ds-rcc65.patch
@@ -0,0 +1,240 @@
+diff -dPNur mpg123-0.65/configure.ac mpg123-0.65-new/configure.ac
+--- mpg123-0.65/configure.ac 2007-02-07 10:24:33.000000000 +0100
++++ mpg123-0.65-new/configure.ac 2007-04-15 13:36:52.000000000 +0200
+@@ -137,6 +137,21 @@
+ AC_CHECK_LIB([m], [sqrt])
+ AC_CHECK_LIB([mx], [powf])
+
++# LibRCC
++AC_CHECK_LIB(rcc, rccInit,[
++ AC_CHECK_HEADERS(librcc.h,[
++ LIBRCC_LIBS="-lrcc"
++ LIBRCC_INCLUDES="-DHAVE_LIBRCC"
++ ],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])
++AC_SUBST(LIBRCC_LIBS)
++AC_SUBST(LIBRCC_INCLUDES)
++
+ # Check for JACK
+ PKG_CHECK_MODULES(JACK, jack, HAVE_JACK=yes, HAVE_JACK=no)
+
+diff -dPNur mpg123-0.65/src/id3.c mpg123-0.65-new/src/id3.c
+--- mpg123-0.65/src/id3.c 2007-02-07 10:24:33.000000000 +0100
++++ mpg123-0.65-new/src/id3.c 2007-04-15 13:26:06.000000000 +0200
+@@ -6,6 +6,7 @@
+ #include "stringbuf.h"
+ #include "genre.h"
+ #include "id3.h"
++#include "rccpatch.h"
+
+ struct taginfo
+ {
+@@ -499,6 +500,8 @@
+ void print_id3_tag(unsigned char *id3v1buf)
+ {
+ char genre_from_v1 = 0;
++ char *ctitle, *cartist, *calbum, *ccomment;
++
+ if(!(id3.version || id3v1buf)) return;
+ if(id3v1buf != NULL)
+ {
+@@ -688,17 +691,26 @@
+ free_stringbuf(&tmp);
+ }
+
++ if (id3.title.fill) ctitle = rccPatchRecode(id3.title.p);
++ else ctitle = NULL;
++ if (id3.artist.fill) cartist = rccPatchRecode(id3.artist.p);
++ else cartist = NULL;
++ if (id3.album.fill) calbum = rccPatchRecode(id3.album.p);
++ else calbum = NULL;
++ if (id3.comment.fill) ccomment = rccPatchRecode(id3.comment.p);
++ else ccomment = NULL;
++
+ if(param.long_id3)
+ {
+ fprintf(stderr,"\n");
+ /* print id3v2 */
+ /* dammed, I use pointers as bool again! It's so convenient... */
+- fprintf(stderr,"\tTitle: %s\n", id3.title.fill ? id3.title.p : "");
+- fprintf(stderr,"\tArtist: %s\n", id3.artist.fill ? id3.artist.p : "");
+- fprintf(stderr,"\tAlbum: %s\n", id3.album.fill ? id3.album.p : "");
++ fprintf(stderr,"\tTitle: %s\n", ctitle?ctitle:(id3.title.fill ? id3.title.p : ""));
++ fprintf(stderr,"\tArtist: %s\n", cartist?cartist:(id3.artist.fill ? id3.artist.p : ""));
++ fprintf(stderr,"\tAlbum: %s\n", calbum?calbum:(id3.album.fill ? id3.album.p : ""));
+ fprintf(stderr,"\tYear: %s\n", id3.year.fill ? id3.year.p : "");
+ fprintf(stderr,"\tGenre: %s\n", id3.genre.fill ? id3.genre.p : "");
+- fprintf(stderr,"\tComment: %s\n", id3.comment.fill ? id3.comment.p : "");
++ fprintf(stderr,"\tComment: %s\n", ccomment?ccomment:(id3.comment.fill ? id3.comment.p : ""));
+ fprintf(stderr,"\n");
+ }
+ else
+@@ -708,23 +720,23 @@
+ /* one _could_ circumvent the strlen calls... */
+ if(id3.title.fill && id3.artist.fill && strlen(id3.title.p) <= 30 && strlen(id3.title.p) <= 30)
+ {
+- fprintf(stderr,"Title: %-30s Artist: %s\n",id3.title.p,id3.artist.p);
++ fprintf(stderr,"Title: %-30s Artist: %s\n",ctitle?ctitle:id3.title.p,cartist?cartist:id3.artist.p);
+ }
+ else
+ {
+- if(id3.title.fill) fprintf(stderr,"Title: %s\n", id3.title.p);
+- if(id3.artist.fill) fprintf(stderr,"Artist: %s\n", id3.artist.p);
++ if(id3.title.fill) fprintf(stderr,"Title: %s\n", ctitle?ctitle:id3.title.p);
++ if(id3.artist.fill) fprintf(stderr,"Artist: %s\n", cartist?cartist:id3.artist.p);
+ }
+ if (id3.comment.fill && id3.album.fill && strlen(id3.comment.p) <= 30 && strlen(id3.album.p) <= 30)
+ {
+- fprintf(stderr,"Comment: %-30s Album: %s\n",id3.comment.p,id3.album.p);
++ fprintf(stderr,"Comment: %-30s Album: %s\n",ccomment?ccomment:id3.comment.p,calbum?calbum:id3.album.p);
+ }
+ else
+ {
+ if (id3.comment.fill)
+- fprintf(stderr,"Comment: %s\n", id3.comment.p);
++ fprintf(stderr,"Comment: %s\n", ccomment?ccomment:id3.comment.p);
+ if (id3.album.fill)
+- fprintf(stderr,"Album: %s\n", id3.album.p);
++ fprintf(stderr,"Album: %s\n", calbum?calbum:id3.album.p);
+ }
+ if (id3.year.fill && id3.genre.fill && strlen(id3.year.p) <= 30 && strlen(id3.genre.p) <= 30)
+ {
+@@ -738,6 +750,11 @@
+ fprintf(stderr,"Genre: %s\n", id3.genre.p);
+ }
+ }
++
++ if (ctitle) free(ctitle);
++ if (cartist) free(cartist);
++ if (calbum) free(calbum);
++ if (ccomment) free(ccomment);
+ }
+
+ /*
+diff -dPNur mpg123-0.65/src/Makefile.am mpg123-0.65-new/src/Makefile.am
+--- mpg123-0.65/src/Makefile.am 2007-02-07 10:24:33.000000000 +0100
++++ mpg123-0.65-new/src/Makefile.am 2007-04-15 13:38:44.000000000 +0200
+@@ -4,8 +4,8 @@
+ ## see COPYING and AUTHORS files in distribution or http://mpg123.de
+ ## initially written by Nicholas J. Humfrey
+
+-AM_CFLAGS = @AUDIO_CFLAGS@
+-AM_LDFLAGS = @AUDIO_LIBS@
++AM_CFLAGS = @AUDIO_CFLAGS@ @LIBRCC_INCLUDES@
++AM_LDFLAGS = @AUDIO_LIBS@ @LIBRCC_LIBS@
+ mpg123_LDADD = @AUDIO_OBJ@ @CPU_TYPE_LIB@
+ mpg123_DEPENDENCIES = @AUDIO_OBJ@ @CPU_TYPE_LIB@
+
+@@ -14,6 +14,8 @@
+
+ bin_PROGRAMS = mpg123
+ mpg123_SOURCES = \
++ rccpatch.c \
++ rccpatch.h \
+ audio.c \
+ audio.h \
+ buffer.c \
+diff -dPNur mpg123-0.65/src/mpg123.c mpg123-0.65-new/src/mpg123.c
+--- mpg123-0.65/src/mpg123.c 2007-02-07 10:24:33.000000000 +0100
++++ mpg123-0.65-new/src/mpg123.c 2007-04-15 13:30:42.000000000 +0200
+@@ -35,6 +35,7 @@
+ #include "layer3.h"
+ #endif
+ #include "playlist.h"
++#include "rccpatch.h"
+ #include "id3.h"
+ #include "icy.h"
+
+@@ -814,15 +815,18 @@
+
+ if(param.remote) {
+ int ret;
++ rccPatchInit();
+ init_id3();
+ init_icy();
+ ret = control_generic(&fr);
+ clear_icy();
+ exit_id3();
++ rccPatchFree();
+ safe_exit(ret);
+ }
+ #endif
+
++ rccPatchInit();
+ init_icy();
+ init_id3(); /* prepare id3 memory */
+ while ((fname = get_next_file())) {
+@@ -851,7 +855,7 @@
+ }
+ }
+ #endif
+-
++
+ }
+
+ #if !defined(WIN32) && !defined(GENERIC)
+@@ -1031,6 +1035,7 @@
+ } /* end of loop over input files */
+ clear_icy();
+ exit_id3(); /* free id3 memory */
++ rccPatchFree();
+ #ifndef NOXFERMEM
+ if (param.usebuffer) {
+ buffer_end();
+diff -dPNur mpg123-0.65/src/rccpatch.c mpg123-0.65-new/src/rccpatch.c
+--- mpg123-0.65/src/rccpatch.c 1970-01-01 01:00:00.000000000 +0100
++++ mpg123-0.65-new/src/rccpatch.c 2007-04-15 13:13:09.000000000 +0200
+@@ -0,0 +1,40 @@
++#include <librcc.h>
++
++#define ID3_CLASS 0
++#define OUT_CLASS 1
++static rcc_class classes[] = {
++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
++ { "out", RCC_CLASS_STANDARD, NULL, NULL, "Output Encoding", 0 },
++ { NULL }
++};
++
++static int rcc_initialized = 0;
++
++void rccPatchFree() {
++ if (rcc_initialized) {
++ rccFree();
++ rcc_initialized = 0;
++ }
++}
++
++void rccPatchInit() {
++ if (rcc_initialized) return;
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "xmms");
++ rccInitDb4(NULL, NULL, 0);
++ rcc_initialized = 1;
++}
++
++static void rccPatchTryInit() {
++ if (!rcc_initialized) {
++ rccPatchInit();
++ if (rcc_initialized) atexit(rccPatchFree);
++ }
++}
++
++char *rccPatchRecode(const char *str) {
++ char *res;
++ rccPatchTryInit();
++ return rccRecode(NULL, ID3_CLASS, OUT_CLASS, str);
++}
+diff -dPNur mpg123-0.65/src/rccpatch.h mpg123-0.65-new/src/rccpatch.h
+--- mpg123-0.65/src/rccpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ mpg123-0.65-new/src/rccpatch.h 2007-04-15 13:13:09.000000000 +0200
+@@ -0,0 +1,4 @@
++void rccPatchFree();
++void rccPatchInit();
++char *rccPatchRecode(const char *str);
++
diff --git a/patches/p7zip/README b/patches/p7zip/README
new file mode 100644
index 0000000..fe8ae08
--- /dev/null
+++ b/patches/p7zip/README
@@ -0,0 +1,13 @@
+Difference between 'rusxmms' and 'rusxmms-full' patches is following. The full patche besides makefile.machine fixes all
+other architecure-oss specific make files. The 'rusxmms' patch does not. However, you could execute following commands
+to have all makefiles adjusted.
+
+find . -maxdepth 1 -name "makefile.linux*" -print0 | xargs -0 sed -i -e "s/LOCAL_LIBS=-lpthread/LOCAL_LIBS=-lpthread -lrcc/"
+find . -maxdepth 1 -name "makefile.machine" -print0 | xargs -0 sed -i -e "s/LOCAL_LIBS=-lpthread/LOCAL_LIBS=-lpthread -lrcc/"
+
+
+Configuration
+=============
+ - The patch uses "zip" configuration of RCC. This means settings could be
+ altered using "rcc-gtk2-config zip".
+ - ZIP OEM / Output - encodings are important for patch.
diff --git a/patches/p7zip/p7zip_4.44-ds-rusxmms-full.patch b/patches/p7zip/p7zip_4.44-ds-rusxmms-full.patch
new file mode 100644
index 0000000..98e7855
--- /dev/null
+++ b/patches/p7zip/p7zip_4.44-ds-rusxmms-full.patch
@@ -0,0 +1,314 @@
+diff -dPNur p7zip_4.44-old/C/rccrecode.c p7zip_4.44/C/rccrecode.c
+--- p7zip_4.44-old/C/rccrecode.c 1970-01-01 01:00:00.000000000 +0100
++++ p7zip_4.44/C/rccrecode.c 2007-04-14 19:47:03.000000000 +0200
+@@ -0,0 +1,69 @@
++#include <pthread.h>
++#include <librcc.h>
++
++static rcc_class_default_charset default_oem[] = {
++ { "ru", "IBM866" },
++ { NULL, NULL }
++};
++
++static rcc_class_default_charset default_iso[] = {
++ { "ru", "CP1251" },
++ { NULL, NULL }
++};
++
++#define ARC_CLASS 0
++#define OUT_CLASS 1
++#define ARCOUT_CLASS 0
++static rcc_class classes[] = {
++ { "oem", RCC_CLASS_STANDARD, NULL, default_oem, "OEM Encoding", 0 },
++ { "out", RCC_CLASS_STANDARD, "LC_CTYPE", NULL, "Output", 0 },
++ { NULL }
++};
++
++static int initialized = 0;
++static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
++
++void *rcc_init() {
++ rcc_context ctx;
++
++ pthread_mutex_lock(&mutex);
++ if (!initialized) {
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccInitDb4(NULL, NULL, 0);
++ }
++ initialized++;
++ pthread_mutex_unlock(&mutex);
++
++ ctx = rccCreateContext(NULL, 0, 0, classes, 0);
++ if (ctx) rccInitDb4(ctx, NULL, 0);
++
++ return ctx;
++}
++
++
++void rcc_free(void *ctx) {
++ if (ctx) rccFreeContext((rcc_context)ctx);
++
++ pthread_mutex_lock(&mutex);
++ if (initialized == 1) rccFree();
++ initialized--;
++ pthread_mutex_unlock(&mutex);
++}
++
++
++char *rcc_read(void *ctx, const char *string, size_t size) {
++ if (!initialized) {
++ rcc_init();
++ if (!initialized) return NULL;
++ }
++ return rccSizedRecode((rcc_context)ctx, ARC_CLASS, OUT_CLASS, string, size, NULL);
++}
++
++char *rcc_write(rcc_context ctx, const char *string, size_t size) {
++ if (!initialized) {
++ rcc_init();
++ if (!initialized) return NULL;
++ }
++ return rccSizedRecode((rcc_context)ctx, OUT_CLASS, ARCOUT_CLASS, string, size, NULL);
++}
+diff -dPNur p7zip_4.44-old/C/rccrecode.h p7zip_4.44/C/rccrecode.h
+--- p7zip_4.44-old/C/rccrecode.h 1970-01-01 01:00:00.000000000 +0100
++++ p7zip_4.44/C/rccrecode.h 2007-04-14 19:34:20.000000000 +0200
+@@ -0,0 +1,17 @@
++#ifndef _RCC_RECODE_H
++#define _RCC_RECODE_H
++
++# ifdef __cplusplus
++extern "C" {
++# endif
++
++ void *rcc_init();
++ void rcc_free(void *ctx);
++ char *rcc_read(void *ctx, const char *string, size_t size);
++ char *rcc_write(void *ctx, const char *string, size_t size);
++
++# ifdef __cplusplus
++}
++# endif
++
++#endif /* _RCC_RECODE_H */
+diff -dPNur p7zip_4.44-old/CPP/7zip/Archive/Zip/makefile p7zip_4.44/CPP/7zip/Archive/Zip/makefile
+--- p7zip_4.44-old/CPP/7zip/Archive/Zip/makefile 2007-01-23 21:29:41.000000000 +0100
++++ p7zip_4.44/CPP/7zip/Archive/Zip/makefile 2007-04-14 18:36:44.000000000 +0200
+@@ -13,6 +13,7 @@
+ LIBS=$(LOCAL_LIBS_DLL)
+
+ OBJS = \
++../../../../C/rccrecode.o \
+ ../../../Common/MyWindows.o \
+ ../../../Common/Vector.o\
+ ../../../Common/Alloc.o\
+diff -dPNur p7zip_4.44-old/CPP/7zip/Archive/Zip/ZipIn.cpp p7zip_4.44/CPP/7zip/Archive/Zip/ZipIn.cpp
+--- p7zip_4.44-old/CPP/7zip/Archive/Zip/ZipIn.cpp 2007-01-20 18:06:58.000000000 +0100
++++ p7zip_4.44/CPP/7zip/Archive/Zip/ZipIn.cpp 2007-04-14 19:34:59.000000000 +0200
+@@ -9,11 +9,22 @@
+ #include "../../Common/LimitedStreams.h"
+ #include "../../Common/StreamUtils.h"
+
++#include "../../../../C/rccrecode.h"
++
+ namespace NArchive {
+ namespace NZip {
++
++CInArchive::CInArchive() {
++ rccctx = rcc_init();
++}
++
++CInArchive::~CInArchive() {
++ rcc_free(rccctx);
++}
++
+
+ // static const char kEndOfString = '\0';
+-
++
+ bool CInArchive::Open(IInStream *inStream, const UInt64 *searchHeaderSizeLimit)
+ {
+ m_Stream = inStream;
+@@ -179,10 +190,18 @@
+
+ AString CInArchive::ReadFileName(UInt32 nameSize)
+ {
++ char *rccrec;
+ if (nameSize == 0)
+ return AString();
+ SafeReadBytes(m_NameBuffer.GetBuffer(nameSize), nameSize);
+ m_NameBuffer.ReleaseBuffer(nameSize);
++
++ rccrec = rcc_read(rccctx, (LPCSTR)m_NameBuffer, 0);
++ if (rccrec) {
++ m_NameBuffer = rccrec;
++ free(rccrec);
++ }
++
+ return m_NameBuffer;
+ }
+
+diff -dPNur p7zip_4.44-old/CPP/7zip/Archive/Zip/ZipIn.h p7zip_4.44/CPP/7zip/Archive/Zip/ZipIn.h
+--- p7zip_4.44-old/CPP/7zip/Archive/Zip/ZipIn.h 2007-01-20 18:06:28.000000000 +0100
++++ p7zip_4.44/CPP/7zip/Archive/Zip/ZipIn.h 2007-04-14 19:05:31.000000000 +0200
+@@ -104,6 +104,10 @@
+ bool SeekInArchive(UInt64 position);
+ ISequentialInStream *CreateLimitedStream(UInt64 position, UInt64 size);
+ IInStream* CreateStream();
++
++ void *rccctx;
++ CInArchive();
++ ~CInArchive();
+ };
+
+ }}
+diff -dPNur p7zip_4.44-old/CPP/7zip/Archive/Zip/ZipOut.cpp p7zip_4.44/CPP/7zip/Archive/Zip/ZipOut.cpp
+--- p7zip_4.44-old/CPP/7zip/Archive/Zip/ZipOut.cpp 2007-01-20 18:06:57.000000000 +0100
++++ p7zip_4.44/CPP/7zip/Archive/Zip/ZipOut.cpp 2007-04-14 19:44:44.000000000 +0200
+@@ -8,9 +8,19 @@
+ #include "../../Common/OffsetStream.h"
+ #include "../../Common/StreamUtils.h"
+
++#include "../../../../C/rccrecode.h"
++
+ namespace NArchive {
+ namespace NZip {
+
++COutArchive::COutArchive() {
++ rccctx = rcc_init();
++}
++
++COutArchive::~COutArchive() {
++ rcc_free(rccctx);
++}
++
+ void COutArchive::Create(IOutStream *outStream)
+ {
+ m_Stream = outStream;
+@@ -104,6 +114,8 @@
+
+ HRESULT COutArchive::WriteLocalHeader(const CLocalItem &item)
+ {
++ char *rccrec;
++
+ m_Stream->Seek(m_BasePosition, STREAM_SEEK_SET, NULL);
+
+ bool isZip64 = m_IsZip64 || item.PackSize >= 0xFFFFFFFF || item.UnPackSize >= 0xFFFFFFFF;
+@@ -124,6 +136,12 @@
+ return E_FAIL;
+ }
+ WriteUInt16((UInt16)m_ExtraSize); // test it;
++ rccrec = rcc_write(rccctx, (const char *)item.Name, item.Name.Length());
++ if (rccrec) {
++ printf("%u, %s.\n", item.Name.Length(), rccrec);
++ WriteBytes(rccrec, strlen(rccrec));
++ free(rccrec);
++ } else
+ WriteBytes((const char *)item.Name, item.Name.Length());
+
+ UInt32 extraPos = 0;
+@@ -147,6 +165,8 @@
+
+ void COutArchive::WriteCentralHeader(const CItem &item)
+ {
++ char *rccrec;
++
+ m_Stream->Seek(m_BasePosition, STREAM_SEEK_SET, NULL);
+
+ bool isUnPack64 = item.UnPackSize >= 0xFFFFFFFF;
+@@ -175,6 +195,13 @@
+ WriteUInt16(item.InternalAttributes);
+ WriteUInt32(item.ExternalAttributes);
+ WriteUInt32(isPosition64 ? 0xFFFFFFFF: (UInt32)item.LocalHeaderPosition);
++
++ rccrec = rcc_write(rccctx, (const char *)item.Name, item.Name.Length());
++ if (rccrec) {
++ printf("C: %u, %s.\n", item.Name.Length(), rccrec);
++ WriteBytes(rccrec, strlen(rccrec));
++ free(rccrec);
++ } else
+ WriteBytes((const char *)item.Name, item.Name.Length());
+ if (isZip64)
+ {
+diff -dPNur p7zip_4.44-old/CPP/7zip/Archive/Zip/ZipOut.h p7zip_4.44/CPP/7zip/Archive/Zip/ZipOut.h
+--- p7zip_4.44-old/CPP/7zip/Archive/Zip/ZipOut.h 2007-01-20 18:06:29.000000000 +0100
++++ p7zip_4.44/CPP/7zip/Archive/Zip/ZipOut.h 2007-04-14 19:10:12.000000000 +0200
+@@ -44,6 +44,11 @@
+ void CreateStreamForCompressing(IOutStream **outStream);
+ void CreateStreamForCopying(ISequentialOutStream **outStream);
+ void SeekToPackedDataPosition();
++
++ void *rccctx;
++ COutArchive();
++ ~COutArchive();
++
+ };
+
+ }}
+diff -dPNur p7zip_4.44-old/CPP/7zip/Bundles/Alone/makefile p7zip_4.44/CPP/7zip/Bundles/Alone/makefile
+--- p7zip_4.44-old/CPP/7zip/Bundles/Alone/makefile 2007-01-23 21:29:43.000000000 +0100
++++ p7zip_4.44/CPP/7zip/Bundles/Alone/makefile 2007-04-14 18:52:31.000000000 +0200
+@@ -41,6 +41,7 @@
+ $(MY_HOME)/mySplitCommandLine.o
+
+ OBJS=\
++../../../../C/rccrecode.o \
+ ../../../Common/MyWindows.o \
+ 7zAES.o \
+ 7zCompressionMode.o \
+diff -dPNur p7zip_4.44-old/makefile.linux_amd64 p7zip_4.44/makefile.linux_amd64
+--- p7zip_4.44-old/makefile.linux_amd64 2007-04-14 15:37:39.000000000 +0200
++++ p7zip_4.44/makefile.linux_amd64 2007-04-14 18:50:13.000000000 +0200
+@@ -11,6 +11,6 @@
+ CC=x86_64-pc-linux-gnu-gcc $(ALLFLAGS)
+ LINK_SHARED=-shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+diff -dPNur p7zip_4.44-old/makefile.linux_gcc_2.95_no_need_for_libstdc p7zip_4.44/makefile.linux_gcc_2.95_no_need_for_libstdc
+--- p7zip_4.44-old/makefile.linux_gcc_2.95_no_need_for_libstdc 2007-04-14 15:37:39.000000000 +0200
++++ p7zip_4.44/makefile.linux_gcc_2.95_no_need_for_libstdc 2007-04-14 18:51:02.000000000 +0200
+@@ -10,6 +10,6 @@
+ CC=x86_64-pc-linux-gnu-gcc $(ALLFLAGS)
+ LINK_SHARED=-shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+diff -dPNur p7zip_4.44-old/makefile.linux_x86_ppc_alpha p7zip_4.44/makefile.linux_x86_ppc_alpha
+--- p7zip_4.44-old/makefile.linux_x86_ppc_alpha 2007-04-14 15:37:39.000000000 +0200
++++ p7zip_4.44/makefile.linux_x86_ppc_alpha 2007-04-14 18:51:15.000000000 +0200
+@@ -14,6 +14,6 @@
+ CC=x86_64-pc-linux-gnu-gcc $(ALLFLAGS)
+ LINK_SHARED=-shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+diff -dPNur p7zip_4.44-old/makefile.linux_x86_ppc_alpha__gcc_4.X p7zip_4.44/makefile.linux_x86_ppc_alpha__gcc_4.X
+--- p7zip_4.44-old/makefile.linux_x86_ppc_alpha__gcc_4.X 2007-04-14 15:37:39.000000000 +0200
++++ p7zip_4.44/makefile.linux_x86_ppc_alpha__gcc_4.X 2007-04-14 18:51:19.000000000 +0200
+@@ -14,6 +14,6 @@
+ CC=x86_64-pc-linux-gnu-gcc $(ALLFLAGS)
+ LINK_SHARED=-shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+diff -dPNur p7zip_4.44-old/makefile.machine p7zip_4.44/makefile.machine
+--- p7zip_4.44-old/makefile.machine 2007-04-14 15:37:39.000000000 +0200
++++ p7zip_4.44/makefile.machine 2007-04-14 18:49:24.000000000 +0200
+@@ -11,6 +11,6 @@
+ CC=x86_64-pc-linux-gnu-gcc $(ALLFLAGS)
+ LINK_SHARED=-shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
diff --git a/patches/p7zip/p7zip_4.57-ds-rusxmms-full.patch b/patches/p7zip/p7zip_4.57-ds-rusxmms-full.patch
new file mode 100644
index 0000000..24fa504
--- /dev/null
+++ b/patches/p7zip/p7zip_4.57-ds-rusxmms-full.patch
@@ -0,0 +1,419 @@
+diff -dPNur p7zip_4.57/C/rccrecode.c p7zip_4.57-new/C/rccrecode.c
+--- p7zip_4.57/C/rccrecode.c 1970-01-01 01:00:00.000000000 +0100
++++ p7zip_4.57-new/C/rccrecode.c 2008-04-11 22:02:20.000000000 +0200
+@@ -0,0 +1,71 @@
++#include <pthread.h>
++#include <librcc.h>
++
++static rcc_class_default_charset default_oem[] = {
++ { "ru", "IBM866" },
++ { NULL, NULL }
++};
++
++static rcc_class_default_charset default_iso[] = {
++ { "ru", "CP1251" },
++ { NULL, NULL }
++};
++
++#define ARC_CLASS 0
++#define OUT_CLASS 1
++#define ARCOUT_CLASS 0
++static rcc_class classes[] = {
++ { "oem", RCC_CLASS_STANDARD, NULL, default_oem, "OEM Encoding", 0 },
++ { "out", RCC_CLASS_STANDARD, "LC_CTYPE", NULL, "Output", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int initialized = 0;
++static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
++
++void *rcc_init() {
++ rcc_context ctx;
++
++ pthread_mutex_lock(&mutex);
++ if (!initialized) {
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "zip");
++ rccInitDb4(NULL, NULL, 0);
++ }
++ initialized++;
++ pthread_mutex_unlock(&mutex);
++
++ ctx = rccCreateContext(NULL, 0, 0, classes, 0);
++ if (ctx) rccInitDb4(ctx, NULL, 0);
++ if (ctx) rccLoad(ctx, "zip");
++
++ return ctx;
++}
++
++
++void rcc_free(void *ctx) {
++ if (ctx) rccFreeContext((rcc_context)ctx);
++
++ pthread_mutex_lock(&mutex);
++ if (initialized == 1) rccFree();
++ initialized--;
++ pthread_mutex_unlock(&mutex);
++}
++
++
++char *rcc_read(void *ctx, const char *string, size_t size) {
++ if (!initialized) {
++ rcc_init();
++ if (!initialized) return NULL;
++ }
++ return rccSizedRecode((rcc_context)ctx, ARC_CLASS, OUT_CLASS, string, size, NULL);
++}
++
++char *rcc_write(rcc_context ctx, const char *string, size_t size) {
++ if (!initialized) {
++ rcc_init();
++ if (!initialized) return NULL;
++ }
++ return rccSizedRecode((rcc_context)ctx, OUT_CLASS, ARCOUT_CLASS, string, size, NULL);
++}
+diff -dPNur p7zip_4.57/C/rccrecode.h p7zip_4.57-new/C/rccrecode.h
+--- p7zip_4.57/C/rccrecode.h 1970-01-01 01:00:00.000000000 +0100
++++ p7zip_4.57-new/C/rccrecode.h 2008-04-11 22:02:20.000000000 +0200
+@@ -0,0 +1,17 @@
++#ifndef _RCC_RECODE_H
++#define _RCC_RECODE_H
++
++# ifdef __cplusplus
++extern "C" {
++# endif
++
++ void *rcc_init();
++ void rcc_free(void *ctx);
++ char *rcc_read(void *ctx, const char *string, size_t size);
++ char *rcc_write(void *ctx, const char *string, size_t size);
++
++# ifdef __cplusplus
++}
++# endif
++
++#endif /* _RCC_RECODE_H */
+diff -dPNur p7zip_4.57/CPP/7zip/Archive/Zip/ZipIn.cpp p7zip_4.57-new/CPP/7zip/Archive/Zip/ZipIn.cpp
+--- p7zip_4.57/CPP/7zip/Archive/Zip/ZipIn.cpp 2007-12-08 11:19:00.000000000 +0100
++++ p7zip_4.57-new/CPP/7zip/Archive/Zip/ZipIn.cpp 2008-04-11 22:02:20.000000000 +0200
+@@ -9,11 +9,22 @@
+ #include "../../Common/LimitedStreams.h"
+ #include "../../Common/StreamUtils.h"
+
++#include "../../../../C/rccrecode.h"
++
+ namespace NArchive {
+ namespace NZip {
++
++CInArchive::CInArchive() {
++ rccctx = rcc_init();
++}
++
++CInArchive::~CInArchive() {
++ rcc_free(rccctx);
++}
++
+
+ // static const char kEndOfString = '\0';
+-
++
+ bool CInArchive::Open(IInStream *inStream, const UInt64 *searchHeaderSizeLimit)
+ {
+ m_Stream = inStream;
+@@ -196,10 +207,18 @@
+
+ AString CInArchive::ReadFileName(UInt32 nameSize)
+ {
++ char *rccrec;
+ if (nameSize == 0)
+ return AString();
+ SafeReadBytes(m_NameBuffer.GetBuffer(nameSize), nameSize);
+ m_NameBuffer.ReleaseBuffer(nameSize);
++
++ rccrec = rcc_read(rccctx, (LPCSTR)m_NameBuffer, 0);
++ if (rccrec) {
++ m_NameBuffer = rccrec;
++ free(rccrec);
++ }
++
+ return m_NameBuffer;
+ }
+
+diff -dPNur p7zip_4.57/CPP/7zip/Archive/Zip/ZipIn.h p7zip_4.57-new/CPP/7zip/Archive/Zip/ZipIn.h
+--- p7zip_4.57/CPP/7zip/Archive/Zip/ZipIn.h 2007-12-08 11:19:00.000000000 +0100
++++ p7zip_4.57-new/CPP/7zip/Archive/Zip/ZipIn.h 2008-04-11 22:02:20.000000000 +0200
+@@ -106,6 +106,10 @@
+ bool SeekInArchive(UInt64 position);
+ ISequentialInStream *CreateLimitedStream(UInt64 position, UInt64 size);
+ IInStream* CreateStream();
++
++ void *rccctx;
++ CInArchive();
++ ~CInArchive();
+ };
+
+ }}
+diff -dPNur p7zip_4.57/CPP/7zip/Archive/Zip/ZipOut.cpp p7zip_4.57-new/CPP/7zip/Archive/Zip/ZipOut.cpp
+--- p7zip_4.57/CPP/7zip/Archive/Zip/ZipOut.cpp 2007-06-26 20:06:23.000000000 +0200
++++ p7zip_4.57-new/CPP/7zip/Archive/Zip/ZipOut.cpp 2008-04-11 22:02:20.000000000 +0200
+@@ -7,9 +7,19 @@
+ #include "../../Common/OffsetStream.h"
+ #include "../../Common/StreamUtils.h"
+
++#include "../../../../C/rccrecode.h"
++
+ namespace NArchive {
+ namespace NZip {
+
++COutArchive::COutArchive() {
++ rccctx = rcc_init();
++}
++
++COutArchive::~COutArchive() {
++ rcc_free(rccctx);
++}
++
+ void COutArchive::Create(IOutStream *outStream)
+ {
+ if (!m_OutBuffer.Create(1 << 16))
+@@ -112,6 +122,7 @@
+ {
+ SeekTo(m_BasePosition);
+
++ char *rccrec;
+ bool isZip64 = m_IsZip64 || item.PackSize >= 0xFFFFFFFF || item.UnPackSize >= 0xFFFFFFFF;
+
+ WriteUInt32(NSignature::kLocalFileHeader);
+@@ -130,6 +141,12 @@
+ throw CSystemException(E_FAIL);
+ }
+ WriteUInt16((UInt16)m_ExtraSize); // test it;
++ rccrec = rcc_write(rccctx, (const char *)item.Name, item.Name.Length());
++ if (rccrec) {
++ printf("%u, %s.\n", item.Name.Length(), rccrec);
++ WriteBytes(rccrec, strlen(rccrec));
++ free(rccrec);
++ } else
+ WriteBytes((const char *)item.Name, item.Name.Length());
+
+ UInt32 extraPos = 0;
+@@ -154,6 +171,7 @@
+
+ void COutArchive::WriteCentralHeader(const CItem &item)
+ {
++ char *rccrec;
+ bool isUnPack64 = item.UnPackSize >= 0xFFFFFFFF;
+ bool isPack64 = item.PackSize >= 0xFFFFFFFF;
+ bool isPosition64 = item.LocalHeaderPosition >= 0xFFFFFFFF;
+@@ -180,6 +198,13 @@
+ WriteUInt16(item.InternalAttributes);
+ WriteUInt32(item.ExternalAttributes);
+ WriteUInt32(isPosition64 ? 0xFFFFFFFF: (UInt32)item.LocalHeaderPosition);
++
++ rccrec = rcc_write(rccctx, (const char *)item.Name, item.Name.Length());
++ if (rccrec) {
++ printf("C: %u, %s.\n", item.Name.Length(), rccrec);
++ WriteBytes(rccrec, strlen(rccrec));
++ free(rccrec);
++ } else
+ WriteBytes((const char *)item.Name, item.Name.Length());
+ if (isZip64)
+ {
+diff -dPNur p7zip_4.57/CPP/7zip/Archive/Zip/ZipOut.h p7zip_4.57-new/CPP/7zip/Archive/Zip/ZipOut.h
+--- p7zip_4.57/CPP/7zip/Archive/Zip/ZipOut.h 2007-06-26 20:06:22.000000000 +0200
++++ p7zip_4.57-new/CPP/7zip/Archive/Zip/ZipOut.h 2008-04-11 22:02:20.000000000 +0200
+@@ -49,6 +49,11 @@
+ void CreateStreamForCompressing(IOutStream **outStream);
+ void CreateStreamForCopying(ISequentialOutStream **outStream);
+ void SeekToPackedDataPosition();
++
++ void *rccctx;
++ COutArchive();
++ ~COutArchive();
++
+ };
+
+ }}
+diff -dPNur p7zip_4.57/CPP/7zip/Bundles/Alone/makefile p7zip_4.57-new/CPP/7zip/Bundles/Alone/makefile
+--- p7zip_4.57/CPP/7zip/Bundles/Alone/makefile 2007-07-24 20:55:33.000000000 +0200
++++ p7zip_4.57-new/CPP/7zip/Bundles/Alone/makefile 2008-04-11 22:02:20.000000000 +0200
+@@ -15,6 +15,7 @@
+ LIBS=$(LOCAL_LIBS)
+
+ OBJS=\
++rccrecode.o \
+ myGetTickCount.o \
+ wine_date_and_time.o \
+ myAddExeFlag.o \
+diff -dPNur p7zip_4.57/CPP/7zip/Bundles/Alone/makefile.list p7zip_4.57-new/CPP/7zip/Bundles/Alone/makefile.list
+--- p7zip_4.57/CPP/7zip/Bundles/Alone/makefile.list 2007-07-24 20:56:10.000000000 +0200
++++ p7zip_4.57-new/CPP/7zip/Bundles/Alone/makefile.list 2008-04-11 22:02:20.000000000 +0200
+@@ -196,6 +196,7 @@
+ ../../../../C/Compress/Branch/BranchPPC.c \
+ ../../../../C/Compress/Branch/BranchSPARC.c \
+ ../../../../C/Compress/Branch/BranchX86.c \
++ ../../../../C/rccrecode.c \
+ ../../../../C/7zCrc.c \
+ ../../../../C/Sort.c \
+ ../../../../C/Threads.c \
+@@ -237,6 +238,8 @@
+ $(CXX) $(CFLAGS) ../../../Common/IntToString.cpp
+ ListFileUtils.o : ../../../Common/ListFileUtils.cpp
+ $(CXX) $(CFLAGS) ../../../Common/ListFileUtils.cpp
++rccrecode.o : ../../../../C/rccrecode.c
++ $(CC) $(CFLAGS) ../../../../C/rccrecode.c
+ MyWindows.o : ../../../Common/MyWindows.cpp
+ $(CXX) $(CFLAGS) ../../../Common/MyWindows.cpp
+ Random.o : ../../../Common/Random.cpp
+diff -dPNur p7zip_4.57/CPP/7zip/Bundles/Format7zFree/makefile p7zip_4.57-new/CPP/7zip/Bundles/Format7zFree/makefile
+--- p7zip_4.57/CPP/7zip/Bundles/Format7zFree/makefile 2007-08-03 20:16:04.000000000 +0200
++++ p7zip_4.57-new/CPP/7zip/Bundles/Format7zFree/makefile 2008-04-11 22:02:20.000000000 +0200
+@@ -14,6 +14,7 @@
+ LIBS=$(LOCAL_LIBS_DLL)
+
+ OBJS = \
++rccrecode.o \
+ wine_date_and_time.o \
+ myGetTickCount.o \
+ CRC.o \
+diff -dPNur p7zip_4.57/CPP/7zip/Bundles/Format7zFree/makefile.list p7zip_4.57-new/CPP/7zip/Bundles/Format7zFree/makefile.list
+--- p7zip_4.57/CPP/7zip/Bundles/Format7zFree/makefile.list 2007-08-03 20:17:57.000000000 +0200
++++ p7zip_4.57-new/CPP/7zip/Bundles/Format7zFree/makefile.list 2008-04-11 22:02:20.000000000 +0200
+@@ -218,6 +218,7 @@
+ ../../../../C/Compress/Branch/BranchPPC.c \
+ ../../../../C/Compress/Branch/BranchSPARC.c \
+ ../../../../C/Compress/Branch/BranchX86.c \
++ ../../../../C/rccrecode.c \
+ ../../../../C/7zCrc.c \
+ ../../../../C/Sort.c \
+ ../../../../C/Threads.c \
+@@ -237,6 +238,8 @@
+ $(CXX) $(CC_SHARED) $(CFLAGS) ../../../Common/ListFileUtils.cpp
+ MyWindows.o : ../../../Common/MyWindows.cpp
+ $(CXX) $(CC_SHARED) $(CFLAGS) ../../../Common/MyWindows.cpp
++rccrecode.o : ../../../../C/rccrecode.c
++ $(CC) $(CC_SHARED) $(CFLAGS) ../../../../C/rccrecode.c
+ Random.o : ../../../Common/Random.cpp
+ $(CXX) $(CC_SHARED) $(CFLAGS) ../../../Common/Random.cpp
+ StdInStream.o : ../../../Common/StdInStream.cpp
+diff -dPNur p7zip_4.57/makefile.linux_amd64 p7zip_4.57-new/makefile.linux_amd64
+--- p7zip_4.57/makefile.linux_amd64 2007-06-28 09:32:20.000000000 +0200
++++ p7zip_4.57-new/makefile.linux_amd64 2008-04-11 22:02:45.000000000 +0200
+@@ -12,7 +12,7 @@
+ CC_SHARED=-fPIC
+ LINK_SHARED=-fPIC -shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ OBJ_CRC32=$(OBJ_CRC32_C)
+diff -dPNur p7zip_4.57/makefile.linux_amd64_asm p7zip_4.57-new/makefile.linux_amd64_asm
+--- p7zip_4.57/makefile.linux_amd64_asm 2007-06-28 09:32:30.000000000 +0200
++++ p7zip_4.57-new/makefile.linux_amd64_asm 2008-04-11 22:02:45.000000000 +0200
+@@ -13,7 +13,7 @@
+ LINK_SHARED=-fPIC -shared
+ ASM=yasm -f elf -m amd64
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ CPU=x64
+diff -dPNur p7zip_4.57/makefile.linux_gcc_2.95_no_need_for_libstdc p7zip_4.57-new/makefile.linux_gcc_2.95_no_need_for_libstdc
+--- p7zip_4.57/makefile.linux_gcc_2.95_no_need_for_libstdc 2007-06-24 20:49:31.000000000 +0200
++++ p7zip_4.57-new/makefile.linux_gcc_2.95_no_need_for_libstdc 2008-04-11 22:02:45.000000000 +0200
+@@ -11,7 +11,7 @@
+ LINK_SHARED=-shared
+ ASM=nasm -f elf
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ CPU=x86
+diff -dPNur p7zip_4.57/makefile.linux_s390x p7zip_4.57-new/makefile.linux_s390x
+--- p7zip_4.57/makefile.linux_s390x 2007-08-07 18:36:01.000000000 +0200
++++ p7zip_4.57-new/makefile.linux_s390x 2008-04-11 22:02:45.000000000 +0200
+@@ -15,7 +15,7 @@
+ CC_SHARED=-fPIC
+ LINK_SHARED=-fPIC -shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ OBJ_CRC32=$(OBJ_CRC32_C)
+diff -dPNur p7zip_4.57/makefile.linux_x86_asm_gcc_4.X p7zip_4.57-new/makefile.linux_x86_asm_gcc_4.X
+--- p7zip_4.57/makefile.linux_x86_asm_gcc_4.X 2007-08-08 11:15:07.000000000 +0200
++++ p7zip_4.57-new/makefile.linux_x86_asm_gcc_4.X 2008-04-11 22:02:45.000000000 +0200
+@@ -15,7 +15,7 @@
+ LINK_SHARED=-fPIC -shared
+ ASM=nasm -f elf
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ CPU=x86
+diff -dPNur p7zip_4.57/makefile.linux_x86_asm_gcc_4.X_fltk p7zip_4.57-new/makefile.linux_x86_asm_gcc_4.X_fltk
+--- p7zip_4.57/makefile.linux_x86_asm_gcc_4.X_fltk 2007-08-08 11:15:10.000000000 +0200
++++ p7zip_4.57-new/makefile.linux_x86_asm_gcc_4.X_fltk 2008-04-11 22:02:45.000000000 +0200
+@@ -15,7 +15,7 @@
+ LINK_SHARED=-fPIC -shared
+ ASM=nasm -f elf
+
+-LOCAL_LIBS=-lpthread `fltk-config --ldflags`
++LOCAL_LIBS=-lpthread -lrcc `fltk-config --ldflags`
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ CPU=x86
+diff -dPNur p7zip_4.57/makefile.linux_x86_asm_icc p7zip_4.57-new/makefile.linux_x86_asm_icc
+--- p7zip_4.57/makefile.linux_x86_asm_icc 2007-08-08 11:15:15.000000000 +0200
++++ p7zip_4.57-new/makefile.linux_x86_asm_icc 2008-04-11 22:02:45.000000000 +0200
+@@ -16,7 +16,7 @@
+ LINK_SHARED=-fPIC -shared
+ ASM=nasm -f elf
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ CPU=x86
+diff -dPNur p7zip_4.57/makefile.linux_x86_ppc_alpha p7zip_4.57-new/makefile.linux_x86_ppc_alpha
+--- p7zip_4.57/makefile.linux_x86_ppc_alpha 2007-08-08 11:15:18.000000000 +0200
++++ p7zip_4.57-new/makefile.linux_x86_ppc_alpha 2008-04-11 22:02:45.000000000 +0200
+@@ -14,7 +14,7 @@
+ CC_SHARED=-fPIC
+ LINK_SHARED=-fPIC -shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ OBJ_CRC32=$(OBJ_CRC32_C)
+diff -dPNur p7zip_4.57/makefile.linux_x86_ppc_alpha_gcc_4.X p7zip_4.57-new/makefile.linux_x86_ppc_alpha_gcc_4.X
+--- p7zip_4.57/makefile.linux_x86_ppc_alpha_gcc_4.X 2007-08-08 11:15:20.000000000 +0200
++++ p7zip_4.57-new/makefile.linux_x86_ppc_alpha_gcc_4.X 2008-04-11 22:02:45.000000000 +0200
+@@ -14,7 +14,7 @@
+ CC_SHARED=-fPIC
+ LINK_SHARED=-fPIC -shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ OBJ_CRC32=$(OBJ_CRC32_C)
+diff -dPNur p7zip_4.57/makefile.machine p7zip_4.57-new/makefile.machine
+--- p7zip_4.57/makefile.machine 2007-12-15 18:19:48.000000000 +0100
++++ p7zip_4.57-new/makefile.machine 2008-04-11 22:02:20.000000000 +0200
+@@ -14,7 +14,7 @@
+ CC_SHARED=-fPIC
+ LINK_SHARED=-fPIC -shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ OBJ_CRC32=$(OBJ_CRC32_C)
diff --git a/patches/p7zip/p7zip_4.57-ds-rusxmms.patch b/patches/p7zip/p7zip_4.57-ds-rusxmms.patch
new file mode 100644
index 0000000..0913c13
--- /dev/null
+++ b/patches/p7zip/p7zip_4.57-ds-rusxmms.patch
@@ -0,0 +1,336 @@
+diff -dPNur p7zip_4.57/C/rccrecode.c p7zip_4.57-new/C/rccrecode.c
+--- p7zip_4.57/C/rccrecode.c 1970-01-01 01:00:00.000000000 +0100
++++ p7zip_4.57-new/C/rccrecode.c 2008-04-11 20:23:42.000000000 +0200
+@@ -0,0 +1,71 @@
++#include <pthread.h>
++#include <librcc.h>
++
++static rcc_class_default_charset default_oem[] = {
++ { "ru", "IBM866" },
++ { NULL, NULL }
++};
++
++static rcc_class_default_charset default_iso[] = {
++ { "ru", "CP1251" },
++ { NULL, NULL }
++};
++
++#define ARC_CLASS 0
++#define OUT_CLASS 1
++#define ARCOUT_CLASS 0
++static rcc_class classes[] = {
++ { "oem", RCC_CLASS_STANDARD, NULL, default_oem, "OEM Encoding", 0 },
++ { "out", RCC_CLASS_STANDARD, "LC_CTYPE", NULL, "Output", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int initialized = 0;
++static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
++
++void *rcc_init() {
++ rcc_context ctx;
++
++ pthread_mutex_lock(&mutex);
++ if (!initialized) {
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "zip");
++ rccInitDb4(NULL, NULL, 0);
++ }
++ initialized++;
++ pthread_mutex_unlock(&mutex);
++
++ ctx = rccCreateContext(NULL, 0, 0, classes, 0);
++ if (ctx) rccInitDb4(ctx, NULL, 0);
++ if (ctx) rccLoad(ctx, "zip");
++
++ return ctx;
++}
++
++
++void rcc_free(void *ctx) {
++ if (ctx) rccFreeContext((rcc_context)ctx);
++
++ pthread_mutex_lock(&mutex);
++ if (initialized == 1) rccFree();
++ initialized--;
++ pthread_mutex_unlock(&mutex);
++}
++
++
++char *rcc_read(void *ctx, const char *string, size_t size) {
++ if (!initialized) {
++ rcc_init();
++ if (!initialized) return NULL;
++ }
++ return rccSizedRecode((rcc_context)ctx, ARC_CLASS, OUT_CLASS, string, size, NULL);
++}
++
++char *rcc_write(rcc_context ctx, const char *string, size_t size) {
++ if (!initialized) {
++ rcc_init();
++ if (!initialized) return NULL;
++ }
++ return rccSizedRecode((rcc_context)ctx, OUT_CLASS, ARCOUT_CLASS, string, size, NULL);
++}
+diff -dPNur p7zip_4.57/C/rccrecode.h p7zip_4.57-new/C/rccrecode.h
+--- p7zip_4.57/C/rccrecode.h 1970-01-01 01:00:00.000000000 +0100
++++ p7zip_4.57-new/C/rccrecode.h 2008-04-11 20:23:42.000000000 +0200
+@@ -0,0 +1,17 @@
++#ifndef _RCC_RECODE_H
++#define _RCC_RECODE_H
++
++# ifdef __cplusplus
++extern "C" {
++# endif
++
++ void *rcc_init();
++ void rcc_free(void *ctx);
++ char *rcc_read(void *ctx, const char *string, size_t size);
++ char *rcc_write(void *ctx, const char *string, size_t size);
++
++# ifdef __cplusplus
++}
++# endif
++
++#endif /* _RCC_RECODE_H */
+diff -dPNur p7zip_4.57/CPP/7zip/Archive/Zip/ZipIn.cpp p7zip_4.57-new/CPP/7zip/Archive/Zip/ZipIn.cpp
+--- p7zip_4.57/CPP/7zip/Archive/Zip/ZipIn.cpp 2007-12-08 11:19:00.000000000 +0100
++++ p7zip_4.57-new/CPP/7zip/Archive/Zip/ZipIn.cpp 2008-04-11 20:23:42.000000000 +0200
+@@ -9,11 +9,22 @@
+ #include "../../Common/LimitedStreams.h"
+ #include "../../Common/StreamUtils.h"
+
++#include "../../../../C/rccrecode.h"
++
+ namespace NArchive {
+ namespace NZip {
++
++CInArchive::CInArchive() {
++ rccctx = rcc_init();
++}
++
++CInArchive::~CInArchive() {
++ rcc_free(rccctx);
++}
++
+
+ // static const char kEndOfString = '\0';
+-
++
+ bool CInArchive::Open(IInStream *inStream, const UInt64 *searchHeaderSizeLimit)
+ {
+ m_Stream = inStream;
+@@ -196,10 +207,18 @@
+
+ AString CInArchive::ReadFileName(UInt32 nameSize)
+ {
++ char *rccrec;
+ if (nameSize == 0)
+ return AString();
+ SafeReadBytes(m_NameBuffer.GetBuffer(nameSize), nameSize);
+ m_NameBuffer.ReleaseBuffer(nameSize);
++
++ rccrec = rcc_read(rccctx, (LPCSTR)m_NameBuffer, 0);
++ if (rccrec) {
++ m_NameBuffer = rccrec;
++ free(rccrec);
++ }
++
+ return m_NameBuffer;
+ }
+
+diff -dPNur p7zip_4.57/CPP/7zip/Archive/Zip/ZipIn.h p7zip_4.57-new/CPP/7zip/Archive/Zip/ZipIn.h
+--- p7zip_4.57/CPP/7zip/Archive/Zip/ZipIn.h 2007-12-08 11:19:00.000000000 +0100
++++ p7zip_4.57-new/CPP/7zip/Archive/Zip/ZipIn.h 2008-04-11 20:23:42.000000000 +0200
+@@ -106,6 +106,10 @@
+ bool SeekInArchive(UInt64 position);
+ ISequentialInStream *CreateLimitedStream(UInt64 position, UInt64 size);
+ IInStream* CreateStream();
++
++ void *rccctx;
++ CInArchive();
++ ~CInArchive();
+ };
+
+ }}
+diff -dPNur p7zip_4.57/CPP/7zip/Archive/Zip/ZipOut.cpp p7zip_4.57-new/CPP/7zip/Archive/Zip/ZipOut.cpp
+--- p7zip_4.57/CPP/7zip/Archive/Zip/ZipOut.cpp 2007-06-26 20:06:23.000000000 +0200
++++ p7zip_4.57-new/CPP/7zip/Archive/Zip/ZipOut.cpp 2008-04-11 20:23:42.000000000 +0200
+@@ -7,9 +7,19 @@
+ #include "../../Common/OffsetStream.h"
+ #include "../../Common/StreamUtils.h"
+
++#include "../../../../C/rccrecode.h"
++
+ namespace NArchive {
+ namespace NZip {
+
++COutArchive::COutArchive() {
++ rccctx = rcc_init();
++}
++
++COutArchive::~COutArchive() {
++ rcc_free(rccctx);
++}
++
+ void COutArchive::Create(IOutStream *outStream)
+ {
+ if (!m_OutBuffer.Create(1 << 16))
+@@ -112,6 +122,7 @@
+ {
+ SeekTo(m_BasePosition);
+
++ char *rccrec;
+ bool isZip64 = m_IsZip64 || item.PackSize >= 0xFFFFFFFF || item.UnPackSize >= 0xFFFFFFFF;
+
+ WriteUInt32(NSignature::kLocalFileHeader);
+@@ -130,6 +141,12 @@
+ throw CSystemException(E_FAIL);
+ }
+ WriteUInt16((UInt16)m_ExtraSize); // test it;
++ rccrec = rcc_write(rccctx, (const char *)item.Name, item.Name.Length());
++ if (rccrec) {
++ printf("%u, %s.\n", item.Name.Length(), rccrec);
++ WriteBytes(rccrec, strlen(rccrec));
++ free(rccrec);
++ } else
+ WriteBytes((const char *)item.Name, item.Name.Length());
+
+ UInt32 extraPos = 0;
+@@ -154,6 +171,7 @@
+
+ void COutArchive::WriteCentralHeader(const CItem &item)
+ {
++ char *rccrec;
+ bool isUnPack64 = item.UnPackSize >= 0xFFFFFFFF;
+ bool isPack64 = item.PackSize >= 0xFFFFFFFF;
+ bool isPosition64 = item.LocalHeaderPosition >= 0xFFFFFFFF;
+@@ -180,6 +198,13 @@
+ WriteUInt16(item.InternalAttributes);
+ WriteUInt32(item.ExternalAttributes);
+ WriteUInt32(isPosition64 ? 0xFFFFFFFF: (UInt32)item.LocalHeaderPosition);
++
++ rccrec = rcc_write(rccctx, (const char *)item.Name, item.Name.Length());
++ if (rccrec) {
++ printf("C: %u, %s.\n", item.Name.Length(), rccrec);
++ WriteBytes(rccrec, strlen(rccrec));
++ free(rccrec);
++ } else
+ WriteBytes((const char *)item.Name, item.Name.Length());
+ if (isZip64)
+ {
+diff -dPNur p7zip_4.57/CPP/7zip/Archive/Zip/ZipOut.h p7zip_4.57-new/CPP/7zip/Archive/Zip/ZipOut.h
+--- p7zip_4.57/CPP/7zip/Archive/Zip/ZipOut.h 2007-06-26 20:06:22.000000000 +0200
++++ p7zip_4.57-new/CPP/7zip/Archive/Zip/ZipOut.h 2008-04-11 20:23:42.000000000 +0200
+@@ -49,6 +49,11 @@
+ void CreateStreamForCompressing(IOutStream **outStream);
+ void CreateStreamForCopying(ISequentialOutStream **outStream);
+ void SeekToPackedDataPosition();
++
++ void *rccctx;
++ COutArchive();
++ ~COutArchive();
++
+ };
+
+ }}
+diff -dPNur p7zip_4.57/CPP/7zip/Bundles/Alone/makefile p7zip_4.57-new/CPP/7zip/Bundles/Alone/makefile
+--- p7zip_4.57/CPP/7zip/Bundles/Alone/makefile 2007-07-24 20:55:33.000000000 +0200
++++ p7zip_4.57-new/CPP/7zip/Bundles/Alone/makefile 2008-04-11 20:23:42.000000000 +0200
+@@ -15,6 +15,7 @@
+ LIBS=$(LOCAL_LIBS)
+
+ OBJS=\
++rccrecode.o \
+ myGetTickCount.o \
+ wine_date_and_time.o \
+ myAddExeFlag.o \
+diff -dPNur p7zip_4.57/CPP/7zip/Bundles/Alone/makefile.list p7zip_4.57-new/CPP/7zip/Bundles/Alone/makefile.list
+--- p7zip_4.57/CPP/7zip/Bundles/Alone/makefile.list 2007-07-24 20:56:10.000000000 +0200
++++ p7zip_4.57-new/CPP/7zip/Bundles/Alone/makefile.list 2008-04-11 20:23:42.000000000 +0200
+@@ -196,6 +196,7 @@
+ ../../../../C/Compress/Branch/BranchPPC.c \
+ ../../../../C/Compress/Branch/BranchSPARC.c \
+ ../../../../C/Compress/Branch/BranchX86.c \
++ ../../../../C/rccrecode.c \
+ ../../../../C/7zCrc.c \
+ ../../../../C/Sort.c \
+ ../../../../C/Threads.c \
+@@ -237,6 +238,8 @@
+ $(CXX) $(CFLAGS) ../../../Common/IntToString.cpp
+ ListFileUtils.o : ../../../Common/ListFileUtils.cpp
+ $(CXX) $(CFLAGS) ../../../Common/ListFileUtils.cpp
++rccrecode.o : ../../../../C/rccrecode.c
++ $(CC) $(CFLAGS) ../../../../C/rccrecode.c
+ MyWindows.o : ../../../Common/MyWindows.cpp
+ $(CXX) $(CFLAGS) ../../../Common/MyWindows.cpp
+ Random.o : ../../../Common/Random.cpp
+diff -dPNur p7zip_4.57/CPP/7zip/Bundles/Format7zFree/makefile p7zip_4.57-new/CPP/7zip/Bundles/Format7zFree/makefile
+--- p7zip_4.57/CPP/7zip/Bundles/Format7zFree/makefile 2007-08-03 20:16:04.000000000 +0200
++++ p7zip_4.57-new/CPP/7zip/Bundles/Format7zFree/makefile 2008-04-11 20:30:53.000000000 +0200
+@@ -14,6 +14,7 @@
+ LIBS=$(LOCAL_LIBS_DLL)
+
+ OBJS = \
++rccrecode.o \
+ wine_date_and_time.o \
+ myGetTickCount.o \
+ CRC.o \
+diff -dPNur p7zip_4.57/CPP/7zip/Bundles/Format7zFree/makefile.list p7zip_4.57-new/CPP/7zip/Bundles/Format7zFree/makefile.list
+--- p7zip_4.57/CPP/7zip/Bundles/Format7zFree/makefile.list 2007-08-03 20:17:57.000000000 +0200
++++ p7zip_4.57-new/CPP/7zip/Bundles/Format7zFree/makefile.list 2008-04-11 20:32:11.000000000 +0200
+@@ -218,6 +218,7 @@
+ ../../../../C/Compress/Branch/BranchPPC.c \
+ ../../../../C/Compress/Branch/BranchSPARC.c \
+ ../../../../C/Compress/Branch/BranchX86.c \
++ ../../../../C/rccrecode.c \
+ ../../../../C/7zCrc.c \
+ ../../../../C/Sort.c \
+ ../../../../C/Threads.c \
+@@ -237,6 +238,8 @@
+ $(CXX) $(CC_SHARED) $(CFLAGS) ../../../Common/ListFileUtils.cpp
+ MyWindows.o : ../../../Common/MyWindows.cpp
+ $(CXX) $(CC_SHARED) $(CFLAGS) ../../../Common/MyWindows.cpp
++rccrecode.o : ../../../../C/rccrecode.c
++ $(CC) $(CC_SHARED) $(CFLAGS) ../../../../C/rccrecode.c
+ Random.o : ../../../Common/Random.cpp
+ $(CXX) $(CC_SHARED) $(CFLAGS) ../../../Common/Random.cpp
+ StdInStream.o : ../../../Common/StdInStream.cpp
+diff -dPNur p7zip_4.57/makefile.machine p7zip_4.57-new/makefile.machine
+--- p7zip_4.57/makefile.machine 2007-12-15 18:19:48.000000000 +0100
++++ p7zip_4.57-new/makefile.machine 2008-04-11 20:23:42.000000000 +0200
+@@ -14,7 +14,7 @@
+ CC_SHARED=-fPIC
+ LINK_SHARED=-fPIC -shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ OBJ_CRC32=$(OBJ_CRC32_C)
+diff -dPNur p7zip_4.57/makefile.machine.orig p7zip_4.57-new/makefile.machine.orig
+--- p7zip_4.57/makefile.machine.orig 1970-01-01 01:00:00.000000000 +0100
++++ p7zip_4.57-new/makefile.machine.orig 2007-12-15 18:19:48.000000000 +0100
+@@ -0,0 +1,21 @@
++#
++# makefile for Linux (x86, PPC, alpha ...)
++#
++
++OPTFLAGS=-O
++
++ALLFLAGS=${OPTFLAGS} -s \
++ -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE \
++ -DNDEBUG -D_REENTRANT -DENV_UNIX \
++ $(LOCAL_FLAGS)
++
++CXX=g++ $(ALLFLAGS)
++CC=gcc $(ALLFLAGS)
++CC_SHARED=-fPIC
++LINK_SHARED=-fPIC -shared
++
++LOCAL_LIBS=-lpthread
++LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
++
++OBJ_CRC32=$(OBJ_CRC32_C)
++
diff --git a/patches/p7zip/p7zip_4.65-ds-rusxmms-full.patch b/patches/p7zip/p7zip_4.65-ds-rusxmms-full.patch
new file mode 100644
index 0000000..a7ab04e
--- /dev/null
+++ b/patches/p7zip/p7zip_4.65-ds-rusxmms-full.patch
@@ -0,0 +1,425 @@
+diff -dPNur p7zip_4.65/C/rccrecode.c p7zip_4.65-new/C/rccrecode.c
+--- p7zip_4.65/C/rccrecode.c 1970-01-01 01:00:00.000000000 +0100
++++ p7zip_4.65-new/C/rccrecode.c 2009-06-14 02:44:19.000000000 +0200
+@@ -0,0 +1,71 @@
++#include <pthread.h>
++#include <librcc.h>
++
++static rcc_class_default_charset default_oem[] = {
++ { "ru", "IBM866" },
++ { NULL, NULL }
++};
++
++static rcc_class_default_charset default_iso[] = {
++ { "ru", "CP1251" },
++ { NULL, NULL }
++};
++
++#define ARC_CLASS 0
++#define OUT_CLASS 1
++#define ARCOUT_CLASS 0
++static rcc_class classes[] = {
++ { "oem", RCC_CLASS_STANDARD, NULL, default_oem, "OEM Encoding", 0 },
++ { "out", RCC_CLASS_STANDARD, "LC_CTYPE", NULL, "Output", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int initialized = 0;
++static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
++
++void *rcc_init() {
++ rcc_context ctx;
++
++ pthread_mutex_lock(&mutex);
++ if (!initialized) {
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "zip");
++ rccInitDb4(NULL, NULL, 0);
++ }
++ initialized++;
++ pthread_mutex_unlock(&mutex);
++
++ ctx = rccCreateContext(NULL, 0, 0, classes, 0);
++ if (ctx) rccInitDb4(ctx, NULL, 0);
++ if (ctx) rccLoad(ctx, "zip");
++
++ return ctx;
++}
++
++
++void rcc_free(void *ctx) {
++ if (ctx) rccFreeContext((rcc_context)ctx);
++
++ pthread_mutex_lock(&mutex);
++ if (initialized == 1) rccFree();
++ initialized--;
++ pthread_mutex_unlock(&mutex);
++}
++
++
++char *rcc_read(void *ctx, const char *string, size_t size) {
++ if (!initialized) {
++ rcc_init();
++ if (!initialized) return NULL;
++ }
++ return rccSizedRecode((rcc_context)ctx, ARC_CLASS, OUT_CLASS, string, size, NULL);
++}
++
++char *rcc_write(rcc_context ctx, const char *string, size_t size) {
++ if (!initialized) {
++ rcc_init();
++ if (!initialized) return NULL;
++ }
++ return rccSizedRecode((rcc_context)ctx, OUT_CLASS, ARCOUT_CLASS, string, size, NULL);
++}
+diff -dPNur p7zip_4.65/C/rccrecode.h p7zip_4.65-new/C/rccrecode.h
+--- p7zip_4.65/C/rccrecode.h 1970-01-01 01:00:00.000000000 +0100
++++ p7zip_4.65-new/C/rccrecode.h 2009-06-14 02:44:19.000000000 +0200
+@@ -0,0 +1,17 @@
++#ifndef _RCC_RECODE_H
++#define _RCC_RECODE_H
++
++# ifdef __cplusplus
++extern "C" {
++# endif
++
++ void *rcc_init();
++ void rcc_free(void *ctx);
++ char *rcc_read(void *ctx, const char *string, size_t size);
++ char *rcc_write(void *ctx, const char *string, size_t size);
++
++# ifdef __cplusplus
++}
++# endif
++
++#endif /* _RCC_RECODE_H */
+diff -dPNur p7zip_4.65/CPP/7zip/Archive/Zip/ZipIn.cpp p7zip_4.65-new/CPP/7zip/Archive/Zip/ZipIn.cpp
+--- p7zip_4.65/CPP/7zip/Archive/Zip/ZipIn.cpp 2008-11-28 23:03:44.000000000 +0100
++++ p7zip_4.65-new/CPP/7zip/Archive/Zip/ZipIn.cpp 2009-06-14 02:47:28.000000000 +0200
+@@ -9,6 +9,8 @@
+ #include "../../Common/LimitedStreams.h"
+ #include "../../Common/StreamUtils.h"
+
++#include "../../../../C/rccrecode.h"
++
+ extern "C"
+ {
+ #include "../../../../C/CpuArch.h"
+@@ -23,6 +25,14 @@
+
+ // static const char kEndOfString = '\0';
+
++CInArchive::CInArchive() {
++ rccctx = rcc_init();
++}
++
++CInArchive::~CInArchive() {
++ rcc_free(rccctx);
++}
++
+ HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
+ {
+ Close();
+@@ -198,12 +208,21 @@
+
+ AString CInArchive::ReadFileName(UInt32 nameSize)
+ {
++ char *rccrec;
++
+ if (nameSize == 0)
+ return AString();
+ char *p = m_NameBuffer.GetBuffer(nameSize);
+ SafeReadBytes(p, nameSize);
+ p[nameSize] = 0;
+ m_NameBuffer.ReleaseBuffer();
++
++ rccrec = rcc_read(rccctx, (LPCSTR)m_NameBuffer, 0);
++ if (rccrec) {
++ m_NameBuffer = rccrec;
++ free(rccrec);
++ }
++
+ return m_NameBuffer;
+ }
+
+diff -dPNur p7zip_4.65/CPP/7zip/Archive/Zip/ZipIn.h p7zip_4.65-new/CPP/7zip/Archive/Zip/ZipIn.h
+--- p7zip_4.65/CPP/7zip/Archive/Zip/ZipIn.h 2008-08-14 11:11:13.000000000 +0200
++++ p7zip_4.65-new/CPP/7zip/Archive/Zip/ZipIn.h 2009-06-14 02:48:32.000000000 +0200
+@@ -108,6 +108,10 @@
+ ISequentialInStream *CreateLimitedStream(UInt64 position, UInt64 size);
+ IInStream* CreateStream();
+
++ void *rccctx;
++ CInArchive();
++ ~CInArchive();
++
+ bool IsOpen() const { return m_Stream != NULL; }
+ };
+
+diff -dPNur p7zip_4.65/CPP/7zip/Archive/Zip/ZipOut.cpp p7zip_4.65-new/CPP/7zip/Archive/Zip/ZipOut.cpp
+--- p7zip_4.65/CPP/7zip/Archive/Zip/ZipOut.cpp 2008-08-14 11:11:26.000000000 +0200
++++ p7zip_4.65-new/CPP/7zip/Archive/Zip/ZipOut.cpp 2009-06-14 02:44:19.000000000 +0200
+@@ -7,9 +7,19 @@
+ #include "../../Common/OffsetStream.h"
+ #include "../../Common/StreamUtils.h"
+
++#include "../../../../C/rccrecode.h"
++
+ namespace NArchive {
+ namespace NZip {
+
++COutArchive::COutArchive() {
++ rccctx = rcc_init();
++}
++
++COutArchive::~COutArchive() {
++ rcc_free(rccctx);
++}
++
+ void COutArchive::Create(IOutStream *outStream)
+ {
+ if (!m_OutBuffer.Create(1 << 16))
+@@ -112,6 +122,7 @@
+ {
+ SeekTo(m_BasePosition);
+
++ char *rccrec;
+ bool isZip64 = m_IsZip64 || item.PackSize >= 0xFFFFFFFF || item.UnPackSize >= 0xFFFFFFFF;
+
+ WriteUInt32(NSignature::kLocalFileHeader);
+@@ -130,6 +141,12 @@
+ throw CSystemException(E_FAIL);
+ }
+ WriteUInt16((UInt16)m_ExtraSize); // test it;
++ rccrec = rcc_write(rccctx, (const char *)item.Name, item.Name.Length());
++ if (rccrec) {
++ printf("%u, %s.\n", item.Name.Length(), rccrec);
++ WriteBytes(rccrec, strlen(rccrec));
++ free(rccrec);
++ } else
+ WriteBytes((const char *)item.Name, item.Name.Length());
+
+ UInt32 extraPos = 0;
+@@ -154,6 +171,7 @@
+
+ void COutArchive::WriteCentralHeader(const CItem &item)
+ {
++ char *rccrec;
+ bool isUnPack64 = item.UnPackSize >= 0xFFFFFFFF;
+ bool isPack64 = item.PackSize >= 0xFFFFFFFF;
+ bool isPosition64 = item.LocalHeaderPosition >= 0xFFFFFFFF;
+@@ -181,6 +199,13 @@
+ WriteUInt16(item.InternalAttributes);
+ WriteUInt32(item.ExternalAttributes);
+ WriteUInt32(isPosition64 ? 0xFFFFFFFF: (UInt32)item.LocalHeaderPosition);
++
++ rccrec = rcc_write(rccctx, (const char *)item.Name, item.Name.Length());
++ if (rccrec) {
++ printf("C: %u, %s.\n", item.Name.Length(), rccrec);
++ WriteBytes(rccrec, strlen(rccrec));
++ free(rccrec);
++ } else
+ WriteBytes((const char *)item.Name, item.Name.Length());
+ if (isZip64)
+ {
+diff -dPNur p7zip_4.65/CPP/7zip/Archive/Zip/ZipOut.h p7zip_4.65-new/CPP/7zip/Archive/Zip/ZipOut.h
+--- p7zip_4.65/CPP/7zip/Archive/Zip/ZipOut.h 2008-08-14 11:11:13.000000000 +0200
++++ p7zip_4.65-new/CPP/7zip/Archive/Zip/ZipOut.h 2009-06-14 02:44:19.000000000 +0200
+@@ -49,6 +49,11 @@
+ void CreateStreamForCompressing(IOutStream **outStream);
+ void CreateStreamForCopying(ISequentialOutStream **outStream);
+ void SeekToPackedDataPosition();
++
++ void *rccctx;
++ COutArchive();
++ ~COutArchive();
++
+ };
+
+ }}
+diff -dPNur p7zip_4.65/CPP/7zip/Bundles/Alone/makefile p7zip_4.65-new/CPP/7zip/Bundles/Alone/makefile
+--- p7zip_4.65/CPP/7zip/Bundles/Alone/makefile 2009-02-07 19:39:04.000000000 +0100
++++ p7zip_4.65-new/CPP/7zip/Bundles/Alone/makefile 2009-06-14 02:44:19.000000000 +0200
+@@ -250,6 +250,7 @@
+ Sha256.o
+
+ OBJS=\
++rccrecode.o \
+ myGetTickCount.o \
+ wine_date_and_time.o \
+ myAddExeFlag.o \
+diff -dPNur p7zip_4.65/CPP/7zip/Bundles/Alone/makefile.list p7zip_4.65-new/CPP/7zip/Bundles/Alone/makefile.list
+--- p7zip_4.65/CPP/7zip/Bundles/Alone/makefile.list 2009-02-09 21:04:57.000000000 +0100
++++ p7zip_4.65-new/CPP/7zip/Bundles/Alone/makefile.list 2009-06-14 02:51:45.000000000 +0200
+@@ -204,6 +204,7 @@
+ ../../Crypto/ZipStrong.cpp
+
+ SRCS_C=\
++ ../../../../C/rccrecode.c \
+ ../../../../C/Bra.c \
+ ../../../../C/Bra86.c \
+ ../../../../C/BraIA64.c \
+diff -dPNur p7zip_4.65/CPP/7zip/Bundles/Format7zFree/makefile p7zip_4.65-new/CPP/7zip/Bundles/Format7zFree/makefile
+--- p7zip_4.65/CPP/7zip/Bundles/Format7zFree/makefile 2009-02-09 21:47:24.000000000 +0100
++++ p7zip_4.65-new/CPP/7zip/Bundles/Format7zFree/makefile 2009-06-14 03:00:06.000000000 +0200
+@@ -296,6 +296,7 @@
+ Sha256.o \
+
+ OBJS = \
++ rccrecode.o \
+ wine_date_and_time.o \
+ myGetTickCount.o \
+ $(COMMON_OBJS) \
+diff -dPNur p7zip_4.65/CPP/7zip/Bundles/Format7zFree/makefile.list p7zip_4.65-new/CPP/7zip/Bundles/Format7zFree/makefile.list
+--- p7zip_4.65/CPP/7zip/Bundles/Format7zFree/makefile.list 2009-02-09 21:04:32.000000000 +0100
++++ p7zip_4.65-new/CPP/7zip/Bundles/Format7zFree/makefile.list 2009-06-14 03:00:39.000000000 +0200
+@@ -235,6 +235,7 @@
+ ../../Crypto/ZipStrong.cpp
+
+ SRCS_C=\
++ ../../../../C/rccrecode.c \
+ ../../../../C/Bra.c \
+ ../../../../C/Bra86.c \
+ ../../../../C/BraIA64.c \
+diff -dPNur p7zip_4.65/makefile.linux_amd64 p7zip_4.65-new/makefile.linux_amd64
+--- p7zip_4.65/makefile.linux_amd64 2007-06-28 09:32:20.000000000 +0200
++++ p7zip_4.65-new/makefile.linux_amd64 2009-06-14 05:08:19.000000000 +0200
+@@ -12,7 +12,7 @@
+ CC_SHARED=-fPIC
+ LINK_SHARED=-fPIC -shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ OBJ_CRC32=$(OBJ_CRC32_C)
+diff -dPNur p7zip_4.65/makefile.linux_amd64_asm p7zip_4.65-new/makefile.linux_amd64_asm
+--- p7zip_4.65/makefile.linux_amd64_asm 2007-06-28 09:32:30.000000000 +0200
++++ p7zip_4.65-new/makefile.linux_amd64_asm 2009-06-14 05:08:19.000000000 +0200
+@@ -13,7 +13,7 @@
+ LINK_SHARED=-fPIC -shared
+ ASM=yasm -f elf -m amd64
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ CPU=x64
+diff -dPNur p7zip_4.65/makefile.linux_amd64_asm_icc p7zip_4.65-new/makefile.linux_amd64_asm_icc
+--- p7zip_4.65/makefile.linux_amd64_asm_icc 2009-02-09 22:12:36.000000000 +0100
++++ p7zip_4.65-new/makefile.linux_amd64_asm_icc 2009-06-14 05:08:19.000000000 +0200
+@@ -16,7 +16,7 @@
+ LINK_SHARED=-fPIC -shared
+ ASM=yasm -f elf -m amd64
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ CPU=x64
+diff -dPNur p7zip_4.65/makefile.linux_gcc_2.95_no_need_for_libstdc p7zip_4.65-new/makefile.linux_gcc_2.95_no_need_for_libstdc
+--- p7zip_4.65/makefile.linux_gcc_2.95_no_need_for_libstdc 2007-06-24 20:49:31.000000000 +0200
++++ p7zip_4.65-new/makefile.linux_gcc_2.95_no_need_for_libstdc 2009-06-14 05:08:19.000000000 +0200
+@@ -11,7 +11,7 @@
+ LINK_SHARED=-shared
+ ASM=nasm -f elf
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ CPU=x86
+diff -dPNur p7zip_4.65/makefile.linux_s390x p7zip_4.65-new/makefile.linux_s390x
+--- p7zip_4.65/makefile.linux_s390x 2007-08-07 18:36:01.000000000 +0200
++++ p7zip_4.65-new/makefile.linux_s390x 2009-06-14 05:08:19.000000000 +0200
+@@ -15,7 +15,7 @@
+ CC_SHARED=-fPIC
+ LINK_SHARED=-fPIC -shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ OBJ_CRC32=$(OBJ_CRC32_C)
+diff -dPNur p7zip_4.65/makefile.linux_x86_asm_gcc_4.X p7zip_4.65-new/makefile.linux_x86_asm_gcc_4.X
+--- p7zip_4.65/makefile.linux_x86_asm_gcc_4.X 2007-08-08 11:15:07.000000000 +0200
++++ p7zip_4.65-new/makefile.linux_x86_asm_gcc_4.X 2009-06-14 05:08:19.000000000 +0200
+@@ -15,7 +15,7 @@
+ LINK_SHARED=-fPIC -shared
+ ASM=nasm -f elf
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ CPU=x86
+diff -dPNur p7zip_4.65/makefile.linux_x86_asm_gcc_4.X_fltk p7zip_4.65-new/makefile.linux_x86_asm_gcc_4.X_fltk
+--- p7zip_4.65/makefile.linux_x86_asm_gcc_4.X_fltk 2007-08-08 11:15:10.000000000 +0200
++++ p7zip_4.65-new/makefile.linux_x86_asm_gcc_4.X_fltk 2009-06-14 05:08:19.000000000 +0200
+@@ -15,7 +15,7 @@
+ LINK_SHARED=-fPIC -shared
+ ASM=nasm -f elf
+
+-LOCAL_LIBS=-lpthread `fltk-config --ldflags`
++LOCAL_LIBS=-lpthread -lrcc `fltk-config --ldflags`
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ CPU=x86
+diff -dPNur p7zip_4.65/makefile.linux_x86_asm_icc p7zip_4.65-new/makefile.linux_x86_asm_icc
+--- p7zip_4.65/makefile.linux_x86_asm_icc 2007-08-08 11:15:15.000000000 +0200
++++ p7zip_4.65-new/makefile.linux_x86_asm_icc 2009-06-14 05:08:19.000000000 +0200
+@@ -16,7 +16,7 @@
+ LINK_SHARED=-fPIC -shared
+ ASM=nasm -f elf
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ CPU=x86
+diff -dPNur p7zip_4.65/makefile.linux_x86_ppc_alpha p7zip_4.65-new/makefile.linux_x86_ppc_alpha
+--- p7zip_4.65/makefile.linux_x86_ppc_alpha 2007-08-08 11:15:18.000000000 +0200
++++ p7zip_4.65-new/makefile.linux_x86_ppc_alpha 2009-06-14 05:08:19.000000000 +0200
+@@ -14,7 +14,7 @@
+ CC_SHARED=-fPIC
+ LINK_SHARED=-fPIC -shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ OBJ_CRC32=$(OBJ_CRC32_C)
+diff -dPNur p7zip_4.65/makefile.linux_x86_ppc_alpha_gcc_4.X p7zip_4.65-new/makefile.linux_x86_ppc_alpha_gcc_4.X
+--- p7zip_4.65/makefile.linux_x86_ppc_alpha_gcc_4.X 2007-08-08 11:15:20.000000000 +0200
++++ p7zip_4.65-new/makefile.linux_x86_ppc_alpha_gcc_4.X 2009-06-14 05:08:19.000000000 +0200
+@@ -14,7 +14,7 @@
+ CC_SHARED=-fPIC
+ LINK_SHARED=-fPIC -shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ OBJ_CRC32=$(OBJ_CRC32_C)
+diff -dPNur p7zip_4.65/makefile.machine p7zip_4.65-new/makefile.machine
+--- p7zip_4.65/makefile.machine 2009-02-14 18:19:09.000000000 +0100
++++ p7zip_4.65-new/makefile.machine 2009-06-14 02:44:19.000000000 +0200
+@@ -14,7 +14,7 @@
+ CC_SHARED=-fPIC
+ LINK_SHARED=-fPIC -shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ OBJ_CRC32=$(OBJ_CRC32_C)
+diff -dPNur p7zip_4.65/makefile.rules p7zip_4.65-new/makefile.rules
+--- p7zip_4.65/makefile.rules 2009-02-09 21:03:16.000000000 +0100
++++ p7zip_4.65-new/makefile.rules 2009-06-14 02:52:48.000000000 +0200
+@@ -558,3 +558,5 @@
+ 7zCrcT8.o : ../../../../C/7zCrcT8.c
+ $(CC) $(CFLAGS) ../../../../C/7zCrcT8.c
+
++rccrecode.o : ../../../../C/rccrecode.c
++ $(CC) $(CFLAGS) ../../../../C/rccrecode.c
diff --git a/patches/p7zip/p7zip_4.65-ds-rusxmms.patch b/patches/p7zip/p7zip_4.65-ds-rusxmms.patch
new file mode 100644
index 0000000..7072522
--- /dev/null
+++ b/patches/p7zip/p7zip_4.65-ds-rusxmms.patch
@@ -0,0 +1,305 @@
+diff -dPNur p7zip_4.65/C/rccrecode.c p7zip_4.65-new/C/rccrecode.c
+--- p7zip_4.65/C/rccrecode.c 1970-01-01 01:00:00.000000000 +0100
++++ p7zip_4.65-new/C/rccrecode.c 2009-06-14 02:44:19.000000000 +0200
+@@ -0,0 +1,71 @@
++#include <pthread.h>
++#include <librcc.h>
++
++static rcc_class_default_charset default_oem[] = {
++ { "ru", "IBM866" },
++ { NULL, NULL }
++};
++
++static rcc_class_default_charset default_iso[] = {
++ { "ru", "CP1251" },
++ { NULL, NULL }
++};
++
++#define ARC_CLASS 0
++#define OUT_CLASS 1
++#define ARCOUT_CLASS 0
++static rcc_class classes[] = {
++ { "oem", RCC_CLASS_STANDARD, NULL, default_oem, "OEM Encoding", 0 },
++ { "out", RCC_CLASS_STANDARD, "LC_CTYPE", NULL, "Output", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int initialized = 0;
++static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
++
++void *rcc_init() {
++ rcc_context ctx;
++
++ pthread_mutex_lock(&mutex);
++ if (!initialized) {
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "zip");
++ rccInitDb4(NULL, NULL, 0);
++ }
++ initialized++;
++ pthread_mutex_unlock(&mutex);
++
++ ctx = rccCreateContext(NULL, 0, 0, classes, 0);
++ if (ctx) rccInitDb4(ctx, NULL, 0);
++ if (ctx) rccLoad(ctx, "zip");
++
++ return ctx;
++}
++
++
++void rcc_free(void *ctx) {
++ if (ctx) rccFreeContext((rcc_context)ctx);
++
++ pthread_mutex_lock(&mutex);
++ if (initialized == 1) rccFree();
++ initialized--;
++ pthread_mutex_unlock(&mutex);
++}
++
++
++char *rcc_read(void *ctx, const char *string, size_t size) {
++ if (!initialized) {
++ rcc_init();
++ if (!initialized) return NULL;
++ }
++ return rccSizedRecode((rcc_context)ctx, ARC_CLASS, OUT_CLASS, string, size, NULL);
++}
++
++char *rcc_write(rcc_context ctx, const char *string, size_t size) {
++ if (!initialized) {
++ rcc_init();
++ if (!initialized) return NULL;
++ }
++ return rccSizedRecode((rcc_context)ctx, OUT_CLASS, ARCOUT_CLASS, string, size, NULL);
++}
+diff -dPNur p7zip_4.65/C/rccrecode.h p7zip_4.65-new/C/rccrecode.h
+--- p7zip_4.65/C/rccrecode.h 1970-01-01 01:00:00.000000000 +0100
++++ p7zip_4.65-new/C/rccrecode.h 2009-06-14 02:44:19.000000000 +0200
+@@ -0,0 +1,17 @@
++#ifndef _RCC_RECODE_H
++#define _RCC_RECODE_H
++
++# ifdef __cplusplus
++extern "C" {
++# endif
++
++ void *rcc_init();
++ void rcc_free(void *ctx);
++ char *rcc_read(void *ctx, const char *string, size_t size);
++ char *rcc_write(void *ctx, const char *string, size_t size);
++
++# ifdef __cplusplus
++}
++# endif
++
++#endif /* _RCC_RECODE_H */
+diff -dPNur p7zip_4.65/CPP/7zip/Archive/Zip/ZipIn.cpp p7zip_4.65-new/CPP/7zip/Archive/Zip/ZipIn.cpp
+--- p7zip_4.65/CPP/7zip/Archive/Zip/ZipIn.cpp 2008-11-28 23:03:44.000000000 +0100
++++ p7zip_4.65-new/CPP/7zip/Archive/Zip/ZipIn.cpp 2009-06-14 02:47:28.000000000 +0200
+@@ -9,6 +9,8 @@
+ #include "../../Common/LimitedStreams.h"
+ #include "../../Common/StreamUtils.h"
+
++#include "../../../../C/rccrecode.h"
++
+ extern "C"
+ {
+ #include "../../../../C/CpuArch.h"
+@@ -23,6 +25,14 @@
+
+ // static const char kEndOfString = '\0';
+
++CInArchive::CInArchive() {
++ rccctx = rcc_init();
++}
++
++CInArchive::~CInArchive() {
++ rcc_free(rccctx);
++}
++
+ HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
+ {
+ Close();
+@@ -198,12 +208,21 @@
+
+ AString CInArchive::ReadFileName(UInt32 nameSize)
+ {
++ char *rccrec;
++
+ if (nameSize == 0)
+ return AString();
+ char *p = m_NameBuffer.GetBuffer(nameSize);
+ SafeReadBytes(p, nameSize);
+ p[nameSize] = 0;
+ m_NameBuffer.ReleaseBuffer();
++
++ rccrec = rcc_read(rccctx, (LPCSTR)m_NameBuffer, 0);
++ if (rccrec) {
++ m_NameBuffer = rccrec;
++ free(rccrec);
++ }
++
+ return m_NameBuffer;
+ }
+
+diff -dPNur p7zip_4.65/CPP/7zip/Archive/Zip/ZipIn.h p7zip_4.65-new/CPP/7zip/Archive/Zip/ZipIn.h
+--- p7zip_4.65/CPP/7zip/Archive/Zip/ZipIn.h 2008-08-14 11:11:13.000000000 +0200
++++ p7zip_4.65-new/CPP/7zip/Archive/Zip/ZipIn.h 2009-06-14 02:48:32.000000000 +0200
+@@ -108,6 +108,10 @@
+ ISequentialInStream *CreateLimitedStream(UInt64 position, UInt64 size);
+ IInStream* CreateStream();
+
++ void *rccctx;
++ CInArchive();
++ ~CInArchive();
++
+ bool IsOpen() const { return m_Stream != NULL; }
+ };
+
+diff -dPNur p7zip_4.65/CPP/7zip/Archive/Zip/ZipOut.cpp p7zip_4.65-new/CPP/7zip/Archive/Zip/ZipOut.cpp
+--- p7zip_4.65/CPP/7zip/Archive/Zip/ZipOut.cpp 2008-08-14 11:11:26.000000000 +0200
++++ p7zip_4.65-new/CPP/7zip/Archive/Zip/ZipOut.cpp 2009-06-14 02:44:19.000000000 +0200
+@@ -7,9 +7,19 @@
+ #include "../../Common/OffsetStream.h"
+ #include "../../Common/StreamUtils.h"
+
++#include "../../../../C/rccrecode.h"
++
+ namespace NArchive {
+ namespace NZip {
+
++COutArchive::COutArchive() {
++ rccctx = rcc_init();
++}
++
++COutArchive::~COutArchive() {
++ rcc_free(rccctx);
++}
++
+ void COutArchive::Create(IOutStream *outStream)
+ {
+ if (!m_OutBuffer.Create(1 << 16))
+@@ -112,6 +122,7 @@
+ {
+ SeekTo(m_BasePosition);
+
++ char *rccrec;
+ bool isZip64 = m_IsZip64 || item.PackSize >= 0xFFFFFFFF || item.UnPackSize >= 0xFFFFFFFF;
+
+ WriteUInt32(NSignature::kLocalFileHeader);
+@@ -130,6 +141,12 @@
+ throw CSystemException(E_FAIL);
+ }
+ WriteUInt16((UInt16)m_ExtraSize); // test it;
++ rccrec = rcc_write(rccctx, (const char *)item.Name, item.Name.Length());
++ if (rccrec) {
++ printf("%u, %s.\n", item.Name.Length(), rccrec);
++ WriteBytes(rccrec, strlen(rccrec));
++ free(rccrec);
++ } else
+ WriteBytes((const char *)item.Name, item.Name.Length());
+
+ UInt32 extraPos = 0;
+@@ -154,6 +171,7 @@
+
+ void COutArchive::WriteCentralHeader(const CItem &item)
+ {
++ char *rccrec;
+ bool isUnPack64 = item.UnPackSize >= 0xFFFFFFFF;
+ bool isPack64 = item.PackSize >= 0xFFFFFFFF;
+ bool isPosition64 = item.LocalHeaderPosition >= 0xFFFFFFFF;
+@@ -181,6 +199,13 @@
+ WriteUInt16(item.InternalAttributes);
+ WriteUInt32(item.ExternalAttributes);
+ WriteUInt32(isPosition64 ? 0xFFFFFFFF: (UInt32)item.LocalHeaderPosition);
++
++ rccrec = rcc_write(rccctx, (const char *)item.Name, item.Name.Length());
++ if (rccrec) {
++ printf("C: %u, %s.\n", item.Name.Length(), rccrec);
++ WriteBytes(rccrec, strlen(rccrec));
++ free(rccrec);
++ } else
+ WriteBytes((const char *)item.Name, item.Name.Length());
+ if (isZip64)
+ {
+diff -dPNur p7zip_4.65/CPP/7zip/Archive/Zip/ZipOut.h p7zip_4.65-new/CPP/7zip/Archive/Zip/ZipOut.h
+--- p7zip_4.65/CPP/7zip/Archive/Zip/ZipOut.h 2008-08-14 11:11:13.000000000 +0200
++++ p7zip_4.65-new/CPP/7zip/Archive/Zip/ZipOut.h 2009-06-14 02:44:19.000000000 +0200
+@@ -49,6 +49,11 @@
+ void CreateStreamForCompressing(IOutStream **outStream);
+ void CreateStreamForCopying(ISequentialOutStream **outStream);
+ void SeekToPackedDataPosition();
++
++ void *rccctx;
++ COutArchive();
++ ~COutArchive();
++
+ };
+
+ }}
+diff -dPNur p7zip_4.65/CPP/7zip/Bundles/Alone/makefile p7zip_4.65-new/CPP/7zip/Bundles/Alone/makefile
+--- p7zip_4.65/CPP/7zip/Bundles/Alone/makefile 2009-02-07 19:39:04.000000000 +0100
++++ p7zip_4.65-new/CPP/7zip/Bundles/Alone/makefile 2009-06-14 02:44:19.000000000 +0200
+@@ -250,6 +250,7 @@
+ Sha256.o
+
+ OBJS=\
++rccrecode.o \
+ myGetTickCount.o \
+ wine_date_and_time.o \
+ myAddExeFlag.o \
+diff -dPNur p7zip_4.65/CPP/7zip/Bundles/Alone/makefile.list p7zip_4.65-new/CPP/7zip/Bundles/Alone/makefile.list
+--- p7zip_4.65/CPP/7zip/Bundles/Alone/makefile.list 2009-02-09 21:04:57.000000000 +0100
++++ p7zip_4.65-new/CPP/7zip/Bundles/Alone/makefile.list 2009-06-14 02:51:45.000000000 +0200
+@@ -204,6 +204,7 @@
+ ../../Crypto/ZipStrong.cpp
+
+ SRCS_C=\
++ ../../../../C/rccrecode.c \
+ ../../../../C/Bra.c \
+ ../../../../C/Bra86.c \
+ ../../../../C/BraIA64.c \
+diff -dPNur p7zip_4.65/CPP/7zip/Bundles/Format7zFree/makefile p7zip_4.65-new/CPP/7zip/Bundles/Format7zFree/makefile
+--- p7zip_4.65/CPP/7zip/Bundles/Format7zFree/makefile 2009-02-09 21:47:24.000000000 +0100
++++ p7zip_4.65-new/CPP/7zip/Bundles/Format7zFree/makefile 2009-06-14 03:00:06.000000000 +0200
+@@ -296,6 +296,7 @@
+ Sha256.o \
+
+ OBJS = \
++ rccrecode.o \
+ wine_date_and_time.o \
+ myGetTickCount.o \
+ $(COMMON_OBJS) \
+diff -dPNur p7zip_4.65/CPP/7zip/Bundles/Format7zFree/makefile.list p7zip_4.65-new/CPP/7zip/Bundles/Format7zFree/makefile.list
+--- p7zip_4.65/CPP/7zip/Bundles/Format7zFree/makefile.list 2009-02-09 21:04:32.000000000 +0100
++++ p7zip_4.65-new/CPP/7zip/Bundles/Format7zFree/makefile.list 2009-06-14 03:00:39.000000000 +0200
+@@ -235,6 +235,7 @@
+ ../../Crypto/ZipStrong.cpp
+
+ SRCS_C=\
++ ../../../../C/rccrecode.c \
+ ../../../../C/Bra.c \
+ ../../../../C/Bra86.c \
+ ../../../../C/BraIA64.c \
+diff -dPNur p7zip_4.65/makefile.machine p7zip_4.65-new/makefile.machine
+--- p7zip_4.65/makefile.machine 2009-02-14 18:19:09.000000000 +0100
++++ p7zip_4.65-new/makefile.machine 2009-06-14 02:44:19.000000000 +0200
+@@ -14,7 +14,7 @@
+ CC_SHARED=-fPIC
+ LINK_SHARED=-fPIC -shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ OBJ_CRC32=$(OBJ_CRC32_C)
+diff -dPNur p7zip_4.65/makefile.rules p7zip_4.65-new/makefile.rules
+--- p7zip_4.65/makefile.rules 2009-02-09 21:03:16.000000000 +0100
++++ p7zip_4.65-new/makefile.rules 2009-06-14 02:52:48.000000000 +0200
+@@ -558,3 +558,5 @@
+ 7zCrcT8.o : ../../../../C/7zCrcT8.c
+ $(CC) $(CFLAGS) ../../../../C/7zCrcT8.c
+
++rccrecode.o : ../../../../C/rccrecode.c
++ $(CC) $(CFLAGS) ../../../../C/rccrecode.c
diff --git a/patches/p7zip/p7zip_9.04-ds-rusxmms-full.patch b/patches/p7zip/p7zip_9.04-ds-rusxmms-full.patch
new file mode 100644
index 0000000..d44a16f
--- /dev/null
+++ b/patches/p7zip/p7zip_9.04-ds-rusxmms-full.patch
@@ -0,0 +1,414 @@
+diff -dPNur p7zip-9.04~dfsg.1/C/rccrecode.c p7zip-9.04~dfsg.1-ds/C/rccrecode.c
+--- p7zip-9.04~dfsg.1/C/rccrecode.c 1970-01-01 03:00:00.000000000 +0300
++++ p7zip-9.04~dfsg.1-ds/C/rccrecode.c 2009-10-28 03:29:23.000000000 +0300
+@@ -0,0 +1,71 @@
++#include <pthread.h>
++#include <librcc.h>
++
++static rcc_class_default_charset default_oem[] = {
++ { "ru", "IBM866" },
++ { NULL, NULL }
++};
++
++static rcc_class_default_charset default_iso[] = {
++ { "ru", "CP1251" },
++ { NULL, NULL }
++};
++
++#define ARC_CLASS 0
++#define OUT_CLASS 1
++#define ARCOUT_CLASS 0
++static rcc_class classes[] = {
++ { "oem", RCC_CLASS_STANDARD, NULL, default_oem, "OEM Encoding", 0 },
++ { "out", RCC_CLASS_STANDARD, "LC_CTYPE", NULL, "Output", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int initialized = 0;
++static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
++
++void *rcc_init() {
++ rcc_context ctx;
++
++ pthread_mutex_lock(&mutex);
++ if (!initialized) {
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "zip");
++ rccInitDb4(NULL, NULL, 0);
++ }
++ initialized++;
++ pthread_mutex_unlock(&mutex);
++
++ ctx = rccCreateContext(NULL, 0, 0, classes, 0);
++ if (ctx) rccInitDb4(ctx, NULL, 0);
++ if (ctx) rccLoad(ctx, "zip");
++
++ return ctx;
++}
++
++
++void rcc_free(void *ctx) {
++ if (ctx) rccFreeContext((rcc_context)ctx);
++
++ pthread_mutex_lock(&mutex);
++ if (initialized == 1) rccFree();
++ initialized--;
++ pthread_mutex_unlock(&mutex);
++}
++
++
++char *rcc_read(void *ctx, const char *string, size_t size) {
++ if (!initialized) {
++ rcc_init();
++ if (!initialized) return NULL;
++ }
++ return rccSizedRecode((rcc_context)ctx, ARC_CLASS, OUT_CLASS, string, size, NULL);
++}
++
++char *rcc_write(rcc_context ctx, const char *string, size_t size) {
++ if (!initialized) {
++ rcc_init();
++ if (!initialized) return NULL;
++ }
++ return rccSizedRecode((rcc_context)ctx, OUT_CLASS, ARCOUT_CLASS, string, size, NULL);
++}
+diff -dPNur p7zip-9.04~dfsg.1/C/rccrecode.h p7zip-9.04~dfsg.1-ds/C/rccrecode.h
+--- p7zip-9.04~dfsg.1/C/rccrecode.h 1970-01-01 03:00:00.000000000 +0300
++++ p7zip-9.04~dfsg.1-ds/C/rccrecode.h 2009-10-28 03:29:23.000000000 +0300
+@@ -0,0 +1,17 @@
++#ifndef _RCC_RECODE_H
++#define _RCC_RECODE_H
++
++# ifdef __cplusplus
++extern "C" {
++# endif
++
++ void *rcc_init();
++ void rcc_free(void *ctx);
++ char *rcc_read(void *ctx, const char *string, size_t size);
++ char *rcc_write(void *ctx, const char *string, size_t size);
++
++# ifdef __cplusplus
++}
++# endif
++
++#endif /* _RCC_RECODE_H */
+diff -dPNur p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipIn.cpp p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipIn.cpp
+--- p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipIn.cpp 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipIn.cpp 2009-10-28 03:49:06.000000000 +0300
+@@ -9,6 +9,8 @@
+ #include "../../Common/LimitedStreams.h"
+ #include "../../Common/StreamUtils.h"
+
++#include "../../../../C/rccrecode.h"
++
+ #include "ZipIn.h"
+
+ #define Get16(p) GetUi16(p)
+@@ -17,7 +19,17 @@
+
+ namespace NArchive {
+ namespace NZip {
+-
++
++CInArchive::CInArchive()
++{
++ rccctx = rcc_init();
++}
++
++CInArchive::~CInArchive()
++{
++ rcc_free(rccctx);
++}
++
+ HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
+ {
+ _inBufMode = false;
+@@ -200,12 +212,20 @@
+
+ void CInArchive::ReadFileName(UInt32 nameSize, AString &dest)
+ {
++ char *rccrec;
++
+ if (nameSize == 0)
+ dest.Empty();
+ char *p = dest.GetBuffer((int)nameSize);
+ SafeReadBytes(p, nameSize);
+ p[nameSize] = 0;
+ dest.ReleaseBuffer();
++
++ rccrec = rcc_read(rccctx, (LPCSTR)dest, 0);
++ if (rccrec) {
++ dest = rccrec;
++ free(rccrec);
++ }
+ }
+
+ void CInArchive::GetArchiveInfo(CInArchiveInfo &archiveInfo) const
+diff -dPNur p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipIn.h p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipIn.h
+--- p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipIn.h 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipIn.h 2009-10-28 03:29:23.000000000 +0300
+@@ -114,6 +114,10 @@
+ ISequentialInStream *CreateLimitedStream(UInt64 position, UInt64 size);
+ IInStream* CreateStream();
+
++ void *rccctx;
++ CInArchive();
++ ~CInArchive();
++
+ bool IsOpen() const { return m_Stream != NULL; }
+ };
+
+diff -dPNur p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipOut.h p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipOut.h
+--- p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipOut.h 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipOut.h 2009-10-28 05:49:05.000000000 +0300
+@@ -49,6 +49,11 @@
+ void CreateStreamForCompressing(IOutStream **outStream);
+ void CreateStreamForCopying(ISequentialOutStream **outStream);
+ void SeekToPackedDataPosition();
++
++ void *rccctx;
++ COutArchive();
++ ~COutArchive();
++ void Recode(CItem &item);
+ };
+
+ }}
+diff -dPNur p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipUpdate.cpp p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipUpdate.cpp
+--- p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipUpdate.cpp 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipUpdate.cpp 2009-10-28 05:48:04.000000000 +0300
+@@ -87,6 +87,7 @@
+ item.NtfsATime = ui.NtfsATime;
+ item.NtfsCTime = ui.NtfsCTime;
+ item.NtfsTimeIsDefined = ui.NtfsTimeIsDefined;
++ archive.Recode(item);
+ }
+ else
+ isDir = item.IsDir();
+@@ -359,9 +360,11 @@
+ item.NtfsCTime = ui.NtfsCTime;
+ item.NtfsTimeIsDefined = ui.NtfsTimeIsDefined;
+
++ archive.Recode(item);
++
+ item.CentralExtra.RemoveUnknownSubBlocks();
+ item.LocalExtra.RemoveUnknownSubBlocks();
+-
++
+ archive.PrepareWriteCompressedData2((UInt16)item.Name.Length(), item.UnPackSize, item.PackSize, item.LocalExtra.HasWzAesField());
+ item.LocalHeaderPosition = archive.GetCurrentPosition();
+ archive.SeekToPackedDataPosition();
+diff -dPNur p7zip-9.04~dfsg.1/CPP/7zip/Bundles/Alone/makefile p7zip-9.04~dfsg.1-ds/CPP/7zip/Bundles/Alone/makefile
+--- p7zip-9.04~dfsg.1/CPP/7zip/Bundles/Alone/makefile 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/CPP/7zip/Bundles/Alone/makefile 2009-10-28 03:29:24.000000000 +0300
+@@ -244,6 +244,7 @@
+
+
+ OBJS=\
++rccrecode.o \
+ myGetTickCount.o \
+ wine_date_and_time.o \
+ myAddExeFlag.o \
+diff -dPNur p7zip-9.04~dfsg.1/CPP/7zip/Bundles/Alone/makefile.list p7zip-9.04~dfsg.1-ds/CPP/7zip/Bundles/Alone/makefile.list
+--- p7zip-9.04~dfsg.1/CPP/7zip/Bundles/Alone/makefile.list 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/CPP/7zip/Bundles/Alone/makefile.list 2009-10-28 03:40:17.000000000 +0300
+@@ -192,6 +192,7 @@
+ ../../Crypto/ZipStrong.cpp
+
+ SRCS_C=\
++ ../../../../C/rccrecode.c \
+ ../../../../C/Aes.c \
+ ../../../../C/7zStream.c \
+ ../../../../C/Bra.c \
+diff -dPNur p7zip-9.04~dfsg.1/CPP/7zip/Bundles/Format7zFree/makefile p7zip-9.04~dfsg.1-ds/CPP/7zip/Bundles/Format7zFree/makefile
+--- p7zip-9.04~dfsg.1/CPP/7zip/Bundles/Format7zFree/makefile 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/CPP/7zip/Bundles/Format7zFree/makefile 2009-10-28 03:29:24.000000000 +0300
+@@ -284,6 +284,7 @@
+
+
+ OBJS = \
++ rccrecode.o \
+ wine_date_and_time.o \
+ myGetTickCount.o \
+ $(COMMON_OBJS) \
+diff -dPNur p7zip-9.04~dfsg.1/CPP/7zip/Bundles/Format7zFree/makefile.list p7zip-9.04~dfsg.1-ds/CPP/7zip/Bundles/Format7zFree/makefile.list
+--- p7zip-9.04~dfsg.1/CPP/7zip/Bundles/Format7zFree/makefile.list 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/CPP/7zip/Bundles/Format7zFree/makefile.list 2009-10-28 03:34:02.000000000 +0300
+@@ -218,6 +218,7 @@
+ ../../Crypto/ZipStrong.cpp
+
+ SRCS_C=\
++ ../../../../C/rccrecode.c \
+ ../../../../C/7zBuf2.c \
+ ../../../../C/7zStream.c \
+ ../../../../C/Aes.c \
+diff -dPNur p7zip-9.04~dfsg.1/makefile.linux_amd64 p7zip-9.04~dfsg.1-ds/makefile.linux_amd64
+--- p7zip-9.04~dfsg.1/makefile.linux_amd64 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/makefile.linux_amd64 2009-10-28 03:54:47.000000000 +0300
+@@ -12,7 +12,7 @@
+ CC_SHARED=-fPIC
+ LINK_SHARED=-fPIC -shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ OBJ_CRC32=$(OBJ_CRC32_C)
+diff -dPNur p7zip-9.04~dfsg.1/makefile.linux_amd64_asm p7zip-9.04~dfsg.1-ds/makefile.linux_amd64_asm
+--- p7zip-9.04~dfsg.1/makefile.linux_amd64_asm 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/makefile.linux_amd64_asm 2009-10-28 03:54:47.000000000 +0300
+@@ -13,7 +13,7 @@
+ LINK_SHARED=-fPIC -shared
+ ASM=yasm -f elf -m amd64
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ CPU=x64
+diff -dPNur p7zip-9.04~dfsg.1/makefile.linux_amd64_asm_icc p7zip-9.04~dfsg.1-ds/makefile.linux_amd64_asm_icc
+--- p7zip-9.04~dfsg.1/makefile.linux_amd64_asm_icc 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/makefile.linux_amd64_asm_icc 2009-10-28 03:54:47.000000000 +0300
+@@ -16,7 +16,7 @@
+ LINK_SHARED=-fPIC -shared
+ ASM=yasm -f elf -m amd64
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ CPU=x64
+diff -dPNur p7zip-9.04~dfsg.1/makefile.linux_gcc_2.95_no_need_for_libstdc p7zip-9.04~dfsg.1-ds/makefile.linux_gcc_2.95_no_need_for_libstdc
+--- p7zip-9.04~dfsg.1/makefile.linux_gcc_2.95_no_need_for_libstdc 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/makefile.linux_gcc_2.95_no_need_for_libstdc 2009-10-28 03:54:47.000000000 +0300
+@@ -11,7 +11,7 @@
+ LINK_SHARED=-shared
+ ASM=nasm -f elf
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ CPU=x86
+diff -dPNur p7zip-9.04~dfsg.1/makefile.linux_s390x p7zip-9.04~dfsg.1-ds/makefile.linux_s390x
+--- p7zip-9.04~dfsg.1/makefile.linux_s390x 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/makefile.linux_s390x 2009-10-28 03:54:47.000000000 +0300
+@@ -15,7 +15,7 @@
+ CC_SHARED=-fPIC
+ LINK_SHARED=-fPIC -shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ OBJ_CRC32=$(OBJ_CRC32_C)
+diff -dPNur p7zip-9.04~dfsg.1/makefile.linux_x86_asm_gcc_4.X p7zip-9.04~dfsg.1-ds/makefile.linux_x86_asm_gcc_4.X
+--- p7zip-9.04~dfsg.1/makefile.linux_x86_asm_gcc_4.X 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/makefile.linux_x86_asm_gcc_4.X 2009-10-28 03:54:47.000000000 +0300
+@@ -15,7 +15,7 @@
+ LINK_SHARED=-fPIC -shared
+ ASM=nasm -f elf
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ CPU=x86
+diff -dPNur p7zip-9.04~dfsg.1/makefile.linux_x86_asm_gcc_4.X_fltk p7zip-9.04~dfsg.1-ds/makefile.linux_x86_asm_gcc_4.X_fltk
+--- p7zip-9.04~dfsg.1/makefile.linux_x86_asm_gcc_4.X_fltk 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/makefile.linux_x86_asm_gcc_4.X_fltk 2009-10-28 03:54:47.000000000 +0300
+@@ -15,7 +15,7 @@
+ LINK_SHARED=-fPIC -shared
+ ASM=nasm -f elf
+
+-LOCAL_LIBS=-lpthread `fltk-config --ldflags`
++LOCAL_LIBS=-lpthread -lrcc `fltk-config --ldflags`
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ CPU=x86
+diff -dPNur p7zip-9.04~dfsg.1/makefile.linux_x86_asm_icc p7zip-9.04~dfsg.1-ds/makefile.linux_x86_asm_icc
+--- p7zip-9.04~dfsg.1/makefile.linux_x86_asm_icc 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/makefile.linux_x86_asm_icc 2009-10-28 03:54:47.000000000 +0300
+@@ -16,7 +16,7 @@
+ LINK_SHARED=-fPIC -shared
+ ASM=nasm -f elf
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ CPU=x86
+diff -dPNur p7zip-9.04~dfsg.1/makefile.linux_x86_ppc_alpha p7zip-9.04~dfsg.1-ds/makefile.linux_x86_ppc_alpha
+--- p7zip-9.04~dfsg.1/makefile.linux_x86_ppc_alpha 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/makefile.linux_x86_ppc_alpha 2009-10-28 03:54:47.000000000 +0300
+@@ -14,7 +14,7 @@
+ CC_SHARED=-fPIC
+ LINK_SHARED=-fPIC -shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ OBJ_CRC32=$(OBJ_CRC32_C)
+diff -dPNur p7zip-9.04~dfsg.1/makefile.linux_x86_ppc_alpha_gcc_4.X p7zip-9.04~dfsg.1-ds/makefile.linux_x86_ppc_alpha_gcc_4.X
+--- p7zip-9.04~dfsg.1/makefile.linux_x86_ppc_alpha_gcc_4.X 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/makefile.linux_x86_ppc_alpha_gcc_4.X 2009-10-28 03:54:47.000000000 +0300
+@@ -14,7 +14,7 @@
+ CC_SHARED=-fPIC
+ LINK_SHARED=-fPIC -shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ OBJ_CRC32=$(OBJ_CRC32_C)
+diff -dPNur p7zip-9.04~dfsg.1/makefile.machine p7zip-9.04~dfsg.1-ds/makefile.machine
+--- p7zip-9.04~dfsg.1/makefile.machine 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/makefile.machine 2009-10-28 03:29:24.000000000 +0300
+@@ -14,7 +14,7 @@
+ CC_SHARED=-fPIC
+ LINK_SHARED=-fPIC -shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ OBJ_CRC32=$(OBJ_CRC32_C)
+diff -dPNur p7zip-9.04~dfsg.1/makefile.rules p7zip-9.04~dfsg.1-ds/makefile.rules
+--- p7zip-9.04~dfsg.1/makefile.rules 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/makefile.rules 2009-10-28 03:29:24.000000000 +0300
+@@ -586,3 +586,5 @@
+ 7zCrcT8.o : ../../../../C/7zCrcT8.c
+ $(CC) $(CFLAGS) ../../../../C/7zCrcT8.c
+
++rccrecode.o : ../../../../C/rccrecode.c
++ $(CC) $(CFLAGS) ../../../../C/rccrecode.c
+diff -dPNur p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipOut.cpp p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipOut.cpp
+--- p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipOut.cpp 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipOut.cpp 2009-10-28 05:49:01.000000000 +0300
+@@ -7,9 +7,24 @@
+ #include "../../Common/OffsetStream.h"
+ #include "../../Common/StreamUtils.h"
+
++#include "../../../../C/rccrecode.h"
++
+ namespace NArchive {
+ namespace NZip {
+
++COutArchive::COutArchive() {
++ rccctx = rcc_init();
++}
++
++COutArchive::~COutArchive() {
++ rcc_free(rccctx);
++}
++
++void COutArchive::Recode(CItem &item) {
++ char *rccrec = rcc_write(rccctx, (const char *)item.Name, item.Name.Length());
++ if (rccrec) item.Name = rccrec;
++}
++
+ void COutArchive::Create(IOutStream *outStream)
+ {
+ if (!m_OutBuffer.Create(1 << 16))
diff --git a/patches/p7zip/p7zip_9.04-ds-rusxmms.patch b/patches/p7zip/p7zip_9.04-ds-rusxmms.patch
new file mode 100644
index 0000000..7678241
--- /dev/null
+++ b/patches/p7zip/p7zip_9.04-ds-rusxmms.patch
@@ -0,0 +1,294 @@
+diff -dPNur p7zip-9.04~dfsg.1/C/rccrecode.c p7zip-9.04~dfsg.1-ds/C/rccrecode.c
+--- p7zip-9.04~dfsg.1/C/rccrecode.c 1970-01-01 03:00:00.000000000 +0300
++++ p7zip-9.04~dfsg.1-ds/C/rccrecode.c 2009-10-28 03:29:23.000000000 +0300
+@@ -0,0 +1,71 @@
++#include <pthread.h>
++#include <librcc.h>
++
++static rcc_class_default_charset default_oem[] = {
++ { "ru", "IBM866" },
++ { NULL, NULL }
++};
++
++static rcc_class_default_charset default_iso[] = {
++ { "ru", "CP1251" },
++ { NULL, NULL }
++};
++
++#define ARC_CLASS 0
++#define OUT_CLASS 1
++#define ARCOUT_CLASS 0
++static rcc_class classes[] = {
++ { "oem", RCC_CLASS_STANDARD, NULL, default_oem, "OEM Encoding", 0 },
++ { "out", RCC_CLASS_STANDARD, "LC_CTYPE", NULL, "Output", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int initialized = 0;
++static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
++
++void *rcc_init() {
++ rcc_context ctx;
++
++ pthread_mutex_lock(&mutex);
++ if (!initialized) {
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "zip");
++ rccInitDb4(NULL, NULL, 0);
++ }
++ initialized++;
++ pthread_mutex_unlock(&mutex);
++
++ ctx = rccCreateContext(NULL, 0, 0, classes, 0);
++ if (ctx) rccInitDb4(ctx, NULL, 0);
++ if (ctx) rccLoad(ctx, "zip");
++
++ return ctx;
++}
++
++
++void rcc_free(void *ctx) {
++ if (ctx) rccFreeContext((rcc_context)ctx);
++
++ pthread_mutex_lock(&mutex);
++ if (initialized == 1) rccFree();
++ initialized--;
++ pthread_mutex_unlock(&mutex);
++}
++
++
++char *rcc_read(void *ctx, const char *string, size_t size) {
++ if (!initialized) {
++ rcc_init();
++ if (!initialized) return NULL;
++ }
++ return rccSizedRecode((rcc_context)ctx, ARC_CLASS, OUT_CLASS, string, size, NULL);
++}
++
++char *rcc_write(rcc_context ctx, const char *string, size_t size) {
++ if (!initialized) {
++ rcc_init();
++ if (!initialized) return NULL;
++ }
++ return rccSizedRecode((rcc_context)ctx, OUT_CLASS, ARCOUT_CLASS, string, size, NULL);
++}
+diff -dPNur p7zip-9.04~dfsg.1/C/rccrecode.h p7zip-9.04~dfsg.1-ds/C/rccrecode.h
+--- p7zip-9.04~dfsg.1/C/rccrecode.h 1970-01-01 03:00:00.000000000 +0300
++++ p7zip-9.04~dfsg.1-ds/C/rccrecode.h 2009-10-28 03:29:23.000000000 +0300
+@@ -0,0 +1,17 @@
++#ifndef _RCC_RECODE_H
++#define _RCC_RECODE_H
++
++# ifdef __cplusplus
++extern "C" {
++# endif
++
++ void *rcc_init();
++ void rcc_free(void *ctx);
++ char *rcc_read(void *ctx, const char *string, size_t size);
++ char *rcc_write(void *ctx, const char *string, size_t size);
++
++# ifdef __cplusplus
++}
++# endif
++
++#endif /* _RCC_RECODE_H */
+diff -dPNur p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipIn.cpp p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipIn.cpp
+--- p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipIn.cpp 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipIn.cpp 2009-10-28 03:49:06.000000000 +0300
+@@ -9,6 +9,8 @@
+ #include "../../Common/LimitedStreams.h"
+ #include "../../Common/StreamUtils.h"
+
++#include "../../../../C/rccrecode.h"
++
+ #include "ZipIn.h"
+
+ #define Get16(p) GetUi16(p)
+@@ -17,7 +19,17 @@
+
+ namespace NArchive {
+ namespace NZip {
+-
++
++CInArchive::CInArchive()
++{
++ rccctx = rcc_init();
++}
++
++CInArchive::~CInArchive()
++{
++ rcc_free(rccctx);
++}
++
+ HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
+ {
+ _inBufMode = false;
+@@ -200,12 +212,20 @@
+
+ void CInArchive::ReadFileName(UInt32 nameSize, AString &dest)
+ {
++ char *rccrec;
++
+ if (nameSize == 0)
+ dest.Empty();
+ char *p = dest.GetBuffer((int)nameSize);
+ SafeReadBytes(p, nameSize);
+ p[nameSize] = 0;
+ dest.ReleaseBuffer();
++
++ rccrec = rcc_read(rccctx, (LPCSTR)dest, 0);
++ if (rccrec) {
++ dest = rccrec;
++ free(rccrec);
++ }
+ }
+
+ void CInArchive::GetArchiveInfo(CInArchiveInfo &archiveInfo) const
+diff -dPNur p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipIn.h p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipIn.h
+--- p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipIn.h 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipIn.h 2009-10-28 03:29:23.000000000 +0300
+@@ -114,6 +114,10 @@
+ ISequentialInStream *CreateLimitedStream(UInt64 position, UInt64 size);
+ IInStream* CreateStream();
+
++ void *rccctx;
++ CInArchive();
++ ~CInArchive();
++
+ bool IsOpen() const { return m_Stream != NULL; }
+ };
+
+diff -dPNur p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipOut.h p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipOut.h
+--- p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipOut.h 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipOut.h 2009-10-28 05:49:05.000000000 +0300
+@@ -49,6 +49,11 @@
+ void CreateStreamForCompressing(IOutStream **outStream);
+ void CreateStreamForCopying(ISequentialOutStream **outStream);
+ void SeekToPackedDataPosition();
++
++ void *rccctx;
++ COutArchive();
++ ~COutArchive();
++ void Recode(CItem &item);
+ };
+
+ }}
+diff -dPNur p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipUpdate.cpp p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipUpdate.cpp
+--- p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipUpdate.cpp 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipUpdate.cpp 2009-10-28 05:48:04.000000000 +0300
+@@ -87,6 +87,7 @@
+ item.NtfsATime = ui.NtfsATime;
+ item.NtfsCTime = ui.NtfsCTime;
+ item.NtfsTimeIsDefined = ui.NtfsTimeIsDefined;
++ archive.Recode(item);
+ }
+ else
+ isDir = item.IsDir();
+@@ -359,9 +360,11 @@
+ item.NtfsCTime = ui.NtfsCTime;
+ item.NtfsTimeIsDefined = ui.NtfsTimeIsDefined;
+
++ archive.Recode(item);
++
+ item.CentralExtra.RemoveUnknownSubBlocks();
+ item.LocalExtra.RemoveUnknownSubBlocks();
+-
++
+ archive.PrepareWriteCompressedData2((UInt16)item.Name.Length(), item.UnPackSize, item.PackSize, item.LocalExtra.HasWzAesField());
+ item.LocalHeaderPosition = archive.GetCurrentPosition();
+ archive.SeekToPackedDataPosition();
+diff -dPNur p7zip-9.04~dfsg.1/CPP/7zip/Bundles/Alone/makefile p7zip-9.04~dfsg.1-ds/CPP/7zip/Bundles/Alone/makefile
+--- p7zip-9.04~dfsg.1/CPP/7zip/Bundles/Alone/makefile 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/CPP/7zip/Bundles/Alone/makefile 2009-10-28 03:29:24.000000000 +0300
+@@ -244,6 +244,7 @@
+
+
+ OBJS=\
++rccrecode.o \
+ myGetTickCount.o \
+ wine_date_and_time.o \
+ myAddExeFlag.o \
+diff -dPNur p7zip-9.04~dfsg.1/CPP/7zip/Bundles/Alone/makefile.list p7zip-9.04~dfsg.1-ds/CPP/7zip/Bundles/Alone/makefile.list
+--- p7zip-9.04~dfsg.1/CPP/7zip/Bundles/Alone/makefile.list 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/CPP/7zip/Bundles/Alone/makefile.list 2009-10-28 03:40:17.000000000 +0300
+@@ -192,6 +192,7 @@
+ ../../Crypto/ZipStrong.cpp
+
+ SRCS_C=\
++ ../../../../C/rccrecode.c \
+ ../../../../C/Aes.c \
+ ../../../../C/7zStream.c \
+ ../../../../C/Bra.c \
+diff -dPNur p7zip-9.04~dfsg.1/CPP/7zip/Bundles/Format7zFree/makefile p7zip-9.04~dfsg.1-ds/CPP/7zip/Bundles/Format7zFree/makefile
+--- p7zip-9.04~dfsg.1/CPP/7zip/Bundles/Format7zFree/makefile 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/CPP/7zip/Bundles/Format7zFree/makefile 2009-10-28 03:29:24.000000000 +0300
+@@ -284,6 +284,7 @@
+
+
+ OBJS = \
++ rccrecode.o \
+ wine_date_and_time.o \
+ myGetTickCount.o \
+ $(COMMON_OBJS) \
+diff -dPNur p7zip-9.04~dfsg.1/CPP/7zip/Bundles/Format7zFree/makefile.list p7zip-9.04~dfsg.1-ds/CPP/7zip/Bundles/Format7zFree/makefile.list
+--- p7zip-9.04~dfsg.1/CPP/7zip/Bundles/Format7zFree/makefile.list 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/CPP/7zip/Bundles/Format7zFree/makefile.list 2009-10-28 03:34:02.000000000 +0300
+@@ -218,6 +218,7 @@
+ ../../Crypto/ZipStrong.cpp
+
+ SRCS_C=\
++ ../../../../C/rccrecode.c \
+ ../../../../C/7zBuf2.c \
+ ../../../../C/7zStream.c \
+ ../../../../C/Aes.c \
+diff -dPNur p7zip-9.04~dfsg.1/makefile.machine p7zip-9.04~dfsg.1-ds/makefile.machine
+--- p7zip-9.04~dfsg.1/makefile.machine 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/makefile.machine 2009-10-28 03:29:24.000000000 +0300
+@@ -14,7 +14,7 @@
+ CC_SHARED=-fPIC
+ LINK_SHARED=-fPIC -shared
+
+-LOCAL_LIBS=-lpthread
++LOCAL_LIBS=-lpthread -lrcc
+ LOCAL_LIBS_DLL=$(LOCAL_LIBS) -ldl
+
+ OBJ_CRC32=$(OBJ_CRC32_C)
+diff -dPNur p7zip-9.04~dfsg.1/makefile.rules p7zip-9.04~dfsg.1-ds/makefile.rules
+--- p7zip-9.04~dfsg.1/makefile.rules 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/makefile.rules 2009-10-28 03:29:24.000000000 +0300
+@@ -586,3 +586,5 @@
+ 7zCrcT8.o : ../../../../C/7zCrcT8.c
+ $(CC) $(CFLAGS) ../../../../C/7zCrcT8.c
+
++rccrecode.o : ../../../../C/rccrecode.c
++ $(CC) $(CFLAGS) ../../../../C/rccrecode.c
+diff -dPNur p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipOut.cpp p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipOut.cpp
+--- p7zip-9.04~dfsg.1/CPP/7zip/Archive/Zip/ZipOut.cpp 2009-06-27 18:18:14.000000000 +0400
++++ p7zip-9.04~dfsg.1-ds/CPP/7zip/Archive/Zip/ZipOut.cpp 2009-10-28 05:49:01.000000000 +0300
+@@ -7,9 +7,24 @@
+ #include "../../Common/OffsetStream.h"
+ #include "../../Common/StreamUtils.h"
+
++#include "../../../../C/rccrecode.h"
++
+ namespace NArchive {
+ namespace NZip {
+
++COutArchive::COutArchive() {
++ rccctx = rcc_init();
++}
++
++COutArchive::~COutArchive() {
++ rcc_free(rccctx);
++}
++
++void COutArchive::Recode(CItem &item) {
++ char *rccrec = rcc_write(rccctx, (const char *)item.Name, item.Name.Length());
++ if (rccrec) item.Name = rccrec;
++}
++
+ void COutArchive::Create(IOutStream *outStream)
+ {
+ if (!m_OutBuffer.Create(1 << 16))
diff --git a/patches/taglib/README b/patches/taglib/README
new file mode 100644
index 0000000..0d3e78e
--- /dev/null
+++ b/patches/taglib/README
@@ -0,0 +1,36 @@
+taglib-*-ds-rusxmms-enforce.patch will prevent applications from
+overloading taglib ID3 v.1 parser/renderer. For instance, kid3
+has it's onw recoder and bypasses RusXMMS unless this patch is
+applied.
+
+If you want to set unicode ID3v.2 titles, please, set in the
+LibRCC configuration (xmms.xml) "id3v2" class to point appropriate
+unicode encoding.
+
+1. Some Info Encoding Handling
+1.1 Reading encodings
+ - Unicode ID3 v.2 tags are read as defined by ID3V2 tag, no RusXMMS
+ is envolved.
+ - Latin1 ID3 v.2 and ID3 v.1 tags are read using RusXMMS library.
+ The 'id3' and 'id3v2' classes are used corespondingly.
+1.2 Writing ID3 v.1 encodings
+ - ID3 v.1 tag is written using RusXMMS library. The 'id3' class
+ selects output encoding.
+1.3 Writting ID3 v.2 encodings
+ - ID3 v.2 tag output encodings is selected with help of 'id3v2' class of
+ RusXMMS.
+ - If unicode encoding is set (UTF-8, UTF-16, UTF-16BE, UTF-16LE)
+ the tag is written using selected encoding (independent of original one).
+ - Otherwise, original unicode encodings are kept.
+ - If original encoding is also non-unicode, than the tag is stored
+ in 'id3v2' class encoding. However, it is really a bad idea! Set an
+ unicode encoding.
+2. Recomended class settings
+ - id3 = CP1251
+ - id3v2 = UTF-16
+3. Notes
+ - TagLib supports UTF-16LE encodings within ID3 v.2 tags (described by
+ number 4). The UTF-16LE is not part of standard and probably added for
+ compatibility with some nasty Windows applications. RusXMMS will not
+ allow to write tags in UTF-16LE.
+ \ No newline at end of file
diff --git a/patches/taglib/taglib-1.10-ds-rusxmms.patch b/patches/taglib/taglib-1.10-ds-rusxmms.patch
new file mode 100644
index 0000000..584c237
--- /dev/null
+++ b/patches/taglib/taglib-1.10-ds-rusxmms.patch
@@ -0,0 +1,1339 @@
+diff -dPNur taglib-1.10/config.h.cmake taglib-1.10-ds/config.h.cmake
+--- taglib-1.10/config.h.cmake 2015-11-11 22:41:59.000000000 +0100
++++ taglib-1.10-ds/config.h.cmake 2015-11-26 23:03:07.058061207 +0100
+@@ -25,4 +25,7 @@
+ /* Indicates whether debug messages are shown even in release mode */
+ #cmakedefine TRACE_IN_RELEASE 1
+
++/* Defined if you have LibRCC from RusXMMS project */
++#cmakedefine HAVE_LIBRCC 1
++
+ #cmakedefine TESTS_DIR "@TESTS_DIR@"
+diff -dPNur taglib-1.10/ConfigureChecks.cmake taglib-1.10-ds/ConfigureChecks.cmake
+--- taglib-1.10/ConfigureChecks.cmake 2015-11-11 22:41:59.000000000 +0100
++++ taglib-1.10-ds/ConfigureChecks.cmake 2015-11-26 23:03:07.058061207 +0100
+@@ -201,6 +201,8 @@
+ endif()
+ endif()
+
++SET(HAVE_LIBRCC 1)
++
+ if(BUILD_TESTS)
+ find_package(CppUnit)
+ if(NOT CppUnit_FOUND)
+diff -dPNur taglib-1.10/examples/tagreader_c.c taglib-1.10-ds/examples/tagreader_c.c
+--- taglib-1.10/examples/tagreader_c.c 2015-11-11 22:41:59.000000000 +0100
++++ taglib-1.10-ds/examples/tagreader_c.c 2015-11-26 23:03:07.059061228 +0100
+@@ -38,7 +38,7 @@
+ TagLib_Tag *tag;
+ const TagLib_AudioProperties *properties;
+
+- taglib_set_strings_unicode(FALSE);
++ //taglib_set_strings_unicode(FALSE);
+
+ for(i = 1; i < argc; i++) {
+ printf("******************** \"%s\" ********************\n", argv[i]);
+diff -dPNur taglib-1.10/examples/tagwriter.cpp taglib-1.10-ds/examples/tagwriter.cpp
+--- taglib-1.10/examples/tagwriter.cpp 2015-11-11 22:41:59.000000000 +0100
++++ taglib-1.10-ds/examples/tagwriter.cpp 2015-11-26 23:03:07.059061228 +0100
+@@ -115,7 +115,7 @@
+ if(isArgument(argv[i]) && i + 1 < argc && !isArgument(argv[i + 1])) {
+
+ char field = argv[i][1];
+- TagLib::String value = argv[i + 1];
++ TagLib::String value(argv[i + 1], TagLib::String::Locale);
+
+ TagLib::List<TagLib::FileRef>::ConstIterator it;
+ for(it = fileList.begin(); it != fileList.end(); ++it) {
+diff -dPNur taglib-1.10/taglib/CMakeLists.txt taglib-1.10-ds/taglib/CMakeLists.txt
+--- taglib-1.10/taglib/CMakeLists.txt 2015-11-11 22:41:59.000000000 +0100
++++ taglib-1.10-ds/taglib/CMakeLists.txt 2015-11-26 23:03:07.059061228 +0100
+@@ -38,6 +38,7 @@
+ audioproperties.h
+ taglib_export.h
+ ${CMAKE_CURRENT_BINARY_DIR}/../taglib_config.h
++ toolkit/rccpatch.h
+ toolkit/taglib.h
+ toolkit/tstring.h
+ toolkit/tlist.h
+@@ -291,6 +292,7 @@
+ )
+
+ set(toolkit_SRCS
++ toolkit/rccpatch.cpp
+ toolkit/tstring.cpp
+ toolkit/tstringlist.cpp
+ toolkit/tbytevector.cpp
+@@ -337,7 +339,7 @@
+ add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
+
+ if(ZLIB_FOUND)
+- target_link_libraries(tag ${ZLIB_LIBRARIES})
++ target_link_libraries(tag rcc ${ZLIB_LIBRARIES})
+ endif()
+
+ set_target_properties(tag PROPERTIES
+diff -dPNur taglib-1.10/taglib/mpeg/id3v1/id3v1tag.cpp taglib-1.10-ds/taglib/mpeg/id3v1/id3v1tag.cpp
+--- taglib-1.10/taglib/mpeg/id3v1/id3v1tag.cpp 2015-11-11 22:41:59.000000000 +0100
++++ taglib-1.10-ds/taglib/mpeg/id3v1/id3v1tag.cpp 2015-11-26 23:03:07.059061228 +0100
+@@ -64,17 +64,18 @@
+
+ String ID3v1::StringHandler::parse(const ByteVector &data) const
+ {
+- return String(data, String::Latin1).stripWhiteSpace();
++ return String(data, String::Latin1ID3).stripWhiteSpace();
+ }
+
+ ByteVector ID3v1::StringHandler::render(const String &s) const
+ {
+ if(!s.isLatin1())
+ {
++ if (String::ID3WType(String::Latin1) == String::Latin1)
+ return ByteVector();
+ }
+
+- return s.data(String::Latin1);
++ return s.data(String::Latin1ID3);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+@@ -257,7 +258,7 @@
+ d->track = uchar(data[offset + 29]);
+ }
+ else
+- d->comment = data.mid(offset, 30);
++ d->comment = TagPrivate::stringHandler->parse(data.mid(offset, 30));
+
+ offset += 30;
+
+diff -dPNur taglib-1.10/taglib/mpeg/id3v2/frames/commentsframe.cpp taglib-1.10-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp
+--- taglib-1.10/taglib/mpeg/id3v2/frames/commentsframe.cpp 2015-11-11 22:41:59.000000000 +0100
++++ taglib-1.10-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp 2015-11-26 23:03:07.059061228 +0100
+@@ -150,10 +150,10 @@
+ return;
+ }
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+ d->language = data.mid(1, 3);
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
+
+@@ -173,11 +173,13 @@
+ ByteVector v;
+
+ String::Type encoding = d->textEncoding;
++
++ encoding = String::ID3WType(encoding);
+
+ encoding = checkTextEncoding(d->description, encoding);
+ encoding = checkTextEncoding(d->text, encoding);
+
+- v.append(char(encoding));
++ v.append(char(String::ID3RealType(encoding)));
+ v.append(d->language.size() == 3 ? d->language : "XXX");
+ v.append(d->description.data(encoding));
+ v.append(textDelimiter(encoding));
+diff -dPNur taglib-1.10/taglib/mpeg/id3v2/frames/textidentificationframe.cpp taglib-1.10-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp
+--- taglib-1.10/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2015-11-11 22:41:59.000000000 +0100
++++ taglib-1.10-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2015-11-26 23:03:07.059061228 +0100
+@@ -187,12 +187,12 @@
+
+ // read the string data type (the first byte of the field data)
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+
+ // split the byte array into chunks based on the string type (two byte delimiter
+ // for unicode encodings)
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ // build a small counter to strip nulls off the end of the field
+
+@@ -223,11 +223,14 @@
+
+ ByteVector TextIdentificationFrame::renderFields() const
+ {
+- String::Type encoding = checkTextEncoding(d->fieldList, d->textEncoding);
++ String::Type encoding = d->textEncoding;
++
++ encoding = String::ID3WType(encoding);
++ encoding = checkTextEncoding(d->fieldList, encoding);
+
+ ByteVector v;
+
+- v.append(char(encoding));
++ v.append(char(String::ID3RealType(encoding)));
+
+ for(StringList::ConstIterator it = d->fieldList.begin(); it != d->fieldList.end(); it++) {
+
+diff -dPNur taglib-1.10/taglib/mpeg/id3v2/id3v2frame.cpp taglib-1.10-ds/taglib/mpeg/id3v2/id3v2frame.cpp
+--- taglib-1.10/taglib/mpeg/id3v2/id3v2frame.cpp 2015-11-11 22:41:59.000000000 +0100
++++ taglib-1.10-ds/taglib/mpeg/id3v2/id3v2frame.cpp 2015-11-26 23:03:07.060061249 +0100
+@@ -339,7 +339,7 @@
+ if((encoding == String::UTF8 || encoding == String::UTF16BE) && version != 4)
+ return String::UTF16;
+
+- if(encoding != String::Latin1)
++ if((encoding != String::Latin1)&&(encoding != String::Latin1ID3V2))
+ return encoding;
+
+ for(StringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) {
+diff -dPNur taglib-1.10/taglib/toolkit/rccpatch.cpp taglib-1.10-ds/taglib/toolkit/rccpatch.cpp
+--- taglib-1.10/taglib/toolkit/rccpatch.cpp 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.10-ds/taglib/toolkit/rccpatch.cpp 2015-11-26 23:03:07.060061249 +0100
+@@ -0,0 +1,237 @@
++#include <stdlib.h>
++
++#include <string>
++#include "tstring.h"
++#include "tbytevector.h"
++
++//#define RCC_DEBUG
++
++
++#ifndef HAVE_LIBRCC
++# include <config.h>
++#endif
++
++#ifdef HAVE_LIBRCC
++# ifdef RCC_DEBUG
++# include <stdio.h>
++# endif /* RCC_DEBUG */
++# include <librcc.h>
++# include <string.h>
++#endif /* HAVE_LIBRCC */
++
++
++#ifdef HAVE_LIBRCC
++# define ID3_CLASS 0
++# define ID3V2_CLASS 1
++# define UTF_CLASS 2
++# define OUT_CLASS 3
++static rcc_class classes[] = {
++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
++ { "id3v2", RCC_CLASS_STANDARD, "id3", NULL, "ID3 v.2 Encoding", 0 },
++ { "utf", RCC_CLASS_KNOWN, "UTF-8", NULL, "Unicode Encoding", 0},
++ { "out", RCC_CLASS_TRANSLATE_LOCALE, "LC_CTYPE", NULL, "Output Encoding", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int rcc_initialized = 0;
++
++static rcc_context ctx = NULL;
++#endif /* HAVE_LIBRCC */
++
++
++void rccTaglibPatchFree() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) {
++ rccFree();
++ rcc_initialized = 0;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++void rccTaglibPatchInit() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) return;
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "xmms");
++ rccInitDb4(NULL, NULL, 0);
++ rcc_initialized = 1;
++#endif /* HAVE_LIBRCC */
++}
++
++void rccTaglibPatchSetContext(void *newctx) {
++#ifdef HAVE_LIBRCC
++ if (newctx) {
++ ctx = (rcc_context)newctx;
++ rcc_initialized = 1;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++static void rccTaglibPatchTryInit() {
++#ifdef HAVE_LIBRCC
++ if (!rcc_initialized) {
++ rccTaglibPatchInit();
++ if (rcc_initialized) atexit(rccTaglibPatchFree);
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++
++TagLib::ByteVector rccTaglibPatchRecodeOutput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccTaglibPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, OUT_CLASS, s.c_str(), s.length(), &rlen);
++#ifdef RCC_DEBUG
++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
++ if (*c > 127) {
++ printf(" Output: %s - %s\n", s.c_str(), res?res:"null");
++ break;
++ }
++ }
++#endif /* RCC_DEBUG */
++
++ if (res) v.setData(res, rlen);
++ else v.setData("", 0);
++ //v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccTaglibPatchRecodeOutputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccTaglibPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, v2?ID3V2_CLASS:ID3_CLASS, s.c_str(), s.length(), &rlen);
++#ifdef RCC_DEBUG
++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
++ if (*c > 127) {
++ printf(" OutputID3(%i): %s - %s\n", v2, s.c_str(), res?res:"null");
++ break;
++ }
++ }
++#endif /* RCC_DEBUG */
++
++ if (res) v.setData(res, rlen);
++ else v.setData("", 0);
++ //v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccTaglibPatchRecodeInput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccTaglibPatchTryInit();
++
++ res = rccSizedRecode(ctx, OUT_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++#ifdef RCC_DEBUG
++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
++ if (*c > 127) {
++ printf(" Input: %s - %s\n", s.c_str(), res?res:"null");
++ break;
++ }
++ }
++#endif /* RCC_DEBUG */
++
++ if (res) v.setData(res, rlen);
++ else
++#endif /* HAVE_LIBRCC */
++ v.setData("", 0);
++
++ return v;
++}
++
++TagLib::ByteVector rccTaglibPatchRecodeInputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccTaglibPatchTryInit();
++
++ res = rccSizedRecode(ctx, v2?ID3V2_CLASS:ID3_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++#ifdef RCC_DEBUG
++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
++ if (*c > 127) {
++ printf(" InputID3(%i): %s - %s\n", v2, s.c_str(), res?res:"null");
++ break;
++ }
++ }
++#endif /* RCC_DEBUG */
++ if (res) v.setData(res, rlen + 1);
++ else
++#endif /* HAVE_LIBRCC */
++ v.setData("", 0);
++
++ return v;
++}
++
++TagLib::String::Type rccTaglibPatchGetLocaleType() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ char charset[32];
++
++ rccTaglibPatchTryInit();
++ if (!rccLocaleGetCharset(charset, NULL, 31)) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::UTF8;
++}
++
++TagLib::String::Type rccTaglibPatchGetID3Type() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ const char *charset;
++
++ rccTaglibPatchTryInit();
++
++ charset = rccGetCurrentCharsetName(ctx, ID3V2_CLASS);
++ if (charset) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1ID3V2;
++ } else {
++ // Error or no-language configured: If Latin1ID3V2 is returned we normally will use the default unicode encoding unless Latin1 is selected by taglib
++ return TagLib::String::Latin1ID3V2;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::Latin1;
++}
+diff -dPNur taglib-1.10/taglib/toolkit/rccpatch.h taglib-1.10-ds/taglib/toolkit/rccpatch.h
+--- taglib-1.10/taglib/toolkit/rccpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.10-ds/taglib/toolkit/rccpatch.h 2015-11-26 23:03:07.060061249 +0100
+@@ -0,0 +1,20 @@
++#ifndef _RCC_PATCH_H
++#define _RCC_PATCH_H
++
++#include <string.h>
++#include "tstring.h"
++#include "tbytevector.h"
++
++void rccTaglibPatchFree();
++void rccTaglibPatchInit();
++void rccTaglibPatchSetContext(void *newctx);
++
++TagLib::ByteVector rccTaglibPatchRecodeOutput(const std::string &s);
++TagLib::ByteVector rccTaglibPatchRecodeInput(const std::string &s);
++TagLib::ByteVector rccTaglibPatchRecodeOutputID3(const std::string &s, bool v2 = false);
++TagLib::ByteVector rccTaglibPatchRecodeInputID3(const std::string &s, bool v2 = false);
++
++TagLib::String::Type rccTaglibPatchGetLocaleType();
++TagLib::String::Type rccTaglibPatchGetID3Type();
++
++#endif /* _RCC_PATCH_H */
+diff -dPNur taglib-1.10/taglib/toolkit/tstring.cpp taglib-1.10-ds/taglib/toolkit/tstring.cpp
+--- taglib-1.10/taglib/toolkit/tstring.cpp 2015-11-11 22:41:59.000000000 +0100
++++ taglib-1.10-ds/taglib/toolkit/tstring.cpp 2015-11-26 23:10:01.286853654 +0100
+@@ -29,6 +29,7 @@
+ #include <config.h>
+ #endif
+
++#include "rccpatch.h"
+ #include "tstring.h"
+ #include "tdebug.h"
+ #include "tstringlist.h"
+@@ -167,8 +168,11 @@
+ String::String(const std::string &s, Type t)
+ : d(new StringPrivate())
+ {
+- if(t == Latin1)
+- copyFromLatin1(s.c_str(), s.length());
++ if(t == Locale)
++ t = rccTaglibPatchGetLocaleType();
++
++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2)
++ copyFromLatin1(&s[0], s.length(), true, t);
+ else if(t == String::UTF8)
+ copyFromUTF8(s.c_str(), s.length());
+ else {
+@@ -215,8 +219,11 @@
+ String::String(const char *s, Type t)
+ : d(new StringPrivate())
+ {
+- if(t == Latin1)
+- copyFromLatin1(s, ::strlen(s));
++ if(t == Locale)
++ t = rccTaglibPatchGetLocaleType();
++
++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2)
++ copyFromLatin1(s, ::strlen(s), true, t);
+ else if(t == String::UTF8)
+ copyFromUTF8(s, ::strlen(s));
+ else {
+@@ -237,7 +244,10 @@
+ String::String(char c, Type t)
+ : d(new StringPrivate(1, static_cast<uchar>(c)))
+ {
+- if(t != Latin1 && t != UTF8) {
++ if(t == Locale)
++ t = rccTaglibPatchGetLocaleType();
++
++ if(t != Latin1 && t != Latin1ID3 && t != Latin1ID3V2 && t != UTF8) {
+ debug("String::String() -- char should not contain UTF16.");
+ }
+ }
+@@ -248,8 +258,11 @@
+ if(v.isEmpty())
+ return;
+
+- if(t == Latin1)
+- copyFromLatin1(v.data(), v.size());
++ if(t == Locale)
++ t = rccTaglibPatchGetLocaleType();
++
++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2)
++ copyFromLatin1(v.data(), v.size(), true, t);
+ else if(t == UTF8)
+ copyFromUTF8(v.data(), v.size());
+ else
+@@ -396,8 +409,38 @@
+
+ ByteVector String::data(Type t) const
+ {
+- switch(t)
+- {
++ ByteVector v;
++
++ if (t == Locale) {
++ // The source is either Unicode or real Latin1 (if rcc is bypassed)
++ std::string s = to8Bit(true);
++
++ // In case of UTF8 locale, this probably will return NULL (no recoding needed), but we will take UTF8 path in the next swtich
++ v = rccTaglibPatchRecodeOutput(s);
++ if (v.size()) return v;
++
++ t = rccTaglibPatchGetLocaleType();
++ }
++
++ switch(t) {
++ case Latin1ID3:
++ case Latin1ID3V2:
++ {
++ std::string s = to8Bit(true);
++ if (t == Latin1ID3) v = rccTaglibPatchRecodeOutputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccTaglibPatchRecodeOutputID3(s, true);
++ if (v.size())
++ return v;
++
++ // we don't know if we got NULL because rcc is disabled (error) or UTF8 output is required
++ if ((t == Latin1ID3V2)&&(rccTaglibPatchGetID3Type() == UTF8)) {
++ v.setData(s.c_str(), s.length());
++ } else {
++ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
++ v.append(char(*it));
++ }
++ return v;
++ }
+ case Latin1:
+ {
+ ByteVector v(size(), 0);
+@@ -738,12 +781,30 @@
+ // private members
+ ////////////////////////////////////////////////////////////////////////////////
+
+-void String::copyFromLatin1(const char *s, size_t length)
++void String::copyFromLatin1(const char *s, size_t length, bool prepare, Type t)
+ {
+ d->data.resize(length);
+
+ for(size_t i = 0; i < length; ++i)
+ d->data[i] = static_cast<uchar>(s[i]);
++
++ // librcc conversation
++ if (prepare) {
++ std::string s = to8Bit(false);
++ ByteVector v;
++
++ if (t == Latin1ID3) v = rccTaglibPatchRecodeInputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccTaglibPatchRecodeInputID3(s, true);
++ else /* Latin1 converted from Locale */ v = rccTaglibPatchRecodeInput(s);
++
++ if (v.size()) {
++ copyFromUTF8(v.data(), v.size());
++ } else {
++ // We don't know if we got UTF-8 encoded string or either rcc is disable or something is failed,
++ // since standard applications are really expecting here Latin1, it is safe to just check if we have violations of UTF8
++ //if (Unicode::isLegalUTF8(s)) t = UTF8;
++ }
++ }
+ }
+
+ void String::copyFromUTF8(const char *s, size_t length)
+@@ -859,7 +920,33 @@
+
+ std::ostream &operator<<(std::ostream &s, const TagLib::String &str)
+ {
+- s << str.to8Bit();
++ TagLib::ByteVector bv = str.data(TagLib::String::Locale);
++ s << bv;
+ return s;
+ }
+
++TagLib::String::Type TagLib::String::ID3Type(int i)
++{
++ if(i == Latin1)
++ return Latin1ID3V2;
++ return Type(i);
++};
++
++TagLib::String::Type TagLib::String::ID3WType(Type type)
++{
++ Type rcc_type = rccTaglibPatchGetID3Type();
++ if((rcc_type == Latin1ID3)||(rcc_type == Latin1ID3V2)||(rcc_type == Latin1)) {
++ if(type == Latin1) return
++ rcc_type;
++ return type;
++ }
++
++ return rcc_type;
++};
++
++TagLib::String::Type TagLib::String::ID3RealType(Type type)
++{
++ if((type == Latin1ID3) || (type == Latin1ID3V2))
++ return Latin1;
++ return type;
++}
+diff -dPNur taglib-1.10/taglib/toolkit/tstring.h taglib-1.10-ds/taglib/toolkit/tstring.h
+--- taglib-1.10/taglib/toolkit/tstring.h 2015-11-11 22:41:59.000000000 +0100
++++ taglib-1.10-ds/taglib/toolkit/tstring.h 2015-11-26 23:03:07.061061271 +0100
+@@ -96,6 +96,18 @@
+ */
+ enum Type {
+ /*!
++ * Determine using current locale settings
++ */
++ Locale = -1,
++ /*!
++ * Latin1 for ID3 tags.
++ */
++ Latin1ID3 = 65,
++ /*!
++ * Latin1 for ID3v2 tags.
++ */
++ Latin1ID3V2 = 66,
++ /*!
+ * IS08859-1, or <i>Latin1</i> encoding. 8 bit characters.
+ */
+ Latin1 = 0,
+@@ -117,6 +129,10 @@
+ */
+ UTF16LE = 4
+ };
++
++ static Type ID3Type(int i);
++ static Type ID3WType(Type type);
++ static Type ID3RealType(Type type);
+
+ /*!
+ * Constructs an empty String.
+@@ -519,7 +535,7 @@
+ * Converts a \e Latin-1 string into \e UTF-16(without BOM/CPU byte order)
+ * and copies it to the internal buffer.
+ */
+- void copyFromLatin1(const char *s, size_t length);
++ void copyFromLatin1(const char *s, size_t length, bool prepare = false, Type t = Latin1);
+
+ /*!
+ * Converts a \e UTF-8 string into \e UTF-16(without BOM/CPU byte order)
+diff -dPNur taglib-1.10/taglib-1.10-ds-rusxmms.patch taglib-1.10-ds/taglib-1.10-ds-rusxmms.patch
+--- taglib-1.10/taglib-1.10-ds-rusxmms.patch 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.10-ds/taglib-1.10-ds-rusxmms.patch 2015-11-14 15:40:37.000000000 +0100
+@@ -0,0 +1,669 @@
++diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake
++index 88980ea..e4f44d3 100644
++--- a/ConfigureChecks.cmake
+++++ b/ConfigureChecks.cmake
++@@ -201,6 +201,8 @@ if(NOT ZLIB_SOURCE)
++ endif()
++ endif()
++
+++SET(HAVE_LIBRCC 1)
+++
++ if(BUILD_TESTS)
++ find_package(CppUnit)
++ if(NOT CppUnit_FOUND)
++diff --git a/config.h.cmake b/config.h.cmake
++index ee0e67a..2abd49c 100644
++--- a/config.h.cmake
+++++ b/config.h.cmake
++@@ -25,4 +25,7 @@
++ /* Indicates whether debug messages are shown even in release mode */
++ #cmakedefine TRACE_IN_RELEASE 1
++
+++/* Defined if you have LibRCC from RusXMMS project */
+++#cmakedefine HAVE_LIBRCC 1
+++
++ #cmakedefine TESTS_DIR "@TESTS_DIR@"
++diff --git a/examples/tagreader_c.c b/examples/tagreader_c.c
++index 0436992..e0f17d8 100644
++--- a/examples/tagreader_c.c
+++++ b/examples/tagreader_c.c
++@@ -38,7 +38,7 @@ int main(int argc, char *argv[])
++ TagLib_Tag *tag;
++ const TagLib_AudioProperties *properties;
++
++- taglib_set_strings_unicode(FALSE);
+++ //taglib_set_strings_unicode(FALSE);
++
++ for(i = 1; i < argc; i++) {
++ printf("******************** \"%s\" ********************\n", argv[i]);
++diff --git a/examples/tagwriter.cpp b/examples/tagwriter.cpp
++index ed8b0d7..6a7a263 100644
++--- a/examples/tagwriter.cpp
+++++ b/examples/tagwriter.cpp
++@@ -115,7 +115,7 @@ int main(int argc, char *argv[])
++ if(isArgument(argv[i]) && i + 1 < argc && !isArgument(argv[i + 1])) {
++
++ char field = argv[i][1];
++- TagLib::String value = argv[i + 1];
+++ TagLib::String value(argv[i + 1], TagLib::String::Locale);
++
++ TagLib::List<TagLib::FileRef>::ConstIterator it;
++ for(it = fileList.begin(); it != fileList.end(); ++it) {
++diff --git a/taglib/CMakeLists.txt b/taglib/CMakeLists.txt
++index 73c1a2f..cd7ea22 100644
++--- a/taglib/CMakeLists.txt
+++++ b/taglib/CMakeLists.txt
++@@ -38,6 +38,7 @@ set(tag_HDRS
++ audioproperties.h
++ taglib_export.h
++ ${CMAKE_CURRENT_BINARY_DIR}/../taglib_config.h
+++ toolkit/rccpatch.h
++ toolkit/taglib.h
++ toolkit/tstring.h
++ toolkit/tlist.h
++@@ -291,6 +292,7 @@ set(xm_SRCS
++ )
++
++ set(toolkit_SRCS
+++ toolkit/rccpatch.cpp
++ toolkit/tstring.cpp
++ toolkit/tstringlist.cpp
++ toolkit/tbytevector.cpp
++@@ -337,7 +339,7 @@ set(tag_LIB_SRCS
++ add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
++
++ if(ZLIB_FOUND)
++- target_link_libraries(tag ${ZLIB_LIBRARIES})
+++ target_link_libraries(tag rcc ${ZLIB_LIBRARIES})
++ endif()
++
++ set_target_properties(tag PROPERTIES
++diff --git a/taglib/mpeg/id3v1/id3v1tag.cpp b/taglib/mpeg/id3v1/id3v1tag.cpp
++index 9fc8cfd..5352acc 100644
++--- a/taglib/mpeg/id3v1/id3v1tag.cpp
+++++ b/taglib/mpeg/id3v1/id3v1tag.cpp
++@@ -64,17 +64,18 @@ StringHandler::StringHandler()
++
++ String ID3v1::StringHandler::parse(const ByteVector &data) const
++ {
++- return String(data, String::Latin1).stripWhiteSpace();
+++ return String(data, String::Latin1ID3).stripWhiteSpace();
++ }
++
++ ByteVector ID3v1::StringHandler::render(const String &s) const
++ {
++ if(!s.isLatin1())
++ {
+++ if (String::ID3WType(String::Latin1) == String::Latin1)
++ return ByteVector();
++ }
++
++- return s.data(String::Latin1);
+++ return s.data(String::Latin1ID3);
++ }
++
++ ////////////////////////////////////////////////////////////////////////////////
++@@ -257,7 +258,7 @@ void ID3v1::Tag::parse(const ByteVector &data)
++ d->track = uchar(data[offset + 29]);
++ }
++ else
++- d->comment = data.mid(offset, 30);
+++ d->comment = TagPrivate::stringHandler->parse(data.mid(offset, 30));
++
++ offset += 30;
++
++diff --git a/taglib/mpeg/id3v2/frames/commentsframe.cpp b/taglib/mpeg/id3v2/frames/commentsframe.cpp
++index deaa9d9..c0aba2b 100644
++--- a/taglib/mpeg/id3v2/frames/commentsframe.cpp
+++++ b/taglib/mpeg/id3v2/frames/commentsframe.cpp
++@@ -150,10 +150,10 @@ void CommentsFrame::parseFields(const ByteVector &data)
++ return;
++ }
++
++- d->textEncoding = String::Type(data[0]);
+++ d->textEncoding = String::ID3Type(data[0]);
++ d->language = data.mid(1, 3);
++
++- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
+++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
++
++ ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
++
++@@ -173,11 +173,13 @@ ByteVector CommentsFrame::renderFields() const
++ ByteVector v;
++
++ String::Type encoding = d->textEncoding;
+++
+++ encoding = String::ID3WType(encoding);
++
++ encoding = checkTextEncoding(d->description, encoding);
++ encoding = checkTextEncoding(d->text, encoding);
++
++- v.append(char(encoding));
+++ v.append(char(String::ID3RealType(encoding)));
++ v.append(d->language.size() == 3 ? d->language : "XXX");
++ v.append(d->description.data(encoding));
++ v.append(textDelimiter(encoding));
++diff --git a/taglib/mpeg/id3v2/frames/textidentificationframe.cpp b/taglib/mpeg/id3v2/frames/textidentificationframe.cpp
++index b77dd54..a4a7277 100644
++--- a/taglib/mpeg/id3v2/frames/textidentificationframe.cpp
+++++ b/taglib/mpeg/id3v2/frames/textidentificationframe.cpp
++@@ -187,12 +187,12 @@ void TextIdentificationFrame::parseFields(const ByteVector &data)
++
++ // read the string data type (the first byte of the field data)
++
++- d->textEncoding = String::Type(data[0]);
+++ d->textEncoding = String::ID3Type(data[0]);
++
++ // split the byte array into chunks based on the string type (two byte delimiter
++ // for unicode encodings)
++
++- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
+++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
++
++ // build a small counter to strip nulls off the end of the field
++
++@@ -223,11 +223,14 @@ void TextIdentificationFrame::parseFields(const ByteVector &data)
++
++ ByteVector TextIdentificationFrame::renderFields() const
++ {
++- String::Type encoding = checkTextEncoding(d->fieldList, d->textEncoding);
+++ String::Type encoding = d->textEncoding;
+++
+++ encoding = String::ID3WType(encoding);
+++ encoding = checkTextEncoding(d->fieldList, encoding);
++
++ ByteVector v;
++
++- v.append(char(encoding));
+++ v.append(char(String::ID3RealType(encoding)));
++
++ for(StringList::ConstIterator it = d->fieldList.begin(); it != d->fieldList.end(); it++) {
++
++diff --git a/taglib/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp
++index bee5375..fea5ab3 100644
++--- a/taglib/mpeg/id3v2/id3v2frame.cpp
+++++ b/taglib/mpeg/id3v2/id3v2frame.cpp
++@@ -339,7 +339,7 @@ String::Type Frame::checkEncoding(const StringList &fields, String::Type encodin
++ if((encoding == String::UTF8 || encoding == String::UTF16BE) && version != 4)
++ return String::UTF16;
++
++- if(encoding != String::Latin1)
+++ if((encoding != String::Latin1)&&(encoding != String::Latin1ID3V2))
++ return encoding;
++
++ for(StringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) {
++diff --git a/taglib/toolkit/rccpatch.cpp b/taglib/toolkit/rccpatch.cpp
++new file mode 100644
++index 0000000..af99323
++--- /dev/null
+++++ b/taglib/toolkit/rccpatch.cpp
++@@ -0,0 +1,237 @@
+++#include <stdlib.h>
+++
+++#include <string>
+++#include "tstring.h"
+++#include "tbytevector.h"
+++
+++//#define RCC_DEBUG
+++
+++
+++#ifndef HAVE_LIBRCC
+++# include <config.h>
+++#endif
+++
+++#ifdef HAVE_LIBRCC
+++# ifdef RCC_DEBUG
+++# include <stdio.h>
+++# endif /* RCC_DEBUG */
+++# include <librcc.h>
+++# include <string.h>
+++#endif /* HAVE_LIBRCC */
+++
+++
+++#ifdef HAVE_LIBRCC
+++# define ID3_CLASS 0
+++# define ID3V2_CLASS 1
+++# define UTF_CLASS 2
+++# define OUT_CLASS 3
+++static rcc_class classes[] = {
+++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
+++ { "id3v2", RCC_CLASS_STANDARD, "id3", NULL, "ID3 v.2 Encoding", 0 },
+++ { "utf", RCC_CLASS_KNOWN, "UTF-8", NULL, "Unicode Encoding", 0},
+++ { "out", RCC_CLASS_TRANSLATE_LOCALE, "LC_CTYPE", NULL, "Output Encoding", 0 },
+++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
+++};
+++
+++static int rcc_initialized = 0;
+++
+++static rcc_context ctx = NULL;
+++#endif /* HAVE_LIBRCC */
+++
+++
+++void rccTaglibPatchFree() {
+++#ifdef HAVE_LIBRCC
+++ if (rcc_initialized) {
+++ rccFree();
+++ rcc_initialized = 0;
+++ }
+++#endif /* HAVE_LIBRCC */
+++}
+++
+++void rccTaglibPatchInit() {
+++#ifdef HAVE_LIBRCC
+++ if (rcc_initialized) return;
+++ rccInit();
+++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
+++ rccLoad(NULL, "xmms");
+++ rccInitDb4(NULL, NULL, 0);
+++ rcc_initialized = 1;
+++#endif /* HAVE_LIBRCC */
+++}
+++
+++void rccTaglibPatchSetContext(void *newctx) {
+++#ifdef HAVE_LIBRCC
+++ if (newctx) {
+++ ctx = (rcc_context)newctx;
+++ rcc_initialized = 1;
+++ }
+++#endif /* HAVE_LIBRCC */
+++}
+++
+++static void rccTaglibPatchTryInit() {
+++#ifdef HAVE_LIBRCC
+++ if (!rcc_initialized) {
+++ rccTaglibPatchInit();
+++ if (rcc_initialized) atexit(rccTaglibPatchFree);
+++ }
+++#endif /* HAVE_LIBRCC */
+++}
+++
+++
+++TagLib::ByteVector rccTaglibPatchRecodeOutput(const std::string &s) {
+++ TagLib::ByteVector v;
+++#ifdef HAVE_LIBRCC
+++ size_t rlen;
+++ char *res;
+++
+++ rccTaglibPatchTryInit();
+++
+++ res = rccSizedRecode(ctx, UTF_CLASS, OUT_CLASS, s.c_str(), s.length(), &rlen);
+++#ifdef RCC_DEBUG
+++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
+++ if (*c > 127) {
+++ printf(" Output: %s - %s\n", s.c_str(), res?res:"null");
+++ break;
+++ }
+++ }
+++#endif /* RCC_DEBUG */
+++
+++ if (res) v.setData(res, rlen);
+++ else v.setData("", 0);
+++ //v.setData(s.c_str(), s.length());
+++
+++ return v;
+++#else
+++ v.setData("", 0);
+++
+++ return v;
+++#endif /* HAVE_LIBRCC */
+++}
+++
+++TagLib::ByteVector rccTaglibPatchRecodeOutputID3(const std::string &s, bool v2 = false) {
+++ TagLib::ByteVector v;
+++#ifdef HAVE_LIBRCC
+++ size_t rlen;
+++ char *res;
+++
+++ rccTaglibPatchTryInit();
+++
+++ res = rccSizedRecode(ctx, UTF_CLASS, v2?ID3V2_CLASS:ID3_CLASS, s.c_str(), s.length(), &rlen);
+++#ifdef RCC_DEBUG
+++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
+++ if (*c > 127) {
+++ printf(" OutputID3(%i): %s - %s\n", v2, s.c_str(), res?res:"null");
+++ break;
+++ }
+++ }
+++#endif /* RCC_DEBUG */
+++
+++ if (res) v.setData(res, rlen);
+++ else v.setData("", 0);
+++ //v.setData(s.c_str(), s.length());
+++
+++ return v;
+++#else
+++ v.setData("", 0);
+++
+++ return v;
+++#endif /* HAVE_LIBRCC */
+++}
+++
+++TagLib::ByteVector rccTaglibPatchRecodeInput(const std::string &s) {
+++ TagLib::ByteVector v;
+++#ifdef HAVE_LIBRCC
+++ size_t rlen;
+++ char *res;
+++
+++ rccTaglibPatchTryInit();
+++
+++ res = rccSizedRecode(ctx, OUT_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
+++#ifdef RCC_DEBUG
+++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
+++ if (*c > 127) {
+++ printf(" Input: %s - %s\n", s.c_str(), res?res:"null");
+++ break;
+++ }
+++ }
+++#endif /* RCC_DEBUG */
+++
+++ if (res) v.setData(res, rlen);
+++ else
+++#endif /* HAVE_LIBRCC */
+++ v.setData("", 0);
+++
+++ return v;
+++}
+++
+++TagLib::ByteVector rccTaglibPatchRecodeInputID3(const std::string &s, bool v2 = false) {
+++ TagLib::ByteVector v;
+++#ifdef HAVE_LIBRCC
+++ size_t rlen;
+++ char *res;
+++
+++ rccTaglibPatchTryInit();
+++
+++ res = rccSizedRecode(ctx, v2?ID3V2_CLASS:ID3_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
+++#ifdef RCC_DEBUG
+++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
+++ if (*c > 127) {
+++ printf(" InputID3(%i): %s - %s\n", v2, s.c_str(), res?res:"null");
+++ break;
+++ }
+++ }
+++#endif /* RCC_DEBUG */
+++ if (res) v.setData(res, rlen + 1);
+++ else
+++#endif /* HAVE_LIBRCC */
+++ v.setData("", 0);
+++
+++ return v;
+++}
+++
+++TagLib::String::Type rccTaglibPatchGetLocaleType() {
+++#ifdef HAVE_LIBRCC
+++ size_t len;
+++ char charset[32];
+++
+++ rccTaglibPatchTryInit();
+++ if (!rccLocaleGetCharset(charset, NULL, 31)) {
+++ if (!strncmp(charset, "UTF", 3)) {
+++ len = strlen(charset);
+++
+++ if (charset[len-1]=='8') return TagLib::String::UTF8;
+++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
+++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
+++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
+++ }
+++ return TagLib::String::Latin1;
+++ }
+++#endif /* HAVE_LIBRCC */
+++ return TagLib::String::UTF8;
+++}
+++
+++TagLib::String::Type rccTaglibPatchGetID3Type() {
+++#ifdef HAVE_LIBRCC
+++ size_t len;
+++ const char *charset;
+++
+++ rccTaglibPatchTryInit();
+++
+++ charset = rccGetCurrentCharsetName(ctx, ID3V2_CLASS);
+++ if (charset) {
+++ if (!strncmp(charset, "UTF", 3)) {
+++ len = strlen(charset);
+++
+++ if (charset[len-1]=='8') return TagLib::String::UTF8;
+++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
+++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
+++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
+++ }
+++ return TagLib::String::Latin1ID3V2;
+++ } else {
+++ // Error or no-language configured: If Latin1ID3V2 is returned we normally will use the default unicode encoding unless Latin1 is selected by taglib
+++ return TagLib::String::Latin1ID3V2;
+++ }
+++#endif /* HAVE_LIBRCC */
+++ return TagLib::String::Latin1;
+++}
++diff --git a/taglib/toolkit/rccpatch.h b/taglib/toolkit/rccpatch.h
++new file mode 100644
++index 0000000..31f4410
++--- /dev/null
+++++ b/taglib/toolkit/rccpatch.h
++@@ -0,0 +1,20 @@
+++#ifndef _RCC_PATCH_H
+++#define _RCC_PATCH_H
+++
+++#include <string.h>
+++#include "tstring.h"
+++#include "tbytevector.h"
+++
+++void rccTaglibPatchFree();
+++void rccTaglibPatchInit();
+++void rccTaglibPatchSetContext(void *newctx);
+++
+++TagLib::ByteVector rccTaglibPatchRecodeOutput(const std::string &s);
+++TagLib::ByteVector rccTaglibPatchRecodeInput(const std::string &s);
+++TagLib::ByteVector rccTaglibPatchRecodeOutputID3(const std::string &s, bool v2 = false);
+++TagLib::ByteVector rccTaglibPatchRecodeInputID3(const std::string &s, bool v2 = false);
+++
+++TagLib::String::Type rccTaglibPatchGetLocaleType();
+++TagLib::String::Type rccTaglibPatchGetID3Type();
+++
+++#endif /* _RCC_PATCH_H */
++diff --git a/taglib/toolkit/tstring.cpp b/taglib/toolkit/tstring.cpp
++index 258e1fc..70ff160 100644
++--- a/taglib/toolkit/tstring.cpp
+++++ b/taglib/toolkit/tstring.cpp
++@@ -29,6 +29,7 @@
++ #include <config.h>
++ #endif
++
+++#include "rccpatch.h"
++ #include "tstring.h"
++ #include "tdebug.h"
++ #include "tstringlist.h"
++@@ -167,8 +168,11 @@ String::String(const String &s)
++ String::String(const std::string &s, Type t)
++ : d(new StringPrivate())
++ {
++- if(t == Latin1)
++- copyFromLatin1(s.c_str(), s.length());
+++ if(t == Locale)
+++ t = rccTaglibPatchGetLocaleType();
+++
+++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2)
+++ copyFromLatin1(&s[0], s.length(), true, t);
++ else if(t == String::UTF8)
++ copyFromUTF8(s.c_str(), s.length());
++ else {
++@@ -215,8 +219,11 @@ String::String(const wchar_t *s, Type t)
++ String::String(const char *s, Type t)
++ : d(new StringPrivate())
++ {
++- if(t == Latin1)
++- copyFromLatin1(s, ::strlen(s));
+++ if(t == Locale)
+++ t = rccTaglibPatchGetLocaleType();
+++
+++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2)
+++ copyFromLatin1(s, ::strlen(s), true, t);
++ else if(t == String::UTF8)
++ copyFromUTF8(s, ::strlen(s));
++ else {
++@@ -248,8 +255,11 @@ String::String(const ByteVector &v, Type t)
++ if(v.isEmpty())
++ return;
++
++- if(t == Latin1)
++- copyFromLatin1(v.data(), v.size());
+++ if(t == Locale)
+++ t = rccTaglibPatchGetLocaleType();
+++
+++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2)
+++ copyFromLatin1(v.data(), v.size(), true, t);
++ else if(t == UTF8)
++ copyFromUTF8(v.data(), v.size());
++ else
++@@ -396,8 +406,38 @@ bool String::isNull() const
++
++ ByteVector String::data(Type t) const
++ {
++- switch(t)
++- {
+++ ByteVector v;
+++
+++ if (t == Locale) {
+++ // The source is either Unicode or real Latin1 (if rcc is bypassed)
+++ std::string s = to8Bit(true);
+++
+++ // In case of UTF8 locale, this probably will return NULL (no recoding needed), but we will take UTF8 path in the next swtich
+++ v = rccTaglibPatchRecodeOutput(s);
+++ if (v.size()) return v;
+++
+++ t = rccTaglibPatchGetLocaleType();
+++ }
+++
+++ switch(t) {
+++ case Latin1ID3:
+++ case Latin1ID3V2:
+++ {
+++ std::string s = to8Bit(true);
+++ if (t == Latin1ID3) v = rccTaglibPatchRecodeOutputID3(s, false);
+++ else if (t == Latin1ID3V2) v = rccTaglibPatchRecodeOutputID3(s, true);
+++ if (v.size())
+++ return v;
+++
+++ // we don't know if we got NULL because rcc is disabled (error) or UTF8 output is required
+++ if ((t == Latin1ID3V2)&&(rccTaglibPatchGetID3Type() == UTF8)) {
+++ v.setData(s.c_str(), s.length());
+++ } else {
+++ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
+++ v.append(char(*it));
+++ }
+++ return v;
+++ }
++ case Latin1:
++ {
++ ByteVector v(size(), 0);
++@@ -738,12 +778,30 @@ void String::detach()
++ // private members
++ ////////////////////////////////////////////////////////////////////////////////
++
++-void String::copyFromLatin1(const char *s, size_t length)
+++void String::copyFromLatin1(const char *s, size_t length, bool prepare, Type t)
++ {
++ d->data.resize(length);
++
++ for(size_t i = 0; i < length; ++i)
++ d->data[i] = static_cast<uchar>(s[i]);
+++
+++ // librcc conversation
+++ if (prepare) {
+++ std::string s = to8Bit(false);
+++ ByteVector v;
+++
+++ if (t == Latin1ID3) v = rccTaglibPatchRecodeInputID3(s, false);
+++ else if (t == Latin1ID3V2) v = rccTaglibPatchRecodeInputID3(s, true);
+++ else /* Latin1 converted from Locale */ v = rccTaglibPatchRecodeInput(s);
+++
+++ if (v.size()) {
+++ copyFromUTF8(v.data(), v.size());
+++ } else {
+++ // We don't know if we got UTF-8 encoded string or either rcc is disable or something is failed,
+++ // since standard applications are really expecting here Latin1, it is safe to just check if we have violations of UTF8
+++ //if (Unicode::isLegalUTF8(s)) t = UTF8;
+++ }
+++ }
++ }
++
++ void String::copyFromUTF8(const char *s, size_t length)
++@@ -859,7 +917,33 @@ const TagLib::String operator+(const TagLib::String &s1, const char *s2)
++
++ std::ostream &operator<<(std::ostream &s, const TagLib::String &str)
++ {
++- s << str.to8Bit();
+++ TagLib::ByteVector bv = str.data(TagLib::String::Locale);
+++ s << bv;
++ return s;
++ }
++
+++TagLib::String::Type TagLib::String::ID3Type(int i)
+++{
+++ if(i == Latin1)
+++ return Latin1ID3V2;
+++ return Type(i);
+++};
+++
+++TagLib::String::Type TagLib::String::ID3WType(Type type)
+++{
+++ Type rcc_type = rccTaglibPatchGetID3Type();
+++ if((rcc_type == Latin1ID3)||(rcc_type == Latin1ID3V2)||(rcc_type == Latin1)) {
+++ if(type == Latin1) return
+++ rcc_type;
+++ return type;
+++ }
+++
+++ return rcc_type;
+++};
+++
+++TagLib::String::Type TagLib::String::ID3RealType(Type type)
+++{
+++ if((type == Latin1ID3) || (type == Latin1ID3V2))
+++ return Latin1;
+++ return type;
+++}
++diff --git a/taglib/toolkit/tstring.h b/taglib/toolkit/tstring.h
++index 8b73988..8efca25 100644
++--- a/taglib/toolkit/tstring.h
+++++ b/taglib/toolkit/tstring.h
++@@ -96,6 +96,18 @@ namespace TagLib {
++ */
++ enum Type {
++ /*!
+++ * Determine using current locale settings
+++ */
+++ Locale = -1,
+++ /*!
+++ * Latin1 for ID3 tags.
+++ */
+++ Latin1ID3 = 65,
+++ /*!
+++ * Latin1 for ID3v2 tags.
+++ */
+++ Latin1ID3V2 = 66,
+++ /*!
++ * IS08859-1, or <i>Latin1</i> encoding. 8 bit characters.
++ */
++ Latin1 = 0,
++@@ -117,6 +129,10 @@ namespace TagLib {
++ */
++ UTF16LE = 4
++ };
+++
+++ static Type ID3Type(int i);
+++ static Type ID3WType(Type type);
+++ static Type ID3RealType(Type type);
++
++ /*!
++ * Constructs an empty String.
++@@ -519,7 +535,7 @@ namespace TagLib {
++ * Converts a \e Latin-1 string into \e UTF-16(without BOM/CPU byte order)
++ * and copies it to the internal buffer.
++ */
++- void copyFromLatin1(const char *s, size_t length);
+++ void copyFromLatin1(const char *s, size_t length, bool prepare = false, Type t = Latin1);
++
++ /*!
++ * Converts a \e UTF-8 string into \e UTF-16(without BOM/CPU byte order)
diff --git a/patches/taglib/taglib-1.11-ds-rusxmms.patch b/patches/taglib/taglib-1.11-ds-rusxmms.patch
new file mode 100644
index 0000000..9f777c8
--- /dev/null
+++ b/patches/taglib/taglib-1.11-ds-rusxmms.patch
@@ -0,0 +1,684 @@
+diff -dPNur taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/config.h.cmake taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/config.h.cmake
+--- taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/config.h.cmake 2018-10-28 14:43:45.000000000 +0100
++++ taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/config.h.cmake 2019-09-01 08:32:29.569443643 +0200
+@@ -30,6 +30,9 @@
+ /* Indicates whether debug messages are shown even in release mode */
+ #cmakedefine TRACE_IN_RELEASE 1
+
++/* Defined if you have LibRCC from RusXMMS project */
++#cmakedefine HAVE_LIBRCC 1
++
+ #cmakedefine TESTS_DIR "@TESTS_DIR@"
+
+ #endif
+diff -dPNur taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/ConfigureChecks.cmake taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/ConfigureChecks.cmake
+--- taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/ConfigureChecks.cmake 2018-10-28 14:43:45.000000000 +0100
++++ taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/ConfigureChecks.cmake 2019-09-01 08:32:29.577443787 +0200
+@@ -213,3 +213,5 @@
+ if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
+ set(PLATFORM WINRT 1)
+ endif()
++
++SET(HAVE_LIBRCC 1)
+\ No newline at end of file
+diff -dPNur taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/examples/tagreader_c.c taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/examples/tagreader_c.c
+--- taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/examples/tagreader_c.c 2018-10-28 14:43:45.000000000 +0100
++++ taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/examples/tagreader_c.c 2019-09-01 08:32:29.577443787 +0200
+@@ -38,7 +38,7 @@
+ TagLib_Tag *tag;
+ const TagLib_AudioProperties *properties;
+
+- taglib_set_strings_unicode(FALSE);
++ //taglib_set_strings_unicode(FALSE);
+
+ for(i = 1; i < argc; i++) {
+ printf("******************** \"%s\" ********************\n", argv[i]);
+diff -dPNur taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/examples/tagwriter.cpp taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/examples/tagwriter.cpp
+--- taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/examples/tagwriter.cpp 2018-10-28 14:43:45.000000000 +0100
++++ taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/examples/tagwriter.cpp 2019-09-01 08:32:29.577443787 +0200
+@@ -115,7 +115,7 @@
+ if(isArgument(argv[i]) && i + 1 < argc && !isArgument(argv[i + 1])) {
+
+ char field = argv[i][1];
+- TagLib::String value = argv[i + 1];
++ TagLib::String value(argv[i + 1], TagLib::String::Locale);
+
+ TagLib::List<TagLib::FileRef>::ConstIterator it;
+ for(it = fileList.begin(); it != fileList.end(); ++it) {
+diff -dPNur taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/taglib/CMakeLists.txt taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/taglib/CMakeLists.txt
+--- taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/taglib/CMakeLists.txt 2018-10-28 14:43:45.000000000 +0100
++++ taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/taglib/CMakeLists.txt 2019-09-01 08:32:29.577443787 +0200
+@@ -41,6 +41,7 @@
+ audioproperties.h
+ taglib_export.h
+ ${CMAKE_CURRENT_BINARY_DIR}/../taglib_config.h
++ toolkit/rccpatch.h
+ toolkit/taglib.h
+ toolkit/tstring.h
+ toolkit/tlist.h
+@@ -312,6 +313,7 @@
+ )
+
+ set(toolkit_SRCS
++ toolkit/rccpatch.cpp
+ toolkit/tstring.cpp
+ toolkit/tstringlist.cpp
+ toolkit/tbytevector.cpp
+@@ -354,7 +356,9 @@
+ add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
+
+ if(HAVE_ZLIB AND NOT HAVE_ZLIB_SOURCE)
+- target_link_libraries(tag ${ZLIB_LIBRARIES})
++ target_link_libraries(tag rcc ${ZLIB_LIBRARIES})
++else ()
++ target_link_libraries(tag rcc)
+ endif()
+
+ set_target_properties(tag PROPERTIES
+diff -dPNur taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/taglib/mpeg/id3v1/id3v1tag.cpp taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/taglib/mpeg/id3v1/id3v1tag.cpp
+--- taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/taglib/mpeg/id3v1/id3v1tag.cpp 2018-10-28 14:43:45.000000000 +0100
++++ taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/taglib/mpeg/id3v1/id3v1tag.cpp 2019-09-01 08:32:29.577443787 +0200
+@@ -69,15 +69,18 @@
+
+ String ID3v1::StringHandler::parse(const ByteVector &data) const
+ {
+- return String(data, String::Latin1).stripWhiteSpace();
++ return String(data, String::Latin1ID3).stripWhiteSpace();
+ }
+
+ ByteVector ID3v1::StringHandler::render(const String &s) const
+ {
+- if(s.isLatin1())
+- return s.data(String::Latin1);
+- else
++ if(!s.isLatin1())
++ {
++ if (String::ID3WType(String::Latin1) == String::Latin1)
+ return ByteVector();
++ }
++
++ return s.data(String::Latin1ID3);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+@@ -262,7 +265,7 @@
+ d->track = static_cast<unsigned char>(data[offset + 29]);
+ }
+ else
+- d->comment = data.mid(offset, 30);
++ d->comment = stringHandler->parse(data.mid(offset, 30));
+
+ offset += 30;
+
+diff -dPNur taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/taglib/mpeg/id3v2/frames/commentsframe.cpp taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp
+--- taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/taglib/mpeg/id3v2/frames/commentsframe.cpp 2018-10-28 14:43:45.000000000 +0100
++++ taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp 2019-09-01 08:32:29.578443805 +0200
+@@ -150,10 +150,10 @@
+ return;
+ }
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+ d->language = data.mid(1, 3);
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
+
+@@ -173,11 +173,13 @@
+ ByteVector v;
+
+ String::Type encoding = d->textEncoding;
++
++ encoding = String::ID3WType(encoding);
+
+ encoding = checkTextEncoding(d->description, encoding);
+ encoding = checkTextEncoding(d->text, encoding);
+
+- v.append(char(encoding));
++ v.append(char(String::ID3RealType(encoding)));
+ v.append(d->language.size() == 3 ? d->language : "XXX");
+ v.append(d->description.data(encoding));
+ v.append(textDelimiter(encoding));
+diff -dPNur taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/taglib/mpeg/id3v2/frames/textidentificationframe.cpp taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp
+--- taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2018-10-28 14:43:45.000000000 +0100
++++ taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2019-09-01 08:32:29.578443805 +0200
+@@ -191,12 +191,12 @@
+
+ // read the string data type (the first byte of the field data)
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+
+ // split the byte array into chunks based on the string type (two byte delimiter
+ // for unicode encodings)
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ // build a small counter to strip nulls off the end of the field
+
+@@ -227,11 +227,14 @@
+
+ ByteVector TextIdentificationFrame::renderFields() const
+ {
+- String::Type encoding = checkTextEncoding(d->fieldList, d->textEncoding);
++ String::Type encoding = d->textEncoding;
++
++ encoding = String::ID3WType(encoding);
++ encoding = checkTextEncoding(d->fieldList, encoding);
+
+ ByteVector v;
+
+- v.append(char(encoding));
++ v.append(char(String::ID3RealType(encoding)));
+
+ for(StringList::ConstIterator it = d->fieldList.begin(); it != d->fieldList.end(); it++) {
+
+diff -dPNur taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/taglib/mpeg/id3v2/id3v2frame.cpp taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/taglib/mpeg/id3v2/id3v2frame.cpp
+--- taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/taglib/mpeg/id3v2/id3v2frame.cpp 2018-10-28 14:43:45.000000000 +0100
++++ taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/taglib/mpeg/id3v2/id3v2frame.cpp 2019-09-01 08:32:29.578443805 +0200
+@@ -297,7 +297,7 @@
+ if((encoding == String::UTF8 || encoding == String::UTF16BE) && version != 4)
+ return String::UTF16;
+
+- if(encoding != String::Latin1)
++ if((encoding != String::Latin1)&&(encoding != String::Latin1ID3V2))
+ return encoding;
+
+ for(StringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) {
+diff -dPNur taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/taglib/toolkit/rccpatch.cpp taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/taglib/toolkit/rccpatch.cpp
+--- taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/taglib/toolkit/rccpatch.cpp 1970-01-01 01:00:00.000000000 +0100
++++ taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/taglib/toolkit/rccpatch.cpp 2019-09-01 08:32:29.578443805 +0200
+@@ -0,0 +1,237 @@
++#include <stdlib.h>
++
++#include <string>
++#include "tstring.h"
++#include "tbytevector.h"
++
++//#define RCC_DEBUG
++
++
++#ifndef HAVE_LIBRCC
++# include <config.h>
++#endif
++
++#ifdef HAVE_LIBRCC
++# ifdef RCC_DEBUG
++# include <stdio.h>
++# endif /* RCC_DEBUG */
++# include <librcc.h>
++# include <string.h>
++#endif /* HAVE_LIBRCC */
++
++
++#ifdef HAVE_LIBRCC
++# define ID3_CLASS 0
++# define ID3V2_CLASS 1
++# define UTF_CLASS 2
++# define OUT_CLASS 3
++static rcc_class classes[] = {
++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
++ { "id3v2", RCC_CLASS_STANDARD, "id3", NULL, "ID3 v.2 Encoding", 0 },
++ { "utf", RCC_CLASS_KNOWN, "UTF-8", NULL, "Unicode Encoding", 0},
++ { "out", RCC_CLASS_TRANSLATE_LOCALE, "LC_CTYPE", NULL, "Output Encoding", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int rcc_initialized = 0;
++
++static rcc_context ctx = NULL;
++#endif /* HAVE_LIBRCC */
++
++
++void rccTaglibPatchFree() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) {
++ rccFree();
++ rcc_initialized = 0;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++void rccTaglibPatchInit() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) return;
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "xmms");
++ rccInitDb4(NULL, NULL, 0);
++ rcc_initialized = 1;
++#endif /* HAVE_LIBRCC */
++}
++
++void rccTaglibPatchSetContext(void *newctx) {
++#ifdef HAVE_LIBRCC
++ if (newctx) {
++ ctx = (rcc_context)newctx;
++ rcc_initialized = 1;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++static void rccTaglibPatchTryInit() {
++#ifdef HAVE_LIBRCC
++ if (!rcc_initialized) {
++ rccTaglibPatchInit();
++ if (rcc_initialized) atexit(rccTaglibPatchFree);
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++
++TagLib::ByteVector rccTaglibPatchRecodeOutput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccTaglibPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, OUT_CLASS, s.c_str(), s.length(), &rlen);
++#ifdef RCC_DEBUG
++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
++ if (*c > 127) {
++ printf(" Output: %s - %s\n", s.c_str(), res?res:"null");
++ break;
++ }
++ }
++#endif /* RCC_DEBUG */
++
++ if (res) v.setData(res, rlen);
++ else v.setData("", 0);
++ //v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccTaglibPatchRecodeOutputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccTaglibPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, v2?ID3V2_CLASS:ID3_CLASS, s.c_str(), s.length(), &rlen);
++#ifdef RCC_DEBUG
++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
++ if (*c > 127) {
++ printf(" OutputID3(%i): %s - %s\n", v2, s.c_str(), res?res:"null");
++ break;
++ }
++ }
++#endif /* RCC_DEBUG */
++
++ if (res) v.setData(res, rlen);
++ else v.setData("", 0);
++ //v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccTaglibPatchRecodeInput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccTaglibPatchTryInit();
++
++ res = rccSizedRecode(ctx, OUT_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++#ifdef RCC_DEBUG
++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
++ if (*c > 127) {
++ printf(" Input: %s - %s\n", s.c_str(), res?res:"null");
++ break;
++ }
++ }
++#endif /* RCC_DEBUG */
++
++ if (res) v.setData(res, rlen);
++ else
++#endif /* HAVE_LIBRCC */
++ v.setData("", 0);
++
++ return v;
++}
++
++TagLib::ByteVector rccTaglibPatchRecodeInputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccTaglibPatchTryInit();
++
++ res = rccSizedRecode(ctx, v2?ID3V2_CLASS:ID3_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++#ifdef RCC_DEBUG
++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
++ if (*c > 127) {
++ printf(" InputID3(%i): %s - %s\n", v2, s.c_str(), res?res:"null");
++ break;
++ }
++ }
++#endif /* RCC_DEBUG */
++ if (res) v.setData(res, rlen + 1);
++ else
++#endif /* HAVE_LIBRCC */
++ v.setData("", 0);
++
++ return v;
++}
++
++TagLib::String::Type rccTaglibPatchGetLocaleType() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ char charset[32];
++
++ rccTaglibPatchTryInit();
++ if (!rccLocaleGetCharset(charset, NULL, 31)) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::UTF8;
++}
++
++TagLib::String::Type rccTaglibPatchGetID3Type() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ const char *charset;
++
++ rccTaglibPatchTryInit();
++
++ charset = rccGetCurrentCharsetName(ctx, ID3V2_CLASS);
++ if (charset) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1ID3V2;
++ } else {
++ // Error or no-language configured: If Latin1ID3V2 is returned we normally will use the default unicode encoding unless Latin1 is selected by taglib
++ return TagLib::String::Latin1ID3V2;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::Latin1;
++}
+diff -dPNur taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/taglib/toolkit/rccpatch.h taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/taglib/toolkit/rccpatch.h
+--- taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/taglib/toolkit/rccpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/taglib/toolkit/rccpatch.h 2019-09-01 08:32:29.578443805 +0200
+@@ -0,0 +1,20 @@
++#ifndef _RCC_PATCH_H
++#define _RCC_PATCH_H
++
++#include <string.h>
++#include "tstring.h"
++#include "tbytevector.h"
++
++void rccTaglibPatchFree();
++void rccTaglibPatchInit();
++void rccTaglibPatchSetContext(void *newctx);
++
++TagLib::ByteVector rccTaglibPatchRecodeOutput(const std::string &s);
++TagLib::ByteVector rccTaglibPatchRecodeInput(const std::string &s);
++TagLib::ByteVector rccTaglibPatchRecodeOutputID3(const std::string &s, bool v2 = false);
++TagLib::ByteVector rccTaglibPatchRecodeInputID3(const std::string &s, bool v2 = false);
++
++TagLib::String::Type rccTaglibPatchGetLocaleType();
++TagLib::String::Type rccTaglibPatchGetID3Type();
++
++#endif /* _RCC_PATCH_H */
+diff -dPNur taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/taglib/toolkit/tstring.cpp taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/taglib/toolkit/tstring.cpp
+--- taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/taglib/toolkit/tstring.cpp 2018-10-28 14:43:45.000000000 +0100
++++ taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/taglib/toolkit/tstring.cpp 2019-09-01 08:27:18.763821274 +0200
+@@ -33,6 +33,7 @@
+ #include <trefcounter.h>
+ #include <tutils.h>
+
++#include "rccpatch.h"
+ #include "tstring.h"
+
+ namespace
+@@ -48,16 +49,6 @@
+ return String::UTF16BE;
+ }
+
+- // Converts a Latin-1 string into UTF-16(without BOM/CPU byte order)
+- // and copies it to the internal buffer.
+- void copyFromLatin1(std::wstring &data, const char *s, size_t length)
+- {
+- data.resize(length);
+-
+- for(size_t i = 0; i < length; ++i)
+- data[i] = static_cast<unsigned char>(s[i]);
+- }
+-
+ // Converts a UTF-8 string into UTF-16(without BOM/CPU byte order)
+ // and copies it to the internal buffer.
+ void copyFromUTF8(std::wstring &data, const char *s, size_t length)
+@@ -73,6 +64,36 @@
+ debug("String::copyFromUTF8() - UTF8-CPP error: " + message);
+ data.clear();
+ }
++
++ }
++
++ // Converts a Latin-1 string into UTF-16(without BOM/CPU byte order)
++ // and copies it to the internal buffer.
++ void copyFromLatin1(std::wstring &data, const char *s, size_t length, bool prepare, String::Type t)
++ {
++ // librcc conversation
++ if (prepare) {
++ ByteVector v;
++ std::string std_s(s);
++
++ if (t == String::Latin1ID3) v = rccTaglibPatchRecodeInputID3(std_s, false);
++ else if (t == String::Latin1ID3V2) v = rccTaglibPatchRecodeInputID3(std_s, true);
++ else /* Latin1 converted from Locale */ v = rccTaglibPatchRecodeInput(std_s);
++
++ if (v.size()) {
++ copyFromUTF8(data, v.data(), v.size());
++ return;
++ } else {
++ // We don't know if we got UTF-8 encoded string or either rcc is disable or something is failed,
++ // since standard applications are really expecting here Latin1, it is safe to just check if we have violations of UTF8
++ //if (Unicode::isLegalUTF8(s)) t = UTF8;
++ }
++ }
++
++ data.resize(length);
++
++ for(size_t i = 0; i < length; ++i)
++ data[i] = static_cast<unsigned char>(s[i]);
+ }
+
+ // Helper functions to read a UTF-16 character from an array.
+@@ -175,8 +196,11 @@
+ String::String(const std::string &s, Type t) :
+ d(new StringPrivate())
+ {
++ if(t == Locale)
++ t = rccTaglibPatchGetLocaleType();
++
+ if(t == Latin1)
+- copyFromLatin1(d->data, s.c_str(), s.length());
++ copyFromLatin1(d->data, s.c_str(), s.length(), true, t);
+ else if(t == String::UTF8)
+ copyFromUTF8(d->data, s.c_str(), s.length());
+ else {
+@@ -223,8 +247,11 @@
+ String::String(const char *s, Type t) :
+ d(new StringPrivate())
+ {
+- if(t == Latin1)
+- copyFromLatin1(d->data, s, ::strlen(s));
++ if(t == Locale)
++ t = rccTaglibPatchGetLocaleType();
++
++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2)
++ copyFromLatin1(d->data, s, ::strlen(s), true, t);
+ else if(t == String::UTF8)
+ copyFromUTF8(d->data, s, ::strlen(s));
+ else {
+@@ -245,8 +272,11 @@
+ String::String(char c, Type t) :
+ d(new StringPrivate())
+ {
+- if(t == Latin1)
+- copyFromLatin1(d->data, &c, 1);
++ if(t == Locale)
++ t = rccTaglibPatchGetLocaleType();
++
++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2)
++ copyFromLatin1(d->data, &c, 1, true, t);
+ else if(t == String::UTF8)
+ copyFromUTF8(d->data, &c, 1);
+ else {
+@@ -260,8 +290,11 @@
+ if(v.isEmpty())
+ return;
+
+- if(t == Latin1)
+- copyFromLatin1(d->data, v.data(), v.size());
++ if(t == Locale)
++ t = rccTaglibPatchGetLocaleType();
++
++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2)
++ copyFromLatin1(d->data, v.data(), v.size(), true, t);
+ else if(t == UTF8)
+ copyFromUTF8(d->data, v.data(), v.size());
+ else
+@@ -416,8 +449,38 @@
+
+ ByteVector String::data(Type t) const
+ {
+- switch(t)
+- {
++ ByteVector v;
++
++ if (t == Locale) {
++ // The source is either Unicode or real Latin1 (if rcc is bypassed)
++ std::string s = to8Bit(true);
++
++ // In case of UTF8 locale, this probably will return NULL (no recoding needed), but we will take UTF8 path in the next swtich
++ v = rccTaglibPatchRecodeOutput(s);
++ if (v.size()) return v;
++
++ t = rccTaglibPatchGetLocaleType();
++ }
++
++ switch(t) {
++ case Latin1ID3:
++ case Latin1ID3V2:
++ {
++ std::string s = to8Bit(true);
++ if (t == Latin1ID3) v = rccTaglibPatchRecodeOutputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccTaglibPatchRecodeOutputID3(s, true);
++ if (v.size())
++ return v;
++
++ // we don't know if we got NULL because rcc is disabled (error) or UTF8 output is required
++ if ((t == Latin1ID3V2)&&(rccTaglibPatchGetID3Type() == UTF8)) {
++ v.setData(s.c_str(), s.length());
++ } else {
++ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
++ v.append(char(*it));
++ }
++ return v;
++ }
+ case Latin1:
+ {
+ ByteVector v(size(), 0);
+@@ -741,7 +804,33 @@
+
+ std::ostream &operator<<(std::ostream &s, const TagLib::String &str)
+ {
+- s << str.to8Bit();
++ TagLib::ByteVector bv = str.data(TagLib::String::Locale);
++ s << bv;
+ return s;
+ }
+
++TagLib::String::Type TagLib::String::ID3Type(int i)
++{
++ if(i == Latin1)
++ return Latin1ID3V2;
++ return Type(i);
++};
++
++TagLib::String::Type TagLib::String::ID3WType(Type type)
++{
++ Type rcc_type = rccTaglibPatchGetID3Type();
++ if((rcc_type == Latin1ID3)||(rcc_type == Latin1ID3V2)||(rcc_type == Latin1)) {
++ if(type == Latin1) return
++ rcc_type;
++ return type;
++ }
++
++ return rcc_type;
++};
++
++TagLib::String::Type TagLib::String::ID3RealType(Type type)
++{
++ if((type == Latin1ID3) || (type == Latin1ID3V2))
++ return Latin1;
++ return type;
++}
+diff -dPNur taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/taglib/toolkit/tstring.h taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/taglib/toolkit/tstring.h
+--- taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c/taglib/toolkit/tstring.h 2018-10-28 14:43:45.000000000 +0100
++++ taglib-5cb589a5b82c13ba8f0542e5e79629da7645cb3c-ds/taglib/toolkit/tstring.h 2019-09-01 08:32:29.579443823 +0200
+@@ -96,6 +96,18 @@
+ */
+ enum Type {
+ /*!
++ * Determine using current locale settings
++ */
++ Locale = -1,
++ /*!
++ * Latin1 for ID3 tags.
++ */
++ Latin1ID3 = 65,
++ /*!
++ * Latin1 for ID3v2 tags.
++ */
++ Latin1ID3V2 = 66,
++ /*!
+ * IS08859-1, or <i>Latin1</i> encoding. 8 bit characters.
+ */
+ Latin1 = 0,
+@@ -117,6 +129,10 @@
+ */
+ UTF16LE = 4
+ };
++
++ static Type ID3Type(int i);
++ static Type ID3WType(Type type);
++ static Type ID3RealType(Type type);
+
+ /*!
+ * Constructs an empty String.
diff --git a/patches/taglib/taglib-1.4-ds-rusxmms.patch b/patches/taglib/taglib-1.4-ds-rusxmms.patch
new file mode 100644
index 0000000..5b67c7c
--- /dev/null
+++ b/patches/taglib/taglib-1.4-ds-rusxmms.patch
@@ -0,0 +1,504 @@
+diff -dPNur taglib-1.4/configure.in taglib-1.4-new/configure.in
+--- taglib-1.4/configure.in 2005-07-27 02:45:33.000000000 +0200
++++ taglib-1.4-new/configure.in 2005-09-16 01:45:30.000000000 +0200
+@@ -98,6 +98,20 @@
+
+ AC_SUBST(AUTODIRS)
+
++AC_CHECK_LIB(rcc, rccInit,[
++ AC_CHECK_HEADERS(librcc.h,[
++ LIBRCC_LIBS="-lrcc"
++ LIBRCC_INCLUDES="-DHAVE_LIBRCC"
++ ],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])
++AC_SUBST(LIBRCC_LIBS)
++AC_SUBST(LIBRCC_INCLUDES)
++
+ dnl =======================================================
+ dnl FILE: ./taglib/configure.in.in
+ dnl =======================================================
+diff -dPNur taglib-1.4/configure.in.in taglib-1.4-new/configure.in.in
+--- taglib-1.4/configure.in.in 2005-07-23 23:43:58.000000000 +0200
++++ taglib-1.4-new/configure.in.in 2005-09-16 01:45:30.000000000 +0200
+@@ -94,3 +94,16 @@
+
+ AC_SUBST(AUTODIRS)
+
++AC_CHECK_LIB(rcc, rccInit,[
++ AC_CHECK_HEADERS(librcc.h,[
++ LIBRCC_LIBS="-lrcc"
++ LIBRCC_INCLUDES="-DHAVE_LIBRCC"
++ ],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])
++AC_SUBST(LIBRCC_LIBS)
++AC_SUBST(LIBRCC_INCLUDES)
+diff -dPNur taglib-1.4/taglib/mpeg/id3v1/id3v1tag.cpp taglib-1.4-new/taglib/mpeg/id3v1/id3v1tag.cpp
+--- taglib-1.4/taglib/mpeg/id3v1/id3v1tag.cpp 2005-05-17 22:17:16.000000000 +0200
++++ taglib-1.4-new/taglib/mpeg/id3v1/id3v1tag.cpp 2005-09-16 01:46:37.000000000 +0200
+@@ -55,12 +55,12 @@
+
+ String ID3v1::StringHandler::parse(const ByteVector &data) const
+ {
+- return String(data, String::Latin1);
++ return String(data, String::Latin1ID3);
+ }
+
+ ByteVector ID3v1::StringHandler::render(const String &s) const
+ {
+- return s.data(String::Latin1);
++ return s.data(String::Latin1ID3);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+@@ -231,7 +231,7 @@
+ d->track = uchar(data[offset + 29]);
+ }
+ else
+- d->comment = data.mid(offset, 30);
++ d->comment = TagPrivate::stringHandler->parse(data.mid(offset, 30));
+
+ offset += 30;
+
+diff -dPNur taglib-1.4/taglib/mpeg/id3v2/frames/commentsframe.cpp taglib-1.4-new/taglib/mpeg/id3v2/frames/commentsframe.cpp
+--- taglib-1.4/taglib/mpeg/id3v2/frames/commentsframe.cpp 2005-07-25 23:16:32.000000000 +0200
++++ taglib-1.4-new/taglib/mpeg/id3v2/frames/commentsframe.cpp 2005-09-16 01:45:30.000000000 +0200
+@@ -115,7 +115,7 @@
+ return;
+ }
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+ d->language = data.mid(1, 3);
+
+ int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
+@@ -131,12 +131,13 @@
+ ByteVector CommentsFrame::renderFields() const
+ {
+ ByteVector v;
++ String::Type textEncoding = String::ID3WType(d->textEncoding);
+
+- v.append(char(d->textEncoding));
++ v.append(char(textEncoding));
+ v.append(d->language.size() == 3 ? d->language : " ");
+- v.append(d->description.data(d->textEncoding));
+- v.append(textDelimiter(d->textEncoding));
+- v.append(d->text.data(d->textEncoding));
++ v.append(d->description.data(textEncoding));
++ v.append(textDelimiter(textEncoding));
++ v.append(d->text.data(textEncoding));
+
+ return v;
+ }
+diff -dPNur taglib-1.4/taglib/mpeg/id3v2/frames/textidentificationframe.cpp taglib-1.4-new/taglib/mpeg/id3v2/frames/textidentificationframe.cpp
+--- taglib-1.4/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2005-05-17 22:17:26.000000000 +0200
++++ taglib-1.4-new/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2005-09-16 01:45:30.000000000 +0200
+@@ -96,7 +96,7 @@
+ {
+ // read the string data type (the first byte of the field data)
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+
+ // split the byte array into chunks based on the string type (two byte delimiter
+ // for unicode encodings)
+@@ -121,8 +121,9 @@
+ ByteVector v;
+
+ if(d->fieldList.size() > 0) {
++ String::Type textEncoding = String::ID3WType(d->textEncoding);
+
+- v.append(char(d->textEncoding));
++ v.append(char(textEncoding));
+
+ for(StringList::Iterator it = d->fieldList.begin(); it != d->fieldList.end(); it++) {
+
+@@ -131,9 +132,9 @@
+ // encoding.
+
+ if(it != d->fieldList.begin())
+- v.append(textDelimiter(d->textEncoding));
++ v.append(textDelimiter(textEncoding));
+
+- v.append((*it).data(d->textEncoding));
++ v.append((*it).data(textEncoding));
+ }
+ }
+
+diff -dPNur taglib-1.4/taglib/toolkit/Makefile.am taglib-1.4-new/taglib/toolkit/Makefile.am
+--- taglib-1.4/taglib/toolkit/Makefile.am 2005-07-25 23:16:32.000000000 +0200
++++ taglib-1.4-new/taglib/toolkit/Makefile.am 2005-09-16 01:45:30.000000000 +0200
+@@ -1,12 +1,15 @@
+-INCLUDES = $(all_includes)
++INCLUDES = $(all_includes) @LIBRCC_INCLUDES@
++libtoolkit_la_LDFLAGS = @LIBRCC_LIBS@
+
+ noinst_LTLIBRARIES = libtoolkit.la
+
+ libtoolkit_la_SOURCES = \
++ rccpatch.cpp \
+ tstring.cpp tstringlist.cpp tbytevector.cpp \
+ tbytevectorlist.cpp tfile.cpp tdebug.cpp unicode.cpp
+
+ taglib_include_HEADERS = \
++ rccpatch.h \
+ taglib.h tstring.h tlist.h tlist.tcc tstringlist.h \
+ tbytevector.h tbytevectorlist.h tfile.h \
+ tmap.h tmap.tcc
+diff -dPNur taglib-1.4/taglib/toolkit/rccpatch.cpp taglib-1.4-new/taglib/toolkit/rccpatch.cpp
+--- taglib-1.4/taglib/toolkit/rccpatch.cpp 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.4-new/taglib/toolkit/rccpatch.cpp 2005-09-16 01:45:30.000000000 +0200
+@@ -0,0 +1,202 @@
++#include <stdlib.h>
++
++#include <string>
++#include "tstring.h"
++#include "tbytevector.h"
++
++#ifdef HAVE_LIBRCC
++# include <librcc.h>
++#endif /* HAVE_LIBRCC */
++
++
++#ifdef HAVE_LIBRCC
++# define ID3_CLASS 0
++# define ID3V2_CLASS 1
++# define UTF_CLASS 2
++# define OUT_CLASS 3
++static rcc_class classes[] = {
++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
++ { "id3v2", RCC_CLASS_STANDARD, "id3", NULL, "ID3 v.2 Encoding", 0 },
++ { "utf", RCC_CLASS_KNOWN, "UTF-8", NULL, "Unicode Encoding", 0},
++ { "out", RCC_CLASS_TRANSLATE_LOCALE, "LC_CTYPE", NULL, "Output Encoding", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int rcc_initialized = 0;
++
++static rcc_context ctx = NULL;
++#endif /* HAVE_LIBRCC */
++
++
++void rccPatchFree() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) {
++ rccFree();
++ rcc_initialized = 0;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++void rccPatchInit() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) return;
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "xmms");
++ rccInitDb4(NULL, NULL, 0);
++ rcc_initialized = 1;
++#endif /* HAVE_LIBRCC */
++}
++
++void rccPatchSetContext(void *newctx) {
++#ifdef HAVE_LIBRCC
++ if (newctx) {
++ ctx = (rcc_context)newctx;
++ rcc_initialized = 1;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++static void rccPatchTryInit() {
++#ifdef HAVE_LIBRCC
++ if (!rcc_initialized) {
++ rccPatchInit();
++ if (rcc_initialized) atexit(rccPatchFree);
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++char *rccPatchRecode(const char *str, size_t len, size_t *rlen) {
++#ifdef HAVE_LIBRCC
++ rccPatchTryInit();
++
++ return rccSizedRecode(ctx, ID3_CLASS, OUT_CLASS, str, len, rlen);
++#else
++ return NULL;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeOutput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, OUT_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeOutputID3(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, ID3_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeInput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, OUT_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeInputID3(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, ID3_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::String::Type rccPatchGetLocaleType() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ char charset[32];
++
++ rccPatchTryInit();
++
++ if (!rccLocaleGetCharset(charset, NULL, 31)) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::UTF8;
++}
++
++TagLib::String::Type rccPatchGetID3Type() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ const char *charset;
++
++ rccPatchTryInit();
++
++ charset = rccGetCurrentCharsetName(ctx, ID3V2_CLASS);
++ if (charset) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1ID3;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::Latin1;
++}
+diff -dPNur taglib-1.4/taglib/toolkit/rccpatch.h taglib-1.4-new/taglib/toolkit/rccpatch.h
+--- taglib-1.4/taglib/toolkit/rccpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.4-new/taglib/toolkit/rccpatch.h 2005-09-16 01:45:30.000000000 +0200
+@@ -0,0 +1,20 @@
++#ifndef _RCC_PATCH_H
++#define _RCC_PATCH_H
++
++#include <string.h>
++#include "tstring.h"
++#include "tbytevector.h"
++
++void rccPatchFree();
++void rccPatchInit();
++void rccPatchSetContext(void *newctx);
++
++TagLib::ByteVector rccPatchRecodeOutput(const std::string &s);
++TagLib::ByteVector rccPatchRecodeInput(const std::string &s);
++TagLib::ByteVector rccPatchRecodeOutputID3(const std::string &s);
++TagLib::ByteVector rccPatchRecodeInputID3(const std::string &s);
++
++TagLib::String::Type rccPatchGetLocaleType();
++TagLib::String::Type rccPatchGetID3Type();
++
++#endif /* _RCC_PATCH_H */
+diff -dPNur taglib-1.4/taglib/toolkit/tstring.cpp taglib-1.4-new/taglib/toolkit/tstring.cpp
+--- taglib-1.4/taglib/toolkit/tstring.cpp 2005-07-25 23:31:15.000000000 +0200
++++ taglib-1.4-new/taglib/toolkit/tstring.cpp 2005-09-16 01:45:30.000000000 +0200
+@@ -19,6 +19,7 @@
+ * USA *
+ ***************************************************************************/
+
++#include "rccpatch.h"
+ #include "tstring.h"
+ #include "unicode.h"
+ #include "tdebug.h"
+@@ -161,7 +162,7 @@
+ if(v.isEmpty())
+ return;
+
+- if(t == Latin1 || t == UTF8) {
++ if(t == Latin1 || t == Latin1ID3 || t == UTF8) {
+
+ int length = 0;
+ d->data.resize(v.size());
+@@ -334,10 +335,18 @@
+ {
+ ByteVector v;
+
+- switch(t) {
++ if (t == Locale) t = rccPatchGetLocaleType();
+
++ switch(t) {
++ case Locale:
++ case Latin1ID3:
+ case Latin1:
+ {
++ std::string s = to8Bit(true);
++ if (t == Latin1ID3) v = rccPatchRecodeOutputID3(s);
++ else v = rccPatchRecodeOutput(s);
++ if (v.size()) return v;
++
+ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
+ v.append(char(*it));
+ break;
+@@ -649,6 +658,30 @@
+
+ void String::prepare(Type t)
+ {
++ if (t == Locale) t = rccPatchGetLocaleType();
++
++ if ((t == Latin1)||(t == Latin1ID3)) {
++ std::string s = to8Bit(false);
++ ByteVector v;
++
++ if (t == Latin1ID3) v = rccPatchRecodeInputID3(s);
++ else v = rccPatchRecodeInput(s);
++
++ if (v.size()) {
++ int length = 0;
++ d->data.resize(v.size());
++ wstring::iterator targetIt = d->data.begin();
++ for(ByteVector::ConstIterator it = v.begin(); it != v.end() && (*it); ++it) {
++ *targetIt = uchar(*it);
++ ++targetIt;
++ ++length;
++ }
++ d->data.resize(length);
++ }
++
++ t = UTF8;
++ }
++
+ switch(t) {
+ case UTF16:
+ {
+@@ -738,6 +771,17 @@
+
+ std::ostream &operator<<(std::ostream &s, const String &str)
+ {
+- s << str.to8Bit();
++ ByteVector bv = str.data(String::Locale);
++ s << bv;
+ return s;
+ }
++
++String::Type String::ID3Type(int i) {
++ if (i == Latin1) return Latin1ID3;
++ return Type(i);
++};
++
++String::Type String::ID3WType(Type type) {
++ if ((type == Latin1)||(type == Latin1ID3)) return rccPatchGetID3Type();
++ return type;
++};
+diff -dPNur taglib-1.4/taglib/toolkit/tstring.h taglib-1.4-new/taglib/toolkit/tstring.h
+--- taglib-1.4/taglib/toolkit/tstring.h 2005-07-25 23:55:14.000000000 +0200
++++ taglib-1.4-new/taglib/toolkit/tstring.h 2005-09-16 01:45:30.000000000 +0200
+@@ -75,6 +75,14 @@
+ */
+ enum Type {
+ /*!
++ * Determine using current locale settings
++ */
++ Locale = -1,
++ /*!
++ * Latin1 for ID3 tags.
++ */
++ Latin1ID3 = 5,
++ /*!
+ * IS08859-1, or <i>Latin1</i> encoding. 8 bit characters.
+ */
+ Latin1 = 0,
+@@ -97,6 +105,9 @@
+ UTF16LE = 4
+ };
+
++ static Type ID3Type(int i);
++ static Type ID3WType(Type type);
++
+ /*!
+ * Constructs an empty String.
+ */
diff --git a/patches/taglib/taglib-1.5-ds-rusxmms.patch b/patches/taglib/taglib-1.5-ds-rusxmms.patch
new file mode 100644
index 0000000..0d80fb5
--- /dev/null
+++ b/patches/taglib/taglib-1.5-ds-rusxmms.patch
@@ -0,0 +1,584 @@
+diff -dPNur taglib-1.5/config-taglib.h.cmake taglib-1.5-ds/config-taglib.h.cmake
+--- taglib-1.5/config-taglib.h.cmake 2008-01-11 01:56:23.000000000 +0100
++++ taglib-1.5-ds/config-taglib.h.cmake 2008-11-20 15:58:31.000000000 +0100
+@@ -6,4 +6,6 @@
+ /* Define if you have libz */
+ #cmakedefine HAVE_ZLIB 1
+
++#cmakedefine HAVE_LIBRCC 1
++
+ #cmakedefine NO_ITUNES_HACKS 1
+diff -dPNur taglib-1.5/ConfigureChecks.cmake taglib-1.5-ds/ConfigureChecks.cmake
+--- taglib-1.5/ConfigureChecks.cmake 2008-01-11 01:56:23.000000000 +0100
++++ taglib-1.5-ds/ConfigureChecks.cmake 2008-11-20 15:58:31.000000000 +0100
+@@ -14,6 +14,8 @@
+ #check for libz using the cmake supplied FindZLIB.cmake
+ FIND_PACKAGE(ZLIB)
+
++SET(HAVE_LIBRCC 1)
++
+ IF(ZLIB_FOUND)
+ SET(HAVE_ZLIB 1)
+ ELSE(ZLIB_FOUND)
+diff -dPNur taglib-1.5/configure.in taglib-1.5-ds/configure.in
+--- taglib-1.5/configure.in 2008-02-13 00:53:05.000000000 +0100
++++ taglib-1.5-ds/configure.in 2008-11-20 15:24:19.000000000 +0100
+@@ -98,6 +98,20 @@
+
+ AC_SUBST(AUTODIRS)
+
++AC_CHECK_LIB(rcc, rccInit,[
++ AC_CHECK_HEADERS(librcc.h,[
++ LIBRCC_LIBS="-lrcc"
++ LIBRCC_INCLUDES="-DHAVE_LIBRCC"
++ ],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])
++AC_SUBST(LIBRCC_LIBS)
++AC_SUBST(LIBRCC_INCLUDES)
++
+ #AM_INIT_AUTOMAKE(taglib,1.0)
+ dnl don't remove the below
+ dnl AC_OUTPUT(taglib-config)
+diff -dPNur taglib-1.5/configure.in.in taglib-1.5-ds/configure.in.in
+--- taglib-1.5/configure.in.in 2008-01-30 02:34:06.000000000 +0100
++++ taglib-1.5-ds/configure.in.in 2008-11-20 15:24:19.000000000 +0100
+@@ -94,6 +94,21 @@
+
+ AC_SUBST(AUTODIRS)
+
++AC_CHECK_LIB(rcc, rccInit,[
++ AC_CHECK_HEADERS(librcc.h,[
++ LIBRCC_LIBS="-lrcc"
++ LIBRCC_INCLUDES="-DHAVE_LIBRCC"
++ ],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])
++AC_SUBST(LIBRCC_LIBS)
++AC_SUBST(LIBRCC_INCLUDES)
++
++
+ #AM_INIT_AUTOMAKE(taglib,1.0)
+ dnl don't remove the below
+ dnl AC_OUTPUT(taglib-config)
+diff -dPNur taglib-1.5/taglib/CMakeLists.txt taglib-1.5-ds/taglib/CMakeLists.txt
+--- taglib-1.5/taglib/CMakeLists.txt 2008-02-12 05:15:20.000000000 +0100
++++ taglib-1.5-ds/taglib/CMakeLists.txt 2008-11-20 15:58:31.000000000 +0100
+@@ -119,6 +119,7 @@
+ )
+
+ SET(toolkit_SRCS
++toolkit/rccpatch.cpp
+ toolkit/tstring.cpp
+ toolkit/tstringlist.cpp
+ toolkit/tbytevector.cpp
+@@ -140,7 +141,7 @@
+
+ ADD_LIBRARY(tag SHARED ${tag_LIB_SRCS})
+
+-TARGET_LINK_LIBRARIES(tag )
++TARGET_LINK_LIBRARIES(tag rcc)
+ if(ZLIB_FOUND)
+ TARGET_LINK_LIBRARIES(tag ${ZLIB_LIBRARIES})
+ endif(ZLIB_FOUND)
+diff -dPNur taglib-1.5/taglib/mpeg/id3v1/id3v1tag.cpp taglib-1.5-ds/taglib/mpeg/id3v1/id3v1tag.cpp
+--- taglib-1.5/taglib/mpeg/id3v1/id3v1tag.cpp 2008-02-04 16:14:45.000000000 +0100
++++ taglib-1.5-ds/taglib/mpeg/id3v1/id3v1tag.cpp 2008-11-20 15:24:19.000000000 +0100
+@@ -59,17 +59,18 @@
+
+ String ID3v1::StringHandler::parse(const ByteVector &data) const
+ {
+- return String(data, String::Latin1).stripWhiteSpace();
++ return String(data, String::Latin1ID3).stripWhiteSpace();
+ }
+
+ ByteVector ID3v1::StringHandler::render(const String &s) const
+ {
+ if(!s.isLatin1())
+ {
++ if (String::ID3WType(String::Latin1) == String::Latin1)
+ return ByteVector();
+ }
+
+- return s.data(String::Latin1);
++ return s.data(String::Latin1ID3);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+@@ -240,7 +241,7 @@
+ d->track = uchar(data[offset + 29]);
+ }
+ else
+- d->comment = data.mid(offset, 30);
++ d->comment = TagPrivate::stringHandler->parse(data.mid(offset, 30));
+
+ offset += 30;
+
+diff -dPNur taglib-1.5/taglib/mpeg/id3v2/frames/commentsframe.cpp taglib-1.5-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp
+--- taglib-1.5/taglib/mpeg/id3v2/frames/commentsframe.cpp 2008-02-04 16:14:46.000000000 +0100
++++ taglib-1.5-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp 2008-11-20 15:24:19.000000000 +0100
+@@ -136,10 +136,10 @@
+ return;
+ }
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+ d->language = data.mid(1, 3);
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = d->textEncoding == (String::Latin1 || String::Latin1ID3 || String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
+
+@@ -155,10 +155,12 @@
+
+ String::Type encoding = d->textEncoding;
+
++ encoding = String::ID3WType(encoding);
++
+ encoding = checkEncoding(d->description, encoding);
+ encoding = checkEncoding(d->text, encoding);
+-
+- v.append(char(encoding));
++
++ v.append(char(String::ID3RealType(encoding)));
+ v.append(d->language.size() == 3 ? d->language : "XXX");
+ v.append(d->description.data(encoding));
+ v.append(textDelimiter(encoding));
+diff -dPNur taglib-1.5/taglib/mpeg/id3v2/frames/textidentificationframe.cpp taglib-1.5-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp
+--- taglib-1.5/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2008-02-04 16:14:46.000000000 +0100
++++ taglib-1.5-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2008-11-20 15:24:19.000000000 +0100
+@@ -105,12 +105,12 @@
+
+ // read the string data type (the first byte of the field data)
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+
+ // split the byte array into chunks based on the string type (two byte delimiter
+ // for unicode encodings)
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ // build a small counter to strip nulls off the end of the field
+
+@@ -139,11 +139,14 @@
+
+ ByteVector TextIdentificationFrame::renderFields() const
+ {
+- String::Type encoding = checkEncoding(d->fieldList, d->textEncoding);
++ String::Type encoding = d->textEncoding;
++
++ encoding = String::ID3WType(encoding);
++ encoding = checkEncoding(d->fieldList, encoding);
+
+ ByteVector v;
+
+- v.append(char(encoding));
++ v.append(char(String::ID3RealType(encoding)));
+
+ for(StringList::ConstIterator it = d->fieldList.begin(); it != d->fieldList.end(); it++) {
+
+diff -dPNur taglib-1.5/taglib/toolkit/CMakeLists.txt taglib-1.5-ds/taglib/toolkit/CMakeLists.txt
+--- taglib-1.5/taglib/toolkit/CMakeLists.txt 2008-01-11 01:54:01.000000000 +0100
++++ taglib-1.5-ds/taglib/toolkit/CMakeLists.txt 2008-11-20 15:58:31.000000000 +0100
+@@ -1 +1 @@
+-INSTALL( FILES taglib.h tstring.h tlist.h tlist.tcc tstringlist.h tbytevector.h tbytevectorlist.h tfile.h tmap.h tmap.tcc DESTINATION ${INCLUDE_INSTALL_DIR}/taglib)
++INSTALL( FILES rccpatch.h taglib.h tstring.h tlist.h tlist.tcc tstringlist.h tbytevector.h tbytevectorlist.h tfile.h tmap.h tmap.tcc DESTINATION ${INCLUDE_INSTALL_DIR}/taglib)
+diff -dPNur taglib-1.5/taglib/toolkit/Makefile.am taglib-1.5-ds/taglib/toolkit/Makefile.am
+--- taglib-1.5/taglib/toolkit/Makefile.am 2008-01-11 01:54:01.000000000 +0100
++++ taglib-1.5-ds/taglib/toolkit/Makefile.am 2008-11-20 15:24:19.000000000 +0100
+@@ -1,14 +1,20 @@
+ INCLUDES = \
+ -I$(top_srcdir)/taglib \
+- $(all_includes)
++ $(all_includes) \
++ @LIBRCC_INCLUDES@
+
+ noinst_LTLIBRARIES = libtoolkit.la
+
++libtoolkit_la_LDFLAGS = @LIBRCC_LIBS@
++
+ libtoolkit_la_SOURCES = \
++ rccpatch.cpp \
+ tstring.cpp tstringlist.cpp tbytevector.cpp \
+ tbytevectorlist.cpp tfile.cpp tdebug.cpp unicode.cpp
+
++
+ taglib_include_HEADERS = \
++ rccpatch.h \
+ taglib.h tstring.h tlist.h tlist.tcc tstringlist.h \
+ tbytevector.h tbytevectorlist.h tfile.h \
+ tmap.h tmap.tcc
+diff -dPNur taglib-1.5/taglib/toolkit/rccpatch.cpp taglib-1.5-ds/taglib/toolkit/rccpatch.cpp
+--- taglib-1.5/taglib/toolkit/rccpatch.cpp 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.5-ds/taglib/toolkit/rccpatch.cpp 2008-11-20 15:58:31.000000000 +0100
+@@ -0,0 +1,198 @@
++#include <stdlib.h>
++
++#include <string>
++#include "tstring.h"
++#include "tbytevector.h"
++
++#ifndef HAVE_LIBRCC
++# include <config.h>
++#endif
++
++#ifdef HAVE_LIBRCC
++# include <librcc.h>
++# include <string.h>
++#endif /* HAVE_LIBRCC */
++
++
++#ifdef HAVE_LIBRCC
++# define ID3_CLASS 0
++# define ID3V2_CLASS 1
++# define UTF_CLASS 2
++# define OUT_CLASS 3
++static rcc_class classes[] = {
++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
++ { "id3v2", RCC_CLASS_STANDARD, "id3", NULL, "ID3 v.2 Encoding", 0 },
++ { "utf", RCC_CLASS_KNOWN, "UTF-8", NULL, "Unicode Encoding", 0},
++ { "out", RCC_CLASS_TRANSLATE_LOCALE, "LC_CTYPE", NULL, "Output Encoding", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int rcc_initialized = 0;
++
++static rcc_context ctx = NULL;
++#endif /* HAVE_LIBRCC */
++
++
++void rccPatchFree() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) {
++ rccFree();
++ rcc_initialized = 0;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++void rccPatchInit() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) return;
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "xmms");
++ rccInitDb4(NULL, NULL, 0);
++ rcc_initialized = 1;
++#endif /* HAVE_LIBRCC */
++}
++
++void rccPatchSetContext(void *newctx) {
++#ifdef HAVE_LIBRCC
++ if (newctx) {
++ ctx = (rcc_context)newctx;
++ rcc_initialized = 1;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++static void rccPatchTryInit() {
++#ifdef HAVE_LIBRCC
++ if (!rcc_initialized) {
++ rccPatchInit();
++ if (rcc_initialized) atexit(rccPatchFree);
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++
++TagLib::ByteVector rccPatchRecodeOutput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, OUT_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeOutputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, v2?ID3V2_CLASS:ID3_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeInput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, OUT_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeInputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, v2?ID3V2_CLASS:ID3_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::String::Type rccPatchGetLocaleType() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ char charset[32];
++
++ rccPatchTryInit();
++
++ if (!rccLocaleGetCharset(charset, NULL, 31)) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::UTF8;
++}
++
++TagLib::String::Type rccPatchGetID3Type() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ const char *charset;
++
++ rccPatchTryInit();
++
++ charset = rccGetCurrentCharsetName(ctx, ID3V2_CLASS);
++ if (charset) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1ID3V2;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::Latin1;
++}
+diff -dPNur taglib-1.5/taglib/toolkit/rccpatch.h taglib-1.5-ds/taglib/toolkit/rccpatch.h
+--- taglib-1.5/taglib/toolkit/rccpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.5-ds/taglib/toolkit/rccpatch.h 2008-11-20 15:24:19.000000000 +0100
+@@ -0,0 +1,20 @@
++#ifndef _RCC_PATCH_H
++#define _RCC_PATCH_H
++
++#include <string.h>
++#include "tstring.h"
++#include "tbytevector.h"
++
++void rccPatchFree();
++void rccPatchInit();
++void rccPatchSetContext(void *newctx);
++
++TagLib::ByteVector rccPatchRecodeOutput(const std::string &s);
++TagLib::ByteVector rccPatchRecodeInput(const std::string &s);
++TagLib::ByteVector rccPatchRecodeOutputID3(const std::string &s, bool v2 = false);
++TagLib::ByteVector rccPatchRecodeInputID3(const std::string &s, bool v2 = false);
++
++TagLib::String::Type rccPatchGetLocaleType();
++TagLib::String::Type rccPatchGetID3Type();
++
++#endif /* _RCC_PATCH_H */
+diff -dPNur taglib-1.5/taglib/toolkit/tstring.cpp taglib-1.5-ds/taglib/toolkit/tstring.cpp
+--- taglib-1.5/taglib/toolkit/tstring.cpp 2008-02-04 16:14:45.000000000 +0100
++++ taglib-1.5-ds/taglib/toolkit/tstring.cpp 2008-11-20 15:24:21.000000000 +0100
+@@ -23,6 +23,7 @@
+ * http://www.mozilla.org/MPL/ *
+ ***************************************************************************/
+
++#include "rccpatch.h"
+ #include "tstring.h"
+ #include "unicode.h"
+ #include "tdebug.h"
+@@ -167,7 +168,7 @@
+ if(v.isEmpty())
+ return;
+
+- if(t == Latin1 || t == UTF8) {
++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2 || t == UTF8) {
+
+ int length = 0;
+ d->data.resize(v.size());
+@@ -358,10 +359,21 @@
+ {
+ ByteVector v;
+
+- switch(t) {
++ if (t == Locale) t = rccPatchGetLocaleType();
+
++ switch(t) {
++ case Locale:
+ case Latin1:
++ case Latin1ID3:
++ case Latin1ID3V2:
+ {
++ std::string s = to8Bit(true);
++ if (t == Latin1ID3) v = rccPatchRecodeOutputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccPatchRecodeOutputID3(s, true);
++ else /* if (t == Latin1(Locale) */ v = rccPatchRecodeOutput(s);
++
++ if (v.size()) return v;
++
+ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
+ v.append(char(*it));
+ break;
+@@ -692,6 +704,31 @@
+
+ void String::prepare(Type t)
+ {
++ if (t == Locale) t = rccPatchGetLocaleType();
++
++ if ((t == Latin1)||(t == Latin1ID3)||(t == Latin1ID3V2)) {
++ std::string s = to8Bit(false);
++ ByteVector v;
++
++ if (t == Latin1ID3) v = rccPatchRecodeInputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccPatchRecodeInputID3(s, true);
++ else /* Latin1 converted from Locale */ v = rccPatchRecodeInput(s);
++
++ if (v.size()) {
++ int length = 0;
++ d->data.resize(v.size());
++ wstring::iterator targetIt = d->data.begin();
++ for(ByteVector::ConstIterator it = v.begin(); it != v.end() && (*it); ++it) {
++ *targetIt = uchar(*it);
++ ++targetIt;
++ ++length;
++ }
++ d->data.resize(length);
++ }
++
++ t = UTF8;
++ }
++
+ switch(t) {
+ case UTF16:
+ {
+@@ -781,6 +818,27 @@
+
+ std::ostream &operator<<(std::ostream &s, const String &str)
+ {
+- s << str.to8Bit();
++ ByteVector bv = str.data(String::Locale);
++ s << bv;
+ return s;
+ }
++
++String::Type String::ID3Type(int i) {
++ if (i == Latin1) return Latin1ID3V2;
++ return Type(i);
++};
++
++String::Type String::ID3WType(Type type) {
++ Type rcc_type = rccPatchGetID3Type();
++ if ((rcc_type == Latin1ID3)||(rcc_type == Latin1ID3V2)) {
++ if (type == Latin1) return rcc_type;
++ return type;
++ }
++
++ return rcc_type;
++};
++
++String::Type String::ID3RealType(Type type) {
++ if ((type == Latin1ID3)||(type == Latin1ID3V2)) return Latin1;
++ return type;
++}
+diff -dPNur taglib-1.5/taglib/toolkit/tstring.h taglib-1.5-ds/taglib/toolkit/tstring.h
+--- taglib-1.5/taglib/toolkit/tstring.h 2008-02-04 16:14:45.000000000 +0100
++++ taglib-1.5-ds/taglib/toolkit/tstring.h 2008-11-20 15:24:21.000000000 +0100
+@@ -81,6 +81,18 @@
+ */
+ enum Type {
+ /*!
++ * Determine using current locale settings
++ */
++ Locale = -1,
++ /*!
++ * Latin1 for ID3 tags.
++ */
++ Latin1ID3 = 65,
++ /*!
++ * Latin1 for ID3 tags.
++ */
++ Latin1ID3V2 = 66,
++ /*!
+ * IS08859-1, or <i>Latin1</i> encoding. 8 bit characters.
+ */
+ Latin1 = 0,
+@@ -103,6 +115,10 @@
+ UTF16LE = 4
+ };
+
++ static Type ID3Type(int i);
++ static Type ID3WType(Type type);
++ static Type ID3RealType(Type type);
++
+ /*!
+ * Constructs an empty String.
+ */
diff --git a/patches/taglib/taglib-1.6-ds-rusxmms.patch b/patches/taglib/taglib-1.6-ds-rusxmms.patch
new file mode 100644
index 0000000..5eb1a2a
--- /dev/null
+++ b/patches/taglib/taglib-1.6-ds-rusxmms.patch
@@ -0,0 +1,587 @@
+diff -dPNur taglib-1.6/config-taglib.h.cmake taglib-1.6-ds/config-taglib.h.cmake
+--- taglib-1.6/config-taglib.h.cmake 2008-11-12 09:17:11.000000000 +0100
++++ taglib-1.6-ds/config-taglib.h.cmake 2009-10-02 17:53:08.000000000 +0200
+@@ -6,6 +6,8 @@
+ /* Define if you have libz */
+ #cmakedefine HAVE_ZLIB 1
+
++#cmakedefine HAVE_LIBRCC 1
++
+ #cmakedefine NO_ITUNES_HACKS 1
+ #cmakedefine WITH_ASF 1
+ #cmakedefine WITH_MP4 1
+diff -dPNur taglib-1.6/ConfigureChecks.cmake taglib-1.6-ds/ConfigureChecks.cmake
+--- taglib-1.6/ConfigureChecks.cmake 2008-12-21 22:46:41.000000000 +0100
++++ taglib-1.6-ds/ConfigureChecks.cmake 2009-10-02 17:53:08.000000000 +0200
+@@ -14,6 +14,8 @@
+ #check for libz using the cmake supplied FindZLIB.cmake
+ FIND_PACKAGE(ZLIB)
+
++SET(HAVE_LIBRCC 1)
++
+ IF(ZLIB_FOUND)
+ SET(HAVE_ZLIB 1)
+ ELSE(ZLIB_FOUND)
+diff -dPNur taglib-1.6/configure.in taglib-1.6-ds/configure.in
+--- taglib-1.6/configure.in 2009-09-13 13:58:46.000000000 +0200
++++ taglib-1.6-ds/configure.in 2009-10-02 17:53:08.000000000 +0200
+@@ -99,6 +99,20 @@
+
+ AC_SUBST(AUTODIRS)
+
++AC_CHECK_LIB(rcc, rccInit,[
++ AC_CHECK_HEADERS(librcc.h,[
++ LIBRCC_LIBS="-lrcc"
++ LIBRCC_INCLUDES="-DHAVE_LIBRCC"
++ ],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])
++AC_SUBST(LIBRCC_LIBS)
++AC_SUBST(LIBRCC_INCLUDES)
++
+ #AM_INIT_AUTOMAKE(taglib,1.0)
+ dnl don't remove the below
+ dnl AC_OUTPUT(taglib-config)
+diff -dPNur taglib-1.6/configure.in.in taglib-1.6-ds/configure.in.in
+--- taglib-1.6/configure.in.in 2009-09-13 13:30:19.000000000 +0200
++++ taglib-1.6-ds/configure.in.in 2009-10-02 17:53:08.000000000 +0200
+@@ -95,6 +95,21 @@
+
+ AC_SUBST(AUTODIRS)
+
++AC_CHECK_LIB(rcc, rccInit,[
++ AC_CHECK_HEADERS(librcc.h,[
++ LIBRCC_LIBS="-lrcc"
++ LIBRCC_INCLUDES="-DHAVE_LIBRCC"
++ ],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])
++AC_SUBST(LIBRCC_LIBS)
++AC_SUBST(LIBRCC_INCLUDES)
++
++
+ #AM_INIT_AUTOMAKE(taglib,1.0)
+ dnl don't remove the below
+ dnl AC_OUTPUT(taglib-config)
+diff -dPNur taglib-1.6/taglib/CMakeLists.txt taglib-1.6-ds/taglib/CMakeLists.txt
+--- taglib-1.6/taglib/CMakeLists.txt 2009-09-13 12:19:34.000000000 +0200
++++ taglib-1.6-ds/taglib/CMakeLists.txt 2009-10-02 17:53:08.000000000 +0200
+@@ -163,6 +163,7 @@
+ )
+
+ SET(toolkit_SRCS
++toolkit/rccpatch.cpp
+ toolkit/tstring.cpp
+ toolkit/tstringlist.cpp
+ toolkit/tbytevector.cpp
+@@ -190,7 +191,7 @@
+ add_library(tag SHARED ${tag_LIB_SRCS})
+ endif(ENABLE_STATIC)
+
+-TARGET_LINK_LIBRARIES(tag )
++TARGET_LINK_LIBRARIES(tag rcc)
+ if(ZLIB_FOUND)
+ TARGET_LINK_LIBRARIES(tag ${ZLIB_LIBRARIES})
+ endif(ZLIB_FOUND)
+diff -dPNur taglib-1.6/taglib/mpeg/id3v1/id3v1tag.cpp taglib-1.6-ds/taglib/mpeg/id3v1/id3v1tag.cpp
+--- taglib-1.6/taglib/mpeg/id3v1/id3v1tag.cpp 2008-02-04 16:11:56.000000000 +0100
++++ taglib-1.6-ds/taglib/mpeg/id3v1/id3v1tag.cpp 2009-10-02 17:53:08.000000000 +0200
+@@ -59,17 +59,18 @@
+
+ String ID3v1::StringHandler::parse(const ByteVector &data) const
+ {
+- return String(data, String::Latin1).stripWhiteSpace();
++ return String(data, String::Latin1ID3).stripWhiteSpace();
+ }
+
+ ByteVector ID3v1::StringHandler::render(const String &s) const
+ {
+ if(!s.isLatin1())
+ {
++ if (String::ID3WType(String::Latin1) == String::Latin1)
+ return ByteVector();
+ }
+
+- return s.data(String::Latin1);
++ return s.data(String::Latin1ID3);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+@@ -240,7 +241,7 @@
+ d->track = uchar(data[offset + 29]);
+ }
+ else
+- d->comment = data.mid(offset, 30);
++ d->comment = TagPrivate::stringHandler->parse(data.mid(offset, 30));
+
+ offset += 30;
+
+diff -dPNur taglib-1.6/taglib/mpeg/id3v2/frames/commentsframe.cpp taglib-1.6-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp
+--- taglib-1.6/taglib/mpeg/id3v2/frames/commentsframe.cpp 2008-02-04 16:11:56.000000000 +0100
++++ taglib-1.6-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp 2009-10-02 17:53:08.000000000 +0200
+@@ -136,10 +136,10 @@
+ return;
+ }
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+ d->language = data.mid(1, 3);
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = d->textEncoding == (String::Latin1 || String::Latin1ID3 || String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
+
+@@ -155,10 +155,12 @@
+
+ String::Type encoding = d->textEncoding;
+
++ encoding = String::ID3WType(encoding);
++
+ encoding = checkEncoding(d->description, encoding);
+ encoding = checkEncoding(d->text, encoding);
+-
+- v.append(char(encoding));
++
++ v.append(char(String::ID3RealType(encoding)));
+ v.append(d->language.size() == 3 ? d->language : "XXX");
+ v.append(d->description.data(encoding));
+ v.append(textDelimiter(encoding));
+diff -dPNur taglib-1.6/taglib/mpeg/id3v2/frames/textidentificationframe.cpp taglib-1.6-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp
+--- taglib-1.6/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2008-02-04 16:11:56.000000000 +0100
++++ taglib-1.6-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2009-10-02 17:53:08.000000000 +0200
+@@ -105,12 +105,12 @@
+
+ // read the string data type (the first byte of the field data)
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+
+ // split the byte array into chunks based on the string type (two byte delimiter
+ // for unicode encodings)
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ // build a small counter to strip nulls off the end of the field
+
+@@ -139,11 +139,14 @@
+
+ ByteVector TextIdentificationFrame::renderFields() const
+ {
+- String::Type encoding = checkEncoding(d->fieldList, d->textEncoding);
++ String::Type encoding = d->textEncoding;
++
++ encoding = String::ID3WType(encoding);
++ encoding = checkEncoding(d->fieldList, encoding);
+
+ ByteVector v;
+
+- v.append(char(encoding));
++ v.append(char(String::ID3RealType(encoding)));
+
+ for(StringList::ConstIterator it = d->fieldList.begin(); it != d->fieldList.end(); it++) {
+
+diff -dPNur taglib-1.6/taglib/toolkit/CMakeLists.txt taglib-1.6-ds/taglib/toolkit/CMakeLists.txt
+--- taglib-1.6/taglib/toolkit/CMakeLists.txt 2006-09-20 14:52:28.000000000 +0200
++++ taglib-1.6-ds/taglib/toolkit/CMakeLists.txt 2009-10-02 17:53:08.000000000 +0200
+@@ -1 +1 @@
+-INSTALL( FILES taglib.h tstring.h tlist.h tlist.tcc tstringlist.h tbytevector.h tbytevectorlist.h tfile.h tmap.h tmap.tcc DESTINATION ${INCLUDE_INSTALL_DIR}/taglib)
++INSTALL( FILES rccpatch.h taglib.h tstring.h tlist.h tlist.tcc tstringlist.h tbytevector.h tbytevectorlist.h tfile.h tmap.h tmap.tcc DESTINATION ${INCLUDE_INSTALL_DIR}/taglib)
+diff -dPNur taglib-1.6/taglib/toolkit/Makefile.am taglib-1.6-ds/taglib/toolkit/Makefile.am
+--- taglib-1.6/taglib/toolkit/Makefile.am 2009-09-04 11:07:27.000000000 +0200
++++ taglib-1.6-ds/taglib/toolkit/Makefile.am 2009-10-03 02:34:01.000000000 +0200
+@@ -1,15 +1,21 @@
+ DEFS = -DMAKE_TAGLIB_LIB @DEFS@
+ INCLUDES = \
+ -I$(top_srcdir)/taglib \
+- $(all_includes)
++ $(all_includes) \
++ @LIBRCC_INCLUDES@
++
++libtoolkit_la_LIBADD = @LIBRCC_LIBS@
+
+ noinst_LTLIBRARIES = libtoolkit.la
+
+ libtoolkit_la_SOURCES = \
++ rccpatch.cpp \
+ tstring.cpp tstringlist.cpp tbytevector.cpp \
+ tbytevectorlist.cpp tfile.cpp tdebug.cpp unicode.cpp
+
++
+ taglib_include_HEADERS = \
++ rccpatch.h \
+ taglib.h tstring.h tlist.h tlist.tcc tstringlist.h \
+ tbytevector.h tbytevectorlist.h tfile.h \
+ tmap.h tmap.tcc
+diff -dPNur taglib-1.6/taglib/toolkit/rccpatch.cpp taglib-1.6-ds/taglib/toolkit/rccpatch.cpp
+--- taglib-1.6/taglib/toolkit/rccpatch.cpp 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.6-ds/taglib/toolkit/rccpatch.cpp 2009-10-02 17:53:08.000000000 +0200
+@@ -0,0 +1,198 @@
++#include <stdlib.h>
++
++#include <string>
++#include "tstring.h"
++#include "tbytevector.h"
++
++#ifndef HAVE_LIBRCC
++# include <config.h>
++#endif
++
++#ifdef HAVE_LIBRCC
++# include <librcc.h>
++# include <string.h>
++#endif /* HAVE_LIBRCC */
++
++
++#ifdef HAVE_LIBRCC
++# define ID3_CLASS 0
++# define ID3V2_CLASS 1
++# define UTF_CLASS 2
++# define OUT_CLASS 3
++static rcc_class classes[] = {
++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
++ { "id3v2", RCC_CLASS_STANDARD, "id3", NULL, "ID3 v.2 Encoding", 0 },
++ { "utf", RCC_CLASS_KNOWN, "UTF-8", NULL, "Unicode Encoding", 0},
++ { "out", RCC_CLASS_TRANSLATE_LOCALE, "LC_CTYPE", NULL, "Output Encoding", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int rcc_initialized = 0;
++
++static rcc_context ctx = NULL;
++#endif /* HAVE_LIBRCC */
++
++
++void rccPatchFree() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) {
++ rccFree();
++ rcc_initialized = 0;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++void rccPatchInit() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) return;
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "xmms");
++ rccInitDb4(NULL, NULL, 0);
++ rcc_initialized = 1;
++#endif /* HAVE_LIBRCC */
++}
++
++void rccPatchSetContext(void *newctx) {
++#ifdef HAVE_LIBRCC
++ if (newctx) {
++ ctx = (rcc_context)newctx;
++ rcc_initialized = 1;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++static void rccPatchTryInit() {
++#ifdef HAVE_LIBRCC
++ if (!rcc_initialized) {
++ rccPatchInit();
++ if (rcc_initialized) atexit(rccPatchFree);
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++
++TagLib::ByteVector rccPatchRecodeOutput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, OUT_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeOutputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, v2?ID3V2_CLASS:ID3_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeInput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, OUT_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeInputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, v2?ID3V2_CLASS:ID3_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::String::Type rccPatchGetLocaleType() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ char charset[32];
++
++ rccPatchTryInit();
++
++ if (!rccLocaleGetCharset(charset, NULL, 31)) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::UTF8;
++}
++
++TagLib::String::Type rccPatchGetID3Type() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ const char *charset;
++
++ rccPatchTryInit();
++
++ charset = rccGetCurrentCharsetName(ctx, ID3V2_CLASS);
++ if (charset) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1ID3V2;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::Latin1;
++}
+diff -dPNur taglib-1.6/taglib/toolkit/rccpatch.h taglib-1.6-ds/taglib/toolkit/rccpatch.h
+--- taglib-1.6/taglib/toolkit/rccpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.6-ds/taglib/toolkit/rccpatch.h 2009-10-02 17:53:08.000000000 +0200
+@@ -0,0 +1,20 @@
++#ifndef _RCC_PATCH_H
++#define _RCC_PATCH_H
++
++#include <string.h>
++#include "tstring.h"
++#include "tbytevector.h"
++
++void rccPatchFree();
++void rccPatchInit();
++void rccPatchSetContext(void *newctx);
++
++TagLib::ByteVector rccPatchRecodeOutput(const std::string &s);
++TagLib::ByteVector rccPatchRecodeInput(const std::string &s);
++TagLib::ByteVector rccPatchRecodeOutputID3(const std::string &s, bool v2 = false);
++TagLib::ByteVector rccPatchRecodeInputID3(const std::string &s, bool v2 = false);
++
++TagLib::String::Type rccPatchGetLocaleType();
++TagLib::String::Type rccPatchGetID3Type();
++
++#endif /* _RCC_PATCH_H */
+diff -dPNur taglib-1.6/taglib/toolkit/tstring.cpp taglib-1.6-ds/taglib/toolkit/tstring.cpp
+--- taglib-1.6/taglib/toolkit/tstring.cpp 2009-04-29 17:57:05.000000000 +0200
++++ taglib-1.6-ds/taglib/toolkit/tstring.cpp 2009-10-02 17:53:08.000000000 +0200
+@@ -23,6 +23,7 @@
+ * http://www.mozilla.org/MPL/ *
+ ***************************************************************************/
+
++#include "rccpatch.h"
+ #include "tstring.h"
+ #include "unicode.h"
+ #include "tdebug.h"
+@@ -167,7 +168,7 @@
+ if(v.isEmpty())
+ return;
+
+- if(t == Latin1 || t == UTF8) {
++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2 || t == UTF8) {
+
+ int length = 0;
+ d->data.resize(v.size());
+@@ -369,10 +370,21 @@
+ {
+ ByteVector v;
+
+- switch(t) {
++ if (t == Locale) t = rccPatchGetLocaleType();
+
++ switch(t) {
++ case Locale:
+ case Latin1:
++ case Latin1ID3:
++ case Latin1ID3V2:
+ {
++ std::string s = to8Bit(true);
++ if (t == Latin1ID3) v = rccPatchRecodeOutputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccPatchRecodeOutputID3(s, true);
++ else /* if (t == Latin1(Locale) */ v = rccPatchRecodeOutput(s);
++
++ if (v.size()) return v;
++
+ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
+ v.append(char(*it));
+ break;
+@@ -707,6 +719,31 @@
+
+ void String::prepare(Type t)
+ {
++ if (t == Locale) t = rccPatchGetLocaleType();
++
++ if ((t == Latin1)||(t == Latin1ID3)||(t == Latin1ID3V2)) {
++ std::string s = to8Bit(false);
++ ByteVector v;
++
++ if (t == Latin1ID3) v = rccPatchRecodeInputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccPatchRecodeInputID3(s, true);
++ else /* Latin1 converted from Locale */ v = rccPatchRecodeInput(s);
++
++ if (v.size()) {
++ int length = 0;
++ d->data.resize(v.size());
++ wstring::iterator targetIt = d->data.begin();
++ for(ByteVector::ConstIterator it = v.begin(); it != v.end() && (*it); ++it) {
++ *targetIt = uchar(*it);
++ ++targetIt;
++ ++length;
++ }
++ d->data.resize(length);
++ }
++
++ t = UTF8;
++ }
++
+ switch(t) {
+ case UTF16:
+ {
+@@ -796,6 +833,27 @@
+
+ std::ostream &operator<<(std::ostream &s, const String &str)
+ {
+- s << str.to8Bit();
++ ByteVector bv = str.data(String::Locale);
++ s << bv;
+ return s;
+ }
++
++String::Type String::ID3Type(int i) {
++ if (i == Latin1) return Latin1ID3V2;
++ return Type(i);
++};
++
++String::Type String::ID3WType(Type type) {
++ Type rcc_type = rccPatchGetID3Type();
++ if ((rcc_type == Latin1ID3)||(rcc_type == Latin1ID3V2)) {
++ if (type == Latin1) return rcc_type;
++ return type;
++ }
++
++ return rcc_type;
++};
++
++String::Type String::ID3RealType(Type type) {
++ if ((type == Latin1ID3)||(type == Latin1ID3V2)) return Latin1;
++ return type;
++}
+diff -dPNur taglib-1.6/taglib/toolkit/tstring.h taglib-1.6-ds/taglib/toolkit/tstring.h
+--- taglib-1.6/taglib/toolkit/tstring.h 2009-07-02 22:54:32.000000000 +0200
++++ taglib-1.6-ds/taglib/toolkit/tstring.h 2009-10-02 17:53:08.000000000 +0200
+@@ -81,6 +81,18 @@
+ */
+ enum Type {
+ /*!
++ * Determine using current locale settings
++ */
++ Locale = -1,
++ /*!
++ * Latin1 for ID3 tags.
++ */
++ Latin1ID3 = 65,
++ /*!
++ * Latin1 for ID3 tags.
++ */
++ Latin1ID3V2 = 66,
++ /*!
+ * IS08859-1, or <i>Latin1</i> encoding. 8 bit characters.
+ */
+ Latin1 = 0,
+@@ -103,6 +115,10 @@
+ UTF16LE = 4
+ };
+
++ static Type ID3Type(int i);
++ static Type ID3WType(Type type);
++ static Type ID3RealType(Type type);
++
+ /*!
+ * Constructs an empty String.
+ */
diff --git a/patches/taglib/taglib-1.7-ds-rusxmms-r2.patch b/patches/taglib/taglib-1.7-ds-rusxmms-r2.patch
new file mode 100644
index 0000000..0757692
--- /dev/null
+++ b/patches/taglib/taglib-1.7-ds-rusxmms-r2.patch
@@ -0,0 +1,506 @@
+diff -dPNur taglib-1.7.2/config-taglib.h.cmake taglib-1.7.2-ds/config-taglib.h.cmake
+--- taglib-1.7.2/config-taglib.h.cmake 2012-04-20 17:57:13.000000000 +0200
++++ taglib-1.7.2-ds/config-taglib.h.cmake 2013-01-29 12:30:59.000000000 +0100
+@@ -6,6 +6,8 @@
+ /* Define if you have libz */
+ #cmakedefine HAVE_ZLIB 1
+
++#cmakedefine HAVE_LIBRCC 1
++
+ #cmakedefine NO_ITUNES_HACKS 1
+ #cmakedefine WITH_ASF 1
+ #cmakedefine WITH_MP4 1
+diff -dPNur taglib-1.7.2/ConfigureChecks.cmake taglib-1.7.2-ds/ConfigureChecks.cmake
+--- taglib-1.7.2/ConfigureChecks.cmake 2012-04-20 17:57:13.000000000 +0200
++++ taglib-1.7.2-ds/ConfigureChecks.cmake 2013-01-29 12:30:59.000000000 +0100
+@@ -14,6 +14,8 @@
+ #check for libz using the cmake supplied FindZLIB.cmake
+ FIND_PACKAGE(ZLIB)
+
++SET(HAVE_LIBRCC 1)
++
+ IF(ZLIB_FOUND)
+ SET(HAVE_ZLIB 1)
+ ELSE(ZLIB_FOUND)
+diff -dPNur taglib-1.7.2/taglib/CMakeLists.txt taglib-1.7.2-ds/taglib/CMakeLists.txt
+--- taglib-1.7.2/taglib/CMakeLists.txt 2012-04-20 17:57:13.000000000 +0200
++++ taglib-1.7.2-ds/taglib/CMakeLists.txt 2013-01-29 12:30:59.000000000 +0100
+@@ -171,6 +171,7 @@
+ )
+
+ SET(toolkit_SRCS
++toolkit/rccpatch.cpp
+ toolkit/tstring.cpp
+ toolkit/tstringlist.cpp
+ toolkit/tbytevector.cpp
+@@ -198,7 +199,7 @@
+ add_library(tag SHARED ${tag_LIB_SRCS})
+ endif(ENABLE_STATIC)
+
+-TARGET_LINK_LIBRARIES(tag )
++TARGET_LINK_LIBRARIES(tag rcc)
+ if(ZLIB_FOUND)
+ TARGET_LINK_LIBRARIES(tag ${ZLIB_LIBRARIES})
+ endif(ZLIB_FOUND)
+diff -dPNur taglib-1.7.2/taglib/CMakeLists.txt.orig taglib-1.7.2-ds/taglib/CMakeLists.txt.orig
+diff -dPNur taglib-1.7.2/taglib/mpeg/id3v1/id3v1tag.cpp taglib-1.7.2-ds/taglib/mpeg/id3v1/id3v1tag.cpp
+--- taglib-1.7.2/taglib/mpeg/id3v1/id3v1tag.cpp 2012-04-20 17:57:13.000000000 +0200
++++ taglib-1.7.2-ds/taglib/mpeg/id3v1/id3v1tag.cpp 2013-01-29 12:30:59.000000000 +0100
+@@ -59,17 +59,18 @@
+
+ String ID3v1::StringHandler::parse(const ByteVector &data) const
+ {
+- return String(data, String::Latin1).stripWhiteSpace();
++ return String(data, String::Latin1ID3).stripWhiteSpace();
+ }
+
+ ByteVector ID3v1::StringHandler::render(const String &s) const
+ {
+ if(!s.isLatin1())
+ {
++ if (String::ID3WType(String::Latin1) == String::Latin1)
+ return ByteVector();
+ }
+
+- return s.data(String::Latin1);
++ return s.data(String::Latin1ID3);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+@@ -240,7 +241,7 @@
+ d->track = uchar(data[offset + 29]);
+ }
+ else
+- d->comment = data.mid(offset, 30);
++ d->comment = TagPrivate::stringHandler->parse(data.mid(offset, 30));
+
+ offset += 30;
+
+diff -dPNur taglib-1.7.2/taglib/mpeg/id3v2/frames/commentsframe.cpp taglib-1.7.2-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp
+--- taglib-1.7.2/taglib/mpeg/id3v2/frames/commentsframe.cpp 2012-04-20 17:57:13.000000000 +0200
++++ taglib-1.7.2-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp 2013-01-29 12:30:59.000000000 +0100
+@@ -136,10 +136,10 @@
+ return;
+ }
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+ d->language = data.mid(1, 3);
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
+
+@@ -155,10 +155,12 @@
+
+ String::Type encoding = d->textEncoding;
+
++ encoding = String::ID3WType(encoding);
++
+ encoding = checkEncoding(d->description, encoding);
+ encoding = checkEncoding(d->text, encoding);
+-
+- v.append(char(encoding));
++
++ v.append(char(String::ID3RealType(encoding)));
+ v.append(d->language.size() == 3 ? d->language : "XXX");
+ v.append(d->description.data(encoding));
+ v.append(textDelimiter(encoding));
+diff -dPNur taglib-1.7.2/taglib/mpeg/id3v2/frames/textidentificationframe.cpp taglib-1.7.2-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp
+--- taglib-1.7.2/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2012-04-20 17:57:13.000000000 +0200
++++ taglib-1.7.2-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2013-01-29 12:30:59.000000000 +0100
+@@ -105,12 +105,12 @@
+
+ // read the string data type (the first byte of the field data)
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+
+ // split the byte array into chunks based on the string type (two byte delimiter
+ // for unicode encodings)
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ // build a small counter to strip nulls off the end of the field
+
+@@ -139,11 +139,14 @@
+
+ ByteVector TextIdentificationFrame::renderFields() const
+ {
+- String::Type encoding = checkEncoding(d->fieldList, d->textEncoding);
++ String::Type encoding = d->textEncoding;
++
++ encoding = String::ID3WType(encoding);
++ encoding = checkEncoding(d->fieldList, encoding);
+
+ ByteVector v;
+
+- v.append(char(encoding));
++ v.append(char(String::ID3RealType(encoding)));
+
+ for(StringList::ConstIterator it = d->fieldList.begin(); it != d->fieldList.end(); it++) {
+
+diff -dPNur taglib-1.7.2/taglib/toolkit/CMakeLists.txt taglib-1.7.2-ds/taglib/toolkit/CMakeLists.txt
+--- taglib-1.7.2/taglib/toolkit/CMakeLists.txt 2012-04-20 17:57:13.000000000 +0200
++++ taglib-1.7.2-ds/taglib/toolkit/CMakeLists.txt 2013-01-29 12:30:59.000000000 +0100
+@@ -1 +1 @@
+-INSTALL( FILES taglib.h tstring.h tlist.h tlist.tcc tstringlist.h tbytevector.h tbytevectorlist.h tfile.h tmap.h tmap.tcc DESTINATION ${INCLUDE_INSTALL_DIR}/taglib)
++INSTALL( FILES rccpatch.h taglib.h tstring.h tlist.h tlist.tcc tstringlist.h tbytevector.h tbytevectorlist.h tfile.h tmap.h tmap.tcc DESTINATION ${INCLUDE_INSTALL_DIR}/taglib)
+diff -dPNur taglib-1.7.2/taglib/toolkit/rccpatch.cpp taglib-1.7.2-ds/taglib/toolkit/rccpatch.cpp
+--- taglib-1.7.2/taglib/toolkit/rccpatch.cpp 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.7.2-ds/taglib/toolkit/rccpatch.cpp 2013-01-29 12:35:45.000000000 +0100
+@@ -0,0 +1,192 @@
++#include <stdlib.h>
++
++#include <string>
++#include "tstring.h"
++#include "tbytevector.h"
++
++#ifndef HAVE_LIBRCC
++# include <config.h>
++#endif
++
++#ifdef HAVE_LIBRCC
++# include <librcc.h>
++# include <string.h>
++#endif /* HAVE_LIBRCC */
++
++
++#ifdef HAVE_LIBRCC
++# define ID3_CLASS 0
++# define ID3V2_CLASS 1
++# define UTF_CLASS 2
++# define OUT_CLASS 3
++static rcc_class classes[] = {
++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
++ { "id3v2", RCC_CLASS_STANDARD, "id3", NULL, "ID3 v.2 Encoding", 0 },
++ { "utf", RCC_CLASS_KNOWN, "UTF-8", NULL, "Unicode Encoding", 0},
++ { "out", RCC_CLASS_TRANSLATE_LOCALE, "LC_CTYPE", NULL, "Output Encoding", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int rcc_initialized = 0;
++
++static rcc_context ctx = NULL;
++#endif /* HAVE_LIBRCC */
++
++
++void rccPatchFree() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) {
++ rccFree();
++ rcc_initialized = 0;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++void rccPatchInit() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) return;
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "xmms");
++ rccInitDb4(NULL, NULL, 0);
++ rcc_initialized = 1;
++#endif /* HAVE_LIBRCC */
++}
++
++void rccPatchSetContext(void *newctx) {
++#ifdef HAVE_LIBRCC
++ if (newctx) {
++ ctx = (rcc_context)newctx;
++ rcc_initialized = 1;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++static void rccPatchTryInit() {
++#ifdef HAVE_LIBRCC
++ if (!rcc_initialized) {
++ rccPatchInit();
++ if (rcc_initialized) atexit(rccPatchFree);
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++
++TagLib::ByteVector rccPatchRecodeOutput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, OUT_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeOutputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, v2?ID3V2_CLASS:ID3_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeInput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, OUT_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else
++#endif /* HAVE_LIBRCC */
++ v.setData("", 0);
++
++ return v;
++}
++
++TagLib::ByteVector rccPatchRecodeInputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, v2?ID3V2_CLASS:ID3_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else
++#endif /* HAVE_LIBRCC */
++ v.setData("", 0);
++
++ return v;
++}
++
++TagLib::String::Type rccPatchGetLocaleType() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ char charset[32];
++
++ rccPatchTryInit();
++
++ if (!rccLocaleGetCharset(charset, NULL, 31)) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::UTF8;
++}
++
++TagLib::String::Type rccPatchGetID3Type() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ const char *charset;
++
++ rccPatchTryInit();
++
++ charset = rccGetCurrentCharsetName(ctx, ID3V2_CLASS);
++ if (charset) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1ID3V2;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::Latin1;
++}
+diff -dPNur taglib-1.7.2/taglib/toolkit/rccpatch.h taglib-1.7.2-ds/taglib/toolkit/rccpatch.h
+--- taglib-1.7.2/taglib/toolkit/rccpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.7.2-ds/taglib/toolkit/rccpatch.h 2013-01-29 12:30:59.000000000 +0100
+@@ -0,0 +1,20 @@
++#ifndef _RCC_PATCH_H
++#define _RCC_PATCH_H
++
++#include <string.h>
++#include "tstring.h"
++#include "tbytevector.h"
++
++void rccPatchFree();
++void rccPatchInit();
++void rccPatchSetContext(void *newctx);
++
++TagLib::ByteVector rccPatchRecodeOutput(const std::string &s);
++TagLib::ByteVector rccPatchRecodeInput(const std::string &s);
++TagLib::ByteVector rccPatchRecodeOutputID3(const std::string &s, bool v2 = false);
++TagLib::ByteVector rccPatchRecodeInputID3(const std::string &s, bool v2 = false);
++
++TagLib::String::Type rccPatchGetLocaleType();
++TagLib::String::Type rccPatchGetID3Type();
++
++#endif /* _RCC_PATCH_H */
+diff -dPNur taglib-1.7.2/taglib/toolkit/tstring.cpp taglib-1.7.2-ds/taglib/toolkit/tstring.cpp
+--- taglib-1.7.2/taglib/toolkit/tstring.cpp 2012-04-20 17:57:13.000000000 +0200
++++ taglib-1.7.2-ds/taglib/toolkit/tstring.cpp 2013-01-29 12:34:24.000000000 +0100
+@@ -23,6 +23,7 @@
+ * http://www.mozilla.org/MPL/ *
+ ***************************************************************************/
+
++#include "rccpatch.h"
+ #include "tstring.h"
+ #include "unicode.h"
+ #include "tdebug.h"
+@@ -167,7 +168,7 @@
+ if(v.isEmpty())
+ return;
+
+- if(t == Latin1 || t == UTF8) {
++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2 || t == UTF8) {
+
+ int length = 0;
+ d->data.resize(v.size());
+@@ -369,10 +370,21 @@
+ {
+ ByteVector v;
+
+- switch(t) {
++ if (t == Locale) t = rccPatchGetLocaleType();
+
++ switch(t) {
++ case Locale:
+ case Latin1:
++ case Latin1ID3:
++ case Latin1ID3V2:
+ {
++ std::string s = to8Bit(true);
++ if (t == Latin1ID3) v = rccPatchRecodeOutputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccPatchRecodeOutputID3(s, true);
++ else /* if (t == Latin1(Locale) */ v = rccPatchRecodeOutput(s);
++
++ if (v.size()) return v;
++
+ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
+ v.append(char(*it));
+ break;
+@@ -717,6 +729,30 @@
+
+ void String::prepare(Type t)
+ {
++ if (t == Locale) t = rccPatchGetLocaleType();
++
++ if ((t == Latin1)||(t == Latin1ID3)||(t == Latin1ID3V2)) {
++ std::string s = to8Bit(false);
++ ByteVector v;
++
++ if (t == Latin1ID3) v = rccPatchRecodeInputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccPatchRecodeInputID3(s, true);
++ else /* Latin1 converted from Locale */ v = rccPatchRecodeInput(s);
++
++ if (v.size()) {
++ int length = 0;
++ d->data.resize(v.size());
++ wstring::iterator targetIt = d->data.begin();
++ for(ByteVector::ConstIterator it = v.begin(); it != v.end() && (*it); ++it) {
++ *targetIt = uchar(*it);
++ ++targetIt;
++ ++length;
++ }
++ d->data.resize(length);
++ t = UTF8;
++ }
++ }
++
+ switch(t) {
+ case UTF16:
+ {
+@@ -806,6 +842,27 @@
+
+ std::ostream &operator<<(std::ostream &s, const String &str)
+ {
+- s << str.to8Bit();
++ ByteVector bv = str.data(String::Locale);
++ s << bv;
+ return s;
+ }
++
++String::Type String::ID3Type(int i) {
++ if (i == Latin1) return Latin1ID3V2;
++ return Type(i);
++};
++
++String::Type String::ID3WType(Type type) {
++ Type rcc_type = rccPatchGetID3Type();
++ if ((rcc_type == Latin1ID3)||(rcc_type == Latin1ID3V2)) {
++ if (type == Latin1) return rcc_type;
++ return type;
++ }
++
++ return rcc_type;
++};
++
++String::Type String::ID3RealType(Type type) {
++ if ((type == Latin1ID3)||(type == Latin1ID3V2)) return Latin1;
++ return type;
++}
+diff -dPNur taglib-1.7.2/taglib/toolkit/tstring.h taglib-1.7.2-ds/taglib/toolkit/tstring.h
+--- taglib-1.7.2/taglib/toolkit/tstring.h 2012-04-20 17:57:13.000000000 +0200
++++ taglib-1.7.2-ds/taglib/toolkit/tstring.h 2013-01-29 12:30:59.000000000 +0100
+@@ -88,6 +88,18 @@
+ */
+ enum Type {
+ /*!
++ * Determine using current locale settings
++ */
++ Locale = -1,
++ /*!
++ * Latin1 for ID3 tags.
++ */
++ Latin1ID3 = 65,
++ /*!
++ * Latin1 for ID3 tags.
++ */
++ Latin1ID3V2 = 66,
++ /*!
+ * IS08859-1, or <i>Latin1</i> encoding. 8 bit characters.
+ */
+ Latin1 = 0,
+@@ -110,6 +122,10 @@
+ UTF16LE = 4
+ };
+
++ static Type ID3Type(int i);
++ static Type ID3WType(Type type);
++ static Type ID3RealType(Type type);
++
+ /*!
+ * Constructs an empty String.
+ */
diff --git a/patches/taglib/taglib-1.7-ds-rusxmms.patch b/patches/taglib/taglib-1.7-ds-rusxmms.patch
new file mode 100644
index 0000000..fce777f
--- /dev/null
+++ b/patches/taglib/taglib-1.7-ds-rusxmms.patch
@@ -0,0 +1,515 @@
+diff -dPNur taglib-1.6/config-taglib.h.cmake taglib-1.6-ds/config-taglib.h.cmake
+--- taglib-1.6/config-taglib.h.cmake 2008-11-12 09:17:11.000000000 +0100
++++ taglib-1.6-ds/config-taglib.h.cmake 2009-10-02 17:53:08.000000000 +0200
+@@ -6,6 +6,8 @@
+ /* Define if you have libz */
+ #cmakedefine HAVE_ZLIB 1
+
++#cmakedefine HAVE_LIBRCC 1
++
+ #cmakedefine NO_ITUNES_HACKS 1
+ #cmakedefine WITH_ASF 1
+ #cmakedefine WITH_MP4 1
+diff -dPNur taglib-1.6/ConfigureChecks.cmake taglib-1.6-ds/ConfigureChecks.cmake
+--- taglib-1.6/ConfigureChecks.cmake 2008-12-21 22:46:41.000000000 +0100
++++ taglib-1.6-ds/ConfigureChecks.cmake 2009-10-02 17:53:08.000000000 +0200
+@@ -14,6 +14,8 @@
+ #check for libz using the cmake supplied FindZLIB.cmake
+ FIND_PACKAGE(ZLIB)
+
++SET(HAVE_LIBRCC 1)
++
+ IF(ZLIB_FOUND)
+ SET(HAVE_ZLIB 1)
+ ELSE(ZLIB_FOUND)
+diff -dPNur taglib-1.6/configure.in taglib-1.6-ds/configure.in
+diff -dPNur taglib-1.6/configure.in.in taglib-1.6-ds/configure.in.in
+diff -dPNur taglib-1.6/taglib/CMakeLists.txt taglib-1.6-ds/taglib/CMakeLists.txt
+--- taglib-1.6/taglib/CMakeLists.txt 2009-09-13 12:19:34.000000000 +0200
++++ taglib-1.6-ds/taglib/CMakeLists.txt 2009-10-02 17:53:08.000000000 +0200
+@@ -163,6 +163,7 @@
+ )
+
+ SET(toolkit_SRCS
++toolkit/rccpatch.cpp
+ toolkit/tstring.cpp
+ toolkit/tstringlist.cpp
+ toolkit/tbytevector.cpp
+@@ -190,7 +191,7 @@
+ add_library(tag SHARED ${tag_LIB_SRCS})
+ endif(ENABLE_STATIC)
+
+-TARGET_LINK_LIBRARIES(tag )
++TARGET_LINK_LIBRARIES(tag rcc)
+ if(ZLIB_FOUND)
+ TARGET_LINK_LIBRARIES(tag ${ZLIB_LIBRARIES})
+ endif(ZLIB_FOUND)
+diff -dPNur taglib-1.6/taglib/mpeg/id3v1/id3v1tag.cpp taglib-1.6-ds/taglib/mpeg/id3v1/id3v1tag.cpp
+--- taglib-1.6/taglib/mpeg/id3v1/id3v1tag.cpp 2008-02-04 16:11:56.000000000 +0100
++++ taglib-1.6-ds/taglib/mpeg/id3v1/id3v1tag.cpp 2009-10-02 17:53:08.000000000 +0200
+@@ -59,17 +59,18 @@
+
+ String ID3v1::StringHandler::parse(const ByteVector &data) const
+ {
+- return String(data, String::Latin1).stripWhiteSpace();
++ return String(data, String::Latin1ID3).stripWhiteSpace();
+ }
+
+ ByteVector ID3v1::StringHandler::render(const String &s) const
+ {
+ if(!s.isLatin1())
+ {
++ if (String::ID3WType(String::Latin1) == String::Latin1)
+ return ByteVector();
+ }
+
+- return s.data(String::Latin1);
++ return s.data(String::Latin1ID3);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+@@ -240,7 +241,7 @@
+ d->track = uchar(data[offset + 29]);
+ }
+ else
+- d->comment = data.mid(offset, 30);
++ d->comment = TagPrivate::stringHandler->parse(data.mid(offset, 30));
+
+ offset += 30;
+
+diff -dPNur taglib-1.6/taglib/mpeg/id3v2/frames/commentsframe.cpp taglib-1.6-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp
+--- taglib-1.6/taglib/mpeg/id3v2/frames/commentsframe.cpp 2008-02-04 16:11:56.000000000 +0100
++++ taglib-1.6-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp 2009-10-02 17:53:08.000000000 +0200
+@@ -136,10 +136,10 @@
+ return;
+ }
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+ d->language = data.mid(1, 3);
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
+
+@@ -155,10 +155,12 @@
+
+ String::Type encoding = d->textEncoding;
+
++ encoding = String::ID3WType(encoding);
++
+ encoding = checkEncoding(d->description, encoding);
+ encoding = checkEncoding(d->text, encoding);
+-
+- v.append(char(encoding));
++
++ v.append(char(String::ID3RealType(encoding)));
+ v.append(d->language.size() == 3 ? d->language : "XXX");
+ v.append(d->description.data(encoding));
+ v.append(textDelimiter(encoding));
+diff -dPNur taglib-1.6/taglib/mpeg/id3v2/frames/textidentificationframe.cpp taglib-1.6-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp
+--- taglib-1.6/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2008-02-04 16:11:56.000000000 +0100
++++ taglib-1.6-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2009-10-02 17:53:08.000000000 +0200
+@@ -105,12 +105,12 @@
+
+ // read the string data type (the first byte of the field data)
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+
+ // split the byte array into chunks based on the string type (two byte delimiter
+ // for unicode encodings)
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ // build a small counter to strip nulls off the end of the field
+
+@@ -139,11 +139,14 @@
+
+ ByteVector TextIdentificationFrame::renderFields() const
+ {
+- String::Type encoding = checkEncoding(d->fieldList, d->textEncoding);
++ String::Type encoding = d->textEncoding;
++
++ encoding = String::ID3WType(encoding);
++ encoding = checkEncoding(d->fieldList, encoding);
+
+ ByteVector v;
+
+- v.append(char(encoding));
++ v.append(char(String::ID3RealType(encoding)));
+
+ for(StringList::ConstIterator it = d->fieldList.begin(); it != d->fieldList.end(); it++) {
+
+diff -dPNur taglib-1.6/taglib/toolkit/CMakeLists.txt taglib-1.6-ds/taglib/toolkit/CMakeLists.txt
+--- taglib-1.6/taglib/toolkit/CMakeLists.txt 2006-09-20 14:52:28.000000000 +0200
++++ taglib-1.6-ds/taglib/toolkit/CMakeLists.txt 2009-10-02 17:53:08.000000000 +0200
+@@ -1 +1 @@
+-INSTALL( FILES taglib.h tstring.h tlist.h tlist.tcc tstringlist.h tbytevector.h tbytevectorlist.h tfile.h tmap.h tmap.tcc DESTINATION ${INCLUDE_INSTALL_DIR}/taglib)
++INSTALL( FILES rccpatch.h taglib.h tstring.h tlist.h tlist.tcc tstringlist.h tbytevector.h tbytevectorlist.h tfile.h tmap.h tmap.tcc DESTINATION ${INCLUDE_INSTALL_DIR}/taglib)
+diff -dPNur taglib-1.6/taglib/toolkit/Makefile.am taglib-1.6-ds/taglib/toolkit/Makefile.am
+diff -dPNur taglib-1.6/taglib/toolkit/rccpatch.cpp taglib-1.6-ds/taglib/toolkit/rccpatch.cpp
+--- taglib-1.6/taglib/toolkit/rccpatch.cpp 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.6-ds/taglib/toolkit/rccpatch.cpp 2009-10-02 17:53:08.000000000 +0200
+@@ -0,0 +1,198 @@
++#include <stdlib.h>
++
++#include <string>
++#include "tstring.h"
++#include "tbytevector.h"
++
++#ifndef HAVE_LIBRCC
++# include <config.h>
++#endif
++
++#ifdef HAVE_LIBRCC
++# include <librcc.h>
++# include <string.h>
++#endif /* HAVE_LIBRCC */
++
++
++#ifdef HAVE_LIBRCC
++# define ID3_CLASS 0
++# define ID3V2_CLASS 1
++# define UTF_CLASS 2
++# define OUT_CLASS 3
++static rcc_class classes[] = {
++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
++ { "id3v2", RCC_CLASS_STANDARD, "id3", NULL, "ID3 v.2 Encoding", 0 },
++ { "utf", RCC_CLASS_KNOWN, "UTF-8", NULL, "Unicode Encoding", 0},
++ { "out", RCC_CLASS_TRANSLATE_LOCALE, "LC_CTYPE", NULL, "Output Encoding", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int rcc_initialized = 0;
++
++static rcc_context ctx = NULL;
++#endif /* HAVE_LIBRCC */
++
++
++void rccPatchFree() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) {
++ rccFree();
++ rcc_initialized = 0;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++void rccPatchInit() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) return;
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "xmms");
++ rccInitDb4(NULL, NULL, 0);
++ rcc_initialized = 1;
++#endif /* HAVE_LIBRCC */
++}
++
++void rccPatchSetContext(void *newctx) {
++#ifdef HAVE_LIBRCC
++ if (newctx) {
++ ctx = (rcc_context)newctx;
++ rcc_initialized = 1;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++static void rccPatchTryInit() {
++#ifdef HAVE_LIBRCC
++ if (!rcc_initialized) {
++ rccPatchInit();
++ if (rcc_initialized) atexit(rccPatchFree);
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++
++TagLib::ByteVector rccPatchRecodeOutput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, OUT_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeOutputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, v2?ID3V2_CLASS:ID3_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeInput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, OUT_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeInputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, v2?ID3V2_CLASS:ID3_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::String::Type rccPatchGetLocaleType() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ char charset[32];
++
++ rccPatchTryInit();
++
++ if (!rccLocaleGetCharset(charset, NULL, 31)) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::UTF8;
++}
++
++TagLib::String::Type rccPatchGetID3Type() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ const char *charset;
++
++ rccPatchTryInit();
++
++ charset = rccGetCurrentCharsetName(ctx, ID3V2_CLASS);
++ if (charset) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1ID3V2;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::Latin1;
++}
+diff -dPNur taglib-1.6/taglib/toolkit/rccpatch.h taglib-1.6-ds/taglib/toolkit/rccpatch.h
+--- taglib-1.6/taglib/toolkit/rccpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.6-ds/taglib/toolkit/rccpatch.h 2009-10-02 17:53:08.000000000 +0200
+@@ -0,0 +1,20 @@
++#ifndef _RCC_PATCH_H
++#define _RCC_PATCH_H
++
++#include <string.h>
++#include "tstring.h"
++#include "tbytevector.h"
++
++void rccPatchFree();
++void rccPatchInit();
++void rccPatchSetContext(void *newctx);
++
++TagLib::ByteVector rccPatchRecodeOutput(const std::string &s);
++TagLib::ByteVector rccPatchRecodeInput(const std::string &s);
++TagLib::ByteVector rccPatchRecodeOutputID3(const std::string &s, bool v2 = false);
++TagLib::ByteVector rccPatchRecodeInputID3(const std::string &s, bool v2 = false);
++
++TagLib::String::Type rccPatchGetLocaleType();
++TagLib::String::Type rccPatchGetID3Type();
++
++#endif /* _RCC_PATCH_H */
+diff -dPNur taglib-1.6/taglib/toolkit/tstring.cpp taglib-1.6-ds/taglib/toolkit/tstring.cpp
+--- taglib-1.6/taglib/toolkit/tstring.cpp 2009-04-29 17:57:05.000000000 +0200
++++ taglib-1.6-ds/taglib/toolkit/tstring.cpp 2009-10-02 17:53:08.000000000 +0200
+@@ -23,6 +23,7 @@
+ * http://www.mozilla.org/MPL/ *
+ ***************************************************************************/
+
++#include "rccpatch.h"
+ #include "tstring.h"
+ #include "unicode.h"
+ #include "tdebug.h"
+@@ -167,7 +168,7 @@
+ if(v.isEmpty())
+ return;
+
+- if(t == Latin1 || t == UTF8) {
++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2 || t == UTF8) {
+
+ int length = 0;
+ d->data.resize(v.size());
+@@ -369,10 +370,21 @@
+ {
+ ByteVector v;
+
+- switch(t) {
++ if (t == Locale) t = rccPatchGetLocaleType();
+
++ switch(t) {
++ case Locale:
+ case Latin1:
++ case Latin1ID3:
++ case Latin1ID3V2:
+ {
++ std::string s = to8Bit(true);
++ if (t == Latin1ID3) v = rccPatchRecodeOutputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccPatchRecodeOutputID3(s, true);
++ else /* if (t == Latin1(Locale) */ v = rccPatchRecodeOutput(s);
++
++ if (v.size()) return v;
++
+ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
+ v.append(char(*it));
+ break;
+@@ -707,6 +719,31 @@
+
+ void String::prepare(Type t)
+ {
++ if (t == Locale) t = rccPatchGetLocaleType();
++
++ if ((t == Latin1)||(t == Latin1ID3)||(t == Latin1ID3V2)) {
++ std::string s = to8Bit(false);
++ ByteVector v;
++
++ if (t == Latin1ID3) v = rccPatchRecodeInputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccPatchRecodeInputID3(s, true);
++ else /* Latin1 converted from Locale */ v = rccPatchRecodeInput(s);
++
++ if (v.size()) {
++ int length = 0;
++ d->data.resize(v.size());
++ wstring::iterator targetIt = d->data.begin();
++ for(ByteVector::ConstIterator it = v.begin(); it != v.end() && (*it); ++it) {
++ *targetIt = uchar(*it);
++ ++targetIt;
++ ++length;
++ }
++ d->data.resize(length);
++ }
++
++ t = UTF8;
++ }
++
+ switch(t) {
+ case UTF16:
+ {
+@@ -796,6 +833,27 @@
+
+ std::ostream &operator<<(std::ostream &s, const String &str)
+ {
+- s << str.to8Bit();
++ ByteVector bv = str.data(String::Locale);
++ s << bv;
+ return s;
+ }
++
++String::Type String::ID3Type(int i) {
++ if (i == Latin1) return Latin1ID3V2;
++ return Type(i);
++};
++
++String::Type String::ID3WType(Type type) {
++ Type rcc_type = rccPatchGetID3Type();
++ if ((rcc_type == Latin1ID3)||(rcc_type == Latin1ID3V2)) {
++ if (type == Latin1) return rcc_type;
++ return type;
++ }
++
++ return rcc_type;
++};
++
++String::Type String::ID3RealType(Type type) {
++ if ((type == Latin1ID3)||(type == Latin1ID3V2)) return Latin1;
++ return type;
++}
+diff -dPNur taglib-1.6/taglib/toolkit/tstring.h taglib-1.6-ds/taglib/toolkit/tstring.h
+--- taglib-1.6/taglib/toolkit/tstring.h 2009-07-02 22:54:32.000000000 +0200
++++ taglib-1.6-ds/taglib/toolkit/tstring.h 2009-10-02 17:53:08.000000000 +0200
+@@ -81,6 +81,18 @@
+ */
+ enum Type {
+ /*!
++ * Determine using current locale settings
++ */
++ Locale = -1,
++ /*!
++ * Latin1 for ID3 tags.
++ */
++ Latin1ID3 = 65,
++ /*!
++ * Latin1 for ID3 tags.
++ */
++ Latin1ID3V2 = 66,
++ /*!
+ * IS08859-1, or <i>Latin1</i> encoding. 8 bit characters.
+ */
+ Latin1 = 0,
+@@ -103,6 +115,10 @@
+ UTF16LE = 4
+ };
+
++ static Type ID3Type(int i);
++ static Type ID3WType(Type type);
++ static Type ID3RealType(Type type);
++
+ /*!
+ * Constructs an empty String.
+ */
diff --git a/patches/taglib/taglib-1.8-ds-rusxmms-r2.patch b/patches/taglib/taglib-1.8-ds-rusxmms-r2.patch
new file mode 100644
index 0000000..bed92a2
--- /dev/null
+++ b/patches/taglib/taglib-1.8-ds-rusxmms-r2.patch
@@ -0,0 +1,507 @@
+diff -dPNur taglib-1.8/config-taglib.h.cmake taglib-1.8-ds/config-taglib.h.cmake
+--- taglib-1.8/config-taglib.h.cmake 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/config-taglib.h.cmake 2013-01-29 12:45:48.000000000 +0100
+@@ -3,6 +3,8 @@
+ /* Define if you have libz */
+ #cmakedefine HAVE_ZLIB 1
+
++#cmakedefine HAVE_LIBRCC 1
++
+ #cmakedefine NO_ITUNES_HACKS 1
+ #cmakedefine WITH_ASF 1
+ #cmakedefine WITH_MP4 1
+diff -dPNur taglib-1.8/ConfigureChecks.cmake taglib-1.8-ds/ConfigureChecks.cmake
+--- taglib-1.8/ConfigureChecks.cmake 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/ConfigureChecks.cmake 2013-01-29 12:45:48.000000000 +0100
+@@ -14,6 +14,8 @@
+ set(HAVE_ZLIB 0)
+ endif()
+
++SET(HAVE_LIBRCC 1)
++
+ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
+ find_package(CppUnit)
+ if(NOT CppUnit_FOUND AND BUILD_TESTS)
+diff -dPNur taglib-1.8/taglib/CMakeLists.txt taglib-1.8-ds/taglib/CMakeLists.txt
+--- taglib-1.8/taglib/CMakeLists.txt 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/CMakeLists.txt 2013-01-29 12:45:48.000000000 +0100
+@@ -35,6 +35,7 @@
+ audioproperties.h
+ taglib_export.h
+ ${CMAKE_BINARY_DIR}/taglib_config.h
++ toolkit/rccpatch.h
+ toolkit/taglib.h
+ toolkit/tstring.h
+ toolkit/tlist.h
+@@ -269,6 +270,7 @@
+ )
+
+ set(toolkit_SRCS
++ toolkit/rccpatch.cpp
+ toolkit/tstring.cpp
+ toolkit/tstringlist.cpp
+ toolkit/tbytevector.cpp
+@@ -296,7 +298,7 @@
+ add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
+
+ if(ZLIB_FOUND)
+- target_link_libraries(tag ${ZLIB_LIBRARIES})
++ target_link_libraries(tag rcc ${ZLIB_LIBRARIES})
+ endif()
+
+ set_target_properties(tag PROPERTIES
+diff -dPNur taglib-1.8/taglib/mpeg/id3v1/id3v1tag.cpp taglib-1.8-ds/taglib/mpeg/id3v1/id3v1tag.cpp
+--- taglib-1.8/taglib/mpeg/id3v1/id3v1tag.cpp 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/mpeg/id3v1/id3v1tag.cpp 2013-01-29 12:45:48.000000000 +0100
+@@ -64,17 +64,18 @@
+
+ String ID3v1::StringHandler::parse(const ByteVector &data) const
+ {
+- return String(data, String::Latin1).stripWhiteSpace();
++ return String(data, String::Latin1ID3).stripWhiteSpace();
+ }
+
+ ByteVector ID3v1::StringHandler::render(const String &s) const
+ {
+ if(!s.isLatin1())
+ {
++ if (String::ID3WType(String::Latin1) == String::Latin1)
+ return ByteVector();
+ }
+
+- return s.data(String::Latin1);
++ return s.data(String::Latin1ID3);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+@@ -247,7 +248,7 @@
+ d->track = uchar(data[offset + 29]);
+ }
+ else
+- d->comment = data.mid(offset, 30);
++ d->comment = TagPrivate::stringHandler->parse(data.mid(offset, 30));
+
+ offset += 30;
+
+diff -dPNur taglib-1.8/taglib/mpeg/id3v2/frames/commentsframe.cpp taglib-1.8-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp
+--- taglib-1.8/taglib/mpeg/id3v2/frames/commentsframe.cpp 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp 2013-01-29 12:45:48.000000000 +0100
+@@ -150,10 +150,10 @@
+ return;
+ }
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+ d->language = data.mid(1, 3);
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
+
+@@ -174,10 +174,12 @@
+
+ String::Type encoding = d->textEncoding;
+
++ encoding = String::ID3WType(encoding);
++
+ encoding = checkTextEncoding(d->description, encoding);
+ encoding = checkTextEncoding(d->text, encoding);
+-
+- v.append(char(encoding));
++
++ v.append(char(String::ID3RealType(encoding)));
+ v.append(d->language.size() == 3 ? d->language : "XXX");
+ v.append(d->description.data(encoding));
+ v.append(textDelimiter(encoding));
+diff -dPNur taglib-1.8/taglib/mpeg/id3v2/frames/textidentificationframe.cpp taglib-1.8-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp
+--- taglib-1.8/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2013-01-29 12:45:48.000000000 +0100
+@@ -187,12 +187,12 @@
+
+ // read the string data type (the first byte of the field data)
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+
+ // split the byte array into chunks based on the string type (two byte delimiter
+ // for unicode encodings)
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ // build a small counter to strip nulls off the end of the field
+
+@@ -223,11 +223,14 @@
+
+ ByteVector TextIdentificationFrame::renderFields() const
+ {
+- String::Type encoding = checkTextEncoding(d->fieldList, d->textEncoding);
++ String::Type encoding = d->textEncoding;
++
++ encoding = String::ID3WType(encoding);
++ encoding = checkTextEncoding(d->fieldList, encoding);
+
+ ByteVector v;
+
+- v.append(char(encoding));
++ v.append(char(String::ID3RealType(encoding)));
+
+ for(StringList::ConstIterator it = d->fieldList.begin(); it != d->fieldList.end(); it++) {
+
+diff -dPNur taglib-1.8/taglib/toolkit/rccpatch.cpp taglib-1.8-ds/taglib/toolkit/rccpatch.cpp
+--- taglib-1.8/taglib/toolkit/rccpatch.cpp 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.8-ds/taglib/toolkit/rccpatch.cpp 2013-01-29 12:35:45.000000000 +0100
+@@ -0,0 +1,192 @@
++#include <stdlib.h>
++
++#include <string>
++#include "tstring.h"
++#include "tbytevector.h"
++
++#ifndef HAVE_LIBRCC
++# include <config.h>
++#endif
++
++#ifdef HAVE_LIBRCC
++# include <librcc.h>
++# include <string.h>
++#endif /* HAVE_LIBRCC */
++
++
++#ifdef HAVE_LIBRCC
++# define ID3_CLASS 0
++# define ID3V2_CLASS 1
++# define UTF_CLASS 2
++# define OUT_CLASS 3
++static rcc_class classes[] = {
++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
++ { "id3v2", RCC_CLASS_STANDARD, "id3", NULL, "ID3 v.2 Encoding", 0 },
++ { "utf", RCC_CLASS_KNOWN, "UTF-8", NULL, "Unicode Encoding", 0},
++ { "out", RCC_CLASS_TRANSLATE_LOCALE, "LC_CTYPE", NULL, "Output Encoding", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int rcc_initialized = 0;
++
++static rcc_context ctx = NULL;
++#endif /* HAVE_LIBRCC */
++
++
++void rccPatchFree() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) {
++ rccFree();
++ rcc_initialized = 0;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++void rccPatchInit() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) return;
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "xmms");
++ rccInitDb4(NULL, NULL, 0);
++ rcc_initialized = 1;
++#endif /* HAVE_LIBRCC */
++}
++
++void rccPatchSetContext(void *newctx) {
++#ifdef HAVE_LIBRCC
++ if (newctx) {
++ ctx = (rcc_context)newctx;
++ rcc_initialized = 1;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++static void rccPatchTryInit() {
++#ifdef HAVE_LIBRCC
++ if (!rcc_initialized) {
++ rccPatchInit();
++ if (rcc_initialized) atexit(rccPatchFree);
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++
++TagLib::ByteVector rccPatchRecodeOutput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, OUT_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeOutputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, v2?ID3V2_CLASS:ID3_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeInput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, OUT_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else
++#endif /* HAVE_LIBRCC */
++ v.setData("", 0);
++
++ return v;
++}
++
++TagLib::ByteVector rccPatchRecodeInputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, v2?ID3V2_CLASS:ID3_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else
++#endif /* HAVE_LIBRCC */
++ v.setData("", 0);
++
++ return v;
++}
++
++TagLib::String::Type rccPatchGetLocaleType() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ char charset[32];
++
++ rccPatchTryInit();
++
++ if (!rccLocaleGetCharset(charset, NULL, 31)) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::UTF8;
++}
++
++TagLib::String::Type rccPatchGetID3Type() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ const char *charset;
++
++ rccPatchTryInit();
++
++ charset = rccGetCurrentCharsetName(ctx, ID3V2_CLASS);
++ if (charset) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1ID3V2;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::Latin1;
++}
+diff -dPNur taglib-1.8/taglib/toolkit/rccpatch.h taglib-1.8-ds/taglib/toolkit/rccpatch.h
+--- taglib-1.8/taglib/toolkit/rccpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.8-ds/taglib/toolkit/rccpatch.h 2013-01-29 12:45:48.000000000 +0100
+@@ -0,0 +1,20 @@
++#ifndef _RCC_PATCH_H
++#define _RCC_PATCH_H
++
++#include <string.h>
++#include "tstring.h"
++#include "tbytevector.h"
++
++void rccPatchFree();
++void rccPatchInit();
++void rccPatchSetContext(void *newctx);
++
++TagLib::ByteVector rccPatchRecodeOutput(const std::string &s);
++TagLib::ByteVector rccPatchRecodeInput(const std::string &s);
++TagLib::ByteVector rccPatchRecodeOutputID3(const std::string &s, bool v2 = false);
++TagLib::ByteVector rccPatchRecodeInputID3(const std::string &s, bool v2 = false);
++
++TagLib::String::Type rccPatchGetLocaleType();
++TagLib::String::Type rccPatchGetID3Type();
++
++#endif /* _RCC_PATCH_H */
+diff -dPNur taglib-1.8/taglib/toolkit/tstring.cpp taglib-1.8-ds/taglib/toolkit/tstring.cpp
+--- taglib-1.8/taglib/toolkit/tstring.cpp 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/toolkit/tstring.cpp 2013-01-29 12:46:14.000000000 +0100
+@@ -23,6 +23,7 @@
+ * http://www.mozilla.org/MPL/ *
+ ***************************************************************************/
+
++#include "rccpatch.h"
+ #include "tstring.h"
+ #include "unicode.h"
+ #include "tdebug.h"
+@@ -168,7 +169,7 @@
+ if(v.isEmpty())
+ return;
+
+- if(t == Latin1 || t == UTF8) {
++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2 || t == UTF8) {
+
+ int length = 0;
+ d->data.resize(v.size());
+@@ -397,10 +398,21 @@
+ {
+ ByteVector v;
+
+- switch(t) {
++ if (t == Locale) t = rccPatchGetLocaleType();
+
++ switch(t) {
++ case Locale:
+ case Latin1:
++ case Latin1ID3:
++ case Latin1ID3V2:
+ {
++ std::string s = to8Bit(true);
++ if (t == Latin1ID3) v = rccPatchRecodeOutputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccPatchRecodeOutputID3(s, true);
++ else /* if (t == Latin1(Locale) */ v = rccPatchRecodeOutput(s);
++
++ if (v.size()) return v;
++
+ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
+ v.append(char(*it));
+ break;
+@@ -750,6 +762,30 @@
+
+ void String::prepare(Type t)
+ {
++ if (t == Locale) t = rccPatchGetLocaleType();
++
++ if ((t == Latin1)||(t == Latin1ID3)||(t == Latin1ID3V2)) {
++ std::string s = to8Bit(false);
++ ByteVector v;
++
++ if (t == Latin1ID3) v = rccPatchRecodeInputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccPatchRecodeInputID3(s, true);
++ else /* Latin1 converted from Locale */ v = rccPatchRecodeInput(s);
++
++ if (v.size()) {
++ int length = 0;
++ d->data.resize(v.size());
++ wstring::iterator targetIt = d->data.begin();
++ for(ByteVector::ConstIterator it = v.begin(); it != v.end() && (*it); ++it) {
++ *targetIt = uchar(*it);
++ ++targetIt;
++ ++length;
++ }
++ d->data.resize(length);
++ t = UTF8;
++ }
++ }
++
+ switch(t) {
+ case UTF16:
+ {
+@@ -839,6 +875,27 @@
+
+ std::ostream &operator<<(std::ostream &s, const String &str)
+ {
+- s << str.to8Bit();
++ ByteVector bv = str.data(String::Locale);
++ s << bv;
+ return s;
+ }
++
++String::Type String::ID3Type(int i) {
++ if (i == Latin1) return Latin1ID3V2;
++ return Type(i);
++};
++
++String::Type String::ID3WType(Type type) {
++ Type rcc_type = rccPatchGetID3Type();
++ if ((rcc_type == Latin1ID3)||(rcc_type == Latin1ID3V2)) {
++ if (type == Latin1) return rcc_type;
++ return type;
++ }
++
++ return rcc_type;
++};
++
++String::Type String::ID3RealType(Type type) {
++ if ((type == Latin1ID3)||(type == Latin1ID3V2)) return Latin1;
++ return type;
++}
+diff -dPNur taglib-1.8/taglib/toolkit/tstring.h taglib-1.8-ds/taglib/toolkit/tstring.h
+--- taglib-1.8/taglib/toolkit/tstring.h 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/toolkit/tstring.h 2013-01-29 12:45:48.000000000 +0100
+@@ -90,6 +90,18 @@
+ */
+ enum Type {
+ /*!
++ * Determine using current locale settings
++ */
++ Locale = -1,
++ /*!
++ * Latin1 for ID3 tags.
++ */
++ Latin1ID3 = 65,
++ /*!
++ * Latin1 for ID3 tags.
++ */
++ Latin1ID3V2 = 66,
++ /*!
+ * IS08859-1, or <i>Latin1</i> encoding. 8 bit characters.
+ */
+ Latin1 = 0,
+@@ -112,6 +124,10 @@
+ UTF16LE = 4
+ };
+
++ static Type ID3Type(int i);
++ static Type ID3WType(Type type);
++ static Type ID3RealType(Type type);
++
+ /*!
+ * Constructs an empty String.
+ */
diff --git a/patches/taglib/taglib-1.8-ds-rusxmms-r9.patch b/patches/taglib/taglib-1.8-ds-rusxmms-r9.patch
new file mode 100644
index 0000000..28a7b84
--- /dev/null
+++ b/patches/taglib/taglib-1.8-ds-rusxmms-r9.patch
@@ -0,0 +1,609 @@
+diff -dPNur taglib-1.8/config-taglib.h.cmake taglib-1.8-ds/config-taglib.h.cmake
+--- taglib-1.8/config-taglib.h.cmake 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/config-taglib.h.cmake 2013-05-22 20:13:15.000000000 +0200
+@@ -3,6 +3,8 @@
+ /* Define if you have libz */
+ #cmakedefine HAVE_ZLIB 1
+
++#cmakedefine HAVE_LIBRCC 1
++
+ #cmakedefine NO_ITUNES_HACKS 1
+ #cmakedefine WITH_ASF 1
+ #cmakedefine WITH_MP4 1
+diff -dPNur taglib-1.8/ConfigureChecks.cmake taglib-1.8-ds/ConfigureChecks.cmake
+--- taglib-1.8/ConfigureChecks.cmake 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/ConfigureChecks.cmake 2013-05-22 20:13:15.000000000 +0200
+@@ -14,6 +14,8 @@
+ set(HAVE_ZLIB 0)
+ endif()
+
++SET(HAVE_LIBRCC 1)
++
+ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
+ find_package(CppUnit)
+ if(NOT CppUnit_FOUND AND BUILD_TESTS)
+diff -dPNur taglib-1.8/examples/tagreader_c.c taglib-1.8-ds/examples/tagreader_c.c
+--- taglib-1.8/examples/tagreader_c.c 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/examples/tagreader_c.c 2013-05-22 20:13:15.000000000 +0200
+@@ -38,7 +38,7 @@
+ TagLib_Tag *tag;
+ const TagLib_AudioProperties *properties;
+
+- taglib_set_strings_unicode(FALSE);
++// taglib_set_strings_unicode(FALSE);
+
+ for(i = 1; i < argc; i++) {
+ printf("******************** \"%s\" ********************\n", argv[i]);
+diff -dPNur taglib-1.8/examples/tagwriter.cpp taglib-1.8-ds/examples/tagwriter.cpp
+--- taglib-1.8/examples/tagwriter.cpp 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/examples/tagwriter.cpp 2013-05-22 20:13:15.000000000 +0200
+@@ -92,7 +92,7 @@
+ if(isArgument(argv[i]) && i + 1 < argc && !isArgument(argv[i + 1])) {
+
+ char field = argv[i][1];
+- TagLib::String value = argv[i + 1];
++ TagLib::String value(argv[i + 1], TagLib::String::Locale);
+
+ TagLib::List<TagLib::FileRef>::Iterator it;
+ for(it = fileList.begin(); it != fileList.end(); ++it) {
+diff -dPNur taglib-1.8/taglib/CMakeLists.txt taglib-1.8-ds/taglib/CMakeLists.txt
+--- taglib-1.8/taglib/CMakeLists.txt 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/CMakeLists.txt 2013-05-22 20:13:15.000000000 +0200
+@@ -35,6 +35,7 @@
+ audioproperties.h
+ taglib_export.h
+ ${CMAKE_BINARY_DIR}/taglib_config.h
++ toolkit/rccpatch.h
+ toolkit/taglib.h
+ toolkit/tstring.h
+ toolkit/tlist.h
+@@ -269,6 +270,7 @@
+ )
+
+ set(toolkit_SRCS
++ toolkit/rccpatch.cpp
+ toolkit/tstring.cpp
+ toolkit/tstringlist.cpp
+ toolkit/tbytevector.cpp
+@@ -296,7 +298,7 @@
+ add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
+
+ if(ZLIB_FOUND)
+- target_link_libraries(tag ${ZLIB_LIBRARIES})
++ target_link_libraries(tag rcc ${ZLIB_LIBRARIES})
+ endif()
+
+ set_target_properties(tag PROPERTIES
+diff -dPNur taglib-1.8/taglib/mpeg/id3v1/id3v1tag.cpp taglib-1.8-ds/taglib/mpeg/id3v1/id3v1tag.cpp
+--- taglib-1.8/taglib/mpeg/id3v1/id3v1tag.cpp 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/mpeg/id3v1/id3v1tag.cpp 2013-05-22 20:13:15.000000000 +0200
+@@ -64,17 +64,18 @@
+
+ String ID3v1::StringHandler::parse(const ByteVector &data) const
+ {
+- return String(data, String::Latin1).stripWhiteSpace();
++ return String(data, String::Latin1ID3).stripWhiteSpace();
+ }
+
+ ByteVector ID3v1::StringHandler::render(const String &s) const
+ {
+ if(!s.isLatin1())
+ {
++ if (String::ID3WType(String::Latin1) == String::Latin1)
+ return ByteVector();
+ }
+
+- return s.data(String::Latin1);
++ return s.data(String::Latin1ID3);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+@@ -247,7 +248,7 @@
+ d->track = uchar(data[offset + 29]);
+ }
+ else
+- d->comment = data.mid(offset, 30);
++ d->comment = TagPrivate::stringHandler->parse(data.mid(offset, 30));
+
+ offset += 30;
+
+diff -dPNur taglib-1.8/taglib/mpeg/id3v2/frames/commentsframe.cpp taglib-1.8-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp
+--- taglib-1.8/taglib/mpeg/id3v2/frames/commentsframe.cpp 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp 2013-05-22 20:13:16.000000000 +0200
+@@ -150,10 +150,10 @@
+ return;
+ }
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+ d->language = data.mid(1, 3);
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
+
+@@ -174,10 +174,12 @@
+
+ String::Type encoding = d->textEncoding;
+
++ encoding = String::ID3WType(encoding);
++
+ encoding = checkTextEncoding(d->description, encoding);
+ encoding = checkTextEncoding(d->text, encoding);
+-
+- v.append(char(encoding));
++
++ v.append(char(String::ID3RealType(encoding)));
+ v.append(d->language.size() == 3 ? d->language : "XXX");
+ v.append(d->description.data(encoding));
+ v.append(textDelimiter(encoding));
+diff -dPNur taglib-1.8/taglib/mpeg/id3v2/frames/textidentificationframe.cpp taglib-1.8-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp
+--- taglib-1.8/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2013-05-22 20:13:16.000000000 +0200
+@@ -187,12 +187,12 @@
+
+ // read the string data type (the first byte of the field data)
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+
+ // split the byte array into chunks based on the string type (two byte delimiter
+ // for unicode encodings)
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ // build a small counter to strip nulls off the end of the field
+
+@@ -223,11 +223,14 @@
+
+ ByteVector TextIdentificationFrame::renderFields() const
+ {
+- String::Type encoding = checkTextEncoding(d->fieldList, d->textEncoding);
++ String::Type encoding = d->textEncoding;
++
++ encoding = String::ID3WType(encoding);
++ encoding = checkTextEncoding(d->fieldList, encoding);
+
+ ByteVector v;
+
+- v.append(char(encoding));
++ v.append(char(String::ID3RealType(encoding)));
+
+ for(StringList::ConstIterator it = d->fieldList.begin(); it != d->fieldList.end(); it++) {
+
+diff -dPNur taglib-1.8/taglib/mpeg/id3v2/id3v2frame.cpp taglib-1.8-ds/taglib/mpeg/id3v2/id3v2frame.cpp
+--- taglib-1.8/taglib/mpeg/id3v2/id3v2frame.cpp 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/mpeg/id3v2/id3v2frame.cpp 2013-05-22 20:10:07.000000000 +0200
+@@ -295,7 +295,7 @@
+ if((encoding == String::UTF8 || encoding == String::UTF16BE) && version != 4)
+ return String::UTF16;
+
+- if(encoding != String::Latin1)
++ if((encoding != String::Latin1)&&(encoding != String::Latin1ID3V2))
+ return encoding;
+
+ for(StringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) {
+diff -dPNur taglib-1.8/taglib/toolkit/rccpatch.cpp taglib-1.8-ds/taglib/toolkit/rccpatch.cpp
+--- taglib-1.8/taglib/toolkit/rccpatch.cpp 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.8-ds/taglib/toolkit/rccpatch.cpp 2013-05-22 20:13:16.000000000 +0200
+@@ -0,0 +1,237 @@
++#include <stdlib.h>
++
++#include <string>
++#include "tstring.h"
++#include "tbytevector.h"
++
++//#define RCC_DEBUG
++
++
++#ifndef HAVE_LIBRCC
++# include <config.h>
++#endif
++
++#ifdef HAVE_LIBRCC
++# ifdef RCC_DEBUG
++# include <stdio.h>
++# endif /* RCC_DEBUG */
++# include <librcc.h>
++# include <string.h>
++#endif /* HAVE_LIBRCC */
++
++
++#ifdef HAVE_LIBRCC
++# define ID3_CLASS 0
++# define ID3V2_CLASS 1
++# define UTF_CLASS 2
++# define OUT_CLASS 3
++static rcc_class classes[] = {
++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
++ { "id3v2", RCC_CLASS_STANDARD, "id3", NULL, "ID3 v.2 Encoding", 0 },
++ { "utf", RCC_CLASS_KNOWN, "UTF-8", NULL, "Unicode Encoding", 0},
++ { "out", RCC_CLASS_TRANSLATE_LOCALE, "LC_CTYPE", NULL, "Output Encoding", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int rcc_initialized = 0;
++
++static rcc_context ctx = NULL;
++#endif /* HAVE_LIBRCC */
++
++
++void rccTaglibPatchFree() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) {
++ rccFree();
++ rcc_initialized = 0;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++void rccTaglibPatchInit() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) return;
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "xmms");
++ rccInitDb4(NULL, NULL, 0);
++ rcc_initialized = 1;
++#endif /* HAVE_LIBRCC */
++}
++
++void rccTaglibPatchSetContext(void *newctx) {
++#ifdef HAVE_LIBRCC
++ if (newctx) {
++ ctx = (rcc_context)newctx;
++ rcc_initialized = 1;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++static void rccTaglibPatchTryInit() {
++#ifdef HAVE_LIBRCC
++ if (!rcc_initialized) {
++ rccTaglibPatchInit();
++ if (rcc_initialized) atexit(rccTaglibPatchFree);
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++
++TagLib::ByteVector rccTaglibPatchRecodeOutput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccTaglibPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, OUT_CLASS, s.c_str(), s.length(), &rlen);
++#ifdef RCC_DEBUG
++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
++ if (*c > 127) {
++ printf(" Output: %s - %s\n", s.c_str(), res?res:"null");
++ break;
++ }
++ }
++#endif /* RCC_DEBUG */
++
++ if (res) v.setData(res, rlen);
++ else v.setData("", 0);
++ //v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccTaglibPatchRecodeOutputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccTaglibPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, v2?ID3V2_CLASS:ID3_CLASS, s.c_str(), s.length(), &rlen);
++#ifdef RCC_DEBUG
++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
++ if (*c > 127) {
++ printf(" OutputID3(%i): %s - %s\n", v2, s.c_str(), res?res:"null");
++ break;
++ }
++ }
++#endif /* RCC_DEBUG */
++
++ if (res) v.setData(res, rlen);
++ else v.setData("", 0);
++ //v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccTaglibPatchRecodeInput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccTaglibPatchTryInit();
++
++ res = rccSizedRecode(ctx, OUT_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++#ifdef RCC_DEBUG
++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
++ if (*c > 127) {
++ printf(" Input: %s - %s\n", s.c_str(), res?res:"null");
++ break;
++ }
++ }
++#endif /* RCC_DEBUG */
++
++ if (res) v.setData(res, rlen);
++ else
++#endif /* HAVE_LIBRCC */
++ v.setData("", 0);
++
++ return v;
++}
++
++TagLib::ByteVector rccTaglibPatchRecodeInputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccTaglibPatchTryInit();
++
++ res = rccSizedRecode(ctx, v2?ID3V2_CLASS:ID3_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++#ifdef RCC_DEBUG
++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
++ if (*c > 127) {
++ printf(" InputID3(%i): %s - %s\n", v2, s.c_str(), res?res:"null");
++ break;
++ }
++ }
++#endif /* RCC_DEBUG */
++ if (res) v.setData(res, rlen);
++ else
++#endif /* HAVE_LIBRCC */
++ v.setData("", 0);
++
++ return v;
++}
++
++TagLib::String::Type rccTaglibPatchGetLocaleType() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ char charset[32];
++
++ rccTaglibPatchTryInit();
++ if (!rccLocaleGetCharset(charset, NULL, 31)) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::UTF8;
++}
++
++TagLib::String::Type rccTaglibPatchGetID3Type() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ const char *charset;
++
++ rccTaglibPatchTryInit();
++
++ charset = rccGetCurrentCharsetName(ctx, ID3V2_CLASS);
++ if (charset) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1ID3V2;
++ } else {
++ // Error or no-language configured: If Latin1ID3V2 is returned we normally will use the default unicode encoding unless Latin1 is selected by taglib
++ return TagLib::String::Latin1ID3V2;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::Latin1;
++}
+diff -dPNur taglib-1.8/taglib/toolkit/rccpatch.h taglib-1.8-ds/taglib/toolkit/rccpatch.h
+--- taglib-1.8/taglib/toolkit/rccpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.8-ds/taglib/toolkit/rccpatch.h 2013-05-22 20:13:16.000000000 +0200
+@@ -0,0 +1,20 @@
++#ifndef _RCC_PATCH_H
++#define _RCC_PATCH_H
++
++#include <string.h>
++#include "tstring.h"
++#include "tbytevector.h"
++
++void rccTaglibPatchFree();
++void rccTaglibPatchInit();
++void rccTaglibPatchSetContext(void *newctx);
++
++TagLib::ByteVector rccTaglibPatchRecodeOutput(const std::string &s);
++TagLib::ByteVector rccTaglibPatchRecodeInput(const std::string &s);
++TagLib::ByteVector rccTaglibPatchRecodeOutputID3(const std::string &s, bool v2 = false);
++TagLib::ByteVector rccTaglibPatchRecodeInputID3(const std::string &s, bool v2 = false);
++
++TagLib::String::Type rccTaglibPatchGetLocaleType();
++TagLib::String::Type rccTaglibPatchGetID3Type();
++
++#endif /* _RCC_PATCH_H */
+diff -dPNur taglib-1.8/taglib/toolkit/tstring.cpp taglib-1.8-ds/taglib/toolkit/tstring.cpp
+--- taglib-1.8/taglib/toolkit/tstring.cpp 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/toolkit/tstring.cpp 2013-05-22 20:13:16.000000000 +0200
+@@ -23,6 +23,7 @@
+ * http://www.mozilla.org/MPL/ *
+ ***************************************************************************/
+
++#include "rccpatch.h"
+ #include "tstring.h"
+ #include "unicode.h"
+ #include "tdebug.h"
+@@ -168,7 +169,7 @@
+ if(v.isEmpty())
+ return;
+
+- if(t == Latin1 || t == UTF8) {
++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2 || t == UTF8) {
+
+ int length = 0;
+ d->data.resize(v.size());
+@@ -397,10 +398,38 @@
+ {
+ ByteVector v;
+
+- switch(t) {
++ if (t == Locale) {
++ // The source is either Unicode or real Latin1 (if rcc is bypassed)
++ std::string s = to8Bit(true);
++
++ // In case of UTF8 locale, this probably will return NULL (no recoding needed), but we will take UTF8 path in the next swtich
++ v = rccTaglibPatchRecodeOutput(s);
++ if (v.size()) return v;
+
++ t = rccTaglibPatchGetLocaleType();
++ }
++
++ switch(t) {
++ case Latin1ID3:
++ case Latin1ID3V2:
++ {
++ std::string s = to8Bit(true);
++ if (t == Latin1ID3) v = rccTaglibPatchRecodeOutputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccTaglibPatchRecodeOutputID3(s, true);
++ if (v.size()) break;
++
++ // we don't know if we got NULL because rcc is disabled (error) or UTF8 output is required
++ if ((t == Latin1ID3V2)&&(rccTaglibPatchGetID3Type() == UTF8)) {
++ v.setData(s.c_str(), s.length());
++ } else {
++ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
++ v.append(char(*it));
++ }
++ break;
++ }
+ case Latin1:
+ {
++ // We can have the UTF16 inside, but first 256 positions is equal to Latin1
+ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
+ v.append(char(*it));
+ break;
+@@ -750,6 +779,34 @@
+
+ void String::prepare(Type t)
+ {
++ if (t == Locale) t = rccTaglibPatchGetLocaleType();
++
++ if ((t == Latin1)||(t == Latin1ID3)||(t == Latin1ID3V2)) {
++ std::string s = to8Bit(false);
++ ByteVector v;
++
++ if (t == Latin1ID3) v = rccTaglibPatchRecodeInputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccTaglibPatchRecodeInputID3(s, true);
++ else /* Latin1 converted from Locale */ v = rccTaglibPatchRecodeInput(s);
++
++ if (v.size()) {
++ int length = 0;
++ d->data.resize(v.size());
++ wstring::iterator targetIt = d->data.begin();
++ for(ByteVector::ConstIterator it = v.begin(); it != v.end() && (*it); ++it) {
++ *targetIt = uchar(*it);
++ ++targetIt;
++ ++length;
++ }
++ d->data.resize(length);
++ t = UTF8;
++ } else {
++ // We don't know if we got UTF-8 encoded string or either rcc is disable or something is failed,
++ // since standard applications are really expecting here Latin1, it is safe to just check if we have violations of UTF8
++ //if (Unicode::isLegalUTF8(s)) t = UTF8;
++ }
++ }
++
+ switch(t) {
+ case UTF16:
+ {
+@@ -839,6 +896,27 @@
+
+ std::ostream &operator<<(std::ostream &s, const String &str)
+ {
+- s << str.to8Bit();
++ ByteVector bv = str.data(String::Locale);
++ s << bv;
+ return s;
+ }
++
++String::Type String::ID3Type(int i) {
++ if (i == Latin1) return Latin1ID3V2;
++ return Type(i);
++};
++
++String::Type String::ID3WType(Type type) {
++ Type rcc_type = rccTaglibPatchGetID3Type();
++ if ((rcc_type == Latin1ID3)||(rcc_type == Latin1ID3V2)||(rcc_type == Latin1)) {
++ if (type == Latin1) return rcc_type;
++ return type;
++ }
++
++ return rcc_type;
++};
++
++String::Type String::ID3RealType(Type type) {
++ if ((type == Latin1ID3)||(type == Latin1ID3V2)) return Latin1;
++ return type;
++}
+diff -dPNur taglib-1.8/taglib/toolkit/tstring.h taglib-1.8-ds/taglib/toolkit/tstring.h
+--- taglib-1.8/taglib/toolkit/tstring.h 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/toolkit/tstring.h 2013-05-22 20:13:16.000000000 +0200
+@@ -90,6 +90,18 @@
+ */
+ enum Type {
+ /*!
++ * Determine using current locale settings
++ */
++ Locale = -1,
++ /*!
++ * Latin1 for ID3 tags.
++ */
++ Latin1ID3 = 65,
++ /*!
++ * Latin1 for ID3 tags.
++ */
++ Latin1ID3V2 = 66,
++ /*!
+ * IS08859-1, or <i>Latin1</i> encoding. 8 bit characters.
+ */
+ Latin1 = 0,
+@@ -112,6 +124,10 @@
+ UTF16LE = 4
+ };
+
++ static Type ID3Type(int i);
++ static Type ID3WType(Type type);
++ static Type ID3RealType(Type type);
++
+ /*!
+ * Constructs an empty String.
+ */
diff --git a/patches/taglib/taglib-1.8-ds-rusxmms.patch b/patches/taglib/taglib-1.8-ds-rusxmms.patch
new file mode 100644
index 0000000..a5c04a5
--- /dev/null
+++ b/patches/taglib/taglib-1.8-ds-rusxmms.patch
@@ -0,0 +1,519 @@
+diff -dPNur taglib-1.8/config-taglib.h.cmake taglib-1.8-ds/config-taglib.h.cmake
+--- taglib-1.8/config-taglib.h.cmake 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/config-taglib.h.cmake 2012-11-11 09:59:50.000000000 +0100
+@@ -3,6 +3,8 @@
+ /* Define if you have libz */
+ #cmakedefine HAVE_ZLIB 1
+
++#cmakedefine HAVE_LIBRCC 1
++
+ #cmakedefine NO_ITUNES_HACKS 1
+ #cmakedefine WITH_ASF 1
+ #cmakedefine WITH_MP4 1
+diff -dPNur taglib-1.8/ConfigureChecks.cmake taglib-1.8-ds/ConfigureChecks.cmake
+--- taglib-1.8/ConfigureChecks.cmake 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/ConfigureChecks.cmake 2012-11-11 10:01:48.000000000 +0100
+@@ -14,6 +14,8 @@
+ set(HAVE_ZLIB 0)
+ endif()
+
++SET(HAVE_LIBRCC 1)
++
+ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
+ find_package(CppUnit)
+ if(NOT CppUnit_FOUND AND BUILD_TESTS)
+diff -dPNur taglib-1.8/taglib/CMakeLists.txt taglib-1.8-ds/taglib/CMakeLists.txt
+--- taglib-1.8/taglib/CMakeLists.txt 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/CMakeLists.txt 2012-11-11 10:04:40.000000000 +0100
+@@ -35,6 +35,7 @@
+ audioproperties.h
+ taglib_export.h
+ ${CMAKE_BINARY_DIR}/taglib_config.h
++ toolkit/rccpatch.h
+ toolkit/taglib.h
+ toolkit/tstring.h
+ toolkit/tlist.h
+@@ -269,6 +270,7 @@
+ )
+
+ set(toolkit_SRCS
++ toolkit/rccpatch.cpp
+ toolkit/tstring.cpp
+ toolkit/tstringlist.cpp
+ toolkit/tbytevector.cpp
+@@ -296,7 +298,7 @@
+ add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
+
+ if(ZLIB_FOUND)
+- target_link_libraries(tag ${ZLIB_LIBRARIES})
++ target_link_libraries(tag rcc ${ZLIB_LIBRARIES})
+ endif()
+
+ set_target_properties(tag PROPERTIES
+diff -dPNur taglib-1.8/taglib/mpeg/id3v1/id3v1tag.cpp taglib-1.8-ds/taglib/mpeg/id3v1/id3v1tag.cpp
+--- taglib-1.8/taglib/mpeg/id3v1/id3v1tag.cpp 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/mpeg/id3v1/id3v1tag.cpp 2012-11-11 09:59:50.000000000 +0100
+@@ -64,17 +64,18 @@
+
+ String ID3v1::StringHandler::parse(const ByteVector &data) const
+ {
+- return String(data, String::Latin1).stripWhiteSpace();
++ return String(data, String::Latin1ID3).stripWhiteSpace();
+ }
+
+ ByteVector ID3v1::StringHandler::render(const String &s) const
+ {
+ if(!s.isLatin1())
+ {
++ if (String::ID3WType(String::Latin1) == String::Latin1)
+ return ByteVector();
+ }
+
+- return s.data(String::Latin1);
++ return s.data(String::Latin1ID3);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+@@ -247,7 +248,7 @@
+ d->track = uchar(data[offset + 29]);
+ }
+ else
+- d->comment = data.mid(offset, 30);
++ d->comment = TagPrivate::stringHandler->parse(data.mid(offset, 30));
+
+ offset += 30;
+
+diff -dPNur taglib-1.8/taglib/mpeg/id3v1/id3v1tag.cpp.orig taglib-1.8-ds/taglib/mpeg/id3v1/id3v1tag.cpp.orig
+diff -dPNur taglib-1.8/taglib/mpeg/id3v2/frames/commentsframe.cpp taglib-1.8-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp
+--- taglib-1.8/taglib/mpeg/id3v2/frames/commentsframe.cpp 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp 2012-11-11 09:59:50.000000000 +0100
+@@ -150,10 +150,10 @@
+ return;
+ }
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+ d->language = data.mid(1, 3);
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
+
+@@ -174,10 +174,12 @@
+
+ String::Type encoding = d->textEncoding;
+
++ encoding = String::ID3WType(encoding);
++
+ encoding = checkTextEncoding(d->description, encoding);
+ encoding = checkTextEncoding(d->text, encoding);
+-
+- v.append(char(encoding));
++
++ v.append(char(String::ID3RealType(encoding)));
+ v.append(d->language.size() == 3 ? d->language : "XXX");
+ v.append(d->description.data(encoding));
+ v.append(textDelimiter(encoding));
+diff -dPNur taglib-1.8/taglib/mpeg/id3v2/frames/commentsframe.cpp.orig taglib-1.8-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp.orig
+diff -dPNur taglib-1.8/taglib/mpeg/id3v2/frames/textidentificationframe.cpp taglib-1.8-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp
+--- taglib-1.8/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2012-11-11 09:59:50.000000000 +0100
+@@ -187,12 +187,12 @@
+
+ // read the string data type (the first byte of the field data)
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+
+ // split the byte array into chunks based on the string type (two byte delimiter
+ // for unicode encodings)
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ // build a small counter to strip nulls off the end of the field
+
+@@ -223,11 +223,14 @@
+
+ ByteVector TextIdentificationFrame::renderFields() const
+ {
+- String::Type encoding = checkTextEncoding(d->fieldList, d->textEncoding);
++ String::Type encoding = d->textEncoding;
++
++ encoding = String::ID3WType(encoding);
++ encoding = checkTextEncoding(d->fieldList, encoding);
+
+ ByteVector v;
+
+- v.append(char(encoding));
++ v.append(char(String::ID3RealType(encoding)));
+
+ for(StringList::ConstIterator it = d->fieldList.begin(); it != d->fieldList.end(); it++) {
+
+diff -dPNur taglib-1.8/taglib/mpeg/id3v2/frames/textidentificationframe.cpp.orig taglib-1.8-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp.orig
+diff -dPNur taglib-1.8/taglib/toolkit/rccpatch.cpp taglib-1.8-ds/taglib/toolkit/rccpatch.cpp
+--- taglib-1.8/taglib/toolkit/rccpatch.cpp 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.8-ds/taglib/toolkit/rccpatch.cpp 2012-11-11 10:00:09.000000000 +0100
+@@ -0,0 +1,198 @@
++#include <stdlib.h>
++
++#include <string>
++#include "tstring.h"
++#include "tbytevector.h"
++
++#ifndef HAVE_LIBRCC
++# include <config.h>
++#endif
++
++#ifdef HAVE_LIBRCC
++# include <librcc.h>
++# include <string.h>
++#endif /* HAVE_LIBRCC */
++
++
++#ifdef HAVE_LIBRCC
++# define ID3_CLASS 0
++# define ID3V2_CLASS 1
++# define UTF_CLASS 2
++# define OUT_CLASS 3
++static rcc_class classes[] = {
++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
++ { "id3v2", RCC_CLASS_STANDARD, "id3", NULL, "ID3 v.2 Encoding", 0 },
++ { "utf", RCC_CLASS_KNOWN, "UTF-8", NULL, "Unicode Encoding", 0},
++ { "out", RCC_CLASS_TRANSLATE_LOCALE, "LC_CTYPE", NULL, "Output Encoding", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int rcc_initialized = 0;
++
++static rcc_context ctx = NULL;
++#endif /* HAVE_LIBRCC */
++
++
++void rccPatchFree() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) {
++ rccFree();
++ rcc_initialized = 0;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++void rccPatchInit() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) return;
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "xmms");
++ rccInitDb4(NULL, NULL, 0);
++ rcc_initialized = 1;
++#endif /* HAVE_LIBRCC */
++}
++
++void rccPatchSetContext(void *newctx) {
++#ifdef HAVE_LIBRCC
++ if (newctx) {
++ ctx = (rcc_context)newctx;
++ rcc_initialized = 1;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++static void rccPatchTryInit() {
++#ifdef HAVE_LIBRCC
++ if (!rcc_initialized) {
++ rccPatchInit();
++ if (rcc_initialized) atexit(rccPatchFree);
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++
++TagLib::ByteVector rccPatchRecodeOutput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, OUT_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeOutputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, v2?ID3V2_CLASS:ID3_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeInput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, OUT_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccPatchRecodeInputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccPatchTryInit();
++
++ res = rccSizedRecode(ctx, v2?ID3V2_CLASS:ID3_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++ if (res) v.setData(res, rlen);
++ else v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::String::Type rccPatchGetLocaleType() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ char charset[32];
++
++ rccPatchTryInit();
++
++ if (!rccLocaleGetCharset(charset, NULL, 31)) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::UTF8;
++}
++
++TagLib::String::Type rccPatchGetID3Type() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ const char *charset;
++
++ rccPatchTryInit();
++
++ charset = rccGetCurrentCharsetName(ctx, ID3V2_CLASS);
++ if (charset) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1ID3V2;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::Latin1;
++}
+diff -dPNur taglib-1.8/taglib/toolkit/rccpatch.h taglib-1.8-ds/taglib/toolkit/rccpatch.h
+--- taglib-1.8/taglib/toolkit/rccpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.8-ds/taglib/toolkit/rccpatch.h 2012-11-11 10:00:09.000000000 +0100
+@@ -0,0 +1,20 @@
++#ifndef _RCC_PATCH_H
++#define _RCC_PATCH_H
++
++#include <string.h>
++#include "tstring.h"
++#include "tbytevector.h"
++
++void rccPatchFree();
++void rccPatchInit();
++void rccPatchSetContext(void *newctx);
++
++TagLib::ByteVector rccPatchRecodeOutput(const std::string &s);
++TagLib::ByteVector rccPatchRecodeInput(const std::string &s);
++TagLib::ByteVector rccPatchRecodeOutputID3(const std::string &s, bool v2 = false);
++TagLib::ByteVector rccPatchRecodeInputID3(const std::string &s, bool v2 = false);
++
++TagLib::String::Type rccPatchGetLocaleType();
++TagLib::String::Type rccPatchGetID3Type();
++
++#endif /* _RCC_PATCH_H */
+diff -dPNur taglib-1.8/taglib/toolkit/tstring.cpp taglib-1.8-ds/taglib/toolkit/tstring.cpp
+--- taglib-1.8/taglib/toolkit/tstring.cpp 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/toolkit/tstring.cpp 2012-11-11 10:00:09.000000000 +0100
+@@ -23,6 +23,7 @@
+ * http://www.mozilla.org/MPL/ *
+ ***************************************************************************/
+
++#include "rccpatch.h"
+ #include "tstring.h"
+ #include "unicode.h"
+ #include "tdebug.h"
+@@ -168,7 +169,7 @@
+ if(v.isEmpty())
+ return;
+
+- if(t == Latin1 || t == UTF8) {
++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2 || t == UTF8) {
+
+ int length = 0;
+ d->data.resize(v.size());
+@@ -397,10 +398,21 @@
+ {
+ ByteVector v;
+
+- switch(t) {
++ if (t == Locale) t = rccPatchGetLocaleType();
+
++ switch(t) {
++ case Locale:
+ case Latin1:
++ case Latin1ID3:
++ case Latin1ID3V2:
+ {
++ std::string s = to8Bit(true);
++ if (t == Latin1ID3) v = rccPatchRecodeOutputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccPatchRecodeOutputID3(s, true);
++ else /* if (t == Latin1(Locale) */ v = rccPatchRecodeOutput(s);
++
++ if (v.size()) return v;
++
+ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
+ v.append(char(*it));
+ break;
+@@ -750,6 +762,31 @@
+
+ void String::prepare(Type t)
+ {
++ if (t == Locale) t = rccPatchGetLocaleType();
++
++ if ((t == Latin1)||(t == Latin1ID3)||(t == Latin1ID3V2)) {
++ std::string s = to8Bit(false);
++ ByteVector v;
++
++ if (t == Latin1ID3) v = rccPatchRecodeInputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccPatchRecodeInputID3(s, true);
++ else /* Latin1 converted from Locale */ v = rccPatchRecodeInput(s);
++
++ if (v.size()) {
++ int length = 0;
++ d->data.resize(v.size());
++ wstring::iterator targetIt = d->data.begin();
++ for(ByteVector::ConstIterator it = v.begin(); it != v.end() && (*it); ++it) {
++ *targetIt = uchar(*it);
++ ++targetIt;
++ ++length;
++ }
++ d->data.resize(length);
++ }
++
++ t = UTF8;
++ }
++
+ switch(t) {
+ case UTF16:
+ {
+@@ -839,6 +876,27 @@
+
+ std::ostream &operator<<(std::ostream &s, const String &str)
+ {
+- s << str.to8Bit();
++ ByteVector bv = str.data(String::Locale);
++ s << bv;
+ return s;
+ }
++
++String::Type String::ID3Type(int i) {
++ if (i == Latin1) return Latin1ID3V2;
++ return Type(i);
++};
++
++String::Type String::ID3WType(Type type) {
++ Type rcc_type = rccPatchGetID3Type();
++ if ((rcc_type == Latin1ID3)||(rcc_type == Latin1ID3V2)) {
++ if (type == Latin1) return rcc_type;
++ return type;
++ }
++
++ return rcc_type;
++};
++
++String::Type String::ID3RealType(Type type) {
++ if ((type == Latin1ID3)||(type == Latin1ID3V2)) return Latin1;
++ return type;
++}
+diff -dPNur taglib-1.8/taglib/toolkit/tstring.cpp.orig taglib-1.8-ds/taglib/toolkit/tstring.cpp.orig
+diff -dPNur taglib-1.8/taglib/toolkit/tstring.h taglib-1.8-ds/taglib/toolkit/tstring.h
+--- taglib-1.8/taglib/toolkit/tstring.h 2012-09-06 20:03:15.000000000 +0200
++++ taglib-1.8-ds/taglib/toolkit/tstring.h 2012-11-11 10:00:09.000000000 +0100
+@@ -90,6 +90,18 @@
+ */
+ enum Type {
+ /*!
++ * Determine using current locale settings
++ */
++ Locale = -1,
++ /*!
++ * Latin1 for ID3 tags.
++ */
++ Latin1ID3 = 65,
++ /*!
++ * Latin1 for ID3 tags.
++ */
++ Latin1ID3V2 = 66,
++ /*!
+ * IS08859-1, or <i>Latin1</i> encoding. 8 bit characters.
+ */
+ Latin1 = 0,
+@@ -112,6 +124,10 @@
+ UTF16LE = 4
+ };
+
++ static Type ID3Type(int i);
++ static Type ID3WType(Type type);
++ static Type ID3RealType(Type type);
++
+ /*!
+ * Constructs an empty String.
+ */
+diff -dPNur taglib-1.8/taglib/toolkit/tstring.h.orig taglib-1.8-ds/taglib/toolkit/tstring.h.orig
diff --git a/patches/taglib/taglib-1.9.1-ds-rusxmms-enforce.patch b/patches/taglib/taglib-1.9.1-ds-rusxmms-enforce.patch
new file mode 100644
index 0000000..e980b7a
--- /dev/null
+++ b/patches/taglib/taglib-1.9.1-ds-rusxmms-enforce.patch
@@ -0,0 +1,20 @@
+--- taglib-1.9.1/taglib/mpeg/id3v1/id3v1tag.h 2013-10-08 17:50:01.000000000 +0200
++++ taglib-1.9.1-taurus/taglib/mpeg/id3v1/id3v1tag.h 2013-11-12 00:24:31.206495291 +0100
+@@ -68,7 +68,7 @@
+ * Decode a string from \a data. The default implementation assumes that
+ * \a data is an ISO-8859-1 (Latin1) character array.
+ */
+- virtual String parse(const ByteVector &data) const;
++ String parse(const ByteVector &data) const;
+
+ /*!
+ * Encode a ByteVector with the data from \a s. The default implementation
+@@ -79,7 +79,7 @@
+ * instead do not write an ID3v1 tag in the case that the data is not
+ * ISO-8859-1.
+ */
+- virtual ByteVector render(const String &s) const;
++ ByteVector render(const String &s) const;
+ };
+
+ //! The main class in the ID3v1 implementation
diff --git a/patches/taglib/taglib-1.9.1-ds-rusxmms.patch b/patches/taglib/taglib-1.9.1-ds-rusxmms.patch
new file mode 100644
index 0000000..b64b5b5
--- /dev/null
+++ b/patches/taglib/taglib-1.9.1-ds-rusxmms.patch
@@ -0,0 +1,676 @@
+diff -dPNur taglib-1.9.1/config.h.cmake taglib-1.9.1-ds/config.h.cmake
+--- taglib-1.9.1/config.h.cmake 2013-10-08 17:50:01.000000000 +0200
++++ taglib-1.9.1-ds/config.h.cmake 2013-11-11 13:43:48.500304915 +0100
+@@ -31,6 +31,9 @@
+ /* Defined if you have libz */
+ #cmakedefine HAVE_ZLIB 1
+
++/* Defined if you have LibRCC from RusXMMS project */
++#cmakedefine HAVE_LIBRCC 1
++
+ /* Indicates whether debug messages are shown even in release mode */
+ #cmakedefine TRACE_IN_RELEASE 1
+
+diff -dPNur taglib-1.9.1/ConfigureChecks.cmake taglib-1.9.1-ds/ConfigureChecks.cmake
+--- taglib-1.9.1/ConfigureChecks.cmake 2013-10-08 17:50:01.000000000 +0200
++++ taglib-1.9.1-ds/ConfigureChecks.cmake 2013-11-11 13:42:53.017126134 +0100
+@@ -216,6 +216,7 @@
+ set(HAVE_ZLIB 0)
+ endif()
+
++SET(HAVE_LIBRCC 1)
+
+ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
+
+diff -dPNur taglib-1.9.1/examples/tagreader_c.c taglib-1.9.1-ds/examples/tagreader_c.c
+--- taglib-1.9.1/examples/tagreader_c.c 2013-10-08 17:50:01.000000000 +0200
++++ taglib-1.9.1-ds/examples/tagreader_c.c 2013-11-11 13:42:53.017126134 +0100
+@@ -38,7 +38,7 @@
+ TagLib_Tag *tag;
+ const TagLib_AudioProperties *properties;
+
+- taglib_set_strings_unicode(FALSE);
++// taglib_set_strings_unicode(FALSE);
+
+ for(i = 1; i < argc; i++) {
+ printf("******************** \"%s\" ********************\n", argv[i]);
+diff -dPNur taglib-1.9.1/examples/tagwriter.cpp taglib-1.9.1-ds/examples/tagwriter.cpp
+--- taglib-1.9.1/examples/tagwriter.cpp 2013-10-08 17:50:01.000000000 +0200
++++ taglib-1.9.1-ds/examples/tagwriter.cpp 2013-11-11 13:42:53.028126368 +0100
+@@ -92,7 +92,7 @@
+ if(isArgument(argv[i]) && i + 1 < argc && !isArgument(argv[i + 1])) {
+
+ char field = argv[i][1];
+- TagLib::String value = argv[i + 1];
++ TagLib::String value(argv[i + 1], TagLib::String::Locale);
+
+ TagLib::List<TagLib::FileRef>::Iterator it;
+ for(it = fileList.begin(); it != fileList.end(); ++it) {
+diff -dPNur taglib-1.9.1/taglib/CMakeLists.txt taglib-1.9.1-ds/taglib/CMakeLists.txt
+--- taglib-1.9.1/taglib/CMakeLists.txt 2013-10-08 17:50:01.000000000 +0200
++++ taglib-1.9.1-ds/taglib/CMakeLists.txt 2013-11-11 13:42:53.042126665 +0100
+@@ -36,6 +36,7 @@
+ audioproperties.h
+ taglib_export.h
+ ${CMAKE_BINARY_DIR}/taglib_config.h
++ toolkit/rccpatch.h
+ toolkit/taglib.h
+ toolkit/tstring.h
+ toolkit/tlist.h
+@@ -281,6 +282,7 @@
+ )
+
+ set(toolkit_SRCS
++ toolkit/rccpatch.cpp
+ toolkit/tstring.cpp
+ toolkit/tstringlist.cpp
+ toolkit/tbytevector.cpp
+@@ -310,7 +312,7 @@
+ add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
+
+ if(ZLIB_FOUND)
+- target_link_libraries(tag ${ZLIB_LIBRARIES})
++ target_link_libraries(tag rcc ${ZLIB_LIBRARIES})
+ endif()
+
+ set_target_properties(tag PROPERTIES
+diff -dPNur taglib-1.9.1/taglib/mpeg/id3v1/id3v1tag.cpp taglib-1.9.1-ds/taglib/mpeg/id3v1/id3v1tag.cpp
+--- taglib-1.9.1/taglib/mpeg/id3v1/id3v1tag.cpp 2013-10-08 17:50:01.000000000 +0200
++++ taglib-1.9.1-ds/taglib/mpeg/id3v1/id3v1tag.cpp 2013-11-11 13:42:53.043126686 +0100
+@@ -64,17 +64,18 @@
+
+ String ID3v1::StringHandler::parse(const ByteVector &data) const
+ {
+- return String(data, String::Latin1).stripWhiteSpace();
++ return String(data, String::Latin1ID3).stripWhiteSpace();
+ }
+
+ ByteVector ID3v1::StringHandler::render(const String &s) const
+ {
+ if(!s.isLatin1())
+ {
++ if (String::ID3WType(String::Latin1) == String::Latin1)
+ return ByteVector();
+ }
+
+- return s.data(String::Latin1);
++ return s.data(String::Latin1ID3);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+@@ -257,7 +258,7 @@
+ d->track = uchar(data[offset + 29]);
+ }
+ else
+- d->comment = data.mid(offset, 30);
++ d->comment = TagPrivate::stringHandler->parse(data.mid(offset, 30));
+
+ offset += 30;
+
+diff -dPNur taglib-1.9.1/taglib/mpeg/id3v2/frames/commentsframe.cpp taglib-1.9.1-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp
+--- taglib-1.9.1/taglib/mpeg/id3v2/frames/commentsframe.cpp 2013-10-08 17:50:01.000000000 +0200
++++ taglib-1.9.1-ds/taglib/mpeg/id3v2/frames/commentsframe.cpp 2013-11-11 13:42:53.043126686 +0100
+@@ -150,10 +150,10 @@
+ return;
+ }
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+ d->language = data.mid(1, 3);
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
+
+@@ -174,10 +174,12 @@
+
+ String::Type encoding = d->textEncoding;
+
++ encoding = String::ID3WType(encoding);
++
+ encoding = checkTextEncoding(d->description, encoding);
+ encoding = checkTextEncoding(d->text, encoding);
+
+- v.append(char(encoding));
++ v.append(char(String::ID3RealType(encoding)));
+ v.append(d->language.size() == 3 ? d->language : "XXX");
+ v.append(d->description.data(encoding));
+ v.append(textDelimiter(encoding));
+diff -dPNur taglib-1.9.1/taglib/mpeg/id3v2/frames/textidentificationframe.cpp taglib-1.9.1-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp
+--- taglib-1.9.1/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2013-10-08 17:50:01.000000000 +0200
++++ taglib-1.9.1-ds/taglib/mpeg/id3v2/frames/textidentificationframe.cpp 2013-11-11 13:42:53.044126708 +0100
+@@ -187,12 +187,12 @@
+
+ // read the string data type (the first byte of the field data)
+
+- d->textEncoding = String::Type(data[0]);
++ d->textEncoding = String::ID3Type(data[0]);
+
+ // split the byte array into chunks based on the string type (two byte delimiter
+ // for unicode encodings)
+
+- int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
++ int byteAlign = (d->textEncoding == String::Latin1 || d->textEncoding == String::Latin1ID3 || d->textEncoding == String::Latin1ID3V2 || d->textEncoding == String::UTF8) ? 1 : 2;
+
+ // build a small counter to strip nulls off the end of the field
+
+@@ -223,11 +223,14 @@
+
+ ByteVector TextIdentificationFrame::renderFields() const
+ {
+- String::Type encoding = checkTextEncoding(d->fieldList, d->textEncoding);
++ String::Type encoding = d->textEncoding;
++
++ encoding = String::ID3WType(encoding);
++ encoding = checkTextEncoding(d->fieldList, encoding);
+
+ ByteVector v;
+
+- v.append(char(encoding));
++ v.append(char(String::ID3RealType(encoding)));
+
+ for(StringList::ConstIterator it = d->fieldList.begin(); it != d->fieldList.end(); it++) {
+
+diff -dPNur taglib-1.9.1/taglib/mpeg/id3v2/id3v2frame.cpp taglib-1.9.1-ds/taglib/mpeg/id3v2/id3v2frame.cpp
+--- taglib-1.9.1/taglib/mpeg/id3v2/id3v2frame.cpp 2013-10-08 17:50:01.000000000 +0200
++++ taglib-1.9.1-ds/taglib/mpeg/id3v2/id3v2frame.cpp 2013-11-11 13:42:53.045126729 +0100
+@@ -302,7 +302,7 @@
+ if((encoding == String::UTF8 || encoding == String::UTF16BE) && version != 4)
+ return String::UTF16;
+
+- if(encoding != String::Latin1)
++ if((encoding != String::Latin1)&&(encoding != String::Latin1ID3V2))
+ return encoding;
+
+ for(StringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) {
+diff -dPNur taglib-1.9.1/taglib/toolkit/rccpatch.cpp taglib-1.9.1-ds/taglib/toolkit/rccpatch.cpp
+--- taglib-1.9.1/taglib/toolkit/rccpatch.cpp 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.9.1-ds/taglib/toolkit/rccpatch.cpp 2013-11-11 13:42:53.045126729 +0100
+@@ -0,0 +1,237 @@
++#include <stdlib.h>
++
++#include <string>
++#include "tstring.h"
++#include "tbytevector.h"
++
++//#define RCC_DEBUG
++
++
++#ifndef HAVE_LIBRCC
++# include <config.h>
++#endif
++
++#ifdef HAVE_LIBRCC
++# ifdef RCC_DEBUG
++# include <stdio.h>
++# endif /* RCC_DEBUG */
++# include <librcc.h>
++# include <string.h>
++#endif /* HAVE_LIBRCC */
++
++
++#ifdef HAVE_LIBRCC
++# define ID3_CLASS 0
++# define ID3V2_CLASS 1
++# define UTF_CLASS 2
++# define OUT_CLASS 3
++static rcc_class classes[] = {
++ { "id3", RCC_CLASS_STANDARD, NULL, NULL, "ID3 Encoding", 0 },
++ { "id3v2", RCC_CLASS_STANDARD, "id3", NULL, "ID3 v.2 Encoding", 0 },
++ { "utf", RCC_CLASS_KNOWN, "UTF-8", NULL, "Unicode Encoding", 0},
++ { "out", RCC_CLASS_TRANSLATE_LOCALE, "LC_CTYPE", NULL, "Output Encoding", 0 },
++ { NULL, RCC_CLASS_STANDARD, NULL, NULL, NULL, 0 }
++};
++
++static int rcc_initialized = 0;
++
++static rcc_context ctx = NULL;
++#endif /* HAVE_LIBRCC */
++
++
++void rccTaglibPatchFree() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) {
++ rccFree();
++ rcc_initialized = 0;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++void rccTaglibPatchInit() {
++#ifdef HAVE_LIBRCC
++ if (rcc_initialized) return;
++ rccInit();
++ rccInitDefaultContext(NULL, 0, 0, classes, 0);
++ rccLoad(NULL, "xmms");
++ rccInitDb4(NULL, NULL, 0);
++ rcc_initialized = 1;
++#endif /* HAVE_LIBRCC */
++}
++
++void rccTaglibPatchSetContext(void *newctx) {
++#ifdef HAVE_LIBRCC
++ if (newctx) {
++ ctx = (rcc_context)newctx;
++ rcc_initialized = 1;
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++static void rccTaglibPatchTryInit() {
++#ifdef HAVE_LIBRCC
++ if (!rcc_initialized) {
++ rccTaglibPatchInit();
++ if (rcc_initialized) atexit(rccTaglibPatchFree);
++ }
++#endif /* HAVE_LIBRCC */
++}
++
++
++TagLib::ByteVector rccTaglibPatchRecodeOutput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccTaglibPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, OUT_CLASS, s.c_str(), s.length(), &rlen);
++#ifdef RCC_DEBUG
++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
++ if (*c > 127) {
++ printf(" Output: %s - %s\n", s.c_str(), res?res:"null");
++ break;
++ }
++ }
++#endif /* RCC_DEBUG */
++
++ if (res) v.setData(res, rlen);
++ else v.setData("", 0);
++ //v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccTaglibPatchRecodeOutputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccTaglibPatchTryInit();
++
++ res = rccSizedRecode(ctx, UTF_CLASS, v2?ID3V2_CLASS:ID3_CLASS, s.c_str(), s.length(), &rlen);
++#ifdef RCC_DEBUG
++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
++ if (*c > 127) {
++ printf(" OutputID3(%i): %s - %s\n", v2, s.c_str(), res?res:"null");
++ break;
++ }
++ }
++#endif /* RCC_DEBUG */
++
++ if (res) v.setData(res, rlen);
++ else v.setData("", 0);
++ //v.setData(s.c_str(), s.length());
++
++ return v;
++#else
++ v.setData("", 0);
++
++ return v;
++#endif /* HAVE_LIBRCC */
++}
++
++TagLib::ByteVector rccTaglibPatchRecodeInput(const std::string &s) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccTaglibPatchTryInit();
++
++ res = rccSizedRecode(ctx, OUT_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++#ifdef RCC_DEBUG
++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
++ if (*c > 127) {
++ printf(" Input: %s - %s\n", s.c_str(), res?res:"null");
++ break;
++ }
++ }
++#endif /* RCC_DEBUG */
++
++ if (res) v.setData(res, rlen);
++ else
++#endif /* HAVE_LIBRCC */
++ v.setData("", 0);
++
++ return v;
++}
++
++TagLib::ByteVector rccTaglibPatchRecodeInputID3(const std::string &s, bool v2 = false) {
++ TagLib::ByteVector v;
++#ifdef HAVE_LIBRCC
++ size_t rlen;
++ char *res;
++
++ rccTaglibPatchTryInit();
++
++ res = rccSizedRecode(ctx, v2?ID3V2_CLASS:ID3_CLASS, UTF_CLASS, s.c_str(), s.length(), &rlen);
++#ifdef RCC_DEBUG
++ for (const unsigned char *c = (const unsigned char*)s.c_str(); *c; c++) {
++ if (*c > 127) {
++ printf(" InputID3(%i): %s - %s\n", v2, s.c_str(), res?res:"null");
++ break;
++ }
++ }
++#endif /* RCC_DEBUG */
++ if (res) v.setData(res, rlen + 1);
++ else
++#endif /* HAVE_LIBRCC */
++ v.setData("", 0);
++
++ return v;
++}
++
++TagLib::String::Type rccTaglibPatchGetLocaleType() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ char charset[32];
++
++ rccTaglibPatchTryInit();
++ if (!rccLocaleGetCharset(charset, NULL, 31)) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::UTF8;
++}
++
++TagLib::String::Type rccTaglibPatchGetID3Type() {
++#ifdef HAVE_LIBRCC
++ size_t len;
++ const char *charset;
++
++ rccTaglibPatchTryInit();
++
++ charset = rccGetCurrentCharsetName(ctx, ID3V2_CLASS);
++ if (charset) {
++ if (!strncmp(charset, "UTF", 3)) {
++ len = strlen(charset);
++
++ if (charset[len-1]=='8') return TagLib::String::UTF8;
++ if (!strcmp(charset+(len-2),"16")) return TagLib::String::UTF16;
++ if (!strcmp(charset+(len-4),"16LE")) return TagLib::String::UTF16LE;
++ if (!strcmp(charset+(len-4),"16BE")) return TagLib::String::UTF16BE;
++ }
++ return TagLib::String::Latin1ID3V2;
++ } else {
++ // Error or no-language configured: If Latin1ID3V2 is returned we normally will use the default unicode encoding unless Latin1 is selected by taglib
++ return TagLib::String::Latin1ID3V2;
++ }
++#endif /* HAVE_LIBRCC */
++ return TagLib::String::Latin1;
++}
+diff -dPNur taglib-1.9.1/taglib/toolkit/rccpatch.h taglib-1.9.1-ds/taglib/toolkit/rccpatch.h
+--- taglib-1.9.1/taglib/toolkit/rccpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ taglib-1.9.1-ds/taglib/toolkit/rccpatch.h 2013-11-11 13:42:53.045126729 +0100
+@@ -0,0 +1,20 @@
++#ifndef _RCC_PATCH_H
++#define _RCC_PATCH_H
++
++#include <string.h>
++#include "tstring.h"
++#include "tbytevector.h"
++
++void rccTaglibPatchFree();
++void rccTaglibPatchInit();
++void rccTaglibPatchSetContext(void *newctx);
++
++TagLib::ByteVector rccTaglibPatchRecodeOutput(const std::string &s);
++TagLib::ByteVector rccTaglibPatchRecodeInput(const std::string &s);
++TagLib::ByteVector rccTaglibPatchRecodeOutputID3(const std::string &s, bool v2 = false);
++TagLib::ByteVector rccTaglibPatchRecodeInputID3(const std::string &s, bool v2 = false);
++
++TagLib::String::Type rccTaglibPatchGetLocaleType();
++TagLib::String::Type rccTaglibPatchGetID3Type();
++
++#endif /* _RCC_PATCH_H */
+diff -dPNur taglib-1.9.1/taglib/toolkit/tstring.cpp taglib-1.9.1-ds/taglib/toolkit/tstring.cpp
+--- taglib-1.9.1/taglib/toolkit/tstring.cpp 2013-10-08 17:50:01.000000000 +0200
++++ taglib-1.9.1-ds/taglib/toolkit/tstring.cpp 2013-11-11 13:42:53.046126750 +0100
+@@ -29,6 +29,7 @@
+ #include <config.h>
+ #endif
+
++#include "rccpatch.h"
+ #include "tstring.h"
+ #include "tdebug.h"
+ #include "tstringlist.h"
+@@ -197,8 +198,11 @@
+ String::String(const std::string &s, Type t)
+ : d(new StringPrivate())
+ {
+- if(t == Latin1)
+- copyFromLatin1(&s[0], s.length());
++ if(t == Locale)
++ t = rccTaglibPatchGetLocaleType();
++
++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2)
++ copyFromLatin1(&s[0], s.length(), true, t);
+ else if(t == String::UTF8)
+ copyFromUTF8(&s[0], s.length());
+ else {
+@@ -229,8 +233,11 @@
+ String::String(const char *s, Type t)
+ : d(new StringPrivate())
+ {
+- if(t == Latin1)
+- copyFromLatin1(s, ::strlen(s));
++ if(t == Locale)
++ t = rccTaglibPatchGetLocaleType();
++
++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2)
++ copyFromLatin1(s, ::strlen(s), true, t);
+ else if(t == String::UTF8)
+ copyFromUTF8(s, ::strlen(s));
+ else {
+@@ -251,7 +258,10 @@
+ String::String(char c, Type t)
+ : d(new StringPrivate(1, static_cast<uchar>(c)))
+ {
+- if(t != Latin1 && t != UTF8) {
++ if(t == Locale)
++ t = rccTaglibPatchGetLocaleType();
++
++ if(t != Latin1 && t != Latin1ID3 && t != Latin1ID3V2 && t != UTF8) {
+ debug("String::String() -- A char should not contain UTF16.");
+ }
+ }
+@@ -262,8 +272,11 @@
+ if(v.isEmpty())
+ return;
+
+- if(t == Latin1)
+- copyFromLatin1(v.data(), v.size());
++ if(t == Locale)
++ t = rccTaglibPatchGetLocaleType();
++
++ if(t == Latin1 || t == Latin1ID3 || t == Latin1ID3V2)
++ copyFromLatin1(v.data(), v.size(), true, t);
+ else if(t == UTF8)
+ copyFromUTF8(v.data(), v.size());
+ else
+@@ -428,16 +441,46 @@
+
+ ByteVector String::data(Type t) const
+ {
+- switch(t)
+- {
++ ByteVector v;
++
++ if (t == Locale) {
++ // The source is either Unicode or real Latin1 (if rcc is bypassed)
++ std::string s = to8Bit(true);
++
++ // In case of UTF8 locale, this probably will return NULL (no recoding needed), but we will take UTF8 path in the next swtich
++ v = rccTaglibPatchRecodeOutput(s);
++ if (v.size()) return v;
++
++ t = rccTaglibPatchGetLocaleType();
++ }
++
++ switch(t) {
++ case Latin1ID3:
++ case Latin1ID3V2:
++ {
++ std::string s = to8Bit(true);
++ if (t == Latin1ID3) v = rccTaglibPatchRecodeOutputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccTaglibPatchRecodeOutputID3(s, true);
++ if (v.size())
++ return v;
++
++ // we don't know if we got NULL because rcc is disabled (error) or UTF8 output is required
++ if ((t == Latin1ID3V2)&&(rccTaglibPatchGetID3Type() == UTF8)) {
++ v.setData(s.c_str(), s.length());
++ } else {
++ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
++ v.append(char(*it));
++ }
++ return v;
++ }
+ case Latin1:
+ {
+ ByteVector v(size(), 0);
+ char *p = v.data();
+-
++
+ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
+ *p++ = static_cast<char>(*it);
+-
++
+ return v;
+ }
+ case UTF8:
+@@ -763,12 +806,29 @@
+ // private members
+ ////////////////////////////////////////////////////////////////////////////////
+
+-void String::copyFromLatin1(const char *s, size_t length)
++void String::copyFromLatin1(const char *s, size_t length, bool prepare, Type t)
+ {
+ d->data.resize(length);
+-
+ for(size_t i = 0; i < length; ++i)
+ d->data[i] = static_cast<uchar>(s[i]);
++
++ // librcc conversation
++ if (prepare) {
++ std::string s = to8Bit(false);
++ ByteVector v;
++
++ if (t == Latin1ID3) v = rccTaglibPatchRecodeInputID3(s, false);
++ else if (t == Latin1ID3V2) v = rccTaglibPatchRecodeInputID3(s, true);
++ else /* Latin1 converted from Locale */ v = rccTaglibPatchRecodeInput(s);
++
++ if (v.size()) {
++ copyFromUTF8(v.data(), v.size());
++ } else {
++ // We don't know if we got UTF-8 encoded string or either rcc is disable or something is failed,
++ // since standard applications are really expecting here Latin1, it is safe to just check if we have violations of UTF8
++ //if (Unicode::isLegalUTF8(s)) t = UTF8;
++ }
++ }
+ }
+
+ void String::copyFromUTF8(const char *s, size_t length)
+@@ -874,7 +934,33 @@
+
+ std::ostream &operator<<(std::ostream &s, const TagLib::String &str)
+ {
+- s << str.to8Bit();
++ TagLib::ByteVector bv = str.data(TagLib::String::Locale);
++ s << bv;
+ return s;
+ }
+
++TagLib::String::Type TagLib::String::ID3Type(int i)
++{
++ if(i == Latin1)
++ return Latin1ID3V2;
++ return Type(i);
++};
++
++TagLib::String::Type TagLib::String::ID3WType(Type type)
++{
++ Type rcc_type = rccTaglibPatchGetID3Type();
++ if((rcc_type == Latin1ID3)||(rcc_type == Latin1ID3V2)||(rcc_type == Latin1)) {
++ if(type == Latin1) return
++ rcc_type;
++ return type;
++ }
++
++ return rcc_type;
++};
++
++TagLib::String::Type TagLib::String::ID3RealType(Type type)
++{
++ if((type == Latin1ID3) || (type == Latin1ID3V2))
++ return Latin1;
++ return type;
++}
+diff -dPNur taglib-1.9.1/taglib/toolkit/tstring.h taglib-1.9.1-ds/taglib/toolkit/tstring.h
+--- taglib-1.9.1/taglib/toolkit/tstring.h 2013-10-08 17:50:01.000000000 +0200
++++ taglib-1.9.1-ds/taglib/toolkit/tstring.h 2013-11-11 13:42:53.047126771 +0100
+@@ -90,6 +90,18 @@
+ */
+ enum Type {
+ /*!
++ * Determine using current locale settings
++ */
++ Locale = -1,
++ /*!
++ * Latin1 for ID3 tags.
++ */
++ Latin1ID3 = 65,
++ /*!
++ * Latin1 for ID3v2 tags.
++ */
++ Latin1ID3V2 = 66,
++ /*!
+ * IS08859-1, or <i>Latin1</i> encoding. 8 bit characters.
+ */
+ Latin1 = 0,
+@@ -112,6 +124,10 @@
+ UTF16LE = 4
+ };
+
++ static Type ID3Type(int i);
++ static Type ID3WType(Type type);
++ static Type ID3RealType(Type type);
++
+ /*!
+ * Constructs an empty String.
+ */
+@@ -479,7 +495,7 @@
+ * Converts a \e Latin-1 string into \e UTF-16(without BOM/CPU byte order)
+ * and copies it to the internal buffer.
+ */
+- void copyFromLatin1(const char *s, size_t length);
++ void copyFromLatin1(const char *s, size_t length, bool prepare = false, Type t = Latin1);
+
+ /*!
+ * Converts a \e UTF-8 string into \e UTF-16(without BOM/CPU byte order)
diff --git a/patches/unzip/README b/patches/unzip/README
new file mode 100644
index 0000000..03c08f6
--- /dev/null
+++ b/patches/unzip/README
@@ -0,0 +1,30 @@
+You can use patch in two modes:
+ standard: The unzip will be dynamically linked with librcc (In that case
+ some problems with building OpenOffice from sources may arise)
+
+ lazy: In this case the librcc will be dynamically loaded (with help of
+ dlopen, dlsym) during execution.
+
+
+Usage
+=====
+1. Apply patches: unzip-*-ds-rusxmms.patch, unzip-ds-unixenc.patch
+2. In the root of `unzip` source tree run either `update_shared` for standard
+mode or `update_lazy` for lazy mode.
+
+
+Configuration
+=============
+ - The patch uses "zip" configuration of RCC. This means settings could be
+ altered using "rcc-gtk2-config zip".
+ - ZIP OEM / ZIP ISO / Output - encodings are important for patch.
+
+
+Build
+=====
+ On Unix, call following command to get list of possible targets:
+ make -f unix/Makefile list
+ and then just build with:
+ make -f unix/Makefile <target>
+ for example:
+ make -f unix/Makefile linux_noasm
diff --git a/patches/unzip/unzip-5.52-ds-rusxmms.patch b/patches/unzip/unzip-5.52-ds-rusxmms.patch
new file mode 100644
index 0000000..87835f0
--- /dev/null
+++ b/patches/unzip/unzip-5.52-ds-rusxmms.patch
@@ -0,0 +1,159 @@
+diff -dPNur unzip-5.52/dsrecode.h unzip-5.52-ds/dsrecode.h
+--- unzip-5.52/dsrecode.h 1970-01-01 01:00:00.000000000 +0100
++++ unzip-5.52-ds/dsrecode.h 2008-04-11 22:24:24.000000000 +0200
+@@ -0,0 +1,128 @@
++#include <librcc.h>
++
++static rcc_class_default_charset default_oem[] = {
++ { "ru", "IBM866" },
++ { NULL, NULL }
++};
++
++static rcc_class_default_charset default_iso[] = {
++ { "ru", "CP1251" },
++ { NULL, NULL }
++};
++
++#define OEM_CLASS 0
++#define ISO_CLASS 1
++#define OUT_CLASS 2
++static rcc_class classes[] = {
++ { "oem", RCC_CLASS_STANDARD, NULL, default_oem, "OEM_INTERN", 0 },
++ { "iso", RCC_CLASS_STANDARD, NULL, default_iso, "ISO_INTERN", 0 },
++ { "out", RCC_CLASS_STANDARD, "LC_CTYPE", NULL, "Output", 0 },
++ { NULL }
++};
++
++int initialized = 0;
++
++#ifdef RCC_LAZY
++#include <dlfcn.h>
++# define RCC_LIBRARY "librcc.so.0"
++int (*rccInit2)();
++int (*rccFree2)();
++int (*rccInitDefaultContext2)(const char *locale_variable, unsigned int max_languages, unsigned int max_classes, rcc_class_ptr defclasses, rcc_init_flags flags);
++int (*rccInitDb42)(rcc_context ctx, const char *name, rcc_db4_flags flags);
++char* (*rccSizedRecode2)(rcc_context ctx, rcc_class_id from, rcc_class_id to, const char *buf, size_t len, size_t *rlen);
++int (*rccLoad2)(rcc_context ctx, const char *name);
++
++
++static char *rccRecode2(rcc_context ctx, rcc_class_id from, rcc_class_id to, const char *buf) {
++ return rccSizedRecode2(ctx, from, to, buf, 0, NULL);
++}
++
++void *rcc_handle;
++#else /* RCC_LAZY */
++#define rccInit2 rccInit
++#define rccFree2 rccFree
++#define rccInitDefaultContext2 rccInitDefaultContext
++#define rccInitDb42 rccInitDb4
++#define rccRecode2 rccRecode
++#define rccLoad2 rccLoad
++#endif /* RCC_LAZY */
++
++static void rccUnzipFree() {
++ if (initialized > 0) {
++ rccFree2();
++#ifdef RCC_LAZY
++ dlclose(rcc_handle);
++#endif /* RCC_LAZY */
++ initialized = 0;
++ }
++}
++
++
++static int rccUnzipInit() {
++ if (initialized) return 0;
++
++#ifdef RCC_LAZY
++ if (sizeof(size_t) == 8) {
++ rcc_handle = dlopen("/usr/lib64/" RCC_LIBRARY, RTLD_NOW);
++ if (!rcc_handle) rcc_handle = dlopen("/usr/local/lib64/" RCC_LIBRARY, RTLD_NOW);
++ } else {
++ rcc_handle = dlopen("/usr/lib32/" RCC_LIBRARY, RTLD_NOW);
++ if (!rcc_handle) rcc_handle = dlopen("/usr/local/lib32/" RCC_LIBRARY, RTLD_NOW);
++ }
++ if (!rcc_handle) {
++ rcc_handle = dlopen("/usr/lib/" RCC_LIBRARY, RTLD_NOW);
++ if (!rcc_handle) rcc_handle = dlopen("/usr/local/lib/" RCC_LIBRARY, RTLD_NOW);
++ }
++ if (!rcc_handle) {
++ initialized = -1;
++ return 1;
++ }
++
++ rccInit2 = dlsym(rcc_handle, "rccInit");
++ rccFree2 = dlsym(rcc_handle, "rccFree");
++ rccInitDefaultContext2 = dlsym(rcc_handle, "rccInitDefaultContext");
++ rccInitDb42 = dlsym(rcc_handle, "rccInitDb4");
++ rccSizedRecode2 = dlsym(rcc_handle, "rccSizedRecode");
++ rccLoad2 = dlsym(rcc_handle, "rccLoad");
++
++ if ((!rccInit2)||(!rccFree2)||(!rccInitDefaultContext2)||(!rccInitDb42)||(!rccSizedRecode2)||(!rccLoad2)) {
++ dlclose(rcc_handle);
++ initialized = -1;
++ return 1;
++ }
++#endif /* RCC_LAZY */
++
++ rccInit2();
++ rccInitDefaultContext2(NULL, 0, 0, classes, 0);
++ rccLoad2(NULL, "zip");
++ rccInitDb42(NULL, NULL, 0);
++ atexit(rccUnzipFree);
++ initialized = 1;
++ return 0;
++}
++
++
++
++void _DS_OEM_INTERN(char *string) {
++ char *str;
++ rccUnzipInit();
++ if (initialized>0) {
++ str = rccRecode2(NULL, OEM_CLASS, OUT_CLASS, string);
++ if (str) {
++ strncpy(string,str,FILNAMSIZ);
++ free(str);
++ }
++ }
++}
++
++void _DS_ISO_INTERN(char *string) {
++ char *str;
++ rccUnzipInit();
++ if (initialized>0) {
++ str = rccRecode2(NULL, ISO_CLASS, OUT_CLASS, string);
++ if (str) {
++ strncpy(string,str,FILNAMSIZ);
++ free(str);
++ }
++ }
++}
+diff -dPNur unzip-5.52/fileio.c unzip-5.52-ds/fileio.c
+--- unzip-5.52/fileio.c 2005-02-27 03:10:12.000000000 +0100
++++ unzip-5.52-ds/fileio.c 2008-04-11 22:25:31.000000000 +0200
+@@ -78,7 +78,7 @@
+ # endif
+ #endif
+ #include "ebcdic.h" /* definition/initialization of ebcdic[] */
+-
++#include "dsrecode.h"
+
+ /*
+ Note: Under Windows, the maximum size of the buffer that can be used
+diff -dPNur unzip-5.52/unzpriv.h unzip-5.52-ds/unzpriv.h
+--- unzip-5.52/unzpriv.h 2005-02-04 00:26:42.000000000 +0100
++++ unzip-5.52-ds/unzpriv.h 2008-04-11 22:25:31.000000000 +0200
+@@ -2564,9 +2564,9 @@
+ ((hostver) == 25 || (hostver) == 26 || (hostver) == 40))) || \
+ (hostnum) == FS_HPFS_ || \
+ ((hostnum) == FS_NTFS_ && (hostver) == 50)) { \
+- _OEM_INTERN((string)); \
++ _DS_OEM_INTERN((string)); \
+ } else { \
+- _ISO_INTERN((string)); \
++ _DS_ISO_INTERN((string)); \
+ }
+ #endif
+
diff --git a/patches/unzip/unzip-5.52-ds-rusxmms2.patch b/patches/unzip/unzip-5.52-ds-rusxmms2.patch
new file mode 100644
index 0000000..6670a10
--- /dev/null
+++ b/patches/unzip/unzip-5.52-ds-rusxmms2.patch
@@ -0,0 +1,149 @@
+diff -dPNur unzip-5.52/fileio.c unzip-5.52-ds/fileio.c
+--- unzip-5.52/fileio.c 2005-02-27 03:10:12.000000000 +0100
++++ unzip-5.52-ds/fileio.c 2008-04-11 22:25:31.000000000 +0200
+@@ -78,7 +78,7 @@
+ # endif
+ #endif
+ #include "ebcdic.h" /* definition/initialization of ebcdic[] */
+-
++#include "dsrecode.h"
+
+ /*
+ Note: Under Windows, the maximum size of the buffer that can be used
+diff -dPNur unzip-5.52/unzpriv.h unzip-5.52-ds/unzpriv.h
+--- unzip-5.52/unzpriv.h 2005-02-04 00:26:42.000000000 +0100
++++ unzip-5.52-ds/unzpriv.h 2008-04-11 22:25:31.000000000 +0200
+@@ -2564,9 +2564,9 @@
+ ((hostver) == 25 || (hostver) == 26 || (hostver) == 40))) || \
+ (hostnum) == FS_HPFS_ || \
+ ((hostnum) == FS_NTFS_ && (hostver) == 50)) { \
+- _OEM_INTERN((string)); \
++ _DS_OEM_INTERN((string)); \
+ } else { \
+- _ISO_INTERN((string)); \
++ _DS_ISO_INTERN((string)); \
+ }
+ #endif
+
+diff -dPNur unzip-5.52/dsrecode.h unzip-5.52-ds/dsrecode.h
+--- unzip-5.52/dsrecode.h 1970-01-01 01:00:00.000000000 +0100
++++ unzip-5.52-ds/dsrecode.h 2008-04-11 22:24:24.000000000 +0200
+@@ -0,0 +1,118 @@
++#include "librcc.h"
++
++static rcc_class_default_charset default_oem[] = {
++ { "ru", "IBM866" },
++ { NULL, NULL }
++};
++
++static rcc_class_default_charset default_iso[] = {
++ { "ru", "CP1251" },
++ { NULL, NULL }
++};
++
++#define OEM_CLASS 0
++#define ISO_CLASS 1
++#define OUT_CLASS 2
++static rcc_class classes[] = {
++ { "oem", RCC_CLASS_STANDARD, NULL, default_oem, "OEM_INTERN", 0 },
++ { "iso", RCC_CLASS_STANDARD, NULL, default_iso, "ISO_INTERN", 0 },
++ { "out", RCC_CLASS_STANDARD, "LC_CTYPE", NULL, "Output", 0 },
++ { NULL }
++};
++
++int initialized = 0;
++
++#ifdef RCC_LAZY
++#include <dlfcn.h>
++# define RCC_LIBRARY "librcc.so.0"
++int (*rccInit2)();
++int (*rccFree2)();
++int (*rccInitDefaultContext2)(const char *locale_variable, unsigned int max_languages, unsigned int max_classes, rcc_class_ptr defclasses, rcc_init_flags flags);
++int (*rccInitDb42)(rcc_context ctx, const char *name, rcc_db4_flags flags);
++char* (*rccSizedRecode2)(rcc_context ctx, rcc_class_id from, rcc_class_id to, const char *buf, size_t len, size_t *rlen);
++int (*rccLoad2)(rcc_context ctx, const char *name);
++
++
++static char *rccRecode2(rcc_context ctx, rcc_class_id from, rcc_class_id to, const char *buf) {
++ return rccSizedRecode2(ctx, from, to, buf, 0, NULL);
++}
++
++void *rcc_handle;
++#else /* RCC_LAZY */
++#define rccInit2 rccInit
++#define rccFree2 rccFree
++#define rccInitDefaultContext2 rccInitDefaultContext
++#define rccInitDb42 rccInitDb4
++#define rccRecode2 rccRecode
++#define rccLoad2 rccLoad
++#endif /* RCC_LAZY */
++
++static void rccUnzipFree() {
++ if (initialized > 0) {
++ rccFree2();
++#ifdef RCC_LAZY
++ dlclose(rcc_handle);
++#endif /* RCC_LAZY */
++ initialized = 0;
++ }
++}
++
++
++static int rccUnzipInit() {
++ if (initialized) return 0;
++
++#ifdef RCC_LAZY
++ rcc_handle = dlopen(RCC_LIBRARY, RTLD_NOW);
++ if (!rcc_handle) {
++ initialized = -1;
++ return 1;
++ }
++
++ rccInit2 = dlsym(rcc_handle, "rccInit");
++ rccFree2 = dlsym(rcc_handle, "rccFree");
++ rccInitDefaultContext2 = dlsym(rcc_handle, "rccInitDefaultContext");
++ rccInitDb42 = dlsym(rcc_handle, "rccInitDb4");
++ rccSizedRecode2 = dlsym(rcc_handle, "rccSizedRecode");
++ rccLoad2 = dlsym(rcc_handle, "rccLoad");
++
++ if ((!rccInit2)||(!rccFree2)||(!rccInitDefaultContext2)||(!rccInitDb42)||(!rccSizedRecode2)||(!rccLoad2)) {
++ dlclose(rcc_handle);
++ initialized = -1;
++ return 1;
++ }
++#endif /* RCC_LAZY */
++
++ rccInit2();
++ rccInitDefaultContext2(NULL, 0, 0, classes, 0);
++ rccLoad2(NULL, "zip");
++ rccInitDb42(NULL, NULL, 0);
++ atexit(rccUnzipFree);
++ initialized = 1;
++ return 0;
++}
++
++
++
++void _DS_OEM_INTERN(char *string) {
++ char *str;
++ rccUnzipInit();
++ if (initialized>0) {
++ str = rccRecode2(NULL, OEM_CLASS, OUT_CLASS, string);
++ if (str) {
++ strncpy(string,str,FILNAMSIZ);
++ free(str);
++ }
++ }
++}
++
++void _DS_ISO_INTERN(char *string) {
++ char *str;
++ rccUnzipInit();
++ if (initialized>0) {
++ str = rccRecode2(NULL, ISO_CLASS, OUT_CLASS, string);
++ if (str) {
++ strncpy(string,str,FILNAMSIZ);
++ free(str);
++ }
++ }
++}
diff --git a/patches/unzip/unzip-ds-unixenc.patch b/patches/unzip/unzip-ds-unixenc.patch
new file mode 100644
index 0000000..e33e6ba
--- /dev/null
+++ b/patches/unzip/unzip-ds-unixenc.patch
@@ -0,0 +1,9 @@
+diff -dPNur unzip-5.50/unzpriv.h unzip-5.50-new/unzpriv.h
+--- unzip-5.50/unzpriv.h Sun Feb 17 21:01:48 2002
++++ unzip-5.50-new/unzpriv.h Tue Jun 10 07:16:23 2003
+@@ -2424,4 +2424,5 @@
+ !(((islochdr) || (isuxatt)) && \
+ ((hostver) == 25 || (hostver) == 26 || (hostver) == 40))) || \
+ (hostnum) == FS_HPFS_ || \
++ (hostnum) == UNIX_ || \
+ ((hostnum) == FS_NTFS_ && (hostver) == 50)) { \
diff --git a/patches/unzip/unzip60-ds-isprint.patch b/patches/unzip/unzip60-ds-isprint.patch
new file mode 100644
index 0000000..64ea1ee
--- /dev/null
+++ b/patches/unzip/unzip60-ds-isprint.patch
@@ -0,0 +1,12 @@
+diff -dPNur unzip60/extract.c unzip60-ds/extract.c
+--- unzip60/extract.c 2009-03-14 02:32:52.000000000 +0100
++++ unzip60-ds/extract.c 2010-07-06 16:34:09.000000000 +0200
+@@ -2596,7 +2596,7 @@
+ */
+ # define UZ_FNFILTER_REPLACECHAR '?'
+ # endif
+- if (!isprint(*r)) {
++ if (*r < 32) { //(!isprint(*r)) {
+ if (*r < 32) {
+ /* ASCII control codes are escaped as "^{letter}". */
+ if (se != NULL && (s > (space + (size-4)))) {
diff --git a/patches/unzip/update_lazy b/patches/unzip/update_lazy
new file mode 100755
index 0000000..de1b04a
--- /dev/null
+++ b/patches/unzip/update_lazy
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+ sed -i \
+ -e "s:-O3:\${CFLAGS}:" \
+ -e "s:CC=gcc :CC=\"gcc -DRCC_LAZY=1\":" \
+ -e "s:LD=gcc :LD=\"gcc \${LDFLAGS} -ldl\" :" \
+ -e "s:-O :\${CFLAGS} :" \
+ -e "s:LF2 = -s:LF2 = :" \
+ unix/Makefile \
diff --git a/patches/unzip/update_shared b/patches/unzip/update_shared
new file mode 100755
index 0000000..1dd8b2a
--- /dev/null
+++ b/patches/unzip/update_shared
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+ sed -i \
+ -e "s:-O3:\${CFLAGS}:" \
+ -e "s:CC=gcc :CC=gcc :" \
+ -e "s:LD=gcc :LD=\"gcc \${LDFLAGS} -lrcc\" :" \
+ -e "s:-O :\${CFLAGS} :" \
+ -e "s:LF2 = -s:LF2 = :" \
+ unix/Makefile \
diff --git a/patches/xmms-plugins/xmms-wma-csa1.tar.bz2 b/patches/xmms-plugins/xmms-wma-csa1.tar.bz2
new file mode 100644
index 0000000..de6968c
--- /dev/null
+++ b/patches/xmms-plugins/xmms-wma-csa1.tar.bz2
Binary files differ
diff --git a/patches/xmms/extra/xmms-ds-mpg123-wrongencoding.patch b/patches/xmms/extra/xmms-ds-mpg123-wrongencoding.patch
new file mode 100644
index 0000000..66a2100
--- /dev/null
+++ b/patches/xmms/extra/xmms-ds-mpg123-wrongencoding.patch
@@ -0,0 +1,157 @@
+diff -dPNur xmms-1.2.11-orig/Input/mpg123/id3_frame_text.c xmms-1.2.11/Input/mpg123/id3_frame_text.c
+--- xmms-1.2.11-orig/Input/mpg123/id3_frame_text.c 2008-04-11 15:44:34.000000000 +0200
++++ xmms-1.2.11/Input/mpg123/id3_frame_text.c 2008-04-11 15:52:25.000000000 +0200
+@@ -46,6 +46,7 @@
+ break;
+ case ID3_ENCODING_UTF16:
+ case ID3_ENCODING_UTF16BE:
++ case ID3_ENCODING_UTF16LE:
+ while (*text != 0 || *(text + 1) != 0)
+ {
+ text += 2;
+@@ -73,6 +74,8 @@
+ return xmms_charset_from_utf16(text);
+ case ID3_ENCODING_UTF16BE:
+ return xmms_charset_from_utf16be(text);
++ case ID3_ENCODING_UTF16LE:
++ return xmms_charset_from_utf16le(text);
+ default:
+ return NULL;
+ }
+@@ -88,6 +91,8 @@
+ */
+ gint8 id3_get_encoding(struct id3_frame *frame)
+ {
++ gint8 encoding;
++
+ /* Type check */
+ if (!id3_frame_is_text(frame) &&
+ frame->fr_desc->fd_id != ID3_WXXX &&
+@@ -106,7 +111,21 @@
+ if (id3_decompress_frame(frame) == -1)
+ return -1;
+
+- return *(gint8 *) frame->fr_data;
++ encoding = *(gint8 *) frame->fr_data;
++
++ switch (encoding) {
++ case ID3_ENCODING_ISO_8859_1:
++ case ID3_ENCODING_UTF8:
++ case ID3_ENCODING_UTF16:
++ case ID3_ENCODING_UTF16BE:
++ return encoding;
++ case ID3_ENCODING_UTF16LE:
++ printf("ID3V2 frame (%.4s) has invalid encoding (%u). Assuming UTF-16LE.\n", frame->fr_desc->fd_idstr?frame->fr_desc->fd_idstr:"UNKN", encoding);
++ return encoding;
++ default:
++ printf("ID3V2 frame (%.4s) has invalid encoding (%u). Assuming Latin1.\n", frame->fr_desc->fd_idstr?frame->fr_desc->fd_idstr:"UNKN", encoding);
++ return ID3_ENCODING_ISO_8859_1;
++ }
+ }
+
+
+@@ -269,6 +288,7 @@
+ else if (encoding == ID3_ENCODING_UTF8) ctext = xmms_charset_to_utf8(text);
+ else if (encoding == ID3_ENCODING_UTF16) ctext = xmms_charset_convert(text, strlen(text), NULL, "UTF-16");
+ else if (encoding == ID3_ENCODING_UTF16BE) ctext = xmms_charset_convert(text, strlen(text), NULL, "UTF-16BE");
++ else if (encoding == ID3_ENCODING_UTF16LE) ctext = xmms_charset_convert(text, strlen(text), NULL, "UTF-16LE");
+ else ctext = NULL;
+
+ if (ctext) text = ctext;
+@@ -433,6 +453,7 @@
+ else if (encoding == ID3_ENCODING_UTF8) ctext = xmms_charset_to_utf8(text);
+ else if (encoding == ID3_ENCODING_UTF16) ctext = xmms_charset_convert(text, strlen(text), NULL, "UTF-16");
+ else if (encoding == ID3_ENCODING_UTF16BE) ctext = xmms_charset_convert(text, strlen(text), NULL, "UTF-16BE");
++ else if (encoding == ID3_ENCODING_UTF16LE) ctext = xmms_charset_convert(text, strlen(text), NULL, "UTF-16LE");
+ else ctext = NULL;
+
+ if (ctext) text = ctext;
+@@ -453,11 +474,12 @@
+ *(gint8 *) frame->fr_raw_data = encoding;
+ memcpy((char*)frame->fr_raw_data + 1, xmms_rcc_get_language(), 3);
+
+- if ((encoding == ID3_ENCODING_UTF16)||(encoding == ID3_ENCODING_UTF16BE)) {
++ if ((encoding == ID3_ENCODING_UTF16)||(encoding == ID3_ENCODING_UTF16BE)||(encoding == ID3_ENCODING_UTF16LE)) {
+ int i;
+ lang = "Comments";
+ if (encoding == ID3_ENCODING_UTF16) cdata = xmms_charset_convert(lang, strlen(lang), NULL, "UTF-16");
+- else cdata = xmms_charset_convert(lang, strlen(lang), NULL, "UTF-16BE");
++ else if (encoding == ID3_ENCODING_UTF16BE) cdata = xmms_charset_convert(lang, strlen(lang), NULL, "UTF-16BE");
++ else cdata = xmms_charset_convert(lang, strlen(lang), NULL, "UTF-16LE");
+ memcpy((char *) frame->fr_raw_data + 4, cdata, 20);
+ g_free(cdata);
+ } else
+diff -dPNur xmms-1.2.11-orig/Input/mpg123/id3.h xmms-1.2.11/Input/mpg123/id3.h
+--- xmms-1.2.11-orig/Input/mpg123/id3.h 2008-04-11 15:44:34.000000000 +0200
++++ xmms-1.2.11/Input/mpg123/id3.h 2008-04-11 15:50:58.000000000 +0200
+@@ -141,6 +141,7 @@
+ #define ID3_ENCODING_UTF16 0x01
+ #define ID3_ENCODING_UTF16BE 0x02
+ #define ID3_ENCODING_UTF8 0x03
++#define ID3_ENCODING_UTF16LE 0x04
+
+ /*
+ * ID3 frame id numbers.
+@@ -312,7 +313,7 @@
+
+
+ #define ID3_TEXT_FRAME_ENCODING(frame) \
+- (*(guint8*)(frame)->fr_data)
++ id3_get_encoding(frame)
+
+ #define ID3_TEXT_FRAME_PTR(frame) \
+ ((char *)(frame)->fr_data + 1)
+diff -dPNur xmms-1.2.11-orig/libxmms/charset.c xmms-1.2.11/libxmms/charset.c
+--- xmms-1.2.11-orig/libxmms/charset.c 2008-04-10 22:49:57.000000000 +0200
++++ xmms-1.2.11/libxmms/charset.c 2008-04-11 15:50:21.000000000 +0200
+@@ -141,6 +141,14 @@
+ return xmms_charset_convert(string, utf16_strlen(string), "UTF-16BE", NULL);
+ }
+
++char *xmms_charset_from_utf16le(const unsigned char *string)
++{
++ if (!string)
++ return NULL;
++
++ return xmms_charset_convert(string, utf16_strlen(string), "UTF-16LE", NULL);
++}
++
+ char* xmms_charset_from_latin1(const char *string)
+ {
+ char *cstring;
+@@ -174,6 +182,9 @@
+ if (!strcmp(from, "UTF-16BE") && !to)
+ return xmms_charset_from_utf16be(string);
+
++ if (!strcmp(from, "UTF-16LE") && !to)
++ return xmms_charset_from_utf16le(string);
++
+ return g_strdup(string);
+ }
+
+@@ -275,6 +286,14 @@
+ return utf16_to_ascii(string, FALSE);
+ }
+
++char *xmms_charset_from_utf16le(const unsigned char *string)
++{
++ if (!string)
++ return NULL;
++
++ return utf16_to_ascii(string, TRUE);
++}
++
+ char* xmms_charset_from_latin1(const char *string)
+ {
+ char *cstring;
+diff -dPNur xmms-1.2.11-orig/libxmms/charset.h xmms-1.2.11/libxmms/charset.h
+--- xmms-1.2.11-orig/libxmms/charset.h 2008-04-10 22:49:57.000000000 +0200
++++ xmms-1.2.11/libxmms/charset.h 2008-04-11 15:49:47.000000000 +0200
+@@ -14,6 +14,7 @@
+ char* xmms_charset_from_utf8(const char *string);
+ char* xmms_charset_from_utf16(const unsigned char *string);
+ char* xmms_charset_from_utf16be(const unsigned char *string);
++char *xmms_charset_from_utf16le(const unsigned char *string);
+ char* xmms_charset_from_latin1(const char *string);
+
+ size_t utf16_strlen(const char *string);
diff --git a/patches/xmms/plugins/xmms-ds-mpg123-editor-keys.patch b/patches/xmms/plugins/xmms-ds-mpg123-editor-keys.patch
new file mode 100644
index 0000000..38a74e2
--- /dev/null
+++ b/patches/xmms/plugins/xmms-ds-mpg123-editor-keys.patch
@@ -0,0 +1,164 @@
+diff -dPNur rusxmms-new/Input/mpg123/fileinfo.c rusxmms-new-keys/Input/mpg123/fileinfo.c
+--- rusxmms-new/Input/mpg123/fileinfo.c 2005-07-17 11:46:05.000000000 +0200
++++ rusxmms-new-keys/Input/mpg123/fileinfo.c 2005-07-18 01:12:36.000000000 +0200
+@@ -31,6 +31,10 @@
+ #define MAX_STR_LEN 100
+ #define MAX_ENTRY_LEN2 1023
+
++#include <gdk/gdkkeysyms.h>
++#include <gdk/gdktypes.h>
++#include "../../xmms/xmms.h"
++
+ static GtkWidget *window = NULL;
+ static GtkWidget *notebook = NULL;
+ static GtkWidget *filename_entry, *id3v1_frame, *id3v2_frame;
+@@ -507,6 +511,106 @@
+ gtk_widget_destroy(w);
+ }
+
++static int restore_focus;
++static GtkWidget *save;
++
++
++static GtkWidget **widgets1[] = { &v1_title_entry, &v1_artist_entry, &v1_album_entry, &v1_comment_entry, &v1_year_entry, NULL };
++static GtkWidget **widgets2[] = { &v2_title_entry, &v2_artist_entry, &v2_album_entry, &v2_comment_entry, &v2_year_entry, &v2_composer_entry, &v2_orig_artist_entry, &v2_url_entry, &v2_encoded_by_entry, NULL };
++/* Info: gdktypes.h, gdkkeysyms.h */
++gboolean mpg123_keypress_cb(GtkWidget * w, GdkEventKey * event, gpointer close) {
++ gint pos;
++ GtkWidget ***widgets, *widget, *focused;
++
++ switch(event->keyval) {
++ case GDK_Return:
++ restore_focus=1;
++ gtk_signal_emit_by_name(GTK_OBJECT(save), "clicked", NULL);
++ gtk_signal_emit_by_name(GTK_OBJECT(close), "clicked", NULL);
++ return TRUE;
++ break;
++ case GDK_Escape:
++ restore_focus=1;
++ return TRUE;
++ break;
++ }
++
++ if (event->state&GDK_CONTROL_MASK) {
++ switch(event->keyval) {
++ case GDK_Left:
++ gtk_notebook_prev_page(GTK_NOTEBOOK(notebook));
++ pos = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
++ if (pos == 0) gtk_window_set_focus(GTK_WINDOW(window), *widgets2[0]);
++ else if (pos == 1) gtk_window_set_focus(GTK_WINDOW(window), *widgets1[0]);
++ gtk_window_activate_focus(GTK_WINDOW(window));
++ return TRUE;
++ break;
++ case GDK_Right:
++ gtk_notebook_next_page(GTK_NOTEBOOK(notebook));
++ pos = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
++ if (pos == 0) gtk_window_set_focus(GTK_WINDOW(window), *widgets2[0]);
++ else if (pos == 1) gtk_window_set_focus(GTK_WINDOW(window), *widgets1[0]);
++ gtk_window_activate_focus(GTK_WINDOW(window));
++ return TRUE;
++ break;
++ }
++ }
++ {
++ switch(event->keyval) {
++ case GDK_Page_Up:
++ pos = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
++
++ focused = GTK_WINDOW(window)->focus_widget;
++ if (pos == 1) widgets = widgets1;
++ else if (pos == 0) widgets = widgets2;
++ else return FALSE;
++
++ for (pos = 0; widgets[pos]; pos++) {
++ if (focused == *widgets[pos]) {
++ if (pos == 0) {
++ while (widgets[pos+1]) pos++;
++ widget = *widgets[pos];
++ } else widget = *widgets[pos - 1];
++
++ gtk_window_set_focus(GTK_WINDOW(window), widget);
++ return TRUE;
++ }
++ }
++ break;
++ case GDK_Page_Down:
++ case GDK_Tab:
++ pos = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
++
++ focused = GTK_WINDOW(window)->focus_widget;
++ if (pos == 1) widgets = widgets1;
++ else if (pos == 0) widgets = widgets2;
++ else return FALSE;
++
++ for (pos = 0; widgets[pos]; pos++) {
++ if (focused == *widgets[pos]) {
++ if (widgets[pos + 1]) widget = *widgets[pos + 1];
++ else widget = *widgets[0];
++
++ gtk_window_set_focus(GTK_WINDOW(window), widget);
++ return TRUE;
++ }
++ }
++
++ break;
++ }
++ }
++
++ return FALSE;
++}
++
++void gtk_widget_destroyed_focus(GtkWidget *widget, GtkWidget **widget_pointer) {
++ gtk_widget_destroyed(widget,widget_pointer);
++ if (restore_focus) {
++ gtk_widget_hide(playlistwin);
++ gtk_widget_show(playlistwin);
++ }
++}
++
+ void mpg123_file_info_box(char *filename)
+ {
+ int i;
+@@ -517,6 +621,8 @@
+ const char *emphasis[4];
+ const char *bool_label[2];
+
++ restore_focus = 0;
++
+ emphasis[0] = _("None");
+ emphasis[1] = _("50/15 ms");
+ emphasis[2] = "";
+@@ -529,15 +635,16 @@
+ GtkWidget *window_vbox,
+ *id3v1_vbox, *id3v2_vbox, *id3v1_frame_vbox, *id3v2_frame_vbox,
+ *mpeg_lvbox, *mpeg_rvbox, *mpeg_hbox, *mpeg_box, *mpeg_frame,
+- *bbox, *save, *close, *copy_to, *copy_from,
++ *bbox, *close, *copy_to, *copy_from,
+ *table, *label, *filename_hbox;
++ GtkAccelGroup *ag;
+
+ v1_labels_list = g_ptr_array_new();
+ v2_labels_list = g_ptr_array_new();
+
+ window = gtk_window_new(GTK_WINDOW_DIALOG);
+- gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &window);
+- gtk_signal_connect(GTK_OBJECT(window), "key_press_event", file_info_box_keypress_cb, NULL);
++ gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed_focus), &window);
++// gtk_signal_connect(GTK_OBJECT(window),"key_press_event",file_info_box_keypress_cb, NULL);
+ gtk_container_set_border_width(GTK_CONTAINER(window), 10);
+
+ window_vbox = gtk_vbox_new(FALSE,10);
+@@ -916,6 +1023,13 @@
+ GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(window));
+ gtk_box_pack_start(GTK_BOX(bbox), close, TRUE, TRUE, 5);
+
++ ag = gtk_accel_group_new();
++ gtk_accel_group_add(ag, GDK_Escape, 0, 0, GTK_OBJECT(close), "clicked");
++ gtk_accel_group_add(ag, GDK_Return, 0, 0, GTK_OBJECT(save), "clicked");
++ gtk_window_add_accel_group(GTK_WINDOW(window), ag);
++
++ gtk_signal_connect(GTK_OBJECT(window), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), close);
++
+ gtk_container_add(GTK_CONTAINER(window), window_vbox);
+ gtk_widget_show_all(window);
+ }
diff --git a/patches/xmms/plugins/xmms-ds-mpg123-editor.patch b/patches/xmms/plugins/xmms-ds-mpg123-editor.patch
new file mode 100644
index 0000000..a4afb70
--- /dev/null
+++ b/patches/xmms/plugins/xmms-ds-mpg123-editor.patch
@@ -0,0 +1,202 @@
+diff -dPNur xmms-1.2.10/Input/mpg123/fileinfo.c xmms-1.2.10-new/Input/mpg123/fileinfo.c
+--- rusxmms/Input/mpg123/fileinfo.c 2005-07-17 01:54:55.000000000 +0200
++++ rusxmms-new/Input/mpg123/fileinfo.c 2005-07-17 11:46:05.000000000 +0200
+@@ -24,6 +24,7 @@
+ #include <string.h>
+ #include <errno.h>
+ #include <libxmms/xentry.h>
++#include <libxmms/rcc.h>
+ #include <gdk/gdkkeysyms.h>
+ #include "mpg123.h"
+
+@@ -60,19 +61,33 @@
+
+ static void set_entry_tag_v1(GtkEntry * entry, const char * tag, int length)
+ {
+- char *text = g_strchomp(g_strndup(tag, length));
++ char *text;
++
++ text = xmms_rcc_sized_recode(XMMS_RCC_ID3, XMMS_RCC_CTYPE, tag, length);
++ if (!text) text = g_strchomp(g_strndup(tag, length));
++
+ gtk_entry_set_text(entry, text);
+ g_free(text);
+ }
+
+ static void get_entry_tag_v1(GtkEntry * entry, char * tag, int length)
+ {
++ gchar *ctext;
++
++ ctext = xmms_rcc_recode(XMMS_RCC_CTYPE, XMMS_RCC_ID3, gtk_entry_get_text(entry));
++
++ if (ctext) {
++ strncpy(tag, ctext, length);
++ free(ctext);
++ } else
+ strncpy(tag, gtk_entry_get_text(entry), length);
+ }
+
+ void copy_entry_tag_v1(GtkEntry * src, GtkEntry * dest, int length)
+ {
+- set_entry_tag_v1(dest, gtk_entry_get_text(src), length);
++ char *text = g_strchomp(g_strndup(gtk_entry_get_text(src), length));
++ gtk_entry_set_text(dest, text);
++ g_free(text);
+ return;
+ }
+
+@@ -497,6 +512,7 @@
+ int i;
+ struct id3v1tag_t id3v1tag;
+ FILE *fh;
++ gchar *cfilename;
+ char *tmp, *title;
+ const char *emphasis[4];
+ const char *bool_label[2];
+@@ -908,6 +924,9 @@
+ g_free(current_filename);
+ current_filename = g_strdup(filename);
+
++ cfilename = xmms_rcc_recode(XMMS_RCC_FS, XMMS_RCC_OUT, filename);
++ if (cfilename) filename=cfilename;
++
+ title = g_strdup_printf(_("File Info - %s"), g_basename(filename));
+ gtk_window_set_title(GTK_WINDOW(window), title);
+ g_free(title);
+@@ -922,6 +941,8 @@
+ gtk_entry_set_text(GTK_ENTRY(v2_title_entry), title);
+ g_free(title);
+
++ if (cfilename) g_free(cfilename);
++
+ gtk_entry_set_text(GTK_ENTRY(v1_artist_entry), "");
+ gtk_entry_set_text(GTK_ENTRY(v1_album_entry), "");
+ gtk_entry_set_text(GTK_ENTRY(v1_year_entry), "");
+diff -dPNur xmms-1.2.10/Input/mpg123/id3_frame_text.c xmms-1.2.10-new/Input/mpg123/id3_frame_text.c
+--- xmms-1.2.10/Input/mpg123/id3_frame_text.c 2005-08-10 03:20:03.000000000 +0200
++++ xmms-1.2.10-new/Input/mpg123/id3_frame_text.c 2005-08-10 03:29:40.000000000 +0200
+@@ -28,6 +28,7 @@
+ #include "id3_header.h"
+
+ #include "libxmms/charset.h"
++#include "libxmms/rcc.h"
+
+ /* For extern mpg123_cfg */
+ #include "mpg123.h"
+@@ -247,6 +248,9 @@
+ */
+ int id3_set_text(struct id3_frame *frame, char *text)
+ {
++ char *ctext;
++ gint8 encoding;
++
+ /* Type check */
+ if (frame->fr_desc->fd_idstr[0] != 'T')
+ return -1;
+@@ -256,18 +260,36 @@
+ */
+ id3_frame_clear_data(frame);
+
++
++ /*
++ * Recoding.
++ */
++ encoding = (gint8)xmms_rcc_get_id3v2_encoding();
++ if (encoding == ID3_ENCODING_ISO_8859_1) ctext = xmms_rcc_recode(XMMS_RCC_CTYPE, XMMS_RCC_ID3V2, text);
++ else if (encoding == ID3_ENCODING_UTF8) ctext = xmms_charset_to_utf8(text);
++ else if (encoding == ID3_ENCODING_UTF16) ctext = xmms_charset_convert(text, strlen(text), NULL, "UTF-16");
++ else if (encoding == ID3_ENCODING_UTF16BE) ctext = xmms_charset_convert(text, strlen(text), NULL, "UTF-16BE");
++ else ctext = NULL;
++
++ if (ctext) text = ctext;
++
+ /*
+ * Allocate memory for new data.
+ */
++ if ((encoding == ID3_ENCODING_UTF16)||(encoding == ID3_ENCODING_UTF16BE))
++ frame->fr_raw_size = utf16_strlen(text) + 2;
++ else
+ frame->fr_raw_size = strlen(text) + 1;
+ frame->fr_raw_data = g_malloc(frame->fr_raw_size + 1);
+
+ /*
+ * Copy contents.
+ */
+- *(gint8 *) frame->fr_raw_data = ID3_ENCODING_ISO_8859_1;
++ *(gint8 *) frame->fr_raw_data = encoding;
+ memcpy((char *) frame->fr_raw_data + 1, text, frame->fr_raw_size);
+
++ if (ctext) free(ctext);
++
+ frame->fr_altered = 1;
+ frame->fr_owner->id3_altered = 1;
+
+@@ -389,7 +411,10 @@
+ */
+ int id3_set_comment(struct id3_frame *frame, char *text)
+ {
++ char *ctext, *cdata;
++ const char *lang;
+ int *intp;
++ gint8 encoding;
+
+ /* Type check */
+ if (frame->fr_desc->fd_id != ID3_COMM)
+@@ -401,8 +426,23 @@
+ id3_frame_clear_data(frame);
+
+ /*
++ * Recoding.
++ */
++ encoding = (gint8)xmms_rcc_get_id3v2_encoding();
++ if (encoding == ID3_ENCODING_ISO_8859_1) ctext = xmms_rcc_recode(XMMS_RCC_CTYPE, XMMS_RCC_ID3V2, text);
++ else if (encoding == ID3_ENCODING_UTF8) ctext = xmms_charset_to_utf8(text);
++ else if (encoding == ID3_ENCODING_UTF16) ctext = xmms_charset_convert(text, strlen(text), NULL, "UTF-16");
++ else if (encoding == ID3_ENCODING_UTF16BE) ctext = xmms_charset_convert(text, strlen(text), NULL, "UTF-16BE");
++ else ctext = NULL;
++
++ if (ctext) text = ctext;
++
++ /*
+ * Allocate memory for new data.
+ */
++ if ((encoding == ID3_ENCODING_UTF16)||(encoding == ID3_ENCODING_UTF16BE))
++ frame->fr_raw_size = 25 + utf16_strlen(text);
++ else
+ frame->fr_raw_size = 13 + strlen(text);
+ frame->fr_raw_data = g_malloc(frame->fr_raw_size + 1); /* <encode>XXXComments\0<comment><\0>
+
+@@ -410,18 +450,29 @@
+ * block, so don't waste time with a calloc()
+ */
+
+- ((guint8 *)frame->fr_raw_data)[0] = ID3_ENCODING_ISO_8859_1;
+- ((guint8 *)frame->fr_raw_data)[1] = 0x58;
+- ((guint8 *)frame->fr_raw_data)[2] = 0x58;
+- ((guint8 *)frame->fr_raw_data)[3] = 0x58;
++ *(gint8 *) frame->fr_raw_data = encoding;
++ memcpy((char*)frame->fr_raw_data + 1, xmms_rcc_get_language(), 3);
+
++ if ((encoding == ID3_ENCODING_UTF16)||(encoding == ID3_ENCODING_UTF16BE)) {
++ int i;
++ lang = "Comments";
++ if (encoding == ID3_ENCODING_UTF16) cdata = xmms_charset_convert(lang, strlen(lang), NULL, "UTF-16");
++ else cdata = xmms_charset_convert(lang, strlen(lang), NULL, "UTF-16BE");
++ memcpy((char *) frame->fr_raw_data + 4, cdata, 20);
++ g_free(cdata);
++ } else
+ memcpy((char *) frame->fr_raw_data + 4, "Comments", 9);
+
+ /*
+ * Copy contents.
+ */
++ if ((encoding == ID3_ENCODING_UTF16)||(encoding == ID3_ENCODING_UTF16BE))
++ memcpy((char *) frame->fr_raw_data + 24, text, utf16_strlen(text) + 2);
++ else
+ memcpy((char *) frame->fr_raw_data + 13, text, strlen(text) + 1);
+
++ if (ctext) free(ctext);
++
+ frame->fr_altered = 1;
+ frame->fr_owner->id3_altered = 1;
+
diff --git a/patches/xmms/plugins/xmms-ds-mpg123.patch b/patches/xmms/plugins/xmms-ds-mpg123.patch
new file mode 100644
index 0000000..fc9c42e
--- /dev/null
+++ b/patches/xmms/plugins/xmms-ds-mpg123.patch
@@ -0,0 +1,20 @@
+diff -dPNur xmms-1.2.11/Input/mpg123/mpg123.c xmms-1.2.11-new/Input/mpg123/mpg123.c
+--- xmms-1.2.11/Input/mpg123/mpg123.c 2007-11-25 00:04:27.000000000 +0100
++++ xmms-1.2.11-new/Input/mpg123/mpg123.c 2007-11-25 01:18:57.000000000 +0100
+@@ -3,6 +3,7 @@
+ #include "libxmms/configfile.h"
+ #include "libxmms/titlestring.h"
+ #include "libxmms/charset.h"
++#include "libxmms/rcc.h"
+ #include <string.h>
+ #include <stdlib.h>
+ #include <pthread.h>
+@@ -496,6 +497,8 @@
+ /*
+ * Format according to filename.
+ */
++ ret = xmms_rcc_recode(XMMS_RCC_FS, XMMS_RCC_CTYPE, g_basename(filename));
++ if (!ret)
+ ret = g_strdup(g_basename(filename));
+ if (extname(ret) != NULL)
+ *(extname(ret) - 1) = '\0'; /* removes period */
diff --git a/patches/xmms/plugins/xmms-ds-vorbis-editor-keys.patch b/patches/xmms/plugins/xmms-ds-vorbis-editor-keys.patch
new file mode 100644
index 0000000..bf59951
--- /dev/null
+++ b/patches/xmms/plugins/xmms-ds-vorbis-editor-keys.patch
@@ -0,0 +1,78 @@
+diff -dPNur xmms-1.2.11/Input/vorbis/fileinfo.c xmms-1.2.11-new/Input/vorbis/fileinfo.c
+--- xmms-1.2.11/Input/vorbis/fileinfo.c 2007-11-25 01:23:31.000000000 +0100
++++ xmms-1.2.11-new/Input/vorbis/fileinfo.c 2007-11-25 01:24:01.000000000 +0100
+@@ -39,6 +39,9 @@
+ #include "libxmms/charset.h"
+ #include <xmms/i18n.h>
+
++#include "../../xmms/xmms.h"
++#include <gdk/gdkkeysyms.h>
++
+ #include "vorbis.h"
+ #include "vcedit.h"
+
+@@ -444,6 +447,28 @@
+ }
+
+ /***********************************************************************/
++static int restore_focus;
++
++gboolean vorbis_keypress_cb(GtkWidget * w, GdkEventKey * event, gpointer save) {
++ switch(event->keyval) {
++ case GDK_Return:
++ restore_focus=1;
++ gtk_signal_emit_by_name(GTK_OBJECT(save), "clicked", NULL);
++ break;
++ case GDK_Escape:
++ restore_focus=1;
++ break;
++ }
++ return TRUE;
++}
++
++void gtk_widget_destroyed_focus(GtkWidget *widget, GtkWidget **widget_pointer) {
++ gtk_widget_destroyed(widget,widget_pointer);
++ if (restore_focus) {
++ gtk_widget_hide(playlistwin);
++ gtk_widget_show(playlistwin);
++ }
++}
+
+ void vorbis_file_info_box(char *fn)
+ {
+@@ -465,6 +490,8 @@
+
+ g_free(vte.filename);
+ vte.filename = g_strdup(fn);
++
++ restore_focus = 0;
+
+ if (!window)
+ {
+@@ -472,11 +499,12 @@
+ GtkWidget *hbox, *label, *filename_hbox, *vbox, *left_vbox;
+ GtkWidget *table, *bbox, *cancel_button;
+ GtkWidget *save_button, *remove_button;
++ GtkAccelGroup *ag;
+
+ window = gtk_window_new(GTK_WINDOW_DIALOG);
+ gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE);
+ gtk_signal_connect(GTK_OBJECT(window), "destroy",
+- GTK_SIGNAL_FUNC(gtk_widget_destroyed), &window);
++ GTK_SIGNAL_FUNC(gtk_widget_destroyed_focus), &window);
+ gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
+ keypress_cb, NULL);
+ gtk_container_set_border_width(GTK_CONTAINER(window), 10);
+@@ -809,6 +837,12 @@
+ FALSE, 0);
+
+ gtk_widget_show_all(window);
++
++ ag = gtk_accel_group_new();
++ gtk_accel_group_add(ag, GDK_Escape, 0, 0, GTK_OBJECT(cancel_button), "clicked");
++ gtk_accel_group_add(ag, GDK_Return, 0, 0, GTK_OBJECT(save_button), "clicked");
++ gtk_window_add_accel_group(GTK_WINDOW(window), ag);
++ gtk_signal_connect(GTK_OBJECT(window),"key_press_event",GTK_SIGNAL_FUNC(vorbis_keypress_cb),save_button);
+ } else
+ gdk_window_raise(window->window);
+
diff --git a/patches/xmms/plugins/xmms-ds-vorbis-editor.patch b/patches/xmms/plugins/xmms-ds-vorbis-editor.patch
new file mode 100644
index 0000000..47a7ba9
--- /dev/null
+++ b/patches/xmms/plugins/xmms-ds-vorbis-editor.patch
@@ -0,0 +1,51 @@
+diff -dPNur xmms-1.2.11/Input/vorbis/fileinfo.c xmms-1.2.11-new/Input/vorbis/fileinfo.c
+--- xmms-1.2.11/Input/vorbis/fileinfo.c 2007-11-25 00:04:27.000000000 +0100
++++ xmms-1.2.11-new/Input/vorbis/fileinfo.c 2007-11-25 01:20:57.000000000 +0100
+@@ -34,6 +34,7 @@
+ #include <vorbis/codec.h>
+ #include <vorbis/vorbisfile.h>
+
++#include "libxmms/rcc.h"
+ #include "libxmms/util.h"
+ #include "libxmms/charset.h"
+ #include <xmms/i18n.h>
+@@ -451,6 +452,7 @@
+ char *description, *version, *isrc, *copyright, *organization;
+ char *location, *vendor = "N/A";
+ char *rg_track_gain, *rg_album_gain, *rg_track_peak, *rg_album_peak;
++ gchar *cfilename;
+
+ int time, minutes, seconds, bitrate, avgbitrate, rate, channels;
+ int filesize, i;
+@@ -931,6 +933,11 @@
+ gtk_entry_set_text(GTK_ENTRY(isrc_entry), isrc);
+ gtk_entry_set_text(GTK_ENTRY(location_entry), location);
+ #endif
++ cfilename = xmms_rcc_recode(XMMS_RCC_FS, XMMS_RCC_CTYPE, vte.filename);
++ if (cfilename) {
++ gtk_entry_set_text(GTK_ENTRY(filename_entry), cfilename);
++ g_free(cfilename);
++ } else
+ gtk_entry_set_text(GTK_ENTRY(filename_entry), vte.filename);
+ gtk_editable_set_position(GTK_EDITABLE(filename_entry), -1);
+
+diff -dPNur xmms-1.2.11/Input/vorbis/vorbis.c xmms-1.2.11-new/Input/vorbis/vorbis.c
+--- xmms-1.2.11/Input/vorbis/vorbis.c 2007-11-25 00:04:27.000000000 +0100
++++ xmms-1.2.11-new/Input/vorbis/vorbis.c 2007-11-25 01:20:57.000000000 +0100
+@@ -46,6 +46,7 @@
+ #include "xmms/plugin.h"
+ #include "libxmms/util.h"
+ #include "libxmms/configfile.h"
++#include "libxmms/rcc.h"
+ #include "libxmms/titlestring.h"
+ #include "libxmms/charset.h"
+ #include <xmms/i18n.h>
+@@ -754,6 +755,8 @@
+ if (!vorbis_is_streaming)
+ {
+ char *tmp;
++ displaytitle = xmms_rcc_recode(XMMS_RCC_FS, XMMS_RCC_OUT, g_basename(fn));
++ if (!displaytitle)
+ displaytitle = g_strdup(g_basename(fn));
+ if ((tmp = strrchr(displaytitle, '.')) != NULL)
+ *tmp = '\0';
diff --git a/patches/xmms/xmms-ds-playlist.patch b/patches/xmms/xmms-ds-playlist.patch
new file mode 100644
index 0000000..b72c8d0
--- /dev/null
+++ b/patches/xmms/xmms-ds-playlist.patch
@@ -0,0 +1,526 @@
+diff -dPNur xmms-1.2.11/xmms/input.c xmms-1.2.11-new/xmms/input.c
+--- xmms-1.2.11/xmms/input.c 2005-05-15 02:01:21.000000000 +0200
++++ xmms-1.2.11-new/xmms/input.c 2008-10-27 18:14:08.000000000 +0100
+@@ -22,6 +22,7 @@
+ #include "libxmms/titlestring.h"
+ #include "libxmms/util.h"
+ #include "libxmms/xentry.h"
++#include "libxmms/rcc.h"
+
+ static pthread_mutex_t vis_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+@@ -372,8 +373,12 @@
+
+ (*title) = xmms_get_titlestring(xmms_get_gentitle_format(),
+ input);
+- if ( (*title) == NULL )
++ if ( (*title) == NULL ) {
++ (*title) = xmms_rcc_recode(XMMS_RCC_FS, XMMS_RCC_CTYPE, input->file_name);
++ if (!*title)
+ (*title) = g_strdup(input->file_name);
++ }
++
+ (*length) = -1;
+ g_free(temp);
+ g_free(input);
+diff -dPNur xmms-1.2.11/xmms/main.c xmms-1.2.11-new/xmms/main.c
+--- xmms-1.2.11/xmms/main.c 2006-07-16 15:40:04.000000000 +0200
++++ xmms-1.2.11-new/xmms/main.c 2008-10-27 18:14:08.000000000 +0100
+@@ -36,6 +36,7 @@
+ #include "libxmms/xmmsctrl.h"
+ #include "libxmms/util.h"
+ #include "libxmms/dirbrowser.h"
++#include "libxmms/rcc.h"
+ #include "xmms_mini.xpm"
+
+ #define RANDTABLE_SIZE 128
+@@ -899,6 +900,7 @@
+ gtk_timeout_remove(mainwin_timeout_tag);
+ util_set_cursor(NULL);
+ save_config();
++ xmms_rcc_free();
+ cleanup_ctrlsocket();
+ playlist_stop_get_info_thread();
+ playlist_clear();
+@@ -1036,8 +1038,14 @@
+
+ void mainwin_lock_info_text(char *text)
+ {
++ gchar *ctext;
++ ctext = xmms_rcc_recode(XMMS_RCC_CTYPE, XMMS_RCC_OUT, text);
++ if (ctext) text=ctext;
++
+ mainwin_info_text_locked = TRUE;
+ textbox_set_text(mainwin_info, text);
++
++ if (ctext) g_free(ctext);
+ }
+
+ void mainwin_release_info_text(void)
+@@ -1695,9 +1703,10 @@
+ int match = 0;
+ char *title, *filename;
+
+- title = ((PlaylistEntry *) playlist->data)->title;
+- filename = ((PlaylistEntry *) playlist->data)->filename;
+-
++ title = playlist_check_entrytitle((PlaylistEntry *) playlist->data);
++ filename = xmms_rcc_get(XMMS_RCC_OUT, ((PlaylistEntry *) playlist->data)->fnstring);
++ if (!filename) filename = g_strdup(((PlaylistEntry *) playlist->data)->filename);
++
+ if (title)
+ desc_buf[1] = title;
+ else if (strchr(filename, '/'))
+@@ -1749,7 +1758,7 @@
+ */
+ match = mainwin_jump_to_file_match(song, words, nw);
+ }
+-
++
+ if (match)
+ {
+ int row, queue_pos, *data_buf;
+@@ -1772,6 +1781,10 @@
+ row_to_select = row;
+ }
+
++ g_free(filename);
++ g_free(title);
++
++
+ songnr++;
+ playlist = playlist->next;
+ }
+@@ -2072,8 +2085,10 @@
+ int row, *data_buf;
+ char *title, *filename, *tmp_buf, *desc_buf[2];
+
+- title = ((PlaylistEntry *) queue->data)->title;
+- filename = ((PlaylistEntry *) queue->data)->filename;
++ title = playlist_check_entrytitle((PlaylistEntry *) queue->data);
++ filename = xmms_rcc_get(XMMS_RCC_OUT, ((PlaylistEntry *) queue->data)->fnstring);
++ if (!filename) filename = g_strdup(((PlaylistEntry *) queue->data)->filename);
++
+ if (title)
+ desc_buf[1] = title;
+ else if (strchr(filename, '/'))
+@@ -2086,6 +2101,9 @@
+ row = gtk_clist_append(GTK_CLIST(qlist), desc_buf);
+ g_free(tmp_buf);
+
++ g_free(filename);
++ g_free(title);
++
+ data_buf = g_malloc(sizeof (int));
+ *data_buf = pos;
+ gtk_clist_set_row_data_full(qlist, row, data_buf, g_free);
+@@ -4217,6 +4235,7 @@
+ #endif
+
+ read_config();
++ xmms_rcc_init();
+
+ #if defined(HAVE_SCHED_SETSCHEDULER) && defined(HAVE_SCHED_GET_PRIORITY_MAX)
+ if (cfg.use_realtime)
+diff -dPNur xmms-1.2.11/xmms/playlist.c xmms-1.2.11-new/xmms/playlist.c
+--- xmms-1.2.11/xmms/playlist.c 2007-11-16 22:51:30.000000000 +0100
++++ xmms-1.2.11-new/xmms/playlist.c 2008-10-27 18:14:31.000000000 +0100
+@@ -20,6 +20,8 @@
+ #include "xmms.h"
+ #include <time.h>
+ #include "libxmms/util.h"
++#include <locale.h>
++#include "libxmms/rcc.h"
+ #include <sys/stat.h>
+ #include <unistd.h>
+
+@@ -91,6 +93,8 @@
+ entry = node->data;
+ if (entry->filename)
+ g_free(entry->filename);
++ if (entry->fnstring)
++ g_free(entry->fnstring);
+ if (entry->title)
+ g_free(entry->title);
+ g_free(entry);
+@@ -166,6 +170,8 @@
+
+ if (entry->filename)
+ g_free(entry->filename);
++ if (entry->fnstring)
++ g_free(entry->fnstring);
+ if (entry->title)
+ g_free(entry->title);
+ shuffle_list = g_list_remove(shuffle_list, entry);
+@@ -280,9 +286,17 @@
+ PlaylistEntry *entry;
+
+ entry = g_malloc0(sizeof (PlaylistEntry));
++
+ entry->filename = g_strdup(filename);
++ entry->fnstring = xmms_rcc_put(XMMS_RCC_FS, filename);
++ if (!entry->fnstring) entry->fnstring = g_strdup(filename);
++
+ if (title)
++ {
++ entry->title = xmms_rcc_put(XMMS_RCC_CTYPE, title);
++ if (!entry->title)
+ entry->title = g_strdup(title);
++ }
+ entry->length = len;
+
+ PL_LOCK();
+@@ -624,6 +638,8 @@
+ if (playlist_position)
+ {
+ g_free(playlist_position->title);
++ playlist_position->title = xmms_rcc_put(XMMS_RCC_CTYPE, title);
++ if (!playlist_position->title)
+ playlist_position->title = g_strdup(title);
+ playlist_position->length = length;
+ }
+@@ -1015,6 +1031,7 @@
+ char *playlist_get_info_text(void)
+ {
+ char *text, *title, *tmp, *numbers, *length;
++ char *ctitle;
+
+ PL_LOCK();
+ if (!playlist_position)
+@@ -1023,10 +1040,20 @@
+ return NULL;
+ }
+
+- if (playlist_position->title)
++ if (playlist_position->title) {
++ ctitle = xmms_rcc_get(XMMS_RCC_OUT, playlist_position->title);
++ if (ctitle) title = ctitle;
++ else
+ title = playlist_position->title;
+- else
++ } else {
++ ctitle = xmms_rcc_get(XMMS_RCC_OUT, playlist_position->fnstring);
++ if (ctitle) {
++ title = g_strdup(g_basename(ctitle));
++ g_free(ctitle);
++ ctitle = title;
++ } else
+ title = g_basename(playlist_position->filename);
++ }
+
+ /*
+ * If the user don't want numbers in the playlist, don't
+@@ -1048,6 +1075,7 @@
+ text = g_strdup_printf("%s%s%s", numbers, title, length);
+ g_free(numbers);
+ g_free(length);
++ if (ctitle) g_free(ctitle);
+
+ PL_UNLOCK();
+
+@@ -1083,6 +1111,7 @@
+ {
+ GList *node;
+ FILE *file;
++ gchar *ctitle, *cfn;
+
+ if ((file = fopen(filename, "w")) == NULL)
+ return FALSE;
+@@ -1101,10 +1130,12 @@
+ while (node)
+ {
+ PlaylistEntry *entry = node->data;
++ if (!strstr(entry->filename,"://")) cfn = xmms_rcc_fs2pl(entry->fnstring, entry->filename);
++ else cfn=NULL;
+ if (is_pls)
+ fprintf(file, "File%d=%s\n",
+ g_list_position(playlist, node) + 1,
+- entry->filename);
++ cfn?cfn:entry->filename);
+ else
+ {
+ if (entry->title && cfg.use_pl_metadata)
+@@ -1116,11 +1147,14 @@
+ else
+ seconds = -1;
+
++ ctitle = xmms_rcc_get(XMMS_RCC_PL, entry->title);
+ fprintf(file, "#EXTINF:%d,%s\n",
+- seconds, entry->title);
++ seconds, ctitle?ctitle:entry->title);
++ if (ctitle) g_free(ctitle);
+ }
+- fprintf(file, "%s\n", entry->filename);
++ fprintf(file, "%s\n", cfn?cfn:entry->filename);
+ }
++ if (cfn) g_free(cfn);
+ node = g_list_next(node);
+ }
+ PL_UNLOCK();
+@@ -1151,16 +1185,32 @@
+ *temp = '\0';
+ else
+ {
++ if ((!strstr(filename,"://"))&&(!strstr(playlist_name, "://"))) {
++ temp = xmms_rcc_fs(XMMS_RCC_PLFS, XMMS_RCC_FS, NULL, NULL, filename);
++ if (temp) filename = temp;
++ } else temp = NULL;
+ __playlist_ins_with_info(filename, pos, title, len);
++ if (temp) g_free(temp);
+ return;
+ }
++ if ((!strstr(filename,"://"))&&(!strstr(playlist_name, "://"))) {
++ temp = xmms_rcc_fs(XMMS_RCC_PLFS, XMMS_RCC_FS, path, path, filename);
++ if (temp) filename = temp;
++ } else temp = NULL;
++ if (!temp)
+ temp = g_strdup_printf("%s/%s", path, filename);
+ __playlist_ins_with_info(temp, pos, title, len);
+ g_free(temp);
+ g_free(path);
+ }
+- else
++ else {
++ if ((filename[0] == '/')&&(!strstr(playlist_name, "://"))) {
++ temp = xmms_rcc_fs(XMMS_RCC_PLFS, XMMS_RCC_FS, NULL, NULL, filename);
++ if (temp) filename = temp;
++ } else temp = NULL;
+ __playlist_ins_with_info(filename, pos, title, len);
++ if (temp) g_free(temp);
++ }
+ }
+
+ static void parse_extm3u_info(char *info, char **title, int *length)
+@@ -1182,6 +1232,10 @@
+ *length *= 1000;
+ if ((str = strchr(info, ',')) != NULL)
+ {
++ if (*str)
++ while ((*(str+1)==' ')||(*(str+1)=='\t')) str++; /* g_strstrip removes leading and ending whitespaces */
++ *title = xmms_rcc_put(XMMS_RCC_PL, str + 1);
++ if (!*title)
+ *title = g_strdup(str + 1);
+ g_strstrip(*title);
+ if (strlen(*title) < 1)
+@@ -1210,7 +1264,7 @@
+ line = read_ini_string(filename, "playlist", "NumberOfEntries");
+ if (line == NULL)
+ return 0;
+-
++
+ noe = atoi(line);
+ g_free(line);
+
+@@ -1372,9 +1426,27 @@
+ return ret;
+ }
+
++gchar* playlist_check_entrytitle(PlaylistEntry *entry) {
++ gchar *title, *fn;
++
++ if (!entry) return NULL;
++
++ title = xmms_rcc_get(XMMS_RCC_OUT, entry->title);
++ if (!title) {
++ fn = xmms_rcc_get(XMMS_RCC_OUT, entry->fnstring);
++ if (fn) {
++ title = g_strdup(g_basename(fn));
++ g_free(fn);
++ } else title = g_strdup(g_basename(entry->filename));
++ }
++
++ return title;
++}
++
+ char * playlist_get_songtitle(int pos)
+ {
+ char *title = NULL, *filename;
++ char *ctitle;
+ PlaylistEntry *entry;
+ GList *node;
+
+@@ -1396,19 +1468,30 @@
+
+ if (entry->title == NULL && entry->length == -1)
+ {
+- if (playlist_get_info_entry(entry))
++ if (playlist_get_info_entry(entry)&&(entry->title)) {
++ title = xmms_rcc_get(XMMS_RCC_OUT, entry->title);
++ if (!title)
+ title = g_strdup(entry->title);
++ }
+
+ PL_UNLOCK();
+ }
+ else
+ {
++ title = xmms_rcc_get(XMMS_RCC_OUT, entry->title);
++ if (!title)
+ title = g_strdup(entry->title);
+ PL_UNLOCK();
+ }
+
+- if (title == NULL)
++ if (title == NULL) {
++ ctitle = xmms_rcc_get(XMMS_RCC_OUT, entry->filename);
++ if (ctitle) {
++ title = g_strdup(g_basename(ctitle));
++ g_free(ctitle);
++ } else
+ title = g_strdup(g_basename(filename));
++ }
+
+ g_free(filename);
+
+@@ -1453,36 +1536,41 @@
+
+ static int playlist_sort_by_title_cmpfunc(PlaylistEntry * a, PlaylistEntry * b)
+ {
+- char *a_title, *b_title;
++ const char *a_title, *b_title;
+
+ if (a->title)
+- a_title = a->title;
++ a_title = xmms_rcc_string(a->title);
+ else
+ {
+- if (strrchr(a->filename, '/'))
+- a_title = strrchr(a->filename, '/') + 1;
++ if (strrchr(xmms_rcc_string(a->fnstring), '/'))
++ a_title = strrchr(xmms_rcc_string(a->fnstring), '/') + 1;
+ else
+- a_title = a->filename;
++ a_title = xmms_rcc_string(a->filename);
+ }
+
+ if (b->title)
+- b_title = b->title;
++ b_title = xmms_rcc_string(b->title);
+ else
+ {
+- if (strrchr(a->filename, '/'))
+- b_title = strrchr(b->filename, '/') + 1;
++ if (strrchr(xmms_rcc_string(a->fnstring), '/'))
++ b_title = strrchr(xmms_rcc_string(b->fnstring), '/') + 1;
+ else
+- b_title = b->filename;
+-
++ b_title = xmms_rcc_string(b->filename);
+ }
+- return strcasecmp(a_title, b_title);
++ return strcoll(a_title, b_title);
+ }
+
+ void playlist_sort_by_title(void)
+ {
++ char *locale;
++ locale = setlocale(LC_COLLATE,"en_US.UTF-8");
++ playlist_select_all(1);
++ playlist_read_info_selection();
++ playlist_select_all(0);
+ PL_LOCK();
+ playlist = g_list_sort(playlist, (GCompareFunc) playlist_sort_by_title_cmpfunc);
+ PL_UNLOCK();
++ setlocale(LC_COLLATE,locale);
+ }
+
+ static int playlist_sort_by_filename_cmpfunc(PlaylistEntry * a, PlaylistEntry * b)
+@@ -1640,9 +1728,13 @@
+
+ void playlist_sort_selected_by_title(void)
+ {
++ char *locale;
++ locale = setlocale(LC_COLLATE,"en_US.UTF-8");
++ playlist_read_info_selection();
+ PL_LOCK();
+ playlist = playlist_sort_selected(playlist, (GCompareFunc) playlist_sort_by_title_cmpfunc);
+ PL_UNLOCK();
++ setlocale(LC_COLLATE,locale);
+ }
+
+ void playlist_sort_selected_by_filename(void)
+@@ -1859,6 +1951,8 @@
+ return FALSE;
+
+ /* entry is still around */
++ entry->title = xmms_rcc_put(XMMS_RCC_CTYPE, temp_title);
++ if (!entry->title)
+ entry->title = temp_title;
+ entry->length = temp_length;
+
+diff -dPNur xmms-1.2.11/xmms/playlist.h xmms-1.2.11-new/xmms/playlist.h
+--- xmms-1.2.11/xmms/playlist.h 2007-11-16 22:51:30.000000000 +0100
++++ xmms-1.2.11-new/xmms/playlist.h 2008-10-27 18:14:08.000000000 +0100
+@@ -23,6 +23,7 @@
+ typedef struct
+ {
+ gchar *filename;
++ gchar *fnstring;
+ gchar *title;
+ gint length;
+ gboolean selected;
+@@ -86,6 +87,7 @@
+ void playlist_fileinfo(gint pos);
+ void playlist_delete_index(glong index);
+ void playlist_delete_filenames(GList *filenames);
++gchar* playlist_check_entrytitle(PlaylistEntry *entry);
+ gchar* playlist_get_filename(gint pos);
+ gchar* playlist_get_songtitle(gint pos);
+ gint playlist_get_songtime(gint pos);
+diff -dPNur xmms-1.2.11/xmms/playlist_list.c xmms-1.2.11-new/xmms/playlist_list.c
+--- xmms-1.2.11/xmms/playlist_list.c 2003-06-11 20:44:17.000000000 +0200
++++ xmms-1.2.11-new/xmms/playlist_list.c 2008-10-27 18:14:08.000000000 +0100
+@@ -451,10 +451,8 @@
+ else
+ gdk_gc_set_foreground(gc, get_skin_color(SKIN_PLEDIT_NORMAL));
+
+- if (entry->title)
+- title = entry->title;
+- else
+- title = g_basename(entry->filename);
++
++ title = playlist_check_entrytitle(entry);
+
+ pos = playlist_get_queue_position(entry);
+
+@@ -497,6 +495,8 @@
+ playlist_list_draw_string(pl, playlist_list_font,
+ i - pl->pl_first, tw, text);
+ g_free(text);
++
++ g_free(title);
+ }
+ PL_UNLOCK();
+ }
+diff -dPNur xmms-1.2.11/xmms/prefswin.c xmms-1.2.11-new/xmms/prefswin.c
+--- xmms-1.2.11/xmms/prefswin.c 2007-11-16 22:51:30.000000000 +0100
++++ xmms-1.2.11-new/xmms/prefswin.c 2008-10-27 18:14:08.000000000 +0100
+@@ -18,6 +18,7 @@
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+ #include "xmms.h"
++#include "libxmms/rcc.h"
+ #include "libxmms/util.h"
+ #include "libxmms/titlestring.h"
+
+@@ -397,6 +398,8 @@
+ draw_playlist_window(TRUE);
+ draw_equalizer_window(TRUE);
+
++ xmms_rcc_prefswin_apply();
++
+ save_config();
+ }
+
+@@ -1105,6 +1108,11 @@
+ gtk_notebook_append_page(GTK_NOTEBOOK(prefswin_notebook), prefswin_fonts_vbox, gtk_label_new(_("Fonts")));
+
+ /*
++ * Coding Conversion page
++ */
++ xmms_rcc_prefswin_create(prefswin_notebook);
++
++ /*
+ * Title page
+ */
+ prefswin_title_vbox = gtk_vbox_new(FALSE, 0);
diff --git a/patches/xmms/xmms-ds-rusxmms-charset.patch b/patches/xmms/xmms-ds-rusxmms-charset.patch
new file mode 100644
index 0000000..670f9e3
--- /dev/null
+++ b/patches/xmms/xmms-ds-rusxmms-charset.patch
@@ -0,0 +1,87 @@
+diff -dPNur xmms-1.2.10/libxmms/charset.c xmms-1.2.10-new/libxmms/charset.c
+--- xmms-1.2.10/libxmms/charset.c 2005-08-10 03:20:03.000000000 +0200
++++ xmms-1.2.10-new/libxmms/charset.c 2005-08-10 02:54:12.000000000 +0200
+@@ -20,6 +20,7 @@
+ #include <langinfo.h>
+ #endif
+
++#include "rcc.h"
+ #include "charset.h"
+
+ char* xmms_charset_get_current(void)
+@@ -37,7 +38,7 @@
+ return charset;
+ }
+
+-static size_t utf16_strlen(const char *string)
++size_t utf16_strlen(const char *string)
+ {
+ size_t len = 0;
+
+@@ -78,7 +79,7 @@
+ /* + 1 for nul in case len == 1 */
+ outsize = ((insize + 3) & ~3) + 1;
+ out = g_malloc(outsize);
+- outleft = outsize - 1;
++ outleft = outsize - 2;
+ outptr = out;
+
+ retry:
+@@ -92,7 +93,7 @@
+ outsize = (outsize - 1) * 2 + 1;
+ out = g_realloc(out, outsize);
+ outptr = out + used;
+- outleft = outsize - 1 - used;
++ outleft = outsize - 2 - used;
+ goto retry;
+ case EINVAL:
+ break;
+@@ -110,6 +111,7 @@
+ }
+ }
+ *outptr = '\0';
++ *(outptr+1) = '\0';
+
+ iconv_close(cd);
+ return out;
+@@ -141,10 +143,14 @@
+
+ char* xmms_charset_from_latin1(const char *string)
+ {
++ char *cstring;
+ char *to = xmms_charset_get_current();
+
+ if (!string)
+ return NULL;
++
++ cstring = xmms_rcc_recode(XMMS_RCC_ID3, XMMS_RCC_CTYPE, string);
++ if (cstring) return cstring;
+
+ if (!strcmp(to, "UTF-8"))
+ return xmms_charset_convert(string, strlen(string), "ISO-8859-1", to);
+@@ -271,9 +277,14 @@
+
+ char* xmms_charset_from_latin1(const char *string)
+ {
++ char *cstring;
++
+ if (!string)
+ return NULL;
+
++ cstring = xmms_rcc_recode(XMMS_RCC_ID3, XMMS_RCC_CTYPE, string);
++ if (cstring) return cstring;
++
+ return g_strdup(string);
+ }
+
+diff -dPNur xmms-1.2.10/libxmms/charset.h xmms-1.2.10-new/libxmms/charset.h
+--- xmms-1.2.10/libxmms/charset.h 2005-08-10 03:20:03.000000000 +0200
++++ xmms-1.2.10-new/libxmms/charset.h 2005-08-10 01:41:01.000000000 +0200
+@@ -16,5 +16,7 @@
+ char* xmms_charset_from_utf16be(const unsigned char *string);
+ char* xmms_charset_from_latin1(const char *string);
+
++size_t utf16_strlen(const char *string);
++
+ #endif /* XMMS_CHARSET_H */
+
diff --git a/patches/xmms/xmms-ds-rusxmms.patch b/patches/xmms/xmms-ds-rusxmms.patch
new file mode 100644
index 0000000..6b9287f
--- /dev/null
+++ b/patches/xmms/xmms-ds-rusxmms.patch
@@ -0,0 +1,172 @@
+diff -dPNur xmms-1.2.11/libxmms/configure.in xmms-1.2.11-new/libxmms/configure.in
+--- xmms-1.2.11/libxmms/configure.in 2007-11-16 22:51:24.000000000 +0100
++++ xmms-1.2.11-new/libxmms/configure.in 2007-11-25 17:38:56.000000000 +0100
+@@ -57,6 +57,16 @@
+ AC_DEFINE([HAVE_CODESET],,[Define if nl_langinfo(CODESET) is available.])
+ fi
+
++AC_CHECK_LIB(rccgtk, rccUiInit,[
++ AC_CHECK_HEADERS(librcc.h librccui.h,[
++ LIBRCC_LIBS="-lrccgtk"
++ LIBRCC_INCLUDES=""
++ ],[
++ LIBRCC_LIBS=""
++ LIBRCC_INCLUDES=""
++])])
++AC_SUBST(LIBRCC_LIBS)
++AC_SUBST(LIBRCC_INCLUDES)
+
+ AC_OUTPUT([
+ Makefile
+diff -dPNur xmms-1.2.11/libxmms/Makefile.am xmms-1.2.11-new/libxmms/Makefile.am
+--- xmms-1.2.11/libxmms/Makefile.am 2004-04-04 10:51:20.000000000 +0200
++++ xmms-1.2.11-new/libxmms/Makefile.am 2007-11-25 17:38:56.000000000 +0100
+@@ -4,10 +4,11 @@
+ lib_LTLIBRARIES = libxmms.la
+
+ libxmms_la_LDFLAGS = -export-dynamic -version-info $(LIBXMMS_MAJOR_VERSION):$(LIBXMMS_MINOR_VERSION):$(LIBXMMS_MICRO_VERSION)
+-libxmms_la_LIBADD = @GTK_LIBS@ @POSIX_LIBS@
+-INCLUDES = @GTK_CFLAGS@ -I../intl -I..
++libxmms_la_LIBADD = @LIBRCC_LIBS@ @GTK_LIBS@ @POSIX_LIBS@
++INCLUDES = @LIBRCC_INCLUDES@ @GTK_CFLAGS@ -I../intl -I..
+
+ libxmms_la_SOURCES = \
++rcc.c rcc.h rcc_langs.h \
+ configfile.c configfile.h \
+ xmmsctrl.c xmmsctrl.h \
+ dirbrowser.c dirbrowser.h \
+diff -dPNur xmms-1.2.11/libxmms/titlestring.c xmms-1.2.11-new/libxmms/titlestring.c
+--- xmms-1.2.11/libxmms/titlestring.c 2006-07-24 00:11:51.000000000 +0200
++++ xmms-1.2.11-new/libxmms/titlestring.c 2007-11-25 17:39:21.000000000 +0100
+@@ -29,6 +29,7 @@
+ #include <string.h>
+ #include <ctype.h>
+
++#include "rcc.h"
+ #include "titlestring.h"
+ #include "../xmms/i18n.h"
+
+@@ -42,8 +43,9 @@
+ PAD_SIDE_RIGHT,
+ };
+
++#define xmms_vputstr(out, pstr, pad) xmms_charset_vputstr(out, pstr, pad, from, pflag, c)
+
+-static int xmms_vputstr(GString *output, char *pstr, struct padding *pad)
++static int xmms_orig_vputstr(GString *output, char *pstr, struct padding *pad)
+ {
+ int i;
+ /* Lenght of the string that is actually printed */
+@@ -83,6 +85,22 @@
+ return TRUE;
+ }
+
++static int xmms_charset_vputstr(GString *output, char *pstr, struct padding *pad, int from, gint pflag, char c) {
++ int res;
++ gchar *cstring = NULL;
++
++ if ((pflag&1)&&((c=='f')||(c=='F'))) {
++ cstring = xmms_rcc_recode(XMMS_RCC_FS, XMMS_RCC_CTYPE, pstr);
++ }
++ if ((pflag&2)&&((c=='a')||(c=='c')||(c=='g')||(c=='p')||(c=='t'))) {
++ cstring = xmms_rcc_recode((xmms_rcc_class)from, XMMS_RCC_CTYPE, pstr);
++ }
++
++ res = xmms_orig_vputstr(output, cstring?cstring:pstr, pad);
++ if (cstring) g_free(cstring);
++
++ return res;
++}
+
+ static int xmms_vputnum(GString *output, int ival, struct padding *pad)
+ {
+@@ -124,7 +142,7 @@
+ return TRUE;
+ }
+
+-static int parse_variable(char **fmt, GString *string, TitleInput *input)
++static int parse_variable(char **fmt, GString *string, TitleInput *input, int from, gint pflag)
+ {
+ struct padding padding;
+ char *ptr = *fmt;
+@@ -237,9 +255,33 @@
+ return exp;
+ }
+
++static int xmms_charset_analyze_fmt(gchar *fmt) {
++ int flag = 0;
++
++ if (!fmt) return 0;
++ for (;*fmt;fmt++) {
++ while ((*fmt != '%')&&(*fmt != 0)) fmt++;
++ if (!*fmt) break;
++ while ((*fmt == '-')||(*fmt == ' ')||(*fmt == '0')) fmt++;
++ while (*fmt >= '0' && *fmt <= '9') fmt++;
++ if (*fmt == '.') { fmt++; while (*fmt >= '0' && *fmt <= '9') fmt++; }
++ if ((*fmt=='a')||(*fmt=='c')||(*fmt=='g')||(*fmt=='p')||(*fmt=='t')) flag|=1;
++ if ((*fmt=='f')||(*fmt=='F')) flag|=2;
++ }
++
++ return flag;
++}
++
++gchar *xmms_charset_get_titlestring(gchar *fmt, TitleInput *input, int from);
+
+ char *xmms_get_titlestring(char *fmt, TitleInput *input)
+ {
++ return xmms_charset_get_titlestring(fmt, input, (xmms_rcc_class)-1);
++}
++
++gchar *xmms_charset_get_titlestring(gchar *fmt, TitleInput *input, int from) {
++ gint pflag;
++ gchar *cstring = NULL;
+ int f_output = 0;
+ GString *string;
+ char *strptr;
+@@ -249,12 +291,14 @@
+
+ string = g_string_new("");
+
++ pflag = (from == (xmms_rcc_class)-1)?0:xmms_charset_analyze_fmt(fmt);
++
+ while (*fmt)
+ {
+ if (*fmt == '%')
+ {
+ fmt++;
+- f_output += parse_variable(&fmt, string, input);
++ f_output += parse_variable(&fmt, string, input, from, pflag);
+ }
+ else
+ /* Normal character */
+@@ -271,6 +315,19 @@
+ /* Return the result */
+ strptr = string->str;
+ g_string_free(string, FALSE);
++
++ if (pflag<3) {
++ if (pflag&2)
++ cstring = xmms_rcc_recode(XMMS_RCC_FS, XMMS_RCC_CTYPE, strptr);
++ else
++ cstring = xmms_rcc_recode((xmms_rcc_class)from, XMMS_RCC_CTYPE, strptr);
++
++ if (cstring) {
++ g_free(strptr);
++ return cstring;
++ }
++ }
++
+ return strptr;
+ }
+
+diff -dPNur xmms-1.2.11/libxmms/titlestring.h xmms-1.2.11-new/libxmms/titlestring.h
+--- xmms-1.2.11/libxmms/titlestring.h 2005-05-15 02:01:20.000000000 +0200
++++ xmms-1.2.11-new/libxmms/titlestring.h 2007-11-25 17:38:56.000000000 +0100
+@@ -77,6 +77,7 @@
+ #endif
+
+ gchar *xmms_get_titlestring(gchar *fmt, TitleInput *input);
++gchar *xmms_charset_get_titlestring(gchar *fmt, TitleInput *input, int from);
+ GtkWidget* xmms_titlestring_descriptions(char* tags, int rows);
+
+ #ifdef __cplusplus
diff --git a/patches/xmms/xmms-ds-shade.patch b/patches/xmms/xmms-ds-shade.patch
new file mode 100644
index 0000000..41362df
--- /dev/null
+++ b/patches/xmms/xmms-ds-shade.patch
@@ -0,0 +1,194 @@
+diff -dPNur xmms-1.2.11/xmms/main.c xmms-1.2.11-new/xmms/main.c
+--- xmms-1.2.11/xmms/main.c 2007-11-25 01:10:52.000000000 +0100
++++ xmms-1.2.11-new/xmms/main.c 2007-11-25 01:11:13.000000000 +0100
+@@ -382,6 +382,8 @@
+
+ cfg.gentitle_format = NULL;
+
++ cfg.shade_font = NULL;
++
+ filename = g_strconcat(g_get_home_dir(), "/.xmms/config", NULL);
+ cfgfile = xmms_cfg_open_file(filename);
+ if (cfgfile)
+@@ -481,6 +483,8 @@
+ }
+ xmms_cfg_read_string(cfgfile, "xmms", "generic_title_format", &cfg.gentitle_format);
+
++ xmms_cfg_read_string(cfgfile, "xmms", "shade_font", &cfg.shade_font);
++
+ xmms_cfg_free(cfgfile);
+ }
+
+@@ -519,6 +523,13 @@
+ if (cfg.eqpreset_extension == NULL)
+ cfg.eqpreset_extension = g_strdup("preset");
+
++ if (cfg.shade_font && strlen(cfg.shade_font) == 0) {
++ g_free(cfg.shade_font);
++ cfg.shade_font = NULL;
++ }
++ if (!cfg.shade_font)
++ cfg.shade_font = g_strdup("-misc-fixed-medium-r-*-*-7-*");
++
+ g_free(filename);
+ }
+
+@@ -677,6 +688,8 @@
+ }
+ xmms_cfg_write_string(cfgfile, "xmms", "generic_title_format", cfg.gentitle_format);
+
++ xmms_cfg_write_string(cfgfile, "xmms", "shade_font", cfg.shade_font);
++
+ xmms_cfg_write_file(cfgfile, filename);
+ xmms_cfg_free(cfgfile);
+
+diff -dPNur xmms-1.2.11/xmms/main.h xmms-1.2.11-new/xmms/main.h
+--- xmms-1.2.11/xmms/main.h 2006-07-16 15:40:04.000000000 +0200
++++ xmms-1.2.11-new/xmms/main.h 2007-11-25 01:12:55.000000000 +0100
+@@ -60,6 +60,7 @@
+ gint mouse_change;
+ gboolean playlist_transparent;
+ gchar *gentitle_format;
++ gchar *shade_font;
+ }
+ Config;
+
+diff -dPNur xmms-1.2.11/xmms/playlistwin.c xmms-1.2.11-new/xmms/playlistwin.c
+--- xmms-1.2.11/xmms/playlistwin.c 2007-11-16 22:51:30.000000000 +0100
++++ xmms-1.2.11-new/xmms/playlistwin.c 2007-11-25 01:11:13.000000000 +0100
+@@ -39,7 +39,8 @@
+ PButton *playlistwin_shade, *playlistwin_close;
+ static PlaylistSlider *playlistwin_slider = NULL;
+ static TextBox *playlistwin_time_min, *playlistwin_time_sec;
+-static TextBox *playlistwin_info, *playlistwin_sinfo;
++static TextBox *playlistwin_info;
++TextBox *playlistwin_sinfo;
+ static SButton *playlistwin_srew, *playlistwin_splay;
+ static SButton *playlistwin_spause, *playlistwin_sstop;
+ static SButton *playlistwin_sfwd, *playlistwin_seject;
+@@ -1882,6 +1883,7 @@
+ static void playlistwin_create_widgets(void)
+ {
+ playlistwin_sinfo = create_textbox(&playlistwin_wlist, playlistwin_bg, playlistwin_gc, 4, 4, cfg.playlist_width - 35, FALSE, SKIN_TEXT);
++ textbox_set_xfont(playlistwin_sinfo, cfg.mainwin_use_xfont, cfg.shade_font);
+ if (!cfg.playlist_shaded)
+ hide_widget(playlistwin_sinfo);
+ if (cfg.playlist_shaded)
+diff -dPNur xmms-1.2.11/xmms/prefswin.c xmms-1.2.11-new/xmms/prefswin.c
+--- xmms-1.2.11/xmms/prefswin.c 2007-11-25 01:10:52.000000000 +0100
++++ xmms-1.2.11-new/xmms/prefswin.c 2007-11-25 01:11:13.000000000 +0100
+@@ -35,6 +35,7 @@
+
+ static GtkWidget *prefswin_options_sd_entry, *prefswin_options_pbs_entry;
+
++static GtkWidget *prefswin_shade_font_entry, *prefswin_shade_font_browse;
+ static GtkWidget *prefswin_options_font_entry, *prefswin_options_font_browse;
+ static GtkWidget *prefswin_options_fontset, *prefswin_mainwin_font_entry;
+ static GtkWidget *prefswin_mainwin_xfont, *prefswin_options_mouse_spin;
+@@ -48,6 +49,7 @@
+ extern PButton *playlistwin_shade, *playlistwin_close, *equalizerwin_close;
+ extern PButton *mainwin_menubtn, *mainwin_minimize, *mainwin_shade, *mainwin_close;
+ extern TextBox *mainwin_info;
++extern TextBox *playlistwin_sinfo;
+ extern gboolean mainwin_focus, equalizerwin_focus, playlistwin_focus;
+
+ static gboolean is_opening = FALSE;
+@@ -343,11 +345,13 @@
+ gboolean show_wm_old = cfg.show_wm_decorations;
+ g_free(cfg.playlist_font);
+ g_free(cfg.mainwin_font);
++ g_free(cfg.shade_font);
+ g_free(cfg.gentitle_format);
+ prefswin_options_write_data();
+ cfg.snap_distance = CLAMP(atoi(gtk_entry_get_text(GTK_ENTRY(prefswin_options_sd_entry))), 0, 1000);
+ cfg.playlist_font = g_strdup(gtk_entry_get_text(GTK_ENTRY(prefswin_options_font_entry)));
+ cfg.mainwin_font = g_strdup(gtk_entry_get_text(GTK_ENTRY(prefswin_mainwin_font_entry)));
++ cfg.shade_font = g_strdup(gtk_entry_get_text(GTK_ENTRY(prefswin_shade_font_entry)));
+ cfg.gentitle_format = g_strdup(gtk_entry_get_text(GTK_ENTRY(prefswin_title_entry)));
+ cfg.pause_between_songs_time = CLAMP(atoi(gtk_entry_get_text(GTK_ENTRY(prefswin_options_pbs_entry))), 0, 1000);
+ cfg.mouse_change = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(prefswin_options_mouse_spin));
+@@ -390,6 +394,7 @@
+ prefswin_toggle_wm_decorations();
+
+ textbox_set_xfont(mainwin_info, cfg.mainwin_use_xfont, cfg.mainwin_font);
++ textbox_set_xfont(playlistwin_sinfo, cfg.mainwin_use_xfont, cfg.shade_font);
+ playlist_list_set_font(cfg.playlist_font);
+ playlistwin_update_list();
+ mainwin_set_info_text();
+@@ -475,6 +480,34 @@
+ }
+ }
+
++void prefswin_shade_font_browse_ok(GtkWidget * w, gpointer data)
++{
++ GtkFontSelectionDialog *fontsel = GTK_FONT_SELECTION_DIALOG(data);
++ gchar *fontname;
++
++ fontname = gtk_font_selection_dialog_get_font_name(fontsel);
++
++ if (fontname)
++ gtk_entry_set_text(GTK_ENTRY(prefswin_shade_font_entry), fontname);
++
++ gtk_widget_destroy(GTK_WIDGET(fontsel));
++}
++
++void prefswin_shade_font_browse_cb(GtkWidget * w, gpointer data)
++{
++ static GtkWidget *fontsel;
++
++ if (fontsel != NULL)
++ return;
++
++ fontsel = gtk_font_selection_dialog_new(_("Select playlist font:"));
++ gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(fontsel), gtk_entry_get_text(GTK_ENTRY(prefswin_shade_font_entry)));
++ gtk_signal_connect(GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->ok_button), "clicked", GTK_SIGNAL_FUNC(prefswin_shade_font_browse_ok), fontsel);
++ gtk_signal_connect_object(GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(fontsel));
++ gtk_signal_connect(GTK_OBJECT(fontsel), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &fontsel);
++ gtk_widget_show(fontsel);
++}
++
+ void prefswin_gplugins_use_cb(GtkToggleButton * w, gpointer data)
+ {
+ gint sel;
+@@ -693,7 +726,10 @@
+ GtkWidget *prefswin_vplugins_box, *prefswin_vplugins_vbox;
+ GtkWidget *prefswin_vplugins_frame, *prefswin_vplugins_hbox;
+ GtkWidget *prefswin_vplugins_hbbox;
+-
++
++ GtkWidget *prefswin_fonts_shade_frame;
++ GtkWidget *options_font_shade_hbox, *options_font_shade_vbox;
++
+ GtkWidget *prefswin_options_frame, *prefswin_options_vbox;
+ GtkWidget *prefswin_mainwin_frame, *prefswin_mainwin_vbox;
+ GtkWidget *prefswin_fonts_vbox, *prefswin_fonts_playlist_frame;
+@@ -1105,6 +1141,21 @@
+ gtk_widget_set_usize(prefswin_mainwin_font_browse, 85, 17);
+ gtk_box_pack_start(GTK_BOX(prefswin_mainwin_font_hbox), prefswin_mainwin_font_browse, FALSE, TRUE, 0);
+
++ prefswin_fonts_shade_frame = gtk_frame_new(_("Shade"));
++ gtk_container_set_border_width(GTK_CONTAINER(prefswin_fonts_shade_frame), 5);
++ gtk_box_pack_start(GTK_BOX(prefswin_fonts_vbox), prefswin_fonts_shade_frame, FALSE, FALSE, 0);
++ options_font_shade_vbox = gtk_vbox_new(FALSE, 5);
++ gtk_container_border_width(GTK_CONTAINER(options_font_shade_vbox), 5);
++ gtk_container_add(GTK_CONTAINER(prefswin_fonts_shade_frame), options_font_shade_vbox);
++ options_font_shade_hbox = gtk_hbox_new(FALSE, 5);
++ gtk_box_pack_start_defaults(GTK_BOX(options_font_shade_vbox), options_font_shade_hbox);
++ prefswin_shade_font_entry = gtk_entry_new();
++ gtk_box_pack_start(GTK_BOX(options_font_shade_hbox), prefswin_shade_font_entry, TRUE, TRUE, 0);
++ prefswin_shade_font_browse = gtk_button_new_with_label(_("Browse"));
++ gtk_signal_connect(GTK_OBJECT(prefswin_shade_font_browse), "clicked", GTK_SIGNAL_FUNC(prefswin_shade_font_browse_cb), NULL);
++ gtk_widget_set_usize(prefswin_shade_font_browse, 85, 17);
++ gtk_box_pack_start(GTK_BOX(options_font_shade_hbox), prefswin_shade_font_browse, FALSE, TRUE, 0);
++
+ gtk_notebook_append_page(GTK_NOTEBOOK(prefswin_notebook), prefswin_fonts_vbox, gtk_label_new(_("Fonts")));
+
+ /*
+@@ -1390,6 +1441,7 @@
+
+ gtk_entry_set_text(GTK_ENTRY(prefswin_options_font_entry), cfg.playlist_font);
+ gtk_entry_set_text(GTK_ENTRY(prefswin_mainwin_font_entry), cfg.mainwin_font);
++ gtk_entry_set_text(GTK_ENTRY(prefswin_shade_font_entry), cfg.shade_font);
+ gtk_entry_set_text(GTK_ENTRY(prefswin_title_entry), cfg.gentitle_format);
+ sprintf(temp, "%u", cfg.snap_distance);
+ gtk_entry_set_text(GTK_ENTRY(prefswin_options_sd_entry), temp);
diff --git a/patches/xmms/xmms-ds-textbox.patch b/patches/xmms/xmms-ds-textbox.patch
new file mode 100644
index 0000000..8442606
--- /dev/null
+++ b/patches/xmms/xmms-ds-textbox.patch
@@ -0,0 +1,31 @@
+diff -dPNur xmms-1.2.10/xmms/textbox.c xmms-1.2.10-patched/xmms/textbox.c
+--- xmms-1.2.10/xmms/textbox.c 2001-12-01 18:00:51.000000000 +0100
++++ xmms-1.2.10-patched/xmms/textbox.c 2005-06-02 22:43:42.000000000 +0200
+@@ -166,6 +166,8 @@
+ GdkGC *gc, *maskgc;
+ GdkColor *c, pattern;
+ GdkBitmap *mask;
++ GdkWChar *wtext;
++ int len;
+
+ length = strlen(pixmaptext);
+
+@@ -192,6 +194,18 @@
+ tb->tb_pixmap_width, tb->tb_widget.height);
+ pattern.pixel = 1;
+ gdk_gc_set_foreground(maskgc, &pattern);
++ if (cfg.use_fontsets) {
++ wtext = g_malloc((length + 1) * sizeof(GdkWChar));
++ len = gdk_mbstowcs(wtext, pixmaptext, length + 1);
++ if (len == -1) {
++ for (len = 0; pixmaptext[len] != '\0'; len++)
++ wtext[len] = pixmaptext[len];
++ }
++ wtext[len] = L'\0';
++ gdk_draw_text_wc(mask,tb->tb_font, maskgc, 0, tb->tb_font->ascent, wtext, len );
++ g_free(wtext);
++ }
++ else
+ gdk_draw_text(mask, tb->tb_font, maskgc, 0,
+ tb->tb_font->ascent, pixmaptext, length);
+ gdk_gc_unref(maskgc);
diff --git a/source/rcc.c b/source/rcc.c
new file mode 100644
index 0000000..c02f050
--- /dev/null
+++ b/source/rcc.c
@@ -0,0 +1,185 @@
+#include <stdio.h>
+#include <string.h>
+
+#include <gtk/gtklabel.h>
+#include <gtk/gtknotebook.h>
+
+#include "config.h"
+#ifdef HAVE_LIBRCC_H
+# ifdef HAVE_LIBRCCUI_H
+# define HAVE_LIBRCC
+# include <librcc.h>
+# include <librccui.h>
+# endif /* HAVE_LIBRCCUI_H */
+#endif /* HAVE_LIBRCC_H */
+
+#include "rcc.h"
+#include "rcc_langs.h"
+
+#ifdef HAVE_LIBRCC
+static rcc_context ctx = NULL;
+static rcc_ui_context uictx = NULL;
+
+static rcc_class_default_charset default_id3[] = {
+ { "ru", "CP1251" },
+ { NULL, NULL }
+};
+
+static rcc_class classes[] = {
+ { "id3", RCC_CLASS_STANDARD, NULL, default_id3, "ID3 Encoding", 0 },
+ { "id3v2", RCC_CLASS_STANDARD, "id3", default_id3, "ID3 v.2 Encoding", 0 },
+ { "pl", RCC_CLASS_STANDARD, "id3", default_id3, "PlayList Title Encoding", 0 },
+ { "plfs", RCC_CLASS_STANDARD, "pl", NULL, "PlayList File Encoding", 0 },
+ { "fs", RCC_CLASS_FS, "LC_CTYPE", NULL, "FileSystem Encoding", 0 },
+ { "out", RCC_CLASS_TRANSLATE_LOCALE, "LC_CTYPE", NULL, NULL, 0 },
+ { "ctype", RCC_CLASS_STANDARD, "LC_CTYPE", NULL, NULL, RCC_CLASS_FLAG_CONST|RCC_CLASS_FLAG_SKIP_SAVELOAD },
+ { "utf8", RCC_CLASS_STANDARD, "UTF-8", NULL, NULL, RCC_CLASS_FLAG_CONST|RCC_CLASS_FLAG_SKIP_SAVELOAD },
+ { NULL }
+};
+#endif /* HAVE_LIBRCC */
+
+
+
+void xmms_rcc_init() {
+#ifdef HAVE_LIBRCC
+ rccInit();
+ rccUiInit();
+ ctx = rccCreateContext(NULL, 0, 0, classes, 0);
+ rccInitDb4(ctx, NULL, 0);
+ rccLoad(ctx, "xmms");
+ uictx = rccUiCreateContext(ctx);
+#endif /* HAVE_LIBRCC */
+}
+
+void xmms_rcc_free() {
+#ifdef HAVE_LIBRCC
+ rccUiFreeContext(uictx);
+ rccSave(ctx, "xmms");
+ rccFreeContext(ctx);
+ rccUiFree();
+ rccFree();
+#endif /* HAVE_LIBRCC */
+}
+
+void xmms_rcc_prefswin_create(void *prefswin_notebook) {
+#ifdef HAVE_LIBRCC
+ GtkWidget *vbox;
+ vbox = (GtkWidget*)rccUiGetPage(uictx, NULL);
+ gtk_notebook_append_page(GTK_NOTEBOOK(prefswin_notebook), vbox, gtk_label_new(rccUiGetDefaultPageName()->title));
+#endif /* HAVE_LIBRCC */
+}
+
+void xmms_rcc_prefswin_apply() {
+#ifdef HAVE_LIBRCC
+ rccUiUpdate(uictx);
+ rccSave(ctx, "xmms");
+#endif /* HAVE_LIBRCC */
+}
+
+char *xmms_rcc_fs2pl(const char *fnstring, const char *filename) {
+#ifdef HAVE_LIBRCC
+ rcc_language_config config;
+
+ if (!rccStringCheck(fnstring)) return NULL;
+
+ config = rccGetConfig(ctx, rccStringGetLanguage(fnstring));
+ if (!config) return NULL;
+
+ if (rccConfigGetCurrentCharset(config, (rcc_class_id)XMMS_RCC_FS) == rccConfigGetCurrentCharset(config, (rcc_class_id)XMMS_RCC_PLFS))
+ return NULL;
+#endif /* HAVE_LIBRCC */
+
+ return xmms_rcc_get(XMMS_RCC_PLFS, fnstring);
+}
+
+
+/* rcc_string to out */
+char *xmms_rcc_get(xmms_rcc_class charset, const char *buf) {
+#ifdef HAVE_LIBRCC
+ return (char*)rccTo(ctx, (rcc_class_id)charset, (const rcc_string)buf);
+#else /* HAVE_LIBRCC */
+ return NULL;
+#endif /* HAVE_LIBRCC */
+}
+
+char *xmms_rcc_put(xmms_rcc_class charset, const char *buf) {
+#ifdef HAVE_LIBRCC
+ return (char*)rccFrom(ctx, (rcc_class_id)charset, buf);
+#else /* HAVE_LIBRCC */
+ return NULL;
+#endif /* HAVE_LIBRCC */
+}
+
+char *xmms_rcc_sized_put(xmms_rcc_class charset, const char *buf, int size) {
+#ifdef HAVE_LIBRCC
+ return (char*)rccSizedFrom(ctx, (rcc_class_id)charset, buf, size);
+#else /* HAVE_LIBRCC */
+ return NULL;
+#endif /* HAVE_LIBRCC */
+}
+
+char *xmms_rcc_recode(xmms_rcc_class from, xmms_rcc_class to, const char *buf) {
+#ifdef HAVE_LIBRCC
+ if (((from==XMMS_RCC_CTYPE)&&(to==XMMS_RCC_OUT))||((from==XMMS_RCC_OUT)&&(to==XMMS_RCC_CTYPE))) {
+ if (!rccGetSelectedCharset(ctx, XMMS_RCC_OUT)) return NULL;
+ }
+
+ return (char*)rccRecode(ctx, (rcc_class_id)from, (rcc_class_id)to, buf);
+#else /* HAVE_LIBRCC */
+ return NULL;
+#endif /* HAVE_LIBRCC */
+}
+
+char *xmms_rcc_sized_recode(xmms_rcc_class from, xmms_rcc_class to, const char *buf, int size) {
+#ifdef HAVE_LIBRCC
+ return (char*)rccSizedRecode(ctx, (rcc_class_id)from, (rcc_class_id)to, buf, size, NULL);
+#else /* HAVE_LIBRCC */
+ return NULL;
+#endif /* HAVE_LIBRCC */
+}
+
+char *xmms_rcc_fs(xmms_rcc_class from, xmms_rcc_class to, const char *fspath, const char *path, const char *filename) {
+#ifdef HAVE_LIBRCC
+ return (char*)rccFS(ctx, (rcc_class_id)from, (rcc_class_id)to, fspath, path, filename);
+#else /* HAVE_LIBRCC */
+ return NULL;
+#endif /* HAVE_LIBRCC */
+}
+
+const char *xmms_rcc_string(const char *buf) {
+#ifdef HAVE_LIBRCC
+ return rccGetString((const rcc_string)buf);
+#else /* HAVE_LIBRCC */
+ return buf;
+#endif /* HAVE_LIBRCC */
+}
+
+const char *xmms_rcc_get_language() {
+ const char *lang;
+#ifdef HAVE_LIBRCC
+ lang = rccGetCurrentLanguageName(ctx);
+#else /* HAVE_LIBRCC */
+ lang = NULL;
+#endif /* HAVE_LIBRCC */
+ return xmms_rcc_get_639_2_name(lang);
+}
+
+#define ID3_ENCODING_ISO_8859_1 0x00
+#define ID3_ENCODING_UTF16 0x01
+#define ID3_ENCODING_UTF16BE 0x02
+#define ID3_ENCODING_UTF8 0x03
+
+int xmms_rcc_get_id3v2_encoding() {
+#ifdef HAVE_LIBRCC
+ const char *name;
+
+ name = rccGetCurrentCharsetName(ctx, (rcc_class_id)XMMS_RCC_ID3V2);
+ if (!name) return ID3_ENCODING_ISO_8859_1;
+
+ if ((!strcasecmp(name,"UTF-8"))||(!strcasecmp(name,"UTF8"))) return ID3_ENCODING_UTF8;
+ if ((!strcasecmp(name,"UTF-16"))||(!strcasecmp(name,"UTF16"))||(!strcasecmp(name,"UTF16LE"))||(!strcasecmp(name,"UTF-16LE"))) return ID3_ENCODING_UTF16;
+ if ((!strcasecmp(name,"UTF-16BE"))||(!strcasecmp(name,"UTF16BE"))) return ID3_ENCODING_UTF16BE;
+#endif /* HAVE_LIBRCC */
+
+ return ID3_ENCODING_ISO_8859_1;
+}
diff --git a/source/rcc.h b/source/rcc.h
new file mode 100644
index 0000000..d5bc9b2
--- /dev/null
+++ b/source/rcc.h
@@ -0,0 +1,32 @@
+#ifndef _XMMS_CHARSET_H
+#define _XMMS_CHARSET_H
+
+typedef enum xmms_rcc_class_t {
+ XMMS_RCC_ID3 = 0,
+ XMMS_RCC_ID3V2,
+ XMMS_RCC_PL,
+ XMMS_RCC_PLFS,
+ XMMS_RCC_FS,
+ XMMS_RCC_OUT,
+ XMMS_RCC_CTYPE,
+ XMMS_RCC_UTF8
+} xmms_rcc_class;
+
+void xmms_rcc_init();
+void xmms_rcc_free();
+void xmms_rcc_prefswin_create(void *prefswin_notebook);
+void xmms_rcc_prefswin_apply();
+
+char *xmms_rcc_fs2pl(const char *fnstring, const char *filename);
+char *xmms_rcc_get(xmms_rcc_class charset, const char *buf);
+char *xmms_rcc_put(xmms_rcc_class charset, const char *buf);
+char *xmms_rcc_sized_put(xmms_rcc_class charset, const char *buf, int size);
+char *xmms_rcc_recode(xmms_rcc_class from, xmms_rcc_class to, const char *buf);
+char *xmms_rcc_sized_recode(xmms_rcc_class from, xmms_rcc_class to, const char *buf, int size);
+char *xmms_rcc_fs(xmms_rcc_class from, xmms_rcc_class to, const char *fspath, const char *path, const char *filename);
+
+const char *xmms_rcc_string(const char *buf);
+const char *xmms_rcc_get_language();
+int xmms_rcc_get_id3v2_encoding();
+
+#endif /* _XMMS_CHARSET_H */
diff --git a/source/rcc_langs.h b/source/rcc_langs.h
new file mode 100644
index 0000000..70abebf
--- /dev/null
+++ b/source/rcc_langs.h
@@ -0,0 +1,120 @@
+typedef struct xmms_rcc_langs_t {
+ const char *sn;
+ const char *name;
+} xmms_rcc_langs;
+
+static const char *default_lang = "XXX";
+static xmms_rcc_langs langs[] = {
+ { "aa", "aar" },
+ { "ab", "abk" },
+ { "af", "afr" },
+ { "ak", "aka" },
+ { "am", "amh" },
+ { "ar", "ara" },
+ { "an", "arg" },
+ { "as", "asm" },
+ { "av", "ava" },
+ { "ae", "ave" },
+ { "ay", "aym" },
+ { "ba", "bak" },
+ { "bm", "bam" },
+ { "bn", "ben" },
+ { "bh", "bih" },
+ { "bi", "bis" },
+ { "bs", "bos" },
+ { "br", "bre" },
+ { "bg", "bul" },
+ { "ch", "cha" },
+ { "cv", "chv" },
+ { "kw", "cor" },
+ { "co", "cos" },
+ { "cr", "cre" },
+ { "dv", "div" },
+ { "dz", "dzo" },
+ { "en", "eng" },
+ { "et", "est" },
+ { "fj", "fij" },
+ { "fi", "fin" },
+ { "fy", "fry" },
+ { "ff", "ful" },
+ { "ig", "ibo" },
+ { "io", "ido" },
+ { "iu", "iku" },
+ { "ie", "ile" },
+ { "ik", "ipk" },
+ { "it", "ita" },
+ { "jv", "jav" },
+ { "ja", "jpn" },
+ { "kn", "kan" },
+ { "ks", "kas" },
+ { "kr", "kau" },
+ { "kk", "kaz" },
+ { "km", "khm" },
+ { "rw", "kin" },
+ { "ky", "kir" },
+ { "kv", "kom" },
+ { "kg", "kon" },
+ { "ku", "kur" },
+ { "lo", "lao" },
+ { "la", "lat" },
+ { "lv", "lav" },
+ { "ln", "lin" },
+ { "lt", "lit" },
+ { "lg", "lug" },
+ { "mh", "mah" },
+ { "ml", "mal" },
+ { "mr", "mar" },
+ { "mg", "mlg" },
+ { "mt", "mlt" },
+ { "mo", "mol" },
+ { "mn", "mon" },
+ { "na", "nau" },
+ { "ng", "ndo" },
+ { "oj", "oji" },
+ { "or", "ori" },
+ { "om", "orm" },
+ { "pi", "pli" },
+ { "ps", "pus" },
+ { "qu", "que" },
+ { "rn", "run" },
+ { "ru", "rus" },
+ { "sg", "sag" },
+ { "sa", "san" },
+ { "sn", "sna" },
+ { "sd", "snd" },
+ { "so", "som" },
+ { "sc", "srd" },
+ { "ss", "ssw" },
+ { "sw", "swa" },
+ { "ty", "tah" },
+ { "ta", "tam" },
+ { "tg", "tgk" },
+ { "tl", "tgl" },
+ { "ti", "tir" },
+ { "tn", "tsn" },
+ { "ts", "tso" },
+ { "tr", "tur" },
+ { "tw", "twi" },
+ { "uk", "ukr" },
+ { "ur", "urd" },
+ { "uz", "uzb" },
+ { "vi", "vie" },
+ { "wa", "wln" },
+ { "wo", "wol" },
+ { "xh", "xho" },
+ { "yi", "yid" },
+ { "yo", "yor" },
+ { "zu", "zul" },
+ { NULL, NULL }
+};
+
+const char *xmms_rcc_get_639_2_name(const char *sn) {
+ unsigned int i;
+
+ if ((!sn)||(!strcasecmp(sn, "off"))) return default_lang;
+
+ for (i=0; langs[i].sn; i++)
+ if (!strcasecmp(sn, langs[i].sn)) return langs[i].name;
+
+ return default_lang;
+}