summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--media-sound/xmms/ChangeLog8
-rw-r--r--media-sound/xmms/files/digest-xmms-1.2.8-r12
-rw-r--r--media-sound/xmms/files/xmms-1.2.8-dtd.patch1985
-rw-r--r--media-sound/xmms/files/xmms-1.2.8-jump.patch76
-rw-r--r--media-sound/xmms/files/xmms-1.2.8-russian-charset.patch2174
-rw-r--r--media-sound/xmms/files/xmms-1.2.8-sigterm.patch31
-rw-r--r--media-sound/xmms/xmms-1.2.8-r1.ebuild139
7 files changed, 4414 insertions, 1 deletions
diff --git a/media-sound/xmms/ChangeLog b/media-sound/xmms/ChangeLog
index b8bfe1d87efb..09ec24933832 100644
--- a/media-sound/xmms/ChangeLog
+++ b/media-sound/xmms/ChangeLog
@@ -1,6 +1,12 @@
# ChangeLog for media-sound/xmms
# Copyright 2002-2003 Gentoo Technologies, Inc.; Distributed under the GPL v2
-# $Header: /var/cvsroot/gentoo-x86/media-sound/xmms/ChangeLog,v 1.66 2003/09/12 19:27:39 seemant Exp $
+# $Header: /var/cvsroot/gentoo-x86/media-sound/xmms/ChangeLog,v 1.67 2003/09/13 03:46:59 vapier Exp $
+
+*xmms-1.2.8-r1 (12 Sep 2003)
+
+ 12 Sep 2003; Mike Frysinger <vapier@gentoo.org> :
+ Added Dynamic Taste Detection #27967 and removed some of the elibtoolize
+ and autotools crud.
*xmms-1.2.8 (12 Sep 2003)
diff --git a/media-sound/xmms/files/digest-xmms-1.2.8-r1 b/media-sound/xmms/files/digest-xmms-1.2.8-r1
new file mode 100644
index 000000000000..55451e9e64ea
--- /dev/null
+++ b/media-sound/xmms/files/digest-xmms-1.2.8-r1
@@ -0,0 +1,2 @@
+MD5 bf1d7ea568b01d72e99d75ae9d0d1594 xmms-1.2.8.tar.gz 3884444
+MD5 a68edd5b03d650c366f09852a8414ee2 gentoo_ice.zip 68458
diff --git a/media-sound/xmms/files/xmms-1.2.8-dtd.patch b/media-sound/xmms/files/xmms-1.2.8-dtd.patch
new file mode 100644
index 000000000000..94b342568b0c
--- /dev/null
+++ b/media-sound/xmms/files/xmms-1.2.8-dtd.patch
@@ -0,0 +1,1985 @@
+diff -ruN dtd-old/AUTHORS dtd-new/AUTHORS
+--- dtd-old/AUTHORS 2003-01-07 15:50:08.000000000 +0100
++++ dtd-new/AUTHORS 2003-09-08 13:46:19.000000000 +0200
+@@ -42,6 +42,7 @@
+ Chris Wilson
+ Dave Yearke
+ Stephan K. Zitz
++ Johan Walles (Dynamic Taste Detection)
+
+ Default skin: Leonard "Blayde" Tan
+ Robin Sylvestre (Equalizer and Playlist)
+diff -ruN dtd-old/README dtd-new/README
+--- dtd-old/README 2003-09-04 23:00:18.000000000 +0200
++++ dtd-new/README 2003-09-08 13:46:19.000000000 +0200
+@@ -57,6 +57,7 @@
+ 5. Features
+ 5.1 Supported File formats
+ 5.2 Supported Features
++ 5.2.1 Dynamic Taste Detection
+ 6. Obtaining XMMS
+ 7. Misc
+ 7.1 Shoutcast support
+@@ -1121,6 +1122,34 @@
+ Compiles and works on other UNIX's
+ Proxy authentication support
+
++5.2.1 Dynamic Taste Detection
++-----------------------------
++Basically, Dynamic Taste Detection collects statistics on how you
++listen to songs, and then adapts the shuffle play and randomize
++playlist functions to better follow your taste. It takes care of
++itself entirely, so that's all you really need to know about it ;-).
++
++For those of you who are curious, here is how it works. Two kinds of
++statistics are collected:
++
++- What songs you don't like
++- What songs you like to hear next to each other
++
++The first one is easy. When you skip a song, it looses a point.
++Songs with low scores are placed at the end of the playlist.
++
++What songs you want to hear next to each other are a bit more
++intricate. Let's say you have two songs, A and B. A (or any song
++above it in the playlist) is playing. You move song B to the position
++right after A. A finishes playing, and B starts. This is interpreted
++as "you want to hear A and B next to each other".
++
++In this example, A and B will then *tend* to (i.e. not always) end up
++next to each other in the playlist (though not necessarily in that
++order).
++
++To explicitly change your opinion of a song, or disconnect one song
++from another, you can do so from the right-click menu in the playlist.
+
+ 6. Obtaining XMMS
+ -------------------
+diff -ruN dtd-old/xmms/Makefile.am dtd-new/xmms/Makefile.am
+--- dtd-old/xmms/Makefile.am 2003-09-08 13:31:40.000000000 +0200
++++ dtd-new/xmms/Makefile.am 2003-09-08 13:46:19.000000000 +0200
+@@ -34,6 +34,7 @@
+ menurow.c menurow.h \
+ hslider.c hslider.h \
+ monostereo.c monostereo.h \
++dtd.c dtd.h \
+ vis.c vis.h \
+ svis.c svis.h \
+ number.c number.h \
+diff -ruN dtd-old/xmms/dtd.c dtd-new/xmms/dtd.c
+--- dtd-old/xmms/dtd.c 1970-01-01 01:00:00.000000000 +0100
++++ dtd-new/xmms/dtd.c 2003-09-08 14:37:21.000000000 +0200
+@@ -0,0 +1,624 @@
++/* XMMS - Cross-platform multimedia player
++ * Copyright (C) 2001 Johan Walles, d92-jwa@nada.kth.se
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++/* Hint: DTD = Dynamic Taste Detection */
++
++#include <assert.h>
++
++#include "xmms.h"
++
++static GList *dtd_recommendations_list;
++
++static DtdNextSong *dtd_find_next_song(DtdStartSong *start_song,
++ const gchar *filename)
++{
++ GList *iterator;
++
++ for (iterator = start_song->next_song;
++ iterator != NULL;
++ iterator = g_list_next(iterator))
++ {
++ if (strcmp(((DtdNextSong *)(iterator->data))->filename,
++ filename) == 0)
++ {
++ return (DtdNextSong *)(iterator->data);
++ }
++ }
++
++ return NULL;
++}
++
++static DtdStartSong *dtd_find_start_song(const gchar *filename)
++{
++ GList *iterator;
++
++ if (filename == NULL)
++ {
++ return NULL;
++ }
++
++ for (iterator = dtd_recommendations_list;
++ iterator != NULL;
++ iterator = g_list_next(iterator))
++ {
++ if (strcmp(((DtdStartSong *)(iterator->data))->filename,
++ filename) == 0)
++ {
++ return (DtdStartSong *)(iterator->data);
++ }
++ }
++
++ return NULL;
++}
++
++/*
++ * Memorize that after playing the file named start, the user might
++ * want to hear the file named follow.
++ */
++static void __dtd_recommend_next(int weight, const gchar *start, const gchar *follow)
++{
++ DtdStartSong *start_song;
++ DtdNextSong *next_song;
++
++ /* Check if the start song already has recommendations. */
++ if ((start_song = dtd_find_start_song(start)) ==
++ NULL)
++ {
++ /* Nope, create a new recommendation start node */
++ start_song = g_new(DtdStartSong, 1);
++
++ start_song->filename = g_strdup(start);
++ start_song->next_song = NULL;
++ start_song->score = 0;
++
++ /* Add the recommendation start node to the
++ recommendation start node list. */
++ dtd_recommendations_list =
++ g_list_append(dtd_recommendations_list,
++ start_song);
++ }
++
++ /* Check if the next song is already recommended for this
++ start song */
++ if ((next_song = dtd_find_next_song(start_song,
++ follow)) == NULL)
++ {
++ /* It's not. Create a recommendation next node */
++ next_song = g_new(DtdNextSong, 1);
++
++ next_song->filename = g_strdup(follow);
++
++ next_song->weight = 0;
++
++ /* Add the new recommendation to the start node */
++ start_song->next_song =
++ g_list_append(start_song->next_song,
++ next_song);
++ }
++
++ next_song->weight += weight;
++}
++
++/*
++ * Memorize that after playing the file named start, the user might
++ * want to hear the file named follow.
++ */
++void dtd_recommend_next(const gchar *start, const gchar *follow)
++{
++ /* Recommend symmetrically */
++ __dtd_recommend_next(1, start, follow);
++ __dtd_recommend_next(1, follow, start);
++}
++
++void __dtd_dissociate(const gchar *file1, const gchar *file2)
++{
++ DtdStartSong *startSong;
++ DtdNextSong *nextSong = NULL;
++ GList *nextSongIterator;
++
++ // Find the start pointer for file1
++ startSong = dtd_find_start_song(file1);
++ if (startSong == NULL)
++ {
++ // No such recommendation exists
++ return;
++ }
++
++ // Find its next pointer for file2
++ for (nextSongIterator = startSong->next_song;
++ nextSongIterator != NULL;
++ nextSongIterator = g_list_next(nextSongIterator))
++ {
++ nextSong = (DtdNextSong *)(nextSongIterator->data);
++ if (strcmp(file2, nextSong->filename) == 0)
++ {
++ break;
++ }
++ }
++ if (nextSong == NULL)
++ {
++ // No such recommendation exists
++ return;
++ }
++
++ // Remove the next pointer
++ startSong->next_song =
++ g_list_remove_link(startSong->next_song, nextSongIterator);
++ g_free(nextSong->filename);
++ g_free(nextSong);
++ g_list_free_1(nextSongIterator);
++}
++
++void dtd_dissociate(const gchar *file1, const gchar *file2)
++{
++ // The slave's chain is heavy in both ends, so let's set 'em
++ // both free :-)
++ __dtd_dissociate(file1, file2);
++ __dtd_dissociate(file2, file1);
++}
++
++/*
++ * Modify a song's score.
++ */
++void dtd_set_score(const gchar *filename, gint score)
++{
++ DtdStartSong *start_song = dtd_find_start_song(filename);
++
++ if (start_song == NULL)
++ {
++ start_song = g_new(DtdStartSong, 1);
++
++ start_song->filename = g_strdup(filename);
++ start_song->next_song = NULL;
++ start_song->score = 0;
++
++ /* Add the new recommendation start node to the
++ recommendation start node list. */
++ dtd_recommendations_list =
++ g_list_append(dtd_recommendations_list,
++ start_song);
++ }
++
++ start_song->score = score;
++ /* Scores > 0 means "always put this song at the start of the
++ list". Probably we don't want that.
++ */
++ if (start_song->score > 0)
++ {
++ start_song->score = 0;
++ }
++}
++
++void dtd_change_score(const gchar *filename, gint delta)
++{
++ DtdStartSong *start_song = dtd_find_start_song(filename);
++
++ if (start_song == NULL)
++ {
++ start_song = g_new(DtdStartSong, 1);
++
++ start_song->filename = g_strdup(filename);
++ start_song->next_song = NULL;
++ start_song->score = 0;
++
++ /* Add the new recommendation start node to the
++ recommendation start node list. */
++ dtd_recommendations_list =
++ g_list_append(dtd_recommendations_list,
++ start_song);
++ }
++
++ /* Scores > 0 means "always put this song at the start of the
++ list". Probably we don't want that.
++ */
++ start_song->score += delta;
++ if (start_song->score > 0)
++ {
++ start_song->score = 0;
++ }
++}
++
++/*
++ * Retrieve a song's score.
++ */
++gint dtd_get_score(const gchar *filename)
++{
++ DtdStartSong *start_song = dtd_find_start_song(filename);
++
++ if (start_song == NULL)
++ {
++ return 0;
++ }
++ else
++ {
++ return start_song->score;
++ }
++}
++
++const GList *dtd_get_recommendations(const gchar *start)
++{
++ DtdStartSong *dtdStartSong = dtd_find_start_song(start);
++
++ return dtdStartSong ? dtdStartSong->next_song : NULL;
++}
++
++static void __dtd_set_score(gchar *filename, gint score)
++{
++ DtdStartSong *start_song = dtd_find_start_song(filename);
++
++ if (start_song == NULL)
++ {
++ start_song = g_new(DtdStartSong, 1);
++
++ start_song->filename = g_strdup(filename);
++ start_song->next_song = NULL;
++ start_song->score = 0;
++
++ /* Add the new recommendation start node to the
++ recommendation start node list. */
++ dtd_recommendations_list =
++ g_list_append(dtd_recommendations_list,
++ start_song);
++ }
++
++ start_song->score = score;
++}
++
++static guint dtd_sum_recommendations(DtdStartSong *start_song)
++{
++ GList *iterator;
++ guint sum = 0;
++
++ assert(start_song != NULL);
++
++ for (iterator = start_song->next_song;
++ iterator != NULL;
++ iterator = g_list_next(iterator))
++ {
++ sum += ((DtdNextSong *)(iterator->data))->weight;
++ }
++
++ assert(sum != 0);
++
++ return sum;
++}
++
++static const gchar *dtd_get_nth_recommendation(DtdStartSong *start_song,
++ guint n)
++{
++ GList *iterator;
++ guint sum = 0;
++
++ assert(start_song != NULL);
++
++ if (n == 0)
++ return NULL;
++
++ for (iterator = start_song->next_song;
++ iterator != NULL;
++ iterator = g_list_next(iterator))
++ {
++ sum += ((DtdNextSong *)(iterator->data))->weight;
++
++ if (sum >= n)
++ return ((DtdNextSong *)(iterator->data))->filename;
++ }
++
++ /* If we get here then the n passed to this function was too
++ large. It can be no larger than the number calculated by
++ dtd_sum_recommendations(). */
++ assert(FALSE);
++
++ return NULL;
++}
++
++/*
++ Try to recommend a song to play after first_filename. NULL means
++ that no recommendation is given (for whatever reason).
++
++ FIXME: This method should accept a list of songs *not* to choose
++ between. If the user has asked for a recommendation and received an
++ answer that is not in the current playlist, they must be able to try
++ again.
++*/
++const gchar *dtd_get_recommendation(const gchar *first_filename)
++{
++ DtdStartSong *start_song;
++ guint recommendation_total;
++
++ if (first_filename == NULL)
++ {
++ return NULL;
++ }
++
++ if ((start_song = dtd_find_start_song(first_filename)) ==
++ NULL)
++ {
++ /* There are no recommendations for first_filename */
++ return NULL;
++ }
++
++ if (start_song->next_song == NULL)
++ {
++ return NULL;
++ }
++
++ recommendation_total = dtd_sum_recommendations(start_song);
++
++ return dtd_get_nth_recommendation(start_song,
++ rand() % (recommendation_total + 1));
++}
++
++static void dtd_clear(void)
++{
++ GList *start_iterator;
++ GList *next_iterator;
++
++ if (dtd_recommendations_list == NULL)
++ {
++ return;
++ }
++
++ /* Free all memory used by the recommendations */
++ for (start_iterator = dtd_recommendations_list;
++ start_iterator != NULL;
++ start_iterator = g_list_next(start_iterator))
++ {
++ /* Free all file names in the next list */
++ for (next_iterator = ((DtdStartSong *)(start_iterator->data))->next_song;
++ next_iterator != NULL;
++ next_iterator = g_list_next(next_iterator))
++ {
++ free(((DtdNextSong *)(next_iterator->data))->filename);
++ ((DtdNextSong *)(next_iterator->data))->filename = NULL;
++
++ free(next_iterator->data);
++ next_iterator->data = NULL;
++ }
++ /* Free the next list */
++ g_list_free(((DtdStartSong *)(start_iterator->data))->next_song);
++ ((DtdStartSong *)(start_iterator->data))->next_song = NULL;
++
++ /* Free the file name */
++ free(((DtdStartSong *)(start_iterator->data))->filename);
++ ((DtdStartSong *)(start_iterator->data))->filename = NULL;
++
++ /* Free the struct */
++ free(start_iterator->data);
++ start_iterator->data = NULL;
++ }
++ g_list_free(dtd_recommendations_list);
++ dtd_recommendations_list = NULL;
++}
++
++void dtd_init(void)
++{
++ gchar *dtd_data_file_name;
++ FILE *dtd_data_file;
++
++ gchar start[950], follow[950];
++ gchar line[999];
++ int weight;
++
++ enum { NEXT, SCORES } section = NEXT;
++
++ gboolean failure = FALSE;
++
++ /* We have no recommendations to begin with */
++ dtd_recommendations_list = NULL;
++
++ /* Make up a filename for the dtd data file */
++ dtd_data_file_name = g_strconcat(g_get_home_dir(),
++ "/.xmms/",
++ DTD_DATA_FILE_NAME,
++ NULL);
++
++ /* Open the dtd data file for input */
++ dtd_data_file = fopen(dtd_data_file_name,
++ "r");
++ if (dtd_data_file == NULL)
++ {
++ return;
++ }
++
++ /* Parse the dtd data file */
++ start[0] = '\0';
++ while (!feof(dtd_data_file))
++ {
++ if (fgets(line, 990, dtd_data_file) != NULL)
++ {
++ if (sscanf(line, "%d %940[^\n]\n", &weight, follow) == 2)
++ {
++ /* Found a line with both number and name */
++
++ switch (section)
++ {
++ case NEXT:
++ if (start[0] != '\0')
++ {
++ if (weight < 0)
++ {
++ failure= TRUE;
++ }
++ else if (weight > 0)
++ {
++ __dtd_recommend_next(weight, start, follow);
++ }
++ }
++ else
++ {
++ /* Parse error! */
++ failure = TRUE;
++ }
++ break;
++
++ case SCORES:
++ if (weight != 0)
++ {
++ __dtd_set_score(follow, weight);
++ }
++ break;
++ }
++
++ if (failure) break;
++ }
++ else if (sscanf(line, "%940[^\n]\n", start) == 1)
++ {
++ if (strcmp(start, "scores") == 0)
++ {
++ /* The scores section starts here */
++ section = SCORES;
++ start[0] = '\0';
++ }
++ else
++ {
++ /* New start song read OK */
++ section = NEXT;
++ }
++ }
++ else
++ {
++ /* Parse error! */
++ failure = TRUE;
++ break;
++ }
++ }
++ }
++
++ if (failure)
++ {
++ /* Free all space occupied by the recommendations list
++ in favour of remembering a broken list. */
++ dtd_clear();
++ }
++
++ fclose(dtd_data_file);
++ dtd_data_file = NULL;
++}
++
++void dtd_persist(void)
++{
++ gchar *dtd_data_file_name;
++ FILE *dtd_data_file;
++
++ GList *start_iterator;
++ GList *next_iterator;
++
++ gboolean failure = FALSE;
++
++ /* Are there any recommendations to store */
++ if (dtd_recommendations_list == NULL)
++ {
++ /* Nope. */
++ return;
++ }
++
++ /* Make up a filename for the dtd data file */
++ dtd_data_file_name = g_strconcat(g_get_home_dir(),
++ "/.xmms/",
++ DTD_DATA_FILE_NAME,
++ NULL);
++
++ /* Open the dtd data file for output */
++ dtd_data_file = fopen(dtd_data_file_name,
++ "w");
++ if (dtd_data_file == NULL)
++ {
++ /* FIXME: We fail silently. Should we output a
++ * warning somehow? */
++ return;
++ }
++
++ /* Loop through all recommendation starts, storing their
++ recommended followers */
++ for (start_iterator = dtd_recommendations_list;
++ start_iterator != NULL;
++ start_iterator = g_list_next(start_iterator))
++ {
++ if (((DtdStartSong *)(start_iterator->data))->next_song == NULL)
++ {
++ /* Don't store songs with only a score (yet). */
++ continue;
++ }
++
++ if (failure ||
++ (fprintf(dtd_data_file,
++ "%s\n",
++ ((DtdStartSong *)(start_iterator->data))->filename) == 0))
++ {
++ failure = TRUE;
++ break;
++ }
++
++ for (next_iterator = ((DtdStartSong *)(start_iterator->data))->next_song;
++ next_iterator != NULL;
++ next_iterator = g_list_next(next_iterator))
++ {
++ if (fprintf(dtd_data_file, "%d %s\n",
++ ((DtdNextSong *)(next_iterator->data))->weight,
++ ((DtdNextSong *)(next_iterator->data))->filename) == 0)
++ {
++ failure = TRUE;
++ break;
++ }
++ }
++ }
++
++ if (!failure)
++ {
++ /* Output the "here starts the scoring section" marker. */
++ if (fprintf(dtd_data_file, "scores\n") == 0)
++ {
++ failure = TRUE;
++ }
++
++ /* Output all (non-zero) scores */
++ for (start_iterator = dtd_recommendations_list;
++ start_iterator != NULL;
++ start_iterator = g_list_next(start_iterator))
++ {
++ if (((DtdStartSong *)(start_iterator->data))->score == 0)
++ {
++ /* Don't store songs with zero score. */
++ continue;
++ }
++
++ if (failure ||
++ (fprintf(dtd_data_file,
++ "%d %s\n",
++ ((DtdStartSong *)(start_iterator->data))->score,
++ ((DtdStartSong *)(start_iterator->data))->filename) == 0))
++ {
++ failure = TRUE;
++ break;
++ }
++ }
++ }
++
++ fclose(dtd_data_file);
++ dtd_data_file = NULL;
++
++ if (failure)
++ {
++ /* If writing of the data file failed, attempt to
++ remove it rather than leaving a broken data file
++ behind. */
++
++ unlink(dtd_data_file_name);
++ }
++}
+diff -ruN dtd-old/xmms/dtd.h dtd-new/xmms/dtd.h
+--- dtd-old/xmms/dtd.h 1970-01-01 01:00:00.000000000 +0100
++++ dtd-new/xmms/dtd.h 2003-09-08 14:38:32.000000000 +0200
+@@ -0,0 +1,73 @@
++/* XMMS - Cross-platform multimedia player
++ * Copyright (C) 2001 Johan Walles, d92-jwa@nada.kth.se
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++/* Hint: DTD = Dynamic Taste Detection */
++
++/* FIXME: Should lists prove to be too slow we could use balanced
++ trees for the DtdStartSong collection. The DtdNextSong is scanned
++ through however, so tree-ifying will do no good. */
++
++#ifndef DTD_H
++#define DTD_H
++
++#define DTD_DATA_FILE_NAME "dtd-stats"
++
++typedef struct
++{
++ gchar *filename;
++ guint weight;
++}
++DtdNextSong;
++
++typedef struct
++{
++ gchar *filename;
++ gint score;
++ GList *next_song;
++}
++DtdStartSong;
++
++void dtd_init(void);
++void dtd_persist(void);
++
++// Tell DTD that after start, the user tends to want to hear follow
++void dtd_recommend_next(const gchar *start,
++ const gchar *follow);
++
++// Tell DTD to break the association between file1 and file2 (if one
++// exists)
++void dtd_dissociate(const gchar *file1,
++ const gchar *file2);
++
++
++// Let DTD recommend what to play after start. NULL = No
++// recommendation = anything goes.
++const gchar *dtd_get_recommendation(const gchar *start);
++
++// Change the absolute score for a file
++void dtd_change_score(const gchar *filename, gint delta);
++void dtd_set_score(const gchar *filename, gint score);
++
++// Get the score for a file. Unknown file = score 0.
++gint dtd_get_score(const gchar *filename);
++
++// Get the list of recommendations for a given file. NULL = no
++// recommendations for that file.
++const GList *dtd_get_recommendations(const gchar *start);
++
++#endif
+diff -ruN dtd-old/xmms/main.c dtd-new/xmms/main.c
+--- dtd-old/xmms/main.c 2003-09-08 13:31:40.000000000 +0200
++++ dtd-new/xmms/main.c 2003-09-08 13:49:16.000000000 +0200
+@@ -476,6 +476,8 @@
+ }
+ }
+ xmms_cfg_read_string(cfgfile, "xmms", "generic_title_format", &cfg.gentitle_format);
++
++ xmms_cfg_read_boolean(cfgfile, "xmms", "dynamic_taste_detection", &cfg.dynamic_taste_detection);
+
+ xmms_cfg_free(cfgfile);
+ }
+@@ -674,6 +676,8 @@
+ g_free(str);
+ }
+ xmms_cfg_write_string(cfgfile, "xmms", "generic_title_format", cfg.gentitle_format);
++
++ xmms_cfg_write_boolean(cfgfile, "xmms", "dynamic_taste_detection", cfg.dynamic_taste_detection);
+
+ xmms_cfg_write_file(cfgfile, filename);
+ xmms_cfg_free(cfgfile);
+@@ -903,6 +907,7 @@
+ playlist_clear();
+ cleanup_plugins();
+ sm_cleanup();
++ dtd_persist();
+ gtk_exit(0);
+ }
+
+@@ -3130,6 +3135,7 @@
+ GDK_THREADS_LEAVE();
+ }
+
++ cfg.dynamic_taste_detection = TRUE;
+
+ GDK_THREADS_ENTER();
+ check_ctrlsocket();
+@@ -3608,6 +3614,10 @@
+ mainwin_timeout_tag = gtk_timeout_add(10, idle_func, NULL);
+ playlist_start_get_info_thread();
+
++
++ /* Initialize dynamic taste detection */
++ dtd_init();
++
+ enable_x11r5_session_management(argc, argv);
+ sm_init(argc, argv, options.previous_session_id);
+ gtk_main();
+diff -ruN dtd-old/xmms/main.h dtd-new/xmms/main.h
+--- dtd-old/xmms/main.h 2003-09-08 13:31:40.000000000 +0200
++++ dtd-new/xmms/main.h 2003-09-08 13:51:48.000000000 +0200
+@@ -64,6 +64,7 @@
+ gchar *charset_id3, *charset_output, *charset_fs, *charset_pl;
+ gint autocharset;
+ gchar *shade_font;
++ gboolean dynamic_taste_detection;
+ }
+ Config;
+
+diff -ruN dtd-old/xmms/playlist.c dtd-new/xmms/playlist.c
+--- dtd-old/xmms/playlist.c 2003-09-08 13:31:40.000000000 +0200
++++ dtd-new/xmms/playlist.c 2003-09-08 14:12:58.000000000 +0200
+@@ -23,6 +23,7 @@
+ #include "charset.h"
+ #include <sys/stat.h>
+ #include <unistd.h>
++#include <assert.h>
+
+ GList *playlist = NULL;
+ GList *shuffle_list = NULL;
+@@ -653,6 +654,17 @@
+
+ plist_pos_list = find_playlist_position_list();
+
++ if ((plist_pos_list != NULL) &&
++ (cfg.repeat || (g_list_next(plist_pos_list) != NULL)))
++ {
++ /*
++ The user is skipping a song. We interpret this as
++ meaning "I don't like this song".
++ */
++ dtd_change_score(((PlaylistEntry *)(plist_pos_list->data))->filename,
++ -1);
++ }
++
+ if (!cfg.repeat && !g_list_next(plist_pos_list))
+ {
+ PL_UNLOCK();
+@@ -725,7 +737,18 @@
+
+ plist_pos_list = find_playlist_position_list();
+ if (g_list_previous(plist_pos_list))
+- playlist_position = plist_pos_list->prev->data;
++ {
++ playlist_position =
++ g_list_previous(plist_pos_list)->data;
++
++ /*
++ The user has gone back to a song. We interpret that
++ as meaning "I like this song so much that I want to
++ hear it again".
++ */
++ dtd_change_score(playlist_position->filename,
++ 1);
++ }
+ else if (cfg.repeat)
+ {
+ GList *node;
+@@ -740,7 +763,7 @@
+ }
+ PL_UNLOCK();
+ playlist_check_pos_current();
+-
++
+ if (restart_playing)
+ playlist_play();
+ else
+@@ -856,6 +879,7 @@
+
+ playlist_position = node->data;
+ PL_UNLOCK();
++ ((PlaylistEntry *)playlist_position)->moved = FALSE;
+ playlist_check_pos_current();
+
+ if (restart_playing)
+@@ -875,12 +899,19 @@
+
+ void playlist_eof_reached(void)
+ {
++ /*
++ * This function is called whenever a song ends and the next
++ * one should start playing.
++ */
++
+ GList *plist_pos_list;
++ GList *previous_plist_pos;
+
+ input_stop();
+
+ PL_LOCK();
+ plist_pos_list = find_playlist_position_list();
++ previous_plist_pos = plist_pos_list->data;
+
+ if (cfg.no_playlist_advance)
+ {
+@@ -912,6 +943,25 @@
+ else
+ playlist_position = plist_pos_list->next->data;
+ PL_UNLOCK();
++
++ if (!cfg.shuffle && (((PlaylistEntry *)playlist_position)->moved))
++ {
++ /*
++ * Song A has stopped playing. Song B will start
++ * playing now. Song B had moved. Thus, we conclude
++ * that the user wants to hear Song B after song A.
++ * Therefore, Song B should be added to Song A's
++ * next-song-preferences.
++ *
++ * Song A is previous_plist_pos. Song B is
++ * playlist_position. */
++
++ dtd_recommend_next(((PlaylistEntry *)previous_plist_pos)->filename,
++ ((PlaylistEntry *)playlist_position)->filename);
++
++ ((PlaylistEntry *)playlist_position)->moved = FALSE;
++ }
++
+ playlist_check_pos_current();
+ playlist_play();
+ mainwin_set_info_text();
+@@ -1146,6 +1196,7 @@
+ ext = strrchr(filename, '.');
+ if (ext && !strcasecmp(ext, ".pls"))
+ {
++ /* It's a playlist, let's parse it. */
+ int noe, i;
+ char key[10];
+
+@@ -1283,6 +1334,11 @@
+ char *ret;
+ PlaylistEntry *entry;
+ GList *node;
++
++ if (pos < 0)
++ {
++ return NULL;
++ }
+
+ PL_LOCK();
+ if (!playlist)
+@@ -1290,6 +1346,11 @@
+ PL_UNLOCK();
+ return NULL;
+ }
++ if (pos >= __get_playlist_length())
++ {
++ PL_UNLOCK();
++ return NULL;
++ }
+ node = g_list_nth(playlist, pos);
+ if (!node)
+ {
+@@ -1355,6 +1416,84 @@
+ return title;
+ }
+
++int playlist_get_score(gint pos)
++{
++ PlaylistEntry *entry;
++ GList *node;
++ gint score;
++
++ PL_LOCK();
++ if (!playlist)
++ {
++ PL_UNLOCK();
++ return 0;
++ }
++ node = g_list_nth(playlist, pos);
++ if (!node)
++ {
++ PL_UNLOCK();
++ return 0;
++ }
++ entry = node->data;
++ score = dtd_get_score(entry->filename);
++ PL_UNLOCK();
++
++ return score;
++}
++
++void playlist_set_score(gint pos, gint score)
++{
++ PlaylistEntry *entry;
++ GList *node;
++
++ PL_LOCK();
++ if (!playlist)
++ {
++ PL_UNLOCK();
++ return;
++ }
++ node = g_list_nth(playlist, pos);
++ if (!node)
++ {
++ PL_UNLOCK();
++ return;
++ }
++ entry = node->data;
++ dtd_set_score(entry->filename, score);
++ PL_UNLOCK();
++}
++
++const gchar* playlist_filename2songtitle(const gchar *filename)
++{
++ GList *iterator;
++
++ if (filename == NULL)
++ {
++ return NULL;
++ }
++
++ // Scan loaded songs for filename
++ PL_LOCK();
++ for (iterator = get_playlist();
++ iterator != NULL;
++ iterator = g_list_next(iterator))
++ {
++ PlaylistEntry *currentEntry =
++ (PlaylistEntry *)(iterator->data);
++
++ if (strcmp(currentEntry->filename, filename) == 0)
++ {
++ // FIXME: Race condition
++ PL_UNLOCK();
++ return currentEntry->title;
++ }
++ }
++ PL_UNLOCK();
++
++ // Not found / don't know
++ return NULL;
++}
++
+ gint playlist_get_songtime(gint pos)
+ {
+ int retval = -1;
+@@ -1616,6 +1755,26 @@
+ PL_UNLOCK();
+ }
+
++static guint playlist_filename_to_index(PlaylistEntry *playlist_entries[],
++ guint n_entries,
++ const gchar *filename)
++{
++ guint i;
++
++ for (i = 0; i < n_entries; i++)
++ {
++ if (strcmp(playlist_entries[i]->filename,
++ filename) == 0)
++ {
++ return i;
++ }
++ }
++
++ return -1;
++}
++
++/* This function is used by the qsort() call in
++ smart_playlist_shuffle_list() (below). */
+ void playlist_sort_selected_by_date(void)
+ {
+ PL_LOCK();
+@@ -1630,7 +1789,20 @@
+ PL_UNLOCK();
+ }
+
+-static GList *playlist_shuffle_list(GList *list)
++static int playlist_entry_score_comparator(const void *a,
++ const void *b)
++{
++
++ int score_a = dtd_get_score((*((PlaylistEntry **)a))->filename);
++ int score_b = dtd_get_score((*((PlaylistEntry **)b))->filename);
++
++ /* We want high scores before low, so if score_a > score_b we
++ want to return something negative. */
++
++ return score_b - score_a;
++}
++
++static GList *smart_playlist_shuffle_list(GList *list)
+ {
+ /* Caller should hold playlist mutex */
+ /*
+@@ -1639,43 +1811,213 @@
+ * fuction is run.
+ */
+ int len = g_list_length(list);
+- int i, j;
+- GList *node, **ptrs;
++ gint i;
++ gint next_score_section = -1;
++ GList *iterator;
++ PlaylistEntry **ptrs;
+
+- if (!len)
++ if (len == 0)
+ return NULL;
+
+- ptrs = g_new(GList *, len);
++ ptrs = g_new(PlaylistEntry *, len);
+
+- for (node = list, i = 0; i < len; node = g_list_next(node), i++)
+- ptrs[i] = node;
++ /* Convert the list into an array of pointers */
++ for (iterator = list, i = 0; i < len; iterator = g_list_next(iterator), i++)
++ {
++ ptrs[i] = (PlaylistEntry *)(iterator->data);
++
++ // Shuffling songs voids information about user listening preferences
++ ptrs[i]->moved = FALSE;
++ }
++ g_list_free(list);
++ list = NULL;
++
++ /* Sort the array by score */
++ qsort(ptrs, len, sizeof(PlaylistEntry *), playlist_entry_score_comparator);
++
++ /* Shuffle the pointer array */
++ for (i = 0; i < len; i++)
++ {
++ PlaylistEntry *swap_ptr;
++ gint j;
++
++ const gchar *previous_filename;
++ const gchar *new_filename;
++
++ if (i >= next_score_section)
++ {
++ /* We are in a new score section; find out
++ where the next one starts */
++ gint current_score =
++ dtd_get_score(ptrs[i]->filename);
++
++ for (next_score_section = i;
++ next_score_section < len;
++ next_score_section++)
++ {
++ if (dtd_get_score(ptrs[next_score_section]->filename) !=
++ current_score)
++ {
++ break;
++ }
++ }
++ }
++
++ if (i == 0)
++ {
++ previous_filename = NULL;
++ }
++ else
++ {
++ previous_filename = ptrs[i - 1]->filename;
++ }
++
++ /* Find out from which index we should get the next song */
++ do {
++ if ((new_filename = dtd_get_recommendation(previous_filename)) !=
++ NULL)
++ {
++ j = playlist_filename_to_index(&(ptrs[i]),
++ len - i,
++ new_filename);
++ if (j != -1)
++ j += i;
++ }
++ else
++ {
++ /* Only find new songs in our current
++ score section */
++ j = (rand() % (next_score_section - i)) + i;
++
++ assert(j < next_score_section);
++ }
++ } while (j == -1);
++
++ assert(j < len);
++ assert(j >= i);
++
++ if (j < next_score_section)
++ {
++ /* Swap pointer #i and pointer #j */
++ swap_ptr = ptrs[i];
++ ptrs[i] = ptrs[j];
++ ptrs[j] = swap_ptr;
++ }
++ else
++ {
++ /*
++ We need to preserve the score section
++ ordering, so we do it the slow way if i and
++ j are in different scoring sections.
++ */
++ gint k;
++ swap_ptr = ptrs[i];
++ ptrs[i] = ptrs[j];
++
++ for (k = j; k >= (i + 2); k--)
++ {
++ ptrs[k] = ptrs[k - 1];
++ }
++
++ ptrs[i + 1] = swap_ptr;
++ }
++ }
++
++ /* Create a new list from the pointer array */
++ for (i = 0; i < len; i++)
++ {
++ list = g_list_append(list, ptrs[i]);
++ }
+
+- j = random() % len;
+- list = ptrs[j];
+- ptrs[j]->next = NULL;
+- ptrs[j] = ptrs[0];
++ g_free(ptrs);
+
+- for (i = 1; i < len; i++)
+- {
+- j = random() % (len - i);
+- list->prev = ptrs[i + j];
+- ptrs[i + j]->next = list;
+- list = ptrs[i + j];
+- ptrs[i + j] = ptrs[i];
+- }
+- list->prev = NULL;
++ return list;
++}
++
++static GList *old_playlist_shuffle_list(GList *list)
++{
++ /* Caller should hold playlist mutex */
++ /*
++ * Note that this doesn't make a copy of the original list.
++ * The pointer to the original list is not valid after this
++ * fuction is run.
++ */
++ int len = g_list_length(list);
++ gint i;
++ GList *iterator;
++ PlaylistEntry **ptrs;
++
++ if (len == 0)
++ return NULL;
++
++ ptrs = g_new(PlaylistEntry *, len);
++
++ /* Convert the list into an array of pointers */
++ for (iterator = list, i = 0; i < len; iterator = g_list_next(iterator), i++)
++ {
++ ptrs[i] = (PlaylistEntry *)(iterator->data);
++
++ // Shuffling songs voids information about user listening preferences
++ ptrs[i]->moved = FALSE;
++ }
++ g_list_free(list);
++ list = NULL;
++
++ /* Shuffle the pointer array */
++ for (i = 0; i < len; i++)
++ {
++ PlaylistEntry *swap_ptr;
++ gint j;
++
++ /* Pick a random song among the ones that are left */
++ j = (rand() % (len - i)) + i;
++
++ /* Swap pointer #i and pointer #j */
++ swap_ptr = ptrs[i];
++ ptrs[i] = ptrs[j];
++ ptrs[j] = swap_ptr;
++ }
++
++ /* Create a new list from the pointer array */
++ for (i = 0; i < len; i++)
++ {
++ list = g_list_append(list, ptrs[i]);
++ }
+
+ g_free(ptrs);
+
+ return list;
+ }
+
++static GList *playlist_shuffle_list(GList *list)
++{
++ if (cfg.dynamic_taste_detection)
++ {
++ return smart_playlist_shuffle_list(list);
++ }
++ else
++ {
++ return old_playlist_shuffle_list(list);
++ }
++}
++
+ void playlist_random(void)
+ {
++ GList *for_each;
++
+ PL_LOCK();
+
+ playlist = playlist_shuffle_list(playlist);
+
++ /* Remove all moved-marks from the playlist */
++
++ for (for_each = playlist;
++ for_each != NULL;
++ for_each = g_list_next(for_each))
++ {
++ ((PlaylistEntry *) for_each->data)->moved = FALSE;
++ }
++
+ PL_UNLOCK();
+ }
+
+@@ -1710,7 +2052,52 @@
+ PL_UNLOCK();
+ return num;
+ }
+-
++
++gint playlist_get_single_selection(void)
++{
++ // Returns the playlist position of the current selection. If
++ // 0 or > 1 songs are selected, this method returns -1.
++ GList *iterator;
++ gint currentPos;
++
++ gint selectedPos = -1;
++
++ for (iterator = get_playlist(), currentPos = 0;
++ iterator != NULL;
++ iterator = g_list_next(iterator), currentPos++)
++ {
++ if (((PlaylistEntry *)(iterator->data))->selected)
++ {
++ if (selectedPos == -1)
++ {
++ // First selected song found
++ selectedPos = currentPos;
++ }
++ else
++ {
++ // > 1 selected song found
++ return -1;
++ }
++ }
++ }
++
++ return selectedPos;
++}
++
++gboolean playlist_is_selected(gint pos)
++{
++ GList *node;
++ gboolean isSelected = 0;
++
++ PL_LOCK();
++ if ((node = g_list_nth(get_playlist(), pos)) != NULL)
++ {
++ isSelected = ((PlaylistEntry *)(node->data))->selected;
++ }
++ PL_UNLOCK();
++
++ return isSelected;
++}
+
+ static void playlist_generate_shuffle_list(void)
+ {
+diff -ruN dtd-old/xmms/playlist.h dtd-new/xmms/playlist.h
+--- dtd-old/xmms/playlist.h 2003-09-08 13:31:40.000000000 +0200
++++ dtd-new/xmms/playlist.h 2003-09-08 14:14:13.000000000 +0200
+@@ -26,7 +26,8 @@
+ gchar *title;
+ gchar *sort;
+ gint length;
+- gboolean selected;
++ gboolean selected;
++ gboolean moved;
+ }
+ PlaylistEntry;
+
+@@ -81,10 +82,15 @@
+ void playlist_delete_filenames(GList *filenames);
+ gchar* playlist_get_filename(gint pos);
+ gchar* playlist_get_songtitle(gint pos);
++gint playlist_get_score(gint pos);
++void playlist_set_score(gint pos, gint score);
++const gchar* playlist_filename2songtitle(const gchar *filename);
+ gint playlist_get_songtime(gint pos);
+ GList * playlist_get_selected(void);
+ GList * playlist_get_selected_list(void);
+ int playlist_get_num_selected(void);
++gboolean playlist_is_selected(gint pos);
++gint playlist_get_single_selection(void);
+ void playlist_get_total_time(gulong *total_time, gulong *selection_time, gboolean *total_more, gboolean *selection_more);
+ void playlist_select_all(gboolean set);
+ void playlist_select_range(int min, int max, gboolean sel);
+diff -ruN dtd-old/xmms/playlist_list.c dtd-new/xmms/playlist_list.c
+--- dtd-old/xmms/playlist_list.c 2003-09-08 13:31:40.000000000 +0200
++++ dtd-new/xmms/playlist_list.c 2003-09-08 13:46:19.000000000 +0200
+@@ -24,6 +24,8 @@
+ #endif
+ #include <X11/Xatom.h>
+
++static gint playlist_move_delta = 0;
++static PlaylistEntry *playlist_last_moved_song = NULL;
+ static GdkFont *playlist_list_font = NULL;
+
+ static int playlist_list_auto_drag_down_func(gpointer data)
+@@ -61,24 +63,35 @@
+ GList *list;
+
+ PL_LOCK();
++
++ /* If the first song is selected... */
+ if ((list = get_playlist()) == NULL)
+ {
+ PL_UNLOCK();
+ return;
+ }
++
+ if (((PlaylistEntry *) list->data)->selected)
+ {
+- /* We are at the top */
++ /* ... don't move. */
+ PL_UNLOCK();
+ return;
+ }
+- while (list)
++
++ while (list)
+ {
+- if (((PlaylistEntry *) list->data)->selected)
++ if (((PlaylistEntry *) list->data)->selected)
++ {
++ playlist_last_moved_song =
++ (PlaylistEntry *) list->data;
+ glist_moveup(list);
++ }
+ list = g_list_next(list);
+ }
+ PL_UNLOCK();
++
++ playlist_move_delta--;
++
+ if (pl->pl_prev_selected != -1)
+ pl->pl_prev_selected--;
+ if (pl->pl_prev_min != -1)
+@@ -92,24 +105,36 @@
+ GList *list;
+
+ PL_LOCK();
++
++ /* If the last song is selected... */
+ if ((list = g_list_last(get_playlist())) == NULL)
+ {
+ PL_UNLOCK();
+ return;
+ }
++
+ if (((PlaylistEntry *) list->data)->selected)
+ {
+- /* We are at the bottom */
++ /* ... don't move. */
+ PL_UNLOCK();
+ return;
+ }
++
+ while (list)
+ {
+ if (((PlaylistEntry *) list->data)->selected)
++ {
++ playlist_last_moved_song =
++ (PlaylistEntry *) list->data;
+ glist_movedown(list);
++ }
+ list = g_list_previous(list);
+ }
++
+ PL_UNLOCK();
++
++ playlist_move_delta++;
++
+ if (pl->pl_prev_selected != -1)
+ pl->pl_prev_selected++;
+ if (pl->pl_prev_min != -1)
+@@ -177,6 +202,7 @@
+ playlist_play();
+ }
+ pl->pl_dragging = TRUE;
++ playlist_move_delta = 0;
+ playlistwin_update_list();
+ }
+ }
+@@ -244,11 +270,36 @@
+ }
+ }
+
++int playlist_n_selected_songs(void)
++{
++ int n_selected = 0;
++ GList *list;
++
++ for (list = get_playlist();
++ list != NULL;
++ list = g_list_next(list))
++ {
++ if (((PlaylistEntry *) list->data)->selected)
++ n_selected++;
++ }
++
++ return n_selected;
++}
++
+ void playlist_list_button_release_cb(GtkWidget * widget, GdkEventButton * event, PlayList_List * pl)
+ {
+ pl->pl_dragging = FALSE;
+ pl->pl_auto_drag_down = FALSE;
+ pl->pl_auto_drag_up = FALSE;
++
++ if ((playlist_move_delta < 0) && (playlist_n_selected_songs() == 1))
++ {
++ /* Exactly one song has been moved upwards, mark it as moved. */
++ playlist_last_moved_song->moved = TRUE;
++ }
++
++ playlist_move_delta = 0;
++ playlist_last_moved_song = NULL;
+ }
+
+ #ifdef HAVE_WCHAR_H
+diff -ruN dtd-old/xmms/playlistwin.c dtd-new/xmms/playlistwin.c
+--- dtd-old/xmms/playlistwin.c 2003-09-08 13:31:40.000000000 +0200
++++ dtd-new/xmms/playlistwin.c 2003-09-08 14:16:39.000000000 +0200
+@@ -17,6 +17,7 @@
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
++#include <assert.h>
+ #include "xmms.h"
+ #include "libxmms/dirbrowser.h"
+ #include "libxmms/util.h"
+@@ -24,7 +25,7 @@
+ GtkWidget *playlistwin;
+ static GtkWidget *playlistwin_url_window = NULL;
+ static GtkItemFactory *playlistwin_sort_menu, *playlistwin_sub_menu;
+-static GtkItemFactory *playlistwin_popup_menu, *playlistwin_save_menu;
++static GtkItemFactory *playlistwin_popup_menu, *playlistwin_save_menu, *playlistwin_dtd_menu;
+
+ static GdkPixmap *playlistwin_bg;
+ static GdkBitmap *playlistwin_mask;
+@@ -68,13 +69,15 @@
+ SEL_INV, SEL_ZERO, SEL_ALL,
+ MISC_SORT, MISC_FILEINFO, MISC_MISCOPTS,
+ PLIST_NEW, PLIST_SAVE, PLIST_LOAD,
+- SEL_LOOKUP,
++ SEL_LOOKUP, DTD_GOODSONG, DTD_BADSONG,
++ DTD_DISSOCIATE
+ };
+
+ void playlistwin_sort_menu_callback(gpointer cb_data, guint action, GtkWidget * w);
+ void playlistwin_sub_menu_callback(gpointer cb_data, guint action, GtkWidget * w);
+ void playlistwin_save_type_cb(gpointer cb_data, guint action, GtkWidget * w);
+ void playlistwin_set_hints(void);
++void playlistwin_dissociate_callback(GtkMenuItem *menuitem, gpointer ignored);
+
+ enum
+ {
+@@ -151,7 +154,11 @@
+ GtkItemFactoryEntry playlistwin_popup_menu_entries[] =
+ {
+ {N_("/View File Info"), NULL, playlistwin_popup_menu_callback, MISC_FILEINFO, "<Item>"},
++
+ {N_("/-"), NULL, NULL, 0, "<Separator>"},
++
++ {N_("/Dynamic Taste Detection"), NULL, NULL, 0, "<Item>"},
++
+ {N_("/Add"), NULL, NULL, 0, "<Branch>"},
+ {N_("/Add/File"), NULL, playlistwin_popup_menu_callback, ADD_FILE, "<Item>"},
+ {N_("/Add/Directory"), NULL, playlistwin_popup_menu_callback, ADD_DIR, "<Item>"},
+@@ -182,6 +189,21 @@
+ sizeof(playlistwin_popup_menu_entries) /
+ sizeof(playlistwin_popup_menu_entries[0]);
+
++GtkItemFactoryEntry playlistwin_dtd_menu_entries[] =
++{
++ {N_("/I Like this Song"),
++ NULL, playlistwin_popup_menu_callback, DTD_GOODSONG,
++ "<RadioItem>"},
++ {N_("/I Don't Like this Song"),
++ NULL, playlistwin_popup_menu_callback, DTD_BADSONG,
++ /* <RadioItem> */ "/I Like this Song"},
++ {N_("/Dissociate From"), NULL, NULL, 0, "<Item>"}
++};
++
++static const int playlistwin_dtd_menu_entries_num =
++ sizeof(playlistwin_dtd_menu_entries) /
++ sizeof(playlistwin_dtd_menu_entries[0]);
++
+ void playlistwin_draw_frame(void);
+
+ static void playlistwin_update_info(void)
+@@ -1030,6 +1052,35 @@
+ case PLIST_LOAD:
+ playlistwin_show_load_filesel();
+ break;
++
++ /* DTD button */
++ case DTD_GOODSONG: {
++ gint score;
++ gint selected_playlist_pos = playlist_get_single_selection();
++
++ assert(selected_playlist_pos > -1);
++ score = playlist_get_score(selected_playlist_pos);
++ if (score < 0)
++ {
++ playlist_set_score(selected_playlist_pos, 0);
++ }
++
++ break;
++ }
++
++ case DTD_BADSONG: {
++ gint score;
++ gint selected_playlist_pos = playlist_get_single_selection();
++
++ assert(selected_playlist_pos > -1);
++ score = playlist_get_score(selected_playlist_pos);
++ if (score >= 0)
++ {
++ playlist_set_score(selected_playlist_pos, -1);
++ }
++
++ break;
++ }
+ }
+ }
+
+@@ -1053,6 +1104,133 @@
+ inside_widget(x, y, playlistwin_sscroll_down));
+ }
+
++gchar *playlistwin_fileName2songName(const gchar *fileName)
++{
++ const gchar *afterLastSlash, *beforeLastDot, *c;
++ gchar *returnMe;
++ gint i;
++ const gchar *loadedTitle =
++ playlist_filename2songtitle(fileName);
++
++ if (loadedTitle != NULL)
++ {
++ return g_strdup(loadedTitle);
++ }
++
++ // Ditch the last slash and everything before it, as well as
++ // the last dot and everything after it before returning it.
++ afterLastSlash = strrchr(fileName, '/');
++ if (afterLastSlash != NULL)
++ {
++ afterLastSlash++;
++ }
++ else
++ {
++ afterLastSlash = fileName;
++ }
++
++ beforeLastDot = strrchr(fileName, '.');
++ if ((beforeLastDot != NULL) && (beforeLastDot > afterLastSlash))
++ {
++ beforeLastDot--;
++ }
++ else
++ {
++ beforeLastDot = fileName + strlen(fileName) - 1;
++ }
++
++ if ((afterLastSlash >= beforeLastDot) ||
++ (beforeLastDot <= fileName) ||
++ ((afterLastSlash - fileName) >= strlen(fileName)))
++ {
++ return g_strdup(fileName);
++ }
++
++ // Copy from after last slash to before last dot
++ returnMe = g_new(gchar, (beforeLastDot - afterLastSlash) + 1);
++ i = 0;
++ for (c = afterLastSlash; c <= beforeLastDot; c++)
++ {
++ returnMe[i++] = *c;
++ }
++ returnMe[i] = '\0';
++
++ return returnMe;
++}
++
++void playlistwin_remove_dissociation_menu(GtkMenuItem *attachPoint)
++{
++ // Nuke any previous dissociations menu
++
++ // FIXME: I have absolutely no clue to how GTK+ refcounting
++ // works. This implementation may or may not leak memory.
++ // Candidates for memory leaks are the user_data fields of the
++ // labels (they should be freed).
++ // /Johan Walles - jan 05 / 2002
++ gtk_menu_item_remove_submenu(attachPoint);
++}
++
++void playlistwin_setup_dissociation_menu(GtkMenuItem *attachPoint,
++ gint selected_playlist_pos)
++{
++ const GList *recommendations = NULL;
++ const gchar *filename = playlist_get_filename(selected_playlist_pos);
++
++ // Ditch the previous dissociations menu (if any)
++ playlistwin_remove_dissociation_menu(attachPoint);
++
++ recommendations =
++ dtd_get_recommendations(filename);
++
++ if (recommendations != NULL)
++ {
++ // Create a new menu
++ GtkWidget *dissociateMenu = gtk_menu_new();
++ const GList *iterator;
++
++ // Associate it with the selected song
++ gtk_object_set_user_data(GTK_OBJECT(dissociateMenu), g_strdup(filename));
++
++ // For all recommendations...
++ for (iterator = recommendations;
++ iterator != NULL;
++ iterator = g_list_next(iterator))
++ {
++ const gchar *filename = ((DtdNextSong *)(iterator->data))->filename;
++ gchar *songName = playlistwin_fileName2songName(filename);
++ GtkWidget *newMenuItem;
++
++ // ... create a new menu entry...
++ newMenuItem = gtk_menu_item_new_with_label(songName);
++ g_free(songName);
++
++ // ... with its user_data set to point
++ // to the associated file name...
++ gtk_object_set_user_data(GTK_OBJECT(newMenuItem), g_strdup(filename));
++
++ // ... tell it what to do on receiving a click...
++ gtk_signal_connect(GTK_OBJECT(newMenuItem),
++ "activate",
++ GTK_SIGNAL_FUNC(playlistwin_dissociate_callback),
++ NULL);
++
++ // ... and add it to the new menu.
++ gtk_menu_append(GTK_MENU(dissociateMenu), newMenuItem);
++ }
++
++ // Enable our new dissociations menu
++ gtk_widget_show_all(dissociateMenu);
++ gtk_menu_item_set_submenu(attachPoint, dissociateMenu);
++ gtk_widget_set_sensitive(GTK_WIDGET(attachPoint), 1);
++ }
++ else
++ {
++ // ... or disable the dissociation menu heading
++ // if there is nothing to dissociate from.
++ gtk_widget_set_sensitive(GTK_WIDGET(attachPoint), 0);
++ }
++}
++
+ #define REGION_L(x1,x2,y1,y2) \
+ (event->x >= (x1) && event->x < (x2) && \
+ event->y >= cfg.playlist_height - (y1) && \
+@@ -1227,18 +1405,101 @@
+ else if (event->button == 3 &&
+ inside_widget(event->x, event->y, playlistwin_list))
+ {
+- int pos, sensitive;
++ // FIXME: This block is much too long, put it in its
++ // own function
++
++ gint clicked_playlist_pos, selected_playlist_pos;
++ gboolean sensitive;
++
+ GtkWidget *w;
+- pos = playlist_list_get_playlist_position(playlistwin_list,
+- event->x, event->y);
+- sensitive = pos != -1;
++
++ clicked_playlist_pos =
++ playlist_list_get_playlist_position(playlistwin_list,
++ event->x, event->y);
++
++ // Unless the clicked song is part of a (multiple)
++ // selection...
++ if (!playlist_is_selected(clicked_playlist_pos))
++ {
++ // ... select just the current song before
++ // displaying the menu
++ playlist_select_all(0);
++ playlist_select_range(clicked_playlist_pos, clicked_playlist_pos, 1);
++ playlistwin_update_list();
++ }
++
++ selected_playlist_pos = playlist_get_single_selection();
++ sensitive = (selected_playlist_pos != -1);
++
++ // Disable "View File Info" if not exactly one song is selected
+ w = gtk_item_factory_get_widget(playlistwin_popup_menu,
+ "/View File Info");
+ gtk_widget_set_sensitive(w, sensitive);
++
++ // Disable the DTD menu hierarchy if not exactly one
++ // song is selected
++ // FIXME: or if it's disabled in the prefs
++ w = gtk_item_factory_get_widget(playlistwin_popup_menu,
++ "/Dynamic Taste Detection");
++ gtk_widget_set_sensitive(w, sensitive);
++
++ if (sensitive)
++ {
++ GtkWidget *playlistwin_dtd_menu_widget =
++ gtk_item_factory_get_widget(playlistwin_dtd_menu, "<Main>");
++
++ gtk_widget_show_all(playlistwin_dtd_menu_widget);
++ gtk_menu_item_set_submenu(GTK_MENU_ITEM(w), playlistwin_dtd_menu_widget);
++ }
++ else
++ {
++ gtk_menu_item_remove_submenu(GTK_MENU_ITEM(w));
++ gtk_widget_hide_all(gtk_item_factory_get_widget(playlistwin_dtd_menu, "<Main>"));
++ }
++
++ if (selected_playlist_pos != -1)
++ {
++ // Enable either "/I Like this Song" (if the
++ // song scores >= 0) or "Bad Song" (if the
++ // song scores < 0).
++ gint score = dtd_get_score(playlist_get_filename(selected_playlist_pos));
++
++ w = gtk_item_factory_get_widget(playlistwin_dtd_menu,
++ "/I Like this Song");
++ if (score >= 0)
++ {
++ gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(w), 1);
++ }
++ gtk_widget_set_sensitive(w, 1);
++ w = gtk_item_factory_get_widget(playlistwin_dtd_menu,
++ "/I Don't Like this Song");
++ if (score < 0)
++ {
++ gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(w), 1);
++ }
++ gtk_widget_set_sensitive(w, 1);
++ }
++ else
++ {
++ // We don't do ratings of multiple songs at once
++ w = gtk_item_factory_get_widget(playlistwin_dtd_menu,
++ "/I Like this Song");
++ gtk_widget_set_sensitive(w, 0);
++ gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(w), 0);
++ w = gtk_item_factory_get_widget(playlistwin_dtd_menu,
++ "/I Don't Like this Song");
++ gtk_widget_set_sensitive(w, 0);
++ gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(w), 0);
++ }
+
++ // Create the dissociation menu
++ w = gtk_item_factory_get_widget(playlistwin_dtd_menu,
++ "/Dissociate From");
++ playlistwin_setup_dissociation_menu(GTK_MENU_ITEM(w), selected_playlist_pos);
++
+ playlistwin_set_sensitive_sortmenu();
+ util_item_factory_popup_with_data(playlistwin_popup_menu,
+- GINT_TO_POINTER(pos), NULL,
++ GINT_TO_POINTER(selected_playlist_pos), NULL,
+ event->x_root,
+ event->y_root + 5,
+ 3, event->time);
+@@ -1946,6 +2207,11 @@
+ cfg.playlist_height,
+ gdk_rgb_get_visual()->depth);
+
++ playlistwin_dtd_menu = gtk_item_factory_new(GTK_TYPE_MENU, "<Main>", NULL);
++ gtk_item_factory_create_items(GTK_ITEM_FACTORY(playlistwin_dtd_menu),
++ playlistwin_dtd_menu_entries_num,
++ playlistwin_dtd_menu_entries, NULL);
++
+ playlistwin_popup_menu =
+ gtk_item_factory_new(GTK_TYPE_MENU, "<Main>", NULL);
+ gtk_item_factory_set_translate_func(playlistwin_popup_menu,
+@@ -2022,6 +2288,23 @@
+ tbutton_set_toggled(mainwin_pl, FALSE);
+ }
+
++void playlistwin_dissociate_callback(GtkMenuItem *menuitem,
++ gpointer ignored)
++{
++ gchar *first, *second;
++
++ // Find out "first" by checking the user data of the menuitem
++ // that was activated
++ first = (gchar *)gtk_object_get_user_data(GTK_OBJECT(menuitem));
++
++ // Find out "second" by checking the user data of the menu
++ // that is the parent of the menuitem that was activated
++ second = (gchar *)gtk_object_get_user_data(GTK_OBJECT(GTK_WIDGET(menuitem)->parent));
++
++ dtd_dissociate(first, second);
++}
++
++
+ void playlistwin_popup_menu_callback(gpointer cb_data, guint action, GtkWidget * w)
+ {
+ int pos = GPOINTER_TO_INT(gtk_item_factory_popup_data_from_widget(w));
+@@ -2034,6 +2317,17 @@
+ case SEL_LOOKUP:
+ playlist_read_info_selection();
+ break;
++ case DTD_DISSOCIATE:
++
++ break;
++ case DTD_GOODSONG:
++ case DTD_BADSONG:
++ // Act only upon the active item
++ if (GTK_CHECK_MENU_ITEM(w)->active)
++ {
++ playlistwin_popup_handler(action);
++ }
++ break;
+ default:
+ playlistwin_popup_handler(action);
+ }
+diff -ruN dtd-old/xmms/prefswin.c dtd-new/xmms/prefswin.c
+--- dtd-old/xmms/prefswin.c 2003-09-08 13:31:40.000000000 +0200
++++ dtd-new/xmms/prefswin.c 2003-09-08 13:46:19.000000000 +0200
+@@ -1042,6 +1042,8 @@
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(prefswin_notebook), prefswin_options_vbox, gtk_label_new(_("Options")));
+
++ prefswin_option_new_with_label_to_table(&cfg.dynamic_taste_detection, _("Dynamic taste detection"), GTK_TABLE(options_table), 1, 10);
++
+ /*
+ * Fonts page
+ */
+diff -ruN dtd-old/xmms/xmms.h dtd-new/xmms/xmms.h
+--- dtd-old/xmms/xmms.h 2001-03-14 15:06:39.000000000 +0100
++++ dtd-new/xmms/xmms.h 2003-09-08 13:46:19.000000000 +0200
+@@ -81,6 +81,7 @@
+ #include "sm.h"
+ #include "dnd.h"
+ #include "urldecode.h"
++#include "dtd.h"
+
+ #include "config.h"
+
diff --git a/media-sound/xmms/files/xmms-1.2.8-jump.patch b/media-sound/xmms/files/xmms-1.2.8-jump.patch
new file mode 100644
index 000000000000..5ebd59b3d08a
--- /dev/null
+++ b/media-sound/xmms/files/xmms-1.2.8-jump.patch
@@ -0,0 +1,76 @@
+diff -Naur xmms-1.2.8/libxmms/xmmsctrl.c xmms-1.2.8-new/libxmms/xmmsctrl.c
+--- xmms-1.2.8/libxmms/xmmsctrl.c 2003-09-04 15:48:34.000000000 +0200
++++ xmms-1.2.8-new/libxmms/xmmsctrl.c 2003-09-06 18:18:53.000000000 +0200
+@@ -381,6 +381,11 @@
+ remote_cmd(session, CMD_PLAY_PAUSE);
+ }
+
++void xmms_remote_show_jump_box(gint session)
++{
++ remote_cmd(session, CMD_SHOW_JUMP_BOX);
++}
++
+ gboolean xmms_remote_is_playing(gint session)
+ {
+ return remote_get_gboolean(session, CMD_IS_PLAYING);
+diff -Naur xmms-1.2.8/libxmms/xmmsctrl.h xmms-1.2.8-new/libxmms/xmmsctrl.h
+--- xmms-1.2.8/libxmms/xmmsctrl.h 2003-06-09 15:22:10.000000000 +0200
++++ xmms-1.2.8-new/libxmms/xmmsctrl.h 2003-09-06 18:18:53.000000000 +0200
+@@ -61,6 +61,7 @@
+ gboolean xmms_remote_is_pl_win(gint session);
+ gboolean xmms_remote_is_eq_win(gint session);
+ void xmms_remote_show_prefs_box(gint session);
++void xmms_remote_show_jump_box(gint session);
+ void xmms_remote_toggle_aot(gint session, gboolean ontop);
+ void xmms_remote_eject(gint session);
+ void xmms_remote_playlist_prev(gint session);
+diff -Naur xmms-1.2.8/xmms/controlsocket.c xmms-1.2.8-new/xmms/controlsocket.c
+--- xmms-1.2.8/xmms/controlsocket.c 2003-09-04 15:48:34.000000000 +0200
++++ xmms-1.2.8-new/xmms/controlsocket.c 2003-09-06 18:18:53.000000000 +0200
+@@ -506,6 +506,9 @@
+ case CMD_SET_SKIN:
+ load_skin(data);
+ break;
++ case CMD_SHOW_JUMP_BOX:
++ show_jump_to_file();
++ break;
+ case CMD_PL_WIN_TOGGLE:
+ tbool = *((gboolean *) data);
+ playlistwin_show(!!tbool);
+diff -Naur xmms-1.2.8/xmms/controlsocket.h xmms-1.2.8-new/xmms/controlsocket.h
+--- xmms-1.2.8/xmms/controlsocket.h 2003-06-09 15:22:10.000000000 +0200
++++ xmms-1.2.8-new/xmms/controlsocket.h 2003-09-06 18:18:53.000000000 +0200
+@@ -46,6 +46,7 @@
+ CMD_GET_EQ, CMD_GET_EQ_PREAMP, CMD_GET_EQ_BAND,
+ CMD_SET_EQ, CMD_SET_EQ_PREAMP, CMD_SET_EQ_BAND,
+ CMD_QUIT, CMD_PLAYLIST_INS_URL_STRING, CMD_PLAYLIST_INS, CMD_PLAY_PAUSE,
++ CMD_SHOW_JUMP_BOX
+ };
+
+ typedef struct
+diff -Naur xmms-1.2.8/xmms/main.c xmms-1.2.8-new/xmms/main.c
+--- xmms-1.2.8/xmms/main.c 2003-09-02 15:01:40.000000000 +0200
++++ xmms-1.2.8-new/xmms/main.c 2003-09-06 18:18:53.000000000 +0200
+@@ -1847,6 +1847,11 @@
+ GTK_CLIST(clist)->focus_row = GPOINTER_TO_INT(GTK_CLIST(clist)->selection->data);
+ }
+
++void show_jump_to_file(void)
++{
++ mainwin_jump_to_file();
++}
++
+ static gboolean mainwin_configure(GtkWidget * window, GdkEventConfigure *event, gpointer data)
+ {
+ if (!GTK_WIDGET_VISIBLE(window))
+diff -Naur xmms-1.2.8/xmms/main.h xmms-1.2.8-new/xmms/main.h
+--- xmms-1.2.8/xmms/main.h 2002-10-06 18:35:27.000000000 +0200
++++ xmms-1.2.8-new/xmms/main.h 2003-09-06 18:18:53.000000000 +0200
+@@ -117,6 +117,7 @@
+ void mainwin_set_shade_menu_cb(gboolean shaded);
+ void mainwin_set_shade(gboolean shaded);
+ void mainwin_shade_toggle(void);
++void show_jump_to_file(void);
+
+ #define PLAYER_HEIGHT ((cfg.player_shaded ? 14 : 116) * (cfg.doublesize + 1))
+ #define PLAYER_WIDTH (275 * (cfg.doublesize + 1))
diff --git a/media-sound/xmms/files/xmms-1.2.8-russian-charset.patch b/media-sound/xmms/files/xmms-1.2.8-russian-charset.patch
new file mode 100644
index 000000000000..57ffb8612f89
--- /dev/null
+++ b/media-sound/xmms/files/xmms-1.2.8-russian-charset.patch
@@ -0,0 +1,2174 @@
+diff -Naur xmms-1.2.8/Input/mpg123/fileinfo.c xmms-1.2.8-new/Input/mpg123/fileinfo.c
+--- xmms-1.2.8/Input/mpg123/fileinfo.c 2003-07-14 15:24:28.000000000 +0200
++++ xmms-1.2.8-new/Input/mpg123/fileinfo.c 2003-09-06 19:42:29.000000000 +0200
+@@ -26,6 +26,10 @@
+ #include <libxmms/xentry.h>
+ #include "mpg123.h"
+
++#include "libxmms/charset.h"
++#include <gdk/gdkkeysyms.h>
++#include "xmms/xmms.h"
++
+ static GtkWidget *window = NULL;
+ static GtkWidget *filename_entry, *id3_frame;
+ static GtkWidget *title_entry, *artist_entry, *album_entry, *year_entry, *tracknum_entry, *comment_entry;
+@@ -46,20 +50,35 @@
+ {
+ gint stripped_len;
+ gchar *text;
++ gchar *ctag;
++ gint clength;
+
++ ctag=xmms_charset_recode_id3(tag,length,&clength);
++ if (ctag) {
++ tag=ctag;
++ length=clength;
++ }
++
+ stripped_len = mpg123_strip_spaces(tag, length);
+ text = g_strdup_printf("%-*.*s", stripped_len, stripped_len, tag);
+ gtk_entry_set_text(entry, text);
+ g_free(text);
++ if (ctag) g_free(ctag);
+ }
+
+ static void get_entry_tag(GtkEntry * entry, gchar * tag, gint length)
+ {
+ gchar *text;
++ gchar *ctext;
+
+ text = gtk_entry_get_text(entry);
++ ctext = xmms_charset_recode_output(text,length,NULL);
++ if (ctext) text=ctext;
++
+ memset(tag, ' ', length);
+ memcpy(tag, text, strlen(text) > length ? length : strlen(text));
++
++ if (ctext) g_free(ctext);
+ }
+
+ static gint find_genre_id(gchar * text)
+@@ -225,6 +244,31 @@
+ }
+ }
+
++static int restore_focus;
++
++gboolean mpg123_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);
++// playlistwin_real_hide();
++// playlistwin_real_show();
++ }
++}
++
+ void mpg123_file_info_box(char *filename)
+ {
+ gint i;
+@@ -233,7 +277,10 @@
+ gchar *tmp, *title;
+ const gchar *emphasis[4];
+ const gchar *bool_label[2];
++ gchar *cfilename;
+
++ restore_focus=0;
++
+ emphasis[0] = _("None");
+ emphasis[1] = _("50/15 ms");
+ emphasis[2] = "";
+@@ -247,10 +294,11 @@
+ GtkWidget *mpeg_frame, *mpeg_box;
+ GtkWidget *label, *filename_hbox;
+ GtkWidget *bbox, *save, *remove_id3, *cancel;
++ 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_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed_focus), &window);
+ gtk_container_set_border_width(GTK_CONTAINER(window), 10);
+
+ vbox = gtk_vbox_new(FALSE, 10);
+@@ -392,6 +440,18 @@
+ gtk_label_set_justify(GTK_LABEL(mpeg_fileinfo), GTK_JUSTIFY_LEFT);
+ gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_fileinfo, FALSE, FALSE, 0);
+
++ ag = gtk_accel_group_new();
++ gtk_accel_group_add(ag, GDK_Escape, 0, 0, GTK_OBJECT(cancel), "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(title_entry), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), save);
++ gtk_signal_connect(GTK_OBJECT(artist_entry), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), save);
++ gtk_signal_connect(GTK_OBJECT(album_entry), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), save);
++ gtk_signal_connect(GTK_OBJECT(comment_entry), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), save);
++ gtk_signal_connect(GTK_OBJECT(year_entry), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), save);
++ gtk_signal_connect(GTK_OBJECT(tracknum_entry), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), save);*/
++ gtk_signal_connect(GTK_OBJECT(window),"key_press_event",GTK_SIGNAL_FUNC(mpg123_keypress_cb),save);
++
+ gtk_widget_show_all(window);
+ }
+
+@@ -399,6 +459,9 @@
+ g_free(current_filename);
+ current_filename = g_strdup(filename);
+
++ cfilename=xmms_charset_recode_fsout(filename,0,NULL);
++ 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);
+@@ -412,6 +475,8 @@
+ gtk_entry_set_text(GTK_ENTRY(title_entry), title);
+ g_free(title);
+
++ if (cfilename) g_free(cfilename);
++
+ gtk_entry_set_text(GTK_ENTRY(artist_entry), "");
+ gtk_entry_set_text(GTK_ENTRY(album_entry), "");
+ gtk_entry_set_text(GTK_ENTRY(year_entry), "");
+diff -Naur xmms-1.2.8/Input/mpg123/mpg123.c xmms-1.2.8-new/Input/mpg123/mpg123.c
+--- xmms-1.2.8/Input/mpg123/mpg123.c 2003-07-10 15:12:19.000000000 +0200
++++ xmms-1.2.8-new/Input/mpg123/mpg123.c 2003-09-06 19:42:29.000000000 +0200
+@@ -551,6 +551,7 @@
+ {
+ gchar *ret = NULL, *path, *temp;
+ TitleInput *input;
++ gint len;
+
+ XMMS_NEW_TITLEINPUT(input);
+
+@@ -582,9 +583,18 @@
+ /*
+ * Format according to filename.
+ */
++ ret = xmms_charset_recode_fs(g_basename(filename),0,&len);
++ if (ret) {
++ for (;len>=0;len--)
++ if (ret[len]=='.') {
++ ret[len]=0;
++ break;
++ }
++ } else {
+ ret = g_strdup(g_basename(filename));
+ if (extname(ret) != NULL)
+ *(extname(ret) - 1) = '\0'; /* removes period */
++ }
+ }
+
+ return ret;
+diff -Naur xmms-1.2.8/Input/vorbis/vorbis.c xmms-1.2.8-new/Input/vorbis/vorbis.c
+--- xmms-1.2.8/Input/vorbis/vorbis.c 2003-07-14 15:24:28.000000000 +0200
++++ xmms-1.2.8-new/Input/vorbis/vorbis.c 2003-09-06 19:42:29.000000000 +0200
+@@ -47,6 +47,7 @@
+ #include "libxmms/util.h"
+ #include "libxmms/configfile.h"
+ #include "libxmms/titlestring.h"
++#include "libxmms/charset.h"
+ #include <xmms/i18n.h>
+
+ #include "vorbis.h"
+@@ -668,6 +669,7 @@
+ gchar *displaytitle = NULL, *tmp, *path, *temp;
+ vorbis_comment *comment;
+ TitleInput *input;
++ gint len;
+
+ XMMS_NEW_TITLEINPUT(input);
+ path = g_strdup(fn);
+@@ -705,10 +707,20 @@
+
+ if (!displaytitle)
+ {
+- if (!vorbis_is_streaming)
+- displaytitle = g_strdup_printf("%s", g_basename(fn));
+- else
++ if (!vorbis_is_streaming) {
++ displaytitle = xmms_charset_recode_fs(g_basename(fn),0,&len);
++ if (displaytitle) {
++ for (;len>=0;len--)
++ if (displaytitle[len]=='.') {
++ displaytitle[len]=0;
++ break;
++ }
++ } else {
++ displaytitle = g_strdup_printf("%s", g_basename(fn));
++ }
++ } else {
+ displaytitle = vorbis_http_get_title(fn);
++ }
+ }
+
+ return displaytitle;
+diff -Naur xmms-1.2.8/libxmms/charset_auto_russian.h xmms-1.2.8-new/libxmms/charset_auto_russian.h
+--- xmms-1.2.8/libxmms/charset_auto_russian.h 1970-01-01 01:00:00.000000000 +0100
++++ xmms-1.2.8-new/libxmms/charset_auto_russian.h 2003-09-06 19:42:29.000000000 +0200
+@@ -0,0 +1,293 @@
++/* Stealed from GPPL's (http://gppl.terminal.ru) autorecode-xmms1.2.7 patch,
++ changed to support limited size strings */
++
++typedef struct lng_stat {
++ unsigned char a;
++ unsigned char b;
++ int rate;
++} lng_stat;
++
++//statistics calculated from "Moskva-Petushki"
++//1292 elems
++static const lng_stat win[]={
++{'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',8}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',4}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1},
++{'�','�',1}, {'�','�',1}, {'�','�',6}, {'�','�',60}, {'�','�',5}, {'�','�',7}, {'�','�',23}, {'�','�',2}, {'�','�',6}, {'�','�',4}, {'�','�',1}, {'�','�',27}, {'�','�',15}, {'�','�',10}, {'�','�',25}, {'�','�',21},
++{'�','�',51}, {'�','�',12}, {'�','�',13}, {'�','�',31}, {'�','�',15}, {'�','�',1}, {'�','�',6}, {'�','�',17}, {'�','�',8}, {'�','�',20}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',3}, {'�','�',2},
++{'�','�',16}, {'�','�',1}, {'�','�',4}, {'�','�',3}, {'�','�',25}, {'�','�',10}, {'�','�',9}, {'�','�',8}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',3}, {'�','�',8}, {'�','�',2}, {'�','�',1}, {'�','�',2},
++{'�','�',15}, {'�','�',2}, {'�','�',3}, {'�','�',5}, {'�','�',98}, {'�','�',5}, {'�','�',7}, {'�','�',5}, {'�','�',8}, {'�','�',7}, {'�','�',6}, {'�','�',107}, {'�','�',18}, {'�','�',2}, {'�','�',74}, {'�','�',6},
++{'�','�',1}, {'�','�',3}, {'�','�',39}, {'�','�',8}, {'�','�',2}, {'�','�',6}, {'�','�',16}, {'�','�',26}, {'�','�',4}, {'�','�',3}, {'�','�',1}, {'�','�',31}, {'�','�',7}, {'�','�',2}, {'�','�',167}, {'�','�',6},
++{'�','�',17}, {'�','�',3}, {'�','�',7}, {'�','�',3}, {'�','�',1}, {'�','�',20}, {'�','�',9}, {'�','�',19}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',9}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',5},
++{'�','�',2}, {'�','�',19}, {'�','�',69}, {'�','�',1}, {'�','�',1}, {'�','�',5}, {'�','�',5}, {'�','�',1}, {'�','�',14}, {'�','�',5}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',31}, {'�','�',3}, {'�','�',1},
++{'�','�',2}, {'�','�',23}, {'�','�',2}, {'�','�',12}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',2}, {'�','�',2}, {'�','�',8}, {'�','�',78}, {'�','�',7}, {'�','�',20}, {'�','�',7}, {'�','�',1},
++{'�','�',17}, {'�','�',3}, {'�','�',27}, {'�','�',15}, {'�','�',11}, {'�','�',21}, {'�','�',26}, {'�','�',21}, {'�','�',4}, {'�','�',21}, {'�','�',37}, {'�','�',5}, {'�','�',2}, {'�','�',3}, {'�','�',8}, {'�','�',1},
++{'�','�',2}, {'�','�',12}, {'�','�',3}, {'�','�',100}, {'�','�',4}, {'�','�',1}, {'�','�',14}, {'�','�',4}, {'�','�',62}, {'�','�',2}, {'�','�',24}, {'�','�',10}, {'�','�',62}, {'�','�',1}, {'�','�',1}, {'�','�',2},
++{'�','�',7}, {'�','�',11}, {'�','�',4}, {'�','�',12}, {'�','�',15}, {'�','�',3}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',31}, {'�','�',3}, {'�','�',1}, {'�','�',10}, {'�','�',44},
++{'�','�',1}, {'�','�',19}, {'�','�',93}, {'�','�',1}, {'�','�',6}, {'�','�',29}, {'�','�',2}, {'�','�',1}, {'�','�',3}, {'�','�',56}, {'�','�',111}, {'�','�',47}, {'�','�',2}, {'�','�',98}, {'�','�',100}, {'�','�',1},
++{'�','�',2}, {'�','�',6}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',18}, {'�','�',1}, {'�','�',4}, {'�','�',20}, {'�','�',2}, {'�','�',2}, {'�','�',7}, {'�','�',9}, {'�','�',2}, {'�','�',96},
++{'�','�',5}, {'�','�',17}, {'�','�',16}, {'�','�',21}, {'�','�',35}, {'�','�',13}, {'�','�',1}, {'�','�',6}, {'�','�',3}, {'�','�',18}, {'�','�',148}, {'�','�',11}, {'�','�',9}, {'�','�',186}, {'�','�',43}, {'�','�',38},
++{'�','�',4}, {'�','�',1}, {'�','�',22}, {'�','�',13}, {'�','�',5}, {'�','�',13}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',3}, {'�','�',19}, {'�','�',1}, {'�','�',4}, {'�','�',1}, {'�','�',56},
++{'�','�',16}, {'�','�',22}, {'�','�',11}, {'�','�',12}, {'�','�',6}, {'�','�',26}, {'�','�',6}, {'�','�',6}, {'�','�',3}, {'�','�',35}, {'�','�',4}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',1},
++{'�','�',4}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',79}, {'�','�',1}, {'�','�',18}, {'�','�',17}, {'�','�',34}, {'�','�',11}, {'�','�',31}, {'�','�',55}, {'�','�',2}, {'�','�',3}, {'�','�',2}, {'�','�',3},
++{'�','�',1}, {'�','�',3}, {'�','�',2}, {'�','�',4}, {'�','�',4}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',9}, {'�','�',5}, {'�','�',1}, {'�','�',1}, {'�','�',9}, {'�','�',8}, {'�','�',2},
++{'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',7}, {'�','�',2}, {'�','�',1}, {'�','�',16}, {'�','�',2}, {'�','�',1}, {'�','�',8}, {'�','�',1}, {'�','�',3}, {'�','�',20}, {'�','�',14}, {'�','�',7}, {'�','�',3},
++{'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',46}, {'�','�',60}, {'�','�',8}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',7}, {'�','�',8}, {'�','�',8}, {'�','�',7}, {'�','�',1}, {'�','�',1}, {'�','�',5},
++{'�','�',1}, {'�','�',1}, {'�','�',4}, {'�','�',1}, {'�','�',10}, {'�','�',1}, {'�','�',61}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',4}, {'�','�',49}, {'�','�',5}, {'�','�',8}, {'�','�',9},
++{'�','�',2}, {'�','�',12}, {'�','�',4}, {'�','�',10}, {'�','�',5}, {'�','�',6}, {'�','�',28}, {'�','�',24}, {'�','�',39}, {'�','�',4}, {'�','�',30}, {'�','�',7}, {'�','�',5}, {'�','�',3}, {'�','�',4}, {'�','�',3},
++{'�','�',3}, {'�','�',56}, {'�','�',18}, {'�','�',48}, {'�','�',9}, {'�','�',26}, {'�','�',12}, {'�','�',7}, {'�','�',11}, {'�','�',53}, {'�','�',59}, {'�','�',9}, {'�','�',21}, {'�','�',32}, {'�','�',27}, {'�','�',52},
++{'�','�',7}, {'�','�',21}, {'�','�',42}, {'�','�',10}, {'�','�',1}, {'�','�',4}, {'�','�',16}, {'�','�',2}, {'�','�',7}, {'�','�',22}, {'�','�',69}, {'�','�',257}, {'�','�',701}, {'�','�',218}, {'�','�',548}, {'�','�',328},
++{'�','�',262}, {'�','�',626}, {'�','�',225}, {'�','�',194}, {'�','�',1285}, {'�','�',1288}, {'�','�',771}, {'�','�',911}, {'�','�',166}, {'�','�',421}, {'�','�',368}, {'�','�',802}, {'�','�',874}, {'�','�',98}, {'�','�',38}, {'�','�',228},
++{'�','�',24}, {'�','�',300}, {'�','�',121}, {'�','�',27}, {'�','�',63}, {'�','�',238}, {'�','�',384}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',199}, {'�','�',6}, {'�','�',13}, {'�','�',6}, {'�','�',420}, {'�','�',2},
++{'�','�',142}, {'�','�',7}, {'�','�',137}, {'�','�',7}, {'�','�',31}, {'�','�',327}, {'�','�',4}, {'�','�',172}, {'�','�',18}, {'�','�',1}, {'�','�',288}, {'�','�',11}, {'�','�',3}, {'�','�',2}, {'�','�',1}, {'�','�',32},
++{'�','�',561}, {'�','�',10}, {'�','�',7}, {'�','�',1}, {'�','�',134}, {'�','�',11}, {'�','�',1}, {'�','�',6}, {'�','�',7}, {'�','�',5}, {'�','�',5}, {'�','�',6}, {'�','�',14}, {'�','�',2}, {'�','�',9}, {'�','�',13},
++{'�','�',17}, {'�','�',51}, {'�','�',7}, {'�','�',18}, {'�','�',4}, {'�','�',2}, {'�','�',8}, {'�','�',1}, {'�','�',2}, {'�','�',7}, {'�','�',2}, {'�','�',4}, {'�','�',989}, {'�','�',26}, {'�','�',62}, {'�','�',43},
++{'�','�',74}, {'�','�',911}, {'�','�',26}, {'�','�',88}, {'�','�',387}, {'�','�',1}, {'�','�',105}, {'�','�',122}, {'�','�',65}, {'�','�',231}, {'�','�',1220}, {'�','�',118}, {'�','�',97}, {'�','�',851}, {'�','�',123}, {'�','�',116},
++{'�','�',1}, {'�','�',9}, {'�','�',11}, {'�','�',47}, {'�','�',53}, {'�','�',2}, {'�','�',518}, {'�','�',33}, {'�','�',44}, {'�','�',3}, {'�','�',48}, {'�','�',3}, {'�','�',1}, {'�','�',3}, {'�','�',1}, {'�','�',3},
++{'�','�',3}, {'�','�',4}, {'�','�',1}, {'�','�',4}, {'�','�',3}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',210}, {'�','�',18}, {'�','�',6}, {'�','�',3}, {'�','�',191},
++{'�','�',83}, {'�','�',2}, {'�','�',103}, {'�','�',20}, {'�','�',205}, {'�','�',3}, {'�','�',62}, {'�','�',1340}, {'�','�',7}, {'�','�',180}, {'�','�',13}, {'�','�',4}, {'�','�',88}, {'�','�',1}, {'�','�',15}, {'�','�',1},
++{'�','�',4}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',795},
++{'�','�',7}, {'�','�',165}, {'�','�',6}, {'�','�',14}, {'�','�',839}, {'�','�',8}, {'�','�',7}, {'�','�',487}, {'�','�',1}, {'�','�',79}, {'�','�',78}, {'�','�',10}, {'�','�',289}, {'�','�',613}, {'�','�',17}, {'�','�',195},
++{'�','�',62}, {'�','�',41}, {'�','�',417}, {'�','�',1}, {'�','�',11}, {'�','�',138}, {'�','�',6}, {'�','�',111}, {'�','�',200}, {'�','�',6}, {'�','�',68}, {'�','�',30}, {'�','�',8}, {'�','�',33}, {'�','�',7}, {'�','�',34},
++{'�','�',13}, {'�','�',1}, {'�','�',6}, {'�','�',21}, {'�','�',21}, {'�','�',4}, {'�','�',13}, {'�','�',20}, {'�','�',21}, {'�','�',31}, {'�','�',2}, {'�','�',13}, {'�','�',10}, {'�','�',7}, {'�','�',4}, {'�','�',2},
++{'�','�',1}, {'�','�',9}, {'�','�',2}, {'�','�',11}, {'�','�',1}, {'�','�',25}, {'�','�',44}, {'�','�',463}, {'�','�',704}, {'�','�',592}, {'�','�',613}, {'�','�',281}, {'�','�',136}, {'�','�',444}, {'�','�',177}, {'�','�',297},
++{'�','�',398}, {'�','�',926}, {'�','�',982}, {'�','�',1512}, {'�','�',206}, {'�','�',524}, {'�','�',1231}, {'�','�',1082}, {'�','�',1607}, {'�','�',103}, {'�','�',15}, {'�','�',182}, {'�','�',66}, {'�','�',308}, {'�','�',235}, {'�','�',137},
++{'�','�',79}, {'�','�',24}, {'�','�',87}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',187}, {'�','�',4}, {'�','�',9}, {'�','�',1}, {'�','�',145}, {'�','�',690}, {'�','�',4}, {'�','�',1},
++{'�','�',214}, {'�','�',35}, {'�','�',1}, {'�','�',8}, {'�','�',96}, {'�','�',13}, {'�','�',14}, {'�','�',7}, {'�','�',4}, {'�','�',8}, {'�','�',69}, {'�','�',1}, {'�','�',1}, {'�','�',6}, {'�','�',2}, {'�','�',7},
++{'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',4}, {'�','�',3}, {'�','�',1}, {'�','�',8}, {'�','�',4}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',889}, {'�','�',39},
++{'�','�',147}, {'�','�',67}, {'�','�',174}, {'�','�',56}, {'�','�',18}, {'�','�',11}, {'�','�',113}, {'�','�',16}, {'�','�',33}, {'�','�',64}, {'�','�',302}, {'�','�',109}, {'�','�',31}, {'�','�',53}, {'�','�',21}, {'�','�',7},
++{'�','�',109}, {'�','�',3}, {'�','�',11}, {'�','�',4}, {'�','�',71}, {'�','�',20}, {'�','�',4}, {'�','�',43}, {'�','�',39}, {'�','�',10}, {'�','�',46}, {'�','�',5}, {'�','�',25}, {'�','�',11}, {'�','�',2}, {'�','�',1},
++{'�','�',30}, {'�','�',24}, {'�','�',5}, {'�','�',36}, {'�','�',39}, {'�','�',25}, {'�','�',49}, {'�','�',3}, {'�','�',22}, {'�','�',25}, {'�','�',8}, {'�','�',3}, {'�','�',4}, {'�','�',2}, {'�','�',4}, {'�','�',3},
++{'�','�',7}, {'�','�',28}, {'�','�',90}, {'�','�',242}, {'�','�',658}, {'�','�',153}, {'�','�',435}, {'�','�',280}, {'�','�',87}, {'�','�',388}, {'�','�',301}, {'�','�',216}, {'�','�',492}, {'�','�',888}, {'�','�',573}, {'�','�',958},
++{'�','�',216}, {'�','�',335}, {'�','�',275}, {'�','�',668}, {'�','�',949}, {'�','�',108}, {'�','�',17}, {'�','�',324}, {'�','�',123}, {'�','�',363}, {'�','�',134}, {'�','�',36}, {'�','�',39}, {'�','�',22}, {'�','�',234}, {'�','�',20},
++{'�','�',6}, {'�','�',20}, {'�','�',6}, {'�','�',16}, {'�','�',6}, {'�','�',2}, {'�','�',22}, {'�','�',18}, {'�','�',2}, {'�','�',12}, {'�','�',13}, {'�','�',15}, {'�','�',13}, {'�','�',7}, {'�','�',9}, {'�','�',15},
++{'�','�',3}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',7}, {'�','�',8}, {'�','�',21}, {'�','�',36}, {'�','�',112}, {'�','�',26}, {'�','�',100}, {'�','�',15}, {'�','�',21}, {'�','�',30}, {'�','�',88}, {'�','�',1},
++{'�','�',87}, {'�','�',25}, {'�','�',47}, {'�','�',117}, {'�','�',55}, {'�','�',133}, {'�','�',42}, {'�','�',162}, {'�','�',131}, {'�','�',17}, {'�','�',6}, {'�','�',7}, {'�','�',11}, {'�','�',69}, {'�','�',20}, {'�','�',2},
++{'�','�',15}, {'�','�',16}, {'�','�',11}, {'�','�',13}, {'�','�',7}, {'�','�',4}, {'�','�',2}, {'�','�',1}, {'�','�',5}, {'�','�',8}, {'�','�',9}, {'�','�',2}, {'�','�',5}, {'�','�',20}, {'�','�',7}, {'�','�',9},
++{'�','�',7}, {'�','�',9}, {'�','�',4}, {'�','�',1}, {'�','�',1}, {'�','�',4}, {'�','�',2}, {'�','�',8}, {'�','�',1358}, {'�','�',49}, {'�','�',147}, {'�','�',15}, {'�','�',48}, {'�','�',93}, {'�','�',22}, {'�','�',55},
++{'�','�',678}, {'�','�',28}, {'�','�',81}, {'�','�',22}, {'�','�',151}, {'�','�',1337}, {'�','�',64}, {'�','�',273}, {'�','�',131}, {'�','�',238}, {'�','�',384}, {'�','�',7}, {'�','�',10}, {'�','�',5}, {'�','�',31}, {'�','�',2},
++{'�','�',2}, {'�','�',13}, {'�','�',25}, {'�','�',20}, {'�','�',2}, {'�','�',25}, {'�','�',5}, {'�','�',14}, {'�','�',3}, {'�','�',1}, {'�','�',19}, {'�','�',12}, {'�','�',2}, {'�','�',14}, {'�','�',31}, {'�','�',20},
++{'�','�',20}, {'�','�',1}, {'�','�',20}, {'�','�',10}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',4}, {'�','�',19}, {'�','�',758}, {'�','�',34}, {'�','�',102}, {'�','�',35}, {'�','�',55},
++{'�','�',675}, {'�','�',33}, {'�','�',23}, {'�','�',1300}, {'�','�',1}, {'�','�',114}, {'�','�',21}, {'�','�',45}, {'�','�',111}, {'�','�',947}, {'�','�',83}, {'�','�',18}, {'�','�',320}, {'�','�',61}, {'�','�',264}, {'�','�',5},
++{'�','�',7}, {'�','�',2}, {'�','�',66}, {'�','�',1}, {'�','�',146}, {'�','�',642}, {'�','�',21}, {'�','�',159}, {'�','�',299}, {'�','�',11}, {'�','�',2}, {'�','�',14}, {'�','�',8}, {'�','�',10}, {'�','�',1}, {'�','�',5},
++{'�','�',12}, {'�','�',10}, {'�','�',6}, {'�','�',23}, {'�','�',12}, {'�','�',12}, {'�','�',1}, {'�','�',9}, {'�','�',16}, {'�','�',2}, {'�','�',2}, {'�','�',3}, {'�','�',1}, {'�','�',1}, {'�','�',10}, {'�','�',447},
++{'�','�',69}, {'�','�',119}, {'�','�',33}, {'�','�',41}, {'�','�',838}, {'�','�',27}, {'�','�',38}, {'�','�',484}, {'�','�',2}, {'�','�',71}, {'�','�',74}, {'�','�',96}, {'�','�',497}, {'�','�',761}, {'�','�',132}, {'�','�',41},
++{'�','�',137}, {'�','�',74}, {'�','�',411}, {'�','�',5}, {'�','�',13}, {'�','�',4}, {'�','�',46}, {'�','�',3}, {'�','�',3}, {'�','�',207}, {'�','�',33}, {'�','�',17}, {'�','�',82}, {'�','�',11}, {'�','�',5}, {'�','�',8},
++{'�','�',3}, {'�','�',4}, {'�','�',7}, {'�','�',10}, {'�','�',1}, {'�','�',2}, {'�','�',6}, {'�','�',2}, {'�','�',9}, {'�','�',2}, {'�','�',2}, {'�','�',6}, {'�','�',1}, {'�','�',1}, {'�','�',6}, {'�','�',1},
++{'�','�',2}, {'�','�',2}, {'�','�',1706}, {'�','�',28}, {'�','�',55}, {'�','�',48}, {'�','�',97}, {'�','�',1870}, {'�','�',5}, {'�','�',20}, {'�','�',1319}, {'�','�',84}, {'�','�',7}, {'�','�',11}, {'�','�',331}, {'�','�',1483},
++{'�','�',54}, {'�','�',27}, {'�','�',133}, {'�','�',165}, {'�','�',480}, {'�','�',14}, {'�','�',4}, {'�','�',65}, {'�','�',55}, {'�','�',3}, {'�','�',31}, {'�','�',385}, {'�','�',196}, {'�','�',7}, {'�','�',30}, {'�','�',396},
++{'�','�',34}, {'�','�',5}, {'�','�',44}, {'�','�',9}, {'�','�',16}, {'�','�',16}, {'�','�',4}, {'�','�',17}, {'�','�',43}, {'�','�',17}, {'�','�',3}, {'�','�',23}, {'�','�',49}, {'�','�',23}, {'�','�',40}, {'�','�',6},
++{'�','�',14}, {'�','�',19}, {'�','�',4}, {'�','�',7}, {'�','�',4}, {'�','�',2}, {'�','�',12}, {'�','�',5}, {'�','�',27}, {'�','�',34}, {'�','�',652}, {'�','�',1590}, {'�','�',915}, {'�','�',946}, {'�','�',457}, {'�','�',437},
++{'�','�',279}, {'�','�',291}, {'�','�',602}, {'�','�',498}, {'�','�',923}, {'�','�',1073}, {'�','�',1281}, {'�','�',241}, {'�','�',588}, {'�','�',921}, {'�','�',1264}, {'�','�',1536}, {'�','�',152}, {'�','�',35}, {'�','�',137}, {'�','�',51},
++{'�','�',459}, {'�','�',223}, {'�','�',56}, {'�','�',89}, {'�','�',81}, {'�','�',154}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',252}, {'�','�',1}, {'�','�',1}, {'�','�',364}, {'�','�',356}, {'�','�',8}, {'�','�',189},
++{'�','�',11}, {'�','�',1543}, {'�','�',6}, {'�','�',845}, {'�','�',6}, {'�','�',23}, {'�','�',202}, {'�','�',2}, {'�','�',33}, {'�','�',122}, {'�','�',149}, {'�','�',3}, {'�','�',4}, {'�','�',3}, {'�','�',6}, {'�','�',2},
++{'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',5}, {'�','�',8}, {'�','�',10}, {'�','�',2}, {'�','�',4}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',3}, {'�','�',6}, {'�','�',1264}, {'�','�',29}, {'�','�',98},
++{'�','�',53}, {'�','�',129}, {'�','�',936}, {'�','�',41}, {'�','�',20}, {'�','�',777}, {'�','�',1}, {'�','�',54}, {'�','�',29}, {'�','�',39}, {'�','�',181}, {'�','�',1126}, {'�','�',38}, {'�','�',34}, {'�','�',76}, {'�','�',122},
++{'�','�',355}, {'�','�',8}, {'�','�',36}, {'�','�',23}, {'�','�',9}, {'�','�',49}, {'�','�',5}, {'�','�',169}, {'�','�',98}, {'�','�',7}, {'�','�',59}, {'�','�',159}, {'�','�',6}, {'�','�',4}, {'�','�',2}, {'�','�',2},
++{'�','�',1}, {'�','�',1}, {'�','�',4}, {'�','�',2}, {'�','�',3}, {'�','�',4}, {'�','�',5}, {'�','�',3}, {'�','�',3}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',1}, {'�','�',306},
++{'�','�',33}, {'�','�',259}, {'�','�',18}, {'�','�',41}, {'�','�',973}, {'�','�',8}, {'�','�',11}, {'�','�',295}, {'�','�',691}, {'�','�',519}, {'�','�',222}, {'�','�',236}, {'�','�',403}, {'�','�',274}, {'�','�',58}, {'�','�',222},
++{'�','�',1721}, {'�','�',146}, {'�','�',18}, {'�','�',34}, {'�','�',6}, {'�','�',48}, {'�','�',20}, {'�','�',2}, {'�','�',79}, {'�','�',401}, {'�','�',10}, {'�','�',27}, {'�','�',680}, {'�','�',29}, {'�','�',5}, {'�','�',25},
++{'�','�',3}, {'�','�',11}, {'�','�',9}, {'�','�',2}, {'�','�',5}, {'�','�',16}, {'�','�',21}, {'�','�',20}, {'�','�',24}, {'�','�',10}, {'�','�',23}, {'�','�',2}, {'�','�',10}, {'�','�',13}, {'�','�',2}, {'�','�',2},
++{'�','�',3}, {'�','�',11}, {'�','�',1}, {'�','�',3}, {'�','�',1}, {'�','�',16}, {'�','�',1225}, {'�','�',56}, {'�','�',436}, {'�','�',25}, {'�','�',67}, {'�','�',864}, {'�','�',17}, {'�','�',33}, {'�','�',758}, {'�','�',1},
++{'�','�',180}, {'�','�',52}, {'�','�',65}, {'�','�',329}, {'�','�',2625}, {'�','�',119}, {'�','�',616}, {'�','�',337}, {'�','�',117}, {'�','�',398}, {'�','�',1}, {'�','�',24}, {'�','�',10}, {'�','�',87}, {'�','�',3}, {'�','�',4},
++{'�','�',398}, {'�','�',1292}, {'�','�',37}, {'�','�',13}, {'�','�',103}, {'�','�',22}, {'�','�',4}, {'�','�',19}, {'�','�',4}, {'�','�',20}, {'�','�',3}, {'�','�',2}, {'�','�',25}, {'�','�',9}, {'�','�',6}, {'�','�',12},
++{'�','�',18}, {'�','�',13}, {'�','�',18}, {'�','�',1}, {'�','�',18}, {'�','�',12}, {'�','�',2}, {'�','�',2}, {'�','�',5}, {'�','�',6}, {'�','�',3}, {'�','�',5}, {'�','�',17}, {'�','�',45}, {'�','�',142}, {'�','�',184},
++{'�','�',161}, {'�','�',445}, {'�','�',83}, {'�','�',269}, {'�','�',75}, {'�','�',111}, {'�','�',40}, {'�','�',169}, {'�','�',251}, {'�','�',322}, {'�','�',195}, {'�','�',50}, {'�','�',258}, {'�','�',203}, {'�','�',380}, {'�','�',442},
++{'�','�',24}, {'�','�',5}, {'�','�',97}, {'�','�',241}, {'�','�',302}, {'�','�',37}, {'�','�',19}, {'�','�',156}, {'�','�',31}, {'�','�',1}, {'�','�',2}, {'�','�',21}, {'�','�',45}, {'�','�',47}, {'�','�',14}, {'�','�',2},
++{'�','�',24}, {'�','�',20}, {'�','�',4}, {'�','�',5}, {'�','�',8}, {'�','�',6}, {'�','�',4}, {'�','�',1}, {'�','�',2}, {'�','�',7}, {'�','�',7}, {'�','�',4}, {'�','�',3}, {'�','�',3}, {'�','�',5}, {'�','�',4},
++{'�','�',2}, {'�','�',1}, {'�','�',10}, {'�','�',5}, {'�','�',7}, {'�','�',4}, {'�','�',5}, {'�','�',3}, {'�','�',5}, {'�','�',2}, {'�','�',5}, {'�','�',135}, {'�','�',21}, {'�','�',72}, {'�','�',27}, {'�','�',21},
++{'�','�',25}, {'�','�',4}, {'�','�',13}, {'�','�',75}, {'�','�',16}, {'�','�',43}, {'�','�',49}, {'�','�',83}, {'�','�',450}, {'�','�',70}, {'�','�',19}, {'�','�',39}, {'�','�',27}, {'�','�',42}, {'�','�',3}, {'�','�',4},
++{'�','�',2}, {'�','�',18}, {'�','�',8}, {'�','�',1}, {'�','�',6}, {'�','�',7}, {'�','�',9}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1},
++{'�','�',179}, {'�','�',1}, {'�','�',11}, {'�','�',162}, {'�','�',5}, {'�','�',75}, {'�','�',6}, {'�','�',5}, {'�','�',1}, {'�','�',2}, {'�','�',29}, {'�','�',4}, {'�','�',2}, {'�','�',4}, {'�','�',4}, {'�','�',41},
++{'�','�',2}, {'�','�',41}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',1}, {'�','�',3}, {'�','�',2}, {'�','�',1}, {'�','�',294}, {'�','�',1}, {'�','�',6}, {'�','�',874}, {'�','�',319},
++{'�','�',76}, {'�','�',9}, {'�','�',4}, {'�','�',158}, {'�','�',10}, {'�','�',3}, {'�','�',8}, {'�','�',2}, {'�','�',536}, {'�','�',101}, {'�','�',7}, {'�','�',37}, {'�','�',49}, {'�','�',1}, {'�','�',1}, {'�','�',150},
++{'�','�',5}, {'�','�',388}, {'�','�',188}, {'�','�',201}, {'�','�',68}, {'�','�',4}, {'�','�',78}, {'�','�',53}, {'�','�',8}, {'�','�',3}, {'�','�',2}, {'�','�',17}, {'�','�',45}, {'�','�',1}, {'�','�',1}, {'�','�',200},
++{'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',68}, {'�','�',242}, {'�','�',96}, {'�','�',7}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',20}, {'�','�',2}, {'�','�',14}, {'�','�',6}, {'�','�',28}, {'�','�',4},
++{'�','�',8}, {'�','�',6}, {'�','�',1}, {'�','�',4}, {'�','�',12}, {'�','�',10}, {'�','�',2}, {'�','�',15}, {'�','�',16}, {'�','�',13}, {'�','�',6}, {'�','�',3}, {'�','�',7}, {'�','�',10}, {'�','�',1}, {'�','�',1},
++{'�','�',4}, {'�','�',5}, {'�','�',1}, {'�','�',10}, {'�','�',10}, {'�','�',66}, {'�','�',248}, {'�','�',37}, {'�','�',65}, {'�','�',138}, {'�','�',21}, {'�','�',45}, {'�','�',74}, {'�','�',264}, {'�','�',83}, {'�','�',305},
++{'�','�',164}, {'�','�',141}, {'�','�',34}, {'�','�',231}, {'�','�',61}, {'�','�',189}, {'�','�',148}, {'�','�',35}, {'�','�',1}, {'�','�',146}, {'�','�',3}, {'�','�',63}, {'�','�',80}, {'�','�',1}, {'�','�',9}, {'�','�',10},
++{'�','�',44}, {'�','�',8}, {'�','�',45}, {'�','�',7}, {'�','�',18}, {'�','�',12}, {'�','�',1}, {'�','�',4}, {'�','�',29}, {'�','�',19}, {'�','�',18}, {'�','�',36}, {'�','�',25}, {'�','�',42}, {'�','�',4}, {'�','�',25},
++{'�','�',24}, {'�','�',10}, {'�','�',1}, {'�','�',3}, {'�','�',14}, {'�','�',1}, {'�','�',4}, {'�','�',21}, {'�','�',38}, {'�','�',63}, {'�','�',196}, {'�','�',35}, {'�','�',110}, {'�','�',154}, {'�','�',11}, {'�','�',58},
++{'�','�',194}, {'�','�',1}, {'�','�',324}, {'�','�',36}, {'�','�',124}, {'�','�',321}, {'�','�',92}, {'�','�',164}, {'�','�',41}, {'�','�',302}, {'�','�',152}, {'�','�',48}, {'�','�',5}, {'�','�',20}, {'�','�',15}, {'�','�',88},
++{'�','�',85}, {'�','�',1}, {'�','�',27}, {'�','�',88}, {'�','�',110}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',6}, {'�','�',9}, {'�','�',6}, {'�','�',3}, {'�','�',8}, {'�','�',4},
++{'�','�',531}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',10}, {'�','�',1}, {'�','�',10}, {'�','�',1}, {'�','�',3}, {'�','�',1}, {'�','�',2}, {'�','�',17}, {'�','�',4}, {'�','�',2}, {'�','�',3}, {'�','�',16},
++{'�','�',4}, {'�','�',5}, {'�','�',1}, {'�','�',3}, {'�','�',2}, {'�','�',2}, {'�','�',3}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',9}, {'�','�',15}, {'�','�',73}, {'�','�',37}, {'�','�',12}, {'�','�',72},
++{'�','�',12}, {'�','�',6}, {'�','�',11}, {'�','�',45}, {'�','�',1}, {'�','�',30}, {'�','�',9}, {'�','�',28}, {'�','�',58}, {'�','�',17}, {'�','�',58}, {'�','�',14}, {'�','�',71}, {'�','�',139}, {'�','�',10}, {'�','�',5},
++{'�','�',5}, {'�','�',3}, {'�','�',42}, {'�','�',11}, {'�','�',27}, {'�','�',2}, {'�','�',10}, {'�','�',20}, {'�','�',18}, {'�','�',5}, {'�','�',28}, {'�','�',2}, {'�','�',23}, {'�','�',6}, {'�','�',2}, {'�','�',3},
++{'�','�',15}, {'�','�',14}, {'�','�',3}, {'�','�',10}, {'�','�',23}, {'�','�',22}, {'�','�',26}, {'�','�',5}, {'�','�',12}, {'�','�',17}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',7}, {'�','�',2}, {'�','�',10},
++{'�','�',18}, {'�','�',23}, {'�','�',85}, {'�','�',260}, {'�','�',44}, {'�','�',170}, {'�','�',70}, {'�','�',63}, {'�','�',114}, {'�','�',146}, {'�','�',7}, {'�','�',145}, {'�','�',137}, {'�','�',108}, {'�','�',275}, {'�','�',110},
++{'�','�',204}, {'�','�',43}, {'�','�',259}, {'�','�',457}, {'�','�',64}, {'�','�',2}, {'�','�',37}, {'�','�',18}, {'�','�',75}, {'�','�',13}, {'�','�',33}, {'�','�',22}, {'�','�',31}, {'�','�',47}
++};
++
++static const lng_stat koi[]={
++{'�','�',10}, {'�','�',15}, {'�','�',73}, {'�','�',3}, {'�','�',72}, {'�','�',12}, {'�','�',5}, {'�','�',12}, {'�','�',5}, {'�','�',45}, {'�','�',1}, {'�','�',30}, {'�','�',9}, {'�','�',28}, {'�','�',58}, {'�','�',17},
++{'�','�',58}, {'�','�',20}, {'�','�',14}, {'�','�',71}, {'�','�',139}, {'�','�',10}, {'�','�',6}, {'�','�',37}, {'�','�',11}, {'�','�',11}, {'�','�',2}, {'�','�',27}, {'�','�',42}, {'�','�',10}, {'�','�',1}, {'�','�',3},
++{'�','�',1}, {'�','�',3}, {'�','�',17}, {'�','�',4}, {'�','�',2}, {'�','�',3}, {'�','�',16}, {'�','�',4}, {'�','�',5}, {'�','�',9}, {'�','�',1}, {'�','�',3}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',10},
++{'�','�',2}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',238}, {'�','�',69}, {'�','�',257}, {'�','�',24}, {'�','�',548}, {'�','�',328}, {'�','�',38}, {'�','�',218}, {'�','�',228}, {'�','�',225}, {'�','�',194}, {'�','�',1285},
++{'�','�',1288}, {'�','�',771}, {'�','�',911}, {'�','�',166}, {'�','�',421}, {'�','�',384}, {'�','�',368}, {'�','�',802}, {'�','�',874}, {'�','�',98}, {'�','�',262}, {'�','�',701}, {'�','�',626}, {'�','�',121}, {'�','�',63}, {'�','�',27},
++{'�','�',300}, {'�','�',56}, {'�','�',18}, {'�','�',26}, {'�','�',12}, {'�','�',1}, {'�','�',9}, {'�','�',4}, {'�','�',53}, {'�','�',59}, {'�','�',9}, {'�','�',21}, {'�','�',32}, {'�','�',27}, {'�','�',52}, {'�','�',22},
++{'�','�',7}, {'�','�',21}, {'�','�',42}, {'�','�',10}, {'�','�',7}, {'�','�',48}, {'�','�',11}, {'�','�',2}, {'�','�',7}, {'�','�',16}, {'�','�',1}, {'�','�',199}, {'�','�',6}, {'�','�',3}, {'�','�',6}, {'�','�',420},
++{'�','�',11}, {'�','�',142}, {'�','�',7}, {'�','�',137}, {'�','�',7}, {'�','�',31}, {'�','�',327}, {'�','�',4}, {'�','�',134}, {'�','�',172}, {'�','�',18}, {'�','�',1}, {'�','�',288}, {'�','�',13}, {'�','�',10}, {'�','�',561},
++{'�','�',2}, {'�','�',1}, {'�','�',7}, {'�','�',32}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',179}, {'�','�',1}, {'�','�',162}, {'�','�',75}, {'�','�',6}, {'�','�',5}, {'�','�',1}, {'�','�',2},
++{'�','�',29}, {'�','�',4}, {'�','�',2}, {'�','�',4}, {'�','�',4}, {'�','�',41}, {'�','�',11}, {'�','�',41}, {'�','�',5}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',1},
++{'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',795}, {'�','�',7}, {'�','�',138}, {'�','�',14}, {'�','�',839}, {'�','�',1}, {'�','�',6}, {'�','�',11}, {'�','�',487}, {'�','�',1}, {'�','�',79}, {'�','�',78}, {'�','�',10},
++{'�','�',289}, {'�','�',613}, {'�','�',17}, {'�','�',68}, {'�','�',195}, {'�','�',62}, {'�','�',41}, {'�','�',417}, {'�','�',8}, {'�','�',165}, {'�','�',200}, {'�','�',111}, {'�','�',7}, {'�','�',6}, {'�','�',6}, {'�','�',1},
++{'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',24}, {'�','�',44}, {'�','�',463},
++{'�','�',66}, {'�','�',613}, {'�','�',281}, {'�','�',15}, {'�','�',592}, {'�','�',182}, {'�','�',177}, {'�','�',297}, {'�','�',398}, {'�','�',926}, {'�','�',982}, {'�','�',1512}, {'�','�',206}, {'�','�',524}, {'�','�',87}, {'�','�',1231},
++{'�','�',1082}, {'�','�',1607}, {'�','�',103}, {'�','�',136}, {'�','�',704}, {'�','�',444}, {'�','�',235}, {'�','�',79}, {'�','�',137}, {'�','�',308}, {'�','�',1}, {'�','�',30}, {'�','�',8}, {'�','�',1}, {'�','�',34}, {'�','�',13},
++{'�','�',4}, {'�','�',7}, {'�','�',2}, {'�','�',21}, {'�','�',21}, {'�','�',4}, {'�','�',13}, {'�','�',20}, {'�','�',21}, {'�','�',31}, {'�','�',25}, {'�','�',2}, {'�','�',13}, {'�','�',10}, {'�','�',7}, {'�','�',1},
++{'�','�',33}, {'�','�',6}, {'�','�',2}, {'�','�',11}, {'�','�',9}, {'�','�',2}, {'�','�',21}, {'�','�',45}, {'�','�',6}, {'�','�',47}, {'�','�',14}, {'�','�',2}, {'�','�',24}, {'�','�',20}, {'�','�',4}, {'�','�',5},
++{'�','�',8}, {'�','�',1}, {'�','�',4}, {'�','�',2}, {'�','�',1}, {'�','�',210}, {'�','�',18}, {'�','�',191}, {'�','�',83}, {'�','�',3}, {'�','�',1}, {'�','�',103}, {'�','�',20}, {'�','�',205}, {'�','�',3}, {'�','�',62},
++{'�','�',1340}, {'�','�',7}, {'�','�',4}, {'�','�',180}, {'�','�',13}, {'�','�',4}, {'�','�',88}, {'�','�',6}, {'�','�',2}, {'�','�',1}, {'�','�',15}, {'�','�',3}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',4},
++{'�','�',1}, {'�','�',4}, {'�','�',3}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',3}, {'�','�',3}, {'�','�',1}, {'�','�',1}, {'�','�',135}, {'�','�',21}, {'�','�',2}, {'�','�',21}, {'�','�',25},
++{'�','�',3}, {'�','�',27}, {'�','�',4}, {'�','�',75}, {'�','�',16}, {'�','�',43}, {'�','�',49}, {'�','�',83}, {'�','�',450}, {'�','�',70}, {'�','�',9}, {'�','�',19}, {'�','�',39}, {'�','�',27}, {'�','�',42}, {'�','�',4},
++{'�','�',72}, {'�','�',6}, {'�','�',1}, {'�','�',13}, {'�','�',8}, {'�','�',7}, {'�','�',18}, {'�','�',7}, {'�','�',4}, {'�','�',3}, {'�','�',5}, {'�','�',4}, {'�','�',2}, {'�','�',1}, {'�','�',10}, {'�','�',5},
++{'�','�',7}, {'�','�',5}, {'�','�',4}, {'�','�',5}, {'�','�',3}, {'�','�',7}, {'�','�',5}, {'�','�',2}, {'�','�',3}, {'�','�',22}, {'�','�',90}, {'�','�',242}, {'�','�',123}, {'�','�',435}, {'�','�',280}, {'�','�',17},
++{'�','�',153}, {'�','�',324}, {'�','�',301}, {'�','�',216}, {'�','�',492}, {'�','�',888}, {'�','�',573}, {'�','�',958}, {'�','�',216}, {'�','�',335}, {'�','�',234}, {'�','�',275}, {'�','�',668}, {'�','�',949}, {'�','�',108}, {'�','�',87},
++{'�','�',658}, {'�','�',388}, {'�','�',134}, {'�','�',39}, {'�','�',36}, {'�','�',363}, {'�','�',39}, {'�','�',10}, {'�','�',2}, {'�','�',25}, {'�','�',11}, {'�','�',3}, {'�','�',5}, {'�','�',4}, {'�','�',30}, {'�','�',24},
++{'�','�',5}, {'�','�',36}, {'�','�',39}, {'�','�',25}, {'�','�',49}, {'�','�',28}, {'�','�',3}, {'�','�',22}, {'�','�',25}, {'�','�',8}, {'�','�',2}, {'�','�',46}, {'�','�',1}, {'�','�',3}, {'�','�',7}, {'�','�',4},
++{'�','�',21}, {'�','�',36}, {'�','�',11}, {'�','�',100}, {'�','�',15}, {'�','�',6}, {'�','�',26}, {'�','�',7}, {'�','�',88}, {'�','�',1}, {'�','�',87}, {'�','�',25}, {'�','�',47}, {'�','�',117}, {'�','�',55}, {'�','�',133},
++{'�','�',16}, {'�','�',42}, {'�','�',162}, {'�','�',131}, {'�','�',17}, {'�','�',21}, {'�','�',112}, {'�','�',30}, {'�','�',20}, {'�','�',15}, {'�','�',2}, {'�','�',69}, {'�','�',20}, {'�','�',6}, {'�','�',16}, {'�','�',6},
++{'�','�',1}, {'�','�',6}, {'�','�',1}, {'�','�',22}, {'�','�',18}, {'�','�',2}, {'�','�',12}, {'�','�',13}, {'�','�',15}, {'�','�',13}, {'�','�',8}, {'�','�',7}, {'�','�',9}, {'�','�',15}, {'�','�',3}, {'�','�',20},
++{'�','�',2}, {'�','�',7}, {'�','�',1}, {'�','�',1358}, {'�','�',49}, {'�','�',5}, {'�','�',48}, {'�','�',93}, {'�','�',7}, {'�','�',15}, {'�','�',10}, {'�','�',678}, {'�','�',28}, {'�','�',81}, {'�','�',22}, {'�','�',151},
++{'�','�',1337}, {'�','�',64}, {'�','�',25}, {'�','�',273}, {'�','�',131}, {'�','�',238}, {'�','�',384}, {'�','�',22}, {'�','�',147}, {'�','�',2}, {'�','�',55}, {'�','�',2}, {'�','�',13}, {'�','�',31}, {'�','�',11}, {'�','�',1},
++{'�','�',4}, {'�','�',2}, {'�','�',7}, {'�','�',1}, {'�','�',8}, {'�','�',9}, {'�','�',2}, {'�','�',5}, {'�','�',20}, {'�','�',7}, {'�','�',9}, {'�','�',8}, {'�','�',7}, {'�','�',9}, {'�','�',4}, {'�','�',1},
++{'�','�',13}, {'�','�',5}, {'�','�',2}, {'�','�',4}, {'�','�',159}, {'�','�',758}, {'�','�',34}, {'�','�',2}, {'�','�',55}, {'�','�',675}, {'�','�',5}, {'�','�',35}, {'�','�',7}, {'�','�',1300}, {'�','�',1}, {'�','�',114},
++{'�','�',21}, {'�','�',45}, {'�','�',111}, {'�','�',947}, {'�','�',83}, {'�','�',299}, {'�','�',18}, {'�','�',320}, {'�','�',61}, {'�','�',264}, {'�','�',33}, {'�','�',102}, {'�','�',642}, {'�','�',146}, {'�','�',23}, {'�','�',1},
++{'�','�',21}, {'�','�',66}, {'�','�',20}, {'�','�',2}, {'�','�',1}, {'�','�',14}, {'�','�',3}, {'�','�',1}, {'�','�',5}, {'�','�',1}, {'�','�',19}, {'�','�',12}, {'�','�',2}, {'�','�',14}, {'�','�',31}, {'�','�',20},
++{'�','�',20}, {'�','�',19}, {'�','�',1}, {'�','�',20}, {'�','�',10}, {'�','�',1}, {'�','�',1}, {'�','�',25}, {'�','�',4}, {'�','�',2}, {'�','�',447}, {'�','�',69}, {'�','�',4}, {'�','�',41}, {'�','�',838}, {'�','�',5},
++{'�','�',33}, {'�','�',13}, {'�','�',484}, {'�','�',2}, {'�','�',71}, {'�','�',74}, {'�','�',96}, {'�','�',497}, {'�','�',761}, {'�','�',132}, {'�','�',82}, {'�','�',41}, {'�','�',137}, {'�','�',74}, {'�','�',411}, {'�','�',27},
++{'�','�',119}, {'�','�',33}, {'�','�',207}, {'�','�',38}, {'�','�',3}, {'�','�',17}, {'�','�',3}, {'�','�',46}, {'�','�',11}, {'�','�',2}, {'�','�',10}, {'�','�',1}, {'�','�',8}, {'�','�',2}, {'�','�',12}, {'�','�',10},
++{'�','�',6}, {'�','�',23}, {'�','�',12}, {'�','�',12}, {'�','�',10}, {'�','�',1}, {'�','�',9}, {'�','�',16}, {'�','�',2}, {'�','�',14}, {'�','�',5}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',30}, {'�','�',1706},
++{'�','�',28}, {'�','�',65}, {'�','�',97}, {'�','�',1870}, {'�','�',14}, {'�','�',48}, {'�','�',4}, {'�','�',1319}, {'�','�',84}, {'�','�',7}, {'�','�',11}, {'�','�',331}, {'�','�',1483}, {'�','�',54}, {'�','�',396}, {'�','�',27},
++{'�','�',133}, {'�','�',165}, {'�','�',480}, {'�','�',5}, {'�','�',55}, {'�','�',196}, {'�','�',385}, {'�','�',20}, {'�','�',3}, {'�','�',7}, {'�','�',31}, {'�','�',55}, {'�','�',11}, {'�','�',1}, {'�','�',3}, {'�','�',4},
++{'�','�',8}, {'�','�',7}, {'�','�',10}, {'�','�',1}, {'�','�',2}, {'�','�',6}, {'�','�',2}, {'�','�',9}, {'�','�',2}, {'�','�',2}, {'�','�',2}, {'�','�',6}, {'�','�',1}, {'�','�',5}, {'�','�',1}, {'�','�',2},
++{'�','�',6}, {'�','�',81}, {'�','�',34}, {'�','�',652}, {'�','�',51}, {'�','�',946}, {'�','�',457}, {'�','�',35}, {'�','�',915}, {'�','�',137}, {'�','�',291}, {'�','�',602}, {'�','�',498}, {'�','�',923}, {'�','�',1073}, {'�','�',1281},
++{'�','�',241}, {'�','�',588}, {'�','�',154}, {'�','�',921}, {'�','�',1264}, {'�','�',1536}, {'�','�',152}, {'�','�',437}, {'�','�',1590}, {'�','�',279}, {'�','�',223}, {'�','�',89}, {'�','�',56}, {'�','�',459}, {'�','�',34}, {'�','�',5},
++{'�','�',2}, {'�','�',16}, {'�','�',16}, {'�','�',7}, {'�','�',9}, {'�','�',4}, {'�','�',43}, {'�','�',17}, {'�','�',3}, {'�','�',23}, {'�','�',49}, {'�','�',23}, {'�','�',40}, {'�','�',27}, {'�','�',6}, {'�','�',14},
++{'�','�',19}, {'�','�',4}, {'�','�',4}, {'�','�',44}, {'�','�',17}, {'�','�',5}, {'�','�',12}, {'�','�',252}, {'�','�',1}, {'�','�',364}, {'�','�',356}, {'�','�',8}, {'�','�',189}, {'�','�',11}, {'�','�',1543}, {'�','�',6},
++{'�','�',149}, {'�','�',845}, {'�','�',6}, {'�','�',23}, {'�','�',202}, {'�','�',1}, {'�','�',122}, {'�','�',33}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',31}, {'�','�',23}, {'�','�',85}, {'�','�',18},
++{'�','�',170}, {'�','�',70}, {'�','�',2}, {'�','�',44}, {'�','�',37}, {'�','�',146}, {'�','�',7}, {'�','�',145}, {'�','�',137}, {'�','�',108}, {'�','�',275}, {'�','�',110}, {'�','�',204}, {'�','�',47}, {'�','�',43}, {'�','�',259},
++{'�','�',457}, {'�','�',64}, {'�','�',63}, {'�','�',260}, {'�','�',114}, {'�','�',13}, {'�','�',22}, {'�','�',33}, {'�','�',75}, {'�','�',18}, {'�','�',5}, {'�','�',1}, {'�','�',23}, {'�','�',6}, {'�','�',2}, {'�','�',2},
++{'�','�',15}, {'�','�',14}, {'�','�',3}, {'�','�',10}, {'�','�',23}, {'�','�',22}, {'�','�',26}, {'�','�',18}, {'�','�',5}, {'�','�',12}, {'�','�',17}, {'�','�',1}, {'�','�',2}, {'�','�',28}, {'�','�',3}, {'�','�',2},
++{'�','�',10}, {'�','�',7}, {'�','�',59}, {'�','�',1264}, {'�','�',29}, {'�','�',23}, {'�','�',129}, {'�','�',936}, {'�','�',8}, {'�','�',53}, {'�','�',36}, {'�','�',777}, {'�','�',1}, {'�','�',54}, {'�','�',29}, {'�','�',39},
++{'�','�',181}, {'�','�',1126}, {'�','�',38}, {'�','�',159}, {'�','�',34}, {'�','�',76}, {'�','�',122}, {'�','�',355}, {'�','�',41}, {'�','�',98}, {'�','�',98}, {'�','�',169}, {'�','�',20}, {'�','�',49}, {'�','�',7}, {'�','�',5},
++{'�','�',9}, {'�','�',3}, {'�','�',4}, {'�','�',6}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',5}, {'�','�',8}, {'�','�',10}, {'�','�',6}, {'�','�',2}, {'�','�',4}, {'�','�',3},
++{'�','�',1}, {'�','�',3}, {'�','�',2}, {'�','�',27}, {'�','�',306}, {'�','�',33}, {'�','�',6}, {'�','�',41}, {'�','�',973}, {'�','�',18}, {'�','�',18}, {'�','�',34}, {'�','�',295}, {'�','�',691}, {'�','�',519}, {'�','�',222},
++{'�','�',236}, {'�','�',403}, {'�','�',274}, {'�','�',680}, {'�','�',58}, {'�','�',222}, {'�','�',1721}, {'�','�',146}, {'�','�',8}, {'�','�',259}, {'�','�',401}, {'�','�',79}, {'�','�',11}, {'�','�',20}, {'�','�',10}, {'�','�',2},
++{'�','�',48}, {'�','�',6}, {'�','�',2}, {'�','�',2}, {'�','�',4}, {'�','�',2}, {'�','�',3}, {'�','�',4}, {'�','�',5}, {'�','�',3}, {'�','�',3}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1},
++{'�','�',4}, {'�','�',1}, {'�','�',3}, {'�','�',1}, {'�','�',13}, {'�','�',1225}, {'�','�',56}, {'�','�',10}, {'�','�',67}, {'�','�',864}, {'�','�',1}, {'�','�',25}, {'�','�',24}, {'�','�',758}, {'�','�',1}, {'�','�',180},
++{'�','�',52}, {'�','�',65}, {'�','�',329}, {'�','�',2625}, {'�','�',119}, {'�','�',103}, {'�','�',616}, {'�','�',337}, {'�','�',117}, {'�','�',398}, {'�','�',17}, {'�','�',436}, {'�','�',1292}, {'�','�',398}, {'�','�',33}, {'�','�',3},
++{'�','�',37}, {'�','�',4}, {'�','�',87}, {'�','�',1}, {'�','�',29}, {'�','�',5}, {'�','�',11}, {'�','�',9}, {'�','�',2}, {'�','�',3}, {'�','�',3}, {'�','�',16}, {'�','�',21}, {'�','�',20}, {'�','�',24}, {'�','�',10},
++{'�','�',23}, {'�','�',16}, {'�','�',2}, {'�','�',10}, {'�','�',13}, {'�','�',2}, {'�','�',2}, {'�','�',25}, {'�','�',5}, {'�','�',1}, {'�','�',3}, {'�','�',11}, {'�','�',156}, {'�','�',45}, {'�','�',142}, {'�','�',445},
++{'�','�',83}, {'�','�',5}, {'�','�',161}, {'�','�',97}, {'�','�',111}, {'�','�',40}, {'�','�',169}, {'�','�',251}, {'�','�',322}, {'�','�',195}, {'�','�',50}, {'�','�',258}, {'�','�',31}, {'�','�',203}, {'�','�',380}, {'�','�',442},
++{'�','�',24}, {'�','�',269}, {'�','�',184}, {'�','�',75}, {'�','�',302}, {'�','�',19}, {'�','�',37}, {'�','�',241}, {'�','�',22}, {'�','�',4}, {'�','�',20}, {'�','�',3}, {'�','�',2}, {'�','�',4}, {'�','�',5}, {'�','�',25},
++{'�','�',9}, {'�','�',6}, {'�','�',12}, {'�','�',18}, {'�','�',13}, {'�','�',18}, {'�','�',17}, {'�','�',1}, {'�','�',18}, {'�','�',12}, {'�','�',2}, {'�','�',19}, {'�','�',2}, {'�','�',3}, {'�','�',5}, {'�','�',6},
++{'�','�',187}, {'�','�',4}, {'�','�',145}, {'�','�',690}, {'�','�',1}, {'�','�',1}, {'�','�',214}, {'�','�',35}, {'�','�',1}, {'�','�',8}, {'�','�',96}, {'�','�',13}, {'�','�',14}, {'�','�',7}, {'�','�',7}, {'�','�',4},
++{'�','�',8}, {'�','�',69}, {'�','�',4}, {'�','�',9}, {'�','�',6}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',1}, {'�','�',3}, {'�','�',989}, {'�','�',26},
++{'�','�',11}, {'�','�',74}, {'�','�',911}, {'�','�',1}, {'�','�',43}, {'�','�',9}, {'�','�',387}, {'�','�',1}, {'�','�',105}, {'�','�',122}, {'�','�',65}, {'�','�',231}, {'�','�',1220}, {'�','�',118}, {'�','�',48}, {'�','�',97},
++{'�','�',851}, {'�','�',123}, {'�','�',116}, {'�','�',26}, {'�','�',62}, {'�','�',33}, {'�','�',518}, {'�','�',88}, {'�','�',53}, {'�','�',44}, {'�','�',2}, {'�','�',47}, {'�','�',11}, {'�','�',1}, {'�','�',1}, {'�','�',5},
++{'�','�',5}, {'�','�',7}, {'�','�',8}, {'�','�',6}, {'�','�',14}, {'�','�',2}, {'�','�',9}, {'�','�',13}, {'�','�',17}, {'�','�',51}, {'�','�',4}, {'�','�',7}, {'�','�',18}, {'�','�',4}, {'�','�',2}, {'�','�',6},
++{'�','�',7}, {'�','�',2}, {'�','�',2}, {'�','�',88}, {'�','�',38}, {'�','�',63}, {'�','�',15}, {'�','�',110}, {'�','�',154}, {'�','�',5}, {'�','�',35}, {'�','�',20}, {'�','�',194}, {'�','�',1}, {'�','�',324}, {'�','�',36},
++{'�','�',124}, {'�','�',321}, {'�','�',92}, {'�','�',164}, {'�','�',110}, {'�','�',41}, {'�','�',302}, {'�','�',152}, {'�','�',48}, {'�','�',11}, {'�','�',196}, {'�','�',58}, {'�','�',85}, {'�','�',27}, {'�','�',1}, {'�','�',88},
++{'�','�',44}, {'�','�',8}, {'�','�',18}, {'�','�',12}, {'�','�',1}, {'�','�',7}, {'�','�',3}, {'�','�',29}, {'�','�',19}, {'�','�',18}, {'�','�',36}, {'�','�',25}, {'�','�',42}, {'�','�',21}, {'�','�',4}, {'�','�',25},
++{'�','�',24}, {'�','�',10}, {'�','�',1}, {'�','�',45}, {'�','�',4}, {'�','�',1}, {'�','�',4}, {'�','�',14}, {'�','�',10}, {'�','�',66}, {'�','�',3}, {'�','�',65}, {'�','�',138}, {'�','�',1}, {'�','�',37}, {'�','�',146},
++{'�','�',74}, {'�','�',264}, {'�','�',83}, {'�','�',305}, {'�','�',164}, {'�','�',141}, {'�','�',34}, {'�','�',231}, {'�','�',10}, {'�','�',61}, {'�','�',189}, {'�','�',148}, {'�','�',35}, {'�','�',21}, {'�','�',248}, {'�','�',45},
++{'�','�',80}, {'�','�',9}, {'�','�',1}, {'�','�',63}, {'�','�',1}, {'�','�',14}, {'�','�',6}, {'�','�',8}, {'�','�',6}, {'�','�',1}, {'�','�',4}, {'�','�',12}, {'�','�',10}, {'�','�',2}, {'�','�',15}, {'�','�',16},
++{'�','�',13}, {'�','�',6}, {'�','�',10}, {'�','�',3}, {'�','�',7}, {'�','�',10}, {'�','�',1}, {'�','�',1}, {'�','�',28}, {'�','�',4}, {'�','�',5}, {'�','�',4}, {'�','�',889}, {'�','�',39}, {'�','�',174}, {'�','�',56},
++{'�','�',67}, {'�','�',3}, {'�','�',113}, {'�','�',16}, {'�','�',33}, {'�','�',64}, {'�','�',302}, {'�','�',109}, {'�','�',31}, {'�','�',43}, {'�','�',53}, {'�','�',21}, {'�','�',7}, {'�','�',109}, {'�','�',18}, {'�','�',147},
++{'�','�',20}, {'�','�',71}, {'�','�',11}, {'�','�',4}, {'�','�',4}, {'�','�',11}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',4}, {'�','�',3}, {'�','�',1}, {'�','�',8}, {'�','�',2}, {'�','�',4},
++{'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',150}, {'�','�',1}, {'�','�',388}, {'�','�',1}, {'�','�',188}, {'�','�',201}, {'�','�',68}, {'�','�',4}, {'�','�',78}, {'�','�',53}, {'�','�',8}, {'�','�',3},
++{'�','�',2}, {'�','�',17}, {'�','�',45}, {'�','�',5}, {'�','�',200}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',6}, {'�','�',9}, {'�','�',6}, {'�','�',3},
++{'�','�',8}, {'�','�',4}, {'�','�',531}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',68}, {'�','�',242}, {'�','�',96}, {'�','�',7}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',20}, {'�','�',2}, {'�','�',1},
++{'�','�',1}, {'�','�',294}, {'�','�',1}, {'�','�',874}, {'�','�',319}, {'�','�',76}, {'�','�',9}, {'�','�',4}, {'�','�',158}, {'�','�',10}, {'�','�',3}, {'�','�',1}, {'�','�',8}, {'�','�',2}, {'�','�',536}, {'�','�',101},
++{'�','�',6}, {'�','�',49}, {'�','�',37}, {'�','�',7}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',1}, {'�','�',3}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1},
++{'�','�',6}, {'�','�',7}, {'�','�',23}, {'�','�',1}, {'�','�',5}, {'�','�',6}, {'�','�',4}, {'�','�',1}, {'�','�',27}, {'�','�',15}, {'�','�',10}, {'�','�',25}, {'�','�',21}, {'�','�',51}, {'�','�',20}, {'�','�',12},
++{'�','�',13}, {'�','�',31}, {'�','�',15}, {'�','�',2}, {'�','�',60}, {'�','�',6}, {'�','�',8}, {'�','�',17}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',8}, {'�','�',2},
++{'�','�',2}, {'�','�',1}, {'�','�',4}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',16}, {'�','�',1}, {'�','�',4}, {'�','�',3}, {'�','�',25}, {'�','�',10},
++{'�','�',9}, {'�','�',2}, {'�','�',8}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',7}, {'�','�',3}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',167}, {'�','�',17}, {'�','�',3}, {'�','�',7},
++{'�','�',3}, {'�','�',1}, {'�','�',20}, {'�','�',9}, {'�','�',19}, {'�','�',6}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',5}, {'�','�',2}, {'�','�',19}, {'�','�',69}, {'�','�',9},
++{'�','�',5}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',7}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',16}, {'�','�',1}, {'�','�',6}, {'�','�',16}, {'�','�',26}, {'�','�',4}, {'�','�',3}, {'�','�',1},
++{'�','�',31}, {'�','�',7}, {'�','�',2}, {'�','�',2}, {'�','�',8}, {'�','�',3}, {'�','�',20}, {'�','�',14}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',8}, {'�','�',20}, {'�','�',7}, {'�','�',2}, {'�','�',7},
++{'�','�',3}, {'�','�',3}, {'�','�',27}, {'�','�',15}, {'�','�',11}, {'�','�',21}, {'�','�',26}, {'�','�',21}, {'�','�',12}, {'�','�',4}, {'�','�',21}, {'�','�',37}, {'�','�',5}, {'�','�',1}, {'�','�',78}, {'�','�',17},
++{'�','�',1}, {'�','�',2}, {'�','�',8}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',100}, {'�','�',4}, {'�','�',1}, {'�','�',14}, {'�','�',4}, {'�','�',62}, {'�','�',2}, {'�','�',24},
++{'�','�',10}, {'�','�',62}, {'�','�',3}, {'�','�',3}, {'�','�',7}, {'�','�',11}, {'�','�',4}, {'�','�',12}, {'�','�',1}, {'�','�',15}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',31}, {'�','�',10}, {'�','�',1},
++{'�','�',44}, {'�','�',1}, {'�','�',19}, {'�','�',93}, {'�','�',1}, {'�','�',6}, {'�','�',3}, {'�','�',29}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',56}, {'�','�',111}, {'�','�',47}, {'�','�',2},
++{'�','�',98}, {'�','�',100}, {'�','�',2}, {'�','�',3}, {'�','�',1}, {'�','�',18}, {'�','�',20}, {'�','�',4}, {'�','�',2}, {'�','�',7}, {'�','�',9}, {'�','�',2}, {'�','�',96}, {'�','�',5}, {'�','�',17}, {'�','�',16},
++{'�','�',21}, {'�','�',35}, {'�','�',1}, {'�','�',2}, {'�','�',6}, {'�','�',1}, {'�','�',13}, {'�','�',2}, {'�','�',6}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',18}, {'�','�',148},
++{'�','�',11}, {'�','�',9}, {'�','�',186}, {'�','�',43}, {'�','�',38}, {'�','�',4}, {'�','�',3}, {'�','�',4}, {'�','�',8}, {'�','�',9}, {'�','�',5}, {'�','�',3}, {'�','�',4}, {'�','�',10}, {'�','�',5}, {'�','�',6},
++{'�','�',28}, {'�','�',24}, {'�','�',39}, {'�','�',4}, {'�','�',30}, {'�','�',7}, {'�','�',5}, {'�','�',2}, {'�','�',49}, {'�','�',12}, {'�','�',3}, {'�','�',3}, {'�','�',4}, {'�','�',1}, {'�','�',22}, {'�','�',13},
++{'�','�',5}, {'�','�',13}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',19}, {'�','�',1}, {'�','�',1}, {'�','�',56}, {'�','�',2}, {'�','�',16}, {'�','�',22}, {'�','�',11}, {'�','�',12}, {'�','�',6}, {'�','�',26},
++{'�','�',6}, {'�','�',1}, {'�','�',6}, {'�','�',3}, {'�','�',35}, {'�','�',4}, {'�','�',4}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',3}, {'�','�',1}, {'�','�',79}, {'�','�',18}, {'�','�',17},
++{'�','�',34}, {'�','�',3}, {'�','�',11}, {'�','�',31}, {'�','�',1}, {'�','�',2}, {'�','�',55}, {'�','�',4}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',3}, {'�','�',2}, {'�','�',1}, {'�','�',2},
++{'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',9}, {'�','�',5}, {'�','�',1}, {'�','�',1}, {'�','�',9}, {'�','�',8}, {'�','�',4}, {'�','�',3}, {'�','�',4}, {'�','�',1}, {'�','�',1}, {'�','�',5},
++{'�','�',1}, {'�','�',14}, {'�','�',5}, {'�','�',2}, {'�','�',1}, {'�','�',15}, {'�','�',5}, {'�','�',98}, {'�','�',3}, {'�','�',7}, {'�','�',5}, {'�','�',8}, {'�','�',7}, {'�','�',6}, {'�','�',107}, {'�','�',18},
++{'�','�',2}, {'�','�',74}, {'�','�',6}, {'�','�',1}, {'�','�',2}, {'�','�',39}, {'�','�',5}, {'�','�',8}, {'�','�',3}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',3}, {'�','�',8}, {'�','�',2},
++{'�','�',2}, {'�','�',31}, {'�','�',1}, {'�','�',2}, {'�','�',23}, {'�','�',2}, {'�','�',12}, {'�','�',3}, {'�','�',1}, {'�','�',3}, {'�','�',7}, {'�','�',8}, {'�','�',8}, {'�','�',7}, {'�','�',1}, {'�','�',1},
++{'�','�',5}, {'�','�',1}, {'�','�',1}, {'�','�',4}, {'�','�',1}, {'�','�',10}, {'�','�',1}, {'�','�',61}, {'�','�',1}, {'�','�',1}, {'�','�',46}, {'�','�',60}, {'�','�',8}, {'�','�',1}
++};
++
++static int
++is_win_charset(char *txt, int len){
++ int j,ki=0,wi=0,d=839,l,koi_stat=0,win_stat=0,ws,ks;
++ unsigned char a,b;
++ l=strlen(txt);
++ if ((len)&&(l>len)) l=len;
++ for(j=0;j<l-1;j++){
++ a=txt[j];
++ b=txt[j+1];
++ //skip bottom half of table
++ if(a<128 || b<128)
++ continue;
++ wi=839;
++ ki=839;
++ ws=0;
++ ks=0;
++ d=839;
++ //binary search through lng_stats
++ do{
++ d>>=1;
++ if(!ws){
++ if(a==win[wi].a){
++ if(b==win[wi].b){
++ ws=1;
++ win_stat+=win[wi].rate;
++ }else if(b<win[wi].b){
++ wi-=d;
++ }else{ //b>win[wi].b
++ wi+=d;
++ }
++ }else if(a<win[wi].a){
++ wi-=d;
++ }else{ //a>win[wi].a
++ wi+=d;
++ }
++ }
++ if(!ks){
++ if(a==koi[ki].a){
++ if(b==koi[ki].b){
++ ks=1;
++ koi_stat+=koi[ki].rate;
++ }else if(b<koi[ki].b){
++ ki-=d;
++ }else{ //b>win[wi].b
++ ki+=d;
++ }
++ }else if(a<koi[ki].a){
++ ki-=d;
++ }else{ //a>win[wi].a
++ ki+=d;
++ }
++ }
++ }while(d);
++ }
++// fprintf(stderr,"\nwin %i, koi %i\n",win_stat,koi_stat);
++ if(win_stat>koi_stat)
++ return 1;
++ else
++ return 0;
++}
++
++/* Only CP1251 and KOI8-R still */
++int autocharset_russian(char *buf,int len) {
++ if (is_win_charset(buf,len)) return 0;
++ return 1;
++}
+diff -Naur xmms-1.2.8/libxmms/charset.c xmms-1.2.8-new/libxmms/charset.c
+--- xmms-1.2.8/libxmms/charset.c 1970-01-01 01:00:00.000000000 +0100
++++ xmms-1.2.8-new/libxmms/charset.c 2003-09-06 19:42:29.000000000 +0200
+@@ -0,0 +1,512 @@
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <errno.h>
++#include <glib.h>
++#include <iconv.h>
++#include <assert.h>
++
++#include "charset.h"
++#include "charset_config.h"
++
++#define LOCALE_STRING_LENGTH 64
++static char charset_default[LOCALE_STRING_LENGTH];
++static char xmms_charset_tmp[CHARSET_MAX_STRING_SIZE];
++
++static int xmms_charset_changed = -1;
++
++static int xmms_charset_id3;
++static int xmms_charset_output;
++static int xmms_charset_fs;
++static int xmms_charset_pl;
++
++static iconv_t iconv_id3=(iconv_t)-1; /* id3 to output */
++static iconv_t iconv_output=(iconv_t)-1; /* output to id3 */
++static iconv_t iconv_fs=(iconv_t)-1; /* fs to output */
++static iconv_t iconv_utf=(iconv_t)-1; /* id3 to UTF-8 */
++static iconv_t iconv_pl2fs=(iconv_t)-1; /* playlist to fs */
++static iconv_t iconv_fs2pl=(iconv_t)-1; /* fs to playlist */
++
++static int xmms_autocharset_mode = 0;
++static int xmms_autocharset_changed = 0;
++static iconv_t *xmms_autocharset = NULL;
++static iconv_t *xmms_autocharset_id2id = NULL;
++static iconv_t *xmms_autocharset_id2fs = NULL;
++static int xmms_autocharset_number = -1;
++
++static int recode_fsout;
++static int recode_fs2utf;
++#include "charset_auto_russian.h"
++
++extern int errno;
++
++/*****************************************************************************
++************************ Auto Recoding ***************************************
++*****************************************************************************/
++static int xmms_autocharset_number_charsets(int m) {
++ const char **encodings;
++ int i;
++
++ if (!m) m=xmms_autocharset_mode;
++ encodings=autocharset_list[m].encodings;
++ for (i=0;encodings[i];i++);
++ return i;
++}
++
++
++static void xmms_autocharset_iconv_close() {
++ int i;
++
++ for (i=0;i<xmms_autocharset_number;i++) {
++ if ((xmms_autocharset)&&(xmms_autocharset[i] != (iconv_t)-1)&&(xmms_autocharset[i] != (iconv_t)-2))
++ iconv_close(xmms_autocharset[i]);
++ if ((xmms_autocharset_id2id)&&(xmms_autocharset_id2id[i] != (iconv_t)-1)&&(xmms_autocharset_id2id[i] != (iconv_t)-2))
++ iconv_close(xmms_autocharset_id2id[i]);
++ if ((xmms_autocharset_id2fs)&&(xmms_autocharset_id2fs[i] != (iconv_t)-1)&&(xmms_autocharset_id2fs[i] != (iconv_t)-2))
++ iconv_close(xmms_autocharset_id2fs[i]);
++ }
++ if (xmms_autocharset) free(xmms_autocharset);
++ if (xmms_autocharset_id2id) free(xmms_autocharset_id2id);
++ if (xmms_autocharset_id2fs) free(xmms_autocharset_id2fs);
++
++ xmms_autocharset=NULL;
++ xmms_autocharset_id2id=NULL;
++ xmms_autocharset_id2fs=NULL;
++ xmms_autocharset_number = -1;
++}
++
++void xmms_autocharset_new_mode(int m) {
++ xmms_autocharset_mode=m;
++ xmms_autocharset_changed=1;
++}
++
++void xmms_autocharset_init(int m) {
++ xmms_autocharset_mode=m;
++ xmms_autocharset_changed=1;
++}
++
++void xmms_autocharset_free() {
++ xmms_autocharset_iconv_close();
++}
++
++static void xmms_autocharset_iconv_open() {
++ const char **encodings;
++ const char *output, *id3, *fs;
++ int i;
++
++ if ((!xmms_autocharset_changed)&&(!xmms_charset_changed)) return;
++ xmms_autocharset_iconv_close();
++ xmms_autocharset_changed=0;
++
++ xmms_autocharset_number = xmms_autocharset_number_charsets(0);
++ if (!xmms_autocharset_number) return;
++
++ xmms_autocharset = (iconv_t*)malloc((xmms_autocharset_number+1)*sizeof(iconv_t));
++ xmms_autocharset_id2id = (iconv_t*)malloc((xmms_autocharset_number+1)*sizeof(iconv_t));
++ xmms_autocharset_id2fs = (iconv_t*)malloc((xmms_autocharset_number+1)*sizeof(iconv_t));
++ if ((!xmms_autocharset)||(!xmms_autocharset_id2id)||(!xmms_autocharset_id2fs)) {
++ if (xmms_autocharset) {
++ free(xmms_autocharset);
++ xmms_autocharset=NULL;
++ }
++ if (xmms_autocharset_id2id) {
++ free(xmms_autocharset_id2id);
++ xmms_autocharset_id2id=NULL;
++ }
++ if (xmms_autocharset_id2fs) {
++ free(xmms_autocharset_id2fs);
++ xmms_autocharset_id2fs=NULL;
++ }
++ xmms_autocharset_number = -1;
++ perror("iconv: allocate memory");
++ return;
++ }
++
++
++ if (xmms_charset_output) output=charset_list[xmms_charset_output];
++ else output=charset_default;
++ if (xmms_charset_id3) id3=charset_list[xmms_charset_id3];
++ else id3=charset_default;
++ if (xmms_charset_fs) fs=charset_list[xmms_charset_fs];
++ else fs=output;
++
++ encodings = autocharset_list[xmms_autocharset_mode].encodings;
++
++ for (i=0;encodings[i];i++) {
++ if (strcmp(output,encodings[i]))
++ xmms_autocharset[i] = iconv_open(output,encodings[i]);
++ else
++ xmms_autocharset[i] = (iconv_t)-2;
++
++ if (strcmp(id3,encodings[i]))
++ xmms_autocharset_id2id[i] = iconv_open(id3,encodings[i]);
++ else
++ xmms_autocharset_id2id[i] = (iconv_t)-2;
++
++ if (strcmp(fs,encodings[i]))
++ xmms_autocharset_id2fs[i] = iconv_open(fs,encodings[i]);
++ else
++ xmms_autocharset_id2fs[i] = (iconv_t)-2;
++ }
++}
++
++
++static iconv_t xmms_autocharset_get(char *buf, int len) {
++ int i;
++ xmms_autocharset_iconv_open();
++ if ((xmms_autocharset)&&(xmms_autocharset_number>0)) {
++ i=autocharset_list[xmms_autocharset_mode].func(buf,len);
++ if (i<xmms_autocharset_number) return xmms_autocharset[i];
++ }
++ return (iconv_t)-1;
++}
++
++static iconv_t xmms_autocharset_get_id2id(char *buf, int len) {
++ int i;
++ xmms_autocharset_iconv_open();
++ if ((xmms_autocharset_id2id)&&(xmms_autocharset_number>0)) {
++ i=autocharset_list[xmms_autocharset_mode].func(buf,len);
++ if (i<xmms_autocharset_number) return xmms_autocharset_id2id[i];
++ }
++ return (iconv_t)-1;
++}
++
++static iconv_t xmms_autocharset_get_id2fs(char *buf, int len) {
++ int i;
++ xmms_autocharset_iconv_open();
++ if ((xmms_autocharset_id2fs)&&(xmms_autocharset_number>0)) {
++ i=autocharset_list[xmms_autocharset_mode].func(buf,len);
++ if (i<xmms_autocharset_number) return xmms_autocharset_id2fs[i];
++ }
++ return (iconv_t)-1;
++}
++
++/*****************************************************************************
++************************ Recoding ********************************************
++*****************************************************************************/
++
++static void xmms_charset_iconv_close() {
++ if (iconv_id3 != (iconv_t)-1) {
++ if (iconv_close(iconv_id3)<0) perror("iconv close: id3");
++ iconv_id3 = (iconv_t)-1;
++ }
++ if (iconv_output != (iconv_t)-1) {
++ if (iconv_close(iconv_output)<0) perror("iconv close: output");
++ iconv_output = (iconv_t)-1;
++ }
++ if (iconv_fs != (iconv_t)-1) {
++ if (iconv_close(iconv_fs)<0) perror("iconv close: fs");
++ iconv_fs = (iconv_t)-1;
++ }
++ if (iconv_fs2pl != (iconv_t)-1) {
++ if (iconv_close(iconv_fs2pl)<0) perror("iconv close: fs2pl");
++ iconv_fs2pl = (iconv_t)-1;
++ }
++ if (iconv_pl2fs != (iconv_t)-1) {
++ if (iconv_close(iconv_pl2fs)<0) perror("iconv close: pl2fs");
++ iconv_pl2fs = (iconv_t)-1;
++ }
++ if (iconv_utf != (iconv_t)-1) {
++ if (iconv_close(iconv_utf)<0) perror("iconv close: utf");
++ iconv_utf=(iconv_t)-1;
++ }
++}
++
++static void xmms_charset_iconv_open() {
++ const char *id3, *output, *fs, *pl;
++ assert(xmms_charset_changed>=0);
++ if (!xmms_charset_changed) return;
++
++ xmms_charset_iconv_close();
++ if (xmms_charset_id3) id3=charset_list[xmms_charset_id3];
++ else id3=charset_default;
++ if (xmms_charset_output) output=charset_list[xmms_charset_output];
++ else output=charset_default;
++ if (xmms_charset_fs) fs=charset_list[xmms_charset_fs];
++ else fs=output;
++ if (xmms_charset_pl) pl=charset_list[xmms_charset_pl];
++ else pl=id3;
++
++
++ if ((xmms_charset_id3 != xmms_charset_output)&&(strcmp(id3,output))) {
++ iconv_id3 = iconv_open(output, id3);
++ iconv_output = iconv_open(id3, output);
++ if (!xmms_charset_fs) iconv_fs = iconv_open(id3, fs);
++ }
++
++ if (strcmp(fs,id3)) {
++ iconv_fs = iconv_open(id3, fs);
++ }
++
++ if (strcmp(fs,pl)) {
++ iconv_fs2pl = iconv_open(pl, fs);
++ iconv_pl2fs = iconv_open(fs,pl);
++ }
++
++ if (strcmp(id3, "UTF-8")) {
++ iconv_utf = iconv_open("UTF-8",id3);
++ }
++
++ if ((xmms_charset_fs)&&(xmms_charset_fs!=xmms_charset_output)) {
++ recode_fsout=1;
++ } else {
++ recode_fsout=0;
++ }
++
++ if (strcmp(fs, "UTF-8")) {
++ recode_fs2utf=1;
++ } else {
++ recode_fs2utf=0;
++ }
++
++ xmms_charset_changed = 0;
++}
++
++
++static int xmms_charset_number(char *c) {
++ int i;
++ if (!c) return 0;
++ for (i=0;charset_list[i];i++)
++ if (strcmp(charset_list[i], c)==0) return i;
++ return 0; /* Default, - if not found */
++}
++
++static void xmms_charset_setdefault() {
++ char *str1, *str2;
++ str1 = getenv(ENV_VARIABLE);
++ if (str1) {
++ str2 = strrchr(str1,'.');
++ if (str2) strncpy(charset_default,str2+1,LOCALE_STRING_LENGTH);
++ else strncpy(charset_default,str1,LOCALE_STRING_LENGTH);
++ charset_default[LOCALE_STRING_LENGTH-1]=0;
++ str2 = strchr(charset_default,'@');
++ if (str2) *str2 = 0;
++ } else {
++ strcpy(charset_default,"UTF-8");
++ }
++}
++
++void xmms_charset_init(char *id3, char *output, char *fs, char *pl) {
++ if (xmms_charset_changed<0) xmms_charset_setdefault();
++
++ xmms_charset_changed = 1;
++ xmms_charset_id3 = xmms_charset_number(id3);
++ xmms_charset_output = xmms_charset_number(output);
++ xmms_charset_fs = xmms_charset_number(fs);
++ xmms_charset_pl = xmms_charset_number(pl);
++}
++
++void xmms_charset_free() {
++ xmms_charset_iconv_close();
++}
++
++void xmms_charset_new_charsets(int id3, int output, int fs, int pl) {
++ if (xmms_charset_changed<0)
++ xmms_charset_setdefault();
++ else
++ if ((xmms_charset_id3==id3)&&(xmms_charset_output==output)&&(xmms_charset_fs==fs)&&(xmms_charset_pl==pl)) return;
++
++ xmms_charset_changed = 1;
++ xmms_charset_id3 = id3;
++ xmms_charset_output = output;
++ xmms_charset_fs = fs;
++ xmms_charset_pl = pl;
++}
++
++// Multi-byte strings still unsupported
++static void iconv_copysymbol(char **in_buf, int *in_left, char **out_buf, int *out_left) {
++ if ((out_left>0)&&(in_left>0)) {
++ (**out_buf)=(**in_buf);
++ (*out_buf)++;
++ (*in_buf)++;
++ (*in_left)--;
++ (*out_left)--;
++ }
++}
++
++static char *xmms_charset_recode(iconv_t icnv, char *buf, int len, int *rlen) {
++ char *in_buf, *out_buf, *res, err;
++ int in_left, out_left, olen;
++ int errors=0;
++
++ assert(buf);
++
++ if (!len) {
++ len=strlen(buf);
++ } else {
++ olen=strlen(buf);
++ if (olen<len) len=olen;
++ }
++
++ if (iconv(icnv, NULL, NULL, NULL, NULL) == -1) {
++ perror( "iconv convert: initialize" );
++ return NULL;
++ }
++
++ in_buf = buf;
++ in_left = len;
++ out_buf = &xmms_charset_tmp;
++ out_left = CHARSET_MAX_STRING_SIZE;
++
++loop:
++ err=iconv(icnv, &in_buf, &in_left, &out_buf, &out_left);
++// printf("%s, %i %u\n",buf,err,errno);
++ if (err<0) {
++ if (errno==E2BIG) {
++ *(int*)(xmms_charset_tmp+(CHARSET_MAX_STRING_SIZE-sizeof(int)))=0;
++ } else if (errno==EILSEQ) {
++ if (errors<CHARSET_MAX_ERRORS) {
++ errors++;
++ iconv_copysymbol(&in_buf, &in_left, &out_buf, &out_left);
++ if (in_left>0) goto loop;
++ } else {
++ perror("iconv convert: invalid encoding?");
++ return NULL;
++ }
++ } else {
++ perror("iconv convert: convert");
++// perror(buf);
++ return NULL;
++ }
++ }
++
++ olen = CHARSET_MAX_STRING_SIZE - out_left;
++ res = g_malloc(olen+1);
++ if (!res) {
++ perror( "iconv convert: malloc" );
++ return NULL;
++ }
++
++ memcpy(res,xmms_charset_tmp,olen);
++ res[olen]=0;
++ if (rlen) *rlen=olen;
++ return res;
++}
++
++char *xmms_charset_recode_id3(char *buf, int len, int *rlen) {
++ iconv_t icnv;
++
++ xmms_charset_iconv_open();
++ icnv = xmms_autocharset_get(buf,len);
++
++ if (icnv == (iconv_t)-2) return NULL;
++
++ if (icnv != (iconv_t)-1)
++ return xmms_charset_recode(icnv, buf, len, rlen);
++
++ if (iconv_id3 != (iconv_t)-1)
++ return xmms_charset_recode(iconv_id3, buf, len, rlen);
++ return NULL;
++}
++
++char *xmms_charset_recode_output(char *buf, int len, int *rlen) {
++ xmms_charset_iconv_open();
++ if (iconv_output != (iconv_t)-1)
++ return xmms_charset_recode(iconv_output, buf, len, rlen);
++ return NULL;
++}
++
++char *xmms_charset_recode_fs(char *buf, int len, int *rlen) {
++ xmms_charset_iconv_open();
++ if (iconv_fs != (iconv_t)-1)
++ return xmms_charset_recode(iconv_fs, buf, len, rlen);
++ return NULL;
++}
++
++char *xmms_charset_recode_fsout(char *buf, int len, int *rlen) {
++ int olen;
++ char *str = NULL;
++
++ xmms_charset_iconv_open();
++ if (recode_fsout) {
++ if (iconv_fs != (iconv_t)-1) {
++ str = xmms_charset_recode(iconv_fs, buf, len, &olen);
++ if (!str) return NULL;
++ len=olen;
++ }
++ if (iconv_id3 != (iconv_t)-1) {
++ buf = xmms_charset_recode(iconv_id3, str ? str : buf, len, &olen);
++ if (str) free(str);
++ } else if (str) {
++ buf = str;
++ } else {
++ buf = NULL;
++ }
++ if (rlen) *rlen=olen;
++ return buf;
++ }
++ return NULL;
++}
++
++char *xmms_charset_recode_fs2pl(char *buf, int len, int *rlen) {
++ xmms_charset_iconv_open();
++ if (iconv_fs2pl != (iconv_t)-1)
++ return xmms_charset_recode(iconv_fs2pl, buf, len, rlen);
++ return NULL;
++}
++
++char *xmms_charset_recode_pl2fs(char *buf, int len, int *rlen) {
++ iconv_t icnv;
++
++ xmms_charset_iconv_open();
++ icnv = xmms_autocharset_get_id2fs(buf,len);
++
++ if (icnv==(iconv_t)-2) return NULL;
++
++ if (icnv != (iconv_t)-1)
++ return xmms_charset_recode(icnv, buf, len, rlen);
++
++ if (iconv_pl2fs != (iconv_t)-1)
++ return xmms_charset_recode(iconv_pl2fs, buf, len, rlen);
++ return NULL;
++}
++
++
++char *xmms_charset_recode_id2utf(char *buf, int len, int *rlen) {
++ char *str = NULL;
++ int olen;
++ iconv_t icnv;
++
++ xmms_charset_iconv_open();
++
++ icnv = xmms_autocharset_get_id2id(buf,len);
++ if ((icnv != (iconv_t)-1)&&(icnv != (iconv_t)-2)) {
++ str = xmms_charset_recode(icnv, buf, len, &olen);
++ if (!str) return NULL;
++ len = olen;
++ }
++
++ xmms_charset_iconv_open();
++ if (iconv_utf != (iconv_t)-1) {
++ buf=xmms_charset_recode(iconv_utf, str ? str : buf, len, rlen);
++ if (str) free(str);
++ return buf;
++ }
++
++ return NULL;
++}
++
++char *xmms_charset_recode_fs2utf(char *buf, int len, int *rlen) {
++ int olen;
++ char *str = NULL;
++
++ xmms_charset_iconv_open();
++ if (recode_fs2utf) {
++ if (iconv_fs != (iconv_t)-1) {
++ str = xmms_charset_recode(iconv_fs, buf, len, &olen);
++ if (!str) return NULL;
++ len=olen;
++ }
++ if (iconv_utf != (iconv_t)-1) {
++ buf = xmms_charset_recode(iconv_utf, str ? str : buf, len, &olen);
++ if (str) free(str);
++ } else if (str) {
++ buf = str;
++ } else {
++ buf = NULL;
++ }
++ if (rlen) *rlen=olen;
++ return buf;
++ }
++ return NULL;
++}
+diff -Naur xmms-1.2.8/libxmms/charset_config.h xmms-1.2.8-new/libxmms/charset_config.h
+--- xmms-1.2.8/libxmms/charset_config.h 1970-01-01 01:00:00.000000000 +0100
++++ xmms-1.2.8-new/libxmms/charset_config.h 2003-09-06 19:42:29.000000000 +0200
+@@ -0,0 +1,13 @@
++/* Misc configuration options */
++
++#define ENV_VARIABLE "LANG"
++#define CHARSET_MAX_STRING_SIZE 1024 /* 256 symbols in UTF-32 */
++#define CHARSET_MAX_ERRORS 3
++
++const char *charset_list[] = { "Default", "KOI8-R", "CP1251", "CP866", "UTF-8", NULL };
++
++const int autocharset_list_ni=2;
++const struct autocharset_list_t autocharset_list[] = {
++ {"Off", NULL, {NULL}},
++ {"Russian", &autocharset_russian, {"CP1251","KOI8-R"} },
++};
+diff -Naur xmms-1.2.8/libxmms/charset.h xmms-1.2.8-new/libxmms/charset.h
+--- xmms-1.2.8/libxmms/charset.h 1970-01-01 01:00:00.000000000 +0100
++++ xmms-1.2.8-new/libxmms/charset.h 2003-09-06 19:42:29.000000000 +0200
+@@ -0,0 +1,36 @@
++#ifndef XMMS_CHARSET_H
++#define XMMS_CHARSET_H
++
++#define AUTOCHARSET_MAX_ENCODINGS 5
++int autocharset_russian(char *buf,int len);
++
++struct autocharset_list_t {
++ char *title;
++ int (*func)(char *buf, int len);
++ char *encodings[AUTOCHARSET_MAX_ENCODINGS];
++};
++
++extern const char *charset_list[];
++extern const int autocharset_list_ni;
++extern const struct autocharset_list_t autocharset_list[];
++
++/* Initialization functions */
++void xmms_autocharset_new_mode(int m);
++void xmms_autocharset_init(int m);
++void xmms_autocharset_free();
++
++void xmms_charset_init(char *id3, char *output, char *fs, char *pl);
++void xmms_charset_free();
++void xmms_charset_new_charsets(int id3, int output, int fs, int pl);
++
++/* Recoding functions */
++char *xmms_charset_recode_id3(char *buf, int len, int *rlen);
++char *xmms_charset_recode_output(char *buf, int len, int *rlen);
++char *xmms_charset_recode_fs(char *buf, int len, int *rlen);
++char *xmms_charset_recode_fsout(char *buf, int len, int *rlen);
++char *xmms_charset_recode_id2utf(char *buf, int len, int *rlen);
++char *xmms_charset_recode_fs2utf(char *buf, int len, int *rlen);
++char *xmms_charset_recode_fs2pl(char *buf, int len, int *rlen);
++char *xmms_charset_recode_pl2fs(char *buf, int len, int *rlen);
++
++#endif /* XMMS_CHARSET_H */
+diff -Naur xmms-1.2.8/libxmms/Makefile.am xmms-1.2.8-new/libxmms/Makefile.am
+--- xmms-1.2.8/libxmms/Makefile.am 2003-09-04 23:00:18.000000000 +0200
++++ xmms-1.2.8-new/libxmms/Makefile.am 2003-09-06 20:11:31.000000000 +0200
+@@ -15,7 +15,9 @@
+ formatter.c formatter.h \
+ titlestring.c titlestring.h \
+ xentry.c xentry.h \
+-xconvert.c xconvert.h
++xconvert.c xconvert.h \
++charset.c charset.h \
++charset_auto_russian.h charset_config.h
+
+-xmmsinclude_HEADERS = configfile.h xmmsctrl.h dirbrowser.h util.h formatter.h titlestring.h
++xmmsinclude_HEADERS = configfile.h xmmsctrl.h dirbrowser.h util.h formatter.h titlestring.h charset.h charset_auto_russian.h
+
+diff -Naur xmms-1.2.8/libxmms/Makefile.in xmms-1.2.8-new/libxmms/Makefile.in
+--- xmms-1.2.8/libxmms/Makefile.in 2003-09-04 23:01:19.000000000 +0200
++++ xmms-1.2.8-new/libxmms/Makefile.in 2003-09-06 19:42:29.000000000 +0200
+@@ -101,10 +101,12 @@
+ formatter.c formatter.h \
+ titlestring.c titlestring.h \
+ xentry.c xentry.h \
+-xconvert.c xconvert.h
++xconvert.c xconvert.h \
++charset.c charset.h \
++charset_auto_russian.h charset_config.h
+
++xmmsinclude_HEADERS = configfile.h xmmsctrl.h dirbrowser.h util.h formatter.h titlestring.h charset.h charset_auto_russian.h
+
+-xmmsinclude_HEADERS = configfile.h xmmsctrl.h dirbrowser.h util.h formatter.h titlestring.h
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs
+ CONFIG_HEADER = config.h
+@@ -118,7 +120,7 @@
+ LIBS = @LIBS@
+ libxmms_la_DEPENDENCIES =
+ libxmms_la_OBJECTS = configfile.lo xmmsctrl.lo dirbrowser.lo util.lo \
+-formatter.lo titlestring.lo xentry.lo xconvert.lo
++formatter.lo titlestring.lo xentry.lo xconvert.lo charset.lo
+ CFLAGS = @CFLAGS@
+ COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+ LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+diff -Naur xmms-1.2.8/libxmms/titlestring.c xmms-1.2.8-new/libxmms/titlestring.c
+--- xmms-1.2.8/libxmms/titlestring.c 2002-01-08 15:09:08.000000000 +0100
++++ xmms-1.2.8-new/libxmms/titlestring.c 2003-09-06 19:42:29.000000000 +0200
+@@ -40,6 +40,8 @@
+ gchar *xmms_get_titlestring(gchar *fmt, TitleInput *input)
+ {
+ gchar c, *p, outbuf[256], convert[16], *string;
++ gchar *cstring = NULL;
++ gint stringlen;
+ gint numdigits, numpr, val, size, i;
+ gint f_left, f_space, f_zero, someflag, width, precision;
+ gboolean did_output = FALSE;
+@@ -164,9 +166,17 @@
+ goto Print_string;
+ case 'f':
+ string = VS(input, file_name);
++ if (string) {
++ cstring=xmms_charset_recode_fs(string,0,&stringlen);
++ if (cstring) string=cstring;
++ }
+ goto Print_string;
+ case 'F':
+ string = VS(input, file_path);
++ if (string) {
++ cstring=xmms_charset_recode_fs(string,0,&stringlen);
++ if (cstring) string=cstring;
++ }
+ goto Print_string;
+ case 'g':
+ string = VS(input, genre);
+@@ -208,6 +218,10 @@
+ while ((c = *string++) != '\0')
+ PUTCH(c);
+ }
++ if (cstring) {
++ g_free(cstring);
++ cstring=NULL;
++ }
+
+ RIGHTPAD(width - numpr);
+ break;
+diff -Naur xmms-1.2.8/xmms/charset.c xmms-1.2.8-new/xmms/charset.c
+--- xmms-1.2.8/xmms/charset.c 1970-01-01 01:00:00.000000000 +0100
++++ xmms-1.2.8-new/xmms/charset.c 2003-09-06 19:42:29.000000000 +0200
+@@ -0,0 +1,393 @@
++#include <stdio.h>
++#include <locale.h>
++#include <string.h>
++#include <gtk/gtk.h>
++#include "xmms.h"
++#include "libxmms/charset.h"
++#include "charset.h"
++
++#define CHARSET_MAX_STRING_SIZE 1024 /* 256 symbols in UTF-32 */
++static char xmms_charset_tmp[CHARSET_MAX_STRING_SIZE];
++
++extern TextBox *playlistwin_sinfo;
++
++static gint selected_id3_charset, selected_output_charset, selected_fs_charset;
++static gint selected_pl_charset;
++static gint selected_autocharset;
++static GtkWidget *prefswin_options_font_shade_entry, *prefswin_options_font_shade_browse;
++
++static void prefswin_font_shade_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_options_font_shade_entry), fontname);
++
++ gtk_widget_destroy(GTK_WIDGET(fontsel));
++}
++
++static void prefswin_font_shade_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_options_font_shade_entry)));
++ gtk_signal_connect(GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->ok_button), "clicked", GTK_SIGNAL_FUNC(prefswin_font_shade_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);
++}
++
++
++static void prefswin_charset_cb(GtkWidget * w, gpointer item)
++{
++ int i;
++ i = GPOINTER_TO_INT(item);
++ if (i>=0x3FFFF) selected_pl_charset=i-0x3FFFF;
++ else if (i>=0x2FFFF) selected_autocharset=i-0x2FFFF;
++ else if (i>=0x1FFFF) selected_fs_charset=i-0x1FFFF;
++ else if (i>=0xFFFF) selected_output_charset=i-0xFFFF;
++ else selected_id3_charset=i;
++}
++
++static void charset_options_add(Config *cfg, GtkOptionMenu *cmenu1, GtkOptionMenu *cmenu2, GtkOptionMenu *cmenu3, GtkOptionMenu *cmenu4)
++{
++ int i;
++ GtkWidget *menu1, *item1;
++ GtkWidget *menu2, *item2;
++ GtkWidget *menu3, *item3;
++ GtkWidget *menu4, *item4;
++
++ menu1 = gtk_menu_new();
++ gtk_option_menu_remove_menu(cmenu1);
++ gtk_option_menu_set_menu(cmenu1, menu1);
++
++ menu2 = gtk_menu_new();
++ gtk_option_menu_remove_menu(cmenu2);
++ gtk_option_menu_set_menu(cmenu2, menu2);
++
++ menu3 = gtk_menu_new();
++ gtk_option_menu_remove_menu(cmenu3);
++ gtk_option_menu_set_menu(cmenu3, menu3);
++
++ menu4 = gtk_menu_new();
++ gtk_option_menu_remove_menu(cmenu4);
++ gtk_option_menu_set_menu(cmenu4, menu4);
++
++ selected_id3_charset=0;
++ selected_output_charset=0;
++ selected_fs_charset=0;
++ selected_pl_charset=0;
++
++ for (i=0;charset_list[i];i++) {
++ item1 = gtk_menu_item_new_with_label(charset_list[i]);
++ gtk_signal_connect(GTK_OBJECT(item1), "activate", GTK_SIGNAL_FUNC(prefswin_charset_cb), GINT_TO_POINTER(i));
++ gtk_widget_show(item1);
++ gtk_menu_append(GTK_MENU(menu1), item1);
++ item2 = gtk_menu_item_new_with_label(charset_list[i]);
++ gtk_signal_connect(GTK_OBJECT(item2), "activate", GTK_SIGNAL_FUNC(prefswin_charset_cb), GINT_TO_POINTER(i+0xFFFF));
++ gtk_widget_show(item2);
++ gtk_menu_append(GTK_MENU(menu2), item2);
++ item3 = gtk_menu_item_new_with_label(charset_list[i]);
++ gtk_signal_connect(GTK_OBJECT(item3), "activate", GTK_SIGNAL_FUNC(prefswin_charset_cb), GINT_TO_POINTER(i+0x1FFFF));
++ gtk_widget_show(item3);
++ gtk_menu_append(GTK_MENU(menu3), item3);
++ item4 = gtk_menu_item_new_with_label(charset_list[i]);
++ gtk_signal_connect(GTK_OBJECT(item4), "activate", GTK_SIGNAL_FUNC(prefswin_charset_cb), GINT_TO_POINTER(i+0x3FFFF));
++ gtk_widget_show(item4);
++ gtk_menu_append(GTK_MENU(menu4), item4);
++ if ((cfg->charset_id3)&&(!strcmp(cfg->charset_id3,charset_list[i]))) {
++ selected_id3_charset=i;
++ }
++ if ((cfg->charset_output)&&(!strcmp(cfg->charset_output,charset_list[i]))) {
++ selected_output_charset=i;
++ }
++ if ((cfg->charset_fs)&&(!strcmp(cfg->charset_fs,charset_list[i]))) {
++ selected_fs_charset=i;
++ }
++ if ((cfg->charset_pl)&&(!strcmp(cfg->charset_pl,charset_list[i]))) {
++ selected_pl_charset=i;
++ }
++ }
++
++ gtk_option_menu_set_history(cmenu1, selected_id3_charset);
++ gtk_option_menu_set_history(cmenu2, selected_output_charset);
++ gtk_option_menu_set_history(cmenu3, selected_fs_charset);
++ gtk_option_menu_set_history(cmenu4, selected_pl_charset);
++}
++
++static void autocharset_options_add(Config *cfg, GtkOptionMenu *cmenu)
++{
++ int i;
++ GtkWidget *menu, *item;
++
++ menu = gtk_menu_new();
++ gtk_option_menu_remove_menu(cmenu);
++ gtk_option_menu_set_menu(cmenu, menu);
++
++ selected_autocharset=0;
++
++
++ for (i=0;i<autocharset_list_ni;i++) {
++ item = gtk_menu_item_new_with_label(autocharset_list[i].title);
++ gtk_signal_connect(GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(prefswin_charset_cb), GINT_TO_POINTER(i+0x2FFFF));
++ gtk_widget_show(item);
++ gtk_menu_append(GTK_MENU(menu), item);
++ if ((cfg->autocharset)&&(cfg->autocharset==i)) {
++ selected_autocharset=i;
++ }
++ }
++
++ gtk_option_menu_set_history(cmenu, selected_autocharset);
++}
++
++void charset_create_prefs_window(Config *cfg, GtkNotebook *prefswin_notebook, GtkWidget *prefswin_fonts_vbox) {
++ GtkWidget *prefswin_encodings_vbox;
++ GtkWidget *prefswin_fonts_shade_frame;
++ GtkWidget *options_font_shade_hbox, *options_font_shade_vbox;
++ GtkWidget *prefswin_charset_frame, *prefswin_charset_vbox;
++ GtkWidget *prefswin_charset_list1, *prefswin_charset_list2, *prefswin_charset_list3, *prefswin_charset_list4;
++ GtkWidget *prefswin_charset_hbox1, *prefswin_charset_hbox2, *prefswin_charset_hbox3, *prefswin_charset_hbox4;
++ GtkWidget *prefswin_charset_label1, *prefswin_charset_label2, *prefswin_charset_label3, *prefswin_charset_label4;
++ GtkWidget *prefswin_autocharset_frame, *prefswin_autocharset_vbox;
++ GtkWidget *prefswin_autocharset_list;
++ GtkWidget *prefswin_autocharset_hbox;
++ GtkWidget *prefswin_autocharset_label;
++
++ 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_options_font_shade_entry = gtk_entry_new();
++ gtk_box_pack_start(GTK_BOX(options_font_shade_hbox), prefswin_options_font_shade_entry, TRUE, TRUE, 0);
++ prefswin_options_font_shade_browse = gtk_button_new_with_label(_("Browse"));
++ gtk_signal_connect(GTK_OBJECT(prefswin_options_font_shade_browse), "clicked", GTK_SIGNAL_FUNC(prefswin_font_shade_browse_cb), NULL);
++ gtk_widget_set_usize(prefswin_options_font_shade_browse, 85, 17);
++ gtk_box_pack_start(GTK_BOX(options_font_shade_hbox), prefswin_options_font_shade_browse, FALSE, TRUE, 0);
++
++ prefswin_encodings_vbox = gtk_vbox_new(FALSE, 0);
++
++ prefswin_charset_frame = gtk_frame_new(_("Encodings"));
++ gtk_box_pack_start(GTK_BOX(prefswin_encodings_vbox), prefswin_charset_frame, FALSE, FALSE, 0);
++ gtk_container_border_width(GTK_CONTAINER(prefswin_charset_frame), 5);
++ prefswin_charset_vbox = gtk_vbox_new(FALSE, 3);
++ gtk_container_add(GTK_CONTAINER(prefswin_charset_frame), prefswin_charset_vbox);
++ gtk_container_border_width(GTK_CONTAINER(prefswin_charset_vbox), 5);
++
++ prefswin_charset_hbox1 = gtk_hbox_new(FALSE, 10);
++ gtk_container_add(GTK_CONTAINER(prefswin_charset_vbox), prefswin_charset_hbox1);
++ gtk_container_border_width(GTK_CONTAINER(prefswin_charset_hbox1), 5);
++ prefswin_charset_label1 = gtk_label_new(_("ID3 Encoding"));
++ gtk_widget_set_usize(prefswin_charset_label1, 120, 17);
++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox1), prefswin_charset_label1, FALSE, FALSE, 0);
++ prefswin_charset_list1 = gtk_option_menu_new();
++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox1), prefswin_charset_list1, TRUE, TRUE, 0);
++
++ prefswin_charset_hbox3 = gtk_hbox_new(FALSE, 10);
++ gtk_container_add(GTK_CONTAINER(prefswin_charset_vbox), prefswin_charset_hbox3);
++ gtk_container_border_width(GTK_CONTAINER(prefswin_charset_hbox3), 5);
++ prefswin_charset_label3 = gtk_label_new(_("FileSystem Encoding"));
++ gtk_widget_set_usize(prefswin_charset_label3, 120, 17);
++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox3), prefswin_charset_label3, FALSE, FALSE, 0);
++ prefswin_charset_list3 = gtk_option_menu_new();
++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox3), prefswin_charset_list3, TRUE, TRUE, 0);
++
++ prefswin_charset_hbox4 = gtk_hbox_new(FALSE, 10);
++ gtk_container_add(GTK_CONTAINER(prefswin_charset_vbox), prefswin_charset_hbox4);
++ gtk_container_border_width(GTK_CONTAINER(prefswin_charset_hbox4), 5);
++ prefswin_charset_label4 = gtk_label_new(_("Playlist Encoding"));
++ gtk_widget_set_usize(prefswin_charset_label4, 120, 17);
++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox4), prefswin_charset_label4, FALSE, FALSE, 0);
++ prefswin_charset_list4 = gtk_option_menu_new();
++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox4), prefswin_charset_list4, TRUE, TRUE, 0);
++
++ prefswin_charset_hbox2 = gtk_hbox_new(FALSE, 10);
++ gtk_container_add(GTK_CONTAINER(prefswin_charset_vbox), prefswin_charset_hbox2);
++ gtk_container_border_width(GTK_CONTAINER(prefswin_charset_hbox2), 5);
++ prefswin_charset_label2 = gtk_label_new(_("Output Encoding"));
++ gtk_widget_set_usize(prefswin_charset_label2, 120, 17);
++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox2), prefswin_charset_label2, FALSE, FALSE, 0);
++ prefswin_charset_list2 = gtk_option_menu_new();
++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox2), prefswin_charset_list2, TRUE, TRUE, 0);
++
++ charset_options_add(cfg, GTK_OPTION_MENU(prefswin_charset_list1),GTK_OPTION_MENU(prefswin_charset_list2),GTK_OPTION_MENU(prefswin_charset_list3), GTK_OPTION_MENU(prefswin_charset_list4));
++
++ prefswin_autocharset_frame = gtk_frame_new(_("Encoding Autoselection"));
++ gtk_box_pack_start(GTK_BOX(prefswin_encodings_vbox), prefswin_autocharset_frame, FALSE, FALSE, 0);
++ gtk_container_border_width(GTK_CONTAINER(prefswin_autocharset_frame), 5);
++ prefswin_autocharset_vbox = gtk_vbox_new(FALSE, 3);
++ gtk_container_add(GTK_CONTAINER(prefswin_autocharset_frame), prefswin_autocharset_vbox);
++ gtk_container_border_width(GTK_CONTAINER(prefswin_autocharset_vbox), 5);
++
++ prefswin_autocharset_hbox = gtk_hbox_new(FALSE, 10);
++ gtk_container_add(GTK_CONTAINER(prefswin_autocharset_vbox), prefswin_autocharset_hbox);
++ gtk_container_border_width(GTK_CONTAINER(prefswin_autocharset_hbox), 5);
++ prefswin_autocharset_label = gtk_label_new(_("Language"));
++ gtk_widget_set_usize(prefswin_autocharset_label, 120, 17);
++ gtk_box_pack_start(GTK_BOX(prefswin_autocharset_hbox), prefswin_autocharset_label, FALSE, FALSE, 0);
++ prefswin_autocharset_list = gtk_option_menu_new();
++ gtk_box_pack_start(GTK_BOX(prefswin_autocharset_hbox), prefswin_autocharset_list, TRUE, TRUE, 0);
++
++ autocharset_options_add(cfg, GTK_OPTION_MENU(prefswin_autocharset_list));
++
++ gtk_notebook_append_page(prefswin_notebook, prefswin_encodings_vbox, gtk_label_new(_("Encodings")));
++}
++
++void charset_prefswin_apply_changes(Config *cfg) {
++ if (cfg->charset_id3) g_free(cfg->charset_id3);
++ if (cfg->charset_output) g_free(cfg->charset_output);
++ if (cfg->charset_fs) g_free(cfg->charset_fs);
++ if (cfg->charset_pl) g_free(cfg->charset_pl);
++ cfg->charset_id3=g_strdup(charset_list[selected_id3_charset]);
++ cfg->charset_output=g_strdup(charset_list[selected_output_charset]);
++ cfg->charset_fs=g_strdup(charset_list[selected_fs_charset]);
++ cfg->charset_pl=g_strdup(charset_list[selected_pl_charset]);
++ xmms_charset_new_charsets(selected_id3_charset, selected_output_charset, selected_fs_charset, selected_pl_charset);
++ cfg->autocharset=selected_autocharset;
++ xmms_autocharset_new_mode(selected_autocharset);
++
++ if (cfg->shade_font) g_free(cfg->shade_font);
++ cfg->shade_font = g_strdup(gtk_entry_get_text(GTK_ENTRY(prefswin_options_font_shade_entry)));
++ textbox_set_xfont(playlistwin_sinfo, cfg->mainwin_use_xfont, cfg->shade_font);
++}
++
++void charset_show_prefs_window(Config *cfg) {
++ gtk_entry_set_text(GTK_ENTRY(prefswin_options_font_shade_entry), cfg->shade_font);
++}
++
++void charset_create_playlist_window(Config *cfg) {
++ textbox_set_xfont(playlistwin_sinfo, cfg->mainwin_use_xfont, cfg->shade_font);
++}
++/*****************************************************************************
++************************ Initialization / Deinitilization ********************
++*****************************************************************************/
++void charset_free(Config *cfg) {
++ xmms_charset_free();
++ xmms_autocharset_free();
++}
++
++void charset_init(Config *cfg) {
++ xmms_charset_init(cfg->charset_id3, cfg->charset_output, cfg->charset_fs, cfg->charset_pl);
++ xmms_autocharset_init(cfg->autocharset);
++}
++
++void charset_read_config(ConfigFile *cfgfile, Config *cfg) {
++ cfg->autocharset=0;
++ xmms_cfg_read_string(cfgfile, "xmms", "shade_font", &cfg->shade_font);
++ xmms_cfg_read_string(cfgfile, "xmms", "charset_id3", &cfg->charset_id3);
++ xmms_cfg_read_string(cfgfile, "xmms", "charset_output", &cfg->charset_output);
++ xmms_cfg_read_string(cfgfile, "xmms", "charset_fs", &cfg->charset_fs);
++ xmms_cfg_read_string(cfgfile, "xmms", "charset_pl", &cfg->charset_pl);
++ xmms_cfg_read_int(cfgfile,"xmms","autocharset",&cfg->autocharset);
++}
++
++void charset_write_config(ConfigFile *cfgfile, Config *cfg) {
++ xmms_cfg_write_string(cfgfile, "xmms", "shade_font", cfg->shade_font);
++ xmms_cfg_write_string(cfgfile, "xmms", "charset_id3", cfg->charset_id3);
++ xmms_cfg_write_string(cfgfile, "xmms", "charset_output", cfg->charset_output);
++ xmms_cfg_write_string(cfgfile, "xmms", "charset_fs", cfg->charset_fs);
++ xmms_cfg_write_string(cfgfile, "xmms", "charset_pl", cfg->charset_pl);
++ xmms_cfg_write_int(cfgfile,"xmms","autocharset",cfg->autocharset);
++}
++
++void charset_default_config(Config *cfg) {
++ if (cfg->shade_font && strlen(cfg->shade_font) == 0)
++ {
++ g_free(cfg->shade_font);
++ cfg->shade_font = NULL;
++ }
++ if (cfg->shade_font == NULL)
++ cfg->shade_font = g_strdup("-misc-fixed-medium-r-*-*-7-*");
++}
++
++/*****************************************************************************
++*********************************** Sort *************************************
++*****************************************************************************/
++gchar *saved_locale;
++
++static void charset_sort_null_entry(gpointer entry, gpointer data) {
++ ((PlaylistEntry*)entry)->sort=NULL;
++}
++
++static void charset_sort_free_entry(gpointer entry, gpointer data) {
++ if (((PlaylistEntry*)entry)->sort)
++ g_free(((PlaylistEntry*)entry)->sort);
++}
++
++void charset_sort_prepare(GList *playlist) {
++ g_list_foreach(playlist, charset_sort_null_entry, NULL);
++ saved_locale=g_strdup(setlocale(LC_COLLATE,NULL));
++ setlocale(LC_COLLATE,"UTF-8");
++}
++
++void charset_sort_free(GList *playlist) {
++ g_list_foreach(playlist, charset_sort_free_entry, NULL);
++ if (saved_locale) {
++ setlocale(LC_COLLATE,saved_locale);
++ g_free(saved_locale);
++ }
++}
++
++char *charset_sort_convert(PlaylistEntry *entry, gchar *title, int fs) {
++ char *str;
++ if (entry->sort) return entry->sort;
++ if (fs)
++ str=xmms_charset_recode_fs2utf(title,0,NULL);
++ else
++ str=xmms_charset_recode_id2utf(title,0,NULL);
++
++ if (!str)
++ entry->sort=g_strdup(title);
++ else {
++ strxfrm(xmms_charset_tmp,str,CHARSET_MAX_STRING_SIZE);
++// *(int*)(xmms_charset_tmp+(CHARSET_MAX_STRING_SIZE-sizeof(int)))=0;
++ xmms_charset_tmp[CHARSET_MAX_STRING_SIZE-1]=0;
++ entry->sort=g_strdup(xmms_charset_tmp);
++ g_free(str);
++ }
++
++ if (entry->sort)
++ return entry->sort;
++
++ return title;
++}
++
++/*****************************************************************************
++*********************************** Misc *************************************
++*****************************************************************************/
++
++void textbox_set_ctext(TextBox * tb, gchar * text)
++{
++ gchar *rtext;
++
++ lock_widget(tb);
++
++ rtext=xmms_charset_recode_id3(text,0,NULL);
++ if (rtext) text=rtext;
++ if (tb->tb_text)
++ {
++ if (!strcmp(text, tb->tb_text))
++ {
++ if (rtext) free(rtext);
++ unlock_widget(tb);
++ return;
++ }
++ g_free(tb->tb_text);
++ }
++
++ tb->tb_text = g_strdup(text);
++ if (rtext) free(rtext);
++
++ unlock_widget(tb);
++ draw_widget(tb);
++}
+diff -Naur xmms-1.2.8/xmms/charset.h xmms-1.2.8-new/xmms/charset.h
+--- xmms-1.2.8/xmms/charset.h 1970-01-01 01:00:00.000000000 +0100
++++ xmms-1.2.8-new/xmms/charset.h 2003-09-06 19:42:29.000000000 +0200
+@@ -0,0 +1,19 @@
++#include "libxmms/configfile.h"
++#include "libxmms/charset.h"
++void charset_create_prefs_window(Config *cfg, GtkNotebook *prefswin_notebook, GtkWidget *prefswin_fonts_vbox);
++void charset_show_prefs_window(Config *cfg);
++void charset_prefswin_apply_changes(Config *cfg);
++void charset_create_playlist_window(Config *cfg);
++
++void charset_init(Config *cfg);
++void charset_free(Config *cfg);
++
++void charset_read_config(ConfigFile *cfgfile, Config *cfg);
++void charset_default_config(Config *cfg);
++void charset_write_config(ConfigFile *cfgfile, Config *cfg);
++
++void charset_sort_prepare(GList *playlist);
++void charset_sort_free(GList *playlist);
++char *charset_sort_convert(PlaylistEntry *entry, gchar *title, int fs);
++
++void textbox_set_ctext(TextBox * tb, gchar * text);
+diff -Naur xmms-1.2.8/xmms/input.c xmms-1.2.8-new/xmms/input.c
+--- xmms-1.2.8/xmms/input.c 2003-07-14 15:24:28.000000000 +0200
++++ xmms-1.2.8-new/xmms/input.c 2003-09-06 19:42:29.000000000 +0200
+@@ -22,6 +22,7 @@
+ #include "libxmms/titlestring.h"
+ #include "libxmms/util.h"
+ #include "libxmms/xentry.h"
++#include "libxmms/charset.h"
+
+ static pthread_mutex_t vis_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+@@ -343,7 +344,7 @@
+ {
+ GList *node;
+ InputPlugin *ip = NULL;
+-
++
+ node = get_input_list();
+ while (node)
+ {
+@@ -371,8 +372,11 @@
+
+ (*title) = xmms_get_titlestring(xmms_get_gentitle_format(),
+ input);
+- if ( (*title) == NULL )
+- (*title) = g_strdup(input->file_name);
++ if ( (*title) == NULL ) {
++ (*title) = xmms_charset_recode_fs(input->file_name,0,NULL);
++ if (!*title) (*title) = g_strdup(input->file_name);
++ }
++
+ (*length) = -1;
+ g_free(temp);
+ g_free(input);
+diff -Naur xmms-1.2.8/xmms/main.c xmms-1.2.8-new/xmms/main.c
+--- xmms-1.2.8/xmms/main.c 2003-09-02 15:01:40.000000000 +0200
++++ xmms-1.2.8-new/xmms/main.c 2003-09-06 19:42:29.000000000 +0200
+@@ -35,6 +35,7 @@
+ #include "libxmms/xmmsctrl.h"
+ #include "libxmms/util.h"
+ #include "libxmms/dirbrowser.h"
++#include "charset.h"
+ #include "xmms_mini.xpm"
+
+ GtkWidget *mainwin, *mainwin_url_window = NULL, *mainwin_dir_browser = NULL;
+@@ -377,7 +378,6 @@
+
+ cfg.gentitle_format = NULL;
+
+-
+ filename = g_strconcat(g_get_home_dir(), "/.xmms/config", NULL);
+ cfgfile = xmms_cfg_open_file(filename);
+ if (cfgfile)
+@@ -428,6 +428,7 @@
+ xmms_cfg_read_boolean(cfgfile, "xmms", "use_fontsets", &cfg.use_fontsets);
+ xmms_cfg_read_boolean(cfgfile, "xmms", "mainwin_use_xfont", &cfg.mainwin_use_xfont);
+ xmms_cfg_read_string(cfgfile, "xmms", "mainwin_font", &cfg.mainwin_font);
++ charset_read_config(cfgfile,&cfg);
+ xmms_cfg_read_int(cfgfile, "xmms", "playlist_position", &cfg.playlist_position);
+ xmms_cfg_read_int(cfgfile, "xmms", "equalizer_x", &cfg.equalizer_x);
+ xmms_cfg_read_int(cfgfile, "xmms", "equalizer_y", &cfg.equalizer_y);
+@@ -514,6 +515,8 @@
+ if (cfg.eqpreset_extension == NULL)
+ cfg.eqpreset_extension = g_strdup("preset");
+
++ charset_default_config(&cfg);
++
+ g_free(filename);
+ }
+
+@@ -594,6 +597,7 @@
+ xmms_cfg_write_boolean(cfgfile, "xmms", "use_fontsets", cfg.use_fontsets);
+ xmms_cfg_write_boolean(cfgfile, "xmms", "mainwin_use_xfont", cfg.mainwin_use_xfont);
+ xmms_cfg_write_string(cfgfile, "xmms", "mainwin_font", cfg.mainwin_font);
++ charset_write_config(cfgfile,&cfg);
+ xmms_cfg_write_int(cfgfile, "xmms", "playlist_position", get_playlist_position());
+ /* dock_get_widget_pos(equalizerwin, &cfg.equalizer_x, &cfg.equalizer_y); */
+ xmms_cfg_write_int(cfgfile, "xmms", "equalizer_x", cfg.equalizer_x);
+@@ -893,6 +897,7 @@
+ gtk_timeout_remove(mainwin_timeout_tag);
+ util_set_cursor(NULL);
+ save_config();
++ charset_free(&cfg);
+ cleanup_ctrlsocket();
+ playlist_stop_get_info_thread();
+ playlist_clear();
+@@ -991,11 +996,11 @@
+
+ if ((text = input_get_info_text()) != NULL)
+ {
+- textbox_set_text(mainwin_info, text);
++ textbox_set_ctext(mainwin_info, text);
+ }
+ else if ((text = playlist_get_info_text()) != NULL)
+ {
+- textbox_set_text(mainwin_info, text);
++ textbox_set_ctext(mainwin_info, text);
+ g_free(text);
+ }
+ else
+@@ -3491,6 +3496,7 @@
+ #endif
+
+ read_config();
++ charset_init(&cfg);
+
+ if (geteuid() == 0)
+ {
+diff -Naur xmms-1.2.8/xmms/main.h xmms-1.2.8-new/xmms/main.h
+--- xmms-1.2.8/xmms/main.h 2002-10-06 18:35:27.000000000 +0200
++++ xmms-1.2.8-new/xmms/main.h 2003-09-06 19:42:29.000000000 +0200
+@@ -19,6 +19,7 @@
+ */
+ #ifndef MAIN_H
+ #define MAIN_H
++#include "libxmms/charset.h"
+
+ typedef enum
+ {
+@@ -60,6 +61,9 @@
+ gint mouse_change;
+ gboolean playlist_transparent;
+ gchar *gentitle_format;
++ gchar *charset_id3, *charset_output, *charset_fs, *charset_pl;
++ gint autocharset;
++ gchar *shade_font;
+ }
+ Config;
+
+diff -Naur xmms-1.2.8/xmms/Makefile.am xmms-1.2.8-new/xmms/Makefile.am
+--- xmms-1.2.8/xmms/Makefile.am 2003-08-11 15:47:41.000000000 +0200
++++ xmms-1.2.8-new/xmms/Makefile.am 2003-09-06 19:42:29.000000000 +0200
+@@ -56,7 +56,8 @@
+ getopt.c getopt1.c getopt.h \
+ urldecode.c urldecode.h \
+ dnd.h \
+-mkdtemp.c
++mkdtemp.c \
++charset.c charset.h
+
+ EXTRA_DIST= xmms_logo.xpm xmms_mini.xpm xmms.desktop xmms.wmconfig
+
+diff -Naur xmms-1.2.8/xmms/Makefile.in xmms-1.2.8-new/xmms/Makefile.in
+--- xmms-1.2.8/xmms/Makefile.in 2003-09-04 23:01:20.000000000 +0200
++++ xmms-1.2.8-new/xmms/Makefile.in 2003-09-06 19:42:29.000000000 +0200
+@@ -228,8 +228,8 @@
+ getopt.c getopt1.c getopt.h \
+ urldecode.c urldecode.h \
+ dnd.h \
+-mkdtemp.c
+-
++mkdtemp.c \
++charset.c charset.h
+
+ EXTRA_DIST = xmms_logo.xpm xmms_mini.xpm xmms.desktop xmms.wmconfig
+
+@@ -257,7 +257,7 @@
+ main.$(OBJEXT) skinwin.$(OBJEXT) prefswin.$(OBJEXT) \
+ playlistwin.$(OBJEXT) equalizer.$(OBJEXT) hints.$(OBJEXT) \
+ about.$(OBJEXT) sm.$(OBJEXT) getopt.$(OBJEXT) getopt1.$(OBJEXT) \
+-urldecode.$(OBJEXT) mkdtemp.$(OBJEXT)
++urldecode.$(OBJEXT) mkdtemp.$(OBJEXT) charset.$(OBJEXT)
+ xmms_DEPENDENCIES = $(top_builddir)/libxmms/libxmms.la
+ CFLAGS = @CFLAGS@
+ COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+diff -Naur xmms-1.2.8/xmms/playlist.c xmms-1.2.8-new/xmms/playlist.c
+--- xmms-1.2.8/xmms/playlist.c 2003-09-02 15:01:40.000000000 +0200
++++ xmms-1.2.8-new/xmms/playlist.c 2003-09-06 19:42:29.000000000 +0200
+@@ -20,6 +20,7 @@
+ #include "xmms.h"
+ #include <time.h>
+ #include "libxmms/util.h"
++#include "charset.h"
+ #include <sys/stat.h>
+ #include <unistd.h>
+
+@@ -939,6 +940,7 @@
+ char *playlist_get_info_text(void)
+ {
+ char *text, *title, *tmp, *numbers, *length;
++ char *ctitle;
+
+ PL_LOCK();
+ if (!playlist_position)
+@@ -947,10 +949,17 @@
+ return NULL;
+ }
+
+- if (playlist_position->title)
++ if (playlist_position->title) {
++ ctitle = xmms_charset_recode_id3(playlist_position->title,0,NULL);
++ if (ctitle) title=ctitle;
++ else
+ title = playlist_position->title;
+- else
++ } else {
++ ctitle = xmms_charset_recode_fsout(g_basename(playlist_position->filename),0,NULL);
++ if (ctitle) title=ctitle;
++ else
+ title = g_basename(playlist_position->filename);
++ }
+
+ /*
+ * If the user don't want numbers in the playlist, don't
+@@ -972,6 +981,7 @@
+ text = g_strdup_printf("%s%s%s", numbers, title, length);
+ g_free(numbers);
+ g_free(length);
++ if (ctitle) g_free(ctitle);
+
+ PL_UNLOCK();
+
+@@ -1007,6 +1017,7 @@
+ {
+ GList *node;
+ FILE *file;
++ gchar *fn, *cfn;
+
+ if ((file = fopen(filename, "w")) == NULL)
+ return FALSE;
+@@ -1025,10 +1036,13 @@
+ while (node)
+ {
+ PlaylistEntry *entry = node->data;
++ cfn=xmms_charset_recode_fs2pl(entry->filename,0,NULL);
++ if (cfn) fn=cfn;
++ else fn=entry->filename;
+ if (is_pls)
+ fprintf(file, "File%d=%s\n",
+ g_list_position(playlist, node) + 1,
+- entry->filename);
++ fn);
+ else
+ {
+ if (entry->title && cfg.use_pl_metadata)
+@@ -1043,8 +1057,9 @@
+ fprintf(file, "#EXTINF:%d,%s\n",
+ seconds, entry->title);
+ }
+- fprintf(file, "%s\n", entry->filename);
++ fprintf(file, "%s\n", fn);
+ }
++ if (cfn) g_free(cfn);
+ node = g_list_next(node);
+ }
+ PL_UNLOCK();
+@@ -1121,6 +1136,7 @@
+ {
+ FILE *file;
+ char *line, *ext;
++ char *cline;
+ guint entries = 0;
+ int linelen = 1024;
+ gboolean extm3u = FALSE;
+@@ -1147,8 +1163,10 @@
+ "playlist", key);
+ if (line != NULL)
+ {
+- playlist_load_ins_file(line, filename, pos,
++ cline = xmms_charset_recode_pl2fs(line,0,NULL);
++ playlist_load_ins_file(cline ? cline : line, filename, pos,
+ NULL, -1);
++ if (cline) g_free(cline);
+ entries++;
+ if (pos >= 0)
+ pos++;
+@@ -1215,7 +1233,9 @@
+ ext_info = NULL;
+ }
+
+- playlist_load_ins_file(line, filename, pos, ext_title, ext_len);
++ cline = xmms_charset_recode_pl2fs(line,linelen,NULL);
++ playlist_load_ins_file(cline ? cline : line, filename, pos, ext_title, ext_len);
++ if (cline) g_free(cline);
+
+ g_free(ext_title);
+ ext_title = NULL;
+@@ -1308,19 +1328,27 @@
+
+ if (entry->title == NULL && entry->length == -1)
+ {
+- if (playlist_get_info_entry(entry))
++ if (playlist_get_info_entry(entry)) {
++ title = xmms_charset_recode_id3(entry->title,0,NULL);
++ if (!title)
+ title = g_strdup(entry->title);
++ }
+
+ PL_UNLOCK();
+ }
+ else
+ {
++ title = xmms_charset_recode_id3(entry->title,0,NULL);
++ if (!title)
+ title = g_strdup(entry->title);
+ PL_UNLOCK();
+ }
+
+- if (title == NULL)
++ if (title == NULL) {
++ title = xmms_charset_recode_fsout(g_basename(filename),0,NULL);
++ if (!title)
+ title = g_strdup(g_basename(filename));
++ }
+
+ g_free(filename);
+
+@@ -1366,6 +1394,7 @@
+ static int playlist_sort_by_title_cmpfunc(PlaylistEntry * a, PlaylistEntry * b)
+ {
+ char *a_title, *b_title;
++ int a_fs=0, b_fs=0;
+
+ if (a->title)
+ a_title = a->title;
+@@ -1375,6 +1404,7 @@
+ a_title = strrchr(a->filename, '/') + 1;
+ else
+ a_title = a->filename;
++ a_fs=1;
+ }
+
+ if (b->title)
+@@ -1385,15 +1415,21 @@
+ b_title = strrchr(b->filename, '/') + 1;
+ else
+ b_title = b->filename;
+-
++ b_fs=1;
+ }
++
++ a_title=charset_sort_convert(a,a_title,a_fs);
++ b_title=charset_sort_convert(b,b_title,b_fs);
++
+ return strcasecmp(a_title, b_title);
+ }
+
+ void playlist_sort_by_title(void)
+ {
+ PL_LOCK();
++ charset_sort_prepare(playlist);
+ playlist = g_list_sort(playlist, (GCompareFunc) playlist_sort_by_title_cmpfunc);
++ charset_sort_free(playlist);
+ PL_UNLOCK();
+ }
+
+@@ -1411,13 +1447,18 @@
+ else
+ b_filename = b->filename;
+
++ a_filename=charset_sort_convert(a,a_filename,1);
++ b_filename=charset_sort_convert(b,b_filename,1);
++
+ return strcasecmp(a_filename, b_filename);
+ }
+
+ void playlist_sort_by_filename(void)
+ {
+ PL_LOCK();
++ charset_sort_prepare(playlist);
+ playlist = g_list_sort(playlist, (GCompareFunc) playlist_sort_by_filename_cmpfunc);
++ charset_sort_free(playlist);
+ PL_UNLOCK();
+ }
+
+@@ -1453,13 +1494,15 @@
+
+ static int playlist_sort_by_path_cmpfunc(PlaylistEntry * a, PlaylistEntry * b)
+ {
+- return playlist_sort_str_by_path_cmpfunc(a->filename, b->filename);
++ return playlist_sort_str_by_path_cmpfunc(charset_sort_convert(a,a->filename,1), charset_sort_convert(b,b->filename,1));
+ }
+
+ void playlist_sort_by_path(void)
+ {
+ PL_LOCK();
++ charset_sort_prepare(playlist);
+ playlist = g_list_sort(playlist, (GCompareFunc) playlist_sort_by_path_cmpfunc);
++ charset_sort_free(playlist);
+ PL_UNLOCK();
+ }
+
+@@ -1549,21 +1592,27 @@
+ void playlist_sort_selected_by_title(void)
+ {
+ PL_LOCK();
++ charset_sort_prepare(playlist);
+ playlist = playlist_sort_selected(playlist, (GCompareFunc) playlist_sort_by_title_cmpfunc);
++ charset_sort_free(playlist);
+ PL_UNLOCK();
+ }
+
+ void playlist_sort_selected_by_filename(void)
+ {
+ PL_LOCK();
++ charset_sort_prepare(playlist);
+ playlist = playlist_sort_selected(playlist, (GCompareFunc) playlist_sort_by_filename_cmpfunc);
++ charset_sort_free(playlist);
+ PL_UNLOCK();
+ }
+
+ void playlist_sort_selected_by_path(void)
+ {
+ PL_LOCK();
++ charset_sort_prepare(playlist);
+ playlist = playlist_sort_selected(playlist, (GCompareFunc) playlist_sort_by_path_cmpfunc);
++ charset_sort_free(playlist);
+ PL_UNLOCK();
+ }
+
+diff -Naur xmms-1.2.8/xmms/playlist.h xmms-1.2.8-new/xmms/playlist.h
+--- xmms-1.2.8/xmms/playlist.h 2003-06-09 15:22:10.000000000 +0200
++++ xmms-1.2.8-new/xmms/playlist.h 2003-09-06 19:42:29.000000000 +0200
+@@ -24,6 +24,7 @@
+ {
+ gchar *filename;
+ gchar *title;
++ gchar *sort;
+ gint length;
+ gboolean selected;
+ }
+diff -Naur xmms-1.2.8/xmms/playlist_list.c xmms-1.2.8-new/xmms/playlist_list.c
+--- xmms-1.2.8/xmms/playlist_list.c 2003-06-09 15:22:10.000000000 +0200
++++ xmms-1.2.8-new/xmms/playlist_list.c 2003-09-06 19:42:29.000000000 +0200
+@@ -287,6 +287,7 @@
+ {
+ GdkWChar *wtext;
+ int len, newlen;
++ gchar *rtext;
+ /*
+ * Convert the string to a wide character string to avoid
+ * destroying multibyte strings, when converting underscores,
+@@ -296,6 +297,10 @@
+ * Allocate some extra space, we might extend it by one
+ * character below
+ */
++
++ rtext = xmms_charset_recode_id3(text,0,NULL);
++ if (rtext) text = rtext;
++
+ wtext = g_malloc((strlen(text) + 3) * sizeof(GdkWChar));
+ len = gdk_mbstowcs(wtext, text, strlen(text) + 1);
+ if (len == -1)
+@@ -312,6 +317,7 @@
+ if (wtext[i] == L'_')
+ wtext[i] = L' ';
+ }
++ if (rtext) g_free(rtext);
+
+ if (cfg.convert_twenty && len > 2)
+ {
+@@ -361,6 +367,7 @@
+ {
+ int len;
+ char *tmp;
++ gchar *rtext;
+
+ if (cfg.convert_underscore)
+ while ((tmp = strchr(text, '_')) != NULL)
+@@ -384,7 +391,10 @@
+ text[len] = '\0';
+ }
+
++ rtext = xmms_charset_recode_id3(text,0,NULL);
++ if (rtext) text = rtext;
+ gdk_draw_text(pl->pl_widget.parent, font, pl->pl_widget.gc, pl->pl_widget.x, pl->pl_widget.y + line * pl->pl_fheight + font->ascent, text, len);
++ if (rtext) g_free(rtext);
+ }
+
+ void playlist_list_draw(Widget * w)
+@@ -396,6 +406,7 @@
+ int width, height;
+ char *text, *title;
+ int i, tw, max_first;
++ gchar *ctitle = NULL;
+
+ gc = pl->pl_widget.gc;
+ width = pl->pl_widget.width;
+@@ -453,8 +464,11 @@
+
+ if (entry->title)
+ title = entry->title;
+- else
+- title = g_basename(entry->filename);
++ else {
++ ctitle = xmms_charset_recode_fs(g_basename(entry->filename),0,NULL);
++ if (ctitle) title = ctitle;
++ else title = g_basename(entry->filename);
++ }
+
+ pos = playlist_get_queue_position(entry);
+
+@@ -497,6 +511,10 @@
+ playlist_list_draw_string(pl, playlist_list_font,
+ i - pl->pl_first, tw, text);
+ g_free(text);
++ if (ctitle) {
++ g_free(ctitle);
++ ctitle=NULL;
++ }
+ }
+ PL_UNLOCK();
+ }
+diff -Naur xmms-1.2.8/xmms/playlistwin.c xmms-1.2.8-new/xmms/playlistwin.c
+--- xmms-1.2.8/xmms/playlistwin.c 2003-04-04 19:42:51.000000000 +0200
++++ xmms-1.2.8-new/xmms/playlistwin.c 2003-09-06 20:24:02.000000000 +0200
+@@ -38,7 +38,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;
+@@ -1846,6 +1847,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);
++ charset_create_playlist_window(&cfg);
+ if (!cfg.playlist_shaded)
+ hide_widget(playlistwin_sinfo);
+ if (cfg.playlist_shaded)
+diff -Naur xmms-1.2.8/xmms/prefswin.c xmms-1.2.8-new/xmms/prefswin.c
+--- xmms-1.2.8/xmms/prefswin.c 2003-07-16 15:17:47.000000000 +0200
++++ xmms-1.2.8-new/xmms/prefswin.c 2003-09-06 19:42:29.000000000 +0200
+@@ -1,4 +1,4 @@
+-/* XMMS - Cross-platform multimedia player
++/* XMMS - Cdross-platform multimedia player
+ * Copyright (C) 1998-2001 Peter Alm, Mikael Alm, Olle Hallnas,
+ * Thomas Nilsson and 4Front Technologies
+ * Copyright (C) 1999-2001 Haavard Kvaalen
+@@ -20,6 +20,7 @@
+ #include "xmms.h"
+ #include "libxmms/util.h"
+ #include "libxmms/titlestring.h"
++#include "charset.h"
+
+ static GtkWidget *prefswin, *prefswin_notebook, *prefswin_ok;
+ static GtkWidget *prefswin_audio_ie_cbox;
+@@ -47,6 +48,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;
+@@ -351,6 +353,8 @@
+ 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));
+
++ charset_prefswin_apply_changes(&cfg);
++
+ set_current_output_plugin(selected_oplugin);
+
+ equalizerwin_set_doublesize(cfg.doublesize && cfg.eq_doublesize_linked);
+@@ -384,7 +388,6 @@
+ textbox_set_scroll(mainwin_info, FALSE);
+ textbox_set_scroll(mainwin_info, TRUE);
+ }
+-
+ if (show_wm_old != cfg.show_wm_decorations)
+ prefswin_toggle_wm_decorations();
+
+@@ -1089,6 +1092,8 @@
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(prefswin_notebook), prefswin_fonts_vbox, gtk_label_new(_("Fonts")));
+
++ charset_create_prefs_window(&cfg,GTK_NOTEBOOK(prefswin_notebook), prefswin_fonts_vbox);
++
+ /*
+ * Title page
+ */
+@@ -1358,6 +1363,8 @@
+ gtk_entry_set_text(GTK_ENTRY(prefswin_options_pbs_entry), temp);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(prefswin_options_mouse_spin), cfg.mouse_change);
+
++ charset_show_prefs_window(&cfg);
++
+ gtk_widget_show_all(prefswin);
+ gtk_widget_grab_default(prefswin_ok);
+
diff --git a/media-sound/xmms/files/xmms-1.2.8-sigterm.patch b/media-sound/xmms/files/xmms-1.2.8-sigterm.patch
new file mode 100644
index 000000000000..001b3df13209
--- /dev/null
+++ b/media-sound/xmms/files/xmms-1.2.8-sigterm.patch
@@ -0,0 +1,31 @@
+diff -Naur xmms-1.2.8/xmms/main.c xmms-1.2.8-new/xmms/main.c
+--- xmms-1.2.8/xmms/main.c 2003-09-02 15:01:40.000000000 +0200
++++ xmms-1.2.8-new/xmms/main.c 2003-09-06 18:15:15.000000000 +0200
+@@ -3343,6 +3343,18 @@
+ exit(1);
+ }
+
++/* Try to exit nicely when receiving a nice exit signal */
++void sigterm_handler(int sig)
++{
++ /* Original author of patch said that you should not use mainwin_quit_cb(),
++ * but his way deadlock xmms, and it anyhow just calls ctrlsocket stuff with
++ * with CMD_QUIT, which anyhow calls mainwin_quit_cb() in turn. This is not
++ * entirely clean, but works.
++ * <azarah@gentoo.org> (19 Jan 2003)
++ xmms_remote_quit(ctrlsocket_get_session_id()); */
++ mainwin_quit_cb();
++}
++
+ static gboolean pposition_configure(GtkWidget *w, GdkEventConfigure *event, gpointer data)
+ {
+ gint x,y;
+@@ -3473,6 +3485,8 @@
+ #endif
+
+ signal(SIGPIPE, SIG_IGN); /* for controlsocket.c */
++ signal(SIGTERM, sigterm_handler);
++ signal(SIGINT, sigterm_handler);
+ signal(SIGSEGV, segfault_handler);
+ g_thread_init(NULL);
+ if (!g_thread_supported())
diff --git a/media-sound/xmms/xmms-1.2.8-r1.ebuild b/media-sound/xmms/xmms-1.2.8-r1.ebuild
new file mode 100644
index 000000000000..dbbf773c9c40
--- /dev/null
+++ b/media-sound/xmms/xmms-1.2.8-r1.ebuild
@@ -0,0 +1,139 @@
+# Copyright 1999-2003 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+# $Header: /var/cvsroot/gentoo-x86/media-sound/xmms/xmms-1.2.8-r1.ebuild,v 1.1 2003/09/13 03:46:59 vapier Exp $
+
+inherit flag-o-matic eutils
+filter-flags -fforce-addr -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
+
+DESCRIPTION="X MultiMedia System"
+HOMEPAGE="http://www.xmms.org/"
+SRC_URI="http://www.xmms.org/files/1.2.x/${P}.tar.gz
+ mirror://gentoo/gentoo_ice.zip"
+
+LICENSE="GPL-2"
+SLOT="0"
+KEYWORDS="~x86 ~ppc ~sparc ~alpha ~hppa ~mips"
+IUSE="xml nls esd gnome opengl mmx oggvorbis 3dnow mikmod directfb ipv6 cjk"
+
+DEPEND="app-arch/unzip
+ =x11-libs/gtk+-1.2*
+ mikmod? ( >=media-libs/libmikmod-3.1.6 )
+ esd? ( >=media-sound/esound-0.2.22 )
+ xml? ( >=dev-libs/libxml-1.8.15 )
+ gnome? ( <gnome-base/gnome-panel-1.5.0 )
+ opengl? ( virtual/opengl )
+ oggvorbis? ( >=media-libs/libvorbis-1.0_beta4 )"
+RDEPEND="${DEPEND}
+ directfb? ( dev-libs/DirectFB )
+ nls? ( dev-util/intltool )"
+
+src_unpack() {
+ unpack ${P}.tar.gz
+ cd ${S}
+
+ # Patch to allow external programmes to have the "jump to" dialog box
+ epatch ${FILESDIR}/${P}-jump.patch
+
+ # Save playlist, etc on SIGTERM and SIGINT, bug #13604.
+ epatch ${FILESDIR}/${P}-sigterm.patch
+
+ # Patch for mpg123 to convert Japanese character code of MP3 tag info
+ # the Japanese patch and the Russian one overlap, so its one or the other
+ if use cjk; then
+ epatch ${FILESDIR}/${P}-mpg123j.patch
+ else
+ # add russian charset support
+ epatch ${FILESDIR}/${P}-russian-charset.patch
+ fi
+
+ # Add dynamic taste detection patch
+ epatch ${FILESDIR}/${P}-dtd.patch
+
+ if [ ! -f ${S}/config.rpath ] ; then
+ touch ${S}/config.rpath
+ chmod +x ${S}/config.rpath
+ fi
+
+ # Add /usr/local/share/xmms/Skins to the search path for skins
+ epatch ${FILESDIR}/${PN}-fhs-skinsdir.patch
+
+ # This patch passes audio output through the output plugin
+ # before recording via the diskwriter plugin
+ # http://forum.xmms.org/viewtopic.php?t=500&sid=c286e1c01fb924a2f81f519969f33764
+ epatch ${FILESDIR}/xmms-diskwriter-audio.patch
+
+ export WANT_AUTOCONF_2_5=1
+ for x in . libxmms ; do
+ cd ${S}/${x}
+ automake --gnu --add-missing --include-deps Makefile || die
+ done
+}
+
+src_compile() {
+ local myconf=""
+
+ # Allow configure to detect mipslinux systems
+ use mips && gnuconfig_update
+
+ if [ `use 3dnow` ] || [ `use mmx` ] ; then
+ myconf="${myconf} --enable-simd"
+ else
+ myconf="${myconf} --disable-simd"
+ fi
+
+ use xml || myconf="${myconf} --disable-cdindex"
+
+ econf \
+ --with-dev-dsp=/dev/sound/dsp \
+ --with-dev-mixer=/dev/sound/mixer \
+ `use_with gnome` \
+ `use_enable oggvorbis vorbis` \
+ `use_enable oggvorbis oggtest` \
+ `use_enable oggvorbis vorbistest` \
+ `use_enable esd` \
+ `use_enable esd esdtest` \
+ `use_enable mikmod` \
+ `use_enable mikmod mikmodtest` \
+ `use_with mikmod libmikmod` \
+ `use_enable opengl` \
+ `use_enable nls` \
+ `use_enable ipv6` \
+ ${myconf} \
+ || die
+
+ ### emake seems to break some compiles, please keep @ make
+ make || die
+}
+
+src_install() {
+ einstall \
+ incdir=${D}/usr/include \
+ sysdir=${D}/usr/share/applets/Multimedia \
+ GNOME_SYSCONFDIR=${D}/etc \
+ install || die "make install failed"
+
+ dodoc AUTHORS ChangeLog COPYING FAQ NEWS README TODO
+
+ keepdir /usr/share/xmms/Skins
+ insinto /usr/share/pixmaps/
+ donewins gnomexmms/gnomexmms.xpm xmms.xpm
+ doins xmms/xmms_logo.xpm
+ insinto /usr/share/pixmaps/mini
+ doins xmms/xmms_mini.xpm
+
+ insinto /etc/X11/wmconfig
+ donewins xmms/xmms.wmconfig xmms
+
+ if [ `use gnome` ] ; then
+ insinto /usr/share/gnome/apps/Multimedia
+ doins xmms/xmms.desktop
+ dosed "s:xmms_mini.xpm:mini/xmms_mini.xpm:" \
+ /usr/share/gnome/apps/Multimedia/xmms.desktop
+ else
+ rm ${D}/usr/share/man/man1/gnomexmms*
+ fi
+
+ # Add the sexy Gentoo Ice skin
+ insinto /usr/share/xmms/Skins
+ doins ${DISTDIR}/gentoo_ice.zip
+}