From ea80587ddb1905851623200b4cf5ef260f8c9631 Mon Sep 17 00:00:00 2001
From: John Regan <john@jrjrtech.com>
Date: Tue, 26 Sep 2017 08:42:53 -0500
Subject: [PATCH 01/10] GME Plugin: fix track numbering

GME starts all track indexes at zero, but subtune prefixes
start at one. This fixes an off-by-one error during track
enumeration.
---
 NEWS                                     | 1 +
 src/decoder/plugins/GmeDecoderPlugin.cxx | 4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/NEWS b/NEWS
index ae617cdce..92593f54c 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,7 @@ ver 0.20.11 (not yet released)
   - curl: support Content-Type application/xml
 * decoder
   - ffmpeg: more reliable song duration
+  - gme: fix track numbering
 * fix case insensitive search without libicu
 
 ver 0.20.10 (2017/08/24)
diff --git a/src/decoder/plugins/GmeDecoderPlugin.cxx b/src/decoder/plugins/GmeDecoderPlugin.cxx
index 403e62346..1b9d06163 100644
--- a/src/decoder/plugins/GmeDecoderPlugin.cxx
+++ b/src/decoder/plugins/GmeDecoderPlugin.cxx
@@ -293,13 +293,13 @@ gme_container_scan(Path path_fs)
 	TagBuilder tag_builder;
 
 	auto tail = list.before_begin();
-	for (unsigned i = 1; i <= num_songs; ++i) {
+	for (unsigned i = 0; i < num_songs; ++i) {
 		ScanMusicEmu(emu, i,
 			     add_tag_handler, &tag_builder);
 
 		char track_name[64];
 		snprintf(track_name, sizeof(track_name),
-			 SUBTUNE_PREFIX "%03u.%s", i, subtune_suffix);
+			 SUBTUNE_PREFIX "%03u.%s", i+1, subtune_suffix);
 		tail = list.emplace_after(tail, track_name,
 					  tag_builder.Commit());
 	}

From fa67c2548a75c3788feb12f494ad3f3627257b42 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Wed, 27 Sep 2017 17:08:16 +0200
Subject: [PATCH 02/10] decoder/Thread: clear the command after catching an
 exception

If an early exception gets caught (e.g. from
AllocatedPath::FromUTF8Throw()) before
DecoderControl::CommandFinishedLocked() is called, the decoder thread
would go in an endless loop, because DecoderCommand::START is still
set.

Closes #118
---
 NEWS                          | 1 +
 src/decoder/DecoderThread.cxx | 1 +
 2 files changed, 2 insertions(+)

diff --git a/NEWS b/NEWS
index 92593f54c..3ab0b453b 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,7 @@ ver 0.20.11 (not yet released)
   - ffmpeg: more reliable song duration
   - gme: fix track numbering
 * fix case insensitive search without libicu
+* fix endless loop when accessing malformed file names in ZIP files
 
 ver 0.20.10 (2017/08/24)
 * decoder
diff --git a/src/decoder/DecoderThread.cxx b/src/decoder/DecoderThread.cxx
index e74ee0f84..7d4a91560 100644
--- a/src/decoder/DecoderThread.cxx
+++ b/src/decoder/DecoderThread.cxx
@@ -508,6 +508,7 @@ try {
 	decoder_run_song(dc, song, uri_utf8, path_fs);
 } catch (...) {
 	dc.state = DecoderState::ERROR;
+	dc.command = DecoderCommand::NONE;
 	dc.error = std::current_exception();
 	dc.client_cond.signal();
 }

From 81b7373637e4b0c5c961b31c64ada8ed2a84d3a2 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Wed, 18 Oct 2017 08:46:31 +0200
Subject: [PATCH 03/10] queue/Queue: MoveOrder() returns to_order

---
 src/queue/Playlist.cxx | 3 +--
 src/queue/Queue.cxx    | 3 ++-
 src/queue/Queue.hxx    | 4 +++-
 3 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/queue/Playlist.cxx b/src/queue/Playlist.cxx
