diff --git a/NEWS b/NEWS index cb203da29..96ddb5f50 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,12 @@ ver 0.22 (not yet released) * switch to C++17 - GCC 7 or clang 4 (or newer) recommended +ver 0.21.14 (not yet released) +* decoder + - sidplay: show track durations in database + - sidplay: convert tag values from Windows-1252 charset + - sidplay: strip text from "Date" tag + ver 0.21.13 (2019/08/06) * input - cdio_paranoia: require libcdio-paranoia 10.2+0.93+1 diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 9dc417f71..dbf63165a 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -2,8 +2,8 @@ + android:versionCode="37" + android:versionName="0.21.14"> diff --git a/src/decoder/plugins/SidplayDecoderPlugin.cxx b/src/decoder/plugins/SidplayDecoderPlugin.cxx index 1ac39d260..c9c71f6eb 100644 --- a/src/decoder/plugins/SidplayDecoderPlugin.cxx +++ b/src/decoder/plugins/SidplayDecoderPlugin.cxx @@ -25,6 +25,7 @@ #include "song/DetachedSong.hxx" #include "fs/Path.hxx" #include "fs/AllocatedPath.hxx" +#include "lib/icu/Converter.hxx" #ifdef HAVE_SIDPLAYFP #include "fs/io/FileReader.hxx" #include "util/RuntimeError.hxx" @@ -32,6 +33,8 @@ #include "util/StringFormat.hxx" #include "util/StringView.hxx" #include "util/Domain.hxx" +#include "util/AllocatedString.hxx" +#include "util/CharUtil.hxx" #include "util/ByteOrder.hxx" #include "Log.hxx" @@ -437,19 +440,70 @@ sidplay_file_decode(DecoderClient &client, Path path_fs) } while (cmd != DecoderCommand::STOP); } +static AllocatedString +Windows1252ToUTF8(const char *s) noexcept +{ +#ifdef HAVE_ICU_CONVERTER + try { + std::unique_ptr + converter(IcuConverter::Create("windows-1252")); + + return converter->ToUTF8(s); + } catch (...) { } +#endif + + /* + * Fallback to not transcoding windows-1252 to utf-8, that may result + * in invalid utf-8 unless nonprintable characters are replaced. + */ + auto t = AllocatedString::Duplicate(s); + + for (size_t i = 0; t[i] != AllocatedString::SENTINEL; i++) + if (!IsPrintableASCII(t[i])) + t[i] = '?'; + + return t; +} + gcc_pure -static const char * +static AllocatedString GetInfoString(const SidTuneInfo &info, unsigned i) noexcept { #ifdef HAVE_SIDPLAYFP - return info.numberOfInfoStrings() > i + const char *s = info.numberOfInfoStrings() > i ? info.infoString(i) - : nullptr; + : ""; #else - return info.numberOfInfoStrings > i + const char *s = info.numberOfInfoStrings > i ? info.infoString[i] - : nullptr; + : ""; #endif + + return Windows1252ToUTF8(s); +} + +gcc_pure +static AllocatedString +GetDateString(const SidTuneInfo &info) noexcept +{ + /* + * Field 2 is called , previously used as . + * It is formatted , + * where may be , , or , for + * example "1987", "199?", "19??" or "1985-87". The may be for example Rob Hubbard. A full field + * may be for example "1987 Rob Hubbard". + */ + AllocatedString release = GetInfoString(info, 2); + + /* Keep the part only for the date. */ + for (size_t i = 0; release[i] != AllocatedString::SENTINEL; i++) + if (std::isspace(release[i])) { + release[i] = AllocatedString::SENTINEL; + break; + } + + return release; } static void @@ -457,31 +511,29 @@ ScanSidTuneInfo(const SidTuneInfo &info, unsigned track, unsigned n_tracks, TagHandler &handler) noexcept { /* title */ - const char *title = GetInfoString(info, 0); - if (title == nullptr) - title = ""; + const auto title = GetInfoString(info, 0); if (n_tracks > 1) { const auto tag_title = StringFormat<1024>("%s (%u/%u)", - title, track, n_tracks); + title.c_str(), track, n_tracks); handler.OnTag(TAG_TITLE, tag_title.c_str()); } else - handler.OnTag(TAG_TITLE, title); + handler.OnTag(TAG_TITLE, title.c_str()); /* artist */ - const char *artist = GetInfoString(info, 1); - if (artist != nullptr) - handler.OnTag(TAG_ARTIST, artist); + const auto artist = GetInfoString(info, 1); + if (!artist.empty()) + handler.OnTag(TAG_ARTIST, artist.c_str()); /* genre */ if (!default_genre.empty()) handler.OnTag(TAG_GENRE, default_genre.c_str()); /* date */ - const char *date = GetInfoString(info, 2); - if (date != nullptr) - handler.OnTag(TAG_DATE, date); + const auto date = GetDateString(info); + if (!date.empty()) + handler.OnTag(TAG_DATE, date.c_str()); /* track */ handler.OnTag(TAG_TRACK, StringFormat<16>("%u", track).c_str()); @@ -556,6 +608,10 @@ sidplay_container_scan(Path path_fs) AddTagHandler h(tag_builder); ScanSidTuneInfo(info, i, n_tracks, h); + const SignedSongTime duration = get_song_length(tune); + if (!duration.IsNegative()) + h.OnDuration(SongTime(duration)); + char track_name[32]; /* Construct container/tune path names, eg. Delta.sid/tune_001.sid */