From fef6b9df807779579ec60874a4e1bace67ea02df Mon Sep 17 00:00:00 2001
From: Shen-Ta Hsieh <ibmibmibm.tw@gmail.com>
Date: Tue, 30 May 2023 02:22:37 +0800
Subject: [PATCH] flac: Try `InputStream` interface if flac failed to read
 through a `wchar_t` path

---
 NEWS                                      |  1 +
 src/decoder/plugins/FlacDecoderPlugin.cxx | 25 +++++++++++++++++++----
 2 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/NEWS b/NEWS
index feecefca8..c2ffcccfa 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,6 @@
 ver 0.23.14 (not yet released)
 * decoder
+  - flac: fix scanning files with non-ASCII names on Windows
   - mad: fix calculation of LAME peak values
 * mixer
   - wasapi: fix problem setting volume
diff --git a/src/decoder/plugins/FlacDecoderPlugin.cxx b/src/decoder/plugins/FlacDecoderPlugin.cxx
index da0374f7a..0094906b6 100644
--- a/src/decoder/plugins/FlacDecoderPlugin.cxx
+++ b/src/decoder/plugins/FlacDecoderPlugin.cxx
@@ -24,6 +24,7 @@
 #include "lib/xiph/FlacMetadataChain.hxx"
 #include "OggCodec.hxx"
 #include "input/InputStream.hxx"
+#include "input/LocalOpen.hxx"
 #include "fs/Path.hxx"
 #include "fs/NarrowPath.hxx"
 #include "Log.hxx"
@@ -71,16 +72,32 @@ flac_write_cb(const FLAC__StreamDecoder *dec, const FLAC__Frame *frame,
 }
 
 static bool
-flac_scan_file(Path path_fs, TagHandler &handler) noexcept
-{
+flac_scan_file(Path path_fs, TagHandler &handler) noexcept {
 	FlacMetadataChain chain;
-	if (!chain.Read(NarrowPath(path_fs))) {
+	const bool succeed = [&chain, &path_fs]() noexcept {
+		// read by NarrowPath
+		if (chain.Read(NarrowPath(path_fs))) {
+			return true;
+		}
+		if (std::is_same_v<Path::value_type, char> ||
+		    chain.GetStatus() != FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE) {
+			return false;
+		}
+		// read by InputStream
+		Mutex mutex;
+		auto is = OpenLocalInputStream(path_fs, mutex);
+		if (is && chain.Read(*is)) {
+			return true;
+		}
+		return false;
+	}();
+
+	if (!succeed) {
 		FmtDebug(flac_domain,
 			 "Failed to read FLAC tags: {}",
 			 chain.GetStatusString());
 		return false;
 	}
-
 	chain.Scan(handler);
 	return true;
 }