index 667e3457f..dfeb0d163 100644
--- a/src/queue/Playlist.cxx
+++ b/src/queue/Playlist.cxx
@@ -310,8 +310,7 @@ playlist::SetRandom(PlayerControl &pc, bool status)
 			   playlist is played after that */
 			unsigned current_order =
 				queue.PositionToOrder(current_position);
-			queue.MoveOrder(current_order, 0);
-			current = 0;
+			current = queue.MoveOrder(current_order, 0);
 		} else
 			current = -1;
 	} else
diff --git a/src/queue/Queue.cxx b/src/queue/Queue.cxx
index d248f306b..9ee9568e0 100644
--- a/src/queue/Queue.cxx
+++ b/src/queue/Queue.cxx
@@ -195,7 +195,7 @@ Queue::MoveRange(unsigned start, unsigned end, unsigned to) noexcept
 	}
 }
 
-void
+unsigned
 Queue::MoveOrder(unsigned from_order, unsigned to_order) noexcept
 {
 	assert(from_order < length);
@@ -212,6 +212,7 @@ Queue::MoveOrder(unsigned from_order, unsigned to_order) noexcept
 	}
 
 	order[to_order] = from_position;
+	return to_order;
 }
 
 void
diff --git a/src/queue/Queue.hxx b/src/queue/Queue.hxx
index bd2f95c1a..b9ca341e4 100644
--- a/src/queue/Queue.hxx
+++ b/src/queue/Queue.hxx
@@ -284,8 +284,10 @@ struct Queue {
 
 	/**
 	 * Moves a song to a new position in the "order" list.
+	 *
+	 * @return to_order
 	 */
-	void MoveOrder(unsigned from_order, unsigned to_order) noexcept;
+	unsigned MoveOrder(unsigned from_order, unsigned to_order) noexcept;
 
 	/**
 	 * Moves a song to a new position.

From f2fac77d8c6ae42ce2e9a67dcfde4085ec44cfdf Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Wed, 18 Oct 2017 08:50:01 +0200
Subject: [PATCH 04/10] queue/Queue: add methods MoveOrderBefore() and
 MoveOrderAfter()

---
 src/queue/Queue.cxx | 18 ++++++++++++++++++
 src/queue/Queue.hxx | 18 ++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/src/queue/Queue.cxx b/src/queue/Queue.cxx
index 9ee9568e0..720778099 100644
--- a/src/queue/Queue.cxx
+++ b/src/queue/Queue.cxx
@@ -215,6 +215,24 @@ Queue::MoveOrder(unsigned from_order, unsigned to_order) noexcept
 	return to_order;
 }
 
+unsigned
+Queue::MoveOrderBefore(unsigned from_order, unsigned to_order) noexcept
+{
+	/* if "from_order" comes before "to_order", then the new
+	   position is "to_order-1"; otherwise the "to_order" song is
+	   moved one ahead */
+	return MoveOrder(from_order, to_order - (from_order < to_order));
+}
+
+unsigned
+Queue::MoveOrderAfter(unsigned from_order, unsigned to_order) noexcept
+{
+	/* if "from_order" comes after "to_order", then the new
+	   position is "to_order+1"; otherwise the "to_order" song is
+	   moved one back */
+	return MoveOrder(from_order, to_order + (from_order > to_order));
+}
+
 void
 Queue::DeletePosition(unsigned position) noexcept
 {
diff --git a/src/queue/Queue.hxx b/src/queue/Queue.hxx
index b9ca341e4..06e800479 100644
--- a/src/queue/Queue.hxx
+++ b/src/queue/Queue.hxx
@@ -289,6 +289,24 @@ struct Queue {
 	 */
 	unsigned MoveOrder(unsigned from_order, unsigned to_order) noexcept;
 
+	/**
+	 * Moves a song to a new position in the "order" list before
+	 * the given one.
+	 *
+	 * @return the new order number of the given "from" song
+	 */
+	unsigned MoveOrderBefore(unsigned from_order,
+				 unsigned to_order) noexcept;
+
+	/**
+	 * Moves a song to a new position in the "order" list after
+	 * the given one.
+	 *
+	 * @return the new order number of the given "from" song
+	 */
+	unsigned MoveOrderAfter(unsigned from_order,
+				unsigned to_order) noexcept;
+
 	/**
 	 * Moves a song to a new position.
 	 */

From 0f79287b04602ce92cb86c056f78d5f70ab71d42 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Tue, 17 Oct 2017 19:02:41 +0200
Subject: [PATCH 05/10] queue/Playlist: move code to MoveOrderToCurrent()

---
 src/queue/Playlist.hxx        | 12 ++++++++++++
 src/queue/PlaylistControl.cxx | 26 +++++++++++++++++++-------
 2 files changed, 31 insertions(+), 7 deletions(-)

diff --git a/src/queue/Playlist.hxx b/src/queue/Playlist.hxx
index 32489e354..c04a0913a 100644
--- a/src/queue/Playlist.hxx
+++ b/src/queue/Playlist.hxx
@@ -356,6 +356,18 @@ public:
 	}
 
 	void SetConsume(bool new_value);
+
+private:
+	/**
+	 * Prepare a manual song change: move the given song to the
+	 * current playback order.  This is done to avoid skipping
+	 * upcoming songs in the order list.  The newly selected song
+	 * shall be inserted in the order list, and the rest shall be
+	 * played after that as previously planned.
+	 *
+	 * @return the new order number of the given song
+	 */
+	unsigned MoveOrderToCurrent(unsigned old_order);
 };
 
 #endif
