aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_flac
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Input/in_flac')
-rw-r--r--Src/Plugins/Input/in_flac/About.cpp54
-rw-r--r--Src/Plugins/Input/in_flac/AlbumArt.cpp270
-rw-r--r--Src/Plugins/Input/in_flac/AlbumArt.h38
-rw-r--r--Src/Plugins/Input/in_flac/DecodeThread.cpp719
-rw-r--r--Src/Plugins/Input/in_flac/ExtendedFileInfo.cpp424
-rw-r--r--Src/Plugins/Input/in_flac/ExtendedRead.cpp166
-rw-r--r--Src/Plugins/Input/in_flac/FLACFileCallbacks.cpp57
-rw-r--r--Src/Plugins/Input/in_flac/FLACFileCallbacks.h35
-rw-r--r--Src/Plugins/Input/in_flac/FileInfo.cpp400
-rw-r--r--Src/Plugins/Input/in_flac/Metadata.cpp577
-rw-r--r--Src/Plugins/Input/in_flac/Metadata.h48
-rw-r--r--Src/Plugins/Input/in_flac/Preferences.cpp110
-rw-r--r--Src/Plugins/Input/in_flac/QuickBuf.h50
-rw-r--r--Src/Plugins/Input/in_flac/RawReader.cpp174
-rw-r--r--Src/Plugins/Input/in_flac/RawReader.h36
-rw-r--r--Src/Plugins/Input/in_flac/Stopper.cpp65
-rw-r--r--Src/Plugins/Input/in_flac/Stopper.h10
-rw-r--r--Src/Plugins/Input/in_flac/StreamFileWin32.cpp129
-rw-r--r--Src/Plugins/Input/in_flac/StreamFileWin32.h23
-rw-r--r--Src/Plugins/Input/in_flac/api__in_flv.h11
-rw-r--r--Src/Plugins/Input/in_flac/in_flac.rc163
-rw-r--r--Src/Plugins/Input/in_flac/in_flac.sln105
-rw-r--r--Src/Plugins/Input/in_flac/in_flac.vcxproj307
-rw-r--r--Src/Plugins/Input/in_flac/in_flac.vcxproj.filters122
-rw-r--r--Src/Plugins/Input/in_flac/main.cpp277
-rw-r--r--Src/Plugins/Input/in_flac/main.h40
-rw-r--r--Src/Plugins/Input/in_flac/mkv_flac_decoder.cpp204
-rw-r--r--Src/Plugins/Input/in_flac/mkv_flac_decoder.h65
-rw-r--r--Src/Plugins/Input/in_flac/resource.h71
-rw-r--r--Src/Plugins/Input/in_flac/version.rc239
30 files changed, 4789 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_flac/About.cpp b/Src/Plugins/Input/in_flac/About.cpp
new file mode 100644
index 00000000..ff5f4ccf
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/About.cpp
@@ -0,0 +1,54 @@
+/*
+** Copyright © 2007-2014 Winamp SA
+**
+** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
+** liable for any damages arising from the use of this software.
+**
+** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
+** alter it and redistribute it freely, subject to the following restrictions:
+**
+** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
+** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+**
+** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+**
+** 3. This notice may not be removed or altered from any source distribution.
+**
+** Author: Ben Allison benski@winamp.com
+** Created: March 1, 2007
+**
+*/
+
+#include "main.h"
+#include "../Agave/Language/api_language.h"
+#include "resource.h"
+#include <FLAC/all.h>
+#include <strsafe.h>
+
+static HMODULE libflac=0;
+static char defaultVersionString[64];
+static const char **versionString=0;
+static const char *GetFLACVersion()
+{
+ return "1.4.2";
+}
+
+int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message)
+{
+ MSGBOXPARAMSW msgbx = {sizeof(MSGBOXPARAMSW),0};
+ msgbx.lpszText = message;
+ msgbx.lpszCaption = title;
+ msgbx.lpszIcon = MAKEINTRESOURCEW(102);
+ msgbx.hInstance = GetModuleHandle(0);
+ msgbx.dwStyle = MB_USERICON;
+ msgbx.hwndOwner = parent;
+ return MessageBoxIndirectW(&msgbx);
+}
+
+void About(HWND hwndParent)
+{
+ wchar_t message[1024] = {0};
+ StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),
+ plugin.description, __DATE__, GetFLACVersion());
+ DoAboutMessageBox(hwndParent,(wchar_t*)plugin.description,message);
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/AlbumArt.cpp b/Src/Plugins/Input/in_flac/AlbumArt.cpp
new file mode 100644
index 00000000..22944db3
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/AlbumArt.cpp
@@ -0,0 +1,270 @@
+/*
+** Copyright © 2007-2014 Winamp SA
+**
+** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
+** liable for any damages arising from the use of this software.
+**
+** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
+** alter it and redistribute it freely, subject to the following restrictions:
+**
+** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
+** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+**
+** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+**
+** 3. This notice may not be removed or altered from any source distribution.
+**
+** Author: Ben Allison benski@winamp.com
+** Created: July 25, 2007
+**
+*/
+
+#include "main.h"
+#include "api__in_flv.h"
+#include "Metadata.h"
+#include "../nu/AutoWide.h"
+#include "AlbumArt.h"
+#include <shlwapi.h>
+#include <strsafe.h>
+
+bool FLAC_AlbumArtProvider::IsMine(const wchar_t *filename)
+{
+ const wchar_t *extension = PathFindExtensionW(filename);
+ if (extension && *extension)
+ {
+ wchar_t exts[128] = {0};
+ GetPrivateProfileStringW(L"in_flac", L"extensions", DEFAULT_EXTENSIONSW, exts, 128, winampINI);
+
+ extension++;
+ wchar_t *b = exts;
+ wchar_t *c;
+ do
+ {
+ wchar_t d[20] = {0};
+ StringCchCopyW(d, 15, b);
+ if ((c = wcschr(b, L';')))
+ {
+ if ((c-b)<15)
+ d[c - b] = 0;
+ }
+
+ if (!lstrcmpiW(extension, d))
+ return true;
+
+ b = c + 1;
+ }
+ while (c);
+ }
+ return false;
+}
+
+int FLAC_AlbumArtProvider::ProviderType()
+{
+ return ALBUMARTPROVIDER_TYPE_EMBEDDED;
+}
+
+bool NameToAPICType(const wchar_t *name, FLAC__StreamMetadata_Picture_Type &num)
+{
+ if (!name || !*name) // default to cover
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER;
+ else if (!_wcsicmp(name, L"fileicon")) // 32x32 pixels 'file icon' (PNG only)
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD;
+ else if (!_wcsicmp(name, L"icon")) // Other file icon
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON;
+ else if (!_wcsicmp(name, L"cover")) // Cover (front)
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER;
+ else if (!_wcsicmp(name, L"back")) // Cover (back)
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER;
+ else if (!_wcsicmp(name, L"leaflet")) // Leaflet page
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE;
+ else if (!_wcsicmp(name, L"media")) // Media (e.g. lable side of CD)
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA;
+ else if (!_wcsicmp(name, L"leadartist")) //Lead artist/lead performer/soloist
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST;
+ else if (!_wcsicmp(name, L"artist")) // Artist/performer
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST;
+ else if (!_wcsicmp(name, L"conductor")) // Conductor
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR;
+ else if (!_wcsicmp(name, L"band")) // Band/Orchestra
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_BAND;
+ else if (!_wcsicmp(name, L"composer")) // Composer
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER;
+ else if (!_wcsicmp(name, L"lyricist")) // Lyricist/text writer
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST;
+ else if (!_wcsicmp(name, L"location")) // Recording Location
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION;
+ else if (!_wcsicmp(name, L"recording")) // During recording
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING;
+ else if (!_wcsicmp(name, L"performance")) // During performance
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE;
+ else if (!_wcsicmp(name, L"preview")) // Movie/video screen capture
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE;
+ else if (!_wcsicmp(name, L"fish")) // A bright coloured fish
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_FISH;
+ else if (!_wcsicmp(name, L"illustration")) // Illustration
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION;
+ else if (!_wcsicmp(name, L"artistlogo")) // Band/artist logotype
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE;
+ else if (!_wcsicmp(name, L"publisherlogo")) // Publisher/Studio logotype
+ num=FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE;
+ else
+ return false;
+ return true;
+}
+
+int FLAC_AlbumArtProvider::GetAlbumArtData(const wchar_t *filename, const wchar_t *type, void **bits, size_t *len, wchar_t **mimeType)
+{
+ FLACMetadata metadata;
+
+ if (metadata.Open(filename))
+ {
+ FLAC__StreamMetadata_Picture_Type pictype;
+ if (NameToAPICType(type, pictype))
+ {
+ if (metadata.GetPicture(pictype, bits, len, mimeType))
+ return ALBUMARTPROVIDER_SUCCESS;
+ }
+ }
+
+ return ALBUMARTPROVIDER_FAILURE;
+}
+
+int FLAC_AlbumArtProvider::SetAlbumArtData(const wchar_t *filename, const wchar_t *type, void *bits, size_t len, const wchar_t *mimeType)
+{
+ if (!info || info && _wcsicmp(filename, info->filename))
+ {
+ FLACMetadata metadata;
+ if (metadata.Open(filename))
+ {
+ FLAC__StreamMetadata_Picture_Type pictype;
+ if (NameToAPICType(type, pictype))
+ {
+ if (metadata.SetPicture(pictype, bits, len, mimeType, 0/*TODO*/, 0/*TODO*/))
+ {
+ if (metadata.Save(filename))
+ return ALBUMARTPROVIDER_SUCCESS;
+ else
+ return ALBUMARTPROVIDER_FAILURE;
+ }
+ }
+ }
+ }
+ else
+ {
+ FLAC__StreamMetadata_Picture_Type pictype;
+ if (NameToAPICType(type, pictype))
+ {
+ if (info->metadata.SetPicture(pictype, bits, len, mimeType, 0/*TODO*/, 0/*TODO*/))
+ {
+ return ALBUMARTPROVIDER_SUCCESS;
+ }
+ }
+ }
+
+ return ALBUMARTPROVIDER_FAILURE;
+}
+
+int FLAC_AlbumArtProvider::DeleteAlbumArt(const wchar_t *filename, const wchar_t *type)
+{
+ if (!info || info && _wcsicmp(filename, info->filename))
+ {
+ FLACMetadata metadata;
+ if (metadata.Open(filename))
+ {
+ FLAC__StreamMetadata_Picture_Type pictype;
+ if (NameToAPICType(type, pictype))
+ {
+ if (info->metadata.RemovePicture(pictype))
+ {
+ if (info->metadata.Save(filename))
+ return ALBUMARTPROVIDER_SUCCESS;
+ else
+ return ALBUMARTPROVIDER_FAILURE;
+ }
+ }
+ }
+ }
+ else
+ {
+ FLAC__StreamMetadata_Picture_Type pictype;
+ if (NameToAPICType(type, pictype))
+ {
+ if (info->metadata.RemovePicture(pictype))
+ {
+ return ALBUMARTPROVIDER_SUCCESS;
+ }
+ }
+ }
+
+ return ALBUMARTPROVIDER_FAILURE;
+}
+
+#define CBCLASS FLAC_AlbumArtProvider
+START_DISPATCH;
+CB(SVC_ALBUMARTPROVIDER_PROVIDERTYPE, ProviderType);
+CB(SVC_ALBUMARTPROVIDER_GETALBUMARTDATA, GetAlbumArtData);
+CB(SVC_ALBUMARTPROVIDER_ISMINE, IsMine);
+CB(SVC_ALBUMARTPROVIDER_SETALBUMARTDATA, SetAlbumArtData);
+CB(SVC_ALBUMARTPROVIDER_DELETEALBUMART, DeleteAlbumArt);
+END_DISPATCH;
+#undef CBCLASS
+
+static FLAC_AlbumArtProvider albumArtProvider;
+
+// {622C3B42-866E-4935-AA52-3B456AE8B036}
+static const GUID flac_albumartproviderGUID =
+ { 0x622c3b42, 0x866e, 0x4935, { 0xaa, 0x52, 0x3b, 0x45, 0x6a, 0xe8, 0xb0, 0x36 } };
+
+FOURCC AlbumArtFactory::GetServiceType()
+{
+ return svc_albumArtProvider::SERVICETYPE;
+}
+
+const char *AlbumArtFactory::GetServiceName()
+{
+ return "FLAC Album Art Provider";
+}
+
+GUID AlbumArtFactory::GetGUID()
+{
+ return flac_albumartproviderGUID;
+}
+
+void *AlbumArtFactory::GetInterface(int global_lock)
+{
+ return &albumArtProvider;
+}
+
+int AlbumArtFactory::SupportNonLockingInterface()
+{
+ return 1;
+}
+
+int AlbumArtFactory::ReleaseInterface(void *ifc)
+{
+ //plugin.service->service_unlock(ifc);
+ return 1;
+}
+
+const char *AlbumArtFactory::GetTestString()
+{
+ return 0;
+}
+
+int AlbumArtFactory::ServiceNotify(int msg, int param1, int param2)
+{
+ return 1;
+}
+
+#define CBCLASS AlbumArtFactory
+START_DISPATCH;
+CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType)
+CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName)
+CB(WASERVICEFACTORY_GETGUID, GetGUID)
+CB(WASERVICEFACTORY_GETINTERFACE, GetInterface)
+CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface)
+CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface)
+CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString)
+CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify)
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/AlbumArt.h b/Src/Plugins/Input/in_flac/AlbumArt.h
new file mode 100644
index 00000000..11975e02
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/AlbumArt.h
@@ -0,0 +1,38 @@
+#ifndef NULLSOFT_IN_FLAC_ALBUMART_H
+#define NULLSOFT_IN_FLAC_ALBUMART_H
+
+#include "../Agave/AlbumArt/svc_albumArtProvider.h"
+
+class FLAC_AlbumArtProvider : public svc_albumArtProvider
+{
+public:
+ bool IsMine(const wchar_t *filename);
+ int ProviderType();
+ // implementation note: use WASABI_API_MEMMGR to alloc bits and mimetype, so that the recipient can free through that
+ int GetAlbumArtData(const wchar_t *filename, const wchar_t *type, void **bits, size_t *len, wchar_t **mimeType);
+ int SetAlbumArtData(const wchar_t *filename, const wchar_t *type, void *bits, size_t len, const wchar_t *mimeType);
+ int DeleteAlbumArt(const wchar_t *filename, const wchar_t *type);
+protected:
+ RECVS_DISPATCH;
+};
+
+#include <api/service/waservicefactory.h>
+
+class AlbumArtFactory : public waServiceFactory
+{
+public:
+ FOURCC GetServiceType();
+ const char *GetServiceName();
+ GUID GetGUID();
+ void *GetInterface(int global_lock);
+ int SupportNonLockingInterface();
+ int ReleaseInterface(void *ifc);
+ const char *GetTestString();
+ int ServiceNotify(int msg, int param1, int param2);
+
+protected:
+ RECVS_DISPATCH;
+};
+
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/DecodeThread.cpp b/Src/Plugins/Input/in_flac/DecodeThread.cpp
new file mode 100644
index 00000000..38c7fcb6
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/DecodeThread.cpp
@@ -0,0 +1,719 @@
+/*
+** Copyright (C) 2007-2011 Nullsoft, Inc.
+**
+** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
+** liable for any damages arising from the use of this software.
+**
+** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
+** alter it and redistribute it freely, subject to the following restrictions:
+**
+** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
+** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+**
+** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+**
+** 3. This notice may not be removed or altered from any source distribution.
+**
+** Author: Ben Allison benski@winamp.com
+** Created: March 1, 2007
+**
+*/
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include "main.h"
+#include <windows.h>
+#include <math.h>
+#include <assert.h>
+#include <locale.h>
+#include <FLAC/all.h>
+#include "StreamFileWin32.h"
+#include "../Winamp/wa_ipc.h"
+#include "QuickBuf.h"
+#include "api__in_flv.h"
+#include "../nu/AudioOutput.h"
+#include "../Agave/Language/api_language.h"
+#include "FLACFileCallbacks.h"
+#include "nx/nxpath.h"
+
+int m_force_seek = -1; // el hacko grande
+const DWORD PAUSE_TIMEOUT = 100; // number of milliseconds to sleep for when paused
+
+static bool paused = false;
+
+// could probably move this inside client_data
+int realbits;
+int bps;
+int samplerate;
+int channels;
+volatile int currentSongLength=-1000;
+int averageBitrate;
+
+// if input plugins weren't single-instance only, we could put this in thread-local-storage
+static FLAC__StreamDecoder *decoder = 0;
+static uint64_t fileSize = 0;
+static FLAC__uint64 decodePosition = 0;
+
+double gain = 1.0;
+static double buffer_scale=1.0;
+
+class FLACWait
+{
+public:
+ int WaitOrAbort(int time_in_ms)
+ {
+ if (WaitForSingleObject(killswitch, time_in_ms) == WAIT_OBJECT_0)
+ return 1;
+ return 0;
+ }
+};
+
+
+volatile int bufferCount=0;
+static void Buffering(int bufStatus, const wchar_t *displayString)
+{
+ if (bufStatus < 0 || bufStatus > 100)
+ return;
+
+ char tempdata[75*2] = {0, };
+
+ int csa = plugin.SAGetMode();
+ if (csa & 1)
+ {
+ for (int x = 0; x < bufStatus*75 / 100; x ++)
+ tempdata[x] = x * 16 / 75;
+ }
+ else if (csa&2)
+ {
+ int offs = (csa & 1) ? 75 : 0;
+ int x = 0;
+ while (x < bufStatus*75 / 100)
+ {
+ tempdata[offs + x++] = -6 + x * 14 / 75;
+ }
+ while (x < 75)
+ {
+ tempdata[offs + x++] = 0;
+ }
+ }
+ else if (csa == 4)
+ {
+ tempdata[0] = tempdata[1] = (bufStatus * 127 / 100);
+ }
+ if (csa) plugin.SAAdd(tempdata, ++bufferCount, (csa == 3) ? 0x80000003 : csa);
+
+ /*
+ TODO
+ wchar_t temp[64] = {0};
+ StringCchPrintf(temp, 64, L"%s: %d%%",displayString, bufStatus);
+ SetStatus(temp);
+ */
+ //SetVideoStatusText(temp); // TODO: find a way to set the old status back
+ // videoOutput->notifyBufferState(static_cast<int>(bufStatus*2.55f));
+}
+
+static nu::AudioOutput<FLACWait> audio_output(&plugin);
+
+#pragma region Truncaters
+inline static void clip(double &x, double a, double b)
+{
+ double x1 = fabs(x - a);
+ double x2 = fabs(x - b);
+ x = x1 + (a + b);
+ x -= x2;
+ x *= 0.5;
+}
+
+static void InterleaveAndTruncate32(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize, double gain)
+{
+ // TODO: this can be sped up significantly
+ FLAC__int32 *output = (FLAC__int32 *)_output;
+
+ for (int b = 0;b < blocksize;b++)
+ {
+ for (int c = 0;c < channels;c++)
+ {
+ double appliedGain = gain * (double)buffer[c][b];
+ // TODO: add dither
+ clip(appliedGain, -2147483648., 2147483647.);
+ *output = (FLAC__int32)appliedGain;
+ output++;
+ }
+ }
+}
+
+static void InterleaveAndTruncate24(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize, double gain)
+{
+ char *output = (char *)_output;
+
+ for (int b = 0;b < blocksize;b++)
+ {
+ for (int c = 0;c < channels;c++)
+ {
+ double appliedGain = gain * (double)buffer[c][b];
+ // TODO: add dither
+ clip(appliedGain, -8388608., 8388607.);
+ FLAC__int32 sample = (FLAC__int32)appliedGain;
+
+ // little endian specific code
+ output[0] = (unsigned char)(sample);
+ output[1] = (unsigned char)(sample >> 8);
+ output[2] = (unsigned char)(sample >> 16);
+ output += 3;
+ }
+ }
+}
+
+static void InterleaveAndTruncate16(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize, double gain)
+{
+ short *output = (short *)_output;
+
+ for (int b = 0;b < blocksize;b++)
+ {
+ for (int c = 0;c < channels;c++)
+ {
+ double appliedGain = gain * (double)buffer[c][b];
+ // TODO: add dither
+ clip(appliedGain, -32768., 32767.);
+ FLAC__int32 sample = (FLAC__int32)appliedGain;
+
+ *output = (short) sample ;
+ output ++;
+ }
+ }
+}
+
+static void InterleaveAndTruncate8(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize, double gain)
+{
+ unsigned char *output = (unsigned char *)_output;
+
+ for (int b = 0;b < blocksize;b++)
+ {
+ for (int c = 0;c < channels;c++)
+ {
+ double appliedGain = gain * (double)buffer[c][b];
+ // TODO: add dither
+ clip(appliedGain, -128., 127.);
+ FLAC__int32 sample = (FLAC__int32)appliedGain;
+
+ *output = (unsigned char)(sample + 128);
+ output++;
+ }
+ }
+}
+
+/* --- Versions without gain adjustment --- */
+
+static void InterleaveAndTruncate32(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize)
+{
+ FLAC__int32 *output = (FLAC__int32 *)_output;
+
+ for (int b = 0;b < blocksize;b++)
+ {
+ for (int c = 0;c < channels;c++)
+ {
+ *output = buffer[c][b];
+ output++;
+ }
+ }
+}
+
+static void InterleaveAndTruncate24(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize)
+{
+ char *output = (char *)_output;
+
+ for (int b = 0;b < blocksize;b++)
+ {
+ for (int c = 0;c < channels;c++)
+ {
+ FLAC__int32 sample = buffer[c][b];
+
+ // little endian specific code
+ output[0] = (unsigned char)(sample);
+ output[1] = (unsigned char)(sample >> 8);
+ output[2] = (unsigned char)(sample >> 16);
+ output += 3;
+ }
+ }
+}
+
+static void InterleaveAndTruncate16(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize)
+{
+ short *output = (short *)_output;
+
+ for (int b = 0;b < blocksize;b++)
+ {
+ for (int c = 0;c < channels;c++)
+ {
+ *output = (short) buffer[c][b];
+ output ++;
+ }
+ }
+}
+
+static void InterleaveAndTruncate8(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize)
+{
+ unsigned char *output = (unsigned char *)_output;
+
+ for (int b = 0;b < blocksize;b++)
+ {
+ for (int c = 0;c < channels;c++)
+ {
+ *output = (unsigned char)(buffer[c][b] + 128);
+ output++;
+ }
+ }
+}
+
+void InterleaveAndTruncate(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize, double gain)
+{
+ if (gain == 1.0) // if it's EXACTLY 1.0, i.e. from a hardcoded value. not meant to be a "RG happens to be 1.0" check
+ {
+ switch(bps)
+ {
+ case 8:
+ InterleaveAndTruncate8(buffer, _output, bps, channels, blocksize);
+ break;
+ case 16:
+ InterleaveAndTruncate16(buffer, _output, bps, channels, blocksize);
+ break;
+ case 24:
+ InterleaveAndTruncate24(buffer, _output, bps, channels, blocksize);
+ break;
+ case 32:
+ InterleaveAndTruncate32(buffer, _output, bps, channels, blocksize);
+ break;
+ }
+ }
+ else // apply replay gain
+ {
+ switch(bps)
+ {
+ case 8:
+ InterleaveAndTruncate8(buffer, _output, bps, channels, blocksize, gain);
+ break;
+ case 16:
+ InterleaveAndTruncate16(buffer, _output, bps, channels, blocksize, gain);
+ break;
+ case 24:
+ InterleaveAndTruncate24(buffer, _output, bps, channels, blocksize, gain);
+ break;
+ case 32:
+ InterleaveAndTruncate32(buffer, _output, bps, channels, blocksize, gain);
+ break;
+ }
+ }
+}
+#pragma endregion
+
+QuickBuf output;
+FLAC__uint64 lastoutputtime;
+static FLAC__StreamDecoderWriteStatus OnAudio(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data)
+{
+ // TODO: if frame bps/samplerate/channels doesn't equal our own, we'll probably have to close & re-open the audio output
+ // mux buffer into interleaved samples
+ FLAC__uint64 newPosition;
+ FLAC__stream_decoder_get_decode_position(decoder, &newPosition);
+ FLAC__uint64 delta = newPosition - decodePosition;
+ decodePosition = newPosition;
+ if (!config_average_bitrate)
+ plugin.SetInfo((int)(delta / (125.*frame->header.blocksize / samplerate)), -1, -1, -1);
+ else if (fixBitrate)
+ {
+ fixBitrate = false;
+ plugin.SetInfo(averageBitrate, -1, -1, -1);
+ }
+
+ if (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER)
+ lastoutputtime = 1000ULL*frame->header.number.sample_number / (FLAC__uint64)samplerate;
+ else
+ lastoutputtime = 0;
+
+ int byteLength = (bps / 8) * channels * frame->header.blocksize;
+ output.Reserve(byteLength*2);
+ InterleaveAndTruncate(buffer, output, bps, channels, frame->header.blocksize, gain);
+
+ audio_output.Write(output, byteLength);
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+static int GetBits(int incoming)
+{
+ int bits = AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"bits", 16);
+ if (bits > 16 && AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false))
+ return bits;
+
+ return min(bits, incoming);
+}
+
+static double GetGain(const FLAC__StreamMetadata *metadata)
+{
+ if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false))
+ {
+ if (metadata)
+ {
+ int gainPos = -1, peakPos = -1;
+ switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_source", 0))
+ {
+ case 0: // track
+ gainPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_TRACK_GAIN");
+ if (gainPos < 0 && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
+ gainPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_ALBUM_GAIN");
+
+ peakPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_TRACK_PEAK");
+ if (peakPos < 0 && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
+ peakPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_ALBUM_PEAK");
+
+ break;
+ case 1:
+ gainPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_ALBUM_GAIN");
+ if (gainPos < 0 && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
+ gainPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_TRACK_GAIN");
+
+ peakPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_ALBUM_PEAK");
+ if (peakPos < 0 && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
+ peakPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_TRACK_PEAK");
+ break;
+ }
+
+ double dB = 0, peak = 1.0;
+ _locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
+ if (gainPos >= 0)
+ {
+ const char *entry = (const char *)metadata->data.vorbis_comment.comments[gainPos].entry;
+ const char *value = strchr(entry, '='); // find the first equal
+ if (value++)
+ {
+ if (value[0] == '+')
+ dB = _atof_l(&value[1], C_locale);
+ else
+ dB = _atof_l(value, C_locale);
+ }
+ }
+ else
+ {
+ dB = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0);
+ return pow(10.0, dB / 20.0);
+ }
+
+ if (peakPos >= 0)
+ {
+ const char *entry = (const char *)metadata->data.vorbis_comment.comments[peakPos].entry;
+ const char *value = strchr(entry, '='); // find the first equal
+ if (value++)
+ {
+ peak = _atof_l(value, C_locale);
+ }
+ }
+
+ switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_mode", 1))
+ {
+ case 0: // apply gain
+ return pow(10.0, dB / 20.0);
+ break;
+ case 1: // apply gain, but don't clip
+ return min(pow(10.0, dB / 20.0), 1.0 / peak);
+ break;
+ case 2: // normalize
+ return 1.0 / peak;
+ break;
+ case 3: // prevent clipping
+ if (peak > 1.0)
+ return 1.0 / peak;
+ else
+ return 1.0;
+ }
+ }
+ else
+ {
+ double dB = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0);
+ return pow(10.0, dB / 20.0);
+ }
+ }
+
+ return 1.0;
+}
+
+static void OnMetadata(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
+{
+ switch (metadata->type)
+ {
+ case FLAC__METADATA_TYPE_STREAMINFO:
+ {
+ realbits = metadata->data.stream_info.bits_per_sample;
+ channels = metadata->data.stream_info.channels;
+ samplerate = metadata->data.stream_info.sample_rate;
+ bps = GetBits(metadata->data.stream_info.bits_per_sample);
+ gain = GetGain(0) * pow(2., (double)(bps - realbits));
+ if (metadata->data.stream_info.total_samples)
+ {
+ currentSongLength = (int)((metadata->data.stream_info.total_samples*1000ULL)/(uint64_t)samplerate);
+ averageBitrate = (int)(fileSize / (125 * metadata->data.stream_info.total_samples / (__int64)samplerate));
+ }
+ else
+ {
+ currentSongLength=-1000;
+ averageBitrate=0;
+ }
+ }
+ break;
+ case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+ gain = GetGain(metadata) * pow(2., (double)(bps - realbits));
+ break;
+ }
+}
+
+static void OnError(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
+{
+ //client_data = client_data; // dummy line so i can set a breakpoint
+}
+
+void CALLBACK APCPause(ULONG_PTR data)
+{
+ paused = !!data;
+ plugin.outMod->Pause(!!paused);
+}
+
+void CALLBACK APCSeek(ULONG_PTR data)
+{
+ // TODO: break out of end-of-file handling if necessary
+ buffer_scale=1.0;
+ int time_in_ms = (int)data;
+ lastoutputtime=time_in_ms; // cheating a bit here :)
+ audio_output.Flush(time_in_ms);
+ __int64 frames = Int32x32To64(time_in_ms, samplerate) / 1000;
+ FLAC__StreamDecoderState state = FLAC__stream_decoder_get_state(decoder);
+ plugin.outMod->Pause(0);
+ FLAC__stream_decoder_seek_absolute(decoder, frames);
+ plugin.outMod->Pause(paused);
+ if (FLAC__stream_decoder_get_state(decoder) == FLAC__STREAM_DECODER_SEEK_ERROR)
+ {
+ FLAC__stream_decoder_flush(decoder);
+ }
+}
+
+static const double prebuffer_seconds = 0.5;
+static bool DoBuffering(nx_file_t file)
+{
+ // TODO: check for disconnect, etc.
+
+ // anything less than half-a-second and we'll rebuffer. but when we rebuffer, we'll get 2 seconds worth of audio
+ // also, every time we're forced to re-buffer, we'll double the buffer amount
+
+ uint64_t required;
+ if (averageBitrate > 0)
+ {
+ required = (uint64_t)((double)averageBitrate * 1000.0 * buffer_scale * prebuffer_seconds / 8.0);
+ }
+ else /* no bitrate specified, let's assume 1000kbps */
+ {
+ required = (uint64_t)(1000.0 * 1000.0 * buffer_scale * prebuffer_seconds / 8.0);
+ }
+
+ uint64_t available;
+ if (NXFileProgressiveDownloaderAvailable(file, required, &available) == NErr_True)
+ return true;
+
+ Buffering(0, 0);
+ bufferCount=lastoutputtime;
+ required *= 4;
+ buffer_scale *= 2.0;
+
+ while (NXFileProgressiveDownloaderAvailable(file, required, &available) == NErr_False)
+ {
+ int percent = (int)((double)available * 100.0 / (double)required);
+ if (percent <= 0)
+ percent=1;
+ if (percent > 99)
+ percent=99;
+
+ Buffering(percent, 0);
+ if (WaitForSingleObject(killswitch, 10) == WAIT_OBJECT_0)
+ {
+ return false;
+ }
+ }
+ Buffering(100, 0);
+ bufferCount=0;
+ return true;
+}
+
+extern HANDLE threadStarted;
+DWORD CALLBACK FLACThread(LPVOID param)
+{
+ buffer_scale=1.0;
+ bool streaming=false;
+ nx_file_t file;
+ FLACClientData state;
+ audio_output.Init(plugin.outMod);
+ paused=false;
+ SetEvent(threadStarted);
+ nx_uri_t filename = (nx_uri_t)param;
+ decodePosition = 0;
+ gain = 1.0;
+ bufferCount = 0;
+ decoder = FLAC__stream_decoder_new();
+ if (decoder == 0)
+ {
+ if (WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0)
+ PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
+
+ free(filename);
+ return 0;
+ }
+
+ FLAC__stream_decoder_set_md5_checking(decoder, false);
+ FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
+
+ plugin.is_seekable = 1;
+
+
+ int ret;
+
+ if (NXPathIsURL(filename) == NErr_True)
+ {
+ // Display a window to request to download
+ //
+ MessageBox(NULL, L"Cannot stream flac file, please download it", NULL, 0);
+ /*
+ char agent[256] = { 0 };
+ snprintf(agent, 256, "User-Agent: %S/%S", "Winamp", "5.9.1");
+ ret = NXFileOpenProgressiveDownloader(&file, filename, nx_file_FILE_read_binary, 0, agent); // TODO: calculate real user agent
+ */
+ return NErr_Disabled;
+ }
+ else
+ {
+ ret = NXFileOpenFile(&file, filename, nx_file_FILE_read_binary);
+ }
+
+ if (ret != NErr_Success)
+ {
+ FLAC__stream_decoder_delete(decoder);
+ if (WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0)
+ PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
+
+ NXURIRelease(filename);
+ return 0;
+ }
+
+ state.SetFile(file);
+
+ NXFileLength(file, &fileSize);
+
+ if (FLAC__stream_decoder_init_stream(
+ decoder,
+ FLAC_NXFile_Read,
+ FLAC_NXFile_Seek,
+ FLAC_NXFile_Tell,
+ FLAC_NXFile_Length,
+ FLAC_NXFile_EOF,
+ OnAudio,
+ OnMetadata, // or NULL
+ OnError,
+ &state
+ ) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
+ {
+ FLAC__stream_decoder_delete(decoder);
+ NXFileRelease(file);
+ if (WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0)
+ PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
+
+ NXURIRelease(filename);
+ return 0;
+ }
+
+ FLAC__stream_decoder_process_until_end_of_metadata(decoder);
+
+
+
+ if (!audio_output.Open(0, channels, samplerate, bps))
+ {
+ FLAC__stream_decoder_finish(decoder);
+ FLAC__stream_decoder_delete(decoder);
+ NXFileRelease(file);
+ if (WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0)
+ PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
+
+ NXURIRelease(filename);
+ return 0;
+ }
+
+ plugin.SetInfo(averageBitrate, -1, -1, -1);
+ plugin.outMod->SetVolume(volume);
+ plugin.outMod->SetPan(pan);
+
+ if (streaming && !DoBuffering(file))
+ {
+ FLAC__stream_decoder_finish(decoder);
+ FLAC__stream_decoder_delete(decoder);
+ NXFileRelease(file);
+ NXURIRelease(filename);
+
+ return 0;
+ }
+
+ if (m_force_seek != -1)
+ APCSeek((ULONG_PTR)m_force_seek);
+ m_force_seek = -1;
+
+ HANDLE events[] = {killswitch};
+ DWORD timeout = 0;
+ while (true)
+ {
+ DWORD result = WaitForMultipleObjectsEx(sizeof(events) / sizeof(*events), events, FALSE, timeout, TRUE);
+ switch (result)
+ {
+ case WAIT_OBJECT_0:// kill thread
+ FLAC__stream_decoder_finish(decoder);
+ FLAC__stream_decoder_delete(decoder);
+ NXFileRelease(file);
+ NXURIRelease(filename);
+ return 0;
+
+ case WAIT_TIMEOUT:
+ timeout = paused ? PAUSE_TIMEOUT : 0;
+ if (streaming && !DoBuffering(file))
+ {
+ FLAC__stream_decoder_finish(decoder);
+ FLAC__stream_decoder_delete(decoder);
+ NXFileRelease(file);
+ NXURIRelease(filename);
+
+ return 0;
+ }
+
+ if (!paused)
+ {
+ FLAC__bool decode_successful = FLAC__stream_decoder_process_single(decoder);
+
+ FLAC__StreamDecoderState FLACstate = FLAC__stream_decoder_get_state(decoder);
+
+ if (FLACstate == FLAC__STREAM_DECODER_END_OF_STREAM)
+ {
+ audio_output.Write(0, 0);
+ if (audio_output.WaitWhilePlaying())
+ {
+ FLAC__stream_decoder_finish(decoder);
+ FLAC__stream_decoder_delete(decoder);
+ NXFileRelease(file);
+ NXURIRelease(filename);
+ return 0;
+ }
+ PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
+ timeout = INFINITE; // sit and wait for killswitch
+ }
+ else if (!decode_successful)
+ {
+ // some other error - abort playback
+ // if we can find FLAC files with errors, we might be able to gracefully handle some errors
+ PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
+ timeout = INFINITE; // sit and wait for killswitch
+ }
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
diff --git a/Src/Plugins/Input/in_flac/ExtendedFileInfo.cpp b/Src/Plugins/Input/in_flac/ExtendedFileInfo.cpp
new file mode 100644
index 00000000..39405687
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/ExtendedFileInfo.cpp
@@ -0,0 +1,424 @@
+/*
+** Copyright (C) 2007-2011 Nullsoft, Inc.
+**
+** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
+** liable for any damages arising from the use of this software.
+**
+** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
+** alter it and redistribute it freely, subject to the following restrictions:
+**
+** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
+** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+**
+** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+**
+** 3. This notice may not be removed or altered from any source distribution.
+**
+** Author: Ben Allison benski@winamp.com
+** Created: March 1, 2007
+**
+*/
+
+#include "main.h"
+#include "Metadata.h"
+#include "../nu/ns_wc.h"
+#include "../nu/AutoChar.h"
+#include "../Winamp/wa_ipc.h"
+#include <shlwapi.h>
+#include "resource.h"
+#include "../Agave/Language/api_language.h"
+#include "Stopper.h"
+#include <strsafe.h>
+
+static int FillFileInfo(wchar_t *infoStr, size_t len, FLACMetadata &metadata)
+{
+ const FLAC__StreamMetadata_StreamInfo *info = metadata.GetStreamInfo();
+ if (info)
+ {
+ unsigned __int64 length = info->total_samples / info->sample_rate;
+ StringCchPrintfExW(infoStr, len, &infoStr, &len, 0, WASABI_API_LNGSTRINGW(IDS_LENGTH_IN_SECONDS), length);
+ StringCchPrintfExW(infoStr, len, &infoStr, &len, 0, WASABI_API_LNGSTRINGW(IDS_CHANNELS), info->channels);
+ StringCchPrintfExW(infoStr, len, &infoStr, &len, 0, WASABI_API_LNGSTRINGW(IDS_BITS_PER_SAMPLE), info->bits_per_sample);
+ StringCchPrintfExW(infoStr, len, &infoStr, &len, 0, WASABI_API_LNGSTRINGW(IDS_SAMPLE_RATE), info->sample_rate);
+ __int64 filesize = metadata.GetFileSize();
+ StringCchPrintfExW(infoStr, len, &infoStr, &len, 0, WASABI_API_LNGSTRINGW(IDS_FILE_SIZE_IN_BYTES), filesize);
+ if (info->total_samples)
+ {
+ StringCchPrintfExW(infoStr, len, &infoStr, &len, 0, WASABI_API_LNGSTRINGW(IDS_AVERAGE_BITRATE), filesize / (125*info->total_samples / (__int64)info->sample_rate)); // (125 is 1000/8)
+ int percent = (int)((100*filesize) / (info->total_samples * (info->bits_per_sample/8) * info->channels));
+ StringCchPrintfExW(infoStr, len, &infoStr, &len, 0, WASABI_API_LNGSTRINGW(IDS_COMPRESSION_RATIO), percent);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+bool KeywordMatch(const char *mainString, const char *keyword)
+{
+ return !_stricmp(mainString, keyword);
+}
+
+Info *info = 0;
+FLACMetadata *getMetadata = 0;
+wchar_t *getFileInfoFn = 0;
+static FILETIME ftLastWriteTime;
+
+// is used to determine if the last write time of the file has changed when
+// asked to get the metadata for the same cached file so we can update things
+BOOL HasFileTimeChanged(const wchar_t *fn)
+{
+ WIN32_FILE_ATTRIBUTE_DATA fileData = {0};
+ if (GetFileAttributesExW(fn, GetFileExInfoStandard, &fileData) == TRUE)
+ {
+ if(CompareFileTime(&ftLastWriteTime, &fileData.ftLastWriteTime))
+ {
+ ftLastWriteTime = fileData.ftLastWriteTime;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+void UpdateFileTimeChanged(const wchar_t *fn)
+{
+ WIN32_FILE_ATTRIBUTE_DATA fileData;
+ if (GetFileAttributesExW(fn, GetFileExInfoStandard, &fileData) == TRUE)
+ {
+ ftLastWriteTime = fileData.ftLastWriteTime;
+ }
+}
+
+void ResetMetadataCache()
+{
+ // cheap way to trigger a metadata reset in a thread-safe manner
+ wchar_t d[10] = {0};
+ extendedFileInfoStructW reset_info = {0};
+ reset_info.filename=L".flac";
+ reset_info.metadata=L"artist";
+ reset_info.ret = d;
+ reset_info.retlen=10;
+ SendMessage(plugin.hMainWindow, WM_WA_IPC, (WPARAM)&reset_info, IPC_GET_EXTENDED_FILE_INFOW);
+}
+
+#define START_TAG_ALIAS(name, alias) if (KeywordMatch(data, name)) lookup=alias
+#define TAG_ALIAS(name, alias) else if (KeywordMatch(data, name)) lookup=alias
+extern "C" __declspec( dllexport ) int winampGetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *dest, int destlen)
+{
+ if (KeywordMatch(data, "type"))
+ {
+ dest[0] = '0';
+ dest[1] = 0;
+ return 1;
+ }
+ if (KeywordMatch(data, "rateable"))
+ {
+ dest[0] = '1';
+ dest[1] = 0;
+ return 1;
+ }
+ else if (KeywordMatch(data, "lossless"))
+ {
+ dest[0] = '1';
+ dest[1] = 0;
+ return 1;
+ }
+
+ if (!fn || (fn && !fn[0]))
+ return 0;
+
+ if (KeywordMatch(data, "family"))
+ {
+ LPCWSTR e;
+ int pID = -1;
+ DWORD lcid;
+ e = PathFindExtensionW(fn);
+ if (L'.' != *e) return 0;
+ e++;
+ lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+ if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"FLAC", -1) ||
+ CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"FLA", -1)) pID = IDS_FAMILY_STRING;
+
+ if (pID != -1 && S_OK == StringCchCopyW(dest, destlen, WASABI_API_LNGSTRINGW(pID))) return 1;
+ return 0;
+ }
+
+ if (KeywordMatch(data, "mime"))
+ {
+ StringCchCopyW(dest, destlen, L"audio/flac");
+ return 1;
+ }
+
+ if (!getMetadata || !getFileInfoFn || _wcsicmp(fn, getFileInfoFn) || HasFileTimeChanged(fn))
+ {
+ if (getMetadata)
+ getMetadata->Reset();
+ else
+ getMetadata = new FLACMetadata;
+
+ if (!getMetadata->Open(fn))
+ {
+ delete getMetadata;
+ getMetadata = 0;
+ dest[0]=0;
+ return 0;
+ }
+ free(getFileInfoFn);
+ getFileInfoFn = _wcsdup(fn);
+ }
+
+ FLACMetadata &metadata = *getMetadata;
+
+ if(KeywordMatch(data, "formatinformation"))
+ return FillFileInfo(dest,destlen,metadata);
+
+ const char *lookup=0;
+ if (KeywordMatch(data, "length"))
+ {
+ unsigned __int64 length_in_msec;
+ if (metadata.GetLengthMilliseconds(&length_in_msec))
+ StringCchPrintfW(dest, destlen, L"%d", length_in_msec);
+ else
+ dest[0]=0;
+ return 1;
+ }
+ else if (KeywordMatch(data, "bitrate"))
+ {
+ // TODO: move this into FLACMetadata
+ const FLAC__StreamMetadata_StreamInfo *streaminfo = metadata.GetStreamInfo();
+ if (streaminfo)
+ {
+ if (streaminfo->total_samples == 0 || streaminfo->sample_rate == 0) // prevent divide-by-zero
+ dest[0]=0;
+ else
+ StringCchPrintfW(dest, destlen, L"%I64d", metadata.GetFileSize() / (125*streaminfo->total_samples / (__int64)streaminfo->sample_rate)); // (125 is 1000/8)
+ }
+ else
+ dest[0]=0;
+ return 1;
+ }
+ TAG_ALIAS("title", "TITLE");
+ TAG_ALIAS("artist", "ARTIST");
+ TAG_ALIAS("album", "ALBUM");
+ TAG_ALIAS("genre", "GENRE");
+ TAG_ALIAS("comment", "COMMENT");
+ TAG_ALIAS("year", "DATE");
+ TAG_ALIAS("track", "TRACKNUMBER");
+ TAG_ALIAS("albumartist", "ALBUM ARTIST");
+ TAG_ALIAS("composer", "COMPOSER");
+ TAG_ALIAS("disc", "DISCNUMBER");
+ TAG_ALIAS("publisher", "PUBLISHER");
+ TAG_ALIAS("conductor", "CONDUCTOR");
+ TAG_ALIAS("tool", "ENCODED-BY");
+ TAG_ALIAS("replaygain_track_gain", "REPLAYGAIN_TRACK_GAIN");
+ TAG_ALIAS("replaygain_track_peak", "REPLAYGAIN_TRACK_PEAK");
+ TAG_ALIAS("replaygain_album_gain", "REPLAYGAIN_ALBUM_GAIN");
+ TAG_ALIAS("replaygain_album_peak", "REPLAYGAIN_ALBUM_PEAK");
+ TAG_ALIAS("GracenoteFileID", "GRACENOTEFILEID");
+ TAG_ALIAS("GracenoteExtData", "GRACENOTEEXTDATA");
+ TAG_ALIAS("bpm", "BPM");
+ TAG_ALIAS("remixing", "REMIXING");
+ TAG_ALIAS("subtitle", "VERSION");
+ TAG_ALIAS("isrc", "ISRC");
+ TAG_ALIAS("category", "CATEGORY");
+ TAG_ALIAS("rating", "RATING");
+ TAG_ALIAS("producer", "PRODUCER");
+
+ if (!lookup)
+ return 0;
+
+ const char *value = metadata.GetMetadata(lookup);
+
+ if(KeywordMatch("comment",data)) {
+ if(!value || !*value) value = metadata.GetMetadata("DESCRIPTION");
+ }
+
+ if(KeywordMatch("year",data)) {
+ if(!value || !*value) value = metadata.GetMetadata("YEAR");
+ }
+
+ if(KeywordMatch("track",data)) {
+ if(!value || !*value) value = metadata.GetMetadata("TRACK");
+ }
+
+ if(KeywordMatch("albumartist",data)) {
+ if(!value || !*value) value = metadata.GetMetadata("ALBUMARTIST");
+ if(!value || !*value) value = metadata.GetMetadata("ENSEMBLE");
+ }
+
+ if(KeywordMatch("publisher",data)) {
+ if(!value || !*value) value = metadata.GetMetadata("ORGANIZATION");
+ }
+
+ if(KeywordMatch("category",data)) {
+ if(!value || !*value) value = metadata.GetMetadata("CONTENTGROUP");
+ if(!value || !*value) value = metadata.GetMetadata("GROUPING");
+ }
+
+ if(KeywordMatch(data, "rating")) {
+ if(!value || !*value) value = metadata.GetMetadata("RATING");
+ if(value && *value) {
+ int rating = atoi(value);
+
+ // keeps things limited to our range of 0-100
+ if (rating >= 100) {
+ rating = 5;
+ }
+ // 1-100 case
+ else if (rating > 5 && rating < 100) {
+ rating = (rating /= 20);
+ // shift up by one rating when in next band
+ // 1-20 = 1, 21-40 = 2, 41-60 = 3, 61-80 = 4, 81-100 = 5
+ rating += ((atoi(value) - (rating * 20)) > 0);
+ }
+ else if (rating > 0 && rating <= 5) {
+ }
+ // otherwise just make sure and set zero
+ else {
+ rating = 0;
+ }
+
+ StringCchPrintfW(dest, destlen, L"%u", rating);
+ return 1;
+ }
+ }
+
+ MultiByteToWideCharSZ(CP_UTF8, 0, value, -1, dest, destlen);
+ return 1;
+}
+
+FLACMetadata *setMetadata=0;
+wchar_t *setFn=0;
+extern "C" __declspec( dllexport ) int winampSetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *val)
+{
+ if (!setMetadata || !setFn || lstrcmpiW(fn, setFn))
+ {
+ free(setFn);
+ setFn=_wcsdup(fn);
+ if (!setMetadata)
+ setMetadata = new FLACMetadata;
+ if (setMetadata->Open(setFn, true) == false)
+ {
+ delete setMetadata;
+ setMetadata=0;
+ return 0;
+ }
+ }
+
+ const char *lookup=0;
+ START_TAG_ALIAS("artist", "ARTIST");
+ TAG_ALIAS("title", "TITLE");
+ TAG_ALIAS("album", "ALBUM");
+ TAG_ALIAS("genre", "GENRE");
+ TAG_ALIAS("comment", "COMMENT");
+ TAG_ALIAS("year", "DATE");
+ TAG_ALIAS("track", "TRACKNUMBER");
+ TAG_ALIAS("albumartist", "ALBUM ARTIST");
+ TAG_ALIAS("composer", "COMPOSER");
+ TAG_ALIAS("disc", "DISCNUMBER");
+ TAG_ALIAS("publisher", "PUBLISHER");
+ TAG_ALIAS("conductor", "CONDUCTOR");
+ TAG_ALIAS("tool", "ENCODED-BY");
+ TAG_ALIAS("replaygain_track_gain", "REPLAYGAIN_TRACK_GAIN");
+ TAG_ALIAS("replaygain_track_peak", "REPLAYGAIN_TRACK_PEAK");
+ TAG_ALIAS("replaygain_album_gain", "REPLAYGAIN_ALBUM_GAIN");
+ TAG_ALIAS("replaygain_album_peak", "REPLAYGAIN_ALBUM_PEAK");
+ TAG_ALIAS("GracenoteFileID", "GRACENOTEFILEID");
+ TAG_ALIAS("GracenoteExtData", "GRACENOTEEXTDATA");
+ TAG_ALIAS("bpm", "BPM");
+ TAG_ALIAS("remixing", "REMIXING");
+ TAG_ALIAS("subtitle", "VERSION");
+ TAG_ALIAS("isrc", "ISRC");
+ TAG_ALIAS("category", "CATEGORY");
+ TAG_ALIAS("rating", "RATING");
+ TAG_ALIAS("producer", "PRODUCER");
+
+ if (!lookup)
+ return 0;
+
+ if (val && *val)
+ {
+ if(KeywordMatch("rating",data))
+ {
+ char temp[128] = {0};
+ StringCchPrintfA(temp, 128, "%u", _wtoi(val)*20);
+ setMetadata->SetMetadata(lookup, temp);
+ }
+ else
+ {
+ setMetadata->SetMetadata(lookup, AutoChar(val, CP_UTF8));
+ }
+ }
+ else
+ {
+ setMetadata->RemoveMetadata(lookup);
+ if(KeywordMatch("comment",data))
+ {
+ // need to remove this one also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using this alternate field
+ setMetadata->RemoveMetadata("DESCRIPTION");
+ }
+ else if(KeywordMatch("year",data))
+ {
+ // need to remove this one also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using this alternate field
+ setMetadata->RemoveMetadata("YEAR");
+ }
+ else if(KeywordMatch("track",data))
+ {
+ // need to remove this one also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using this alternate field
+ setMetadata->RemoveMetadata("TRACK");
+ }
+ else if(KeywordMatch("albumartist",data))
+ {
+ // need to remove these two, also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using these alternate fields
+ setMetadata->RemoveMetadata("ALBUMARTIST");
+ setMetadata->RemoveMetadata("ENSEMBLE");
+ }
+ else if(KeywordMatch("publisher",data))
+ {
+ // need to remove this one also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using this alternate field
+ setMetadata->RemoveMetadata("ORGANIZATION");
+ }
+ else if(KeywordMatch("category",data))
+ {
+ // need to remove these two also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using these alternate fields
+ setMetadata->RemoveMetadata("CONTENTGROUP");
+ setMetadata->RemoveMetadata("GROUPING");
+ }
+ }
+
+ return 1;
+}
+
+extern "C" __declspec(dllexport) int winampWriteExtendedFileInfo()
+{
+ if (setFn && setMetadata)
+ {
+ Stopper stopper;
+ if (lastfn && !_wcsicmp(lastfn, setFn))
+ stopper.Stop();
+ bool success = setMetadata->Save(setFn);
+ stopper.Play();
+ setMetadata->Reset();
+ free(setFn);
+ setFn=0;
+
+ delete getMetadata;
+ getMetadata=0;
+
+ // update last modified so we're not re-queried on our own updates
+ UpdateFileTimeChanged(setFn);
+
+ return !!success;
+ }
+ return 1;
+}
+
+extern "C" __declspec(dllexport) const wchar_t *winampWriteExtendedGetLastError()
+{
+ return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/ExtendedRead.cpp b/Src/Plugins/Input/in_flac/ExtendedRead.cpp
new file mode 100644
index 00000000..d3c08fc8
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/ExtendedRead.cpp
@@ -0,0 +1,166 @@
+/*
+** Copyright (C) 2007-2011 Nullsoft, Inc.
+**
+** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
+** liable for any damages arising from the use of this software.
+**
+** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
+** alter it and redistribute it freely, subject to the following restrictions:
+**
+** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
+** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+**
+** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+**
+** 3. This notice may not be removed or altered from any source distribution.
+**
+** Author: Ben Allison benski@winamp.com
+** Created: March 1, 2007
+**
+*/
+
+#include "main.h"
+#include <FLAC/all.h>
+#include "StreamFileWin32.h"
+#include "QuickBuf.h"
+#include <bfc/platform/types.h>
+#include <assert.h>
+#include "FLACFileCallbacks.h"
+#include "nswasabi/ReferenceCounted.h"
+
+struct ExtendedRead
+{
+ int bps, channels, samplerate, truebps;
+ uint64_t samples;
+ QuickBuf output;
+ FLAC__StreamDecoder *decoder;
+ size_t used;
+ FLACClientData client_data;
+};
+
+static void OnError(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
+{
+ //client_data=client_data; // dummy line so i can set a breakpoint
+}
+
+static void OnMetadata(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
+{
+ ExtendedRead *ext = FLAC_GetObject<ExtendedRead>(client_data);
+
+ switch(metadata->type)
+ {
+ case FLAC__METADATA_TYPE_STREAMINFO:
+ {
+ ext->truebps=metadata->data.stream_info.bits_per_sample;
+ ext->bps = (ext->truebps +7) & (~7);
+ ext->channels=metadata->data.stream_info.channels;
+ ext->samplerate=metadata->data.stream_info.sample_rate;
+ ext->samples=metadata->data.stream_info.total_samples;
+ }
+ break;
+ }
+}
+
+static FLAC__StreamDecoderWriteStatus OnAudio(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data)
+{
+ ExtendedRead *ext = FLAC_GetObject<ExtendedRead>(client_data);
+
+ int byteLength = (ext->bps/8) * ext->channels * frame->header.blocksize;
+ ext->output.Reserve(byteLength);
+ InterleaveAndTruncate(buffer, ext->output, ext->bps, ext->channels, frame->header.blocksize);
+ ext->used = byteLength;
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+extern "C"
+{
+ __declspec( dllexport ) intptr_t winampGetExtendedRead_openW(const wchar_t *fn, int *size, int *bps, int *nch, int *srate)
+ {
+ nx_file_t file;
+ ReferenceCountedNXString filename_nx;
+ ReferenceCountedNXURI filename_uri;
+ NXStringCreateWithUTF16(&filename_nx, fn);
+ NXURICreateWithNXString(&filename_uri, filename_nx);
+
+ int ret = NXFileOpenFile(&file, filename_uri, nx_file_FILE_read_binary);
+ if (ret != NErr_Success)
+ return 0;
+
+ ExtendedRead * e = (ExtendedRead *)calloc(sizeof(ExtendedRead), 1);
+ e->decoder = FLAC__stream_decoder_new();
+ if (e->decoder == 0)
+ {
+ NXFileRelease(file);
+ free(e);
+ return 0;
+ }
+
+ e->client_data.SetFile(file);
+ e->client_data.SetObject(e);
+
+ FLAC__stream_decoder_set_md5_checking(e->decoder, true);
+ if(FLAC__stream_decoder_init_stream(
+ e->decoder,
+ FLAC_NXFile_Read,
+ FLAC_NXFile_Seek,
+ FLAC_NXFile_Tell,
+ FLAC_NXFile_Length,
+ FLAC_NXFile_EOF,
+ OnAudio,
+ OnMetadata,
+ OnError,
+ &e->client_data
+ ) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
+ {
+ FLAC__stream_decoder_delete(e->decoder);
+ NXFileRelease(file);
+ free(e);
+ return 0;
+ }
+
+ FLAC__stream_decoder_process_until_end_of_metadata(e->decoder);
+ *bps = e->truebps;
+ *nch = e->channels;
+ *srate = e->samplerate;
+ *size = (int)(e->samples * (e->bps/8) * e->channels);
+ return (intptr_t) e;
+ }
+
+ __declspec( dllexport ) intptr_t winampGetExtendedRead_getData(intptr_t handle, char *dest, size_t len, int *killswitch)
+ {
+ ExtendedRead *ext = (ExtendedRead *)handle;
+
+ while(!ext->used) // loop until we get some data
+ {
+ if (FLAC__stream_decoder_process_single(ext->decoder) == 0)
+ break; // break out if there's an error
+
+ FLAC__StreamDecoderState FLACstate = FLAC__stream_decoder_get_state(ext->decoder);
+ if (FLACstate == FLAC__STREAM_DECODER_END_OF_STREAM) // break out if we hit EOF
+ break;
+ }
+
+ if (ext->used)
+ {
+ size_t toCopy = min(len, ext->used);
+ memcpy(dest, ext->output, toCopy);
+ if (toCopy < ext->used)
+ ext->output.Move(toCopy);
+ ext->used-=toCopy;
+ return toCopy;
+ }
+
+ return 0;
+ }
+
+ __declspec( dllexport ) void winampGetExtendedRead_close(intptr_t handle)
+ {
+ ExtendedRead *ext = (ExtendedRead *)handle;
+
+ ext->output.Free();
+ FLAC__stream_decoder_finish(ext->decoder);
+ FLAC__stream_decoder_delete(ext->decoder);
+ NXFileRelease(ext->client_data.GetFile());
+ free(ext);
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/FLACFileCallbacks.cpp b/Src/Plugins/Input/in_flac/FLACFileCallbacks.cpp
new file mode 100644
index 00000000..23a53b91
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/FLACFileCallbacks.cpp
@@ -0,0 +1,57 @@
+#include "FLACFileCallbacks.h"
+
+
+FLAC__StreamDecoderReadStatus FLAC_NXFile_Read(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
+{
+ nx_file_t file = FLAC_GetFile(client_data);
+ size_t bytes_to_read = *bytes;
+ size_t bytes_read=0;
+ ns_error_t ret = NXFileRead(file, buffer, bytes_to_read, &bytes_read);
+ *bytes = bytes_read;
+
+ if (ret == NErr_EndOfFile)
+ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+
+ if (ret != NErr_Success)
+ return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+
+ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+}
+
+FLAC__StreamDecoderSeekStatus FLAC_NXFile_Seek(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)
+{
+ nx_file_t file = FLAC_GetFile(client_data);
+ if (NXFileSeek(file, absolute_byte_offset) == NErr_Success)
+ return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
+ else
+ return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
+}
+
+FLAC__StreamDecoderTellStatus FLAC_NXFile_Tell(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
+{
+ nx_file_t file = FLAC_GetFile(client_data);
+
+ if (NXFileTell(file, absolute_byte_offset) == NErr_Success)
+ return FLAC__STREAM_DECODER_TELL_STATUS_OK;
+ else
+ return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
+}
+
+FLAC__StreamDecoderLengthStatus FLAC_NXFile_Length(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)
+{
+ nx_file_t file = FLAC_GetFile(client_data);
+
+ if (NXFileLength(file, stream_length) == NErr_Success)
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
+ else
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
+}
+
+FLAC__bool FLAC_NXFile_EOF(const FLAC__StreamDecoder *decoder, void *client_data)
+{
+ nx_file_t file = FLAC_GetFile(client_data);
+ if (NXFileEndOfFile(file) == NErr_False)
+ return false;
+ else
+ return true;
+}
diff --git a/Src/Plugins/Input/in_flac/FLACFileCallbacks.h b/Src/Plugins/Input/in_flac/FLACFileCallbacks.h
new file mode 100644
index 00000000..7bd2538c
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/FLACFileCallbacks.h
@@ -0,0 +1,35 @@
+#pragma once
+#include <FLAC/all.h>
+#include "nx/nxfile.h"
+
+
+FLAC__StreamDecoderReadStatus FLAC_NXFile_Read(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data);
+FLAC__StreamDecoderSeekStatus FLAC_NXFile_Seek(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data);
+FLAC__StreamDecoderTellStatus FLAC_NXFile_Tell(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
+FLAC__StreamDecoderLengthStatus FLAC_NXFile_Length(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data);
+FLAC__bool FLAC_NXFile_EOF(const FLAC__StreamDecoder *decoder, void *client_data);
+
+
+class FLACClientData
+{
+public:
+ FLACClientData() : object(0) {}
+ void SetFile(nx_file_t file) { this->file = file; }
+ void SetObject(void *object) { this->object = object; }
+ nx_file_t GetFile() { return file; }
+ void *GetObject() { return object; }
+private:
+ nx_file_t file;
+ void *object;
+};
+
+template <typename _t>
+static _t *FLAC_GetObject(void *client_data)
+{
+ return (_t *)((FLACClientData *)client_data)->GetObject();
+}
+
+static nx_file_t FLAC_GetFile(void *client_data)
+{
+ return ((FLACClientData *)client_data)->GetFile();
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/FileInfo.cpp b/Src/Plugins/Input/in_flac/FileInfo.cpp
new file mode 100644
index 00000000..d0684c59
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/FileInfo.cpp
@@ -0,0 +1,400 @@
+/*
+** Copyright (C) 2007-2011 Nullsoft, Inc.
+**
+** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
+** liable for any damages arising from the use of this software.
+**
+** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
+** alter it and redistribute it freely, subject to the following restrictions:
+**
+** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
+** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+**
+** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+**
+** 3. This notice may not be removed or altered from any source distribution.
+**
+** Author: Ben Allison benski@winamp.com
+** Created: March 1, 2007
+**
+*/
+
+#include <FLAC/all.h>
+#include "main.h"
+#include "../nu/ns_wc.h"
+#include <windows.h>
+#include "resource.h"
+#include "Metadata.h"
+#include "../nu/AutoWide.h"
+#include "../nu/AutoChar.h"
+#include "Stopper.h"
+#include <strsafe.h>
+#include <commctrl.h>
+#include "../Agave/Language/api_language.h"
+
+bool FlacTagToWinampTag(wchar_t * tag, int len)
+{
+#define TAG_ALIAS(b,a) if(!_wcsicmp(L ## a, tag)) { lstrcpynW(tag, L ## b, len); return true; }
+ TAG_ALIAS("title", "TITLE");
+ TAG_ALIAS("artist", "ARTIST");
+ TAG_ALIAS("album", "ALBUM");
+ TAG_ALIAS("genre", "GENRE");
+ TAG_ALIAS("comment", "COMMENT");
+ TAG_ALIAS("year", "DATE");
+ TAG_ALIAS("track", "TRACKNUMBER");
+ TAG_ALIAS("albumartist", "ALBUM ARTIST");
+ TAG_ALIAS("composer", "COMPOSER");
+ TAG_ALIAS("disc", "DISCNUMBER");
+ TAG_ALIAS("publisher", "PUBLISHER");
+ TAG_ALIAS("conductor", "CONDUCTOR");
+ TAG_ALIAS("bpm", "BPM");
+ return false;
+#undef TAG_ALIAS
+}
+
+bool WinampTagToFlacTag(wchar_t * tag, int len)
+{
+#define TAG_ALIAS(a,b) if(!_wcsicmp(L ## a, tag)) { lstrcpynW(tag, L ## b, len); return true; }
+ TAG_ALIAS("title", "TITLE");
+ TAG_ALIAS("artist", "ARTIST");
+ TAG_ALIAS("album", "ALBUM");
+ TAG_ALIAS("genre", "GENRE");
+ TAG_ALIAS("comment", "COMMENT");
+ TAG_ALIAS("year", "DATE");
+ TAG_ALIAS("track", "TRACKNUMBER");
+ TAG_ALIAS("albumartist", "ALBUM ARTIST");
+ TAG_ALIAS("composer", "COMPOSER");
+ TAG_ALIAS("disc", "DISCNUMBER");
+ TAG_ALIAS("publisher", "PUBLISHER");
+ TAG_ALIAS("conductor", "CONDUCTOR");
+ TAG_ALIAS("bpm", "BPM");
+ return false;
+#undef TAG_ALIAS
+}
+
+static INT_PTR CALLBACK ChildProc_Advanced(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
+ static int sel=-1;
+ static int ismychange=0;
+ wchar_t key[512]={0};
+ wchar_t value[32768]={0};
+
+ switch(msg)
+ {
+ case WM_NOTIFYFORMAT:
+ return NFR_UNICODE;
+ case WM_INITDIALOG:
+ {
+ #define ListView_InsertColumnW(hwnd, iCol, pcol) \
+ (int)SNDMSG((hwnd), LVM_INSERTCOLUMNW, (WPARAM)(int)(iCol), (LPARAM)(const LV_COLUMNW *)(pcol))
+ SetWindowLongPtr(hwndDlg,GWLP_USERDATA,lParam);
+ sel=-1;
+ HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST);
+ ListView_SetExtendedListViewStyle(hwndlist, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP);
+ LVCOLUMNW lvc = {0, };
+ lvc.mask = LVCF_TEXT|LVCF_WIDTH;
+ lvc.pszText = WASABI_API_LNGSTRINGW(IDS_NAME);
+ lvc.cx = 82;
+ ListView_InsertColumnW(hwndlist, 0, &lvc);
+ lvc.pszText = WASABI_API_LNGSTRINGW(IDS_VALUE);
+ lvc.cx = 160;
+ ListView_InsertColumnW(hwndlist, 1, &lvc);
+
+ Info *info = (Info *)lParam;
+ int n = info->metadata.GetNumMetadataItems();
+ for(int i=0; i<n; i++) {
+ char key_[512] = {0};
+ const char* value_ = info->metadata.EnumMetadata(i,key_,512);
+ if(value_ && key_[0]) {
+ AutoWide k(key_, CP_UTF8);
+ AutoWide v(value_, CP_UTF8);
+ LVITEMW lvi={LVIF_TEXT,i,0};
+ lvi.pszText = k;
+ SendMessage(hwndlist,LVM_INSERTITEMW,0,(LPARAM)&lvi);
+ lvi.iSubItem=1;
+ lvi.pszText = v;
+ SendMessage(hwndlist,LVM_SETITEMW,0,(LPARAM)&lvi);
+ }
+ }
+ ListView_SetColumnWidth(hwndlist,0,LVSCW_AUTOSIZE);
+ ListView_SetColumnWidth(hwndlist,1,LVSCW_AUTOSIZE);
+
+ SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
+ SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
+ EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
+ }
+ break;
+ case WM_DESTROY:
+ {
+ HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST);
+ ListView_DeleteAllItems(hwndlist);
+ while(ListView_DeleteColumn(hwndlist,0));
+ Info * info = (Info*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ delete info;
+ info = 0;
+ }
+ break;
+ case WM_USER:
+ if(wParam && lParam && !ismychange)
+ {
+ wchar_t * value = (wchar_t*)lParam;
+ wchar_t tag[100] = {0};
+ lstrcpynW(tag,(wchar_t*)wParam,100);
+ WinampTagToFlacTag(tag,100);
+ Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if(!*value)
+ {
+ info->metadata.RemoveMetadata(AutoChar(tag,CP_UTF8));
+ if(!_wcsicmp(L"ALBUM ARTIST",tag))
+ {
+ // need to remove these two, also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using these alternate fields
+ info->metadata.RemoveMetadata("ALBUMARTIST");
+ info->metadata.RemoveMetadata("ENSEMBLE");
+ }
+ if(!_wcsicmp(L"PUBLISHER",tag))
+ {
+ // need to remove this also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using this alternate field
+ info->metadata.RemoveMetadata("ORGANIZATION");
+ }
+ if(!_wcsicmp(L"DATE",tag))
+ {
+ // need to remove this also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using this alternate field
+ info->metadata.RemoveMetadata("YEAR");
+ }
+ if(!_wcsicmp(L"TRACKNUMBER",tag))
+ {
+ // need to remove this also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using this alternate field
+ info->metadata.RemoveMetadata("TRACK");
+ }
+ }
+ else
+ {
+ info->metadata.SetMetadata(AutoChar(tag,CP_UTF8),AutoChar(value,CP_UTF8));
+ }
+ HWND hlist = GetDlgItem(hwndDlg,IDC_LIST);
+ int n = ListView_GetItemCount(hlist);
+ for(int i=0; i<n; i++)
+ {
+ key[0]=0;
+ LVITEMW lvi={LVIF_TEXT,i,0};
+ lvi.pszText=key;
+ lvi.cchTextMax=sizeof(key)/sizeof(*key);
+ SendMessage(hlist,LVM_GETITEMW,0,(LPARAM)&lvi);
+ if(!_wcsicmp(key,tag))
+ {
+ lvi.iSubItem = 1;
+ lvi.pszText = value;
+ SendMessage(hlist,LVM_SETITEMW,0,(LPARAM)&lvi);
+ if(!*value)
+ ListView_DeleteItem(hlist,i);
+ else if(ListView_GetItemState(hlist,i,LVIS_SELECTED))
+ SetDlgItemTextW(hwndDlg,IDC_VALUE,value);
+ return 0;
+ }
+ }
+ // bew hew, not found
+ LVITEMW lvi={0,0x7FFFFFF0,0};
+ n = SendMessage(hlist,LVM_INSERTITEMW,0,(LPARAM)&lvi);
+ lvi.mask = LVIF_TEXT;
+ lvi.iItem = n;
+ lvi.iSubItem = 0;
+ lvi.pszText = tag;
+ SendMessage(hlist,LVM_SETITEMW,0,(LPARAM)&lvi);
+ lvi.iSubItem = 1;
+ lvi.pszText = value;
+ SendMessage(hlist,LVM_SETITEMW,0,(LPARAM)&lvi);
+ }
+ break;
+ case WM_NOTIFY:
+ {
+ LPNMHDR l=(LPNMHDR)lParam;
+ if(l->idFrom==IDC_LIST && l->code == LVN_KEYDOWN) {
+ if((((LPNMLVKEYDOWN)l)->wVKey) == VK_DELETE){
+ int selitem = ListView_GetNextItem(l->hwndFrom,-1,LVNI_SELECTED|LVNI_FOCUSED);
+ if(selitem != -1)
+ SendMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_BUTTON_DEL,BN_CLICKED),(LPARAM)GetDlgItem(hwndDlg,IDC_BUTTON_DEL));
+ }
+ }
+ else if(l->idFrom==IDC_LIST && l->code == LVN_ITEMCHANGED) {
+ LPNMLISTVIEW lv=(LPNMLISTVIEW)lParam;
+ if(lv->uNewState & LVIS_SELECTED) {
+ int n = lv->iItem;
+ LVITEMW lvi={LVIF_TEXT,lv->iItem,0};
+ lvi.pszText=key;
+ lvi.cchTextMax=sizeof(key)/sizeof(*key);
+ SendMessage(l->hwndFrom,LVM_GETITEMW,0,(LPARAM)&lvi);
+ lvi.pszText=value;
+ lvi.cchTextMax=sizeof(value)/sizeof(*value);
+ lvi.iSubItem=1;
+ SendMessage(l->hwndFrom,LVM_GETITEMW,0,(LPARAM)&lvi);
+ SetDlgItemTextW(hwndDlg,IDC_NAME,key);
+ SetDlgItemTextW(hwndDlg,IDC_VALUE,value);
+ sel = n;
+ EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),TRUE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),TRUE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),TRUE);
+ }
+ if(lv->uOldState & LVIS_SELECTED) {
+ sel = -1;
+ SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
+ SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
+ EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
+ }
+ }
+ }
+ break;
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDOK:
+ {
+ Info * info = (Info*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ Stopper stopper;
+ if (lastfn && !_wcsicmp(lastfn, info->filename))
+ stopper.Stop();
+ bool success = info->metadata.Save(info->filename);
+ stopper.Play();
+ if (success)
+ {
+ ResetMetadataCache();
+ }
+ else
+ {
+ wchar_t title[128] = {0};
+ MessageBoxW(hwndDlg,WASABI_API_LNGSTRINGW(IDS_CANNOT_SAVE_METADATA),
+ WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA,title,128),
+ MB_OK | MB_ICONWARNING);
+ }
+ }
+ break;
+ case IDC_NAME:
+ case IDC_VALUE:
+ if(HIWORD(wParam) == EN_CHANGE && sel>=0) {
+ LVITEMW lvi={LVIF_TEXT,sel,0};
+ GetDlgItemTextW(hwndDlg,IDC_NAME,key,sizeof(key)/sizeof(*key));
+ GetDlgItemTextW(hwndDlg,IDC_VALUE,value,sizeof(value)/sizeof(*value));
+ lvi.pszText=key;
+ lvi.cchTextMax=sizeof(key)/sizeof(*key);
+ SendMessage(GetDlgItem(hwndDlg,IDC_LIST),LVM_SETITEMW,0,(LPARAM)&lvi);
+ lvi.pszText=value;
+ lvi.cchTextMax=sizeof(value)/sizeof(*value);
+ lvi.iSubItem=1;
+ SendMessage(GetDlgItem(hwndDlg,IDC_LIST),LVM_SETITEMW,0,(LPARAM)&lvi);
+ FlacTagToWinampTag(key,sizeof(key)/sizeof(*key));
+ ismychange=1;
+ SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)value);
+ ismychange=0;
+ }
+ else if(HIWORD(wParam) == EN_KILLFOCUS && sel>=0) {
+ GetDlgItemTextW(hwndDlg,IDC_NAME,key,sizeof(key)/sizeof(*key));
+ GetDlgItemTextW(hwndDlg,IDC_VALUE,value,sizeof(value)/sizeof(*value));
+ Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ char oldkeyA[100]="";
+ bool newitem=true;
+ if(sel < info->metadata.GetNumMetadataItems()) {
+ info->metadata.EnumMetadata(sel,oldkeyA,100);
+ newitem=false;
+ }
+ AutoWide oldkey(oldkeyA);
+ if(!newitem && _wcsicmp(oldkey,key)) { // key changed
+ info->metadata.SetTag(sel,AutoChar(key,CP_UTF8));
+ } else {
+ info->metadata.SetMetadata(AutoChar(key,CP_UTF8),AutoChar(value,CP_UTF8));
+ }
+ FlacTagToWinampTag(key,sizeof(key)/sizeof(*key));
+ ismychange=1;
+ SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)value);
+ ismychange=0;
+ }
+ break;
+ case IDC_BUTTON_DEL:
+ if(sel >= 0){
+ GetDlgItemTextW(hwndDlg,IDC_NAME,key,sizeof(key)/sizeof(*key));
+ SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
+ SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
+ EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
+ Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if(sel < info->metadata.GetNumMetadataItems())
+ info->metadata.RemoveMetadata(sel);
+ ListView_DeleteItem(GetDlgItem(hwndDlg,IDC_LIST),sel);
+ sel=-1;
+ FlacTagToWinampTag(key,sizeof(key)/sizeof(*key));
+ ismychange=1;
+ SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)L"");
+ ismychange=0;
+ }
+ break;
+ case IDC_BUTTON_DELALL:
+ ListView_DeleteAllItems(GetDlgItem(hwndDlg,IDC_LIST));
+ SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
+ SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
+ EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
+ sel=-1;
+ {
+ Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ int n = info->metadata.GetNumMetadataItems();
+ while(n>0) {
+ --n;
+ char tag[100] = {0};
+ info->metadata.EnumMetadata(n,tag,100);
+ MultiByteToWideCharSZ(CP_UTF8, 0, tag, -1, key, sizeof(key)/sizeof(*key));
+ FlacTagToWinampTag(key,sizeof(key)/sizeof(*key));
+ ismychange=1;
+ SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)L"");
+ ismychange=0;
+ info->metadata.RemoveMetadata(n);
+ }
+ }
+ break;
+ case IDC_BUTTON_ADD:
+ {
+ HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST);
+ LVITEMW lvi={0,0x7FFFFFF0,0};
+ int n = SendMessage(hwndlist,LVM_INSERTITEMW,0,(LPARAM)&lvi);
+ ListView_SetItemState(hwndlist,n,LVIS_SELECTED,LVIS_SELECTED);
+ }
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+extern "C"
+{
+ // return 1 if you want winamp to show it's own file info dialogue, 0 if you want to show your own (via In_Module.InfoBox)
+ // if returning 1, remember to implement winampGetExtendedFileInfo("formatinformation")!
+ __declspec(dllexport) int winampUseUnifiedFileInfoDlg(const wchar_t * fn)
+ {
+ return 1;
+ }
+
+ // should return a child window of 513x271 pixels (341x164 in msvc dlg units), or return NULL for no tab.
+ // Fill in name (a buffer of namelen characters), this is the title of the tab (defaults to "Advanced").
+ // filename will be valid for the life of your window. n is the tab number. This function will first be
+ // called with n == 0, then n == 1 and so on until you return NULL (so you can add as many tabs as you like).
+ // The window you return will recieve WM_COMMAND, IDOK/IDCANCEL messages when the user clicks OK or Cancel.
+ // when the user edits a field which is duplicated in another pane, do a SendMessage(GetParent(hwnd),WM_USER,(WPARAM)L"fieldname",(LPARAM)L"newvalue");
+ // this will be broadcast to all panes (including yours) as a WM_USER.
+ __declspec(dllexport) HWND winampAddUnifiedFileInfoPane(int n, const wchar_t * filename, HWND parent, wchar_t *name, size_t namelen)
+ {
+ if(n == 0) { // add first pane
+ SetPropW(parent,L"INBUILT_NOWRITEINFO", (HANDLE)1);
+ info = new Info;
+ info->filename = filename;
+ info->metadata.Open(filename, true);
+ return WASABI_API_CREATEDIALOGPARAMW(IDD_INFOCHILD_ADVANCED,parent,ChildProc_Advanced,(LPARAM)info);
+ }
+ return NULL;
+ }
+}; \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/Metadata.cpp b/Src/Plugins/Input/in_flac/Metadata.cpp
new file mode 100644
index 00000000..49815c54
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/Metadata.cpp
@@ -0,0 +1,577 @@
+/*
+** Copyright (C) 2007-2011 Nullsoft, Inc.
+**
+** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
+** liable for any damages arising from the use of this software.
+**
+** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
+** alter it and redistribute it freely, subject to the following restrictions:
+**
+** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
+** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+**
+** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+**
+** 3. This notice may not be removed or altered from any source distribution.
+**
+** Author: Ben Allison benski@winamp.com
+** Created: March 1, 2007
+**
+*/
+#include "Metadata.h"
+#include <FLAC/all.h>
+#include "StreamFileWin32.h" // for FileSize64
+#include "api__in_flv.h"
+#include <strsafe.h>
+
+struct MetadataReader
+{
+ MetadataReader(HANDLE _handle)
+ {
+ handle = _handle;
+ endOfFile=false;
+ }
+ HANDLE handle;
+ bool endOfFile;
+};
+
+static size_t win32_read(void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle)
+{
+ MetadataReader *reader = (MetadataReader *)handle;
+ DWORD bytesRead=0;
+ BOOL result = ReadFile(reader->handle, ptr, size*nmemb, &bytesRead, NULL);
+ if (result == TRUE && bytesRead == 0)
+ reader->endOfFile=true;
+ return bytesRead/size;
+}
+
+static size_t win32_write(const void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle)
+{
+ MetadataReader *reader = (MetadataReader *)handle;
+ DWORD bytesWritten=0;
+ WriteFile(reader->handle, ptr, size*nmemb, &bytesWritten, NULL);
+ return bytesWritten/size;
+}
+
+static __int64 Seek64(HANDLE hf, __int64 distance, DWORD MoveMethod)
+{
+ LARGE_INTEGER li;
+
+ li.QuadPart = distance;
+
+ li.LowPart = SetFilePointer (hf, li.LowPart, &li.HighPart, MoveMethod);
+
+ if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
+ {
+ li.QuadPart = -1;
+ }
+
+ return li.QuadPart;
+}
+
+static int win32_seek(FLAC__IOHandle handle, FLAC__int64 offset, int whence)
+{
+ MetadataReader *reader = (MetadataReader *)handle;
+ if (Seek64(reader->handle, offset, whence) == -1)
+ return -1;
+ else
+ return 0;
+
+}
+
+static FLAC__int64 win32_tell(FLAC__IOHandle handle)
+{
+ MetadataReader *reader = (MetadataReader *)handle;
+ return Seek64(reader->handle, 0, FILE_CURRENT);
+}
+
+static int win32_eof(FLAC__IOHandle handle)
+{
+ MetadataReader *reader = (MetadataReader *)handle;
+ return !!reader->endOfFile;
+}
+
+static int win32_close(FLAC__IOHandle handle)
+{
+ MetadataReader *reader = (MetadataReader *)handle;
+
+ CloseHandle(reader->handle);
+ reader->handle = INVALID_HANDLE_VALUE;
+ return 0;
+}
+
+static FLAC__IOCallbacks unicodeIO =
+{
+ win32_read,
+ win32_write,
+ win32_seek,
+ win32_tell,
+ win32_eof,
+ win32_close,
+};
+
+FLACMetadata::FLACMetadata()
+{
+ chain=FLAC__metadata_chain_new();
+ itr = FLAC__metadata_iterator_new();
+ block=0;
+ streamInfo=0;
+ filesize=0;
+}
+
+FLACMetadata::~FLACMetadata()
+{
+ if (chain)
+ FLAC__metadata_chain_delete(chain);
+ if (itr)
+ FLAC__metadata_iterator_delete(itr);
+}
+
+void FLACMetadata::Reset()
+{
+ if (chain)
+ FLAC__metadata_chain_delete(chain);
+ if (itr)
+ FLAC__metadata_iterator_delete(itr);
+ chain=FLAC__metadata_chain_new();
+ itr = FLAC__metadata_iterator_new();
+ block=0;
+ streamInfo=0;
+ filesize=0;
+}
+
+const FLAC__StreamMetadata_StreamInfo *FLACMetadata::GetStreamInfo()
+{
+ if (streamInfo)
+ return &streamInfo->data.stream_info;
+ else
+ return 0;
+}
+
+bool FLACMetadata::Open(const wchar_t *filename, bool optimize)
+{
+ if (!chain || !itr)
+ return false;
+
+ HANDLE hfile = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
+ MetadataReader reader(hfile);
+ if (reader.handle == INVALID_HANDLE_VALUE)
+ return false;
+ filesize = FileSize64(reader.handle);
+
+ FLAC__bool success = FLAC__metadata_chain_read_with_callbacks(chain, &reader, unicodeIO);
+ CloseHandle(hfile);
+ if (!success)
+ return false;
+
+ if (optimize)
+ {
+ FLAC__metadata_chain_sort_padding(chain);
+ FLAC__metadata_chain_merge_padding(chain);
+ }
+
+ FLAC__metadata_iterator_init(itr, chain);
+ while (1)
+ {
+ FLAC__MetadataType type=FLAC__metadata_iterator_get_block_type(itr);
+ switch (type)
+ {
+ case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+ block = FLAC__metadata_iterator_get_block(itr);
+ break;
+ case FLAC__METADATA_TYPE_STREAMINFO:
+ streamInfo = FLAC__metadata_iterator_get_block(itr);
+ break;
+ }
+ if (FLAC__metadata_iterator_next(itr) == false)
+ break;
+ }
+ return true;
+}
+
+const char *FLACMetadata::GetMetadata(const char *tag)
+{
+ if (!block)
+ return 0;
+
+ int pos = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, tag);
+ if (pos < 0)
+ {
+ // fail
+ }
+ else
+ {
+ const char *entry = (const char *)block->data.vorbis_comment.comments[pos].entry;
+ const char *metadata = strchr(entry, '='); // find the first equal
+ if (metadata)
+ {
+ return metadata+1;
+ }
+ }
+
+ return 0;
+}
+
+void FLACMetadata::SetMetadata(const char *tag, const char *value)
+{
+ if (!block)
+ {
+ FLAC__metadata_iterator_init(itr, chain);
+ do
+ {
+ if (FLAC__METADATA_TYPE_VORBIS_COMMENT == FLAC__metadata_iterator_get_block_type(itr))
+ {
+ block = FLAC__metadata_iterator_get_block(itr);
+ break;
+ }
+ }
+ while (FLAC__metadata_iterator_next(itr) != 0);
+ if (!block)
+ {
+ block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
+ FLAC__metadata_iterator_insert_block_after(itr, block);
+ }
+ }
+
+ if (!block)
+ return;
+
+ FLAC__StreamMetadata_VorbisComment_Entry entry;
+ size_t totalLen = strlen(tag) + 1 /* = */ + strlen(value);
+ entry.entry = (FLAC__byte *)malloc(totalLen + 1);
+ entry.length = totalLen;
+
+ StringCchPrintfA((char *)entry.entry, totalLen+1, "%s=%s", tag, value);
+
+ int pos = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, tag);
+ if (pos < 0)
+ {
+ //new comment
+ FLAC__metadata_object_vorbiscomment_append_comment(block, entry, true);
+ // would love to not copy, but we can't guarantee that FLAC links to the same CRT as us
+ }
+ else
+ {
+ FLAC__metadata_object_vorbiscomment_set_comment(block, pos, entry, true);
+ // would love to not copy, but we can't guarantee that FLAC links to the same CRT as us
+ }
+ free(entry.entry);
+}
+
+void FLACMetadata::RemoveMetadata(const char *tag)
+{
+ if (!block)
+ return;
+
+ FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, tag);
+}
+
+void FLACMetadata::RemoveMetadata(int n)
+{
+ if (!block)
+ return;
+
+ FLAC__metadata_object_vorbiscomment_delete_comment(block, (unsigned int)n);
+}
+
+static FLAC__StreamMetadata *GetOrMakePadding(FLAC__Metadata_Chain *chain, FLAC__Metadata_Iterator *itr)
+{
+ FLAC__metadata_iterator_init(itr, chain);
+ while (1)
+ {
+ if (FLAC__METADATA_TYPE_PADDING == FLAC__metadata_iterator_get_block_type(itr))
+ {
+ FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(itr);
+ return block;
+ }
+
+ if (FLAC__metadata_iterator_next(itr) == false)
+ break;
+ }
+ FLAC__StreamMetadata *padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
+ if (padding)
+ FLAC__metadata_iterator_insert_block_after(itr, padding);
+
+ return padding;
+}
+
+bool FLACMetadata::Save(const wchar_t *filename)
+{
+ if (FLAC__metadata_chain_check_if_tempfile_needed(chain, true))
+ {
+ // since we needed to write a tempfile, let's add some more padding so it doesn't happen again
+ FLAC__metadata_chain_sort_padding(chain);
+
+ FLAC__StreamMetadata *padding = GetOrMakePadding(chain, itr);
+ if (padding && padding->length < 16384)
+ padding->length = 16384; // TODO: configurable padding size
+
+ HANDLE hfile = CreateFileW(filename, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
+ MetadataReader reader(hfile);
+ if (reader.handle == INVALID_HANDLE_VALUE)
+ return false;
+
+ wchar_t tempPath[MAX_PATH-14] = {0}, tempFile[MAX_PATH] = {0};
+ GetTempPathW(MAX_PATH-14, tempPath);
+ GetTempFileNameW(tempPath, L"waf", 0, tempFile);
+
+ HANDLE hTempFile = CreateFileW(tempFile, GENERIC_READ|GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
+ MetadataReader tempReader(hTempFile);
+
+ FLAC__bool res = FLAC__metadata_chain_write_with_callbacks_and_tempfile(chain, false, &reader, unicodeIO, &tempReader, unicodeIO);
+
+ CloseHandle(hfile);
+ CloseHandle(hTempFile);
+ if (!MoveFileW(tempFile, filename))
+ {
+ if (CopyFileW(tempFile, filename, FALSE))
+ {
+ DeleteFileW(tempFile);
+ }
+ else
+ {
+ DeleteFileW(tempFile);
+ return false;
+ }
+ }
+ return !!res;
+ }
+ else
+ {
+ HANDLE hfile = CreateFileW(filename, GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
+ MetadataReader reader(hfile);
+ if (reader.handle == INVALID_HANDLE_VALUE)
+ return false;
+
+ FLAC__bool res = FLAC__metadata_chain_write_with_callbacks(chain, true, &reader, unicodeIO);
+
+ CloseHandle(hfile);
+ return !!res;
+ }
+}
+
+bool FLACMetadata::GetLengthMilliseconds(unsigned __int64 *length)
+{
+ if (!streamInfo)
+ return false;
+ *length = (__int64)((double)(FLAC__int64)streamInfo->data.stream_info.total_samples / (double)streamInfo->data.stream_info.sample_rate * 1000.0 + 0.5);
+ return true;
+}
+
+int FLACMetadata::GetNumMetadataItems()
+{
+ if (block) return block->data.vorbis_comment.num_comments;
+ else return 0;
+}
+
+const char* FLACMetadata::EnumMetadata(int n, char *tag, int taglen)
+{
+ if (tag) tag[0]=0;
+ if (!block) return 0;
+ const char *entry = (const char *)block->data.vorbis_comment.comments[n].entry;
+ const char *metadata = strchr(entry, '='); // find the first equal
+ if (metadata)
+ {
+ if (tag) lstrcpynA(tag,entry,min(metadata-entry+1,taglen));
+ return metadata+1;
+ }
+ else return 0;
+}
+
+void FLACMetadata::SetTag(int pos, const char *tag)
+{
+ char * value = (char*)EnumMetadata(pos,0,0);
+ value = _strdup(value?value:"");
+ FLAC__StreamMetadata_VorbisComment_Entry entry;
+ size_t totalLen = strlen(tag) + 1 /* = */ + strlen(value);
+ entry.entry = (FLAC__byte *)malloc(totalLen + 1);
+ entry.length = totalLen;
+ StringCchPrintfA((char *)entry.entry, totalLen+1, "%s=%s", tag, value);
+ FLAC__metadata_object_vorbiscomment_set_comment(block, pos, entry, true);
+ free(value);
+}
+
+bool FLACMetadata::GetPicture(FLAC__StreamMetadata_Picture_Type type, void **data, size_t *len, wchar_t **mimeType)
+{
+ if (!chain || !itr)
+ return false;
+
+ FLAC__metadata_iterator_init(itr, chain);
+ while (1)
+ {
+ if (FLAC__METADATA_TYPE_PICTURE == FLAC__metadata_iterator_get_block_type(itr))
+ {
+ FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(itr);
+ FLAC__StreamMetadata_Picture &picture = block->data.picture;
+ if (picture.type == type)
+ {
+ *len = picture.data_length;
+ *data = WASABI_API_MEMMGR->sysMalloc(picture.data_length);
+ if (!*data)
+ return false;
+ memcpy(*data, picture.data, picture.data_length);
+
+ char *type = 0;
+ if (picture.mime_type)
+ type = strchr(picture.mime_type, '/');
+
+ if (type && *type)
+ {
+ type++;
+
+ char *type2 = strchr(type, '/');
+ if (type2 && *type2) type2++;
+ else type2 = type;
+
+ int typelen = MultiByteToWideChar(CP_ACP, 0, type2, -1, 0, 0);
+ *mimeType = (wchar_t *)WASABI_API_MEMMGR->sysMalloc(typelen * sizeof(wchar_t));
+ if (*mimeType)
+ MultiByteToWideChar(CP_ACP, 0, type, -1, *mimeType, typelen);
+ }
+ else
+ *mimeType = 0; // unknown!
+
+ return true;
+ }
+ }
+
+ if (FLAC__metadata_iterator_next(itr) == false)
+ break;
+ }
+
+ return false;
+}
+
+bool FLACMetadata::RemovePicture(FLAC__StreamMetadata_Picture_Type type)
+{
+ if (!chain || !itr)
+ return false;
+
+ FLAC__metadata_iterator_init(itr, chain);
+ while (1)
+ {
+ if (FLAC__METADATA_TYPE_PICTURE == FLAC__metadata_iterator_get_block_type(itr))
+ {
+ FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(itr);
+ FLAC__StreamMetadata_Picture &picture = block->data.picture;
+ if (picture.type == type)
+ {
+ FLAC__metadata_iterator_delete_block(itr, false);
+ return true;
+ }
+ }
+
+ if (FLAC__metadata_iterator_next(itr) == false)
+ break;
+ }
+
+ return false;
+}
+
+bool FLACMetadata::SetPicture(FLAC__StreamMetadata_Picture_Type type, void *data, size_t len, const wchar_t *mimeType, int width, int height)
+{
+ if (!chain || !itr)
+ return false;
+
+ FLAC__metadata_iterator_init(itr, chain);
+ while (1)
+ {
+ if (FLAC__METADATA_TYPE_PICTURE == FLAC__metadata_iterator_get_block_type(itr))
+ {
+ FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(itr);
+ FLAC__StreamMetadata_Picture &picture = block->data.picture;
+ if (picture.type == type)
+ {
+ FLAC__metadata_object_picture_set_data(block, (FLAC__byte *)data, len, true);
+ picture.width = width;
+ picture.height = height;
+ picture.depth = 32;
+ picture.colors = 0;
+
+ FLAC__metadata_object_picture_set_description(block, (FLAC__byte *)"", true);// TODO?
+
+ char mime[256] = {0};
+ if (wcsstr(mimeType, L"/") != 0)
+ {
+ StringCchPrintfA(mime, 256, "%S", mimeType);
+ }
+ else
+ {
+ StringCchPrintfA(mime, 256, "image/%S", mimeType);
+ }
+ FLAC__metadata_object_picture_set_mime_type(block, mime, true);
+ return true;
+ }
+ }
+
+ if (FLAC__metadata_iterator_next(itr) == false)
+ break;
+ }
+
+ // not found. let's add it
+ FLAC__StreamMetadata *newBlock = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PICTURE);
+
+ FLAC__metadata_object_picture_set_data(newBlock, (FLAC__byte *)data, len, true);
+ FLAC__StreamMetadata_Picture &picture = newBlock->data.picture;
+ picture.type = type;
+ picture.width = width;
+ picture.height = height;
+ picture.depth = 32;
+ picture.colors = 0;
+
+ FLAC__metadata_object_picture_set_description(newBlock, (FLAC__byte *)"", true);// TODO?
+
+ char mime[256] = {0};
+ StringCchPrintfA(mime, 256, "image/%S", mimeType);
+ FLAC__metadata_object_picture_set_mime_type(newBlock, mime, true);
+
+ FLAC__metadata_iterator_insert_block_after(itr, newBlock);
+ return true;
+}
+
+bool FLACMetadata::GetIndexPicture(int index, FLAC__StreamMetadata_Picture_Type *type, void **data, size_t *len, wchar_t **mimeType)
+{
+ if (!chain || !itr)
+ return false;
+ int i=0;
+ FLAC__metadata_iterator_init(itr, chain);
+ while (1)
+ {
+ if (FLAC__METADATA_TYPE_PICTURE == FLAC__metadata_iterator_get_block_type(itr))
+ {
+ FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(itr);
+ FLAC__StreamMetadata_Picture &picture = block->data.picture;
+ if (i++ == index)
+ {
+ *type = picture.type;
+ *len = picture.data_length;
+ *data = WASABI_API_MEMMGR->sysMalloc(picture.data_length);
+ if (!*data)
+ return false;
+ memcpy(*data, picture.data, picture.data_length);
+
+ char *type = 0;
+ if (picture.mime_type)
+ type = strchr(picture.mime_type, '/');
+
+ if (type && *type)
+ {
+ type++;
+ int typelen = MultiByteToWideChar(CP_ACP, 0, type, -1, 0, 0);
+ *mimeType = (wchar_t *)WASABI_API_MEMMGR->sysMalloc(typelen * sizeof(wchar_t));
+ if (*mimeType)
+ MultiByteToWideChar(CP_ACP, 0, type, -1, *mimeType, typelen);
+ }
+ else
+ *mimeType = 0; // unknown!
+
+ return true;
+ }
+ }
+
+ if (FLAC__metadata_iterator_next(itr) == false)
+ break;
+ }
+
+ return false;
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/Metadata.h b/Src/Plugins/Input/in_flac/Metadata.h
new file mode 100644
index 00000000..90616aeb
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/Metadata.h
@@ -0,0 +1,48 @@
+#ifndef NULLSOFT_IN_FLAC_METADATA_H
+#define NULLSOFT_IN_FLAC_METADATA_H
+
+#include <FLAC/all.h>
+
+class FLACMetadata
+{
+public:
+ FLACMetadata();
+ ~FLACMetadata();
+ bool Open(const wchar_t *filename, bool optimize=false);
+ void Reset();
+ const char *GetMetadata(const char *tag);
+ void SetMetadata(const char *tag, const char *value);
+ void RemoveMetadata(const char *tag);
+ void RemoveMetadata(int n);
+ bool Save(const wchar_t *filename);
+ const FLAC__StreamMetadata_StreamInfo *GetStreamInfo();
+ __int64 GetFileSize() { return filesize; }
+ bool GetLengthMilliseconds(unsigned __int64 *length);
+ int GetNumMetadataItems();
+ const char* EnumMetadata(int n, char *tag, int len);
+ void SetTag(int n, const char *tag);
+
+ bool GetPicture(FLAC__StreamMetadata_Picture_Type type, void **data, size_t *len, wchar_t **mimeType);
+ bool GetIndexPicture(int index, FLAC__StreamMetadata_Picture_Type *type, void **data, size_t *len, wchar_t **mimeType);
+ bool RemovePicture(FLAC__StreamMetadata_Picture_Type type);
+ bool SetPicture(FLAC__StreamMetadata_Picture_Type type, void *data, size_t len, const wchar_t *mimeType, int width, int height);
+private:
+ FLAC__Metadata_Chain *chain;
+ FLAC__Metadata_Iterator *itr;
+ FLAC__StreamMetadata *block;
+ FLAC__StreamMetadata *streamInfo;
+ __int64 filesize;
+};
+
+class Info
+{
+public:
+ FLACMetadata metadata;
+ const wchar_t *filename;
+};
+
+extern FLACMetadata *getMetadata;
+extern wchar_t *getFileInfoFn;
+extern Info *info;
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/Preferences.cpp b/Src/Plugins/Input/in_flac/Preferences.cpp
new file mode 100644
index 00000000..95500b1c
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/Preferences.cpp
@@ -0,0 +1,110 @@
+/*
+** Copyright (C) 2007-2011 Nullsoft, Inc.
+**
+** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
+** liable for any damages arising from the use of this software.
+**
+** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
+** alter it and redistribute it freely, subject to the following restrictions:
+**
+** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
+** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+**
+** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+**
+** 3. This notice may not be removed or altered from any source distribution.
+**
+** Author: Ben Allison benski@winamp.com
+** Created: March 1, 2007
+**
+*/
+#include "main.h"
+#include <FLAC/all.h>
+#include "resource.h"
+#include "../Agave/Language/api_language.h"
+#include "../nu/AutoChar.h"
+#include <assert.h>
+#include <strsafe.h>
+
+bool fixBitrate=false;
+bool config_average_bitrate=true;
+
+// the return pointer has been malloc'd. Use free() when you are done.
+char *BuildExtensions(const char *extensions)
+{
+ char name[64] = {0};
+ WASABI_API_LNGSTRING_BUF(IDS_FLAC_FILES,name,64);
+ size_t length = strlen(extensions) + 1 + strlen(name) + 2;
+ char *newExt = (char *)malloc(length);
+ char *ret = newExt; // save because we modify newExt
+
+ // copy extensions
+ StringCchCopyExA(newExt, length, extensions, &newExt, &length, 0);
+ newExt++;
+ length--;
+
+ // copy description
+ StringCchCopyExA(newExt, length, name, &newExt, &length, 0);
+ newExt++;
+ length--;
+
+ // double null terminate
+ assert(length == 1);
+ *newExt = 0;
+
+ return ret;
+}
+
+static INT_PTR CALLBACK PreferencesProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ wchar_t config_extensions[128] = {0};
+ GetPrivateProfileStringW(L"in_flac", L"extensions", DEFAULT_EXTENSIONSW, config_extensions, 128, winampINI);
+ SetDlgItemTextW(hwndDlg, IDC_EXTENSIONS, config_extensions);
+ CheckDlgButton(hwndDlg, IDC_AVERAGE_BITRATE, config_average_bitrate?BST_CHECKED:BST_UNCHECKED);
+ }
+ return TRUE;
+
+ case WM_DESTROY:
+ break;
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ wchar_t config_extensions[128] = {0};
+ GetDlgItemTextW(hwndDlg, IDC_EXTENSIONS, config_extensions, 128);
+ if (!lstrcmpiW(config_extensions, DEFAULT_EXTENSIONSW))
+ WritePrivateProfileStringW(L"in_flac", L"extensions", 0, winampINI);
+ else
+ WritePrivateProfileStringW(L"in_flac", L"extensions", config_extensions, winampINI);
+
+ plugin.FileExtensions = BuildExtensions(AutoChar(config_extensions));
+ config_average_bitrate = !!IsDlgButtonChecked(hwndDlg, IDC_AVERAGE_BITRATE);
+ if (config_average_bitrate)
+ WritePrivateProfileStringW(L"in_flac", L"average_bitrate", L"1", winampINI);
+ else
+ WritePrivateProfileStringW(L"in_flac", L"average_bitrate", L"0", winampINI);
+
+ fixBitrate=true;
+ EndDialog(hwndDlg, 0);
+ }
+ break;
+
+ case IDCANCEL:
+ EndDialog(hwndDlg, 0);
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+void Config(HWND hwndParent)
+{
+ WASABI_API_DIALOGBOXW(IDD_PREFERENCES, hwndParent, PreferencesProc);
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/QuickBuf.h b/Src/Plugins/Input/in_flac/QuickBuf.h
new file mode 100644
index 00000000..c615613b
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/QuickBuf.h
@@ -0,0 +1,50 @@
+#ifndef NULLSOFT_IN_FLAC_QUICKBUF_H
+#define NULLSOFT_IN_FLAC_QUICKBUF_H
+
+#include <malloc.h>
+class QuickBuf
+{
+public:
+ QuickBuf() : buffer(0), len(0)
+ {
+ }
+
+ void Reserve(size_t res)
+ {
+ if (res > len)
+ {
+ len=res;
+ free(buffer);
+ buffer = malloc(len);
+ }
+ }
+
+ void Free()
+ {
+ free(buffer);
+ buffer=0;
+ len=0;
+ }
+
+ void Move(size_t offset)
+ {
+ memmove(buffer, (char *)buffer + offset, len-offset);
+ }
+
+
+ operator void *()
+ {
+ return buffer;
+ }
+
+ operator char *()
+ {
+ return (char *)buffer;
+ }
+
+private:
+ void *buffer;
+ size_t len;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/RawReader.cpp b/Src/Plugins/Input/in_flac/RawReader.cpp
new file mode 100644
index 00000000..9c6814f5
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/RawReader.cpp
@@ -0,0 +1,174 @@
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include "RawReader.h"
+#include "main.h"
+#include <limits.h>
+#include <shlwapi.h>
+#include "../nu/AutoWide.h"
+#include <new>
+#include <strsafe.h>
+#include "nswasabi/ReferenceCounted.h"
+
+static bool IsMyExtension(const wchar_t *filename)
+{
+ const wchar_t *extension = PathFindExtensionW(filename);
+ if (extension && *extension)
+ {
+ wchar_t exts[128] = {0};
+ GetPrivateProfileStringW(L"in_flac", L"extensions", DEFAULT_EXTENSIONSW, exts, 128, winampINI);
+
+ extension++;
+ wchar_t *b = exts;
+ wchar_t *c;
+ do
+ {
+ wchar_t d[20] = {0};
+ StringCchCopyW(d, 15, b);
+ if ((c = wcschr(b, L';')))
+ {
+ if ((c-b)<15)
+ d[c - b] = 0;
+ }
+
+ if (!lstrcmpiW(extension, d))
+ return true;
+
+ b = c + 1;
+ }
+ while (c);
+ }
+ return false;
+}
+
+int RawMediaReaderService::CreateRawMediaReader(const wchar_t *filename, ifc_raw_media_reader **out_reader)
+{
+ if (IsMyExtension(filename))
+ {
+ nx_file_t file;
+ ReferenceCountedNXString filename_nx;
+ ReferenceCountedNXURI filename_uri;
+ NXStringCreateWithUTF16(&filename_nx, filename);
+ NXURICreateWithNXString(&filename_uri, filename_nx);
+
+ int ret = NXFileOpenFile(&file, filename_uri, nx_file_FILE_read_binary);
+ if (ret != NErr_Success)
+ return ret;
+
+ RawMediaReader *reader = new (std::nothrow) RawMediaReader();
+ if (!reader)
+ {
+ NXFileRelease(file);
+ return NErr_OutOfMemory;
+ }
+
+ ret = reader->Initialize(file);
+ NXFileRelease(file);
+ if (ret != NErr_Success)
+ {
+ delete reader;
+ return ret;
+ }
+
+ *out_reader = reader;
+ return NErr_Success;
+
+ }
+ else
+ {
+ return NErr_False;
+ }
+}
+
+#define CBCLASS RawMediaReaderService
+START_DISPATCH;
+CB(CREATERAWMEDIAREADER, CreateRawMediaReader);
+END_DISPATCH;
+#undef CBCLASS
+
+static void OnError(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
+{
+ //client_data=client_data; // dummy line so i can set a breakpoint
+}
+
+static void OnMetadata(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
+{
+
+}
+
+static FLAC__StreamDecoderWriteStatus OnAudio(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data)
+{
+
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+RawMediaReader::RawMediaReader()
+{
+ decoder=0;
+ file=0;
+}
+
+RawMediaReader::~RawMediaReader()
+{
+ if (decoder)
+ FLAC__stream_decoder_delete(decoder);
+ NXFileRelease(file);
+}
+
+int RawMediaReader::Initialize(nx_file_t file)
+{
+ this->file = NXFileRetain(file);
+ decoder = FLAC__stream_decoder_new();
+ if (!decoder)
+ return NErr_FailedCreate;
+
+ FLAC__stream_decoder_set_metadata_ignore(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
+ state.SetFile(file);
+ state.SetObject(this);
+ FLAC__stream_decoder_set_md5_checking(decoder, true);
+ if(FLAC__stream_decoder_init_stream(
+ decoder,
+ FLAC_NXFile_Read,
+ FLAC_NXFile_Seek,
+ FLAC_NXFile_Tell,
+ FLAC_NXFile_Length,
+ FLAC_NXFile_EOF,
+ OnAudio,
+ OnMetadata,
+ OnError,
+ &state
+ ) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
+ {
+ FLAC__stream_decoder_delete(decoder);
+ decoder=0;
+ return NErr_Error;
+ }
+
+ FLAC__stream_decoder_process_until_end_of_metadata(decoder);
+ uint64_t position;
+ FLAC__stream_decoder_get_decode_position(decoder, &position);
+ FLAC__stream_decoder_delete(decoder);
+ decoder=0;
+ NXFileSeek(file, position);
+
+ return NErr_Success;
+}
+
+int RawMediaReader::Read(void *buffer, size_t buffer_size, size_t *bytes_read)
+{
+ return NXFileRead(file, buffer, buffer_size, bytes_read);
+}
+
+size_t RawMediaReader::Release()
+{
+
+ delete this;
+ return 0;
+}
+
+#define CBCLASS RawMediaReader
+START_DISPATCH;
+CB(RELEASE, Release);
+CB(RAW_READ, Read);
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/RawReader.h b/Src/Plugins/Input/in_flac/RawReader.h
new file mode 100644
index 00000000..68348e38
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/RawReader.h
@@ -0,0 +1,36 @@
+#pragma once
+#include "../Agave/DecodeFile/svc_raw_media_reader.h"
+#include "../Agave/DecodeFile/ifc_raw_media_reader.h"
+#include "FLACFileCallbacks.h"
+#include <FLAC/all.h>
+
+// {E906F4DC-3080-4B9B-951F-85950193ACBF}
+static const GUID flac_raw_reader_guid =
+{ 0xe906f4dc, 0x3080, 0x4b9b, { 0x95, 0x1f, 0x85, 0x95, 0x1, 0x93, 0xac, 0xbf } };
+
+
+class RawMediaReaderService : public svc_raw_media_reader
+{
+public:
+ static const char *getServiceName() { return "FLAC Raw Reader"; }
+ static GUID getServiceGuid() { return flac_raw_reader_guid; }
+ int CreateRawMediaReader(const wchar_t *filename, ifc_raw_media_reader **reader);
+protected:
+ RECVS_DISPATCH;
+};
+
+class RawMediaReader : public ifc_raw_media_reader
+{
+public:
+ RawMediaReader();
+ ~RawMediaReader();
+ int Initialize(nx_file_t );
+ int Read(void *buffer, size_t buffer_size, size_t *bytes_read);
+ size_t Release();
+protected:
+ RECVS_DISPATCH;
+private:
+ FLAC__StreamDecoder *decoder;
+ FLACClientData state;
+ nx_file_t file;
+}; \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/Stopper.cpp b/Src/Plugins/Input/in_flac/Stopper.cpp
new file mode 100644
index 00000000..3ff4cad3
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/Stopper.cpp
@@ -0,0 +1,65 @@
+/*
+** Copyright (C) 2008 Nullsoft, Inc.
+**
+** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
+** liable for any damages arising from the use of this software.
+**
+** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
+** alter it and redistribute it freely, subject to the following restrictions:
+**
+** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
+** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+**
+** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+**
+** 3. This notice may not be removed or altered from any source distribution.
+**
+** Author: Ben Allison benski@winamp.com
+** Created: January 31, 2008
+**
+*/
+#include "Stopper.h"
+#include "main.h"
+#include "../Winamp/wa_ipc.h"
+
+Stopper::Stopper() : isplaying(0), timems(0)
+{
+}
+
+void Stopper::ChangeTracking(bool mode)
+{
+ SendMessage(plugin.hMainWindow, WM_USER, mode, IPC_ALLOW_PLAYTRACKING); // enable / disable stats updating
+}
+
+void Stopper::Stop()
+{
+ isplaying = SendMessage(plugin.hMainWindow, WM_USER, 0, IPC_ISPLAYING);
+ if (isplaying)
+ {
+ ChangeTracking(0); // disable stats updating
+ timems = SendMessage(plugin.hMainWindow, WM_USER, 0, IPC_GETOUTPUTTIME);
+ SendMessage(plugin.hMainWindow, WM_COMMAND, 40047, 0); // Stop
+ }
+}
+
+void Stopper::Play()
+{
+ if (isplaying) // this works _most_ of the time, not sure why a small portion of the time it doesnt hrmph :/
+ // ideally we should replace it with a system that pauses the decode thread, closes its file,
+ // does the shit, and reopens and reseeks to the new offset. for gaplessness
+ {
+ if (timems)
+ {
+ m_force_seek = timems; // SendMessage(mod.hMainWindow,WM_USER,timems,106);
+ }
+ else
+ m_force_seek = -1;
+ SendMessage(plugin.hMainWindow, WM_COMMAND, 40045, 0); // Play
+ //m_force_seek = -1;
+ if (isplaying & 2)
+ {
+ SendMessage(plugin.hMainWindow, WM_COMMAND, 40046, 0); // Pause
+ }
+ ChangeTracking(1); // enable stats updating
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/Stopper.h b/Src/Plugins/Input/in_flac/Stopper.h
new file mode 100644
index 00000000..05d5ac6f
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/Stopper.h
@@ -0,0 +1,10 @@
+#pragma once
+class Stopper
+{
+public:
+ Stopper();
+ void ChangeTracking(bool);
+ void Stop();
+ void Play();
+ int isplaying, timems;
+}; \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/StreamFileWin32.cpp b/Src/Plugins/Input/in_flac/StreamFileWin32.cpp
new file mode 100644
index 00000000..f34b0863
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/StreamFileWin32.cpp
@@ -0,0 +1,129 @@
+/*
+** Copyright (C) 2007-2012 Nullsoft, Inc.
+**
+** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
+** liable for any damages arising from the use of this software.
+**
+** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
+** alter it and redistribute it freely, subject to the following restrictions:
+**
+** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
+** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+**
+** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+**
+** 3. This notice may not be removed or altered from any source distribution.
+**
+** Author: Ben Allison benski@winamp.com
+** Created: March 1, 2007
+**
+*/
+#include <windows.h>
+#include <FLAC/all.h>
+#include <assert.h>
+#include "StreamFileWin32.h"
+
+FLAC__StreamDecoderReadStatus Win32_Read(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
+{
+ assert(*bytes <= 4294967295U);
+
+ HANDLE file = ((Win32_State *)client_data)->handle;
+ if(*bytes > 0)
+ {
+ assert(sizeof(FLAC__byte) == 1);
+ DWORD bytesRead=0, bytesToRead=*bytes;
+ BOOL result = ReadFile(file, buffer, bytesToRead, &bytesRead, NULL);
+ *bytes = bytesRead;
+
+ if (!result)
+ return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+ else if(bytesRead == 0)
+ {
+ ((Win32_State *)client_data)->endOfFile = true;
+ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+ }
+ else
+ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+ }
+ else
+ return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+}
+
+__int64 Seek64(HANDLE hf, __int64 distance, DWORD MoveMethod)
+{
+ LARGE_INTEGER li;
+
+ li.QuadPart = distance;
+
+ li.LowPart = SetFilePointer (hf, li.LowPart, &li.HighPart, MoveMethod);
+
+ if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
+ {
+ li.QuadPart = -1;
+ }
+
+ return li.QuadPart;
+}
+
+FLAC__StreamDecoderSeekStatus Win32_Seek(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)
+{
+ HANDLE file = ((Win32_State *)client_data)->handle;
+
+ __int64 result = Seek64(file, absolute_byte_offset, FILE_BEGIN);
+
+ if (result == INVALID_SET_FILE_POINTER)
+ return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
+ else
+ {
+ ((Win32_State *)client_data)->endOfFile = false;
+ return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
+ }
+}
+
+FLAC__StreamDecoderTellStatus Win32_Tell(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
+{
+ HANDLE file = ((Win32_State *)client_data)->handle;
+
+ __int64 position = Seek64(file, 0, FILE_CURRENT);
+
+ if (position == INVALID_SET_FILE_POINTER)
+ return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
+ else
+ {
+ *absolute_byte_offset=position;
+ return FLAC__STREAM_DECODER_TELL_STATUS_OK;
+ }
+}
+__int64 FileSize64(HANDLE file)
+{
+ LARGE_INTEGER position;
+ position.QuadPart=0;
+ position.LowPart = GetFileSize(file, (LPDWORD)&position.HighPart);
+
+ if (position.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
+ return INVALID_FILE_SIZE;
+ else
+ return position.QuadPart;
+}
+
+FLAC__StreamDecoderLengthStatus Win32_Length(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)
+{
+ HANDLE file = ((Win32_State *)client_data)->handle;
+
+ LARGE_INTEGER position;
+ position.QuadPart=0;
+ position.LowPart = GetFileSize(file, (LPDWORD)&position.HighPart);
+
+ if (position.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
+ else
+ {
+ *stream_length = position.QuadPart;
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
+ }
+}
+
+FLAC__bool Win32_EOF(const FLAC__StreamDecoder *decoder, void *client_data)
+{
+ return ((Win32_State *)client_data)->endOfFile;
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/StreamFileWin32.h b/Src/Plugins/Input/in_flac/StreamFileWin32.h
new file mode 100644
index 00000000..e3a98d79
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/StreamFileWin32.h
@@ -0,0 +1,23 @@
+#ifndef NULLSOFT_IN_FLAC_STREAMFILEWIN32_H
+#define NULLSOFT_IN_FLAC_STREAMFILEWIN32_H
+
+#include <FLAC/all.h>
+#include <windows.h>
+
+struct Win32_State
+{
+ void *userData;
+ HANDLE handle;
+ bool endOfFile;
+};
+
+FLAC__StreamDecoderReadStatus Win32_Read(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data);
+FLAC__StreamDecoderSeekStatus Win32_Seek(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data);
+FLAC__StreamDecoderTellStatus Win32_Tell(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
+FLAC__StreamDecoderLengthStatus Win32_Length(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data);
+FLAC__bool Win32_EOF(const FLAC__StreamDecoder *decoder, void *client_data);
+
+// helper function extern'd here because DecodeThread needs it
+__int64 FileSize64(HANDLE file);
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/api__in_flv.h b/Src/Plugins/Input/in_flac/api__in_flv.h
new file mode 100644
index 00000000..f82c0db4
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/api__in_flv.h
@@ -0,0 +1,11 @@
+#ifndef NULLSOFT_IN_FLAC_API_H
+#define NULLSOFT_IN_FLAC_API_H
+
+#include "../Agave/Config/api_config.h"
+extern api_config *AGAVE_API_CONFIG;
+
+#include <api/memmgr/api_memmgr.h>
+extern api_memmgr *memmgr;
+#define WASABI_API_MEMMGR memmgr
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/in_flac.rc b/Src/Plugins/Input/in_flac/in_flac.rc
new file mode 100644
index 00000000..937bcb8b
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/in_flac.rc
@@ -0,0 +1,163 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_INFOCHILD_ADVANCED DIALOGEX 0, 0, 341, 164
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ GROUPBOX "Advanced",IDC_STATIC,0,0,341,164
+ CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,6,13,164,143
+ LTEXT "Name:",IDC_STATIC,175,13,22,8
+ EDITTEXT IDC_NAME,175,23,159,14,ES_AUTOHSCROLL
+ LTEXT "Value:",IDC_STATIC,175,39,21,8
+ EDITTEXT IDC_VALUE,175,49,159,90,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL
+ PUSHBUTTON "Add New",IDC_BUTTON_ADD,176,142,50,13
+ PUSHBUTTON "Delete",IDC_BUTTON_DEL,230,142,50,13
+ PUSHBUTTON "Delete All",IDC_BUTTON_DELALL,284,142,50,13
+END
+
+IDD_PREFERENCES DIALOGEX 0, 0, 175, 84
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Nullsoft FLAC Decoder Preferences"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ GROUPBOX "Extensions",IDC_STATIC,5,4,166,43
+ EDITTEXT IDC_EXTENSIONS,12,16,153,13,ES_AUTOHSCROLL
+ LTEXT "Semi-colon separated (e.g. FLAC;FLA)",IDC_STATIC,12,33,124,8
+ CONTROL "Show average bitrate",IDC_AVERAGE_BITRATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,52,110,10
+ DEFPUSHBUTTON "OK",IDOK,67,67,50,13
+ PUSHBUTTON "Cancel",IDCANCEL,121,67,50,13
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_INFOCHILD_ADVANCED, DIALOG
+ BEGIN
+ RIGHTMARGIN, 340
+ BOTTOMMARGIN, 163
+ END
+
+ IDD_PREFERENCES, DIALOG
+ BEGIN
+ LEFTMARGIN, 5
+ RIGHTMARGIN, 171
+ TOPMARGIN, 4
+ BOTTOMMARGIN, 80
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "#include ""version.rc2""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_FLAC_DECODER_OLD "Nullsoft FLAC Decoder"
+ IDS_TO_ADVANCED_MODE "to advanced mode >>"
+ IDS_TO_SIMPLE_MODE "<< to simple mode"
+ IDS_NAME "Name"
+ IDS_VALUE "Value"
+ IDS_TRACK_GAIN "Track Gain: %s\n"
+ IDS_ALBUM_GAIN "Album Gain: %s\n"
+ IDS_NOT_PRESENT "not present"
+ IDS_LENGTH_IN_SECONDS "Length: %I64d seconds\n"
+ IDS_CHANNELS "Channels: %d\n"
+ IDS_BITS_PER_SAMPLE "Bits per sample: %d\n"
+ IDS_SAMPLE_RATE "Sample Rate: %d Hz\n"
+ IDS_FILE_SIZE_IN_BYTES "File Size: %I64d bytes\n"
+ IDS_AVERAGE_BITRATE "Average bitrate: %I64d kbps\n"
+ IDS_COMPRESSION_RATIO "Compression Ratio: %u%%\n"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_FLAC_DECODER "Nullsoft FLAC Decoder v%s"
+ 65535 "{9475116B-F8C4-4dff-BC19-9601B238557D}"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_LIBFLAC_DLL_MISSING "[libflac.dll missing]"
+ IDS_FLAC_FILES "FLAC Files"
+ IDS_FAMILY_STRING "Free Lossless Audio Codec File"
+ IDS_ABOUT_TEXT "%s\n© 2007-2023 Winamp SA\nWritten by Ben Allison\nBuild date: %hs\n\nUsing FLAC v%hs"
+ IDS_CANNOT_SAVE_METADATA "Cannot save metadata: Error writing data."
+ IDS_ERROR_SAVING_METADATA "Error saving metadata"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc2"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/Plugins/Input/in_flac/in_flac.sln b/Src/Plugins/Input/in_flac/in_flac.sln
new file mode 100644
index 00000000..192ea80b
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/in_flac.sln
@@ -0,0 +1,105 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29613.14
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "in_flac", "in_flac.vcxproj", "{2B754A9B-B449-45F2-93B4-49884A7691B6}"
+ ProjectSection(ProjectDependencies) = postProject
+ {4FC28B55-2A14-43D5-86F7-201054F338A9} = {4FC28B55-2A14-43D5-86F7-201054F338A9}
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915} = {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}
+ {4CEFBC83-C215-11DB-8314-0800200C9A66} = {4CEFBC83-C215-11DB-8314-0800200C9A66}
+ {E105A0A2-7391-47C5-86AC-718003524C3D} = {E105A0A2-7391-47C5-86AC-718003524C3D}
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nx", "..\replicant\nx\nx.vcxproj", "{57C90706-B25D-4ACA-9B33-95CDB2427C27}"
+ ProjectSection(ProjectDependencies) = postProject
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nu", "..\replicant\nu\nu.vcxproj", "{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jnetlib", "..\replicant\jnetlib\jnetlib.vcxproj", "{E105A0A2-7391-47C5-86AC-718003524C3D}"
+ ProjectSection(ProjectDependencies) = postProject
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\replicant\zlib\zlib.vcxproj", "{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libFLAC_dynamic", "..\libFLAC\libFLAC_dynamic.vcxproj", "{4CEFBC83-C215-11DB-8314-0800200C9A66}"
+ ProjectSection(ProjectDependencies) = postProject
+ {4FC28B55-2A14-43D5-86F7-201054F338A9} = {4FC28B55-2A14-43D5-86F7-201054F338A9}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libogg", "..\libogg\libogg.vcxproj", "{4FC28B55-2A14-43D5-86F7-201054F338A9}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2B754A9B-B449-45F2-93B4-49884A7691B6}.Debug|Win32.ActiveCfg = Debug|Win32
+ {2B754A9B-B449-45F2-93B4-49884A7691B6}.Debug|Win32.Build.0 = Debug|Win32
+ {2B754A9B-B449-45F2-93B4-49884A7691B6}.Debug|x64.ActiveCfg = Debug|Win32
+ {2B754A9B-B449-45F2-93B4-49884A7691B6}.Release|Win32.ActiveCfg = Release|Win32
+ {2B754A9B-B449-45F2-93B4-49884A7691B6}.Release|Win32.Build.0 = Release|Win32
+ {2B754A9B-B449-45F2-93B4-49884A7691B6}.Release|x64.ActiveCfg = Release|Win32
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|Win32.ActiveCfg = Debug|Win32
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|Win32.Build.0 = Debug|Win32
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|x64.ActiveCfg = Debug|x64
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|x64.Build.0 = Debug|x64
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|Win32.ActiveCfg = Release|Win32
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|Win32.Build.0 = Release|Win32
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|x64.ActiveCfg = Release|x64
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|x64.Build.0 = Release|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.ActiveCfg = Debug|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.Build.0 = Debug|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.ActiveCfg = Debug|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.Build.0 = Debug|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.ActiveCfg = Release|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.Build.0 = Release|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.ActiveCfg = Release|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.Build.0 = Release|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.Build.0 = Debug|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.ActiveCfg = Debug|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.Build.0 = Debug|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.ActiveCfg = Release|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.Build.0 = Release|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.ActiveCfg = Release|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.Build.0 = Release|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.ActiveCfg = Debug|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.Build.0 = Debug|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.ActiveCfg = Debug|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.Build.0 = Debug|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.ActiveCfg = Release|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.Build.0 = Release|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.ActiveCfg = Release|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.Build.0 = Release|x64
+ {4CEFBC83-C215-11DB-8314-0800200C9A66}.Debug|Win32.ActiveCfg = Debug|Win32
+ {4CEFBC83-C215-11DB-8314-0800200C9A66}.Debug|Win32.Build.0 = Debug|Win32
+ {4CEFBC83-C215-11DB-8314-0800200C9A66}.Debug|x64.ActiveCfg = Debug|x64
+ {4CEFBC83-C215-11DB-8314-0800200C9A66}.Debug|x64.Build.0 = Debug|x64
+ {4CEFBC83-C215-11DB-8314-0800200C9A66}.Release|Win32.ActiveCfg = Release|Win32
+ {4CEFBC83-C215-11DB-8314-0800200C9A66}.Release|Win32.Build.0 = Release|Win32
+ {4CEFBC83-C215-11DB-8314-0800200C9A66}.Release|x64.ActiveCfg = Release|x64
+ {4CEFBC83-C215-11DB-8314-0800200C9A66}.Release|x64.Build.0 = Release|x64
+ {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|Win32.ActiveCfg = Debug|Win32
+ {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|Win32.Build.0 = Debug|Win32
+ {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|x64.ActiveCfg = Debug|x64
+ {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|x64.Build.0 = Debug|x64
+ {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|Win32.ActiveCfg = Release|Win32
+ {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|Win32.Build.0 = Release|Win32
+ {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|x64.ActiveCfg = Release|x64
+ {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {A0F24E75-21BB-4DDF-A287-A481B554B4EB}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/Plugins/Input/in_flac/in_flac.vcxproj b/Src/Plugins/Input/in_flac/in_flac.vcxproj
new file mode 100644
index 00000000..cb9250cc
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/in_flac.vcxproj
@@ -0,0 +1,307 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{2B754A9B-B449-45F2-93B4-49884A7691B6}</ProjectGuid>
+ <RootNamespace>in_flac</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnableManifest>false</VcpkgEnableManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;IN_FLAC2_EXPORTS;UNICODE_INPUT_PLUGIN;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_WARNINGS;REPLICANT_INCLUDE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4244;4995;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>nxlite.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;IN_FLAC2_EXPORTS;UNICODE_INPUT_PLUGIN;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_WARNINGS;REPLICANT_INCLUDE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4244;4267;4995;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>nxlite.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;IN_FLAC2_EXPORTS;UNICODE_INPUT_PLUGIN;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_WARNINGS;REPLICANT_INCLUDE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <PrecompiledHeader />
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4995;4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>nxlite.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;IN_FLAC2_EXPORTS;UNICODE_INPUT_PLUGIN;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_WARNINGS;REPLICANT_INCLUDE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <PrecompiledHeader />
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4244;4267;4995;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>nxlite.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\replicant\nx\nx.vcxproj">
+ <Project>{57c90706-b25d-4aca-9b33-95cdb2427c27}</Project>
+ <CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies>
+ <ReferenceOutputAssembly>true</ReferenceOutputAssembly>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
+ <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\..\nu\SpillBuffer.cpp" />
+ <ClCompile Include="About.cpp" />
+ <ClCompile Include="AlbumArt.cpp" />
+ <ClCompile Include="DecodeThread.cpp" />
+ <ClCompile Include="ExtendedFileInfo.cpp" />
+ <ClCompile Include="ExtendedRead.cpp" />
+ <ClCompile Include="FileInfo.cpp" />
+ <ClCompile Include="FLACFileCallbacks.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="Metadata.cpp" />
+ <ClCompile Include="mkv_flac_decoder.cpp" />
+ <ClCompile Include="Preferences.cpp" />
+ <ClCompile Include="RawReader.cpp" />
+ <ClCompile Include="Stopper.cpp" />
+ <ClCompile Include="StreamFileWin32.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\..\Agave\AlbumArt\svc_albumArtProvider.h" />
+ <ClInclude Include="..\..\..\Agave\api\config\api_config.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\memmgr\api_memmgr.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\service\api_service.h" />
+ <ClInclude Include="..\..\..\Winamp\api_language.h" />
+ <ClInclude Include="..\..\..\Winamp\IN2.H" />
+ <ClInclude Include="..\..\..\Winamp\wa_ipc.h" />
+ <ClInclude Include="AlbumArt.h" />
+ <ClInclude Include="api__in_flv.h" />
+ <ClInclude Include="FLACFileCallbacks.h" />
+ <ClInclude Include="main.h" />
+ <ClInclude Include="Metadata.h" />
+ <ClInclude Include="mkv_flac_decoder.h" />
+ <ClInclude Include="QuickBuf.h" />
+ <ClInclude Include="RawReader.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="Stopper.h" />
+ <ClInclude Include="StreamFileWin32.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="in_flac.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/in_flac.vcxproj.filters b/Src/Plugins/Input/in_flac/in_flac.vcxproj.filters
new file mode 100644
index 00000000..06016ed5
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/in_flac.vcxproj.filters
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="About.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="AlbumArt.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="DecodeThread.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ExtendedFileInfo.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ExtendedRead.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FileInfo.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FLACFileCallbacks.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Metadata.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="mkv_flac_decoder.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Preferences.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="RawReader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Stopper.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="StreamFileWin32.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\SpillBuffer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="StreamFileWin32.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Stopper.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="RawReader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="QuickBuf.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="mkv_flac_decoder.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Metadata.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="FLACFileCallbacks.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="api__in_flv.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="AlbumArt.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Agave\api\config\api_config.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Winamp\api_language.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\memmgr\api_memmgr.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\service\api_service.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Winamp\IN2.H">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Agave\AlbumArt\svc_albumArtProvider.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Winamp\wa_ipc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{be0e665c-0dc5-41ca-98ab-39750a1644f6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{f00291da-d3eb-4848-bb67-7282ea12bb80}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{f3f6b1c6-d21e-44bd-8b2e-10cdfa6c3e18}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="in_flac.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/main.cpp b/Src/Plugins/Input/in_flac/main.cpp
new file mode 100644
index 00000000..047d1529
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/main.cpp
@@ -0,0 +1,277 @@
+/*
+** Copyright © 2007-2014 Winamp SA
+**
+** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
+** liable for any damages arising from the use of this software.
+**
+** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
+** alter it and redistribute it freely, subject to the following restrictions:
+**
+** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
+** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+**
+** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+**
+** 3. This notice may not be removed or altered from any source distribution.
+**
+** Author: Ben Allison benski@winamp.com
+** Created: March 1, 2007
+**
+*/
+#include "main.h"
+#include "api__in_flv.h"
+#include "Metadata.h"
+#include "../Agave/Language/api_language.h"
+#include <api/service/waServiceFactory.h>
+#include "../Winamp/wa_ipc.h"
+#include "../nu/Singleton.h"
+#include "../nu/Autochar.h"
+#include <shlwapi.h>
+#include "resource.h"
+#include "AlbumArt.h"
+#include "RawReader.h"
+#include <strsafe.h>
+#include "nswasabi/ReferenceCounted.h"
+#include "mkv_flac_decoder.h"
+
+api_config *AGAVE_API_CONFIG = 0;
+api_memmgr *WASABI_API_MEMMGR=0;
+AlbumArtFactory albumArtFactory;
+static RawMediaReaderService raw_media_reader_service;
+static SingletonServiceFactory<svc_raw_media_reader, RawMediaReaderService> raw_factory;
+static MKVDecoder mkv_service;
+static SingletonServiceFactory<svc_mkvdecoder, MKVDecoder> mkv_factory;
+// wasabi based services for localisation support
+api_language *WASABI_API_LNG = 0;
+HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
+
+HANDLE killswitch=0;
+HANDLE playThread=0;
+
+const wchar_t *winampINI=0;
+void Config(HWND hwndParent);
+void About(HWND hwndParent);
+wchar_t pluginName[256] = {0};
+
+int Init()
+{
+ if (!IsWindow(plugin.hMainWindow))
+ return IN_INIT_FAILURE;
+
+ killswitch = CreateEvent(0, TRUE, FALSE, 0);
+ plugin.service->service_register(&albumArtFactory);
+ raw_factory.Register(plugin.service, &raw_media_reader_service);
+ mkv_factory.Register(plugin.service, &mkv_service);
+
+ waServiceFactory *sf = (waServiceFactory *)plugin.service->service_getServiceByGuid(AgaveConfigGUID);
+ if (sf) AGAVE_API_CONFIG= (api_config *)sf->getInterface();
+ sf = (waServiceFactory *)plugin.service->service_getServiceByGuid(memMgrApiServiceGuid);
+ if (sf) WASABI_API_MEMMGR= (api_memmgr *)sf->getInterface();
+
+ // loader so that we can get the localisation service api for use
+ sf = plugin.service->service_getServiceByGuid(languageApiGUID);
+ if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
+
+ // need to have this initialised before we try to do anything with localisation features
+ WASABI_API_START_LANG(plugin.hDllInstance,InFlacLangGUID);
+
+ StringCchPrintfW(pluginName,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_FLAC_DECODER),PLUGIN_VER);
+ plugin.description = (char*)pluginName;
+
+ winampINI = (const wchar_t *)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GETINIFILEW);
+
+ wchar_t exts[1024] = {0};
+ GetPrivateProfileStringW(L"in_flac", L"extensions", DEFAULT_EXTENSIONSW, exts, 1024, winampINI);
+ plugin.FileExtensions = BuildExtensions(AutoChar(exts));
+
+ config_average_bitrate = !!GetPrivateProfileIntW(L"in_flac", L"average_bitrate", 1, winampINI);
+
+ plugin.UsesOutputPlug|=8;
+ return IN_INIT_SUCCESS;
+}
+
+void Quit()
+{
+ CloseHandle(killswitch);
+ waServiceFactory *sf = (waServiceFactory *)plugin.service->service_getServiceByGuid(AgaveConfigGUID);
+ if (sf) sf->releaseInterface(AGAVE_API_CONFIG);
+ sf = (waServiceFactory *)plugin.service->service_getServiceByGuid(memMgrApiServiceGuid);
+ if (sf) sf->releaseInterface(WASABI_API_MEMMGR);
+
+ plugin.service->service_deregister(&albumArtFactory);
+ raw_factory.Deregister(plugin.service);
+}
+
+void GetFileInfo(const in_char *file, in_char *title, int *length_in_ms)
+{
+ if (length_in_ms)
+ {
+ if (!file || !*file && currentSongLength != -1000)
+ *length_in_ms = currentSongLength;
+ else
+ {
+ FLACMetadata metadata;
+ unsigned __int64 length_in_msec;
+ if (metadata.Open(file) && metadata.GetLengthMilliseconds(&length_in_msec))
+ *length_in_ms = (int)length_in_msec;
+ else
+ *length_in_ms=-1000;
+ }
+ }
+ if (title) *title=0;
+}
+
+int InfoBox(const in_char *file, HWND hwndParent) { return 0; }
+
+int IsOurFile(const in_char *fn)
+{
+ return 0;
+}
+
+wchar_t *lastfn=0;
+HANDLE threadStarted;
+extern FLAC__uint64 lastoutputtime;
+extern volatile int bufferCount;
+int Play(const in_char *fn)
+{
+ free(lastfn);
+ lastfn=_wcsdup(fn);
+ currentSongLength=-1000;
+ plugin.is_seekable = 0;
+ plugin.SetInfo(0,0,0,0);
+ lastoutputtime=0;
+ bufferCount=0;
+ ResetEvent(killswitch);
+ DWORD threadId;
+ threadStarted = CreateEvent(0, TRUE, FALSE, 0);
+
+ ReferenceCountedNXString filename_nx;
+ nx_uri_t filename_uri;
+ NXStringCreateWithUTF16(&filename_nx, fn);
+ NXURICreateWithNXString(&filename_uri, filename_nx);
+
+ playThread=CreateThread(0, 0, FLACThread, filename_uri, 0, &threadId);
+ SetThreadPriority(playThread, AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
+ WaitForSingleObject(threadStarted, INFINITE);
+ CloseHandle(threadStarted);
+ return 0;
+}
+
+int localPause=0;
+void Pause()
+{
+ localPause=1;
+ QueueUserAPC(APCPause, playThread, (ULONG_PTR)1);
+}
+
+void UnPause()
+{
+ localPause=0;
+ QueueUserAPC(APCPause, playThread, (ULONG_PTR)0);
+}
+
+int IsPaused()
+{
+ return localPause;
+}
+
+void Stop()
+{
+ SetEvent(killswitch);
+ WaitForSingleObject(playThread, INFINITE);
+ currentSongLength=-1000;
+ plugin.outMod->Close();
+ plugin.SAVSADeInit();
+ free(lastfn);
+ lastfn=0;
+}
+
+int GetLength()
+{
+ return currentSongLength;
+}
+
+
+int GetOutputTime()
+{
+ if (bufferCount)
+ return bufferCount;
+
+ if (plugin.outMod)
+ {
+ return (int)lastoutputtime + (plugin.outMod->GetOutputTime() - plugin.outMod->GetWrittenTime());
+ }
+ else
+ return 0;
+}
+
+void SetOutputTime(int time_in_ms)
+{
+ lastoutputtime=time_in_ms; // cheating a bit here :)
+ QueueUserAPC(APCSeek, playThread, (ULONG_PTR)time_in_ms);
+}
+
+int pan = 0, volume = -666;
+void SetVolume(int _volume)
+{
+ volume = _volume;
+ if (plugin.outMod)
+ plugin.outMod->SetVolume(volume);
+}
+
+void SetPan(int _pan)
+{
+ pan = _pan;
+ if (plugin.outMod)
+ plugin.outMod->SetPan(pan);
+}
+
+void EQSet(int on, char data[10], int preamp)
+{}
+
+In_Module plugin =
+{
+ IN_VER_RET,
+ "nullsoft(in_flac.dll)",
+ 0,
+ 0,
+ "FLAC\0FLAC Files\0",
+ 1,
+ 1,
+ Config,
+ About,
+ Init,
+ Quit,
+ GetFileInfo,
+ InfoBox,
+ IsOurFile,
+ Play,
+ Pause,
+ UnPause,
+ IsPaused,
+ Stop,
+ GetLength,
+ GetOutputTime,
+ SetOutputTime,
+ SetVolume,
+ SetPan,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ EQSet,
+ 0,
+ 0
+};
+
+extern "C" __declspec(dllexport) In_Module * winampGetInModule2()
+{
+ return &plugin;
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/main.h b/Src/Plugins/Input/in_flac/main.h
new file mode 100644
index 00000000..bbe316a4
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/main.h
@@ -0,0 +1,40 @@
+#ifndef NULLSOFT_IN_FLAC_MAIN_H
+#define NULLSOFT_IN_FLAC_MAIN_H
+
+#define PLUGIN_VER L"3.2"
+
+#include <windows.h>
+extern HANDLE killswitch;
+
+#include "../Winamp/in2.h"
+extern In_Module plugin;
+
+DWORD CALLBACK FLACThread(LPVOID param);
+extern int pan, volume;
+extern volatile int currentSongLength;
+
+void CALLBACK APCPause(ULONG_PTR data);
+void CALLBACK APCSeek(ULONG_PTR data);
+
+void ResetMetadataCache();
+
+#include <FLAC/all.h>
+void InterleaveAndTruncate(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize, double gain=1.0);
+
+extern const wchar_t *winampINI;
+char *BuildExtensions(const char *extensions);
+extern bool config_average_bitrate;
+extern bool fixBitrate;
+
+extern int m_force_seek; // set this to something other than -1 to make the file start from the given time (in ms)
+extern wchar_t *lastfn;
+
+// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}
+static const GUID playbackConfigGroupGUID =
+ {
+ 0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf }
+ };
+
+#define DEFAULT_EXTENSIONS "FLAC"
+#define DEFAULT_EXTENSIONSW L"FLAC"
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/mkv_flac_decoder.cpp b/Src/Plugins/Input/in_flac/mkv_flac_decoder.cpp
new file mode 100644
index 00000000..f7ef28da
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/mkv_flac_decoder.cpp
@@ -0,0 +1,204 @@
+#include"mkv_flac_decoder.h"
+#include "main.h"
+
+static FLAC__StreamDecoderReadStatus Packet_Read(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
+{
+ packet_client_data_t packet = (packet_client_data_t)client_data;
+ size_t to_copy = *bytes;
+ if (to_copy > packet->buffer_length) {
+ to_copy = packet->buffer_length;
+ }
+ memcpy(buffer, packet->buffer, to_copy);
+ *bytes = to_copy;
+ packet->buffer += to_copy;
+ packet->buffer_length -= to_copy;
+ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+}
+
+static FLAC__StreamDecoderSeekStatus Packet_Seek(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)
+{
+ return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
+}
+
+static FLAC__StreamDecoderTellStatus Packet_Tell(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
+{
+ return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
+}
+
+static FLAC__StreamDecoderLengthStatus Packet_Length(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)
+{
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
+}
+
+static FLAC__bool Packet_EOF(const FLAC__StreamDecoder *decoder, void *client_data)
+{
+ return 0;
+}
+
+static void OnError(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
+{
+ //client_data=client_data; // dummy line so i can set a breakpoint
+}
+
+static void OnMetadata(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
+{
+ packet_client_data_t packet = (packet_client_data_t)client_data;
+ switch(metadata->type)
+ {
+ case FLAC__METADATA_TYPE_STREAMINFO:
+ {
+ packet->frame_size = metadata->data.stream_info.max_blocksize;
+ packet->bps=metadata->data.stream_info.bits_per_sample;
+ packet->bytes_per_sample = (packet->bps + 7) / 8;
+ packet->channels=metadata->data.stream_info.channels;
+ packet->sample_rate=metadata->data.stream_info.sample_rate;
+ packet->samples=metadata->data.stream_info.total_samples;
+ }
+ break;
+ }
+}
+
+static FLAC__StreamDecoderWriteStatus OnAudio(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data)
+{
+ packet_client_data_t packet = (packet_client_data_t)client_data;
+
+ size_t byteLength = packet->bytes_per_sample * packet->channels * frame->header.blocksize;
+ if (byteLength > packet->outputBufferBytes[0]) {
+ FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+ }
+ InterleaveAndTruncate(buffer, packet->outputBuffer, packet->bytes_per_sample * 8, packet->channels, frame->header.blocksize);
+ packet->outputBufferBytes[0] = byteLength;
+
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+
+MKVFLACDecoder *MKVFLACDecoder::Create(const nsmkv::TrackEntryData *track_entry_data, const nsmkv::AudioData *audio_data, unsigned int preferred_bits, unsigned int max_channels)
+{
+ FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new();
+ if (!decoder) {
+ return 0;
+ }
+ packet_client_data_t packet = new packet_client_data_s;
+ packet->buffer = 0;
+ packet->buffer_length = 0;
+
+ if(FLAC__stream_decoder_init_stream(
+ decoder,
+ Packet_Read,
+ Packet_Seek,
+ Packet_Tell,
+ Packet_Length,
+ Packet_EOF,
+ OnAudio,
+ OnMetadata,
+ OnError,
+ packet
+ ) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
+ {
+ delete packet;
+ FLAC__stream_decoder_delete(decoder);
+ return 0;
+ }
+
+ packet->buffer = (const uint8_t *)track_entry_data->codec_private;
+ packet->buffer_length = track_entry_data->codec_private_len;
+ if (!FLAC__stream_decoder_process_until_end_of_metadata(decoder)) {
+ delete packet;
+ FLAC__stream_decoder_delete(decoder);
+ return 0;
+ }
+
+ MKVFLACDecoder *mkv_decoder = new MKVFLACDecoder(decoder, packet, preferred_bits);
+ if (!mkv_decoder) {
+ delete packet;
+ FLAC__stream_decoder_delete(decoder);
+ return 0;
+ }
+ return mkv_decoder;
+}
+
+MKVFLACDecoder::MKVFLACDecoder(FLAC__StreamDecoder *decoder, packet_client_data_t packet, unsigned int bps)
+: decoder(decoder), packet(packet), bps(bps)
+{
+}
+
+MKVFLACDecoder::~MKVFLACDecoder()
+{
+ delete packet;
+ FLAC__stream_decoder_delete(decoder);
+}
+
+int MKVFLACDecoder::OutputFrameSize(size_t *frame_size)
+{
+ *frame_size = packet->frame_size * packet->bytes_per_sample * packet->channels;
+ return MKV_SUCCESS;
+}
+
+int MKVFLACDecoder::GetOutputProperties(unsigned int *sampleRate, unsigned int *channels, unsigned int *bitsPerSample, bool *isFloat)
+{
+ *sampleRate = packet->sample_rate;
+ *channels = packet->channels;
+ *bitsPerSample = packet->bps;
+ *isFloat = false;
+
+ return MKV_SUCCESS;
+}
+
+int MKVFLACDecoder::DecodeBlock(void *inputBuffer, size_t inputBufferBytes, void *outputBuffer, size_t *outputBufferBytes)
+{
+ packet->buffer = (const uint8_t *)inputBuffer;
+ packet->buffer_length = inputBufferBytes;
+
+ packet->outputBuffer = outputBuffer;
+ packet->outputBufferBytes = outputBufferBytes;
+
+ if (FLAC__stream_decoder_process_single(decoder) == 0) {
+ return MKV_FAILURE;
+ }
+
+ return MKV_SUCCESS;
+}
+
+void MKVFLACDecoder::Flush()
+{
+ FLAC__stream_decoder_flush(decoder);
+}
+
+void MKVFLACDecoder::Close()
+{
+ delete this;
+}
+
+#define CBCLASS MKVFLACDecoder
+START_DISPATCH;
+CB(OUTPUT_FRAME_SIZE, OutputFrameSize)
+CB(GET_OUTPUT_PROPERTIES, GetOutputProperties)
+CB(DECODE_BLOCK, DecodeBlock)
+VCB(FLUSH, Flush)
+VCB(CLOSE, Close)
+END_DISPATCH;
+#undef CBCLASS
+
+
+int MKVDecoder::CreateAudioDecoder(const char *codec_id, const nsmkv::TrackEntryData *track_entry_data, const nsmkv::AudioData *audio_data, unsigned int preferred_bits, unsigned int max_channels,bool floating_point, ifc_mkvaudiodecoder **decoder)
+{
+ if (!strcmp(codec_id, "A_FLAC"))
+ {
+ MKVFLACDecoder *flac_decoder = MKVFLACDecoder::Create(track_entry_data, audio_data, preferred_bits, max_channels);
+ if (flac_decoder)
+ {
+ *decoder = flac_decoder;
+ return CREATEDECODER_SUCCESS;
+ }
+ return CREATEDECODER_FAILURE;
+ }
+
+ return CREATEDECODER_NOT_MINE;
+}
+
+#define CBCLASS MKVDecoder
+START_DISPATCH;
+CB(CREATE_AUDIO_DECODER, CreateAudioDecoder)
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/mkv_flac_decoder.h b/Src/Plugins/Input/in_flac/mkv_flac_decoder.h
new file mode 100644
index 00000000..b4392d11
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/mkv_flac_decoder.h
@@ -0,0 +1,65 @@
+#pragma once
+#include "../in_mkv/svc_mkvdecoder.h"
+#include "../in_mkv/ifc_mkvaudiodecoder.h"
+#include <FLAC/all.h>
+
+// {F6AF0AD9-608F-4206-892F-765412574A7D}
+static const GUID FLACMKVGUID =
+{ 0xf6af0ad9, 0x608f, 0x4206, { 0x89, 0x2f, 0x76, 0x54, 0x12, 0x57, 0x4a, 0x7d } };
+
+
+
+class packet_client_data_s
+{
+public:
+ const uint8_t *buffer;
+ size_t buffer_length;
+
+ void *outputBuffer;
+ size_t *outputBufferBytes;
+
+ uint32_t frame_size;
+ uint32_t bps;
+ uint32_t bytes_per_sample;
+ uint32_t channels;
+ uint32_t sample_rate;
+ uint64_t samples;
+};
+
+typedef packet_client_data_s *packet_client_data_t;
+
+class MKVDecoder : public svc_mkvdecoder
+{
+public:
+ static const char *getServiceName() { return "FLAC MKV Decoder"; }
+ static GUID getServiceGuid() { return FLACMKVGUID; }
+ int CreateAudioDecoder(const char *codec_id, const nsmkv::TrackEntryData *track_entry_data, const nsmkv::AudioData *audio_data, unsigned int preferred_bits, unsigned int preferred_channels, bool floating_point, ifc_mkvaudiodecoder **decoder);
+protected:
+ RECVS_DISPATCH;
+};
+
+class MKVFLACDecoder : public ifc_mkvaudiodecoder
+{
+public:
+ static MKVFLACDecoder *Create(const nsmkv::TrackEntryData *track_entry_data, const nsmkv::AudioData *audio_data, unsigned int preferred_bits, unsigned int max_channels);
+
+protected:
+ RECVS_DISPATCH;
+private:
+ /* ifc_mkvaudiodecoder implementation */
+ int OutputFrameSize(size_t *frame_size);
+ int GetOutputProperties(unsigned int *sampleRate, unsigned int *channels, unsigned int *bitsPerSample, bool *isFloat);
+ int DecodeBlock(void *inputBuffer, size_t inputBufferBytes, void *outputBuffer, size_t *outputBufferBytes);
+ void Flush();
+ void Close();
+
+private:
+ MKVFLACDecoder(FLAC__StreamDecoder *decoder, packet_client_data_t packet, unsigned int bps);
+ ~MKVFLACDecoder();
+ /* internal implementation */
+
+ /* data */
+ FLAC__StreamDecoder *decoder;
+ unsigned int bps;
+packet_client_data_t packet;
+}; \ No newline at end of file
diff --git a/Src/Plugins/Input/in_flac/resource.h b/Src/Plugins/Input/in_flac/resource.h
new file mode 100644
index 00000000..341dd0ab
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/resource.h
@@ -0,0 +1,71 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by in_flac.rc
+//
+#define IDS_NULLSOFT_FLAC_DECODER_OLD 0
+#define IDS_TO_ADVANCED_MODE 1
+#define IDS_TO_SIMPLE_MODE 2
+#define IDS_NAME 3
+#define IDS_VALUE 4
+#define IDS_TRACK_GAIN 5
+#define IDS_ALBUM_GAIN 6
+#define IDS_NOT_PRESENT 7
+#define IDS_LENGTH_IN_SECONDS 8
+#define IDS_CHANNELS 9
+#define IDS_BITS_PER_SAMPLE 10
+#define IDS_SAMPLE_RATE 11
+#define IDS_FILE_SIZE_IN_BYTES 12
+#define IDS_AVERAGE_BITRATE 13
+#define IDS_COMPRESSION_RATIO 14
+#define IDS_ABOUT_STR 15
+#define IDS_LIBFLAC_DLL_MISSING 16
+#define IDS_FLAC_FILES 17
+#define IDS_FAMILY_STRING 18
+#define IDS_ABOUT_TEXT 19
+#define IDS_CANNOT_SAVE_METADATA 20
+#define IDS_ERROR_SAVING_METADATA 21
+#define IDD_PREFERENCES 102
+#define IDD_INFOCHILD_ADVANCED 105
+#define IDC_FILENAME 1001
+#define IDC_TITLE 1002
+#define IDC_ARTIST 1003
+#define IDC_YEAR 1004
+#define IDC_ALBUM 1005
+#define IDC_GENRE 1006
+#define IDC_COMMENTS 1007
+#define IDC_FILEINFO 1008
+#define IDC_ALBUMARTIST 1009
+#define IDC_UPDATE 1010
+#define IDC_COMPOSER 1011
+#define IDC_EDIT1 1012
+#define IDC_TRACK 1012
+#define IDC_EXTENSIONS 1012
+#define IDC_NAME 1012
+#define IDC_REPLAYGAIN 1013
+#define IDC_DISC 1014
+#define IDC_PUBLISHER 1015
+#define IDC_PUBLISHER2 1017
+#define IDC_TOOL 1017
+#define IDC_CHECK1 1017
+#define IDC_AVERAGE_BITRATE 1017
+#define IDC_COMBO1 1018
+#define IDC_SWITCH 1019
+#define IDC_PLACEHOLDER 1020
+#define IDC_LIST 1022
+#define IDC_VALUE 1024
+#define IDC_BUTTON_ADD 1025
+#define IDC_BUTTON_DEL 1026
+#define IDC_BUTTON5 1028
+#define IDC_BUTTON_DELALL 1028
+#define IDS_NULLSOFT_FLAC_DECODER 65534
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 112
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1029
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/Plugins/Input/in_flac/version.rc2 b/Src/Plugins/Input/in_flac/version.rc2
new file mode 100644
index 00000000..da1d44c9
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../../../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 3,2,0,0
+ PRODUCTVERSION WINAMP_PRODUCTVER
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Winamp SA"
+ VALUE "FileDescription", "Winamp Input Plug-in"
+ VALUE "FileVersion", "3,2,0,0"
+ VALUE "InternalName", "Nullsoft FLAC Decoder"
+ VALUE "LegalCopyright", "Copyright © 2007-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "in_flac.dll"
+ VALUE "ProductName", "Winamp"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END