diff --git a/src/queue/PlaylistControl.cxx b/src/queue/PlaylistControl.cxx
index 99012aedf..c2284b60c 100644
--- a/src/queue/PlaylistControl.cxx
+++ b/src/queue/PlaylistControl.cxx
@@ -56,6 +56,24 @@ playlist::Stop(PlayerControl &pc)
 	}
 }
 
+unsigned
+playlist::MoveOrderToCurrent(unsigned old_order)
+{
+	if (!queue.random)
+		/* no-op because there is no order list */
+		return old_order;
+
+	const unsigned destination_order = playing
+		? (unsigned)current
+		: 0;
+
+	/* swap the new song with the previous "current" one, so
+	   playback continues as planned */
+	queue.SwapOrders(old_order, destination_order);
+
+	return destination_order;
+}
+
 void
 playlist::PlayPosition(PlayerControl &pc, int song)
 {
@@ -90,13 +108,7 @@ playlist::PlayPosition(PlayerControl &pc, int song)
 			   number, because random mode is enabled */
 			i = queue.PositionToOrder(song);
 
-		if (!playing)
-			current = 0;
-
-		/* swap the new song with the previous "current" one,
-		   so playback continues as planned */
-		queue.SwapOrders(i, current);
-		i = current;
+		i = MoveOrderToCurrent(i);
 	}
 
 	stop_on_error = false;

From 91254e9211f6fa9864dd8f2d2bb7b43cccf7cb77 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Tue, 17 Oct 2017 19:44:16 +0200
Subject: [PATCH 06/10] queue/PlaylistControl: keep order list consistency in
 MoveOrderToCurrent()

Our previous use of Queue::SwapOrders() could cause surprising
results:

- sometimes, the old "current" song would be played again (if the
  newly selected song had not been played already)

- sometimes, the old "current" song would not be played again (if the
  newly selected song had already been played)

This is inconsistent, because it should not depend on whether the
newly selected song had already been played.

So instead of Queue::SwapOrders() we now use Queue::MoveOrderAfter()
and Queue::MoveOrderBefore(), which is more expensive, but also more
consistent.  It attempts to retain as much from the previous order
list as possible, and only moves the newly selected song around.
---
 NEWS                          |  1 +
 src/queue/PlaylistControl.cxx | 24 +++++++++++++++---------
 2 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/NEWS b/NEWS
index 3ab0b453b..25bf749f5 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,7 @@ ver 0.20.11 (not yet released)
 * decoder
   - ffmpeg: more reliable song duration
   - gme: fix track numbering
+* improve random song order when switching songs manually
 * fix case insensitive search without libicu
 * fix endless loop when accessing malformed file names in ZIP files
 
diff --git a/src/queue/PlaylistControl.cxx b/src/queue/PlaylistControl.cxx
index c2284b60c..2217756dd 100644
--- a/src/queue/PlaylistControl.cxx
+++ b/src/queue/PlaylistControl.cxx
@@ -63,15 +63,21 @@ playlist::MoveOrderToCurrent(unsigned old_order)
 		/* no-op because there is no order list */
 		return old_order;
 
-	const unsigned destination_order = playing
-		? (unsigned)current
-		: 0;
-
-	/* swap the new song with the previous "current" one, so
-	   playback continues as planned */
-	queue.SwapOrders(old_order, destination_order);
-
-	return destination_order;
+	if (playing) {
+		/* already playing: move the specified song after the
+		   current one (because the current one has already
+		   been playing and shall not be played again) */
+		return queue.MoveOrderAfter(old_order, current);
+	} else if (current >= 0) {
+		/* not playing: move the specified song before the
+		   current one, so it will be played eventually */
+		return queue.MoveOrderBefore(old_order, current);
+	} else {
+		/* not playing anything: move the specified song to
+		   the front */
+		queue.SwapOrders(old_order, 0);
+		return 0;
+	}
 }
 
 void

From 10990a06846c1766e86fe6af227ceb81fbd3c12d Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Tue, 17 Oct 2017 19:33:56 +0200
Subject: [PATCH 07/10] queue/Playlist: call MoveOrderToCurrent() in
 SeekSongOrder() on song change

Applies the improvements from the previous commit to the "seek"
commands, which are also capable of switching songs.

Closes #119
---
 src/queue/PlaylistControl.cxx | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/queue/PlaylistControl.cxx b/src/queue/PlaylistControl.cxx
index 2217756dd..4b17386ae 100644
--- a/src/queue/PlaylistControl.cxx
+++ b/src/queue/PlaylistControl.cxx
@@ -223,6 +223,8 @@ playlist::SeekSongOrder(PlayerControl &pc, unsigned i, SongTime seek_time)
 		/* seeking is not within the current song - prepare
 		   song change */
 
+		i = MoveOrderToCurrent(i);
+
 		playing = true;
 		current = i;
 

From 753a2aa4622b2a116b510ae1ea41b3898c425955 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Wed, 18 Oct 2017 09:51:04 +0200
Subject: [PATCH 08/10] PlaylistSave: move code to playlist_print_path()

---
 src/PlaylistSave.cxx | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/PlaylistSave.cxx b/src/PlaylistSave.cxx
index e64daba8f..88d8cbe74 100644
--- a/src/PlaylistSave.cxx
+++ b/src/PlaylistSave.cxx
@@ -35,6 +35,12 @@
 
 #include <stdexcept>
 
+static void
+playlist_print_path(BufferedOutputStream &os, const Path path)
+{
+	os.Format("%s\n", NarrowPath(path).c_str());
+}
+
 void
 playlist_print_song(BufferedOutputStream &os, const DetachedSong &song)
 {
@@ -44,7 +50,7 @@ playlist_print_song(BufferedOutputStream &os, const DetachedSong &song)
 
 	try {
 		const auto uri_fs = AllocatedPath::FromUTF8Throw(uri_utf8);
-		os.Format("%s\n", NarrowPath(uri_fs).c_str());
+		playlist_print_path(os, uri_fs);
 	} catch (const std::runtime_error &) {
 	}
 }
@@ -63,7 +69,7 @@ playlist_print_uri(BufferedOutputStream &os, const char *uri)
 			AllocatedPath::FromUTF8Throw(uri);
 
 		if (!path.IsNull())
-			os.Format("%s\n", NarrowPath(path).c_str());
+			playlist_print_path(os, path);
 	} catch (const std::runtime_error &) {
 	}
 }

From 9703a401c5ac3f679503b845805fe24db22c458c Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Wed, 18 Oct 2017 10:05:26 +0200
Subject: [PATCH 09/10] Playlist{File,Save}: always use UTF-8 in playlists on
 Windows

Turns out that using CP_ACP is a lousy idea, because only very few
Unicode characters can be represented by it.  Instead, switch to UTF-8
(which every sane person on other operating system already uses).

Closes #102
---
 NEWS                 |  1 +
 src/PlaylistFile.cxx | 11 +++++------
 src/PlaylistSave.cxx | 10 ++++++++--
 3 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/NEWS b/NEWS
index 25bf749f5..2ad95f03d 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,7 @@ ver 0.20.11 (not yet released)
   - gme: fix track numbering
 * improve random song order when switching songs manually
 * fix case insensitive search without libicu
+* fix Unicode file names in playlists on Windows
 * fix endless loop when accessing malformed file names in ZIP files
 
 ver 0.20.10 (2017/08/24)
diff --git a/src/PlaylistFile.cxx b/src/PlaylistFile.cxx
index 2deae4321..c1fcdeb3c 100644
--- a/src/PlaylistFile.cxx
+++ b/src/PlaylistFile.cxx
@@ -207,13 +207,12 @@ try {
 			continue;
 
 #ifdef _UNICODE
-		wchar_t buffer[MAX_PATH];
-		auto result = MultiByteToWideChar(CP_ACP, 0, s, -1,
-						  buffer, ARRAY_SIZE(buffer));
-		if (result <= 0)
+		/* on Windows, playlists always contain UTF-8, because
+		   its "narrow" charset (i.e. CP_ACP) is incapable of
+		   storing all Unicode paths */
+		const auto path = AllocatedPath::FromUTF8(s);
+		if (path.IsNull())
 			continue;
-
-		const Path path = Path::FromFS(buffer);
 #else
 		const Path path = Path::FromFS(s);
 #endif
diff --git a/src/PlaylistSave.cxx b/src/PlaylistSave.cxx
index 88d8cbe74..d3ec30bb2 100644
--- a/src/PlaylistSave.cxx
+++ b/src/PlaylistSave.cxx
@@ -28,7 +28,6 @@
 #include "fs/AllocatedPath.hxx"
 #include "fs/Traits.hxx"
 #include "fs/FileSystem.hxx"
-#include "fs/NarrowPath.hxx"
 #include "fs/io/FileOutputStream.hxx"
 #include "fs/io/BufferedOutputStream.hxx"
 #include "util/UriUtil.hxx"
@@ -38,7 +37,14 @@
 static void
 playlist_print_path(BufferedOutputStream &os, const Path path)
 {
-	os.Format("%s\n", NarrowPath(path).c_str());
+#ifdef _UNICODE
+	/* on Windows, playlists always contain UTF-8, because its
+	   "narrow" charset (i.e. CP_ACP) is incapable of storing all
+	   Unicode paths */
+	os.Format("%s\n", path.ToUTF8().c_str());
+#else
+	os.Format("%s\n", path.c_str());
+#endif
 }
 
 void

From a7fdfa08e138e5b18f84a9ad52489c6c61861d60 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Wed, 18 Oct 2017 10:14:46 +0200
Subject: [PATCH 10/10] release v0.20.11

---
 NEWS | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index 2ad95f03d..804d6d723 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-ver 0.20.11 (not yet released)
+ver 0.20.11 (2017/10/18)
 * storage
   - curl: support Content-Type application/xml
 * decoder