aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_wmvdrm
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Input/in_wmvdrm')
-rw-r--r--Src/Plugins/Input/in_wmvdrm/ASXLoader.cpp490
-rw-r--r--Src/Plugins/Input/in_wmvdrm/ASXLoader.h29
-rw-r--r--Src/Plugins/Input/in_wmvdrm/AlbumArt.cpp202
-rw-r--r--Src/Plugins/Input/in_wmvdrm/AlbumArt.h39
-rw-r--r--Src/Plugins/Input/in_wmvdrm/AllocLayer.cpp1
-rw-r--r--Src/Plugins/Input/in_wmvdrm/AllocLayer.h80
-rw-r--r--Src/Plugins/Input/in_wmvdrm/AudioFormat.cpp61
-rw-r--r--Src/Plugins/Input/in_wmvdrm/AudioFormat.h45
-rw-r--r--Src/Plugins/Input/in_wmvdrm/AudioLayer.cpp253
-rw-r--r--Src/Plugins/Input/in_wmvdrm/AudioLayer.h51
-rw-r--r--Src/Plugins/Input/in_wmvdrm/AudioThread.cpp129
-rw-r--r--Src/Plugins/Input/in_wmvdrm/AudioThread.h40
-rw-r--r--Src/Plugins/Input/in_wmvdrm/AutoChar.h43
-rw-r--r--Src/Plugins/Input/in_wmvdrm/AutoWide.h41
-rw-r--r--Src/Plugins/Input/in_wmvdrm/BufferLayer.cpp95
-rw-r--r--Src/Plugins/Input/in_wmvdrm/BufferLayer.h26
-rw-r--r--Src/Plugins/Input/in_wmvdrm/BufferPool.h203
-rw-r--r--Src/Plugins/Input/in_wmvdrm/CachedData.h48
-rw-r--r--Src/Plugins/Input/in_wmvdrm/ClockLayer.cpp101
-rw-r--r--Src/Plugins/Input/in_wmvdrm/ClockLayer.h36
-rw-r--r--Src/Plugins/Input/in_wmvdrm/ConfigDialog.cpp398
-rw-r--r--Src/Plugins/Input/in_wmvdrm/ConfigDialog.h4
-rw-r--r--Src/Plugins/Input/in_wmvdrm/DESIGN.txt34
-rw-r--r--Src/Plugins/Input/in_wmvdrm/ExtendedFileInfo.cpp597
-rw-r--r--Src/Plugins/Input/in_wmvdrm/ExtendedRead.cpp240
-rw-r--r--Src/Plugins/Input/in_wmvdrm/ExtendedRead.h30
-rw-r--r--Src/Plugins/Input/in_wmvdrm/FactoryHelper.h24
-rw-r--r--Src/Plugins/Input/in_wmvdrm/FileInfoDialog.cpp831
-rw-r--r--Src/Plugins/Input/in_wmvdrm/FileInfoDialog.h41
-rw-r--r--Src/Plugins/Input/in_wmvdrm/FileTypes.cpp179
-rw-r--r--Src/Plugins/Input/in_wmvdrm/FileTypes.h88
-rw-r--r--Src/Plugins/Input/in_wmvdrm/GainLayer.cpp232
-rw-r--r--Src/Plugins/Input/in_wmvdrm/GainLayer.h32
-rw-r--r--Src/Plugins/Input/in_wmvdrm/Main.h52
-rw-r--r--Src/Plugins/Input/in_wmvdrm/MediaThread.cpp110
-rw-r--r--Src/Plugins/Input/in_wmvdrm/MediaThread.h56
-rw-r--r--Src/Plugins/Input/in_wmvdrm/MetaTag.cpp96
-rw-r--r--Src/Plugins/Input/in_wmvdrm/MetaTag.h42
-rw-r--r--Src/Plugins/Input/in_wmvdrm/MetaTagFactory.cpp67
-rw-r--r--Src/Plugins/Input/in_wmvdrm/MetaTagFactory.h24
-rw-r--r--Src/Plugins/Input/in_wmvdrm/OutputStream.h169
-rw-r--r--Src/Plugins/Input/in_wmvdrm/PlaylistHandler.cpp167
-rw-r--r--Src/Plugins/Input/in_wmvdrm/PlaylistHandler.h28
-rw-r--r--Src/Plugins/Input/in_wmvdrm/RawReader.cpp173
-rw-r--r--Src/Plugins/Input/in_wmvdrm/RawReader.h40
-rw-r--r--Src/Plugins/Input/in_wmvdrm/Remaining.h68
-rw-r--r--Src/Plugins/Input/in_wmvdrm/SeekLayer.cpp376
-rw-r--r--Src/Plugins/Input/in_wmvdrm/SeekLayer.h55
-rw-r--r--Src/Plugins/Input/in_wmvdrm/StatusHook.cpp54
-rw-r--r--Src/Plugins/Input/in_wmvdrm/StatusHook.h7
-rw-r--r--Src/Plugins/Input/in_wmvdrm/TODO.txt8
-rw-r--r--Src/Plugins/Input/in_wmvdrm/TagAlias.cpp64
-rw-r--r--Src/Plugins/Input/in_wmvdrm/TagAlias.h7
-rw-r--r--Src/Plugins/Input/in_wmvdrm/VideoDataConverter.cpp41
-rw-r--r--Src/Plugins/Input/in_wmvdrm/VideoDataConverter.h18
-rw-r--r--Src/Plugins/Input/in_wmvdrm/VideoLayer.cpp300
-rw-r--r--Src/Plugins/Input/in_wmvdrm/VideoLayer.h62
-rw-r--r--Src/Plugins/Input/in_wmvdrm/VideoOutputChildDDraw.cpp80
-rw-r--r--Src/Plugins/Input/in_wmvdrm/VideoOutputChildDDraw.h17
-rw-r--r--Src/Plugins/Input/in_wmvdrm/VideoThread.cpp166
-rw-r--r--Src/Plugins/Input/in_wmvdrm/VideoThread.h37
-rw-r--r--Src/Plugins/Input/in_wmvdrm/WMCallback.cpp294
-rw-r--r--Src/Plugins/Input/in_wmvdrm/WMCallback.h52
-rw-r--r--Src/Plugins/Input/in_wmvdrm/WMDRMModule.cpp649
-rw-r--r--Src/Plugins/Input/in_wmvdrm/WMDRMModule.h100
-rw-r--r--Src/Plugins/Input/in_wmvdrm/WMHandler.cpp181
-rw-r--r--Src/Plugins/Input/in_wmvdrm/WMHandler.h113
-rw-r--r--Src/Plugins/Input/in_wmvdrm/WMInformation.cpp880
-rw-r--r--Src/Plugins/Input/in_wmvdrm/WMInformation.h89
-rw-r--r--Src/Plugins/Input/in_wmvdrm/WMPlaylist.cpp45
-rw-r--r--Src/Plugins/Input/in_wmvdrm/WMPlaylist.h56
-rw-r--r--Src/Plugins/Input/in_wmvdrm/WPLLoader.cpp128
-rw-r--r--Src/Plugins/Input/in_wmvdrm/WPLLoader.h19
-rw-r--r--Src/Plugins/Input/in_wmvdrm/WaitLayer.cpp49
-rw-r--r--Src/Plugins/Input/in_wmvdrm/WaitLayer.h25
-rw-r--r--Src/Plugins/Input/in_wmvdrm/WinampInterface.cpp183
-rw-r--r--Src/Plugins/Input/in_wmvdrm/WinampInterface.h121
-rw-r--r--Src/Plugins/Input/in_wmvdrm/XMLString.cpp45
-rw-r--r--Src/Plugins/Input/in_wmvdrm/XMLString.h29
-rw-r--r--Src/Plugins/Input/in_wmvdrm/api.cpp56
-rw-r--r--Src/Plugins/Input/in_wmvdrm/api.h42
-rw-r--r--Src/Plugins/Input/in_wmvdrm/config.cpp118
-rw-r--r--Src/Plugins/Input/in_wmvdrm/config.h50
-rw-r--r--Src/Plugins/Input/in_wmvdrm/directdraw.cpp23
-rw-r--r--Src/Plugins/Input/in_wmvdrm/directdraw.h8
-rw-r--r--Src/Plugins/Input/in_wmvdrm/factory_Handler.cpp80
-rw-r--r--Src/Plugins/Input/in_wmvdrm/factory_Handler.h30
-rw-r--r--Src/Plugins/Input/in_wmvdrm/in_wm.rc331
-rw-r--r--Src/Plugins/Input/in_wmvdrm/in_wmvdrm.sln30
-rw-r--r--Src/Plugins/Input/in_wmvdrm/in_wmvdrm.vcxproj361
-rw-r--r--Src/Plugins/Input/in_wmvdrm/in_wmvdrm.vcxproj.filters330
-rw-r--r--Src/Plugins/Input/in_wmvdrm/loadini.cpp12
-rw-r--r--Src/Plugins/Input/in_wmvdrm/loadini.h8
-rw-r--r--Src/Plugins/Input/in_wmvdrm/main.cpp136
-rw-r--r--Src/Plugins/Input/in_wmvdrm/output/AudioOut.h38
-rw-r--r--Src/Plugins/Input/in_wmvdrm/output/OutPlugin.cpp70
-rw-r--r--Src/Plugins/Input/in_wmvdrm/output/OutPlugin.h28
-rw-r--r--Src/Plugins/Input/in_wmvdrm/res_wav/resource.h60
-rw-r--r--Src/Plugins/Input/in_wmvdrm/resource.h266
-rw-r--r--Src/Plugins/Input/in_wmvdrm/util.cpp169
-rw-r--r--Src/Plugins/Input/in_wmvdrm/util.h20
-rw-r--r--Src/Plugins/Input/in_wmvdrm/version.rc239
-rw-r--r--Src/Plugins/Input/in_wmvdrm/vid_ddraw.cpp771
-rw-r--r--Src/Plugins/Input/in_wmvdrm/vid_ddraw.h50
-rw-r--r--Src/Plugins/Input/in_wmvdrm/vid_overlay.cpp708
-rw-r--r--Src/Plugins/Input/in_wmvdrm/vid_overlay.h55
-rw-r--r--Src/Plugins/Input/in_wmvdrm/vidutils.cpp130
-rw-r--r--Src/Plugins/Input/in_wmvdrm/vidutils.h24
108 files changed, 14228 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_wmvdrm/ASXLoader.cpp b/Src/Plugins/Input/in_wmvdrm/ASXLoader.cpp
new file mode 100644
index 00000000..485e0f77
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/ASXLoader.cpp
@@ -0,0 +1,490 @@
+#include "main.h"
+#include "api.h"
+#include "ASXLoader.h"
+#include <stdio.h>
+#include "../nu/AutoWide.h"
+#include "../xml/ifc_xmlreadercallback.h"
+#include "../xml/obj_xml.h"
+#include "api.h"
+#include <api/service/waservicefactory.h>
+#include "../../..\Components\wac_network\wac_network_http_receiver_api.h"
+#include "../nu/AutoChar.h"
+#include "../Winamp/strutil.h"
+#include <strsafe.h>
+#include "XMLString.h"
+
+void SetUserAgent(api_httpreceiver *http)
+{
+ char agent[256] = {0};
+ StringCchPrintfA(agent, 256, "User-Agent: %S/%S", WASABI_API_APP->main_getAppName(), WASABI_API_APP->main_getVersionNumString());
+ http->addheader(agent);
+}
+
+class ASXInfo : public ifc_plentryinfo
+{
+public:
+ ASXInfo()
+ {
+ memset( returnTemp, 0, sizeof( returnTemp ) );
+ }
+
+ const wchar_t *GetExtendedInfo( const wchar_t *parameter )
+ {
+ if ( !_wcsicmp( parameter, L"context" ) )
+ {
+ if ( isRadio )
+ return L"radio";
+ }
+ else if ( !_wcsicmp( parameter, L"repeat" ) )
+ {
+ if ( repeat )
+ {
+ StringCchPrintfW( returnTemp, 20, L"%d", repeat );
+
+ return returnTemp;
+ }
+ }
+
+ return 0;
+ }
+
+ bool isRadio = false;
+ int repeat = 0;
+
+protected:
+ RECVS_DISPATCH;
+
+ wchar_t returnTemp[ 20 ];
+};
+
+#define CBCLASS ASXInfo
+START_DISPATCH;
+CB( IFC_PLENTRYINFO_GETEXTENDEDINFO, GetExtendedInfo )
+END_DISPATCH;
+#undef CBCLASS
+
+class ASXXML : public ifc_xmlreadercallback
+{
+public:
+ ASXXML(ifc_playlistloadercallback *_playlist, const wchar_t *_root, obj_xml *_parser) : playlist(_playlist), rootPath(_root), parser(_parser)
+ {
+ }
+
+ void OnFileHelper(ifc_playlistloadercallback *playlist, const wchar_t *filename, const wchar_t *title, int length, ifc_plentryinfo *extraInfo)
+ {
+ if (wcsstr(filename, L"://") || PathIsRootW(filename))
+ {
+ playlist->OnFile(filename, title, length, extraInfo);
+ }
+ else
+ {
+ wchar_t fullPath[MAX_PATH] = {0}, canonicalizedPath[MAX_PATH] = {0};
+ PathCombineW(fullPath, rootPath, filename);
+ PathCanonicalizeW(canonicalizedPath, fullPath);
+ playlist->OnFile(canonicalizedPath, title, length, extraInfo);
+ }
+ }
+
+ void StartTag(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params)
+ {
+ if (!_wcsicmp(xmltag, L"ENTRYREF"))
+ {
+ const wchar_t *url = params->getItemValue(L"HREF");
+
+ const wchar_t *titleHack = params->getItemValue(L"CLIENTBIND");
+ int lengthHack = -1;
+ wchar_t titleBuf[256] = L"";
+
+ if (titleHack)
+ {
+ // get the length out of the parantheses
+ StringCchCopyW(titleBuf, 256, titleHack);
+ wchar_t *end = titleBuf + lstrlenW(titleBuf);
+ while (end && *end && *end != '(' && end != titleBuf)
+ end = CharPrevW(titleBuf, end);
+
+ *end = 0;
+ end++;
+ lengthHack = _wtoi(end);
+ }
+
+ wchar_t filename[FILENAME_SIZE] = {0};
+ if (wcschr(url, L'?'))
+ StringCchPrintfW(filename, FILENAME_SIZE, L"%s&=.asx", url);
+ else
+ StringCchPrintfW(filename, FILENAME_SIZE, L"%s?.asx", url);
+
+ OnFileHelper(playlist, filename, titleBuf, lengthHack*1000, &info);
+
+ }
+ else if (!_wcsicmp(xmlpath, L"ASX\fENTRY\fREF") || !_wcsicmp(xmlpath, L"ASX\fREPEAT\fENTRY\fREF"))
+ {
+ const wchar_t *track = params->getItemValue(L"HREF");
+ wchar_t fullTitle[128] = {0}, fullFilename[FILENAME_SIZE] = {0};
+ // if there is no extension given, we need to add ?.wma or &=.wma to the end of the URL
+ // this could be 2 lines of code if that wasn't the case :(
+ if (track)
+ {
+ const wchar_t *trackTitle = 0;
+ if (title.GetString()[0] && artist.GetString()[0])
+ {
+ StringCchPrintfW(fullTitle, 128, L"%s - %s", artist.GetString(), title.GetString());
+ trackTitle = fullTitle;
+ }
+ if (!_wcsnicmp(track, L"http://", 7))
+ {
+ const wchar_t *end = scanstr_backcW(track, L"/.", 0);
+ if (!end || *end == L'/')
+ {
+ if (wcschr(track, L'?'))
+ StringCchPrintfW(fullFilename, FILENAME_SIZE, L"%s&=.wma", track);
+ else
+ StringCchPrintfW(fullFilename, FILENAME_SIZE, L"%s?.wma", track);
+ track = fullFilename;
+ }
+ }
+
+ OnFileHelper(playlist, track, trackTitle, -1, &info);
+ }
+ }
+ else if (!_wcsicmp(xmltag, L"REPEAT"))
+ {
+ const wchar_t *param;
+ if (param = params->getItemValue(L"count"))
+ {
+ info.repeat = _wtoi(param);
+ }
+ else
+ info.repeat = -1;
+ }
+ else if (!_wcsicmp(xmltag, L"PARAM"))
+ {
+ const wchar_t *param;
+ if (param = params->getItemValue(L"name"))
+ {
+ if (!_wcsicmp(param, L"context"))
+ {
+ const wchar_t * value = params->getItemValue(L"value");
+ if (!_wcsicmp(value, L"station"))
+ info.isRadio = true;
+ }
+ else if (!_wcsicmp(param, L"encoding"))
+ {
+ const wchar_t *value=params->getItemValue(L"value");
+ if (value)
+ parser->xmlreader_setEncoding(value); // I hope we can set it on the fly like this!
+ }
+ }
+ }
+ }
+
+ void EndTag(const wchar_t *xmlpath, const wchar_t *xmltag)
+ {
+ if (!_wcsicmp(xmltag, L"REPEAT"))
+ {
+ info.repeat = 0;
+ }
+ }
+ ifc_playlistloadercallback *playlist;
+ XMLString title, artist;
+ ASXInfo info;
+ const wchar_t *rootPath;
+ obj_xml *parser;
+protected:
+ RECVS_DISPATCH;
+
+};
+
+#define CBCLASS ASXXML
+START_DISPATCH;
+VCB(ONSTARTELEMENT, StartTag)
+VCB(ONENDELEMENT, EndTag)
+END_DISPATCH;
+#undef CBCLASS
+
+
+/*
+TODO:
+
+don't add tracks until all parts of the "ENTRY" tag are processed. There are some ASX playlists where the title comes AFTER the ref's
+
+maybe have separate XML callbacks for metadata (title, author, shit like that) so that logic can be separated from the main ASX logic
+*/
+
+// ASX isn't really XML. That means we need to URL-encode the text so our XML parser doesn't choke
+// if microsoft followed standards, the world would be a better place.
+int ASXLoader::GayASX_to_XML_converter(obj_xml *parser, char *buffer, int len)
+{
+ // benski> I have no idea if ASX is always ASCII, or if it's UTF-8 or what.
+ // but really I can't be bothered with Microsoft's lameness right now, so we'll assume it's local code page for the time being
+ char *start = buffer;
+ int sofar = 0;
+ for (int i = 0;i < len;i++)
+ {
+ if (buffer[i] == '&')
+ {
+ if (sofar)
+ {
+ if (parser->xmlreader_feed(start, sofar) != API_XML_SUCCESS)
+ return API_XML_FAILURE;
+ }
+
+ if (parser->xmlreader_feed("&amp;", 5) != API_XML_SUCCESS) // no null terminator
+ return API_XML_FAILURE;
+ start = &buffer[i + 1];
+ sofar = 0;
+ }
+ else
+ {
+ /**
+ * ok, this might look really weird
+ * but ASX doesn't have case sensitivity
+ * so lots of playlists have things like
+ * <title>This is the title</Title>
+ * and so we have to accomodate
+ * for this nonsense
+ */
+
+ if (inTag && !inQuotes)
+ buffer[i] = toupper(buffer[i]);
+
+ if (buffer[i] == '>')
+ {
+ inTag=false;
+ }
+ else if (buffer[i] == '<')
+ {
+ inTag=true;
+ }
+
+ // dro> only do uppercase handling on parts of the tag not inbetween quotes
+ // (some servers just don't like having the urls case messed with, the swines)
+ if (buffer[i] == '"')
+ {
+ if(!inQuotes)
+ inQuotes=true;
+ else
+ inQuotes=false;
+ }
+
+ sofar++;
+ }
+ }
+ if (sofar && parser->xmlreader_feed(start, sofar) != API_XML_SUCCESS)
+ return API_XML_FAILURE;
+ OutputDebugStringA(buffer);
+ return API_XML_SUCCESS;
+}
+
+
+#define HTTP_BUFFER_SIZE 16384
+int ASXLoader::FeedXMLHTTP(api_httpreceiver *http, obj_xml *parser, bool *noData)
+{
+ char downloadedData[HTTP_BUFFER_SIZE] = {0};
+ int xmlResult = API_XML_SUCCESS;
+ int downloadSize = http->get_bytes(downloadedData, HTTP_BUFFER_SIZE);
+ if (downloadSize)
+ {
+ xmlResult = GayASX_to_XML_converter(parser, downloadedData, downloadSize);
+ *noData=false;
+ }
+ else
+ *noData = true;
+
+ return xmlResult;
+}
+
+void ASXLoader::RunXMLDownload(api_httpreceiver *http, obj_xml *parser)
+{
+int ret;
+ bool noData;
+ do
+ {
+ Sleep(50);
+ ret = http->run();
+ if (FeedXMLHTTP(http, parser, &noData) != API_XML_SUCCESS)
+ return ;
+ }
+ while (ret == HTTPRECEIVER_RUN_OK);
+
+ // finish off the data
+ do
+ {
+ if (FeedXMLHTTP(http, parser, &noData) != API_XML_SUCCESS)
+ return ;
+ } while (!noData);
+
+ parser->xmlreader_feed(0, 0);
+}
+
+int ASXLoader::LoadFile(obj_xml *parser, const wchar_t *filename)
+{
+ HANDLE file = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
+
+ if (file == INVALID_HANDLE_VALUE)
+ return IFC_PLAYLISTLOADER_FAILED;
+
+ while (true)
+ {
+ char data[1024] = {0};
+ DWORD bytesRead = 0;
+ if (ReadFile(file, data, 1024, &bytesRead, NULL) && bytesRead)
+ {
+ if (GayASX_to_XML_converter(parser, data, bytesRead) != API_XML_SUCCESS)
+ {
+ CloseHandle(file);
+ return IFC_PLAYLISTLOADER_FAILED;
+ }
+ }
+ else
+ break;
+ }
+
+ CloseHandle(file);
+ if (parser->xmlreader_feed(0, 0) != API_XML_SUCCESS)
+ return IFC_PLAYLISTLOADER_FAILED;
+
+ return IFC_PLAYLISTLOADER_SUCCESS;
+}
+
+
+int ASXLoader::LoadURL(obj_xml *parser, const wchar_t *url)
+{
+ api_httpreceiver *http = 0;
+ waServiceFactory *sf = plugin.service->service_getServiceByGuid(httpreceiverGUID);
+ if (sf) http = (api_httpreceiver *)sf->getInterface();
+
+ if (!http)
+ return IFC_PLAYLISTLOADER_FAILED;
+ http->AllowCompression();
+ http->open(API_DNS_AUTODNS, HTTP_BUFFER_SIZE, winamp.GetProxy());
+ SetUserAgent(http);
+ http->connect(AutoChar(url));
+ int ret;
+
+ do
+ {
+ Sleep(10);
+ ret = http->run();
+ if (ret == -1) // connection failed
+ break;
+
+ // ---- check our reply code ----
+ int replycode = http->getreplycode();
+ switch (replycode)
+ {
+ case 0:
+ case 100:
+ break;
+ case 200:
+ {
+ RunXMLDownload(http, parser);
+ sf->releaseInterface(http);
+ return IFC_PLAYLISTLOADER_SUCCESS;
+ }
+ break;
+ default:
+ sf->releaseInterface(http);
+ return IFC_PLAYLISTLOADER_FAILED;
+ }
+ }
+ while (ret == HTTPRECEIVER_RUN_OK);
+ //const char *er = http->geterrorstr();
+ sf->releaseInterface(http);
+ return IFC_PLAYLISTLOADER_FAILED;
+}
+
+static int loadasxv2fn(const wchar_t *filename, ifc_playlistloadercallback *playlist)
+{
+ int i=1;
+ wchar_t ref[FILENAME_SIZE] = {0};
+ wchar_t key[100] = {0};
+ while (1)
+ {
+ StringCchPrintfW(key, 100, L"Ref%d", i++);
+ GetPrivateProfileStringW(L"Reference", key, L"?", ref, FILENAME_SIZE, filename);
+ if (!lstrcmpiW(ref, L"?"))
+ break;
+ else
+ {
+ if (!_wcsnicmp(ref, L"http://", 7))
+ {
+ const wchar_t *end = scanstr_backcW(ref, L"/.", 0);
+ if (!end || *end == L'/')
+ {
+ if (wcschr(ref, L'?'))
+ StringCchCatW(ref, FILENAME_SIZE, L"&=.wma");
+ else
+ StringCchCatW(ref, FILENAME_SIZE, L"?.wma");
+ }
+ }
+
+ playlist->OnFile(ref, 0, 0, 0);
+ }
+
+ }
+ return IFC_PLAYLISTLOADER_SUCCESS;
+}
+
+int ASXLoader::Load(const wchar_t *filename, ifc_playlistloadercallback *playlist)
+{
+ obj_xml *parser = 0;
+ waServiceFactory *parserFactory = 0;
+
+ HANDLE quickTest = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
+ if (quickTest != INVALID_HANDLE_VALUE)
+ {
+ char reference[11] = {0};
+ DWORD bytesRead=0;
+ ReadFile(quickTest, reference, 11, &bytesRead, 0);
+ CloseHandle(quickTest);
+ if (bytesRead == 11 && !_strnicmp(reference, "[Reference]", 11))
+ return loadasxv2fn(filename, playlist);
+ }
+
+ parserFactory = plugin.service->service_getServiceByGuid(obj_xmlGUID);
+ if (parserFactory)
+ parser = (obj_xml *)parserFactory->getInterface();
+
+ if (parser)
+ {
+ wchar_t rootPath[MAX_PATH] = {0};
+ const wchar_t *callbackPath = playlist->GetBasePath();
+ if (callbackPath)
+ lstrcpynW(rootPath, callbackPath, MAX_PATH);
+ else
+ {
+ lstrcpynW(rootPath, filename, MAX_PATH);
+ PathRemoveFileSpecW(rootPath);
+ }
+
+ ASXXML asxXml(playlist, rootPath, parser);
+ parser->xmlreader_registerCallback(L"ASX\f*", &asxXml);
+ parser->xmlreader_registerCallback(L"ASX\fENTRY\fTITLE", &asxXml.title);
+ parser->xmlreader_registerCallback(L"ASX\fENTRY\fAUTHOR", &asxXml.artist);
+ parser->xmlreader_open();
+ parser->xmlreader_setEncoding(L"windows-1252");
+
+ int ret;
+ if (wcsstr(filename, L"://"))
+ ret = LoadURL(parser, filename);
+ else
+ ret = LoadFile(parser, filename);
+
+ parser->xmlreader_unregisterCallback(&asxXml);
+ parser->xmlreader_unregisterCallback(&asxXml.title);
+ parser->xmlreader_unregisterCallback(&asxXml.artist);
+ parser->xmlreader_close();
+ parserFactory->releaseInterface(parser);
+ return ret;
+ }
+
+
+ return IFC_PLAYLISTLOADER_FAILED;
+}
+
+#define CBCLASS ASXLoader
+START_DISPATCH;
+CB(IFC_PLAYLISTLOADER_LOAD, Load)
+END_DISPATCH;
+#undef CBCLASS
diff --git a/Src/Plugins/Input/in_wmvdrm/ASXLoader.h b/Src/Plugins/Input/in_wmvdrm/ASXLoader.h
new file mode 100644
index 00000000..c562014b
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/ASXLoader.h
@@ -0,0 +1,29 @@
+#ifndef NULLSOFT_PLAYLIST_ASX_LOADER_H
+#define NULLSOFT_PLAYLIST_ASX_LOADER_H
+
+#include "../playlist/ifc_playlistloader.h"
+#include "../playlist/ifc_playlistloadercallback.h"
+#include <stdio.h>
+
+class obj_xml;
+class api_httpreceiver;
+class ASXLoader : public ifc_playlistloader
+{
+public:
+ ASXLoader() : inTag(false), inQuotes(false) {}
+ int Load(const wchar_t *filename, ifc_playlistloadercallback *playlist);
+
+private:
+ int LoadURL(obj_xml *parser, const wchar_t *url);
+ int LoadFile(obj_xml *parser, const wchar_t *filename);
+ void RunXMLDownload(api_httpreceiver *http, obj_xml *parser);
+ int FeedXMLHTTP(api_httpreceiver *http, obj_xml *parser, bool *noData);
+ int GayASX_to_XML_converter(obj_xml *parser, char *buffer, int len);
+
+
+protected:
+ bool inTag;
+ bool inQuotes;
+ RECVS_DISPATCH;
+};
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/AlbumArt.cpp b/Src/Plugins/Input/in_wmvdrm/AlbumArt.cpp
new file mode 100644
index 00000000..a1dc08d2
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/AlbumArt.cpp
@@ -0,0 +1,202 @@
+#include "main.h"
+#include "../nu/AutoWide.h"
+#include "AlbumArt.h"
+#include "util.h"
+#include <shlwapi.h>
+#include <strsafe.h>
+
+bool ASF_AlbumArtProvider::IsMine(const wchar_t *filename)
+{
+ const wchar_t *ext = PathFindExtension(filename);
+ if (ext && *ext)
+ {
+ ext++;
+ return fileTypes.GetAVType(ext) != -1;
+ }
+ return false;
+}
+
+int ASF_AlbumArtProvider::ProviderType()
+{
+ return ALBUMARTPROVIDER_TYPE_EMBEDDED;
+}
+
+bool NameToAPICType(const wchar_t *name, int &num)
+{
+ if (!name || !*name) // default to cover
+ num=0x3;
+ else if (!_wcsicmp(name, L"fileicon")) // 32x32 pixels 'file icon' (PNG only)
+ num=0x1;
+ else if (!_wcsicmp(name, L"icon")) // Other file icon
+ num=0x2;
+ else if (!_wcsicmp(name, L"cover")) // Cover (front)
+ num=0x3;
+ else if (!_wcsicmp(name, L"back")) // Cover (back)
+ num=0x4;
+ else if (!_wcsicmp(name, L"leaflet")) // Leaflet page
+ num=0x5;
+ else if (!_wcsicmp(name, L"media")) // Media (e.g. lable side of CD)
+ num=0x6;
+ else if (!_wcsicmp(name, L"leadartist")) //Lead artist/lead performer/soloist
+ num=0x7;
+ else if (!_wcsicmp(name, L"artist")) // Artist/performer
+ num=0x8;
+ else if (!_wcsicmp(name, L"conductor")) // Conductor
+ num=0x9;
+ else if (!_wcsicmp(name, L"band")) // Band/Orchestra
+ num=0xA;
+ else if (!_wcsicmp(name, L"composer")) // Composer
+ num=0xB;
+ else if (!_wcsicmp(name, L"lyricist")) // Lyricist/text writer
+ num=0xC;
+ else if (!_wcsicmp(name, L"location")) // Recording Location
+ num=0xD;
+ else if (!_wcsicmp(name, L"recording")) // During recording
+ num=0xE;
+ else if (!_wcsicmp(name, L"performance")) // During performance
+ num=0xF;
+ else if (!_wcsicmp(name, L"preview")) // Movie/video screen capture
+ num=0x10;
+ else if (!_wcsicmp(name, L"fish")) // A bright coloured fish
+ num=0x11;
+ else if (!_wcsicmp(name, L"illustration")) // Illustration
+ num=0x12;
+ else if (!_wcsicmp(name, L"artistlogo")) // Band/artist logotype
+ num=0x13;
+ else if (!_wcsicmp(name, L"publisherlogo")) // Publisher/Studio logotype
+ num=0x14;
+ else
+ return false;
+ return true;
+}
+
+
+int ASF_AlbumArtProvider::GetAlbumArtData(const wchar_t *filename, const wchar_t *type, void **bits, size_t *len, wchar_t **mimeType)
+{
+ int pictype;
+ if (NameToAPICType(type, pictype))
+ {
+ WMInformation wm(filename);
+ if (wm.GetPicture(bits, len, mimeType, pictype))
+ return ALBUMARTPROVIDER_SUCCESS;
+ }
+
+ return ALBUMARTPROVIDER_FAILURE;
+}
+
+int ASF_AlbumArtProvider::SetAlbumArtData(const wchar_t *filename, const wchar_t *type, void *bits, size_t len, const wchar_t *mimeType)
+{
+ int pictype;
+ if (NameToAPICType(type, pictype))
+ {
+ WMInformation wm(filename);
+ if (!wm.MakeWritable(filename))
+ return ALBUMARTPROVIDER_READONLY; // can't write
+
+ if (wm.SetPicture(bits, len, mimeType, pictype))
+ {
+ wm.Flush();
+ return ALBUMARTPROVIDER_SUCCESS;
+ }
+ }
+
+ return ALBUMARTPROVIDER_FAILURE;
+}
+
+int ASF_AlbumArtProvider::DeleteAlbumArt(const wchar_t *filename, const wchar_t *type)
+{
+ int pictype;
+ if (NameToAPICType(type, pictype))
+ {
+ WMInformation wm(filename);
+ if (!wm.MakeWritable(filename))
+ {
+ if (wm.HasPicture(pictype))
+ return ALBUMARTPROVIDER_READONLY; // can't write
+ else
+ return ALBUMARTPROVIDER_FAILURE;
+ }
+
+ if (wm.DeletePicture(pictype))
+ {
+ wm.Flush();
+ return ALBUMARTPROVIDER_SUCCESS;
+ }
+ }
+
+ return ALBUMARTPROVIDER_FAILURE;
+}
+
+#define CBCLASS ASF_AlbumArtProvider
+START_DISPATCH;
+CB(SVC_ALBUMARTPROVIDER_PROVIDERTYPE, ProviderType);
+CB(SVC_ALBUMARTPROVIDER_GETALBUMARTDATA, GetAlbumArtData);
+CB(SVC_ALBUMARTPROVIDER_SETALBUMARTDATA, SetAlbumArtData);
+CB(SVC_ALBUMARTPROVIDER_DELETEALBUMART, DeleteAlbumArt);
+CB(SVC_ALBUMARTPROVIDER_ISMINE, IsMine);
+END_DISPATCH;
+#undef CBCLASS
+
+static ASF_AlbumArtProvider albumArtProvider;
+
+// {B4184902-EE79-4015-B9A4-76209C6153FA}
+static const GUID asf_albumartproviderGUID =
+{ 0xb4184902, 0xee79, 0x4015, { 0xb9, 0xa4, 0x76, 0x20, 0x9c, 0x61, 0x53, 0xfa } };
+
+
+FOURCC AlbumArtFactory::GetServiceType()
+{
+ return svc_albumArtProvider::SERVICETYPE;
+}
+
+const char *AlbumArtFactory::GetServiceName()
+{
+ return "ASF Album Art Provider";
+}
+
+GUID AlbumArtFactory::GetGUID()
+{
+ return asf_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;
+}
+
+#ifdef CBCLASS
+#undef CBCLASS
+#endif
+
+#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;
diff --git a/Src/Plugins/Input/in_wmvdrm/AlbumArt.h b/Src/Plugins/Input/in_wmvdrm/AlbumArt.h
new file mode 100644
index 00000000..129377c3
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/AlbumArt.h
@@ -0,0 +1,39 @@
+#ifndef NULLSOFT_IN_MP3_ALBUMART_H
+#define NULLSOFT_IN_MP3_ALBUMART_H
+
+#include "../Agave/AlbumArt/svc_albumArtProvider.h"
+
+class ASF_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>
+#include <api/service/services.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_wmvdrm/AllocLayer.cpp b/Src/Plugins/Input/in_wmvdrm/AllocLayer.cpp
new file mode 100644
index 00000000..8c9c8efc
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/AllocLayer.cpp
@@ -0,0 +1 @@
+#include "AllocLayer.h" \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/AllocLayer.h b/Src/Plugins/Input/in_wmvdrm/AllocLayer.h
new file mode 100644
index 00000000..ed07a3bd
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/AllocLayer.h
@@ -0,0 +1,80 @@
+#ifndef NULLSOFT_ALLOCLAYERH
+#define NULLSOFT_ALLOCLAYERH
+
+#include "WMHandler.h"
+#include "BufferPool.h"
+#include <cassert>
+class AllocLayer : public WMHandler
+{
+public:
+ AllocLayer(IWMReader *reader)
+ : readerAdvanced(0),
+ listenOutput( -1),
+ maxSize(0)
+
+ {
+ reader->QueryInterface(&readerAdvanced);
+
+ }
+ ~AllocLayer()
+ {
+ if (readerAdvanced)
+ {
+ readerAdvanced->Release();
+ readerAdvanced = 0;
+ }
+ }
+
+ void Listen(long output)
+ {
+ listenOutput = output;
+ if (output != -1)
+ {
+ readerAdvanced->SetAllocateForOutput(listenOutput, TRUE);
+ readerAdvanced->GetMaxOutputSampleSize(listenOutput, &maxSize);
+ assert(maxSize>0);
+ pool.SetAllocSize(maxSize);
+ }
+ }
+
+ void Listen(long output, long numBuffers)
+ {
+ listenOutput = output;
+ if (output != -1)
+ {
+ readerAdvanced->SetAllocateForOutput(listenOutput, TRUE);
+ readerAdvanced->GetMaxOutputSampleSize(listenOutput, &maxSize);
+ assert(maxSize>0);
+ pool.SetAllocSize(maxSize);
+ pool.PreAllocate(numBuffers);
+ pool.limit=numBuffers;
+
+ }
+ }
+
+ void FreeBuffers()
+ {
+ pool.FreeBuffers();
+ }
+
+
+ BufferPool pool;
+private:
+
+ void AllocateOutput(long outputNum, long bufferSize, INSSBuffer *&buffer)
+ {
+ if (outputNum == listenOutput)
+ {
+ assert(maxSize >= bufferSize);
+ buffer = pool.GetBuffer(bufferSize);
+ }
+ else
+ WMHandler::AllocateOutput(outputNum, bufferSize, buffer); // let other handlers have a shot at it first.
+ }
+
+ // WMHandler
+ long listenOutput;
+ DWORD maxSize;
+ IWMReaderAdvanced *readerAdvanced;
+};
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/AudioFormat.cpp b/Src/Plugins/Input/in_wmvdrm/AudioFormat.cpp
new file mode 100644
index 00000000..7af75b24
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/AudioFormat.cpp
@@ -0,0 +1,61 @@
+#include "main.h"
+#include "AudioLayer.h"
+
+unsigned long AudioFormat::AudioSamplesToMilliseconds(unsigned long samples)
+{
+ return MulDiv(samples, 1000, SampleRate());
+}
+
+unsigned long AudioFormat::AudioBytesToMilliseconds(unsigned long bytes)
+{
+ return MulDiv(AudioBytesToSamples(bytes), 1000, SampleRate());
+}
+
+unsigned long AudioFormat::AudioMillisecondsToBytes(DWORD milliseconds)
+{
+ return AudioSamplesToBytes(MulDiv(milliseconds, SampleRate(), 1000));
+}
+
+unsigned long AudioFormat::AudioDurationToBytes(QWORD duration)
+{
+ // TODO: potential integer overflow
+ return AudioSamplesToBytes(MulDiv((int)duration, SampleRate(), 1000*10000));
+}
+
+unsigned long AudioFormat::AudioBytesToSamples(unsigned long bytes)
+{
+ return bytes / waveFormat->Format.nBlockAlign;
+}
+
+unsigned long AudioFormat::AudioSamplesToBytes(unsigned long samples)
+{
+ return samples * waveFormat->Format.nBlockAlign;
+}
+
+long AudioFormat::Channels()
+{
+ return waveFormat->Format.nChannels;
+}
+
+long AudioFormat::ValidBits()
+{
+ if (waveFormat->Format.wFormatTag == WAVE_FORMAT_PCM)
+ {
+ return waveFormat->Format.wBitsPerSample;
+ }
+ if (waveFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
+ {
+ return waveFormat->Samples.wValidBitsPerSample;
+ }
+ return 0;
+}
+
+long AudioFormat::BitSize()
+{
+ return waveFormat->Format.wBitsPerSample;
+}
+
+long AudioFormat::SampleRate()
+{
+ return waveFormat->Format.nSamplesPerSec;
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/AudioFormat.h b/Src/Plugins/Input/in_wmvdrm/AudioFormat.h
new file mode 100644
index 00000000..81aceb39
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/AudioFormat.h
@@ -0,0 +1,45 @@
+#ifndef NULLSOFT_IN_WMVDRM_AUDIOFORMAT_H
+#define NULLSOFT_IN_WMVDRM_AUDIOFORMAT_H
+
+#include <mmreg.h>
+#include <wmsdk.h>
+
+class AudioFormat
+{
+public:
+ AudioFormat() : waveFormat(0)
+ {
+ }
+ ~AudioFormat()
+ {
+ delete [] waveFormat;
+ }
+ unsigned long AudioBytesToSamples(unsigned long bytes);
+ unsigned long AudioSamplesToBytes(unsigned long samples);
+ unsigned long AudioBytesToMilliseconds(unsigned long bytes);
+ unsigned long AudioMillisecondsToBytes(DWORD milliseconds);
+ unsigned long AudioDurationToBytes(QWORD duration);
+ unsigned long AudioSamplesToMilliseconds(unsigned long samples);
+ long Channels();
+ long ValidBits();
+ long BitSize();
+ long SampleRate();
+//protected:
+ void Open(WM_MEDIA_TYPE *mediaType)
+ {
+ delete[] waveFormat;
+ waveFormat = (WAVEFORMATEXTENSIBLE *) new unsigned char[mediaType->cbFormat];
+ memcpy(waveFormat, mediaType->pbFormat, mediaType->cbFormat);
+ }
+
+ void Close()
+ {
+ delete [] waveFormat;
+ waveFormat=0;
+ }
+
+private:
+ WAVEFORMATEXTENSIBLE *waveFormat;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/AudioLayer.cpp b/Src/Plugins/Input/in_wmvdrm/AudioLayer.cpp
new file mode 100644
index 00000000..c02f30d4
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/AudioLayer.cpp
@@ -0,0 +1,253 @@
+#include "Main.h"
+#include "AudioLayer.h"
+#include "VideoLayer.h"
+#include <Mmreg.h>
+#include <cassert>
+#include "util.h"
+#include "config.h"
+#include "AudioThread.h"
+#include "api.h"
+
+
+#pragma warning(disable:4355) // warning C4355: 'this' : used in base member initializer list
+
+AudioLayer::AudioLayer(IWMReader *_reader)
+ : reader(_reader), audioOutputNum( -1),
+ reader2(0), offset(0), new_offset(0),
+ startPosition(0), videoCatchup(0),
+ opened(false), killSwitch(0),
+ audioThread(this), latency(0)
+{
+ reader->AddRef();
+ if (FAILED(reader->QueryInterface(&reader2)))
+ reader2 = 0;
+
+ killSwitch = CreateEvent(NULL, TRUE, FALSE, NULL);
+}
+
+void AudioLayer::Opened()
+{
+
+ ResetEvent(killSwitch);
+ if (AudioLayer::OpenAudio())
+ {
+ ResetEvent(killSwitch);
+
+ BOOL dedicatedThread = config_audio_dedicated_thread ? TRUE : FALSE;
+ reader2->SetOutputSetting(audioOutputNum, g_wszDedicatedDeliveryThread, WMT_TYPE_BOOL, (BYTE *) & dedicatedThread, sizeof(dedicatedThread));
+
+ BOOL outOfOrder = config_audio_outoforder ? TRUE : FALSE;
+ reader2->SetOutputSetting(audioOutputNum, g_wszDeliverOnReceive, WMT_TYPE_BOOL, (BYTE *) & outOfOrder, sizeof(outOfOrder));
+
+ BOOL justInTime = config_lowmemory ? TRUE : FALSE;
+ reader2->SetOutputSetting(audioOutputNum, g_wszJustInTimeDecode, WMT_TYPE_BOOL, (BYTE *) & justInTime, sizeof(justInTime));
+
+ opened = true;
+ offset = ((QWORD)latency) * 10000;
+ new_offset = config_audio_early ? (latency + config_audio_early_pad) : 0;
+ reader2->SetOutputSetting(audioOutputNum, g_wszEarlyDataDelivery, WMT_TYPE_DWORD, (BYTE *) & new_offset , sizeof(new_offset));
+
+ winamp.OpenViz(latency, SampleRate());
+ }
+
+ WMHandler::Opened();
+}
+
+void AudioLayer::Started()
+{
+ ResetEvent(killSwitch);
+ if (opened)
+ {
+ audioThread.Start(&First());
+ }
+
+ WMHandler::Started();
+}
+
+void AudioLayer::Stopped()
+{
+ if (opened)
+ audioThread.Stop();
+ WMHandler::Stopped();
+
+}
+
+WM_MEDIA_TYPE *NewMediaType(IWMOutputMediaProps *props)
+{
+ DWORD mediaTypeSize;
+ props->GetMediaType(0, &mediaTypeSize);
+ WM_MEDIA_TYPE *mediaType = (WM_MEDIA_TYPE *)new unsigned char[mediaTypeSize];
+ props->GetMediaType(mediaType, &mediaTypeSize);
+ return mediaType;
+}
+
+bool AudioLayer::OpenAudio()
+{
+ audioOutputNum = -1;
+ DWORD numOutputs, output, format, numFormats;
+ IWMOutputMediaProps *formatProperties;
+ GUID mediaType;
+ if (FAILED((reader->GetOutputCount(&numOutputs))))
+ return false;
+ for (output = 0;output < numOutputs;output++)
+ {
+ HRESULT hr;
+ DWORD speakerConfig = config_audio_num_channels;
+
+ if (AGAVE_API_CONFIG && AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"mono", false)) // force mono?
+ speakerConfig = DSSPEAKER_MONO;
+ else if (AGAVE_API_CONFIG && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"surround", true)) // is surround disallowed?
+ speakerConfig = DSSPEAKER_STEREO;
+
+ hr = reader2->SetOutputSetting(output, g_wszSpeakerConfig, WMT_TYPE_DWORD, (BYTE *) & speakerConfig, sizeof(speakerConfig));
+ assert(hr == S_OK);
+
+ BOOL discreteChannels = TRUE;
+ hr = reader2->SetOutputSetting(output, g_wszEnableDiscreteOutput, WMT_TYPE_BOOL, (BYTE *) & discreteChannels , sizeof(discreteChannels ));
+ assert(hr == S_OK);
+
+ if (FAILED(reader->GetOutputFormatCount(output, &numFormats)))
+ continue;
+ for (format = 0;format < numFormats;format++)
+ {
+
+ reader->GetOutputFormat(output, format, &formatProperties);
+ formatProperties->GetType(&mediaType);
+ if (mediaType == WMMEDIATYPE_Audio)
+ {
+
+ WM_MEDIA_TYPE *mediaType = NewMediaType(formatProperties);
+ if (mediaType->subtype == WMMEDIASUBTYPE_PCM)
+ {
+ if (AGAVE_API_CONFIG)
+ {
+ unsigned int bits = AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"bits", 16);
+
+ WAVEFORMATEXTENSIBLE *waveFormat = (WAVEFORMATEXTENSIBLE *) mediaType->pbFormat;
+ if (waveFormat->Format.cbSize >= 22)
+ waveFormat->Samples.wValidBitsPerSample=bits;
+ waveFormat->Format.wBitsPerSample=bits;
+ waveFormat->Format.nBlockAlign = (waveFormat->Format.wBitsPerSample / 8) * waveFormat->Format.nChannels;
+ waveFormat->Format.nAvgBytesPerSec=waveFormat->Format.nSamplesPerSec * waveFormat->Format.nBlockAlign;
+ if (FAILED(formatProperties->SetMediaType(mediaType)))
+ {
+ // blah, just use the default settings then
+ delete[] mediaType;
+ mediaType = NewMediaType(formatProperties);
+ }
+ }
+ AudioFormat::Open(mediaType);
+ delete mediaType;
+ bool video = false;
+ First().HasVideo(video);
+
+ // this is needed to prevent an audio glitch on first playback
+ if (out)
+ {
+ extern WMDRM mod;
+ out->SetVolume(mod.GetVolume());
+ out->SetPan(mod.GetPan());
+ }
+
+ latency = out->Open(SampleRate(), Channels(), ValidBits(), (video ? -666 : -1), -1);
+
+ if (latency >= 0)
+ {
+ audioOutputNum = output;
+ reader->SetOutputProps(audioOutputNum, formatProperties);
+ formatProperties->Release();
+ return true;
+ }
+ else
+ {
+ formatProperties->Release();
+ AudioFormat::Close();
+ continue;
+ }
+ }
+
+ delete mediaType;
+ formatProperties->Release();
+ }
+ }
+ }
+ return false;
+}
+
+void AudioLayer::SampleReceived(QWORD &timeStamp, QWORD &duration, unsigned long &outputNum, unsigned long &flags, INSSBuffer *&sample)
+{
+ if (outputNum == audioOutputNum)
+ {
+ if (WaitForSingleObject(killSwitch, 0) == WAIT_OBJECT_0)
+ return ;
+
+ if (videoCatchup)
+ {
+ videoCatchup = videoCatchup / 20000;
+ videoCatchup = min(videoCatchup, offset / 40000);
+ unsigned int num = (unsigned int) (videoCatchup / VIDEO_ACCEPTABLE_JITTER_MS);
+ while (num--)
+ {
+ if (WaitForSingleObject(killSwitch, VIDEO_ACCEPTABLE_JITTER_MS) == WAIT_OBJECT_0)
+ return ;
+
+ }
+
+ videoCatchup = 0;
+ }
+
+ while (!audioThread.AddBuffer(sample, timeStamp, flags, false))
+ {
+ if (WaitForSingleObject(killSwitch, VIDEO_ACCEPTABLE_JITTER_MS) == WAIT_OBJECT_0)
+ break;
+ }
+ }
+ else
+ WMHandler::SampleReceived(timeStamp, duration, outputNum, flags, sample);
+}
+
+void AudioLayer::VideoCatchup(QWORD time)
+{
+ videoCatchup = time;
+ WMHandler::VideoCatchup(time);
+}
+
+void AudioLayer::Closed()
+{
+ if (opened)
+ {
+ out->Close();
+ winamp.CloseViz();
+ }
+ opened = false;
+
+ //AudioFormat::Close();
+ WMHandler::Closed();
+}
+
+AudioLayer::~AudioLayer()
+{
+ audioThread.Kill();
+ if (reader2)
+ reader2->Release();
+ if (reader)
+ reader->Release();
+ CloseHandle(killSwitch);
+}
+
+void AudioLayer::Kill()
+{
+ SetEvent(killSwitch);
+ if (opened)
+ audioThread.SignalStop();
+ WMHandler::Kill();
+
+ if (opened)
+ audioThread.WaitForStop();
+}
+
+void AudioLayer::EndOfFile()
+{
+ if (!opened || audioThread.EndOfFile())
+ WMHandler::EndOfFile();
+}
diff --git a/Src/Plugins/Input/in_wmvdrm/AudioLayer.h b/Src/Plugins/Input/in_wmvdrm/AudioLayer.h
new file mode 100644
index 00000000..2f836530
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/AudioLayer.h
@@ -0,0 +1,51 @@
+#ifndef NULLSOFT_AUDIOLAYERH
+#define NULLSOFT_AUDIOLAYERH
+
+#include "WMHandler.h"
+#include <mmreg.h>
+#include "AudioThread.h"
+#include "AudioFormat.h"
+
+class AudioLayer : public WMHandler, public AudioFormat
+{
+public:
+ AudioLayer(IWMReader *_reader);
+ ~AudioLayer();
+bool IsOpen()
+{
+ return opened;
+}
+ void Kill();
+ bool OpenAudio();
+
+ void StartAudioThread();
+private:
+ // WMHandler events
+
+ void Opened();
+ void SampleReceived(QWORD &timeStamp, QWORD &duration, unsigned long &outputNum, unsigned long &flags, INSSBuffer *&sample);
+ void VideoCatchup(QWORD time);
+ void Closed();
+ void EndOfFile();
+ void Started();
+ void Stopped();
+
+ // other people's data
+ IWMReader *reader;
+
+ // our data
+ QWORD startPosition;
+
+ int audioOutputNum;
+ IWMReaderAdvanced2 *reader2;
+ QWORD offset;
+ DWORD new_offset;
+ QWORD videoCatchup;
+ bool opened;
+ HANDLE killSwitch;
+ int latency;
+
+ AudioThread audioThread;
+};
+
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/AudioThread.cpp b/Src/Plugins/Input/in_wmvdrm/AudioThread.cpp
new file mode 100644
index 00000000..b034c636
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/AudioThread.cpp
@@ -0,0 +1,129 @@
+#include "Main.h"
+#include "AudioThread.h"
+#include "AudioLayer.h"
+#include <assert.h>
+
+extern unsigned long endTime;
+
+DWORD WINAPI AudThread_stub(void *ptr)
+{
+ ((AudioThread *)ptr)->AudThread();
+ return 0;
+}
+
+void AudioThread::Start(WMHandler *_output)
+{
+ assert(_output);
+ output = _output;
+ eof=0;
+ ResetEvent(stopped);
+ QueueUserAPC(MediaThread_StartAPC, thread, reinterpret_cast<ULONG_PTR>(static_cast<MediaThread *>(this)));
+}
+
+AudioThread::AudioThread(AudioLayer *audio) : output(0), audioLayer(audio)
+{
+ DWORD id;
+ thread = CreateThread(NULL, 256*1024, AudThread_stub, (void *)this, NULL, &id);
+ SetThreadPriority(thread, AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
+}
+
+void AudioThread::AudThread()
+{
+ int endbreak=0;
+ while (true)
+ {
+ switch (WaitForSingleObjectEx(killEvent, wait, TRUE))
+ {
+ case WAIT_OBJECT_0:
+ //StopAPC();
+ return;
+
+ case WAIT_TIMEOUT:
+ {
+ if (buffers.empty() || endbreak)
+ {
+ SetEvent(bufferFreed);
+
+ if (eof==1)
+ {
+ eof=2;
+ output->EndOfFile();
+ }
+ endbreak = 0;
+ continue;
+ }
+
+ MediaBuffer *buffer = buffers.front();
+ DWORD length;
+ void *data;
+ buffer->buffer->GetBufferAndLength((BYTE **)&data, &length);
+
+ //if (out->CanWrite() >= length)
+ {
+ QWORD timestamptemp = buffer->timestamp/10000LL;
+ DWORD timestamp = static_cast<DWORD>(timestamptemp);
+
+ if (buffer->flags & WM_SF_DISCONTINUITY)
+ {
+ // fill with silence!
+ int msToFill = timestamp - out->GetWrittenTime(); // TODO: maybe use microsoft's time resolution?
+ if (msToFill > 0 && msToFill < 2000)
+ {
+ int bytes = audioLayer->AudioMillisecondsToBytes(msToFill);
+ __int8 *zeroes = (__int8 *)calloc(bytes, 1);
+ if (zeroes)
+ {
+ output->AudioDataReceived(zeroes, bytes, timestamp);
+ free(zeroes);
+ }
+ else
+ {
+ out->Flush(timestamp);
+ }
+ }
+ else if (msToFill > 0)
+ {
+ out->Flush(timestamp);
+ }
+ }
+
+ output->AudioDataReceived(data, length, timestamp);
+
+ // TODO seen a few crash reports failing around here
+ // might be the cause of the random wma fails
+ // but crash dump doesn't help too much afaict
+ try {
+ buffer->buffer->Release();
+ delete buffer;
+ } catch (...) {}
+
+ //buffers.pop_front();
+ if (buffers.size())
+ {
+ buffers.erase(buffers.begin());
+ }
+
+ unsigned long x = endTime;
+ if (x && timestamp > x)
+ {
+ eof = 1; // reached the end baby....
+ endbreak = 1;
+ }
+ }
+ if (buffers.size() < config_audio_cache_frames)
+ SetEvent(bufferFreed);
+ }
+ continue;
+
+ default:
+ continue;
+ }
+ }
+}
+
+void AudioThread::AddAPC(MediaBuffer *buffer)
+{
+ OrderedInsert(buffer);
+ if (buffers.size() >= config_audio_cache_frames)
+ ResetEvent(bufferFreed);
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/AudioThread.h b/Src/Plugins/Input/in_wmvdrm/AudioThread.h
new file mode 100644
index 00000000..4192d604
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/AudioThread.h
@@ -0,0 +1,40 @@
+#ifndef NULLSOFT_AUDIOTHREADH
+#define NULLSOFT_AUDIOTHREADH
+
+#include "WMHandler.h"
+#include "MediaThread.h"
+#include <wmsdk.h>
+
+class AudioLayer;
+
+class AudioThread : public MediaThread
+{
+public:
+ AudioThread(AudioLayer *audio);
+ void Start(WMHandler *output);
+
+ /* AddBuffers put an audio buffer in the queue
+ it returns true if it was added
+ it returns false if it was NOT added. it is up to YOU (the caller) to sleep for a while and call again
+ */
+ void AudThread();
+ bool EndOfFile()
+ {
+ if (buffers.empty()) // if the buffers are empty, then our thread might never get a chance to signal EOF
+ return true;
+
+ if (eof)
+ return true;
+ eof=1;
+
+ return false;
+ }
+
+private:
+ void AddAPC(MediaBuffer *);
+ int eof;
+ WMHandler *output;
+ AudioLayer *audioLayer;
+
+};
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/AutoChar.h b/Src/Plugins/Input/in_wmvdrm/AutoChar.h
new file mode 100644
index 00000000..46cd840e
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/AutoChar.h
@@ -0,0 +1,43 @@
+#ifndef NULLSOFT_AUTOCHARH
+#define NULLSOFT_AUTOCHARH
+
+class AutoChar
+{
+public:
+ AutoChar(const wchar_t *convert) : allocated(false), narrow(0)
+ {
+ // review maybe CP_UTF8?
+
+ int size = WideCharToMultiByte(CP_ACP, 0, convert, -1, 0, 0, NULL, NULL);
+ if (!size)
+ return;
+
+ narrow = new char[size];
+ allocated=true;
+
+ if (!WideCharToMultiByte(CP_ACP, 0, convert, -1, narrow, size, NULL, NULL))
+ {
+ delete [] narrow;
+ narrow=0;
+ allocated=false;
+ }
+ }
+ ~AutoChar()
+ {
+ if (allocated)
+ {
+ delete [] narrow;
+ narrow=0;
+ allocated=false;
+ }
+ }
+ operator char *()
+ {
+ return narrow;
+ }
+private:
+ bool allocated;
+ char *narrow;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/AutoWide.h b/Src/Plugins/Input/in_wmvdrm/AutoWide.h
new file mode 100644
index 00000000..9248dda6
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/AutoWide.h
@@ -0,0 +1,41 @@
+#ifndef AUTOWIDEH
+#define AUTOWIDEH
+
+class AutoWide
+{
+public:
+ AutoWide(const char *convert) : allocated(false), wide(0)
+ {
+ // review maybe CP_UTF8?
+ int size = MultiByteToWideChar(CP_ACP, 0, convert, -1, 0,0);
+ if (!size)
+ return;
+
+ wide = new unsigned short[size];
+ allocated=true;
+ if (!MultiByteToWideChar(CP_ACP, 0, convert, -1, wide,size))
+ {
+ delete wide;
+ wide=0;
+ allocated=false;
+ }
+ }
+ ~AutoWide()
+ {
+ if (allocated)
+ {
+ delete wide;
+ wide=0;
+ allocated=false;
+ }
+ }
+ operator unsigned short *()
+ {
+ return wide;
+ }
+private:
+ bool allocated;
+ unsigned short *wide;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/BufferLayer.cpp b/Src/Plugins/Input/in_wmvdrm/BufferLayer.cpp
new file mode 100644
index 00000000..d41a7cfd
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/BufferLayer.cpp
@@ -0,0 +1,95 @@
+#include "BufferLayer.h"
+#include "Main.h"
+#include "resource.h"
+#define killEvent events[0]
+#define startEvent events[1]
+
+enum
+{
+ KILL_EVENT = 0,
+ START_EVENT = 1,
+};
+
+DWORD WINAPI BufferLayer::BufThread_stub(void *ptr)
+{
+ ((BufferLayer *)ptr)->BufThread();
+ return 0;
+}
+
+BufferLayer::BufferLayer(IWMReader *reader) : reader2(0), buffering(false)
+{
+ if (FAILED(reader->QueryInterface(&reader2)))
+ reader2 = 0;
+
+ startEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ killEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ DWORD id;
+ thread = CreateThread(NULL, 128*1024, BufThread_stub, (void *)this, NULL, &id);
+}
+
+BufferLayer::~BufferLayer()
+{
+ SetEvent(killEvent);
+ ResetEvent(startEvent);
+ WaitForSingleObject(thread, INFINITE);
+ if (reader2) reader2->Release(); reader2 = 0;
+}
+
+
+void BufferLayer::BufferingStarted()
+{
+ winamp.SetStatus(WASABI_API_LNGSTRINGW(IDS_BUFFERING));
+ buffering=true;
+ SetEvent(startEvent);
+ WMHandler::BufferingStarted();
+}
+
+void BufferLayer::BufferingStopped()
+{
+ winamp.SetStatus(L"");
+ buffering=false;
+ ResetEvent(startEvent);
+ WMHandler::BufferingStopped();
+}
+
+int BufferLayer::Wait()
+{
+ if (WaitForSingleObject(killEvent, 0) == WAIT_OBJECT_0)
+ return KILL_EVENT;
+
+ return WaitForMultipleObjects(2, events, FALSE, INFINITE) - WAIT_OBJECT_0;
+
+}
+
+void BufferLayer::BufThread()
+{
+ do
+ {
+ switch (Wait())
+ {
+ case KILL_EVENT:
+ return ;
+ case START_EVENT:
+ {
+ if (reader2)
+ {
+ DWORD percent;
+ QWORD throwAway;
+ if (SUCCEEDED(reader2->GetBufferProgress(&percent, &throwAway)))
+ winamp.Buffering(percent, WASABI_API_LNGSTRINGW(IDS_BUFFERING));
+
+ }
+ Sleep(10);
+ }
+ continue;
+ }
+ }
+ while (true);
+}
+
+void BufferLayer::OpenFailed()
+{
+ ResetEvent(startEvent);
+ WMHandler::OpenFailed();
+}
+
diff --git a/Src/Plugins/Input/in_wmvdrm/BufferLayer.h b/Src/Plugins/Input/in_wmvdrm/BufferLayer.h
new file mode 100644
index 00000000..fdfccdbc
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/BufferLayer.h
@@ -0,0 +1,26 @@
+#ifndef NULLSOFT_BUFFERLAYERH
+#define NULLSOFT_BUFFERLAYERH
+
+#include "WMHandler.h"
+
+class BufferLayer : public WMHandler
+{
+public:
+ BufferLayer(IWMReader *reader);
+ ~BufferLayer();
+
+protected:
+ void BufferingStarted();
+ void BufferingStopped();
+ void OpenFailed();
+
+private:
+ static DWORD WINAPI BufThread_stub(void *ptr);
+ void BufThread();
+ int Wait();
+ HANDLE events[2];
+ IWMReaderAdvanced2 *reader2;
+ HANDLE thread;
+ volatile bool buffering;
+};
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/BufferPool.h b/Src/Plugins/Input/in_wmvdrm/BufferPool.h
new file mode 100644
index 00000000..b4c1ec2a
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/BufferPool.h
@@ -0,0 +1,203 @@
+#ifndef NULLSOFT_BUFFERPOOLH
+#define NULLSOFT_BUFFERPOOLH
+
+#include <wmsdk.h>
+#include "../nu/AutoLock.h"
+#include <deque>
+#include <iostream>
+#include <cassert>
+using namespace Nullsoft::Utility;
+class Buffer;
+class Pool
+{
+public:
+ virtual void ReturnBuffer(Buffer *buffer) = 0;
+};
+
+class Buffer : public INSSBuffer
+{
+public:
+ Buffer(size_t _size, Pool *_pool)
+ : size(_size),
+ length(0),
+ pool(_pool),
+ refCount(1)
+ {
+ buffer = new unsigned char[size];
+ }
+
+ ~Buffer()
+ {
+ delete[] buffer;
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return ++refCount;
+ }
+
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ assert(refCount > 0);
+
+ if (--refCount == 0)
+ {
+ length = 0;
+ pool->ReturnBuffer(this);
+ }
+ return refCount;
+ }
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject)
+ {
+ if (IID_INSSBuffer == iid)
+ {
+ *ppvObject = static_cast<INSSBuffer *>(this);
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ *ppvObject = 0;
+ return E_NOINTERFACE;
+ }
+ }
+
+ HRESULT STDMETHODCALLTYPE GetBuffer(BYTE **ppdwBuffer)
+ {
+ *ppdwBuffer = (BYTE *)buffer;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetBufferAndLength(BYTE **ppdwBuffer, DWORD *pdwLength)
+ {
+ *ppdwBuffer = (BYTE *)buffer;
+ *pdwLength = length;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetLength(DWORD *pdwLength)
+ {
+ *pdwLength = length;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetMaxLength(DWORD *pdwLength)
+ {
+ *pdwLength = size;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE SetLength(DWORD dwLength)
+ {
+ length = dwLength;
+ return S_OK;
+ }
+
+ bool CanFit(size_t sizeCompare)
+ {
+ return (sizeCompare <= size);
+ }
+ size_t size, length;
+ Pool *pool;
+ int refCount;
+ unsigned char *buffer;
+};
+
+class BufferPool : public Pool
+{
+ typedef std::deque<Buffer *> PoolList;
+public:
+ long limit;
+ BufferPool() : allocSize(0),
+ poolSize(0),
+ limit(0)
+ {}
+
+ ~BufferPool()
+ {
+ FreeBuffers();
+ }
+ void FreeBuffers()
+ {
+
+ AutoLock lock (bufferGuard);
+ while (!pool.empty())
+ {
+ Buffer *buff = pool.front();
+ delete buff;
+ pool.pop_front();
+ poolSize = 0;
+ }
+ }
+ void ReturnBuffer(Buffer *buffer)
+ {
+ AutoLock lock (bufferGuard);
+ pool.push_back(buffer);
+ }
+ Buffer *SearchForBuffer(size_t size)
+ {
+ PoolList::iterator itr;
+ AutoLock lock (bufferGuard);
+ for (itr = pool.begin();itr != pool.end();itr++)
+ {
+ if ((*itr)->CanFit(size))
+ {
+ Buffer *buff = *itr;
+ pool.erase(itr);
+ buff->AddRef();
+ return buff;
+ }
+ }
+ return 0;
+ }
+
+ void PreAllocate(size_t count)
+ {
+ if (!allocSize)
+ return ;
+ for (size_t i = 0;i != count;i++)
+ {
+ AutoLock lock (bufferGuard);
+ pool.push_back( new Buffer(allocSize , this));
+ }
+ }
+ INSSBuffer *GetBuffer(size_t size)
+ {
+ Buffer *buff = SearchForBuffer(size);
+
+ /*
+ while (!buff && poolSize>=limit && limit)
+ {
+ Sleep(1);
+ buff = SearchForBuffer(size);
+ }*/
+
+ if (!buff)
+ {
+ poolSize++;
+ std::cerr << "poolsize = " << poolSize << std::endl;
+ buff = new Buffer(allocSize ? allocSize : size, this);
+ }
+ return buff;
+ }
+
+ void test()
+ {
+ std::cerr << "pool.size() == " << pool.size();
+ std::cerr << " and poolsize == " << poolSize << std::endl;
+ }
+
+ void SetAllocSize(long size)
+ {
+ allocSize = size;
+ }
+
+ PoolList pool;
+ LockGuard bufferGuard;
+ size_t allocSize;
+ size_t poolSize;
+
+};
+
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/CachedData.h b/Src/Plugins/Input/in_wmvdrm/CachedData.h
new file mode 100644
index 00000000..8782ead1
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/CachedData.h
@@ -0,0 +1,48 @@
+#ifndef CACHEDDATAH
+#define CACHEDDATAH
+
+template <class DataType>
+class CachedData
+{
+public:
+ CachedData() : cached(false)
+ {
+ }
+ CachedData(DataType _data) : cached(true), data(_data)
+ {
+ }
+ operator DataType()
+ {
+ return data;
+ }
+
+ bool IsCached()
+ {
+ return cached;
+ }
+ void ClearCache()
+ {
+ cached=false;
+ }
+ DataType *operator &()
+ {
+ if (cached)
+ return &data;
+ else
+ return 0;
+ }
+
+ template <class FromType>
+ bool operator =(FromType from)
+ {
+ data = from;
+ cached=true;
+ return cached;
+ }
+
+private:
+ bool cached;
+ DataType data;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/ClockLayer.cpp b/Src/Plugins/Input/in_wmvdrm/ClockLayer.cpp
new file mode 100644
index 00000000..cce27870
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/ClockLayer.cpp
@@ -0,0 +1,101 @@
+#include "Main.h"
+#include "ClockLayer.h"
+#include "config.h"
+
+ClockLayer::ClockLayer(IWMReader *reader)
+ : clock(0), startTime(0),
+ clockTick(2500000), curTime(0),
+ startTimeMilliseconds(0),
+ realTime(false),
+ lastOutputTime(0)
+{
+ if (FAILED(reader->QueryInterface(&clock)))
+ clock=0;
+}
+
+void ClockLayer::Opened()
+{
+ realTime=!config_clock;
+ WMHandler::Opened();
+
+ if (!realTime)
+ {
+ HRESULT hr = clock->SetUserProvidedClock(TRUE);
+
+ if (FAILED(hr))
+ {
+ realTime=false;
+ }
+ }
+ else
+ clock->SetUserProvidedClock(FALSE);
+ curTime = startTime;
+}
+
+void ClockLayer::Started()
+{
+ if (!realTime && config_clock)
+ clock->DeliverTime((QWORD) - 1);
+
+ if (startTimeMilliseconds != 0)
+ out->Flush(startTimeMilliseconds);
+
+ SetLastOutputTime(startTimeMilliseconds);
+ WMHandler::Started();
+}
+
+void ClockLayer::Clock()
+{
+ if (!realTime && config_clock)
+ clock->DeliverTime((QWORD) - 1);
+}
+
+void ClockLayer::SetStartTimeMilliseconds(long time)
+{
+ startTimeMilliseconds=time;
+ startTime = time;
+ startTime *= 10000;
+}
+
+void ClockLayer::TimeReached(QWORD &timeReached)
+{
+ curTime = timeReached;
+}
+
+QWORD ClockLayer::GetStartTime()
+{
+ return startTime;
+}
+
+void ClockLayer::GoRealTime()
+{
+ realTime = true;
+}
+
+int ClockLayer::GetOutputTime()
+{
+ if (realTime)
+ return (int) (curTime / 10000LL);
+ else
+ return lastOutputTime + (out->GetOutputTime() - out->GetWrittenTime());
+}
+
+void ClockLayer::TimeToSync(QWORD timeStamp, __int64 &diff)
+{
+ if (realTime)
+ diff = 0;
+ else
+ {
+ QWORD outputTime = this->GetOutputTime();
+ outputTime *= 10000LL;
+ diff = timeStamp - outputTime;
+ }
+}
+
+void ClockLayer::SampleReceived(QWORD &timeStamp, QWORD &duration, unsigned long &outputNum, unsigned long &flags, INSSBuffer *&sample)
+{
+ if (realTime)
+ curTime = timeStamp;
+
+ WMHandler::SampleReceived(timeStamp, duration, outputNum, flags, sample);
+}
diff --git a/Src/Plugins/Input/in_wmvdrm/ClockLayer.h b/Src/Plugins/Input/in_wmvdrm/ClockLayer.h
new file mode 100644
index 00000000..1fb89230
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/ClockLayer.h
@@ -0,0 +1,36 @@
+#ifndef NULLSOFT_CLOCKLAYERH
+#define NULLSOFT_CLOCKLAYERH
+
+#include "WMHandler.h"
+class ClockLayer : public WMHandler
+{
+public:
+ ClockLayer(IWMReader *reader);
+
+ void SetStartTimeMilliseconds(long time);
+ QWORD GetStartTime();
+
+ void GoRealTime();
+ int GetOutputTime();
+ void SetLastOutputTime(int _outputTime)
+ {
+ lastOutputTime = _outputTime;
+ }
+ void Clock();
+private:
+ // WMHandler
+ void Opened();
+ void Started();
+ void TimeReached(QWORD &timeReached);
+ void TimeToSync(QWORD timeStamp, __int64 &diff);
+ void SampleReceived(QWORD &timeStamp, QWORD &duration, unsigned long &outputNum, unsigned long &flags, INSSBuffer *&sample);
+
+ IWMReaderAdvanced *clock;
+
+ QWORD startTime, clockTick, curTime;
+ DWORD startTimeMilliseconds;
+ bool realTime;
+ int lastOutputTime;
+};
+
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/ConfigDialog.cpp b/Src/Plugins/Input/in_wmvdrm/ConfigDialog.cpp
new file mode 100644
index 00000000..ece52a0e
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/ConfigDialog.cpp
@@ -0,0 +1,398 @@
+#include "Main.h"
+#include "resource.h"
+#include "../nu/ListView.h"
+#include "FileTypes.h"
+#include "AutoChar.h"
+
+W_ListView typeList;
+FileTypes::TypeList types;
+#define CFGSET(hwnd, code, boolval) CheckDlgButton(hwnd, code, ((boolval)?BST_CHECKED:BST_UNCHECKED))
+
+struct SpeakerSetup
+{
+ int description;
+ DWORD value;
+};
+SpeakerSetup speakerList[] =
+{
+ {IDS_STEREO,DSSPEAKER_STEREO},
+ {IDS_QUADROPHONIC,DSSPEAKER_QUAD},
+ {IDS_SURROUND,DSSPEAKER_SURROUND},
+ {IDS_5_1,DSSPEAKER_5POINT1},
+ {IDS_7_1,DSSPEAKER_7POINT1},
+};
+
+void FillFileTypes()
+{
+ typeList.Clear();
+
+ long attrCount = types.size();
+ int pos=0;
+ for (long i = 0;i < attrCount;i++)
+ {
+ typeList.InsertItem(pos, types[i].wtype, 0);
+ typeList.SetItemText(pos, 1, types[i].description);
+ pos++;
+ }
+}
+
+void ResetTypes()
+{
+ {
+ Nullsoft::Utility::AutoLock lock (fileTypes.typeGuard);
+ types=fileTypes.types;
+ }
+ FillFileTypes();
+}
+
+void Preferences_Populate(HWND hwndDlg)
+{
+ CFGSET(hwndDlg, IDC_HTTPMETA, config_http_metadata);
+
+/* CFGSET(hwndDlg, IDC_SILENT, !config_no_silent);
+ CFGSET(hwndDlg, IDC_UNTRUSTED, config_untrusted_ok);
+*/
+ CFGSET(hwndDlg, IDC_EXTRA_ASX, config_extra_asx_extensions);
+
+ SetDlgItemInt(hwndDlg,IDC_BUFFER_TIME, config_buffer_time, FALSE/*unsigned*/);
+
+ SendMessage(GetDlgItem(hwndDlg, IDC_AUDIO_SPEAKER_COUNT), CB_RESETCONTENT, 0, 0);
+ for (int i = 0;i < sizeof(speakerList)/sizeof(speakerList[0]) ;i++)
+ {
+ SendMessage(GetDlgItem(hwndDlg, IDC_AUDIO_SPEAKER_COUNT), CB_ADDSTRING, 0, (LPARAM) WASABI_API_LNGSTRINGW(speakerList[i].description));
+ if (speakerList[i].value == config_audio_num_channels)
+ SendMessage(GetDlgItem(hwndDlg, IDC_AUDIO_SPEAKER_COUNT), CB_SETCURSEL, i, 0);
+ }
+
+ ResetTypes();
+}
+void Preferences_Init(HWND hwndDlg)
+{
+ typeList.setwnd(GetDlgItem(hwndDlg, IDC_TYPELIST));
+ typeList.AddCol(WASABI_API_LNGSTRINGW(IDS_EXT), 75);
+ typeList.AddCol(WASABI_API_LNGSTRINGW(IDS_DESCRIPTION), 250);
+ Preferences_Populate(hwndDlg);
+
+ if(config_col1 == -1)
+ typeList.AutoSizeColumn(0);
+ else
+ typeList.SetColumnWidth(0, config_col1);
+
+ if(config_col2 == -1)
+ typeList.AutoSizeColumn(1);
+ else
+ typeList.SetColumnWidth(1, config_col2);
+
+ if (NULL != WASABI_API_APP)
+ WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(typeList.getwnd(), TRUE);
+}
+
+void Preferences_TypeRemove(HWND hwndDlg)
+{
+ int next = -1;
+
+ do
+ {
+ next = typeList.GetNextSelected(next);
+ if (next != -1)
+ {
+ free(types[next].wtype);
+ types[next].wtype=0;
+ }
+ } while (next != -1);
+
+ for (FileTypes::TypeList::iterator itr = types.begin(); itr != types.end(); )
+ {
+ if (!itr->wtype || !itr->wtype[0])
+ types.erase(itr);
+ else
+ itr++;
+ }
+}
+
+void OnOk(HWND hwndDlg)
+{
+ config_http_metadata=IsDlgButtonChecked(hwndDlg, IDC_HTTPMETA)== BST_CHECKED;
+// config_no_silent=IsDlgButtonChecked(hwndDlg, IDC_SILENT) != BST_CHECKED;
+// config_untrusted_ok=IsDlgButtonChecked(hwndDlg, IDC_UNTRUSTED)== BST_CHECKED;
+ config_extra_asx_extensions=IsDlgButtonChecked(hwndDlg, IDC_EXTRA_ASX)== BST_CHECKED;
+ wchar_t temp[64] = {0};
+
+ GetDlgItemText(hwndDlg,IDC_BUFFER_TIME, temp, 64);
+ int newBufferTime= _wtoi(temp);
+ if (newBufferTime<1000)
+ newBufferTime=1000;
+ if (newBufferTime>60000)
+ newBufferTime=60000;
+ config_buffer_time=newBufferTime;
+
+ int numChannels=SendMessage(GetDlgItem(hwndDlg, IDC_AUDIO_SPEAKER_COUNT), CB_GETCURSEL, 0, 0);
+ if (numChannels!=CB_ERR)
+ config_audio_num_channels = speakerList[numChannels].value;
+
+ fileTypes.SetTypes(types);
+}
+
+static INT_PTR CALLBACK AddTypeProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ CFGSET(hwndDlg, IDC_FILEEXTENSION, true);
+ CFGSET(hwndDlg, IDC_TYPE_AUDIO, true);
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_PROTOCOL:
+ SetDlgItemText(hwndDlg, IDC_STATIC_TYPE, WASABI_API_LNGSTRINGW(IDS_PROTOCOL));
+ break;
+ case IDC_FILEEXTENSION:
+ SetDlgItemText(hwndDlg, IDC_STATIC_TYPE, WASABI_API_LNGSTRINGW(IDS_EXTENSION));
+ break;
+ case IDOK:
+ {
+ wchar_t type[MAX_PATH] = {0}, description[1024] = {0};
+ GetDlgItemText(hwndDlg,IDC_TYPE,type,MAX_PATH);
+ GetDlgItemText(hwndDlg,IDC_DESCRIPTION,description,1024);
+ bool isProtocol = IsDlgButtonChecked(hwndDlg, IDC_PROTOCOL)== BST_CHECKED;
+ int avType = IsDlgButtonChecked(hwndDlg, IDC_TYPE_VIDEO)== BST_CHECKED;
+ types.push_back(FileType(type, description, isProtocol, avType));
+ EndDialog(hwndDlg, 0);
+ }
+ break;
+ case IDCANCEL:
+ EndDialog(hwndDlg, 0);
+ break;
+ }
+ break;
+
+ }
+ return FALSE;
+}
+
+void AddType(HWND hwndDlg)
+{
+ WASABI_API_DIALOGBOXW(IDD_ADDTYPE, hwndDlg, AddTypeProc);
+}
+
+static size_t editIndex=0;
+static INT_PTR CALLBACK EditTypeProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ SetWindowText(hwndDlg, WASABI_API_LNGSTRINGW(IDS_EDIT_FILE_TYPE));
+ CFGSET(hwndDlg, IDC_TYPE_AUDIO, (types[editIndex].avType==FileType::AUDIO));
+ CFGSET(hwndDlg, IDC_TYPE_VIDEO, (types[editIndex].avType==FileType::VIDEO));
+ CFGSET(hwndDlg, IDC_PROTOCOL, types[editIndex].isProtocol);
+ CFGSET(hwndDlg, IDC_FILEEXTENSION, !types[editIndex].isProtocol);
+ SetDlgItemText(hwndDlg,IDC_TYPE, types[editIndex].wtype);
+ SetDlgItemText(hwndDlg,IDC_DESCRIPTION, types[editIndex].description);
+ if (types[editIndex].isProtocol)
+ SetDlgItemText(hwndDlg, IDC_STATIC_TYPE, WASABI_API_LNGSTRINGW(IDS_PROTOCOL));
+ else
+ SetDlgItemText(hwndDlg, IDC_STATIC_TYPE, WASABI_API_LNGSTRINGW(IDS_EXTENSION));
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_PROTOCOL:
+ SetDlgItemText(hwndDlg, IDC_STATIC_TYPE, WASABI_API_LNGSTRINGW(IDS_PROTOCOL));
+ break;
+ case IDC_FILEEXTENSION:
+ SetDlgItemText(hwndDlg, IDC_STATIC_TYPE, WASABI_API_LNGSTRINGW(IDS_EXTENSION));
+ break;
+ case IDOK:
+ {
+ wchar_t type[MAX_PATH] = {0}, description[1024] = {0};
+ GetDlgItemText(hwndDlg,IDC_TYPE,type,MAX_PATH);
+ GetDlgItemText(hwndDlg,IDC_DESCRIPTION,description,1024);
+ bool isProtocol = IsDlgButtonChecked(hwndDlg, IDC_PROTOCOL)== BST_CHECKED;
+
+ types[editIndex].avType = (IsDlgButtonChecked(hwndDlg, IDC_TYPE_VIDEO)== BST_CHECKED);
+ if(types[editIndex].wtype)free(types[editIndex].wtype);
+ types[editIndex].wtype = _wcsdup(type);
+
+ types[editIndex].isProtocol = isProtocol;
+ if(types[editIndex].description)free(types[editIndex].description);
+ types[editIndex].description = _wcsdup(description);
+ EndDialog(hwndDlg, 0);
+ }
+ break;
+ case IDCANCEL:
+ EndDialog(hwndDlg, 0);
+ break;
+ }
+ break;
+
+ }
+ return FALSE;
+}
+
+
+void EditType(HWND hwndDlg)
+{
+ editIndex=-1;
+ do
+ {
+ editIndex=typeList.GetNextSelected(editIndex);
+ if (editIndex != -1)
+ {
+ WASABI_API_DIALOGBOXW(IDD_ADDTYPE, hwndDlg, EditTypeProc);
+ }
+ else
+ return;
+
+ } while (true);
+}
+
+
+void Advanced_Init(HWND hwndDlg)
+{
+ CFGSET(hwndDlg, IDC_AUDIO_THREAD, config_audio_dedicated_thread);
+ CFGSET(hwndDlg, IDC_AUDIO_EARLY, config_audio_early);
+ CFGSET(hwndDlg, IDC_AUDIO_OUTOFORDER, config_audio_outoforder);
+ CFGSET(hwndDlg, IDC_AUDIO_DROP, config_video_catchup);
+
+ CFGSET(hwndDlg, IDC_VIDEO_THREAD, config_video_dedicated_thread);
+ CFGSET(hwndDlg, IDC_VIDEO_EARLY, config_video_early);
+ CFGSET(hwndDlg, IDC_VIDEO_OUTOFORDER, config_video_outoforder);
+ CFGSET(hwndDlg, IDC_VIDEO_NOTIFY, config_video_notifylate);
+
+ CFGSET(hwndDlg, IDC_REALTIME, !config_clock);
+ CFGSET(hwndDlg, IDC_LOWMEMORY, config_lowmemory);
+
+ SetDlgItemInt(hwndDlg,IDC_AUDIO_CACHE_FRAMES, config_audio_cache_frames, TRUE/*signed*/);
+ SetDlgItemInt(hwndDlg,IDC_VIDEO_CACHE_FRAMES, config_video_cache_frames, TRUE);
+ SetDlgItemInt(hwndDlg,IDC_VIDEO_DROP_THRESHOLD, config_video_drop_threshold, TRUE);
+ SetDlgItemInt(hwndDlg,IDC_VIDEO_JITTER, config_video_jitter, TRUE);
+ SetDlgItemInt(hwndDlg,IDC_AUDIO_EARLYPAD, config_audio_early_pad, TRUE);
+ SetDlgItemInt(hwndDlg,IDC_VIDEO_EARLYPAD, config_video_early_pad,TRUE);
+
+}
+
+void Advanced_OnOK(HWND hwndDlg)
+{
+ config_audio_dedicated_thread=IsDlgButtonChecked(hwndDlg, IDC_AUDIO_THREAD)== BST_CHECKED;
+ config_audio_early=IsDlgButtonChecked(hwndDlg, IDC_AUDIO_EARLY)== BST_CHECKED;
+ config_audio_outoforder=IsDlgButtonChecked(hwndDlg, IDC_AUDIO_OUTOFORDER)== BST_CHECKED;
+ config_video_catchup=IsDlgButtonChecked(hwndDlg, IDC_AUDIO_DROP)== BST_CHECKED;
+
+ config_video_dedicated_thread=IsDlgButtonChecked(hwndDlg, IDC_VIDEO_THREAD)== BST_CHECKED;
+ config_video_early=IsDlgButtonChecked(hwndDlg, IDC_VIDEO_EARLY)== BST_CHECKED;
+ config_video_outoforder=IsDlgButtonChecked(hwndDlg, IDC_VIDEO_OUTOFORDER)== BST_CHECKED;
+ config_video_notifylate=IsDlgButtonChecked(hwndDlg, IDC_VIDEO_NOTIFY)== BST_CHECKED;
+
+ config_clock=IsDlgButtonChecked(hwndDlg, IDC_REALTIME)!= BST_CHECKED;
+ config_lowmemory=IsDlgButtonChecked(hwndDlg, IDC_LOWMEMORY)== BST_CHECKED;
+
+ wchar_t temp[64] = {0};
+
+ GetDlgItemText(hwndDlg,IDC_AUDIO_CACHE_FRAMES, temp, 64);
+ config_audio_cache_frames = _wtoi(temp);
+
+ GetDlgItemText(hwndDlg,IDC_VIDEO_CACHE_FRAMES, temp, 64);
+ config_video_cache_frames= _wtoi(temp);
+
+ GetDlgItemText(hwndDlg,IDC_VIDEO_DROP_THRESHOLD, temp, 64);
+ config_video_drop_threshold= _wtoi(temp);
+
+ GetDlgItemText(hwndDlg,IDC_VIDEO_JITTER, temp, 64);
+ config_video_jitter= _wtoi(temp);
+
+ GetDlgItemText(hwndDlg,IDC_AUDIO_EARLYPAD, temp, 64);
+ config_audio_early_pad= _wtoi(temp);
+
+ GetDlgItemText(hwndDlg,IDC_VIDEO_EARLYPAD, temp, 64);
+ config_video_early_pad= _wtoi(temp);
+}
+
+static INT_PTR CALLBACK AdvancedDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ Advanced_Init(hwndDlg);
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ Advanced_OnOK(hwndDlg);
+ EndDialog(hwndDlg, 0);
+ break;
+ case IDCANCEL:
+ EndDialog(hwndDlg, 0);
+ break;
+
+ }
+ break;
+ }
+
+ return 0;
+}
+
+void Advanced(HWND hwndDlg)
+{
+ WASABI_API_DIALOGBOXW(IDD_ADVANCED, hwndDlg, AdvancedDialogProc);
+}
+
+void Preferences_Default(HWND hwndDlg)
+{
+ if (config_no_video)
+ {
+ // TODO: ask the user if they want to enable video support
+ }
+ DefaultConfig();
+ fileTypes.LoadDefaults();
+ Preferences_Populate(hwndDlg);
+}
+
+INT_PTR CALLBACK PreferencesDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ Preferences_Init(hwndDlg);
+ break;
+ case WM_DESTROY:
+ config_col1 = typeList.GetColumnWidth(0);
+ config_col2 = typeList.GetColumnWidth(1);
+
+ if (NULL != WASABI_API_APP)
+ WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(typeList.getwnd(), FALSE);
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_ADVANCED:
+ Advanced(hwndDlg);
+ break;
+ case IDC_DEFAULTTYPE:
+ Preferences_Default(hwndDlg);
+ break;
+ case IDC_ADDTYPE:
+ AddType(hwndDlg);
+ FillFileTypes();
+ break;
+ case IDC_EDITTYPE:
+ EditType(hwndDlg);
+ if(typeList.GetNextSelected(editIndex)!=-1)
+ FillFileTypes();
+ break;
+ case IDC_REMOVETYPE:
+ Preferences_TypeRemove(hwndDlg);
+ FillFileTypes();
+ break;
+ case IDOK:
+ OnOk(hwndDlg);
+ case IDCANCEL:
+ EndDialog(hwndDlg, 0);
+ break;
+ }
+ break;
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/ConfigDialog.h b/Src/Plugins/Input/in_wmvdrm/ConfigDialog.h
new file mode 100644
index 00000000..e2b22e4f
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/ConfigDialog.h
@@ -0,0 +1,4 @@
+#ifndef NULLSOFT_CONFIGDIALOGH
+#define NULLSOFT_CONFIGDIALOGH
+INT_PTR CALLBACK PreferencesDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/DESIGN.txt b/Src/Plugins/Input/in_wmvdrm/DESIGN.txt
new file mode 100644
index 00000000..2785c5c5
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/DESIGN.txt
@@ -0,0 +1,34 @@
+Windows Media works in an asynchronous manner.
+The WM Decompressor (IWMReader) requires that you provide a class
+deriving from IWMReader (and, optionally, IWMReaderAdvanced) to receive information
+Each delivery of uncompressed media arrives in an OnSample method
+Status messages arrive in an OnStatus method.
+
+The main difficulty that arrives from this approach is that ALL messages funnel through
+a single function. Audio data and Video data arrive in the same function.
+Even worse, status messages for file opened, clock error, http buffering, and DRM requirements all arrive in the same function!
+
+In order to handle this properly, a "chain of event handlers" class was developed (WMHandler)
+Any object wishing to receive data or status messages can derive from the WMHandler. All unhandled
+messages are automatically passed to the next handler in the chain. Messages that are handled can be
+passed or not passed based on programming requirements.
+
+The main object sets up the chain. The >> operator has been convienently defined to faciliate the chaining.
+The left side of the >> line must be the source (WMCallback)
+(e.g. callback >> clock >> drm >> video >> audio >> wait >> this;)
+Handlers may create their own mini-chains (which will get added in-whole) using the WMHandler::Inject() method.
+
+Threading
+------
+7 basic threads
+
+1. Main (Windows) thread
+2. Callback/OnStatus thread
+3. Audio Decoder thread
+4. Video Decoder thread
+5. Audio buffering thread (ours)
+6. Video buffering thread (ours)
+7. Buffering-percent status thread (ours)
+
+There might be additional threads (created in another module) calling in, for functions like getextendedfileinfo
+
diff --git a/Src/Plugins/Input/in_wmvdrm/ExtendedFileInfo.cpp b/Src/Plugins/Input/in_wmvdrm/ExtendedFileInfo.cpp
new file mode 100644
index 00000000..53ee3cf4
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/ExtendedFileInfo.cpp
@@ -0,0 +1,597 @@
+#include "main.h"
+#include "../nu/AutoWide.h"
+#include "WMPlaylist.h"
+#include "resource.h"
+#include <math.h>
+#include <strsafe.h>
+
+WMInformation *setFileInfo = 0;
+static wchar_t *setFileInfoName=0;
+static bool forcedStop = false;
+static int outTime = 0;
+float GetGain(WMInformation *info, bool allowDefault);
+
+static const wchar_t *extension(const wchar_t *fn)
+{
+ const wchar_t *x = PathFindExtension(fn);
+
+ if (*x)
+ return CharNext(x);
+ else
+ return x;
+}
+
+static bool KeywordMatch(const char *mainString, const char *keyword)
+{
+ return !_stricmp(mainString, keyword);
+}
+
+static bool KeywordMatch(const wchar_t *mainString, const wchar_t *keyword)
+{
+ return !lstrcmpiW(mainString, keyword);
+}
+
+static bool StartsWith(const wchar_t *mainString, const wchar_t *substring)
+{
+ return !_wcsnicmp(mainString, substring, lstrlenW(substring));
+}
+
+static int Width(int dec)
+{
+ // there's probably a better way
+ int width=3;
+ while (width && (dec % 10) == 0)
+ {
+ dec/=10;
+ width--;
+ }
+ return width;
+}
+
+
+int GetExtendedInformation(WMInformation *getExtendedFileInfo, const wchar_t *fn, const char *data, wchar_t *dest, int destlen)
+{
+ AutoWide tagNameW(data);
+ const wchar_t *tagName = GetAlias(tagNameW);
+
+ if (KeywordMatch(tagName, L"streammetadata"))
+ {
+ if (config_http_metadata)
+ {
+ lstrcpyn(dest, L"1", destlen);
+ return 1;
+ }
+ return 0;
+ }
+ else if (KeywordMatch(tagName, L"type"))
+ {
+ if (!fn || !fn[0])
+ lstrcpyn(dest, (config_no_video ? L"0" : L"1"), destlen);
+ else if (getExtendedFileInfo->IsAttribute(g_wszWMHasVideo))
+ lstrcpyn(dest, L"1", destlen);
+ else if (getExtendedFileInfo->IsAttribute(g_wszWMHasAudio))
+ lstrcpyn(dest, L"0", destlen);
+ else
+ {
+ switch (fileTypes.GetAVType(extension(fn)))
+ {
+ case FileType::AUDIO:
+ lstrcpyn(dest, L"0", destlen);
+ break;
+ case FileType::VIDEO:
+ lstrcpyn(dest, L"1", destlen);
+ break;
+ default:
+ return 0;
+ }
+ }
+ return 1;
+ }
+ else if (KeywordMatch(tagName, L"rateable"))
+ {
+ dest[0] = '1';
+ dest[1] = 0;
+ return 1;
+ }
+ else if (StartsWith(tagName, L"WM/"))
+ {
+ getExtendedFileInfo->GetAttribute(tagName, dest, destlen);
+ return 1;
+ }
+ /*else if (KeywordMatch(data, "burnable")) // note: this isn't supposed to be any kind of protection - just keeps the burner from misbehaving on protected tracks
+ {
+ if (getExtendedFileInfo->IsAttribute(g_wszWMProtected))
+ lstrcpyn(dest, L"0", destlen);
+ else
+ lstrcpyn(dest, L"1", destlen);
+
+ return 1;
+ }*/
+ else if (KeywordMatch(data, "noburnreason")) // note: this isn't supposed to be any kind of protection - just keeps the burner from misbehaving on protected tracks
+ {
+ if (getExtendedFileInfo->IsAttribute(g_wszWMProtected))
+ {
+ lstrcpyn(dest, L"DRM (copy protected) file", destlen);
+ return 1;
+ }
+ }
+ else if ((const wchar_t *)tagNameW != tagName) // if the tag was in the tag list
+ {
+ getExtendedFileInfo->GetAttribute(tagName, dest, destlen);
+ return 1;
+ }
+ else if (KeywordMatch(data, "bitrate"))
+ {
+ StringCchPrintfW(dest, destlen, L"%u", getExtendedFileInfo->GetBitrate() / 1000);
+ return 1;
+ }
+ else if (KeywordMatch(data, "vbr"))
+ {
+ if (getExtendedFileInfo->IsAttribute(g_wszWMIsVBR))
+ StringCchCopyW(dest, destlen, L"1");
+ else if (getExtendedFileInfo->IsNotAttribute(g_wszWMIsVBR))
+ StringCchCopyW(dest, destlen, L"0");
+
+ return 1;
+ }
+ //else if (KeywordMatch(data, "srate"))
+ else if (KeywordMatch(data, "length"))
+ {
+ long length = getExtendedFileInfo->GetLengthMilliseconds();
+ if (length == -1000)
+ return 0;
+
+ _itow(length, dest, 10);
+ return 1;
+ }
+ else if (KeywordMatch(data, "rating"))
+ {
+ wchar_t rating_string[128] = {0};
+ getExtendedFileInfo->GetAttribute(L"WM/SharedUserRating", rating_string, 128);
+ int rating = _wtoi(rating_string);
+ if (rating == 0)
+ dest[0]=0;
+ else if (rating >= 1 && rating <= 12)
+ dest[0]=L'1';
+ else if (rating >= 13 && rating <= 37)
+ dest[0]=L'2';
+ else if (rating >= 38 && rating <= 62)
+ dest[0]=L'3';
+ else if (rating >= 63 && rating <= 86)
+ dest[0]=L'4';
+ else
+ dest[0]=L'5';
+ dest[1]=0;
+ return 1;
+ }
+ else if (KeywordMatch(data, "replaygain_track_gain")
+ || KeywordMatch(data, "replaygain_track_peak")
+ || KeywordMatch(data, "replaygain_album_gain")
+ || KeywordMatch(data, "replaygain_album_peak"))
+ {
+ getExtendedFileInfo->GetAttribute(tagName, dest, destlen);
+ return 1;
+ }
+ else if (KeywordMatch(data, "gain"))
+ {
+ StringCchPrintfW(dest, destlen, L"%-+.2f dB", (float)log10f(GetGain(getExtendedFileInfo, false))*20.0f);
+ return 1;
+ }
+ else if (KeywordMatch(data, "audiocodec"))
+ {
+ if (!getExtendedFileInfo->GetCodecName(dest, destlen))
+ dest[0]=0;
+ return 1;
+ }
+ else if (KeywordMatch(data, "lossless"))
+ {
+ wchar_t codecname[1024] = {0};
+ if (!getExtendedFileInfo->GetCodecName(codecname, 1024))
+ dest[0]=0;
+ else
+ {
+ dest[0] = wcsstr(codecname, L"Lossless")?'1':'0';
+ dest[1]=0;
+ }
+ return 1;
+ }
+ else if (KeywordMatch(data, "GracenoteFileID"))
+ {
+ getExtendedFileInfo->GetAttribute_BinString(L"GN/UniqueFileIdentifier", dest, destlen);
+ return 1;
+ }
+ else if (KeywordMatch(data, "GracenoteExtData"))
+ {
+ getExtendedFileInfo->GetAttribute_BinString(L"GN/ExtData", dest, destlen);
+ return 1;
+ }
+ else if (KeywordMatch(data, "formatinformation"))
+ {
+ // this is a bit of a clusterfuck, but it's safe and (hopefully) logically laid out.
+ wchar_t codec[128]=L"", duration[64]=L"", bitrate[64]=L"", filesize[64]=L"", wmver[64]=L"", seekable[64]=L"";
+ wchar_t stridable[64]=L"", broadcast[64]=L"", protect[64]=L"", trusted[64]=L"", contents[64]=L"";
+ wchar_t buf[128]=L""; // temporary buffer
+
+ // get the codec name string
+ if (getExtendedFileInfo->GetCodecName(buf, 128) && buf[0])
+ StringCchPrintf(codec,128,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_CODEC),buf);
+
+ // get the length string formatted h:mm:ss.tttt
+ long t = getExtendedFileInfo->GetLengthMilliseconds();
+ if (t)
+ {
+ long h = t/36000000;
+ long m = (t/60000)%60;
+ long s = (t/1000)%60;
+ long ms = t%1000;
+ if (h)
+ StringCchPrintf(duration,64,L"%s: %u:%02u:%02u.%03u\n",WASABI_API_LNGSTRINGW(IDS_DURATION),h,m,s,ms);
+ else if (m)
+ StringCchPrintf(duration,64,L"%s: %u:%02u.%03u\n",WASABI_API_LNGSTRINGW(IDS_DURATION),m,s,ms);
+ else
+ StringCchPrintf(duration,64,L"%s: %u.%03u\n",WASABI_API_LNGSTRINGW(IDS_DURATION),s,ms);
+ }
+
+ // get the bitrate string formatted 128.235 kbps
+ long br = getExtendedFileInfo->GetBitrate();
+ wchar_t kbps[16] = {0};
+ StringCchPrintf(bitrate,64,L"%s: %.*f %s\n",
+ WASABI_API_LNGSTRINGW(IDS_BITRATE),
+ Width(br%1000),
+ br/1000.0,
+ WASABI_API_LNGSTRINGW_BUF(IDS_KBPS,kbps,16));
+
+ // get the filesize string, with commas grouping in threes
+ buf[0]=0;
+ getExtendedFileInfo->GetAttribute(L"FileSize",buf,64);
+ uint64_t fs = _wcstoui64(buf, 0, 10);
+ if (fs)
+ {
+ uint64_t fsgb = (fs/1000000000LL);
+ uint64_t fsmb = (fs/1000000LL)%1000LL;
+ uint64_t fskb = (fs/1000LL)%1000LL;
+ uint64_t fsb = fs%1000LL;
+ if (fsgb)
+ StringCchPrintf(filesize,64,L"%s: %I64u,%03I64u,%03I64u,%03I64u\n",WASABI_API_LNGSTRINGW(IDS_FILESIZE),fsgb,fsmb,fskb,fsb);
+ else if (fsmb)
+ StringCchPrintf(filesize,64,L"%s: %I64u,%03I64u,%03I64u\n",WASABI_API_LNGSTRINGW(IDS_FILESIZE),fsmb,fskb,fsb);
+ else if (fskb)
+ StringCchPrintf(filesize,64,L"%s: %I64u,%03I64u\n",WASABI_API_LNGSTRINGW(IDS_FILESIZE),fskb,fsb);
+ else
+ StringCchPrintf(filesize,64,L"%s: %I64u\n",WASABI_API_LNGSTRINGW(IDS_FILESIZE),fsb);
+ }
+
+ // 4 boolean flags, compose their strings
+ wchar_t yes[64] = {0}, no[64] = {0};
+ WASABI_API_LNGSTRINGW_BUF(IDS_YES,yes,64);
+ WASABI_API_LNGSTRINGW_BUF(IDS_NO,no,64);
+
+ buf[0]=0;
+ getExtendedFileInfo->GetAttribute(L"WMFSDKVersion",buf,128);
+ if (buf[0])
+ StringCchPrintf(wmver,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_WMVER),buf);
+
+ StringCchPrintf(seekable,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_SEEKABLE),getExtendedFileInfo->IsAttribute(L"Seekable")?yes:no);
+ StringCchPrintf(stridable,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_STRIDABLE),getExtendedFileInfo->IsAttribute(L"Stridable")?yes:no);
+ StringCchPrintf(broadcast,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_BROADCAST),getExtendedFileInfo->IsAttribute(L"Broadcast")?yes:no);
+ StringCchPrintf(protect,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_PROTECTED),getExtendedFileInfo->IsAttribute(L"Is_Protected")?yes:no);
+ StringCchPrintf(trusted,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_TRUSTED),getExtendedFileInfo->IsAttribute(L"Is_Trusted")?yes:no);
+
+ // file contents. bit gross i know.
+ wchar_t cont[4][16]={L"",L"",L"",L""};
+ int i=0;
+ if (getExtendedFileInfo->IsAttribute(L"HasAudio"))
+ WASABI_API_LNGSTRINGW_BUF(IDS_AUDIO,cont[i++],16);
+ if (getExtendedFileInfo->IsAttribute(L"HasVideo"))
+ WASABI_API_LNGSTRINGW_BUF(IDS_VIDEO,cont[i++],16);
+ if (getExtendedFileInfo->IsAttribute(L"HasImage"))
+ WASABI_API_LNGSTRINGW_BUF(IDS_IMAGE,cont[i++],16);
+ if (getExtendedFileInfo->IsAttribute(L"HasScript"))
+ WASABI_API_LNGSTRINGW_BUF(IDS_SCRIPT,cont[i++],16);
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_NONE,buf,64);
+ if (i == 0)
+ StringCchPrintf(contents,64,L"%s: %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), buf);
+ else if (i == 1)
+ StringCchPrintf(contents,64,L"%s: %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), cont[0]);
+ else if (i == 2)
+ StringCchPrintf(contents,64,L"%s: %s, %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), cont[0], cont[1]);
+ else if (i == 3)
+ StringCchPrintf(contents,64,L"%s: %s, %s, %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), cont[0], cont[1], cont[2]);
+ else if (i == 4)
+ StringCchPrintf(contents,64,L"%s: %s, %s, %s, %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), cont[0], cont[1], cont[2], cont[3]);
+
+ // compose our string together!
+ StringCchPrintf(dest,destlen,L"%s%s%s%s%s%s%s%s%s%s%s",codec, duration, bitrate, filesize, wmver, seekable, stridable, broadcast, protect, trusted, contents);
+ }
+ else
+ return 0;
+
+ return 1;
+}
+
+#if 0 // had to disable this because it was locking the file from being deleted
+WMInformation *lastGetInfo = 0;
+wchar_t *lastGetInfoFn;
+#endif
+
+extern "C" __declspec(dllexport)
+ int winampGetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *dest, int destlen)
+{
+ /* Check if there's a status message for this filename
+ doing this forces Winamp to hit plugin.getfileinfo, which gives us better control
+ over adding things like [Individualizing] to the playlist title for local files
+ */
+ if (winamp.HasStatus(fn))
+ return 0;
+
+ if ((!fn || !*fn) && KeywordMatch(data, "type"))
+ {
+ if (config_no_video)
+ lstrcpyn(dest, L"0", destlen);
+ else
+ lstrcpyn(dest, L"1", destlen);
+ return 1;
+ }
+
+
+ if (KeywordMatch(data, "mime"))
+ {
+ int len;
+ const wchar_t *p;
+ if (!fn || !fn[0]) return 0;
+ len = lstrlenW(fn);
+ if (len < 4 || L'.' != fn[len - 4]) return 0;
+ p = &fn[len - 3];
+ if (!_wcsicmp(p, L"WMA")) { StringCchCopyW(dest, destlen, L"audio/x-ms-wma"); return 1; }
+ if (!_wcsicmp(p, L"WMV")) { StringCchCopyW(dest, destlen, L"video/x-ms-wmv"); return 1; }
+ if (!_wcsicmp(p, L"ASF")) { StringCchCopyW(dest, destlen, L"video/x-ms-asf"); return 1; }
+
+ return 0;
+ }
+ if (KeywordMatch(data, "family"))
+ {
+ LPCWSTR e, p(NULL);
+ DWORD lcid;
+ size_t i;
+ int len2(0);
+
+ if (!fn || !*fn) return 0;
+ e = PathFindExtensionW(fn);
+ if (L'.' != *e) return 0;
+ e++;
+
+ if (!*e) return 0;
+ lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+
+ for (i = 0; i < fileTypes.types.size() && !p; i++)
+ {
+ if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, fileTypes.types.at(i).wtype, -1))
+ p = fileTypes.types.at(i).description;
+ }
+
+ if (p)
+ {
+ wchar_t szTest[16];
+ if (S_OK == StringCchPrintfW(szTest, sizeof(szTest)/sizeof(wchar_t), L" (*.%s)", e))
+ {
+ int len1 = lstrlenW(szTest);
+ len2 = lstrlenW(p);
+ if (len2 > len1 && CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, szTest, -1, (p + len2 - len1), -1))
+ len2 -= len1;
+ }
+ }
+
+ return (p && S_OK == StringCchCopyNW(dest, destlen, p, len2));
+ }
+
+ if (!config_http_metadata && PathIsURL(fn))
+ return 0;
+
+ if (KeywordMatch(data, "type") && !PathFileExistsW(fn))
+ {
+ switch (fileTypes.GetAVType(extension(fn)))
+ {
+ case FileType::AUDIO:
+ lstrcpyn(dest, L"0", destlen);
+ return 1;
+ case FileType::VIDEO:
+ lstrcpyn(dest, L"1", destlen);
+ return 1;
+ default:
+ return 0;
+ }
+ }
+
+
+
+ if (activePlaylist.IsMe(fn))
+ {
+ WMInformation getExtendedFileInfo(activePlaylist.GetFileName());
+
+ return GetExtendedInformation(&getExtendedFileInfo, fn, data, dest, destlen);
+ }
+ else
+ {
+ WMInformation getExtendedFileInfo(fn);
+
+ return GetExtendedInformation(&getExtendedFileInfo, fn, data, dest, destlen);
+ }
+
+ #if 0 // had to disable this because it was locking the file from being deleted
+ if (lastGetInfo && lastGetInfoFn && !_wcsicmp(fn, lastGetInfoFn))
+ {
+ return GetExtendedInformation(lastGetInfo, fn, data, dest, destlen);
+ }
+
+ delete lastGetInfo;
+ lastGetInfo=0;
+ free(lastGetInfoFn);
+ lastGetInfoFn=0;
+
+ if (activePlaylist.IsMe(fn))
+ lastGetInfoFn = _wcsdup(activePlaylist.GetFileName());
+ else
+ lastGetInfoFn = _wcsdup(fn);
+
+ lastGetInfo = new WMInformation(lastGetInfoFn);
+ if (lastGetInfo->ErrorOpening())
+ {
+ if (KeywordMatch(data, "type"))
+ {
+ switch (fileTypes.GetAVType(extension(fn)))
+ {
+ case FileType::AUDIO:
+ lstrcpyn(dest, L"0", destlen);
+ return 1;
+ case FileType::VIDEO:
+ lstrcpyn(dest, L"1", destlen);
+ return 1;
+ default:
+ return 0;
+ }
+ }
+
+ delete lastGetInfo;
+ lastGetInfo=0;
+ free(lastGetInfoFn);
+ lastGetInfoFn=0;
+ return 0;
+ }
+
+ return GetExtendedInformation(lastGetInfo, fn, data, dest, destlen);
+ #endif
+}
+
+extern "C" __declspec(dllexport) int winampClearExtendedFileInfoW(const wchar_t *fn)
+{
+ // TODO: press stop if it's the currently playing file
+ WMInformation wmInfo(fn);
+ wmInfo.ClearAllAttributes();
+ wmInfo.Flush();
+ return 1;
+}
+
+extern "C" __declspec(dllexport) int winampSetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *val)
+{
+ // if (!lastSetInfoFilename.empty() && lastSetInfoFilename != fn)
+ // dosomething();
+
+ #if 0 // had to disable this because it was locking the file from being deleted
+ if (lastGetInfoFn && !_wcsicmp(lastGetInfoFn,fn))
+ {
+ delete lastGetInfo;
+ lastGetInfo=0;
+ free(lastGetInfoFn);
+ lastGetInfoFn=0;
+ }
+#endif
+
+ if (!setFileInfo)
+ {
+ if (activePlaylist.IsMe(fn) && mod.playing)
+ {
+ forcedStop = true;
+ outTime = mod.GetOutputTime();
+ winamp.PressStop();
+ }
+ free(setFileInfoName);
+ setFileInfoName = _wcsdup(fn);
+ setFileInfo = new WMInformation(fn);
+ if (!setFileInfo->MakeWritable(fn))
+ return 0; // can't write
+ }
+
+ AutoWide tagNameW(data);
+ const wchar_t *tagName = GetAlias(tagNameW);
+
+ if (StartsWith(tagName, L"WM/"))
+ {
+ setFileInfo->SetAttribute(tagName, val);
+ return 1;
+ }
+
+ else if ((const wchar_t *)tagNameW != tagName) // if the tag was in the tag list
+ {
+ setFileInfo->SetAttribute(tagName, val);
+ return 1;
+ }
+ else if (KeywordMatch(data, "rating"))
+ {
+ int rating = _wtoi(val);
+ if (rating == 0)
+ setFileInfo->SetAttribute(L"WM/SharedUserRating", L"",WMT_TYPE_DWORD);
+ else
+ {
+ switch(rating)
+ {
+ case 1:
+ setFileInfo->SetAttribute(L"WM/SharedUserRating", L"1",WMT_TYPE_DWORD);
+ break;
+ case 2:
+ setFileInfo->SetAttribute(L"WM/SharedUserRating", L"25",WMT_TYPE_DWORD);
+ break;
+ case 3:
+ setFileInfo->SetAttribute(L"WM/SharedUserRating", L"50",WMT_TYPE_DWORD);
+ break;
+ case 4:
+ setFileInfo->SetAttribute(L"WM/SharedUserRating", L"75",WMT_TYPE_DWORD);
+ break;
+ default:
+ setFileInfo->SetAttribute(L"WM/SharedUserRating", L"99",WMT_TYPE_DWORD);
+ break;
+
+ }
+ }
+ }
+ else if (KeywordMatch(data, "replaygain_track_gain")
+ || KeywordMatch(data, "replaygain_track_peak")
+ || KeywordMatch(data, "replaygain_album_gain")
+ || KeywordMatch(data, "replaygain_album_peak"))
+ {
+ setFileInfo->SetAttribute(tagName, val);
+ return 1;
+ }
+ else if (KeywordMatch(data, "GracenoteFileID"))
+ {
+ setFileInfo->SetAttribute_BinString(L"GN/UniqueFileIdentifier", val);
+ return 1;
+ }
+ else if (KeywordMatch(data, "GracenoteExtData"))
+ {
+ setFileInfo->SetAttribute_BinString(L"GN/ExtData", val);
+ return 1;
+ }
+
+ // else if (KeywordMatch(data, "bitrate"))
+ //else if (KeywordMatch(data, "disc"))
+ // else if (KeywordMatch(data, "vbr"))
+ //else if (KeywordMatch(data, "srate"))
+ // else if (KeywordMatch(data, "length"))
+
+ return 0;
+}
+
+extern "C" __declspec(dllexport) int winampWriteExtendedFileInfo()
+{
+ if (setFileInfo)
+ {
+ bool flushOK = setFileInfo->Flush();
+ delete setFileInfo;
+ setFileInfo = 0;
+
+ if (forcedStop)
+ {
+ mod.startAtMilliseconds = outTime;
+ winamp.PressPlay();
+ }
+ forcedStop=false;
+
+ if (flushOK)
+ return 1;
+ else
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/Src/Plugins/Input/in_wmvdrm/ExtendedRead.cpp b/Src/Plugins/Input/in_wmvdrm/ExtendedRead.cpp
new file mode 100644
index 00000000..d07fa008
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/ExtendedRead.cpp
@@ -0,0 +1,240 @@
+#include "main.h"
+#include "ExtendedRead.h"
+
+extern WM_MEDIA_TYPE *NewMediaType(IWMOutputMediaProps *props);
+
+ExtendedReadStruct::ExtendedReadStruct() : buffer(0), reader(0), streamNum(0), bufferUsed(0), endOfFile(false), length(0)
+{}
+
+ExtendedReadStruct::ExtendedReadStruct(IWMSyncReader *_reader) : buffer(0), reader(0), streamNum(0), bufferUsed(0), endOfFile(false), length(0)
+{
+ reader = _reader;
+ reader->AddRef();
+}
+
+#define CBCLASS ExtendedReadStruct
+START_DISPATCH;
+CB(IFC_AUDIOSTREAM_READAUDIO, ReadAudio)
+END_DISPATCH;
+
+ExtendedReadStruct::~ExtendedReadStruct()
+{
+ if (reader)
+ {
+ reader->Close();
+ reader->Release();
+ }
+ if (buffer)
+ buffer->Release();
+}
+
+size_t ExtendedReadStruct::ReadAudio(void *_buffer, size_t len)
+{
+ __int8 *dest = reinterpret_cast<__int8 *>(_buffer);
+ int bytesCopied = 0;
+ /*
+ while we still have bytes left
+ {
+ read a buffer
+ copy buffer to user passed buffer
+ if we have stuff left in the buffer, save it and return
+ if we hit EOF, return
+ }
+ */
+ size_t frameSize = (BitSize()/8)*Channels();
+ len -= (len % frameSize); // only do whole frames
+ while (len)
+ {
+ if (buffer)
+ {
+ BYTE *bufferBytes;
+ DWORD bufferTotal;
+ buffer->GetBufferAndLength(&bufferBytes, &bufferTotal);
+
+ if (bufferUsed < bufferTotal)
+ {
+ size_t toCopy = min(bufferTotal - bufferUsed, len);
+ memcpy(dest, bufferBytes + bufferUsed, toCopy);
+ bufferUsed += toCopy;
+ len -= toCopy;
+ dest += toCopy;
+ bytesCopied += toCopy;
+
+ if (bufferUsed == bufferTotal)
+ {
+ bufferUsed = 0;
+ buffer->Release();
+ buffer = 0;
+ }
+ }
+ }
+ else if (!endOfFile)
+ {
+ QWORD sampleTime, duration;
+ DWORD flags;
+ DWORD outputNum;
+ WORD streamNum2;
+ HRESULT hr;
+ hr = reader->GetNextSample(streamNum, &buffer, &sampleTime, &duration, &flags, & outputNum, &streamNum2);
+ if (hr == NS_E_NO_MORE_SAMPLES)
+ endOfFile = true;
+
+ }
+ else
+ return bytesCopied;
+ }
+
+ return bytesCopied;
+}
+
+bool ExtendedReadStruct::Open(const wchar_t *filename)
+{
+ //mod.InitWM();
+ if (!reader)
+ {
+ if (!SUCCEEDED(WMCreateSyncReader(NULL, 0, &reader)))
+ {
+ reader = 0;
+ return false;
+ }
+ }
+
+ if (SUCCEEDED(reader->Open(filename)))
+ {
+ return true;
+ }
+ else
+ {
+ reader->Release();
+ reader = 0;
+ return false;
+ }
+}
+
+bool ExtendedReadStruct::FindOutput(int bits, int channels)
+{
+ DWORD numOutputs, output, format, numFormats;
+ IWMOutputMediaProps *formatProperties;
+ GUID mediaType;
+ DWORD audioOutputNum;
+
+ if (FAILED((reader->GetOutputCount(&numOutputs))))
+ return false;
+
+ for (output = 0;output < numOutputs;output++)
+ {
+ HRESULT hr;
+ DWORD speakerConfig;
+ switch (channels)
+ {
+ case 1:
+ speakerConfig = DSSPEAKER_MONO;
+ break;
+ case 2:
+ speakerConfig = DSSPEAKER_STEREO;
+ break;
+ case 0:
+ default:
+ speakerConfig = config_audio_num_channels; // TODO: force max channels?
+ }
+
+ hr = reader->SetOutputSetting(output, g_wszSpeakerConfig, WMT_TYPE_DWORD, (BYTE *) & speakerConfig, sizeof(speakerConfig));
+ assert(hr == S_OK);
+
+ BOOL discreteChannels = TRUE;
+ hr = reader->SetOutputSetting(output, g_wszEnableDiscreteOutput, WMT_TYPE_BOOL, (BYTE *) & discreteChannels , sizeof(discreteChannels));
+ assert(hr == S_OK);
+
+ if (FAILED(reader->GetOutputFormatCount(output, &numFormats)))
+ continue;
+
+ for (format = 0;format < numFormats;format++)
+ {
+ reader->GetOutputFormat(output, format, &formatProperties);
+ formatProperties->GetType(&mediaType);
+ if (mediaType != WMMEDIATYPE_Audio)
+ continue;
+
+ WM_MEDIA_TYPE *mediaType = NewMediaType(formatProperties);
+ if (mediaType->subtype == WMMEDIASUBTYPE_PCM)
+ {
+ if (bits)
+ {
+ WAVEFORMATEXTENSIBLE *waveFormat = (WAVEFORMATEXTENSIBLE *) mediaType->pbFormat;
+ if (waveFormat->Format.cbSize >= 22)
+ waveFormat->Samples.wValidBitsPerSample = bits;
+ waveFormat->Format.wBitsPerSample = bits;
+ waveFormat->Format.nBlockAlign = (waveFormat->Format.wBitsPerSample / 8) * waveFormat->Format.nChannels;
+ waveFormat->Format.nAvgBytesPerSec = waveFormat->Format.nSamplesPerSec * waveFormat->Format.nBlockAlign;
+ if (FAILED(formatProperties->SetMediaType(mediaType)))
+ {
+ // blah, just use the default settings then
+ delete[] mediaType;
+ mediaType = NewMediaType(formatProperties);
+ }
+ }
+
+ AudioFormat::Open(mediaType);
+ delete[] mediaType;
+ audioOutputNum = output;
+ reader->SetOutputProps(audioOutputNum, formatProperties);
+ reader->GetStreamNumberForOutput(audioOutputNum, &streamNum);
+ formatProperties->Release();
+ reader->SetReadStreamSamples(streamNum, FALSE);
+
+ IWMHeaderInfo *headerInfo = 0;
+ reader->QueryInterface(&headerInfo);
+
+ WMT_ATTR_DATATYPE type = WMT_TYPE_QWORD;
+ WORD byteLength = sizeof(QWORD);
+ WORD blah = 0;
+ headerInfo->GetAttributeByName(&blah, g_wszWMDuration, &type, (BYTE *)&length, &byteLength);
+
+ headerInfo->Release();
+
+ return true;
+ }
+
+ delete[] mediaType;
+ formatProperties->Release();
+ }
+ }
+ return false;
+}
+
+extern "C"
+ __declspec(dllexport) intptr_t winampGetExtendedRead_openW(const wchar_t *fn, int *size, int *bps, int *nch, int *srate)
+{
+ ExtendedReadStruct *read = new ExtendedReadStruct;
+
+ if (read->Open(fn)
+ && read->FindOutput(*bps, *nch))
+ {
+ *nch = read->Channels();
+ *bps = read->BitSize();
+ *srate = read->SampleRate();
+ __int64 bytespersec = ((*nch) * (*bps / 8) * (*srate));
+ __int64 s = (bytespersec * ((__int64)read->length)) / (__int64)(1000 * 10000);
+ *size = (int)s;
+ return reinterpret_cast<intptr_t>(read);
+ }
+ else
+ {
+ delete read;
+ return 0;
+ }
+}
+
+extern "C"
+ __declspec(dllexport) intptr_t winampGetExtendedRead_getData(intptr_t handle, char *dest, int len, int *killswitch)
+{
+ ExtendedReadStruct *read = reinterpret_cast<ExtendedReadStruct *>(handle);
+ return read->ReadAudio(dest, len);
+}
+
+extern "C"
+ __declspec(dllexport) void winampGetExtendedRead_close(intptr_t handle)
+{
+ ExtendedReadStruct *read = reinterpret_cast<ExtendedReadStruct *>(handle);
+ delete read;
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/ExtendedRead.h b/Src/Plugins/Input/in_wmvdrm/ExtendedRead.h
new file mode 100644
index 00000000..1e9cd063
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/ExtendedRead.h
@@ -0,0 +1,30 @@
+#ifndef NULLSOFT_IN_WMVDRM_EXTENDEDREAD_H
+#define NULLSOFT_IN_WMVDRM_EXTENDEDREAD_H
+
+#include "AudioFormat.h"
+#include "../Agave/DecodeFile/ifc_audiostream.h"
+#include "main.h"
+
+struct ExtendedReadStruct : public AudioFormat, public ifc_audiostream
+{
+public:
+ ExtendedReadStruct();
+ ExtendedReadStruct(IWMSyncReader *_reader);
+ ~ExtendedReadStruct();
+
+ bool Open(const wchar_t *filename);
+ bool FindOutput(int bits, int channels);
+ size_t ReadAudio(void *buffer, size_t sizeBytes);
+
+ IWMSyncReader *reader;
+ WORD streamNum;
+
+ INSSBuffer *buffer;
+ size_t bufferUsed;
+ bool endOfFile;
+ QWORD length;
+protected:
+ RECVS_DISPATCH;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/FactoryHelper.h b/Src/Plugins/Input/in_wmvdrm/FactoryHelper.h
new file mode 100644
index 00000000..aeae00b5
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/FactoryHelper.h
@@ -0,0 +1,24 @@
+#include "api.h"
+#include <api/service/waservicefactory.h>
+
+template <class api_T>
+void ServiceBuild(api_T *&api_t, GUID factoryGUID_t)
+{
+ if (plugin.service)
+ {
+ waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);
+ if (factory)
+ api_t = (api_T *)factory->getInterface();
+ }
+}
+
+template <class api_T>
+void ServiceRelease(api_T *api_t, GUID factoryGUID_t)
+{
+ if (plugin.service)
+ {
+ waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);
+ if (factory)
+ factory->releaseInterface(api_t);
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/FileInfoDialog.cpp b/Src/Plugins/Input/in_wmvdrm/FileInfoDialog.cpp
new file mode 100644
index 00000000..eb0965f1
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/FileInfoDialog.cpp
@@ -0,0 +1,831 @@
+#include "Main.h"
+#include "FileInfoDialog.h"
+#include "WMInformation.h"
+#include "resource.h"
+#include "../nu/AutoChar.h"
+
+#include "WMDRMModule.h"
+#include "WMPlaylist.h"
+
+// blah! commented out a load of shit because we can't have the advanced pane edit data cause wma sucks.
+
+class Info
+{
+public:
+ Info(const wchar_t *filename);
+ ~Info();
+ //bool Save(HWND parent);
+ int Error();
+ int GetNumMetadataItems();
+ void EnumMetadata(int n,wchar_t *key,int keylen, wchar_t *val, int vallen);
+ //void RemoveMetadata(wchar_t * key);
+ //void RemoveMetadata(int n);
+ //void SetMetadata(wchar_t *key, wchar_t *val);
+ //void SetMetadata(int n, wchar_t *key, wchar_t *val);
+ //void SetTag(int n,wchar_t *key); // changes the key name
+private:
+ WMInformation wminfo;
+ const wchar_t *filename;
+};
+
+Info::Info(const wchar_t *filename) : wminfo(filename), filename(filename)
+{
+}
+
+Info::~Info()
+{
+}
+/*
+bool Info::Save(HWND parent)
+{
+ if (!wminfo.MakeWritable(filename))
+ {
+ wchar_t title[64] = {0};
+ if (activePlaylist.IsMe(filename) && mod.playing)
+ {
+ // TODO: this is a race condition. we might have stopped in between the above if () and now...
+ int outTime = mod.GetOutputTime();
+ winamp.PressStop();
+ wminfo.MakeWritable(filename);
+
+ SendMessage(parent,WM_USER+1,0,0);
+ //WriteEditBoxes();
+
+ wminfo.Flush();
+ wminfo.MakeReadOnly(filename);
+ mod.startAtMilliseconds=outTime;
+ winamp.PressPlay();
+ return true;
+ }
+ else if (!wminfo.MakeReadOnly(filename))
+ MessageBox(parent, WASABI_API_LNGSTRINGW(IDS_UNABLE_TO_WRITE_TO_FILE_DOES_FILE_EXIST),
+ WASABI_API_LNGSTRINGW_BUF(IDS_UNABLE_TO_WRITE_TO_FILE,title,64), MB_OK);
+ else
+ MessageBox(parent, WASABI_API_LNGSTRINGW(IDS_UNABLE_TO_WRITE_TO_FILE_MAY_NOT_HAVE_ACCESS),
+ WASABI_API_LNGSTRINGW_BUF(IDS_UNABLE_TO_WRITE_TO_FILE,title,64), MB_OK);
+
+ return false;
+ }
+
+ SendMessage(parent,WM_USER+1,0,0);
+ //WriteEditBoxes();
+
+ if (!wminfo.Flush())
+ {
+ wchar_t* title = WASABI_API_LNGSTRINGW(IDS_SAVE_FAILED);
+ MessageBox(NULL, title, title, MB_OK);
+ }
+
+ wminfo.MakeReadOnly(filename);
+ return true;
+}
+*/
+int Info::Error()
+{
+ return wminfo.ErrorOpening();
+}
+
+int Info::GetNumMetadataItems()
+{
+ return wminfo.GetNumberAttributes();
+}
+
+void Info::EnumMetadata(int n,wchar_t *key,int keylen, wchar_t *val, int vallen)
+{
+ if(keylen) key[0]=0;
+ if(vallen) val[0]=0;
+ wminfo.GetAttribute(n, key, keylen, val, vallen);
+}
+/*
+void Info::RemoveMetadata(wchar_t * key)
+{
+ wminfo.DeleteAttribute(key);
+}
+
+void Info::RemoveMetadata(int n)
+{
+ wchar_t key[256] = {0};
+ EnumMetadata(n,key,256,NULL,0);
+ if(key[0])
+ RemoveMetadata(key);
+}
+
+void Info::SetMetadata(wchar_t *key, wchar_t *val)
+{
+ wminfo.SetAttribute(key,val);
+}
+
+void Info::SetTag(int n, wchar_t *key)
+{ // changes the key name
+ wchar_t val[2048]=L"";
+ wchar_t oldkey[256]=L"";
+ EnumMetadata(n,oldkey,256,val,2048);
+ RemoveMetadata(oldkey);
+ wminfo.SetAttribute(key,val);
+}
+*/
+bool WMTagToWinampTag(wchar_t * tag, int len)
+{
+ const wchar_t *f = GetAlias_rev(tag);
+ if(f)
+ {
+ lstrcpyn(tag,f,len);
+ return true;
+ }
+ return false;
+}
+
+bool WinampTagToWMTag(wchar_t * tag, int len)
+{
+ const wchar_t *f = GetAlias(tag);
+ if(f)
+ {
+ lstrcpyn(tag,f,len);
+ return true;
+ }
+ return false;
+}
+
+static INT_PTR CALLBACK ChildProc_Advanced(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
+ static int ismychange=0;
+ switch(msg)
+ {
+ case WM_NOTIFYFORMAT:
+ return NFR_UNICODE;
+ case WM_INITDIALOG:
+ {
+ //SetWindowLongPtr(hwndDlg,GWLP_USERDATA,lParam);
+ HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST);
+ ListView_SetExtendedListViewStyle(hwndlist, LVS_EX_FULLROWSELECT);
+ LVCOLUMN lvc = {0, };
+ lvc.mask = LVCF_TEXT|LVCF_WIDTH;
+ lvc.pszText = WASABI_API_LNGSTRINGW(IDS_NAME);
+ lvc.cx = 82;
+ ListView_InsertColumn(hwndlist, 0, &lvc);
+ lvc.pszText = WASABI_API_LNGSTRINGW(IDS_VALUE);
+ lvc.cx = 160;
+ ListView_InsertColumn(hwndlist, 1, &lvc);
+
+ Info *info = (Info *)lParam;
+ int n = info->GetNumMetadataItems();
+ for(int i=0; i<n; i++) {
+ wchar_t key[512] = {0};
+ wchar_t value[2048] = {0};
+ info->EnumMetadata(i,key,512,value,2048);
+ if(key[0]) {
+ LVITEMW lvi={LVIF_TEXT,i,0};
+ lvi.pszText = key;
+ SendMessage(hwndlist,LVM_INSERTITEMW,0,(LPARAM)&lvi);
+ lvi.iSubItem=1;
+ lvi.pszText = value;
+ 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);
+
+ delete info;
+ }
+ 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);
+ if(info) delete info;
+ }
+ break;
+ /*
+ case WM_USER+1:
+ {
+ Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ info->wminfo.ClearAllAttributes();
+ wchar_t key[100] = {0}, value[2048] = {0};
+ HWND hlist = GetDlgItem(hwndDlg,IDC_LIST);
+ int n = ListView_GetItemCount(hlist);
+ for(int i=0; i<n; i++)
+ {
+ key[0]=value[0]=0;
+ LVITEMW lvi={LVIF_TEXT,i,0};
+ lvi.pszText=key;
+ lvi.cchTextMax=100;
+ SendMessage(hlist,LVM_GETITEMW,0,(LPARAM)&lvi);
+ lvi.iSubItem = 1;
+ lvi.pszText = value;
+ lvi.cchTextMax=2048;
+ SendMessage(hlist,LVM_GETITEMW,0,(LPARAM)&lvi);
+ if(key[0])
+ info->SetMetadata(key,value);
+ }
+ }
+ 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);
+ WinampTagToWMTag(tag,100);
+ /*
+ Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if(!*value) info->RemoveMetadata(tag);
+ else info->SetMetadata(tag,value);
+ */
+ HWND hlist = GetDlgItem(hwndDlg,IDC_LIST);
+ int n = ListView_GetItemCount(hlist);
+ for(int i=0; i<n; i++)
+ {
+ wchar_t key[100]=L"";
+ LVITEMW lvi={LVIF_TEXT,i,0};
+ lvi.pszText=key;
+ lvi.cchTextMax=100;
+ 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_ITEMCHANGED) {
+ LPNMLISTVIEW lv=(LPNMLISTVIEW)lParam;
+ if(lv->uNewState & LVIS_SELECTED) {
+ int n = lv->iItem;
+ LVITEMW lvi={LVIF_TEXT,lv->iItem,0};
+ wchar_t key[100] = {0};
+ wchar_t value[1024] = {0};
+ lvi.pszText=key;
+ lvi.cchTextMax=100;
+ SendMessage(l->hwndFrom,LVM_GETITEMW,0,(LPARAM)&lvi);
+ lvi.pszText=value;
+ lvi.cchTextMax=1024;
+ 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) {
+ int n = lv->iItem;
+ 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);
+ //info->Save(hwndDlg);
+ }
+ break;
+ /*
+ case IDC_NAME:
+ case IDC_VALUE:
+ if(HIWORD(wParam) == EN_CHANGE && sel>=0) {
+ wchar_t key[100] = {0};
+ wchar_t value[1024] = {0};
+ LVITEMW lvi={LVIF_TEXT,sel,0};
+ GetDlgItemTextW(hwndDlg,IDC_NAME,key,100);
+ GetDlgItemTextW(hwndDlg,IDC_VALUE,value,1024);
+ lvi.pszText=key;
+ lvi.cchTextMax=100;
+ SendMessage(GetDlgItem(hwndDlg,IDC_LIST),LVM_SETITEMW,0,(LPARAM)&lvi);
+ lvi.pszText=value;
+ lvi.cchTextMax=1024;
+ lvi.iSubItem=1;
+ SendMessage(GetDlgItem(hwndDlg,IDC_LIST),LVM_SETITEMW,0,(LPARAM)&lvi);
+ WMTagToWinampTag(key,100);
+ ismychange=1;
+ SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)value);
+ ismychange=0;
+ }
+ else if(HIWORD(wParam) == EN_KILLFOCUS && sel>=0) {
+ wchar_t key[100] = {0};
+ wchar_t value[1024] = {0};
+ GetDlgItemTextW(hwndDlg,IDC_NAME,key,100);
+ GetDlgItemTextW(hwndDlg,IDC_VALUE,value,1024);
+ Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ wchar_t oldkey[100]=L"";
+ bool newitem=true;
+ if(sel < info->GetNumMetadataItems()) {
+ info->EnumMetadata(sel,oldkey,100,0,0);
+ newitem=false;
+ }
+
+ if(!newitem && wcscmp(oldkey,key)) { // key changed
+ info->SetTag(sel,key);
+ } else {
+ info->SetMetadata(key,value);
+ }
+ WMTagToWinampTag(key,100);
+ ismychange=1;
+ SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)value);
+ ismychange=0;
+ }
+ break;
+ case IDC_BUTTON_DEL:
+ if(sel >= 0) {
+ wchar_t tag[100] = {0};
+ GetDlgItemTextW(hwndDlg,IDC_NAME,tag,100);
+ 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->GetNumMetadataItems())
+ info->RemoveMetadata(sel);
+ ListView_DeleteItem(GetDlgItem(hwndDlg,IDC_LIST),sel);
+ sel=-1;
+ WMTagToWinampTag(tag,100);
+ ismychange=1;
+ SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)tag,(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->GetNumMetadataItems();
+ while(n>0) {
+ --n;
+ wchar_t tag[100] = {0};
+ info->EnumMetadata(n,tag,100,0,0);
+ WMTagToWinampTag(tag,100);
+ ismychange=0;
+ SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)tag,(WPARAM)L"");
+ ismychange=1;
+ info->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 *info = new Info(filename);
+ if(info->Error())
+ {
+ delete info;
+ return NULL;
+ }
+ return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO,parent,ChildProc_Advanced,(LPARAM)info);
+ }
+ return NULL;
+ }
+
+};
+
+/* CUT> we're now using the unified file info dlg. I'll leave this commented out incase we want to do an advanced tab later on.
+
+#define CREATEDIALOGBOX DialogBoxParam
+#define CLOSEDIALOGBOX(x) EndDialog(x, 0)
+extern WMDRM mod;
+enum
+{
+ AttributeColumn = 0,
+ ValueColumn = 1,
+};
+
+void FileInfoDialog::FillAttributeList()
+{
+ attributeList.Clear();
+ WORD attrCount = wmInfo->GetNumberAttributes();
+ wchar_t attrName[32768] = {0}, value[32768] = {0};
+ int pos=0;
+ for (WORD i = 0;i != attrCount;i++)
+ {
+ wmInfo->GetAttribute(i, attrName, 32768, value, 32768);
+ // if (!AttributeInStandardEditor(attrName.c_str()))
+ attributeList.InsertItem(pos, (wchar_t *)attrName, 0);
+ attributeList.SetItemText(pos, 1, (wchar_t *)value);
+ pos++;
+ }
+
+ attributeList.AutoColumnWidth(1);
+}
+
+void FileInfoDialog::FillEditBoxes()
+{
+ wchar_t temp[32768] = {0};
+
+ wmInfo->GetAttribute(g_wszWMAuthor, temp, 32768);
+ SetWindowText(GetDlgItem(fileInfoHWND, IDC_EDIT_ARTIST), temp);
+
+ wmInfo->GetAttribute(g_wszWMTitle, temp, 32768);
+ SetWindowText(GetDlgItem(fileInfoHWND, IDC_EDIT_TITLE), temp);
+
+ wmInfo->GetAttribute(g_wszWMAlbumTitle, temp, 32768);
+ SetWindowText(GetDlgItem(fileInfoHWND, IDC_EDIT_ALBUM), temp);
+
+ wmInfo->GetAttribute(g_wszWMDescription, temp, 32768);
+ SetWindowText(GetDlgItem(fileInfoHWND, IDC_EDIT_COMMENTS), temp);
+
+ wmInfo->GetAttribute(g_wszWMGenre, temp, 32768);
+ SetWindowText(GetDlgItem(fileInfoHWND, IDC_EDIT_GENRE), temp);
+
+ wmInfo->GetAttribute(g_wszWMYear, temp, 32768);
+ SetWindowText(GetDlgItem(fileInfoHWND, IDC_EDIT_YEAR), temp);
+
+ wmInfo->GetAttribute(g_wszWMTrackNumber, temp, 32768);
+ SetWindowText(GetDlgItem(fileInfoHWND, IDC_EDIT_TRACK), temp);
+
+ wmInfo->GetAttribute(g_wszWMPublisher, temp, 32768);
+ SetWindowText(GetDlgItem(fileInfoHWND, IDC_EDIT_PUBLISHER), temp);
+
+ wmInfo->GetAttribute(g_wszWMAlbumArtist, temp, 32768);
+ SetWindowText(GetDlgItem(fileInfoHWND, IDC_EDIT_ALBUMARTIST), temp);
+}
+
+void FileInfoDialog::Init(HWND _hwnd)
+{
+ fileInfoHWND = _hwnd;
+
+ attributeList.setwnd(GetDlgItem(fileInfoHWND, IDC_METADATALIST));
+
+ attributeList.AddCol(WASABI_API_LNGSTRINGW(IDS_ATTRIBUTE), 150);
+ attributeList.AddCol(WASABI_API_LNGSTRINGW(IDS_VALUE), 1);
+ attributeList.AutoColumnWidth(1);
+
+ if (fileNameToShow)
+ SetWindowText(GetDlgItem(fileInfoHWND, IDC_FILENAME), fileNameToShow);
+ else
+ SetWindowText(GetDlgItem(fileInfoHWND, IDC_FILENAME), fileName);
+ FillEditBoxes();
+ FillAttributeList();
+
+ if (wmInfo->NonWritable())
+ {
+ EnableWindow(GetDlgItem(fileInfoHWND, IDC_APPLY), FALSE);
+ EnableWindow(GetDlgItem(fileInfoHWND, IDC_REVERT), FALSE);
+ EnableWindow(GetDlgItem(fileInfoHWND, IDOK), FALSE);
+ SetDlgItemText(fileInfoHWND, IDCANCEL, WASABI_API_LNGSTRINGW(IDS_CLOSE));
+ SendMessage(GetDlgItem(fileInfoHWND, IDC_EDIT_TITLE), EM_SETREADONLY, TRUE, 0);
+ SendMessage(GetDlgItem(fileInfoHWND, IDC_EDIT_ARTIST), EM_SETREADONLY, TRUE, 0);
+ SendMessage(GetDlgItem(fileInfoHWND, IDC_EDIT_ALBUM), EM_SETREADONLY, TRUE, 0);
+ SendMessage(GetDlgItem(fileInfoHWND, IDC_EDIT_COMMENTS), EM_SETREADONLY, TRUE, 0);
+ SendMessage(GetDlgItem(fileInfoHWND, IDC_EDIT_GENRE), EM_SETREADONLY, TRUE, 0);
+ SendMessage(GetDlgItem(fileInfoHWND, IDC_EDIT_YEAR), EM_SETREADONLY, TRUE, 0);
+ SendMessage(GetDlgItem(fileInfoHWND, IDC_EDIT_TRACK), EM_SETREADONLY, TRUE, 0);
+ SendMessage(GetDlgItem(fileInfoHWND, IDC_EDIT_PUBLISHER), EM_SETREADONLY, TRUE, 0);
+ SendMessage(GetDlgItem(fileInfoHWND, IDC_EDIT_ALBUMARTIST), EM_SETREADONLY, TRUE, 0);
+ }
+ SetFocus(GetDlgItem(fileInfoHWND, IDCANCEL));
+ SendMessage(fileInfoHWND, DM_SETDEFID, GetDlgCtrlID(GetDlgItem(fileInfoHWND, IDCANCEL)),0);
+}
+
+void FileInfoDialog::Revert()
+{
+ FillEditBoxes();
+ FillAttributeList();
+}
+
+BOOL FileInfoDialog::MetadataList_Notify(NMHDR *header)
+{
+ switch (header->code)
+ {
+
+ case LVN_ITEMCHANGED:
+ {
+ LPNMLISTVIEW lvNotif = (LPNMLISTVIEW)header;
+ if ((lvNotif->uOldState & LVIS_SELECTED)
+ && !(lvNotif->uNewState & LVIS_SELECTED))
+ {
+ EnableWindow(GetDlgItem(fileInfoHWND, IDC_EDIT),0);
+ EnableWindow(GetDlgItem(fileInfoHWND, IDC_DELETE), 0);
+ }
+ if (lvNotif->uNewState & LVIS_SELECTED)
+ {
+ if (lvNotif->iItem != -1)
+ {
+ EnableWindow(GetDlgItem(fileInfoHWND, IDC_EDIT),1);
+ EnableWindow(GetDlgItem(fileInfoHWND, IDC_DELETE), 1);
+ }
+
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+bool FileInfoDialog::AttributeInStandardEditor(const wchar_t *attrName)
+{
+ return (!wcscmp(attrName, g_wszWMTitle)
+ ||!wcscmp(attrName, g_wszWMAuthor)
+ ||!wcscmp(attrName, g_wszWMAlbumTitle)
+ ||!wcscmp(attrName, g_wszWMDescription)
+ ||!wcscmp(attrName, g_wszWMGenre)
+ ||!wcscmp(attrName, g_wszWMYear)
+ ||!wcscmp(attrName, g_wszWMTrackNumber)
+ ||!wcscmp(attrName, g_wszWMPublisher)
+ || !wcscmp(attrName, g_wszWMAlbumArtist));
+}
+
+void FileInfoDialog::WriteEditBoxHelper(const wchar_t attrName[], DWORD IDC, wchar_t *&temp, int &size)
+{
+ int thisSize = GetWindowTextLength(GetDlgItem(fileInfoHWND, IDC))+1;
+ if (thisSize && thisSize>size)
+ {
+ if (temp)
+ delete[] temp;
+ temp = new wchar_t[thisSize];
+ size=thisSize;
+
+ }
+
+ GetWindowText(GetDlgItem(fileInfoHWND, IDC), temp, size);
+ wmInfo->SetAttribute(attrName, temp);
+}
+
+void FileInfoDialog::WriteEditBoxes()
+{
+ wchar_t *temp=0;
+ int thisSize=0;
+ int size=0;
+
+ WriteEditBoxHelper(g_wszWMTitle, IDC_EDIT_TITLE, temp, size);
+ WriteEditBoxHelper(g_wszWMAuthor, IDC_EDIT_ARTIST, temp, size);
+ WriteEditBoxHelper(g_wszWMAlbumTitle, IDC_EDIT_ALBUM, temp, size);
+ WriteEditBoxHelper(g_wszWMDescription, IDC_EDIT_COMMENTS, temp, size);
+ WriteEditBoxHelper(g_wszWMGenre, IDC_EDIT_GENRE, temp, size);
+ WriteEditBoxHelper(g_wszWMYear, IDC_EDIT_YEAR, temp, size);
+ WriteEditBoxHelper(g_wszWMTrackNumber, IDC_EDIT_TRACK, temp, size);
+ WriteEditBoxHelper(g_wszWMPublisher, IDC_EDIT_PUBLISHER, temp, size);
+ WriteEditBoxHelper(g_wszWMAlbumArtist, IDC_EDIT_ALBUMARTIST, temp, size);
+}
+
+void FileInfoDialog::WriteAttributeListA()
+{
+ int attributeTextLength=0, valueTextLength=0;
+ char *attribute=0, *value=0;
+ size_t numAttrs = attributeList.GetCount();
+ for (size_t i=0;i!=numAttrs;i++)
+ {
+ int textLength;
+ textLength = attributeList.GetTextLength(i, 0);
+ if (textLength>attributeTextLength)
+ {
+ if (attribute)
+ delete[] attribute;
+ attribute = new char[textLength];
+ attributeTextLength=textLength;
+ }
+ attributeList.GetText(i, 0, attribute, attributeTextLength);
+
+ textLength = attributeList.GetTextLength(i, 0);
+ if (textLength>valueTextLength)
+ {
+ if (value)
+ delete[] value;
+ value = new char[textLength];
+ valueTextLength=textLength;
+ }
+ attributeList.GetText(i, 0, value, valueTextLength);
+ }
+}
+
+void FileInfoDialog::WriteAttributeList()
+{
+ int attributeTextLength=0, valueTextLength=0;
+ wchar_t *attribute=0, *value=0;
+ size_t numAttrs = attributeList.GetCount();
+ for (size_t i=0;i!=numAttrs;i++)
+ {
+ int textLength;
+ textLength = attributeList.GetTextLength(i, 0);
+ if (textLength>attributeTextLength)
+ {
+ if (attribute)
+ delete[] attribute;
+ attribute = new wchar_t[textLength];
+ attributeTextLength=textLength;
+ }
+ attributeList.GetText(i, 0, attribute, attributeTextLength);
+
+ textLength = attributeList.GetTextLength(i, 0);
+ if (textLength>valueTextLength)
+ {
+ if (value)
+ delete[] value;
+ value = new wchar_t[textLength];
+ valueTextLength=textLength;
+ }
+ attributeList.GetText(i, 0, value, valueTextLength);
+ }
+}
+
+bool FileInfoDialog::Apply()
+{
+ edited=true;
+ if (!wmInfo->MakeWritable(fileName))
+ {
+ wchar_t title[64] = {0};
+ if (activePlaylist.IsMe(fileName) && mod.playing)
+ {
+ // TODO: this is a race condition. we might have stopped in between the above if () and now...
+ int outTime = mod.GetOutputTime();
+ winamp.PressStop();
+ wmInfo->MakeWritable(fileName);
+ WriteEditBoxes();
+ wmInfo->Flush();
+ wmInfo->MakeReadOnly(fileName);
+ mod.startAtMilliseconds=outTime;
+ winamp.PressPlay();
+ return true;
+ }
+ else if (!wmInfo->MakeReadOnly(fileName))
+ MessageBox(fileInfoHWND, WASABI_API_LNGSTRINGW(IDS_UNABLE_TO_WRITE_TO_FILE_DOES_FILE_EXIST),
+ WASABI_API_LNGSTRINGW_BUF(IDS_UNABLE_TO_WRITE_TO_FILE,title,64), MB_OK);
+ else
+ MessageBox(fileInfoHWND, WASABI_API_LNGSTRINGW(IDS_UNABLE_TO_WRITE_TO_FILE_MAY_NOT_HAVE_ACCESS),
+ WASABI_API_LNGSTRINGW_BUF(IDS_UNABLE_TO_WRITE_TO_FILE,title,64), MB_OK);
+
+ return false;
+ }
+
+ WriteEditBoxes();
+ if (!wmInfo->Flush())
+ {
+ wchar_t* title = WASABI_API_LNGSTRINGW(IDS_SAVE_FAILED);
+ MessageBox(NULL, title, title, MB_OK);
+ }
+
+ wmInfo->MakeReadOnly(fileName);
+ return true;
+}
+
+BOOL FileInfoDialog::OnOk()
+{
+ if (Apply())
+ CLOSEDIALOGBOX(fileInfoHWND);
+ return 0;
+}
+
+BOOL FileInfoDialog::OnCancel()
+{
+ CLOSEDIALOGBOX(fileInfoHWND);
+ return 0;
+}
+
+INT_PTR WINAPI FileInfoDialog::FileInfoProc(HWND wnd, UINT msg, WPARAM wp, LPARAM lp)
+{
+ FileInfoDialog *fileInfoDialog = (FileInfoDialog *)GetWindowLongPtr(wnd, GWLP_USERDATA);
+ switch (msg)
+ {
+ case WM_NOTIFYFORMAT:
+ return NFR_UNICODE;
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR l = (LPNMHDR)lp;
+
+ if (l->hwndFrom == GetDlgItem(wnd, IDC_METADATALIST))
+ return fileInfoDialog->MetadataList_Notify(l);
+ }
+ break;
+
+ case WM_INITDIALOG:
+ SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)lp);
+ fileInfoDialog = (FileInfoDialog *)lp;
+ fileInfoDialog->Init(wnd);
+ return FALSE;
+ case WM_DESTROY:
+ if (fileInfoDialog)
+ {
+ //delete fileInfoDialog;
+ //fileInfoDialog=0;
+ SetWindowLongPtr(wnd, GWLP_USERDATA, 0);
+ }
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wp))
+ {
+ case IDC_REVERT:
+ fileInfoDialog->Revert();
+ break;
+ case IDCANCEL:
+ return fileInfoDialog->OnCancel();
+ break;
+ case IDOK:
+ return fileInfoDialog->OnOk();
+ break;
+ case IDC_APPLY:
+ fileInfoDialog->Apply();
+ break;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+FileInfoDialog::FileInfoDialog(HINSTANCE _hInstance, HWND parent,const wchar_t *_fileName)
+: wmInfo(0),hInstance(_hInstance),fileName(0),fileNameToShow(0), edited(false)
+{
+ if (activePlaylist.IsMe(_fileName))
+ {
+ fileName = _wcsdup(activePlaylist.GetFileName());
+ fileNameToShow = _wcsdup(_fileName);
+ }
+ else
+ fileName = _wcsdup(_fileName);
+
+ wmInfo = new WMInformation(fileName);
+ CREATEDIALOGBOX(hInstance, MAKEINTRESOURCE(IDD_FILEINFO), parent, FileInfoProc, (LPARAM)this);
+}
+
+FileInfoDialog::~FileInfoDialog()
+{
+ delete wmInfo;
+ wmInfo=0;
+ free(fileName);
+ free(fileNameToShow);
+}
+
+bool FileInfoDialog::WasEdited()
+{
+ return edited;
+}
+*/ \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/FileInfoDialog.h b/Src/Plugins/Input/in_wmvdrm/FileInfoDialog.h
new file mode 100644
index 00000000..76d9b7d9
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/FileInfoDialog.h
@@ -0,0 +1,41 @@
+#ifndef NULLSOFT_FILEINFODIALOGH
+#define NULLSOFT_FILEINFODIALOGH
+
+#include "../nu/listview.h"
+#include "WMInformation.h"
+/* CUT> we're now using the unified file info dlg. I'll leave this commented out incase we want to do an advanced tab later on.
+class FileInfoDialog
+{
+public:
+ FileInfoDialog(HINSTANCE _hInstance, HWND parent, const wchar_t *fileName);
+ ~FileInfoDialog();
+ void Init(HWND _hwnd);
+ static INT_PTR WINAPI FileInfoProc(HWND wnd, UINT msg, WPARAM wp, LPARAM lp);
+ BOOL MetadataList_Notify(NMHDR *header);
+ BOOL Edit_Notify(NMHDR *header);
+ BOOL OnOk();
+ BOOL OnCancel();
+ bool WasEdited();
+private:
+ void FillAttributeList();
+ void WriteAttributeList();
+ void WriteAttributeListA();
+ void FillEditBoxes();
+ void WriteEditBoxes();
+ bool Apply();
+ void Revert();
+ void FileInfoDialog::WriteEditBoxHelper(const wchar_t attrName[], DWORD IDC, wchar_t *&temp, int &size);
+ bool AttributeInStandardEditor(const wchar_t *attrName);
+ HWND fileInfoHWND;
+ WMInformation *wmInfo;
+ W_ListView attributeList;
+ HINSTANCE hInstance;
+
+ wchar_t *fileName;
+ wchar_t *fileNameToShow;
+
+ bool edited;
+
+};
+*/
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/FileTypes.cpp b/Src/Plugins/Input/in_wmvdrm/FileTypes.cpp
new file mode 100644
index 00000000..839ae21b
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/FileTypes.cpp
@@ -0,0 +1,179 @@
+#include "main.h"
+#include "FileTypes.h"
+#include "Config.h"
+#include "../nu/Config.h"
+#include "resource.h"
+#include <strsafe.h>
+
+FileTypes fileTypes;
+extern Nullsoft::Utility::Config wmConfig;
+
+bool FileTypes::IsSupportedURL(const wchar_t *fn)
+{
+ Nullsoft::Utility::AutoLock lock(typeGuard);
+ TypeList::iterator itr;
+ for (itr=types.begin();itr!=types.end();itr++)
+ {
+ if (itr->isProtocol && !_wcsnicmp(fn, itr->type, wcslen(itr->type)))
+ return true;
+ }
+ return false;
+}
+
+bool FileTypes::IsDefault()
+{
+ return true;
+}
+
+void FileTypes::LoadDefaults()
+{
+ Nullsoft::Utility::AutoLock lock(typeGuard);
+ types.clear();
+ types.push_back(FileType(L"WMA", WASABI_API_LNGSTRINGW(IDS_WMA_AUDIO_FILE), false, FileType::AUDIO));
+ if (!config_no_video)
+ {
+ types.push_back(FileType(L"WMV", WASABI_API_LNGSTRINGW(IDS_WMA_VIDEO_FILE), false, FileType::VIDEO));
+ types.push_back(FileType(L"ASF", WASABI_API_LNGSTRINGW(IDS_ASF_STREAM), false, FileType::VIDEO));
+ }
+ types.push_back(FileType(L"MMS://", WASABI_API_LNGSTRINGW(IDS_WINDOWS_MEDIA_STREAM), true, FileType::VIDEO));
+ types.push_back(FileType(L"MMSU://", WASABI_API_LNGSTRINGW(IDS_WINDOWS_MEDIA_STREAM), true, FileType::VIDEO));
+ types.push_back(FileType(L"MMST://", WASABI_API_LNGSTRINGW(IDS_WINDOWS_MEDIA_STREAM), true, FileType::VIDEO));
+}
+
+void FileTypes::ReadConfig()
+{
+ Nullsoft::Utility::AutoLock lock(typeGuard);
+ int numTypes = wmConfig.cfg_int(L"numtypes", -1);
+ if (numTypes!=-1)
+ {
+ for (size_t i=0;i!=numTypes;i++)
+ {
+ wchar_t type[1024] = {0}, description[1024] = {0}, temp[64] = {0};
+
+ StringCchPrintf(temp, 64, L"type%u", i);
+ wmConfig.cfg_str(temp).GetString(type, 1024);
+ StringCchPrintf(temp, 64, L"description%u", i);
+ wmConfig.cfg_str(temp).GetString(description, 1024);
+ StringCchPrintf(temp, 64, L"protocol%u", i);
+ bool protocol = !!wmConfig.cfg_int(temp, 0);
+ StringCchPrintf(temp, 64, L"avtype%u", i);
+ int avtype = !!wmConfig.cfg_int(temp, 0);
+ if (!(config_no_video && !protocol && avtype==FileType::VIDEO)) // if we havn't explicity disabled video support
+ types.push_back(FileType(type, description, protocol, avtype));
+ }
+ }
+ else
+ fileTypes.LoadDefaults();
+
+ ResetTypes();
+ numTypes=types.size();
+ for (size_t i=0;i!=numTypes;i++)
+ {
+ if (!types[i].isProtocol)
+ AddType(AutoChar(types[i].type), AutoChar(types[i].description));
+ }
+
+ CheckVideo();
+}
+
+void FileTypes::SetTypes(TypeList &newTypes)
+{
+ Nullsoft::Utility::AutoLock lock(typeGuard);
+ types=newTypes;
+ ResetTypes();
+ int numTypes=types.size();
+ for (size_t i=0;i!=numTypes;i++)
+ {
+ if (!types[i].isProtocol)
+ AddType(AutoChar(types[i].type), AutoChar(types[i].description));
+ }
+
+ CheckVideo();
+}
+void FileTypes::SaveConfig()
+{
+ Nullsoft::Utility::AutoLock lock(typeGuard);
+ wmConfig.cfg_int(L"numtypes", -1) = types.size();
+ for (size_t i=0;i!=types.size();i++)
+ {
+ wchar_t temp[64] = {0};
+
+ StringCchPrintf(temp, 64, L"type%u", i);
+ wmConfig.cfg_str(temp) = types[i].wtype;
+ StringCchPrintf(temp, 64, L"description%u", i);
+ wmConfig.cfg_str(temp) = types[i].description;
+ StringCchPrintf(temp, 64, L"protocol%u", i);
+ wmConfig.cfg_int(temp, 0) = types[i].isProtocol;
+ StringCchPrintf(temp, 64, L"avtype%u", i);
+ wmConfig.cfg_int(temp, 0) = types[i].avType;
+ }
+}
+
+void FileTypes::CheckVideo()
+{
+ bool videoPresent=false;
+ Nullsoft::Utility::AutoLock lock(typeGuard);
+ //wmConfig.cfg_int(L"numtypes", -1) = types.size();
+ for (size_t i=0;i!=types.size();i++)
+ if (types[i].avType == FileType::VIDEO)
+ videoPresent=true;
+
+ config_no_video = !videoPresent;
+}
+
+void FileTypes::ResetTypes()
+{
+ free(typesString);
+ typesString=0;
+}
+
+void FileTypes::AddType(const char *extension, const char *description)
+{
+ size_t oldSize=0, size=0;
+
+ if (typesString)
+ {
+ char *temp=typesString;
+ while (temp && *temp++)
+ {
+ size++;
+ if (*temp == 0)
+ {
+ size++;
+ temp++;
+ }
+ } ;
+ oldSize=size;
+ }
+ size += lstrlenA(extension)+1;
+ size += lstrlenA(description)+1;
+
+ char *newTypes = (char *)calloc(size+1, sizeof(char));
+ if (newTypes)
+ {
+ memcpy(newTypes, typesString, oldSize);
+ free(typesString);
+ typesString=newTypes;
+ newTypes += oldSize;
+ size-=oldSize;
+ StringCchCopyA(newTypes, size, extension);
+ int extSize = lstrlenA(extension)+1;
+ newTypes+=extSize;
+ size-=extSize;
+ StringCchCopyA(newTypes, size, description);
+ newTypes+=lstrlenA(description)+1;
+ *newTypes=0;
+ plugin.FileExtensions = typesString;
+ }
+}
+
+int FileTypes::GetAVType(const wchar_t *ext)
+{
+ Nullsoft::Utility::AutoLock lock(typeGuard);
+ for (size_t i=0;i!=types.size();i++)
+ {
+ if (!types[i].isProtocol && !lstrcmpi(types[i].type, ext))
+ return types[i].avType;
+ }
+ return -1;
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/FileTypes.h b/Src/Plugins/Input/in_wmvdrm/FileTypes.h
new file mode 100644
index 00000000..335ddb70
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/FileTypes.h
@@ -0,0 +1,88 @@
+#ifndef NULLSOFT_FILETYPESH
+#define NULLSOFT_FILETYPESH
+
+#include <vector>
+#include "../nu/AutoLock.h"
+#include "AutoChar.h"
+
+class FileType
+{
+public:
+ enum
+ {
+ AUDIO = 0,
+ VIDEO = 1,
+ };
+ FileType() : wtype(0), type(0), description(0), isProtocol(false), avType(AUDIO)
+ {
+ }
+
+ FileType(wchar_t *_type, wchar_t *_description, bool _protocol, int _avType)
+ {
+ wtype=_wcsdup(_type);
+ type = _wcsdup(_type);
+ description = _wcsdup(_description);
+ isProtocol = _protocol;
+ avType = _avType;
+ }
+ ~FileType()
+ {
+ free(type);
+ free(wtype);
+ free(description);
+ }
+ FileType(const FileType &copy) :wtype(0), type(0), description(0)
+ {
+ operator =(copy);
+ }
+ void operator =(const FileType &copy)
+ {
+
+ if (copy.wtype)
+ wtype=_wcsdup(copy.wtype);
+ if (copy.type)
+ type = _wcsdup(copy.type);
+ if (copy.description)
+ description = _wcsdup(copy.description);
+ isProtocol = copy.isProtocol;
+ avType = copy.avType;
+ }
+ wchar_t *type;
+ wchar_t *wtype;
+ wchar_t *description;
+ bool isProtocol;
+ int avType; // audio or video
+};
+
+class FileTypes
+{
+public:
+ FileTypes()
+ : typesString(0),
+ typeGuard(GUARDNAME("FileTypes::typeGuard"))
+ {}
+ ~FileTypes()
+ {
+ free(typesString);
+ typesString=0;
+ }
+ int GetAVType(const wchar_t *ext);
+ bool IsSupportedURL(const wchar_t *fn);
+ bool IsDefault();
+ void LoadDefaults();
+ void ReadConfig();
+ void SaveConfig();
+ void CheckVideo();
+
+ typedef std::vector<FileType> TypeList;
+ void SetTypes(TypeList &newTypes);
+ TypeList types;
+ Nullsoft::Utility::LockGuard typeGuard;
+private:
+ char *typesString;
+ void ResetTypes();
+ void AddType(const char *type, const char *description);
+};
+
+extern FileTypes fileTypes;
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/GainLayer.cpp b/Src/Plugins/Input/in_wmvdrm/GainLayer.cpp
new file mode 100644
index 00000000..84811f79
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/GainLayer.cpp
@@ -0,0 +1,232 @@
+#include "main.h"
+#include "GainLayer.h"
+#include <malloc.h>
+#include <math.h>
+#include <locale.h>
+#include "api.h"
+
+static void FillFloat(float *floatBuf, void *samples, size_t bps, size_t numSamples, size_t numChannels, float preamp)
+{
+ switch (bps)
+ {
+ case 8:
+ {
+ unsigned __int8 *samples8 = (unsigned __int8 *)samples;
+ size_t totalSamples = numSamples * numChannels;
+ for (size_t x = 0; x != totalSamples; x ++)
+ {
+ floatBuf[x] = (float)(samples8[x] - 128) * preamp;
+ }
+ }
+ break;
+ case 16:
+ {
+ short *samples16 = (short *)samples;
+ size_t totalSamples = numSamples * numChannels;
+ for (size_t x = 0; x != totalSamples; x ++)
+ {
+ floatBuf[x] = (float)samples16[x] * preamp;
+ }
+ }
+ break;
+ case 24:
+ {
+ unsigned __int8 *samples8 = (unsigned __int8 *)samples;
+ size_t totalSamples = numSamples * numChannels;
+ for (size_t x = 0; x != totalSamples; x ++)
+ {
+ long temp = (((long)samples8[0]) << 8);
+ temp = temp | (((long)samples8[1]) << 16);
+ temp = temp | (((long)samples8[2]) << 24);
+ floatBuf[x] = (float)temp * preamp;
+ samples8 += 3;
+ }
+ }
+ break;
+ }
+}
+
+inline static float fastclip(float x, const float a, const float b)
+{
+ float x1 = (float)fabs (x - a);
+ float x2 = (float)fabs (x - b);
+ x = x1 + (a + b);
+ x -= x2;
+ x *= 0.5f;
+ return (x);
+}
+
+#define PA_CLIP_( val, min, max )\
+ { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); }
+
+void Float32_To_Int16_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count)
+{
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+
+ while ( count-- )
+ {
+ long samp = lrint(*src);
+
+ PA_CLIP_( samp, -0x8000, 0x7FFF );
+ *dest = (signed short) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+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;
+}
+
+void Float32_To_Int24_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count)
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ while ( count-- )
+ {
+ /* convert to 32 bit and drop the low 8 bits */
+ double scaled = *src;
+ clip( scaled, -2147483648., 2147483647. );
+ signed long temp = (signed long) scaled;
+
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+static void FillSamples(void *samples, float *floatBuf, size_t bps, size_t numSamples, size_t numChannels)
+{
+ switch (bps)
+ {
+ case 16:
+ Float32_To_Int16_Clip(samples, 1, floatBuf, 1, numSamples*numChannels);
+ break;
+ case 24:
+ Float32_To_Int24_Clip(samples, 1, floatBuf, 1, numSamples*numChannels);
+ break;
+
+ }
+}
+
+float GetGain(WMInformation *info, bool allowDefault)
+{
+ if (AGAVE_API_CONFIG && AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false))
+ {
+ float dB = 0, peak = 1.0f;
+ wchar_t gain[64]=L"", peakVal[64]=L"";
+ switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_source", 0))
+ {
+ case 0: // track
+ info->GetAttribute(L"replaygain_track_gain", gain, 64);
+ if (!gain[0] && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
+ info->GetAttribute(L"replaygain_album_gain", gain, 64);
+
+ info->GetAttribute(L"replaygain_track_peak", peakVal, 64);
+ if (!peakVal[0] && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
+ info->GetAttribute(L"replaygain_album_peak", peakVal, 64);
+
+ break;
+ case 1:
+ info->GetAttribute(L"replaygain_album_gain", gain, 64);
+ if (!gain[0] && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
+ info->GetAttribute(L"replaygain_track_gain", gain, 64);
+
+ info->GetAttribute(L"replaygain_album_peak", peakVal, 64);
+ if (!peakVal[0] && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
+ info->GetAttribute(L"replaygain_track_peak", peakVal, 64);
+
+ break;
+ }
+
+ _locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
+
+ if (gain[0])
+ {
+ if (gain[0] == L'+')
+ dB = static_cast<float>(_wtof_l(&gain[1],C_locale));
+ else
+ dB = static_cast<float>(_wtof_l(gain,C_locale));
+ }
+ else if (allowDefault)
+ {
+ dB = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0);
+ return powf(10.0f, dB / 20.0f);
+ }
+
+ if (peakVal[0])
+ {
+ peak = static_cast<float>(_wtof_l(peakVal,C_locale));
+ }
+
+ switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_mode", 1))
+ {
+ case 0: // apply gain
+ return powf(10.0f, dB / 20.0f);
+ case 1: // apply gain, but don't clip
+ return min(powf(10.0f, dB / 20.0f), 1.0f / peak);
+ case 2: // normalize
+ return 1.0f / peak;
+ case 3: // prevent clipping
+ if (peak > 1.0f)
+ return 1.0f / peak;
+ else
+ return 1.0f;
+ }
+
+ }
+
+ return 1.0f; // no gain
+}
+
+
+void GainLayer::AudioDataReceived(void *_data, unsigned long sizeBytes, DWORD timestamp)
+{
+ if (enabled)
+ {
+ size_t samples = audio->AudioBytesToSamples(sizeBytes);
+ int channels = audio->Channels();
+ if (floatSize < (samples * channels))
+ {
+ delete [] floatData;
+ floatSize = samples * channels;
+ floatData = new float[floatSize];
+ }
+ if (outSize < sizeBytes)
+ {
+ delete [] outData;
+ outSize=sizeBytes;
+ outData = (void *)new __int8[sizeBytes];
+ }
+
+ FillFloat(floatData, _data, audio->BitSize(), samples , channels, replayGain);
+ FillSamples(outData, floatData, audio->BitSize(), samples , channels);
+
+ WMHandler::AudioDataReceived(outData, sizeBytes, timestamp);
+ }
+ else
+ WMHandler::AudioDataReceived(_data, sizeBytes, timestamp);
+}
+
+void GainLayer::Opened()
+{
+ enabled= (AGAVE_API_CONFIG && AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false));
+ if (enabled)
+ replayGain = GetGain(info, true);
+ WMHandler::Opened();
+}
diff --git a/Src/Plugins/Input/in_wmvdrm/GainLayer.h b/Src/Plugins/Input/in_wmvdrm/GainLayer.h
new file mode 100644
index 00000000..6eb6c67d
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/GainLayer.h
@@ -0,0 +1,32 @@
+#ifndef NULLSOFT_GAIN_LAYER_H
+#define NULLSOFT_GAIN_LAYER_H
+
+#include "WMHandler.h"
+#include "AudioFormat.h"
+#include "WMInformation.h"
+class GainLayer : public WMHandler
+{
+public:
+ GainLayer(AudioFormat *_audio, WMInformation *_info)
+ : audio(_audio), info(_info), enabled(false), replayGain(1.0f),
+ floatData(0),floatSize(0), outData(0), outSize(0)
+ {}
+ ~GainLayer()
+ {
+ delete[]floatData;
+ delete[]outData;
+ }
+ void AudioDataReceived(void *_data, unsigned long sizeBytes, DWORD timestamp);
+ void Opened();
+ AudioFormat *audio;
+ WMInformation *info;
+ bool enabled;
+ float replayGain;
+
+ float *floatData;
+ size_t floatSize;
+
+ void *outData;
+ size_t outSize;
+};
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/Main.h b/Src/Plugins/Input/in_wmvdrm/Main.h
new file mode 100644
index 00000000..85b3e4b5
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/Main.h
@@ -0,0 +1,52 @@
+#ifndef NULLSOFT_MAINH
+#define NULLSOFT_MAINH
+
+#define WMDRM_VERSION L"3.95"
+
+#include "WinampInterface.h"
+#include "../Winamp/in2.h"
+#include <windows.h>
+#include "WMDRMModule.h"
+#include <shlwapi.h>
+#include <wmsdk.h>
+#include "config.h"
+#include "WMHandler.h"
+#include "util.h"
+#include "FileTypes.h"
+#include "TagAlias.h"
+#include "WMInformation.h"
+#include "../nu/AutoWide.h"
+#include "../nu/AutoChar.h"
+#include "vidutils.h"
+#include "api.h"
+
+extern WMInformation *setFileInfo;
+
+struct IDispatch;
+extern IDispatch *winampExternal;
+
+extern WinampInterface winamp;
+extern In_Module plugin;
+
+extern WMDRM mod;
+
+#ifdef _DEBUG
+#define SHOW_CALLBACKS
+#endif
+
+//#define SHOW_CALLBACKS
+
+#ifdef SHOW_CALLBACKS
+#include <iostream>
+#define WMTCASE(sw) case sw: std::cerr << #sw << std::endl;
+#define WMT_SHOW_HR_CODE(hr) std::cerr << HRErrorCode(hr) << std::endl;
+#else
+#define WMTCASE(sw) case sw:
+#define WMT_SHOW_HR_CODE(hr)
+#endif
+
+// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}
+static const GUID playbackConfigGroupGUID =
+{ 0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf } };
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/MediaThread.cpp b/Src/Plugins/Input/in_wmvdrm/MediaThread.cpp
new file mode 100644
index 00000000..1120c952
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/MediaThread.cpp
@@ -0,0 +1,110 @@
+#include "main.h"
+#include "MediaThread.h"
+#include "config.h"
+
+MediaThread::MediaThread() : wait(INFINITE), thread(0)
+{
+ killEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ stopped = CreateEvent(NULL, TRUE, TRUE, NULL);
+ bufferFreed = CreateEvent(NULL, TRUE, TRUE, NULL);
+}
+
+MediaThread::~MediaThread()
+{
+ Kill();
+ if (thread)
+ CloseHandle(thread);
+}
+
+VOID CALLBACK MediaThread_StartAPC(ULONG_PTR param)
+{
+ reinterpret_cast<MediaThread *>(param)->StartAPC();
+}
+
+void MediaThread::StartAPC()
+{
+ wait=config_video_jitter;
+}
+
+void MediaThread::StopAPC()
+{
+ BufferList::iterator itr;
+ for (itr = buffers.begin();itr != buffers.end();itr++)
+ {
+ (*itr)->buffer->Release();
+ delete (*itr);
+ }
+
+ buffers.clear();
+ SetEvent(stopped);
+ SetEvent(bufferFreed);
+ wait=INFINITE;
+}
+
+static VOID CALLBACK MediaThread_StopAPC(ULONG_PTR param)
+{
+ reinterpret_cast<MediaThread *>(param)->StopAPC();
+}
+
+void MediaThread::Stop()
+{
+ ResetEvent(stopped);
+ QueueUserAPC(MediaThread_StopAPC, thread, reinterpret_cast<ULONG_PTR>(this));
+ WaitForSingleObject(stopped, INFINITE);
+}
+
+void MediaThread::WaitForStop()
+{
+ WaitForSingleObject(stopped, INFINITE);
+}
+
+void MediaThread::SignalStop()
+{
+ ResetEvent(stopped);
+ QueueUserAPC(MediaThread_StopAPC, thread, reinterpret_cast<ULONG_PTR>(this));
+}
+
+void MediaThread::Kill()
+{
+ SetEvent(killEvent);
+ WaitForSingleObject(stopped, INFINITE);
+}
+
+void MediaThread::OrderedInsert(MediaBuffer *buffer)
+{
+ BufferList::iterator itr;
+ for (itr = buffers.begin();itr != buffers.end(); itr++)
+ {
+ if ((*itr)->timestamp > buffer->timestamp)
+ {
+ buffers.insert(itr, buffer);
+ break;
+ }
+ }
+ if (itr == buffers.end())
+ buffers.push_back(buffer);
+
+}
+
+
+VOID CALLBACK MediaThread_AddAPC(ULONG_PTR param)
+{
+ MediaBufferAPC *apc = reinterpret_cast<MediaBufferAPC *>(param);
+ apc->thread->AddAPC(apc->buffer);
+ delete apc;
+}
+
+bool MediaThread::AddBuffer(INSSBuffer *buff, QWORD ts, unsigned long flags, bool drmProtected)
+{
+ if (WaitForSingleObject(bufferFreed, 0) == WAIT_TIMEOUT)
+ return false;
+
+ buff->AddRef();
+ MediaBuffer *buffer = new MediaBuffer(buff, ts, flags, drmProtected);
+ MediaBufferAPC *apc = new MediaBufferAPC;
+ apc->buffer = buffer;
+ apc->thread = this;
+ QueueUserAPC(MediaThread_AddAPC, thread, reinterpret_cast<ULONG_PTR>(apc));
+ Sleep(config_video_jitter); // sleep for a bit to keep the thread from going nuts
+ return true; // added
+}
diff --git a/Src/Plugins/Input/in_wmvdrm/MediaThread.h b/Src/Plugins/Input/in_wmvdrm/MediaThread.h
new file mode 100644
index 00000000..3dd3fdcb
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/MediaThread.h
@@ -0,0 +1,56 @@
+#ifndef NULLSOFT_MEDIATHREADH
+#define NULLSOFT_MEDIATHREADH
+
+#include <deque>
+#include <wmsdk.h>
+#include <vector>
+
+VOID CALLBACK MediaThread_StartAPC(ULONG_PTR param);
+VOID CALLBACK MediaThread_AddAPC(ULONG_PTR param);
+struct MediaBuffer
+{
+ MediaBuffer(INSSBuffer *b, QWORD t, unsigned long f, bool d) : buffer(b), timestamp(t), flags(f), drmProtected(d) {}
+ INSSBuffer *buffer;
+ QWORD timestamp;
+ unsigned long flags;
+ bool drmProtected;
+};
+struct MediaBufferAPC;
+
+class MediaThread
+{
+public:
+ MediaThread();
+ ~MediaThread();
+
+ bool AddBuffer(INSSBuffer *buff, QWORD ts, unsigned long flags, bool drmProtected);
+
+ void Stop();
+ void SignalStop();
+ void WaitForStop();
+ void Kill();
+
+public:
+ void StopAPC();
+ void StartAPC();
+
+ virtual void AddAPC(MediaBuffer *buffer)=0;
+
+protected:
+ void OrderedInsert(MediaBuffer *buffer);
+
+protected:
+ int wait;
+ HANDLE thread;
+ HANDLE killEvent, stopped, bufferFreed;
+
+ typedef std::vector<MediaBuffer*> BufferList;
+ BufferList buffers;
+};
+
+struct MediaBufferAPC
+{
+ MediaBuffer *buffer;
+ MediaThread *thread;
+};
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/MetaTag.cpp b/Src/Plugins/Input/in_wmvdrm/MetaTag.cpp
new file mode 100644
index 00000000..9eecb772
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/MetaTag.cpp
@@ -0,0 +1,96 @@
+#include "main.h"
+#include "MetaTag.h"
+#include "FileTypes.h"
+#include "TagAlias.h"
+
+ASFMetaTag::~ASFMetaTag()
+{
+ delete info;
+}
+
+const wchar_t *ASFMetaTag::getName()
+{
+ return L"ASF Metadata";
+}
+
+GUID ASFMetaTag::getGUID()
+{
+ return getServiceGuid();
+}
+
+int ASFMetaTag::getFlags()
+{
+ return METATAG_FILE_INFO;
+}
+
+int ASFMetaTag::isOurFile(const wchar_t *filename)
+{
+ const wchar_t *ext = PathFindExtension(filename);
+ return !lstrcmpiW(ext, L".WMA")
+ || !lstrcmpiW(ext, L".WMV")
+ || !lstrcmpiW(ext, L".ASF");
+
+}
+
+int ASFMetaTag::metaTag_open(const wchar_t *filename)
+{
+ info = new WMInformation(filename);
+ return METATAG_SUCCESS; // TODO: can we verify this?
+}
+
+void ASFMetaTag::metaTag_close()
+{
+ delete this;
+}
+
+const wchar_t *ASFMetaTag::enumSupportedTag(int n, int *datatype)
+{
+ return 0;
+}
+
+int ASFMetaTag::getTagSize(const wchar_t *tag, size_t *sizeBytes)
+{
+ size_t size;
+ const wchar_t *tagName = GetAlias(tag);
+ if (info && info->GetAttributeSize(tagName, size))
+ {
+ *sizeBytes = size;
+ return METATAG_SUCCESS;
+ }
+ else
+ {
+ return METATAG_UNKNOWN_TAG;
+ }
+}
+
+int ASFMetaTag::getMetaData(const wchar_t *tag, __int8 *buf, int buflenBytes, int datatype)
+{
+ const wchar_t *tagName = GetAlias(tag);
+ info->GetAttribute(tagName, reinterpret_cast<wchar_t *>(buf), buflenBytes / sizeof(wchar_t));
+ return METATAG_SUCCESS;
+ //return METATAG_FAILED;
+}
+
+int ASFMetaTag::setMetaData(const wchar_t *tag, const __int8 *buf, int buflenBytes, int datatype )
+{
+ return METATAG_FAILED;
+}
+
+
+#ifdef CBCLASS
+#undef CBCLASS
+#endif
+
+#define CBCLASS ASFMetaTag
+START_DISPATCH;
+CB(SVC_METATAG_GETNAME,getName)
+ CB(SVC_METATAG_GETGUID,getGUID)
+ CB(SVC_METATAG_GETFLAGS,getFlags)
+ CB(SVC_METATAG_ISOURFILE,isOurFile)
+ CB(SVC_METATAG_OPEN,metaTag_open)
+ VCB(SVC_METATAG_CLOSE,metaTag_close)
+ CB(SVC_METATAG_ENUMTAGS,enumSupportedTag)
+ CB(SVC_METATAG_GETTAGSIZE,getTagSize)
+ CB(SVC_METATAG_GETMETADATA,getMetaData)
+ CB(SVC_METATAG_SETMETADATA,setMetaData)
+END_DISPATCH;
diff --git a/Src/Plugins/Input/in_wmvdrm/MetaTag.h b/Src/Plugins/Input/in_wmvdrm/MetaTag.h
new file mode 100644
index 00000000..8c674663
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/MetaTag.h
@@ -0,0 +1,42 @@
+#ifndef NULLSOFT_IN_WMVDRM_METATAG_H
+#define NULLSOFT_IN_WMVDRM_METATAG_H
+
+#include "../Agave/Metadata/svc_metatag.h"
+#include "WMInformation.h"
+
+// {8FF721E1-2FF4-4721-A7D5-60FDB32FEB1F}
+static const GUID asfMetaTagGUID =
+{ 0x8ff721e1, 0x2ff4, 0x4721, { 0xa7, 0xd5, 0x60, 0xfd, 0xb3, 0x2f, 0xeb, 0x1f } };
+
+class ASFMetaTag : public svc_metaTag
+{
+public:
+ static const GUID getServiceGuid() { return asfMetaTagGUID; }
+ static const char *getServiceName() { return "ASF Metadata"; }
+public:
+ ASFMetaTag() : info(0)
+ {
+ }
+ ~ASFMetaTag();
+
+ /* These methods are to be used by api_metadata */
+ const wchar_t *getName(); // i.e. "ID3v2" or something
+ GUID getGUID(); // this needs to be the same GUID that you use when registering your service factory
+ int getFlags(); // how this service gets its info
+ int isOurFile(const wchar_t *filename);
+ int metaTag_open(const wchar_t *filename);
+ void metaTag_close(); // self-destructs when this is called (you don't need to call serviceFactory->releaseInterface)
+
+ /* user API starts here */
+ const wchar_t *enumSupportedTag(int n, int *datatype); // returns a list of understood tags. might not be complete (see note [1])
+ int getTagSize(const wchar_t *tag, size_t *sizeBytes); // always gives you BYTES, not characters (be careful with your strings)
+ int getMetaData(const wchar_t *tag, __int8 *buf, int buflenBytes, int datatype); // buflen is BYTES, not characters (be careful with your strings)
+ int setMetaData(const wchar_t *tag, const __int8 *buf, int buflenBytes, int datatype);
+
+private:
+ WMInformation *info;
+ RECVS_DISPATCH;
+};
+
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/MetaTagFactory.cpp b/Src/Plugins/Input/in_wmvdrm/MetaTagFactory.cpp
new file mode 100644
index 00000000..727727b9
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/MetaTagFactory.cpp
@@ -0,0 +1,67 @@
+#include "main.h"
+#include "MetaTag.h"
+#include "api.h"
+#include "MetaTagFactory.h"
+
+FOURCC MetaTagFactory::GetServiceType()
+{
+ return WaSvc::METATAG;
+}
+
+const char *MetaTagFactory::GetServiceName()
+{
+ return ASFMetaTag::getServiceName();
+}
+
+GUID MetaTagFactory::GetGUID()
+{
+ return ASFMetaTag::getServiceGuid();
+}
+
+void *MetaTagFactory::GetInterface(int global_lock)
+{
+ svc_metaTag *ifc= new ASFMetaTag;
+// if (global_lock)
+// plugin.service->service_lock(this, (void *)ifc);
+ return ifc;
+}
+
+int MetaTagFactory::SupportNonLockingInterface()
+{
+ return 1;
+}
+
+int MetaTagFactory::ReleaseInterface(void *ifc)
+{
+ //plugin.service->service_unlock(ifc);
+ svc_metaTag *metaTag = static_cast<svc_metaTag *>(ifc);
+ ASFMetaTag *asfMetaTag = static_cast<ASFMetaTag *>(metaTag);
+ delete asfMetaTag;
+ return 1;
+}
+
+const char *MetaTagFactory::GetTestString()
+{
+ return NULL;
+}
+
+int MetaTagFactory::ServiceNotify(int msg, int param1, int param2)
+{
+ return 1;
+}
+
+#ifdef CBCLASS
+#undef CBCLASS
+#endif
+
+#define CBCLASS MetaTagFactory
+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;
diff --git a/Src/Plugins/Input/in_wmvdrm/MetaTagFactory.h b/Src/Plugins/Input/in_wmvdrm/MetaTagFactory.h
new file mode 100644
index 00000000..0387401d
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/MetaTagFactory.h
@@ -0,0 +1,24 @@
+#ifndef NULLSOFT_IN_WMVDRM_METATAGFACTORY_H
+#define NULLSOFT_IN_WMVDRM_METATAGFACTORY_H
+
+#include <api/service/waservicefactory.h>
+#include <api/service/services.h>
+
+class MetaTagFactory : 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_wmvdrm/OutputStream.h b/Src/Plugins/Input/in_wmvdrm/OutputStream.h
new file mode 100644
index 00000000..bf0fe3db
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/OutputStream.h
@@ -0,0 +1,169 @@
+#ifndef NULLSOFT_OUTPUTSTREAMH
+#define NULLSOFT_OUTPUTSTREAMH
+
+#include <wmsdk.h>
+#define NULLSOFT_INTERFACE_BEGIN(RIID, OBJ) void **&NULLSOFT_interfaceHolder = OBJ; REFIID NULLSOFT_IID = RIID;
+#define NULLSOFT_VALID_INTERFACE(a) if (NULLSOFT_IID == IID_ ## a) { *NULLSOFT_interfaceHolder = static_cast<a *>(this); return S_OK; }
+#define NULLSOFT_INTERFACE_END() *NULLSOFT_interfaceHolder = 0; return E_NOINTERFACE;
+
+class OutputStream : public IWMOutputMediaProps
+{
+public:
+ OutputStream(IWMMediaProps *props) : mediaType(0)
+ {
+ DWORD mediaTypeSize;
+ props->GetMediaType(0, &mediaTypeSize);
+ if (mediaTypeSize)
+ {
+ mediaType = (WM_MEDIA_TYPE *)new unsigned char[mediaTypeSize];
+ props->GetMediaType(mediaType, &mediaTypeSize);
+ }
+ }
+
+ ~OutputStream()
+ {
+ if (mediaType)
+ {
+ delete mediaType;
+ mediaType = 0;
+ }
+ }
+
+ GUID &GetSubType() const
+ {
+ return mediaType->subtype;
+ }
+
+ WM_MEDIA_TYPE *mediaType;
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject)
+ {
+ NULLSOFT_INTERFACE_BEGIN(riid, ppvObject)
+ NULLSOFT_VALID_INTERFACE(IWMOutputMediaProps);
+ NULLSOFT_VALID_INTERFACE(IWMMediaProps);
+ NULLSOFT_INTERFACE_END()
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return 0;
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return 0;
+ }
+ HRESULT STDMETHODCALLTYPE GetType(GUID *pguidType)
+ {
+ if (!mediaType) return E_FAIL;
+ *pguidType = mediaType->majortype;
+ return S_OK;
+ }
+ HRESULT STDMETHODCALLTYPE GetMediaType(WM_MEDIA_TYPE *pType, DWORD *pcbType)
+ {
+ if (!mediaType) return E_FAIL;
+ if (!pType)
+ {
+ if (!pcbType) return E_INVALIDARG;
+ *pcbType = sizeof(WM_MEDIA_TYPE);
+ }
+ else
+ {
+ if (*pcbType < sizeof(WM_MEDIA_TYPE)) ASF_E_BUFFERTOOSMALL;
+ memcpy(pType, mediaType, sizeof(WM_MEDIA_TYPE));
+ }
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE SetMediaType(WM_MEDIA_TYPE *pType)
+ {
+ return E_NOTIMPL;
+ }
+ HRESULT STDMETHODCALLTYPE GetStreamGroupName(WCHAR *pwszName, WORD *pcchName)
+ {
+ return E_NOTIMPL;
+ }
+ HRESULT STDMETHODCALLTYPE GetConnectionName(WCHAR *pwszName, WORD *pcchName)
+ {
+ return E_NOTIMPL;
+ }
+};
+#include <uuids.h>
+class VideoOutputStream : public OutputStream
+{
+public:
+
+ VideoOutputStream(IWMMediaProps *props) : OutputStream(props)
+ {}
+
+ WMVIDEOINFOHEADER *VideoInfo() const
+ {
+ return (WMVIDEOINFOHEADER *)mediaType->pbFormat;
+ }
+int SourceWidth() const
+{
+ return VideoInfo()->rcSource.right - VideoInfo()->rcSource.left;
+ }
+ int DestinationWidth() const
+ {
+ return VideoInfo()->rcTarget.right - VideoInfo()->rcTarget.left;
+ }
+
+ int DestinationHeight() const
+ {
+ return VideoInfo()->rcTarget.bottom - VideoInfo()->rcTarget.top;
+ }
+
+ bool Flipped() const
+ {
+ BITMAPINFOHEADER &info = VideoInfo()->bmiHeader;
+ if (info.biHeight < 0 || info.biCompression == 0)
+ return true;
+ else
+ return false;
+
+ }
+ int bmiHeight()
+ {
+ return VideoInfo()->bmiHeader.biYPelsPerMeter;
+ }
+ int bmiWidth()
+ {
+ return VideoInfo()->bmiHeader.biXPelsPerMeter;
+ }
+ RGBQUAD *CreatePalette()
+ {
+ RGBQUAD *palette = (RGBQUAD *)calloc(1, 1024);
+ BITMAPINFOHEADER &info = VideoInfo()->bmiHeader;
+ memcpy(palette, (char *)(&info) + 40, info.biClrUsed * 4);
+ return palette;
+ }
+ int FourCC() const
+ {
+ BITMAPINFOHEADER &info = VideoInfo()->bmiHeader;
+ int fourcc = info.biCompression;
+ if (fourcc == BI_RGB)
+ {
+ switch(info.biBitCount)
+ {
+ case 32:
+ fourcc='23GR'; // RG32
+ break;
+ case 24:
+ fourcc='42GR'; // RG24
+ break;
+ case 8:
+ fourcc='8BGR'; // RGB8
+ break;
+ }
+ } else if (fourcc == BI_BITFIELDS)
+ fourcc = 0; // TODO: calc a CC that winamp likes
+ return fourcc;
+ }
+
+ bool IsVideo() const
+ {
+ return !!(mediaType->formattype == WMFORMAT_VideoInfo);
+ }
+};
+
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/PlaylistHandler.cpp b/Src/Plugins/Input/in_wmvdrm/PlaylistHandler.cpp
new file mode 100644
index 00000000..7fee0a5e
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/PlaylistHandler.cpp
@@ -0,0 +1,167 @@
+#include "main.h"
+#include "config.h"
+#include "PlaylistHandler.h"
+#include "WPLLoader.h"
+#include "ASXLoader.h"
+#include <shlwapi.h>
+#include "resource.h"
+
+void GetExtension(const wchar_t *filename, wchar_t *ext, size_t extCch)
+{
+ const wchar_t *s = PathFindExtensionW(filename);
+ if (!PathIsURLW(filename)
+ || (!wcsstr(s, L"?") && !wcsstr(s, L"&") && !wcsstr(s, L"=") && *s))
+ {
+ lstrcpynW(ext, s, extCch);
+ return ;
+ }
+ // s is not a terribly good extension, let's try again
+ {
+ wchar_t *copy = _wcsdup(filename);
+ s = L"";
+ again:
+ {
+ wchar_t *p = StrRChrW(copy,NULL, L'?');
+ if (p)
+ {
+ *p = 0;
+ s = PathFindExtensionW(copy);
+ if (!*s) goto again;
+ }
+ lstrcpynW(ext, s, extCch);
+ }
+ free(copy);
+ }
+}
+
+/* ---------------------------------- WPL --------------------------------------- */
+const wchar_t *WPLHandler::enumExtensions(size_t n)
+{
+ switch(n)
+ {
+ case 0:
+ return L"WPL";
+ /*case 1:
+ return L"ZPL";*/
+ default:
+ return 0;
+ }
+}
+
+int WPLHandler::SupportedFilename(const wchar_t *filename)
+{
+ wchar_t ext[16] = {0};
+ GetExtension(filename, ext, 16);
+
+ if (!lstrcmpiW(ext, L".WPL") || !lstrcmpiW(ext, L".ZPL"))
+ return SVC_PLAYLISTHANDLER_SUCCESS;
+
+ // TODO: open file and sniff it for file signature
+ return SVC_PLAYLISTHANDLER_FAILED;
+}
+
+ifc_playlistloader *WPLHandler::CreateLoader(const wchar_t *filename)
+{
+ return new WPLLoader;
+}
+
+void WPLHandler::ReleaseLoader(ifc_playlistloader *loader)
+{
+ WPLLoader *pls;
+
+ pls = static_cast<WPLLoader *>(loader);
+ delete pls;
+}
+
+const wchar_t *WPLHandler::GetName()
+{
+ static wchar_t wplpl[64];
+ // no point re-loading this all of the time since it won't change once we've been loaded
+ return (!wplpl[0]?WASABI_API_LNGSTRINGW_BUF(IDS_WINDOWS_MEDIA_PLAYLIST,wplpl,64):wplpl);
+}
+
+/* --- ASX --- */
+const wchar_t *ASXHandler::enumExtensions(size_t n)
+{
+
+ switch(n)
+ {
+ case 0:
+ return L"ASX";
+ case 1:
+ if (config_extra_asx_extensions)
+ return L"WAX";
+ else
+ return 0;
+ case 2:
+ if (config_extra_asx_extensions)
+ return L"WMX";
+ else
+ return 0;
+ case 3:
+ if (config_extra_asx_extensions)
+ return L"WVX";
+ else
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+int ASXHandler::SupportedFilename(const wchar_t *filename)
+{
+ wchar_t ext[16] = {0};
+ GetExtension(filename, ext, 16);
+
+ if (!lstrcmpiW(ext, L".ASX"))
+ return SVC_PLAYLISTHANDLER_SUCCESS;
+ if (!lstrcmpiW(ext, L".WAX"))
+ return SVC_PLAYLISTHANDLER_SUCCESS;
+ if (!lstrcmpiW(ext, L".WMX"))
+ return SVC_PLAYLISTHANDLER_SUCCESS;
+ if (!lstrcmpiW(ext, L".WVX"))
+ return SVC_PLAYLISTHANDLER_SUCCESS;
+
+ // TODO: open file and sniff it for file signature
+ return SVC_PLAYLISTHANDLER_FAILED;
+}
+
+ifc_playlistloader *ASXHandler::CreateLoader(const wchar_t *filename)
+{
+ return new ASXLoader;
+}
+
+void ASXHandler::ReleaseLoader(ifc_playlistloader *loader)
+{
+ ASXLoader *pls;
+
+ pls = static_cast<ASXLoader *>(loader);
+ delete pls;
+}
+
+const wchar_t *ASXHandler::GetName()
+{
+ static wchar_t asxpl[64];
+ // no point re-loading this all of the time since it won't change once we've been loaded
+ return (!asxpl[0]?WASABI_API_LNGSTRINGW_BUF(IDS_ASX_PLAYLIST,asxpl,64):asxpl);
+}
+
+#define CBCLASS WPLHandler
+START_DISPATCH;
+CB(SVC_PLAYLISTHANDLER_ENUMEXTENSIONS, enumExtensions)
+CB(SVC_PLAYLISTHANDLER_SUPPORTFILENAME, SupportedFilename)
+CB(SVC_PLAYLISTHANDLER_CREATELOADER, CreateLoader)
+VCB(SVC_PLAYLISTHANDLER_RELEASELOADER, ReleaseLoader)
+CB(SVC_PLAYLISTHANDLER_GETNAME, GetName)
+END_DISPATCH;
+#undef CBCLASS
+
+#define CBCLASS ASXHandler
+START_DISPATCH;
+CB(SVC_PLAYLISTHANDLER_ENUMEXTENSIONS, enumExtensions)
+CB(SVC_PLAYLISTHANDLER_SUPPORTFILENAME, SupportedFilename)
+CB(SVC_PLAYLISTHANDLER_CREATELOADER, CreateLoader)
+VCB(SVC_PLAYLISTHANDLER_RELEASELOADER, ReleaseLoader)
+CB(SVC_PLAYLISTHANDLER_GETNAME, GetName)
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/PlaylistHandler.h b/Src/Plugins/Input/in_wmvdrm/PlaylistHandler.h
new file mode 100644
index 00000000..98eae155
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/PlaylistHandler.h
@@ -0,0 +1,28 @@
+#ifndef NULLSOFT_PLAYLISTS_HANDLER_H
+#define NULLSOFT_PLAYLISTS_HANDLER_H
+
+#include "../playlist/svc_playlisthandler.h"
+#include <bfc/platform/types.h>
+
+#define DECLARE_HANDLER(className) class className ## Handler : public svc_playlisthandler {\
+public:\
+ const wchar_t *enumExtensions(size_t n); \
+ int SupportedFilename(const wchar_t *filename); \
+ ifc_playlistloader *CreateLoader(const wchar_t *filename);\
+ void ReleaseLoader(ifc_playlistloader *loader);\
+ const wchar_t *GetName(); \
+protected: RECVS_DISPATCH;}
+
+DECLARE_HANDLER(WPL);
+DECLARE_HANDLER(ASX);
+
+// {DC13A85D-7C61-4462-8CB4-9EBBBD86FA7C}
+static const GUID wplHandlerGUID =
+{ 0xdc13a85d, 0x7c61, 0x4462, { 0x8c, 0xb4, 0x9e, 0xbb, 0xbd, 0x86, 0xfa, 0x7c } };
+// {8909A743-6F00-43ef-B3F6-365000347DE3}
+
+static const GUID asxHandlerGUID =
+{ 0x8909a743, 0x6f00, 0x43ef, { 0xb3, 0xf6, 0x36, 0x50, 0x0, 0x34, 0x7d, 0xe3 } };
+// {6F62CBB8-7E1F-43eb-B3F6-01C2601029A3}
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/RawReader.cpp b/Src/Plugins/Input/in_wmvdrm/RawReader.cpp
new file mode 100644
index 00000000..1189b0ab
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/RawReader.cpp
@@ -0,0 +1,173 @@
+#include "main.h"
+#include "RawReader.h"
+#include "FileTypes.h"
+#include <shlwapi.h>
+
+#define NS_E_FILE_IS_CORRUPTED _HRESULT_TYPEDEF_(0xC00D080DL)
+
+bool IsMyExtension(const wchar_t *filename)
+{
+ const wchar_t *ext = PathFindExtension(filename);
+ if (ext && *ext)
+ {
+ ext++;
+ return fileTypes.GetAVType(ext) != -1;
+ }
+ return false;
+}
+
+int RawMediaReaderService::CreateRawMediaReader(const wchar_t *filename, ifc_raw_media_reader **out_reader)
+{
+ if (IsMyExtension(filename))
+ {
+ IWMSyncReader *reader = 0;
+ if (!SUCCEEDED(WMCreateSyncReader(NULL, 0, &reader)))
+ {
+ return NErr_FailedCreate;
+ }
+
+ if (FAILED(reader->Open(filename)))
+ {
+ reader->Release();
+ reader = 0;
+ return NErr_FileNotFound; // TODO: check HRESULT
+ }
+
+ RawMediaReader *raw_reader = new RawMediaReader();
+ if (!raw_reader)
+ {
+ reader->Close();
+ reader->Release();
+ return NErr_OutOfMemory;
+ }
+
+ int ret = raw_reader->Initialize(reader);
+ if (ret != NErr_Success)
+ {
+ delete raw_reader;
+ return ret;
+ }
+
+ *out_reader = raw_reader;
+ return NErr_Success;
+ }
+ else
+ {
+ return NErr_False;
+ }
+}
+
+#define CBCLASS RawMediaReaderService
+START_DISPATCH;
+CB(CREATERAWMEDIAREADER, CreateRawMediaReader);
+END_DISPATCH;
+#undef CBCLASS
+
+RawMediaReader::RawMediaReader()
+{
+ reader=0;
+ stream_num=0;
+ buffer_used=0;
+ end_of_file=false;
+ length=0;
+ buffer=0;
+ next_output=0;
+}
+
+RawMediaReader::~RawMediaReader()
+{
+ if (reader)
+ {
+ reader->Close();
+ reader->Release();
+ }
+
+ if (buffer)
+ {
+ buffer->Release();
+ }
+}
+
+int RawMediaReader::Initialize(IWMSyncReader *reader)
+{
+ this->reader=reader;
+ return NErr_Success;
+}
+
+int RawMediaReader::Read(void *out_buffer, size_t buffer_size, size_t *bytes_read)
+{
+ /* we don't care about these, but the API does not allows NULL */
+ QWORD sample_time = 0, duration = 0;
+ size_t bytesCopied = 0;
+ uint8_t *dest = (uint8_t *)out_buffer;
+ for (;;)
+ {
+ if (buffer)
+ {
+ BYTE *bufferBytes = 0;
+ DWORD bufferTotal = 0;
+ buffer->GetBufferAndLength(&bufferBytes, &bufferTotal);
+
+ if (buffer_used < bufferTotal)
+ {
+ size_t toCopy = min(bufferTotal - buffer_used, buffer_size);
+ memcpy(dest, bufferBytes + buffer_used, toCopy);
+ buffer_used += toCopy;
+ buffer_size -= toCopy;
+ dest += toCopy;
+ bytesCopied += toCopy;
+
+ if (buffer_used == bufferTotal)
+ {
+ buffer_used = 0;
+ buffer->Release();
+ buffer = 0;
+ }
+ }
+ if (buffer_size == 0)
+ {
+ *bytes_read = bytesCopied;
+ return NErr_Success;
+ }
+ }
+
+ if (stream_num == 0)
+ {
+ DWORD outputs = 0;
+ HRESULT hr=reader->GetOutputCount(&outputs);
+ if (FAILED(hr))
+ return NErr_Error;
+ if (next_output >= outputs)
+ return NErr_EndOfFile;
+ hr=reader->GetStreamNumberForOutput(next_output, &stream_num);
+ if (FAILED(hr))
+ return NErr_Error;
+ hr=reader->SetReadStreamSamples(stream_num, TRUE);
+ if (FAILED(hr))
+ return NErr_Error;
+ next_output++;
+ }
+
+ DWORD flags = 0;
+ HRESULT r = reader->GetNextSample(stream_num, &buffer, &sample_time, &duration, &flags, 0, 0);
+ if (r == NS_E_NO_MORE_SAMPLES || r == NS_E_FILE_IS_CORRUPTED)
+ {
+ stream_num=0;
+ }
+ }
+
+ return NErr_Error;
+}
+
+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_wmvdrm/RawReader.h b/Src/Plugins/Input/in_wmvdrm/RawReader.h
new file mode 100644
index 00000000..f7d2e80b
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/RawReader.h
@@ -0,0 +1,40 @@
+#pragma once
+#include "../Agave/DecodeFile/svc_raw_media_reader.h"
+#include "../Agave/DecodeFile/ifc_raw_media_reader.h"
+#include <mmreg.h>
+#include <wmsdk.h>
+
+// {9AF5FD89-DC41-4F2A-A156-8D1399FDE57B}
+static const GUID wm_raw_reader_guid =
+{ 0x9af5fd89, 0xdc41, 0x4f2a, { 0xa1, 0x56, 0x8d, 0x13, 0x99, 0xfd, 0xe5, 0x7b } };
+
+class RawMediaReaderService : public svc_raw_media_reader
+{
+public:
+ static const char *getServiceName() { return "Windows Media Audio Raw Reader"; }
+ static GUID getServiceGuid() { return wm_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(IWMSyncReader *reader);
+ int Read(void *buffer, size_t buffer_size, size_t *bytes_read);
+ size_t Release();
+protected:
+ RECVS_DISPATCH;
+private:
+ IWMSyncReader *reader;
+ WORD stream_num;
+
+ INSSBuffer *buffer;
+ size_t buffer_used;
+ bool end_of_file;
+ QWORD length;
+ DWORD next_output;
+};
diff --git a/Src/Plugins/Input/in_wmvdrm/Remaining.h b/Src/Plugins/Input/in_wmvdrm/Remaining.h
new file mode 100644
index 00000000..acfbbffe
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/Remaining.h
@@ -0,0 +1,68 @@
+#ifndef NULLSOFT_REMAININGH
+#define NULLSOFT_REMAININGH
+#include <assert.h>
+#include <memory.h>
+/* this class is used to store leftover samples */
+
+class Remaining
+{
+public:
+ Remaining()
+ : store(0), size(0), used(0)
+ {}
+
+ void Allocate(unsigned long _size)
+ {
+ assert(_size);
+ used=0;
+ size=_size;
+ if (store)
+ delete [] store;
+ store = new unsigned char [size];
+ }
+
+ /* Saves the incoming data and updates the pointer positions */
+ template <class storage_t>
+ void UpdatingWrite(storage_t *&data, unsigned long &bytes)
+ {
+ unsigned long bytesToWrite = min(bytes, SizeRemaining());
+ Write(data, bytesToWrite);
+ assert(bytesToWrite);
+ data = (storage_t *)((char *)data + bytesToWrite);
+ bytes -= bytesToWrite;
+ }
+
+ void Write(void *data, unsigned long bytes)
+ {
+ unsigned char *copy = (unsigned char *)store;
+ copy+=used;
+ memcpy(copy, data, bytes);
+ used+=bytes;
+ }
+
+ unsigned long SizeRemaining()
+ {
+ return size-used;
+ }
+
+ bool Empty()
+ {
+ return !used;
+ }
+ bool Full()
+ {
+ return size == used;
+ }
+ void *GetData()
+ {
+ return (void *)store;
+ }
+
+ void Flush()
+ {
+ used=0;
+ }
+ unsigned char *store;
+ long size, used;
+};
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/SeekLayer.cpp b/Src/Plugins/Input/in_wmvdrm/SeekLayer.cpp
new file mode 100644
index 00000000..2b7eabdd
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/SeekLayer.cpp
@@ -0,0 +1,376 @@
+#include "SeekLayer.h"
+#include "Main.h"
+#include "output/AudioOut.h"
+#include "util.h"
+#include <assert.h>
+using namespace Nullsoft::Utility;
+
+struct OpenThreadData
+{
+ IWMReaderCallback *callback;
+ wchar_t *url;
+ IWMReader *reader;
+
+ void open()
+ {
+ reader->Open(url, callback, 0);
+ }
+
+ OpenThreadData(IWMReader *_reader, const wchar_t *_url, IWMReaderCallback *_callback)
+ {
+ reader = _reader;
+ reader->AddRef();
+ callback = _callback;
+ callback->AddRef();
+
+ url = _wcsdup(_url);
+ }
+
+ ~OpenThreadData()
+ {
+ free(url);
+ reader->Release();
+ callback->Release();
+ }
+};
+DWORD WINAPI OpenThread(void *param)
+{
+ OpenThreadData *data = (OpenThreadData *)param;
+ data->open();
+ delete data;
+ return 0;
+}
+
+#define NEW_SEEK
+SeekLayer::SeekLayer(IWMReader *_reader, ClockLayer *_clock)
+ : seekPos(0), reader(_reader), playState(PLAYSTATE_CLOSED), metadata(NULL), clock(_clock),
+ needPause(false), paused(false), needStop(false),
+ seekGuard(GUARDNAME("Seek Guard")),
+ oldState_buffer(PLAYSTATE_NONE)
+{
+ reader->AddRef();
+ reader->QueryInterface(&reader2);
+}
+
+void SeekLayer::DoStop()
+{
+ if (paused)
+ reader->Resume();
+
+ reader->Stop();
+ First().Stopping();
+ First().Kill();
+ if (paused)
+ {
+ paused = false;
+ out->Pause(0);
+ }
+ needStop = false;
+}
+
+void SeekLayer::SeekTo(long position)
+{
+ AutoLock lock (seekGuard LOCKNAME("SeekTo"));
+ if (paused)
+ {
+ reader->Resume();
+ }
+ First().Stopping();
+ First().Kill();
+ seekPos = position;
+ clock->SetLastOutputTime(position);
+ clock->SetStartTimeMilliseconds(position);
+ out->Flush(position);
+ QWORD qSeekPos = position;
+ qSeekPos *= 10000;
+ reader->Start(qSeekPos, 0, 1.0f, NULL);
+ if (paused)
+ {
+ reader->Pause();
+ }
+}
+
+void SeekLayer::Pause()
+{
+ AutoLock lock (seekGuard LOCKNAME("Pause"));
+ if (playState == PLAYSTATE_STARTED)
+ {
+ paused = true;
+ reader->Pause();
+ out->Pause(1);
+ }
+ else
+ {
+ needPause = true;
+ }
+}
+
+int SeekLayer::Open(const wchar_t *filename, IWMReaderCallback *callback)
+{
+ AutoLock lock (seekGuard LOCKNAME("Open"));
+ assert(playState == PLAYSTATE_CLOSED);
+ needStop = false;
+ playState = PLAYSTATE_OPENING;
+ DWORD dummyId;
+ CreateThread(NULL, 0, OpenThread, new OpenThreadData(reader, filename, callback), 0, &dummyId);
+ return 0;
+}
+
+void SeekLayer::Stop()
+{
+ AutoLock lock (seekGuard LOCKNAME("Stop"));
+ needStop = true;
+
+ switch (playState)
+ {
+ case PLAYSTATE_BUFFERING:
+// needStop=false;
+ reader2->StopBuffering();
+// reader->Stop();
+ break;
+
+ case PLAYSTATE_OPENING:
+ // wait for it to open (or connect) and then we'll kill it there
+ break;
+
+ case PLAYSTATE_NONE:
+ case PLAYSTATE_STOPPED:
+ if (FAILED(reader->Close())) // reader->Close() is sometimes synchronous, and sometimes not valid here
+ {
+ playState = PLAYSTATE_CLOSED;
+ return ;
+ }
+ break;
+
+ case PLAYSTATE_OPENED:
+ case PLAYSTATE_STARTED:
+ reader->Stop();
+ First().Stopping();
+ First().Kill();
+ break;
+
+ case PLAYSTATE_CLOSED:
+ needStop = false;
+ break;
+ /*
+ case PLAYSTATE_BUFFERING:
+ reader2->StopBuffering();
+ reader->Stop();
+ break;*/
+
+ case PLAYSTATE_SEEK:
+ break;
+ }
+
+ while (playState != PLAYSTATE_CLOSED)
+ {
+ lock.ManualUnlock();
+ Sleep(55);
+ lock.ManualLock(MANUALLOCKNAME("[Manual Lock]Stop"));
+ }
+ needStop = false;
+}
+
+void SeekLayer::Unpause()
+{
+ AutoLock lock (seekGuard LOCKNAME("Unpause"));
+ if (playState == PLAYSTATE_STARTED)
+ {
+ paused = false;
+ out->Pause(0);
+ reader->Resume();
+ clock->Clock();
+ }
+ else
+ {
+ needPause = false;
+ }
+}
+
+void SeekLayer::Opened()
+{
+ {
+ AutoLock lock (seekGuard LOCKNAME("SeekLayer::Opened"));
+ if (needStop)
+ {
+ playState = PLAYSTATE_OPENED;
+ lock.ManualUnlock();
+ reader->Close();
+ lock.ManualLock(MANUALLOCKNAME("[Manual Lock]SeekLayer::Opened"));
+ return ;
+ }
+
+ playState = PLAYSTATE_OPENED;
+ }
+ WMHandler::Opened();
+}
+
+void SeekLayer::Stopped()
+{
+ {
+ AutoLock lock (seekGuard LOCKNAME("Stopped"));
+ if (needStop)
+ {
+ playState = PLAYSTATE_STOPPED;
+ lock.ManualUnlock();
+ reader->Close();
+ lock.ManualLock(MANUALLOCKNAME("Stopped"));
+ }
+ else
+ {
+ playState = PLAYSTATE_STOPPED;
+ }
+
+ WMHandler::Stopped();
+ }
+}
+
+void SeekLayer::Started()
+{
+ {
+ AutoLock lock (seekGuard LOCKNAME("Started"));
+ playState = PLAYSTATE_STARTED;
+
+ if (needStop)
+ {
+ reader->Stop();
+ First().Stopping();
+ First().Kill();
+ return ;
+ }
+ else if (needPause)
+ {
+ Pause();
+ needPause = false;
+ }
+ }
+ WMHandler::Started();
+}
+
+void SeekLayer::Closed()
+{
+ playState = PLAYSTATE_CLOSED;
+ paused = false;
+ needPause = false;
+ needStop = false;
+ seekPos = 0;
+
+ WMHandler::Closed();
+}
+
+void SeekLayer::BufferingStarted()
+{
+ {
+ AutoLock lock (seekGuard LOCKNAME("BufferingStarted"));
+ if (playState == PLAYSTATE_OPENED)
+ oldState_buffer = PLAYSTATE_NONE;
+ else
+ oldState_buffer = playState;
+ if (playState != PLAYSTATE_STARTED)
+ playState = PLAYSTATE_BUFFERING;
+ if (needStop)
+ reader2->StopBuffering();
+
+ }
+ WMHandler::BufferingStarted();
+}
+
+
+void SeekLayer::BufferingStopped()
+{
+ {
+ AutoLock lock (seekGuard LOCKNAME("BufferingStopped"));
+ if (needStop)
+ reader->Stop();
+ playState = oldState_buffer;
+ }
+ WMHandler::BufferingStopped();
+}
+
+
+void SeekLayer::EndOfFile()
+{
+ {
+ AutoLock lock (seekGuard LOCKNAME("EndOfFile"));
+ if (needStop)
+ return ;
+ }
+ WMHandler::EndOfFile();
+
+}
+
+void SeekLayer::Connecting()
+{
+ {
+ AutoLock lock (seekGuard LOCKNAME("SeekLayer::Connecting"));
+ if (needStop)
+ {
+ playState = PLAYSTATE_NONE;
+ lock.ManualUnlock();
+ reader->Close();
+ lock.ManualLock(MANUALLOCKNAME("[Manual Lock]SeekLayer::Connecting"));
+ return ;
+ }
+ playState = PLAYSTATE_NONE;
+ }
+ WMHandler::Connecting();
+}
+
+
+void SeekLayer::Locating()
+{
+ {
+ AutoLock lock (seekGuard LOCKNAME("SeekLayer::Locating"));
+ if (needStop)
+ {
+ playState = PLAYSTATE_NONE;
+ lock.ManualUnlock();
+ reader->Close();
+ lock.ManualLock(MANUALLOCKNAME("[Manual Lock]SeekLayer::Locating"));
+ return ;
+ }
+ playState = PLAYSTATE_NONE;
+ }
+ WMHandler::Locating();
+}
+
+
+void SeekLayer::OpenCalled()
+{
+ {
+ AutoLock lock (seekGuard LOCKNAME("SeekLayer::OpenCalled"));
+ if (needStop)
+ {
+ playState = PLAYSTATE_NONE;
+ lock.ManualUnlock();
+ reader->Close();
+ lock.ManualLock(MANUALLOCKNAME("[Manual Lock]SeekLayer::OpenCalled"));
+ return ;
+ }
+
+ playState = PLAYSTATE_NONE;
+ }
+ WMHandler::OpenCalled();
+}
+
+void SeekLayer::OpenFailed()
+{
+ {
+ AutoLock lock (seekGuard LOCKNAME("SeekLayer::OpenFailed"));
+ if (playState == PLAYSTATE_OPENING)
+ playState = PLAYSTATE_NONE;
+ }
+ WMHandler::OpenFailed();
+}
+
+void SeekLayer::Error()
+{
+ {
+ AutoLock lock (seekGuard LOCKNAME("SeekLayer::Error"));
+ /*if (playState == PLAYSTATE_OPENING)
+ playState = PLAYSTATE_CLOSED;
+ else */if (playState != PLAYSTATE_CLOSED)
+ playState = PLAYSTATE_NONE;
+ }
+ WMHandler::Error();
+}
diff --git a/Src/Plugins/Input/in_wmvdrm/SeekLayer.h b/Src/Plugins/Input/in_wmvdrm/SeekLayer.h
new file mode 100644
index 00000000..7b896845
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/SeekLayer.h
@@ -0,0 +1,55 @@
+#ifndef NULLSOFT_SEEKLAYERH
+#define NULLSOFT_SEEKLAYERH
+
+#include "WMHandler.h"
+#include "../nu/AutoLock.h"
+#include "ClockLayer.h"
+class SeekLayer : public WMHandler
+{
+ enum PlayState
+ {
+ PLAYSTATE_NONE,
+ PLAYSTATE_OPENING,
+ PLAYSTATE_OPENED,
+ PLAYSTATE_BUFFERING,
+ PLAYSTATE_STARTED,
+ PLAYSTATE_STOPPED,
+ PLAYSTATE_CLOSED,
+ PLAYSTATE_SEEK,
+
+ };
+public:
+ SeekLayer(IWMReader *_reader, ClockLayer *_clock);
+ void SeekTo(long position);
+ void Pause();
+ void Unpause();
+ void Stop();
+ int Open(const wchar_t *filename, IWMReaderCallback *callback);
+
+private:
+ void BufferingStarted();
+ void BufferingStopped();
+ void Started();
+ void Stopped();
+ void Closed();
+ void Opened();
+ void OpenCalled();
+ void Connecting();
+ void Locating();
+ void EndOfFile();
+ void OpenFailed();
+ void Error();
+
+private:
+ void DoStop();
+ bool needPause, paused, needStop;
+ long seekPos;
+ Nullsoft::Utility::LockGuard seekGuard;
+ IWMReader *reader;
+ IWMReaderAdvanced2 *reader2;
+ IWMMetadataEditor *metadata;
+ ClockLayer *clock;
+ PlayState playState, oldState_buffer;
+};
+
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/StatusHook.cpp b/Src/Plugins/Input/in_wmvdrm/StatusHook.cpp
new file mode 100644
index 00000000..45cf618a
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/StatusHook.cpp
@@ -0,0 +1,54 @@
+#if 0
+#include <windows.h>
+#include "../Winamp/wa_ipc.h"
+#include "Main.h"
+#include <shlwapi.h>
+
+static WNDPROC waProc=0;
+static bool winampisUnicode=false;
+
+static LRESULT WINAPI StatusHookProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+
+ if (msg == WM_WA_IPC && lParam == IPC_HOOK_TITLESW)
+ {
+ LRESULT downTheLine = winampisUnicode?CallWindowProcW(waProc, hwnd, msg, wParam, lParam):CallWindowProcA(waProc, hwnd, msg, wParam, lParam);
+ waHookTitleStructW *hook = (waHookTitleStructW *)wParam;
+ if (!PathIsURLW(hook->filename) && winamp.GetStatusHook(hook->title, 2048, hook->filename))
+ {
+ return TRUE;
+ }
+ else
+ return downTheLine;
+ }
+
+ if (waProc)
+ {
+ if (winampisUnicode)
+ return CallWindowProcW(waProc, hwnd, msg, wParam, lParam);
+ else
+ return CallWindowProcA(waProc, hwnd, msg, wParam, lParam);
+ }
+ else
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+void Hook(HWND winamp)
+{
+ if (winamp)
+ {
+ winampisUnicode = !!IsWindowUnicode(winamp);
+ if (winampisUnicode)
+ waProc = (WNDPROC)SetWindowLongPtrW(winamp, GWLP_WNDPROC, (LONG_PTR)StatusHookProc);
+ else
+ waProc = (WNDPROC)SetWindowLongPtrA(winamp, GWLP_WNDPROC, (LONG_PTR)StatusHookProc);
+ }
+}
+
+void Unhook(HWND winamp)
+{
+// if (winamp && GetWindowLongA(winamp,GWL_WNDPROC) == (LONG)StatusHookProc)
+ //SetWindowLong(winamp, GWL_WNDPROC, (LONG)waProc);
+ //waProc=0;
+}
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/StatusHook.h b/Src/Plugins/Input/in_wmvdrm/StatusHook.h
new file mode 100644
index 00000000..400c134a
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/StatusHook.h
@@ -0,0 +1,7 @@
+#ifndef NULLSOFT_STATUSHOOKH
+#define NULLSOFT_STATUSHOOKH
+
+#include <windows.h>
+void Hook(HWND winamp);
+void Unhook(HWND winamp);
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/TODO.txt b/Src/Plugins/Input/in_wmvdrm/TODO.txt
new file mode 100644
index 00000000..3c5fa698
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/TODO.txt
@@ -0,0 +1,8 @@
+advanced preferences:
+defaults button
+
+why do some videos not end properly?
+
+potential race condition over WMDRM::fn
+
+make the messagebox for 'do you want to acquire this shit' have its own message loop so we can killswitch it \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/TagAlias.cpp b/Src/Plugins/Input/in_wmvdrm/TagAlias.cpp
new file mode 100644
index 00000000..5f5e8e59
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/TagAlias.cpp
@@ -0,0 +1,64 @@
+#include "main.h"
+#include "TagAlias.h"
+
+struct TagAliases
+{
+ const wchar_t *winampTag;
+ const wchar_t *wmaTag;
+};
+
+const TagAliases aliases[] =
+ {
+ {L"comment", g_wszWMDescription},
+ {L"album", g_wszWMAlbumTitle},
+ {L"genre", g_wszWMGenre},
+ {L"year", g_wszWMYear},
+ {L"track", g_wszWMTrackNumber},
+ {L"artist", g_wszWMAuthor},
+ {L"title", g_wszWMTitle},
+ {L"copyright", g_wszWMCopyright},
+ {L"composer", g_wszWMComposer},
+ {L"albumartist", g_wszWMAlbumArtist},
+ {L"bpm", g_wszWMBeatsPerMinute},
+ {L"publisher", g_wszWMPublisher},
+ {L"ISRC", g_wszWMISRC},
+ {L"lyricist", g_wszWMWriter},
+ {L"conductor", g_wszWMConductor},
+ {L"tool", g_wszWMToolName},
+ {L"encoder", g_wszWMEncodingSettings},
+ {L"key", g_wszWMInitialKey},
+ {L"mood", g_wszWMMood},
+ {L"disc", g_wszWMPartOfSet},
+ {L"height", g_wszWMVideoHeight},
+ {L"width", g_wszWMVideoWidth},
+ {L"category", g_wszWMCategory},
+ {L"producer", g_wszWMProducer},
+ {L"director", g_wszWMDirector},
+ {L"fps", g_wszWMVideoFrameRate},
+ {0, 0},
+ };
+
+
+const wchar_t *GetAlias(const wchar_t *tag)
+{
+ int i = 0;
+ while (aliases[i].winampTag)
+ {
+ if (!lstrcmpiW(tag, aliases[i].winampTag))
+ return aliases[i].wmaTag;
+ i++;
+ }
+ return tag;
+}
+
+const wchar_t *GetAlias_rev(const wchar_t *tag)
+{
+ int i = 0;
+ while (aliases[i].wmaTag)
+ {
+ if (!lstrcmpiW(tag, aliases[i].wmaTag))
+ return aliases[i].winampTag;
+ i++;
+ }
+ return tag;
+}
diff --git a/Src/Plugins/Input/in_wmvdrm/TagAlias.h b/Src/Plugins/Input/in_wmvdrm/TagAlias.h
new file mode 100644
index 00000000..56a25be1
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/TagAlias.h
@@ -0,0 +1,7 @@
+#ifndef NULLSOFT_IN_WMVDRM_TAGALIAS_H
+#define NULLSOFT_IN_WMVDRM_TAGALIAS_H
+
+#include <wchar.h>
+const wchar_t *GetAlias(const wchar_t *tag);
+const wchar_t *GetAlias_rev(const wchar_t *tag);
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/VideoDataConverter.cpp b/Src/Plugins/Input/in_wmvdrm/VideoDataConverter.cpp
new file mode 100644
index 00000000..35130024
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/VideoDataConverter.cpp
@@ -0,0 +1,41 @@
+#include "main.h"
+#include "VideoDataConverter.h"
+#include <cassert>
+
+class YV12Converter : public VideoDataConverter
+{
+public:
+ YV12Converter(int w, int h)
+ : width(w), height(h)
+ {
+ yv12.y.rowBytes = width;
+ yv12.v.rowBytes = width / 2;
+ yv12.u.rowBytes = width / 2;
+ vOffset = width*height;
+ uOffset = width*height/4;
+ }
+
+ void *Convert(void *videoData)
+ {
+ yv12.y.baseAddr = (unsigned char*)videoData;
+ yv12.v.baseAddr = yv12.y.baseAddr + vOffset;
+ yv12.u.baseAddr = yv12.v.baseAddr + uOffset;
+
+ return (void *)&yv12;
+ }
+
+ int width, height;
+ int vOffset, uOffset;
+ YV12_PLANES yv12;
+};
+
+VideoDataConverter *MakeConverter(VideoOutputStream *stream)
+{
+ switch (stream->FourCC())
+ {
+ case '21VY':
+ return new YV12Converter(stream->DestinationWidth(), stream->DestinationHeight());
+ default:
+ return new VideoDataConverter;
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/VideoDataConverter.h b/Src/Plugins/Input/in_wmvdrm/VideoDataConverter.h
new file mode 100644
index 00000000..d1530cb0
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/VideoDataConverter.h
@@ -0,0 +1,18 @@
+#ifndef NULLSOFT_VIDEODATACONVERTERH
+#define NULLSOFT_VIDEODATACONVERTERH
+
+#include "OutputStream.h"
+class VideoDataConverter
+{
+public:
+ virtual void *Convert(void *videoData)
+ {
+ return videoData;
+ }
+};
+
+
+
+VideoDataConverter *MakeConverter(VideoOutputStream *stream);
+
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/VideoLayer.cpp b/Src/Plugins/Input/in_wmvdrm/VideoLayer.cpp
new file mode 100644
index 00000000..cee4297b
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/VideoLayer.cpp
@@ -0,0 +1,300 @@
+#include "Main.h"
+#include "VideoLayer.h"
+#include <initguid.h>
+#include <wmsdkidl.h>
+#include <cassert>
+#include "util.h"
+#include "resource.h"
+#include <strsafe.h>
+
+#include "config.h"
+#define VIDEO_ACCEPTABLE_DROP (config_video_drop_threshold*10000)
+
+VideoLayer::VideoLayer(IWMReader *_reader)
+ : reader(_reader), videoOutputNum(-1),
+ reader2(0), offset(0), nextRest(0),
+ converter(NULL), videoOpened(false),
+ video_output_opened(false),
+ killSwitch(0), aspect(0),
+ earlyDelivery(0), fourcc(0),
+ drmProtected(false),
+ videoStream(0), flip(false),
+ videoWidth(0), videoHeight(0)
+{
+ reader->AddRef();
+ if (FAILED(reader->QueryInterface(&reader2)))
+ reader2 = 0;
+
+ if (FAILED(reader->QueryInterface(&header)))
+ header = 0;
+
+ killSwitch = CreateEvent(NULL, TRUE, FALSE, NULL);
+}
+
+VideoLayer::~VideoLayer()
+{
+ videoThread.Kill();
+ if (reader2)
+ reader2->Release();
+ if (header)
+ header->Release();
+ reader->Release();
+ CloseHandle(killSwitch);
+}
+
+bool AcceptableFormat(GUID &subtype)
+{
+ if (subtype == WMMEDIASUBTYPE_YV12
+ || subtype == WMMEDIASUBTYPE_YUY2
+ || subtype == WMMEDIASUBTYPE_UYVY
+ //|| subtype == WMMEDIASUBTYPE_YVYU
+ || subtype == WMMEDIASUBTYPE_RGB24
+ || subtype == WMMEDIASUBTYPE_RGB32
+ || subtype == WMMEDIASUBTYPE_I420
+ || subtype == WMMEDIASUBTYPE_IYUV
+ || subtype == WMMEDIASUBTYPE_RGB1
+ || subtype == WMMEDIASUBTYPE_RGB4
+ || subtype == WMMEDIASUBTYPE_RGB8
+ || subtype == WMMEDIASUBTYPE_RGB565
+ || subtype == WMMEDIASUBTYPE_RGB555
+ )
+ return true;
+ else
+ return false;
+}
+
+bool VideoLayer::AttemptOpenVideo(VideoOutputStream *attempt)
+{
+ videoWidth = attempt->DestinationWidth();
+ videoHeight = attempt->DestinationHeight();
+ flip = attempt->Flipped();
+ fourcc = attempt->FourCC();
+ if (!fourcc)
+ return false;
+
+ aspect = 1.0;
+ return true;
+
+}
+
+bool VideoLayer::OpenVideo()
+{
+ videoOutputNum = -1;
+ DWORD numOutputs, numFormats;
+ IWMOutputMediaProps *formatProperties;
+ VideoOutputStream *stream;
+ GUID mediaType;
+
+ reader->GetOutputCount(&numOutputs);
+
+ for (DWORD output = 0;output < numOutputs;output++)
+ {
+ // test the default format first, and if that fails, iterate through the rest
+ const int defaultFormat = -1;
+ HRESULT hr;
+ if (FAILED(hr = reader->GetOutputFormatCount(output, &numFormats)))
+ continue;
+
+ for (int format = 0/*defaultFormat*/;format != numFormats;format++)
+ {
+ if (format == defaultFormat)
+ reader->GetOutputProps(output, &formatProperties);
+ else
+ reader->GetOutputFormat(output, format, &formatProperties);
+
+ formatProperties->GetType(&mediaType);
+
+ if (mediaType == WMMEDIATYPE_Video)
+ {
+ stream = new VideoOutputStream(formatProperties);
+
+ if (stream->IsVideo() // if it's video
+ && AcceptableFormat(stream->GetSubType()) // and a video format we like
+ && AttemptOpenVideo(stream)) // and winamp was able to open it
+ {
+ videoOpened = true;
+ int fourcc = stream->FourCC();
+ if (fourcc == '8BGR')
+ {
+ RGBQUAD *palette = stream->CreatePalette();
+ winamp.SetVideoPalette(palette);
+
+ // TODO: don't leak the palette
+ }
+ char *cc = (char *) & fourcc;
+ char status[512] = {0};
+ StringCchPrintfA(status, 512, WASABI_API_LNGSTRING(IDS_WINDOWS_MEDIA_XXX),
+ stream->DestinationWidth(), stream->DestinationHeight(), cc[0], cc[1], cc[2], cc[3]);
+ winamp.SetVideoStatusText(status);
+ converter = MakeConverter(stream);
+ videoOutputNum = output;
+ videoStream = stream;
+ reader->SetOutputProps(output, formatProperties);
+ formatProperties->Release();
+ return true;
+ }
+
+ delete stream;
+ stream = 0;
+
+ }
+ formatProperties->Release();
+ }
+ }
+ return false;
+}
+
+void VideoLayer::SampleReceived(QWORD &timeStamp, QWORD &duration, unsigned long &outputNum, unsigned long &flags, INSSBuffer *&sample)
+{
+ if (outputNum == videoOutputNum)
+ {
+ if (WaitForSingleObject(killSwitch, 0) == WAIT_OBJECT_0)
+ return ;
+
+ INSSBuffer3 *buff3;
+ if (SUCCEEDED(sample->QueryInterface(&buff3)))
+ {
+ short aspectHex = 0;
+ DWORD size = 2;
+ buff3->GetProperty(WM_SampleExtensionGUID_PixelAspectRatio, &aspectHex, &size);
+ if (aspectHex)
+ {
+ double newAspect = (double)((aspectHex & 0xFF00) >> 8) / (double)(aspectHex & 0xFF) ;
+
+ if (newAspect != aspect)
+ {
+ aspect = newAspect;
+ videoThread.OpenVideo(drmProtected, videoWidth, videoHeight, flip, aspect, fourcc);
+ video_output_opened=true;
+ }
+ }
+ buff3->Release();
+ }
+
+ if (!video_output_opened)
+ {
+ videoThread.OpenVideo(drmProtected, videoWidth, videoHeight, flip, aspect, fourcc);
+ video_output_opened=true;
+ }
+
+ __int64 timeDiff;
+ First().TimeToSync(timeStamp, timeDiff);
+
+ if (timeDiff < -VIDEO_ACCEPTABLE_DROP) // late
+ {
+ timeDiff = -timeDiff;
+ if (config_video_catchup) First().VideoCatchup(timeDiff);
+ if (config_video_framedropoffset) this->VideoFrameDrop((DWORD)(timeDiff / 10000));
+ if (config_video_notifylate) reader2->NotifyLateDelivery(timeDiff);
+
+ // drop the frame
+ }
+ else // early
+ {
+ while (!videoThread.AddBuffer(sample, timeStamp, flags, drmProtected))
+ {
+ if (WaitForSingleObject(killSwitch, VIDEO_ACCEPTABLE_JITTER_MS) == WAIT_OBJECT_0)
+ return ;
+ }
+ }
+ }
+ else
+ WMHandler::SampleReceived(timeStamp, duration, outputNum, flags, sample);
+}
+
+void VideoLayer::Opened()
+{
+ WORD stream = 0;
+ WMT_ATTR_DATATYPE type = WMT_TYPE_BOOL;
+ BOOL value;
+ WORD valueLen = sizeof(value);
+ header->GetAttributeByName(&stream, g_wszWMProtected, &type, (BYTE *)&value, &valueLen);
+ drmProtected = !!value;
+
+ ResetEvent(killSwitch);
+ if (OpenVideo())
+ {
+ ResetEvent(killSwitch);
+ HRESULT hr;
+
+ BOOL dedicatedThread = config_video_dedicated_thread ? TRUE : FALSE;
+ hr = reader2->SetOutputSetting(videoOutputNum, g_wszDedicatedDeliveryThread, WMT_TYPE_BOOL, (BYTE *) & dedicatedThread, sizeof(dedicatedThread));
+ assert(hr == S_OK);
+
+ earlyDelivery = config_video_early ? config_video_early_pad : 0;
+ hr = reader2->SetOutputSetting(videoOutputNum, g_wszEarlyDataDelivery, WMT_TYPE_DWORD, (BYTE *) & earlyDelivery , sizeof(earlyDelivery));
+ assert(hr == S_OK);
+
+ BOOL outOfOrder = config_video_outoforder ? TRUE : FALSE;
+ hr = reader2->SetOutputSetting(videoOutputNum, g_wszDeliverOnReceive, WMT_TYPE_BOOL, (BYTE *) & outOfOrder, sizeof(outOfOrder));
+ assert(hr == S_OK);
+
+ BOOL justInTime = config_lowmemory ? TRUE : FALSE;
+ hr = reader2->SetOutputSetting(videoOutputNum, g_wszJustInTimeDecode, WMT_TYPE_BOOL, (BYTE *) & justInTime, sizeof(justInTime));
+ assert(hr == S_OK);
+ }
+ else
+ {
+ videoOpened = false;
+ }
+
+ WMHandler::Opened();
+}
+
+void VideoLayer::VideoFrameDrop(DWORD lateness)
+{
+ //earlyDelivery+=lateness;
+ lateness += earlyDelivery;
+ HRESULT hr = reader2->SetOutputSetting(videoOutputNum, g_wszEarlyDataDelivery, WMT_TYPE_DWORD, (BYTE *) & lateness, sizeof(lateness));
+ assert(hr == S_OK);
+}
+
+void VideoLayer::Closed()
+{
+ if (video_output_opened)
+ {
+ videoThread.CloseVideo(drmProtected);
+ video_output_opened = false;
+ }
+ videoOpened = false;
+ delete videoStream;
+ videoStream=0;
+ WMHandler::Closed();
+}
+
+
+bool VideoLayer::IsOpen()
+{
+ return videoOpened;
+}
+
+void VideoLayer::HasVideo(bool &video)
+{
+ video=videoOpened;
+}
+
+void VideoLayer::Kill()
+{
+ SetEvent(killSwitch);
+ if (videoOpened)
+ videoThread.SignalStop();//SignalStop();
+
+ WMHandler::Kill();
+ if (videoOpened)
+ videoThread.WaitForStop();
+}
+
+void VideoLayer::Started()
+{
+ ResetEvent(killSwitch);
+ if (videoOpened)
+ videoThread.Start(converter, &First());
+ WMHandler::Started();
+}
+
+void VideoLayer::Stopped()
+{
+ if (videoOpened)
+ videoThread.Stop();
+ WMHandler::Stopped();
+}
diff --git a/Src/Plugins/Input/in_wmvdrm/VideoLayer.h b/Src/Plugins/Input/in_wmvdrm/VideoLayer.h
new file mode 100644
index 00000000..1f95ccdc
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/VideoLayer.h
@@ -0,0 +1,62 @@
+#ifndef NULLSOFT_VIDEOLAYERH
+#define NULLSOFT_VIDEOLAYERH
+
+#include "WMHandler.h"
+#include "OutputStream.h"
+#include <wmsdk.h>
+#include "VideoDataConverter.h"
+#include "Config.h"
+#include "VideoThread.h"
+
+#define VIDEO_ACCEPTABLE_JITTER (config_video_jitter*10000)
+#define VIDEO_ACCEPTABLE_JITTER_MS (config_video_jitter)
+
+class VideoLayer : public WMHandler
+{
+public:
+ VideoLayer(IWMReader *_reader);
+ ~VideoLayer();
+ bool IsOpen();
+ void Kill();
+private:
+ // WMHandler
+ void VideoFrameDrop(DWORD lateness);
+ void SampleReceived(QWORD &timeStamp, QWORD &duration, unsigned long &outputNum, unsigned long &flags, INSSBuffer *&sample);
+ void Opened();
+ void AudioBufferMilliseconds(long ms);
+ void Closed();
+
+ void Started();
+ void Stopped();
+ void HasVideo(bool &video);
+ // utility methods
+ bool AttemptOpenVideo(VideoOutputStream *attempt);
+ bool OpenVideo();
+
+ // other people's data
+ IWMReader *reader;
+
+ // our data
+ IWMReaderAdvanced2 *reader2;
+ IWMHeaderInfo *header;
+ int videoOutputNum;
+ DWORD offset;
+ long nextRest;
+ VideoDataConverter *converter;
+ VideoOutputStream *videoStream;
+
+ bool videoOpened;
+ QWORD catchupTime;
+ double aspect;
+ int fourcc;
+ bool flip;
+ int videoWidth, videoHeight;
+ HANDLE killSwitch;
+ DWORD earlyDelivery;
+ bool drmProtected;
+ bool video_output_opened;
+ VideoThread videoThread;
+
+};
+
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/VideoOutputChildDDraw.cpp b/Src/Plugins/Input/in_wmvdrm/VideoOutputChildDDraw.cpp
new file mode 100644
index 00000000..511fb80a
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/VideoOutputChildDDraw.cpp
@@ -0,0 +1,80 @@
+#include "main.h"
+#include "VideoOutputChildDDraw.h"
+#include <multimon.h>
+#include <ddraw.h>
+
+class MonitorFinder
+{
+public:
+ MonitorFinder(HMONITOR hm) : m_monitor_to_find(hm), m_found_devguid(0)
+ {}
+
+ HMONITOR m_monitor_to_find;
+ int m_found_devguid;
+ GUID m_devguid;
+};
+
+static BOOL WINAPI DDEnumCallbackEx(GUID FAR *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, LPVOID lpContext, HMONITOR hm)
+{
+ MonitorFinder *ovo = (MonitorFinder *)lpContext;
+ if (ovo->m_found_devguid) return 1;
+ if (hm == ovo->m_monitor_to_find)
+ {
+ ovo->m_devguid = *lpGUID;
+ ovo->m_found_devguid = 1;
+ }
+ return 1;
+}
+
+void VideoOutputChildDDraw::update_monitor_coords()
+{
+ //find the correct monitor if multiple monitor support is present
+ m_mon_x = 0;
+ m_mon_y = 0;
+
+ HINSTANCE h = LoadLibrary(L"user32.dll");
+ if (h)
+ {
+ HMONITOR (WINAPI *Mfp)(POINT pt, DWORD dwFlags) = (HMONITOR (WINAPI *)(POINT, DWORD)) GetProcAddress(h, "MonitorFromPoint");
+ HMONITOR (WINAPI *Mfr)(LPCRECT lpcr, DWORD dwFlags) = (HMONITOR (WINAPI *)(LPCRECT, DWORD)) GetProcAddress(h, "MonitorFromRect");
+ HMONITOR (WINAPI *Mfw)(HWND wnd, DWORD dwFlags) = (HMONITOR (WINAPI *)(HWND, DWORD)) GetProcAddress(h, "MonitorFromWindow");
+ BOOL (WINAPI *Gmi)(HMONITOR mon, LPMONITORINFO lpmi) = (BOOL (WINAPI *)(HMONITOR, LPMONITORINFO)) GetProcAddress(h, "GetMonitorInfoA");
+ if (Mfp && Mfr && Mfw && Gmi)
+ {
+ HMONITOR hm = Mfw(parent, 0);
+ if (hm)
+ {
+ HINSTANCE hdd = LoadLibrary(L"ddraw.dll");
+ if (hdd)
+ {
+ typedef BOOL (FAR PASCAL * LPDDENUMCALLBACKEXA)(GUID FAR *, LPSTR, LPSTR, LPVOID, HMONITOR);
+ typedef HRESULT (WINAPI * LPDIRECTDRAWENUMERATEEX)( LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags);
+ LPDIRECTDRAWENUMERATEEX lpDDEnumEx;
+ lpDDEnumEx = (LPDIRECTDRAWENUMERATEEX) GetProcAddress(hdd, "DirectDrawEnumerateExW");
+ if (lpDDEnumEx)
+ {
+ MonitorFinder finder(hm);
+
+ lpDDEnumEx(&DDEnumCallbackEx, &finder, DDENUM_ATTACHEDSECONDARYDEVICES | DDENUM_NONDISPLAYDEVICES);
+ foundGUID=!!finder.m_found_devguid;
+ if (foundGUID)
+ {
+ m_devguid=finder.m_devguid;
+ MONITORINFOEXW mi;
+ memset(&mi, 0, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+ if (Gmi(hm, &mi))
+ {
+ m_mon_x = mi.rcMonitor.left;
+ m_mon_y = mi.rcMonitor.top;
+ }
+ }
+ }
+ FreeLibrary(hdd);
+ }
+ }
+ }
+ FreeLibrary(h);
+ }
+}
+
diff --git a/Src/Plugins/Input/in_wmvdrm/VideoOutputChildDDraw.h b/Src/Plugins/Input/in_wmvdrm/VideoOutputChildDDraw.h
new file mode 100644
index 00000000..e5f72d50
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/VideoOutputChildDDraw.h
@@ -0,0 +1,17 @@
+#ifndef NULLSOFT_VIDEOOUTPUTCHILDDDRAWH
+#define NULLSOFT_VIDEOOUTPUTCHILDDDRAWH
+#include "../Winamp/VideoOutputChild.h"
+
+class VideoOutputChildDDraw : public VideoRenderer
+{
+public:
+ VideoOutputChildDDraw() : m_mon_x(0), m_mon_y(0), foundGUID(false), parent(0), adjuster(0) {}
+ VideoAspectAdjuster *adjuster;
+ void update_monitor_coords();
+ int m_mon_x, m_mon_y;
+ bool foundGUID;
+ GUID m_devguid;
+ HWND parent;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/VideoThread.cpp b/Src/Plugins/Input/in_wmvdrm/VideoThread.cpp
new file mode 100644
index 00000000..fd6f1f09
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/VideoThread.cpp
@@ -0,0 +1,166 @@
+#include "Main.h"
+#include "VideoThread.h"
+#include "VideoLayer.h"
+#include "config.h"
+#include <windows.h>
+
+DWORD WINAPI VidThread_stub(void *ptr)
+{
+ ((VideoThread *)ptr)->VidThread();
+ return 0;
+}
+
+VideoThread::VideoThread() : converter(0), clock(0)
+{
+ drm = false;
+
+ DWORD id;
+ thread = CreateThread(NULL, 256*1024, VidThread_stub, (void *)this, NULL, &id);
+ SetThreadPriority(thread, AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
+}
+
+void VideoThread::Start(VideoDataConverter *_converter, WMHandler *_clock)
+{
+ clock = _clock;
+ if (converter != _converter)
+ {
+ if (converter)
+ delete converter;
+ converter = _converter;
+ }
+ ResetEvent(stopped);
+ QueueUserAPC(MediaThread_StartAPC, thread, reinterpret_cast<ULONG_PTR>(this));
+}
+
+void VideoThread::VidThread()
+{
+ while (true)
+ {
+ switch (WaitForSingleObjectEx(killEvent, wait, TRUE))
+ {
+ case WAIT_OBJECT_0:
+ //StopAPC();
+ return;
+
+ case WAIT_TIMEOUT:
+ {
+ if (buffers.empty())
+ {
+ SetEvent(bufferFreed);
+ continue;
+ }
+
+ MediaBuffer *buffer = buffers.front();
+
+ __int64 diff;
+ clock->TimeToSync(buffer->timestamp, diff);
+ if (diff < VIDEO_ACCEPTABLE_JITTER)
+ {
+ void *data;
+ DWORD size;
+
+ buffer->buffer->GetBufferAndLength((BYTE **)&data, &size);
+ if (buffer->drmProtected)
+ winamp.EncryptedDrawFrame(converter->Convert(data));
+ else
+ winamp.DrawFrame(converter->Convert(data));
+
+ try {
+ buffer->buffer->Release();
+ delete buffer;
+ } catch (...) {}
+
+ //buffers.pop_front();
+ if (buffers.size())
+ {
+ buffers.erase(buffers.begin());
+ }
+ }
+ if (buffers.size() < config_video_cache_frames)
+ SetEvent(bufferFreed);
+ }
+ continue;
+
+ default:
+ continue;
+ }
+ }
+}
+
+void VideoThread::AddAPC(MediaBuffer *buffer)
+{
+ if (buffers.empty())
+ {
+ __int64 diff;
+ clock->TimeToSync(buffer->timestamp, diff);
+ if (diff < VIDEO_ACCEPTABLE_JITTER)
+ {
+ void *data;
+ DWORD size;
+ buffer->buffer->GetBufferAndLength((BYTE **)&data, &size);
+ if (buffer->drmProtected)
+ winamp.EncryptedDrawFrame(converter->Convert(data));
+ else
+ winamp.DrawFrame(converter->Convert(data));
+
+ buffer->buffer->Release();
+ if (buffers.size() >= config_video_cache_frames)
+ ResetEvent(bufferFreed);
+ return;
+ }
+ }
+
+ OrderedInsert(buffer);
+
+ if (buffers.size() >= config_video_cache_frames)
+ ResetEvent(bufferFreed);
+}
+
+struct VideoOpenParameters
+{
+ int width;
+ int height;
+ int color_format;
+ double aspect;
+ int flip;
+ bool drm;
+};
+
+VOID CALLBACK VideoThread::VideoThread_VideoOpenAPC(ULONG_PTR params)
+{
+ VideoOpenParameters *p = (VideoOpenParameters *)params;
+ if (p->drm)
+ {
+ winamp.OpenEncryptedVideo(p->width, p->height, !!p->flip, p->aspect, p->color_format);
+ }
+ else
+ {
+ winamp.OpenVideo(p->width, p->height, !!p->flip, p->aspect, p->color_format);
+ }
+}
+
+void VideoThread::OpenVideo(bool drm, int width, int height, bool flip, double aspect, int fourcc)
+{
+ VideoOpenParameters *p = new VideoOpenParameters;
+ p->width = width;
+ p->height = height;
+ p->color_format = fourcc;
+ p->aspect = aspect;
+ p->flip = flip;
+ p->drm = drm;
+ this->drm = drm;
+ QueueUserAPC(VideoThread_VideoOpenAPC, thread, reinterpret_cast<ULONG_PTR>(p));
+}
+
+VOID CALLBACK VideoThread::VideoThread_VideoCloseAPC(ULONG_PTR params)
+{
+ if (params)
+ winamp.CloseEncryptedVideo();
+ else
+ winamp.CloseVideo();
+}
+
+void VideoThread::CloseVideo(bool drm)
+{
+ QueueUserAPC(VideoThread_VideoCloseAPC, thread, static_cast<ULONG_PTR>(drm));
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/VideoThread.h b/Src/Plugins/Input/in_wmvdrm/VideoThread.h
new file mode 100644
index 00000000..2127710f
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/VideoThread.h
@@ -0,0 +1,37 @@
+#ifndef NULLSOFTVIDEOTHREADH
+#define NULLSOFTVIDEOTHREADH
+
+#include "VideoDataConverter.h"
+#include "WMHandler.h"
+#include <deque>
+#include <wmsdk.h>
+#include "MediaThread.h"
+
+class VideoThread : public MediaThread
+{
+public:
+ VideoThread();
+ void Start(VideoDataConverter *_converter, WMHandler *_clock);
+
+ /* AddBuffers put a video buffer in the queue
+ it returns true if it was added
+ it returns false if it was NOT added. it is up to YOU (the caller) to sleep for a while and call again
+ */
+ void VidThread();
+
+ void OpenVideo(bool drm, int width, int height, bool flip, double aspect, int fourcc);
+ void CloseVideo(bool drm);
+private:
+ static VOID CALLBACK VideoThread_VideoOpenAPC(ULONG_PTR params);
+ static VOID CALLBACK VideoThread_VideoCloseAPC(ULONG_PTR params);
+
+
+ void AddAPC(MediaBuffer *);
+ VideoDataConverter *converter;
+ WMHandler *clock;
+ bool drm;
+
+};
+
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/WMCallback.cpp b/Src/Plugins/Input/in_wmvdrm/WMCallback.cpp
new file mode 100644
index 00000000..cc92ce70
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/WMCallback.cpp
@@ -0,0 +1,294 @@
+#include "Main.h"
+#include "WMCallback.h"
+#include <algorithm>
+#include "WMHandler.h"
+#include "util.h"
+#include <cassert>
+
+#define CAST_TO(x) if (riid== IID_##x) { *ppvObject=static_cast<x *>(this); AddRef(); return S_OK; }
+#define CAST_TO_VIA(x,y) if (riid== IID_##x) { *ppvObject=static_cast<y *>(this); AddRef(); return S_OK; }
+HRESULT WMCallback::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject )
+{
+ CAST_TO(IWMReaderCallback);
+ CAST_TO_VIA(IUnknown, IWMReaderCallback);
+ CAST_TO(IWMReaderCallbackAdvanced);
+#ifdef _DEBUG
+ CAST_TO(IWMCredentialCallback);
+#endif
+
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+}
+
+ULONG WMCallback::AddRef()
+{
+ return InterlockedIncrement(&refCount);
+}
+
+ULONG WMCallback::Release()
+{
+ if (InterlockedDecrement(&refCount) == 0)
+ {
+ delete this;
+ return 0;
+ }
+
+ return refCount;
+}
+
+
+HRESULT WMCallback::OnStatus(WMT_STATUS Status, HRESULT hr, WMT_ATTR_DATATYPE dwType,
+ BYTE __RPC_FAR *pValue, void __RPC_FAR *pvContext)
+{
+ if (!handler)
+ return S_OK;
+
+ switch (Status)
+ {
+ WMTCASE(WMT_INIT_PLAYLIST_BURN)
+ handler->InitPlaylistBurn();
+ break;
+
+ WMTCASE(WMT_NO_RIGHTS)
+ handler->NoRights((wchar_t *)pValue);
+ break;
+
+ WMTCASE(WMT_NO_RIGHTS_EX)
+ handler->NoRightsEx((WM_GET_LICENSE_DATA *&)pValue);
+ break;
+
+ WMTCASE(WMT_NEEDS_INDIVIDUALIZATION)
+ handler->Individualize();
+ break;
+
+ WMTCASE(WMT_END_OF_STREAMING)
+ break;
+
+ WMTCASE(WMT_LICENSEURL_SIGNATURE_STATE)
+ handler->SignatureState((WMT_DRMLA_TRUST *&)pValue);
+ break;
+
+ WMTCASE(WMT_ACQUIRE_LICENSE)
+ WMT_SHOW_HR_CODE(hr)
+ switch (hr)
+ {
+ case NS_S_DRM_LICENSE_ACQUIRED:
+ handler->LicenseAcquired();
+ break;
+ case NS_S_DRM_MONITOR_CANCELLED:
+ handler->MonitorCancelled();
+ break;
+ case NS_S_DRM_ACQUIRE_CANCELLED:
+ handler->SilentCancelled();
+ break;
+ default:
+ handler->AcquireLicense((WM_GET_LICENSE_DATA *&)pValue);
+ }
+ break;
+
+ WMTCASE(WMT_INDIVIDUALIZE)
+ handler->IndividualizeStatus((WM_INDIVIDUALIZE_STATUS *)pValue);
+ break;
+
+ //the file has been opened
+ WMTCASE(WMT_OPENED)
+ if (SUCCEEDED(hr))
+ handler->Opened();
+ else
+ {
+ switch (hr)
+ {
+ WMTCASE(NS_E_DRM_APPCERT_REVOKED)
+ WMTCASE(NS_E_DRM_LICENSE_APP_NOTALLOWED)
+ handler->DRMExpired();
+ WMTCASE(NS_E_LICENSE_REQUIRED)
+ handler->LicenseRequired();
+ break;
+ WMTCASE(NS_E_DRM_NEEDS_INDIVIDUALIZATION)
+ handler->NeedsIndividualization();
+ break;
+ WMTCASE(E_ACCESSDENIED)
+ handler->AccessDenied();
+ break;
+ default:
+ WMT_SHOW_HR_CODE(hr);
+ handler->Error();
+ return S_OK;
+ }
+ handler->OpenCalled();
+ }
+
+ break;
+
+ // Playback of the opened file has begun.
+ WMTCASE( WMT_STARTED)
+ if (SUCCEEDED(hr))
+ handler->Started();
+ else
+ {
+ switch (hr)
+ {
+ WMTCASE(E_ABORT)
+ //handler->OpenFailed();
+ break;
+ WMTCASE(NS_E_DRM_REOPEN_CONTENT)
+ handler->Error();
+ break;
+ default:
+ WMT_SHOW_HR_CODE(hr);
+ handler->Error();
+ break;
+ }
+ }
+ break;
+
+ WMTCASE( WMT_NEW_METADATA)
+ if (SUCCEEDED(hr))
+ handler->NewMetadata();
+ break;
+
+ // The previously playing reader has stopped.
+ WMTCASE( WMT_STOPPED)
+ if (SUCCEEDED(hr))
+ handler->Stopped();
+ else
+ {
+ WMT_SHOW_HR_CODE(hr);
+ handler->Error();
+ }
+ break;
+
+ // The previously playing reader has stopped.
+ WMTCASE( WMT_CLOSED)
+ if (SUCCEEDED(hr))
+ handler->Closed();
+ else
+ {
+ WMT_SHOW_HR_CODE(hr);
+ handler->Error();
+ }
+ break;
+
+ WMTCASE(WMT_ERROR)
+ WMT_SHOW_HR_CODE(hr);
+ //handler->Error();
+
+ break;
+
+ WMTCASE( WMT_BUFFERING_START)
+ if (SUCCEEDED(hr))
+ handler->BufferingStarted();
+ break;
+
+ WMTCASE( WMT_BUFFERING_STOP)
+ if (SUCCEEDED(hr))
+ handler->BufferingStopped();
+ break;
+
+ WMTCASE( WMT_EOF)
+ WMT_SHOW_HR_CODE(hr);
+ handler->EndOfFile();
+ break;
+
+ WMTCASE( WMT_LOCATING)
+ handler->Locating();
+ break;
+
+ WMTCASE( WMT_CONNECTING)
+ handler->Connecting();
+ break;
+
+ WMTCASE( WMT_PREROLL_READY)
+ break;
+
+ WMTCASE( WMT_PREROLL_COMPLETE)
+ break;
+ WMTCASE(WMT_NEW_SOURCEFLAGS)
+ break;
+ WMTCASE(WMT_MISSING_CODEC)
+
+ WMT_SHOW_HR_CODE(hr);
+#ifdef DEBUG
+ std::cerr << dwType << std::endl;
+ std::wcerr << GuidString(*(GUID *)pValue) << std::endl;
+#endif
+ break;
+ default:
+ #ifdef DEBUG
+ std::cerr << "unknown message = " << Status << std::endl;
+ #endif
+ break;
+ };
+ return S_OK;
+}
+
+HRESULT WMCallback::OnStreamSelection(WORD wStreamCount, WORD *pStreamNumbers, WMT_STREAM_SELECTION *pSelections, void *pvContext)
+{
+ #ifdef DEBUG
+ std::cerr << "OnStreamSelection" << std::endl;
+ #endif
+ return E_NOTIMPL;
+}
+
+HRESULT WMCallback::OnOutputPropsChanged(DWORD dwOutputNum, WM_MEDIA_TYPE *pMediaType, void *pvContext)
+{
+ #ifdef DEBUG
+ std::cerr << "OnOutputPropsChanged" << std::endl;
+ #endif
+
+ return E_NOTIMPL;
+}
+
+HRESULT WMCallback::AllocateForStream(WORD wStreamNum, DWORD cbBuffer, INSSBuffer **ppBuffer, void *pvContext)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT WMCallback::AllocateForOutput(DWORD dwOutputNum, DWORD cbBuffer, INSSBuffer **ppBuffer, void *pvContext)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT WMCallback::OnStreamSample(WORD wStreamNum, QWORD cnsSampleTime, QWORD cnsSampleDuration, DWORD dwFlags, INSSBuffer *pSample, void *pvContext)
+{
+ return E_NOTIMPL;
+}
+
+// 0x5A, 0xA5, 0x00, 0x03, 0x74, 0x00, 0x01, 0x01, 0x77,
+//------------------------------------------------------------------------------
+// Name: CWAPlugin::OnSample()
+// Desc: IWMReaderCallback method to process samples.
+//------------------------------------------------------------------------------
+HRESULT WMCallback::OnSample(DWORD dwOutputNum, QWORD cnsSampleTime,
+ QWORD cnsSampleDuration, DWORD dwFlags,
+ INSSBuffer __RPC_FAR *pSample, void __RPC_FAR *pvContext)
+{
+ if (!handler)
+ return S_OK;
+ handler->SampleReceived(cnsSampleTime, cnsSampleDuration, dwOutputNum, dwFlags, pSample);
+ return S_OK;
+}
+
+HRESULT WMCallback::OnTime(QWORD cnsCurrentTime, void *pvContext)
+{
+ if (!handler)
+ return S_OK;
+ handler->TimeReached(cnsCurrentTime);
+ return S_OK;
+}
+
+
+HRESULT WMCallback::AcquireCredentials(WCHAR* pwszRealm, WCHAR* pwszSite,
+ WCHAR* pwszUser, DWORD cchUser,
+ WCHAR* pwszPassword, DWORD cchPassword,
+ HRESULT hrStatus, DWORD* pdwFlags)
+{
+#ifdef _DEBUG
+ std::cout << "WMCallback::AcquireCredentials" << std::endl;
+ std::wcout << pwszRealm << std::endl;
+ std::wcout << pwszSite << std::endl;
+ std::wcout << HRErrorCode(hrStatus) << std::endl;
+ return S_OK;
+#endif
+ return E_NOTIMPL;
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/WMCallback.h b/Src/Plugins/Input/in_wmvdrm/WMCallback.h
new file mode 100644
index 00000000..4ae68c42
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/WMCallback.h
@@ -0,0 +1,52 @@
+#ifndef NULLSOFT_WMCALLBACK
+#define NULLSOFT_WMCALLBACK
+
+#include <wmsdk.h>
+#include <deque>
+#include "WMHandler.h"
+
+
+class WMCallback : public IWMReaderCallback, public IWMReaderCallbackAdvanced, public IWMCredentialCallback
+{
+
+public:
+ WMCallback() : refCount(0), handler(0)
+ {
+ AddRef();
+ }
+
+ ~WMCallback()
+ {
+ }
+
+ WMHandler &operator >> (WMHandler *_handler)
+ {
+ handler = _handler;
+ return *handler;
+ }
+
+
+private:
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+ HRESULT STDMETHODCALLTYPE OnStatus(WMT_STATUS Status, HRESULT hr, WMT_ATTR_DATATYPE dwType, BYTE __RPC_FAR *pValue, void __RPC_FAR *pvContext);
+ virtual HRESULT STDMETHODCALLTYPE OnSample(DWORD dwOutputNum, QWORD cnsSampleTime, QWORD cnsSampleDuration, DWORD dwFlags, INSSBuffer __RPC_FAR *pSample, void __RPC_FAR *pvContext);
+
+ /* IWMReaderCallbackAdvanced */
+ HRESULT STDMETHODCALLTYPE OnStreamSample(WORD wStreamNum, QWORD cnsSampleTime, QWORD cnsSampleDuration, DWORD dwFlags, INSSBuffer *pSample, void *pvContext);
+ HRESULT STDMETHODCALLTYPE OnTime(QWORD cnsCurrentTime, void *pvContext);
+ HRESULT STDMETHODCALLTYPE OnStreamSelection(WORD wStreamCount, WORD *pStreamNumbers, WMT_STREAM_SELECTION *pSelections, void *pvContext);
+ HRESULT STDMETHODCALLTYPE OnOutputPropsChanged(DWORD dwOutputNum, WM_MEDIA_TYPE *pMediaType, void *pvContext);
+ HRESULT STDMETHODCALLTYPE AllocateForStream(WORD wStreamNum, DWORD cbBuffer, INSSBuffer **ppBuffer, void *pvContext);
+ HRESULT STDMETHODCALLTYPE AllocateForOutput(DWORD dwOutputNum, DWORD cbBuffer, INSSBuffer **ppBuffer, void *pvContext);
+
+ /* IWMCredentialCallback */
+ HRESULT STDMETHODCALLTYPE AcquireCredentials(WCHAR* pwszRealm, WCHAR* pwszSite, WCHAR* pwszUser, DWORD cchUser, WCHAR* pwszPassword, DWORD cchPassword, HRESULT hrStatus, DWORD* pdwFlags);
+
+ long refCount;
+ WMHandler *handler;
+
+};
+
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/WMDRMModule.cpp b/Src/Plugins/Input/in_wmvdrm/WMDRMModule.cpp
new file mode 100644
index 00000000..92a6db3c
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/WMDRMModule.cpp
@@ -0,0 +1,649 @@
+#include "Main.h"
+#include "WMDRMModule.h"
+#include "AutoWide.h"
+#include "WMInformation.h"
+#include "AutoChar.h"
+#include "FileInfoDialog.h"
+#include "ConfigDialog.h"
+#include "resource.h"
+#include "StatusHook.h"
+#include "../nu/Config.h"
+#include "util.h"
+#include "WMPlaylist.h"
+#include "api.h"
+#include "output/OutPlugin.h"
+#include "output/AudioOut.h"
+#include <strsafe.h>
+
+extern Nullsoft::Utility::Config wmConfig;
+
+#define SAMPLES_PER_BLOCK 576
+AudioOut *out = 0;
+
+unsigned long endTime = 0;
+unsigned long startTime = 0;
+
+void InitOutputs(HWND hMainWindow, HMODULE hDllInstance)
+{}
+
+void WMDRM::AssignOutput()
+{
+ out = &pluginOut;
+}
+
+WMDRM::WMDRM()
+ : paused(false),
+ clock(0), audio(0), video(0), wait(0), info(0), buffer(0), seek(0), reader(NULL), gain(0),
+ killswitch(0), opened(false),
+ drmProtected(false),
+ volume(-666), pan(0),
+ reader2(0), network(0), playing(false),
+ startAtMilliseconds(0),
+ dspBuffer(0),
+ vizBuffer(0),
+ reader1(0),
+ flushed(false)
+{
+ killswitch = CreateEvent(NULL, TRUE, TRUE, NULL);
+}
+
+WMDRM::~WMDRM()
+{
+ DeleteObject(killswitch);
+ delete [] dspBuffer;
+ delete [] vizBuffer;
+}
+
+static int winampVersion=0;
+
+#define WINAMP_VERSION_MINOR1(winampVersion) ((winampVersion & 0x000000F0)>>4) // returns, i.e. 0x01 for 5.12 and 0x02 for 5.2...
+#define WINAMP_VERSION_MINOR2(winampVersion) ((winampVersion & 0x0000000F)) // returns, i.e. 0x02 for 5.12 and 0x00 for 5.2...
+static void MakeVersionString(QWORD *ver)
+{
+ if (!winampVersion)
+ winampVersion = (int)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GETVERSION);
+
+ LARGE_INTEGER temp;
+ temp.HighPart = MAKELONG(WINAMP_VERSION_MINOR1(winampVersion), WINAMP_VERSION_MAJOR(winampVersion));
+ temp.LowPart = MAKELONG(WASABI_API_APP->main_getBuildNumber(), WINAMP_VERSION_MINOR2(winampVersion));
+
+ *ver = temp.QuadPart;
+}
+
+static void MakeUserAgentString(wchar_t str[256])
+{
+ if (!winampVersion)
+ winampVersion = (int)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GETVERSION);
+
+ StringCchPrintfW(str, 256, L"WinampASF/%01x.%02x",
+ WINAMP_VERSION_MAJOR(winampVersion),
+ WINAMP_VERSION_MINOR(winampVersion));
+}
+
+
+void WMDRM::InitWM()
+{
+ static int triedInit = 0;
+ if (!triedInit)
+ {
+ if (FAILED(WMCreateReader(0, WMT_RIGHT_PLAYBACK, &reader)) || !reader)
+ {
+ reader = 0;
+ plugin.FileExtensions = "\0";
+ return ;
+ }
+ if (FAILED(reader->QueryInterface(&reader1)))
+ reader1 = 0;
+
+ if (FAILED(reader->QueryInterface(&reader2)))
+ reader2 = 0;
+
+ if (FAILED(reader->QueryInterface(&network)))
+ network = 0;
+
+ if (reader1)
+ {
+ QWORD verStr;
+ wchar_t userAgent[256] = {0};
+ MakeVersionString(&verStr);
+ MakeUserAgentString(userAgent);
+ WM_READER_CLIENTINFO info;
+ ZeroMemory(&info, sizeof(WM_READER_CLIENTINFO));
+ info.cbSize = sizeof(WM_READER_CLIENTINFO);
+ info.wszHostExe = L"winamp.exe";
+ info.qwHostVersion = verStr;
+ info.wszPlayerUserAgent = userAgent;
+ info.wszBrowserWebPage = L"http://www.winamp.com";
+
+ reader1->SetClientInfo(&info);
+ }
+
+ clock = new ClockLayer(reader);
+ audio = new AudioLayer(reader);
+ video = new VideoLayer(reader);
+ wait = new WaitLayer(reader);
+ info = new WMInformation(reader);
+ buffer = new BufferLayer(reader);
+ seek = new SeekLayer(reader, clock);
+ gain = new GainLayer(audio, info);
+
+ callback >> seek >> buffer >> clock >> video >> audio >> wait >> gain >> this;
+
+ triedInit = 1;
+ }
+}
+
+void WMDRM::Init()
+{
+ winampVersion = (int)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GETVERSION);
+ InitOutputs(plugin.hMainWindow, plugin.hDllInstance);
+
+ //Hook(plugin.hMainWindow);
+}
+
+void WMDRM::Config(HWND hwndParent)
+{
+ WASABI_API_DIALOGBOXW(IDD_CONFIG, hwndParent, PreferencesDialogProc);
+}
+
+void WMDRM::Quit()
+{
+ activePlaylist.Clear();
+ delete setFileInfo; setFileInfo = 0;
+ delete clock; clock = 0;
+ delete audio; audio = 0;
+ delete video; video = 0;
+ delete wait; wait = 0;
+ delete info; info = 0;
+ delete buffer; buffer = 0;
+ delete seek; seek = 0;
+
+ if (network) network->Release(); network = 0;
+ if (reader2) reader2->Release(); reader2 = 0;
+ if (reader1) reader1->Release(); reader1 = 0;
+ if (reader) reader->Release(); reader = 0;
+
+// Unhook(plugin.hMainWindow);
+}
+
+static void BuildTitle(WMInformation *info, const wchar_t *file, wchar_t *str, size_t len)
+{
+ if (info)
+ {
+ wchar_t artist[256] = L"", title[256] = L"";
+ info->GetAttribute(g_wszWMAuthor, artist, 256);
+ info->GetAttribute(g_wszWMTitle, title, 256);
+
+ if (!artist[0] && !title[0])
+ {
+ if (file && *file)
+ {
+ StringCchCopy(str, len, file);
+ }
+ }
+ else if (artist[0] && title[0])
+ StringCchPrintf(str, len, L"%s - %s", artist, title);
+ else if (artist[0])
+ StringCchCopy(str, len, artist);
+ else if (title[0])
+ StringCchCopy(str, len, title);
+ }
+ else if (file)
+ StringCchCopy(str, len, file);
+
+}
+
+void WMDRM::GetFileInfo(const wchar_t *file, wchar_t *title, int *length_in_ms)
+{
+ InitWM();
+
+ if (length_in_ms) *length_in_ms = -1000;
+ if (file && file[0])
+ {
+ bool isURL = !!PathIsURL(file);
+ if (config_http_metadata || !isURL)
+ {
+ WMInformation getFileInfo(file, true);
+
+ if (title)
+ {
+ BuildTitle(&getFileInfo, file, title, GETFILEINFO_TITLE_LENGTH);
+ }
+ if (length_in_ms) *length_in_ms = getFileInfo.GetLengthMilliseconds();
+ }
+ else
+ {
+ if (title)
+ StringCchCopy(title, GETFILEINFO_TITLE_LENGTH, file);
+ }
+ winamp.GetStatus(title, 256, file);
+ }
+ else if (activePlaylist.GetFileName())
+ {
+ //isURL = !!wcsstr(activePlaylist.GetFileName(), L"://");
+ if (wait && !wait->IsOpen()) // if it's not open, fill in with some default data... WMDRM::Opened() will refresh the title ...
+ {
+ StringCchCopy(title, GETFILEINFO_TITLE_LENGTH, activePlaylist.GetOriginalFileName());
+ if (length_in_ms) *length_in_ms = -1000;
+ //if (isURL)
+ winamp.GetStatus(title, 256, activePlaylist.GetOriginalFileName());
+ return ;
+ }
+
+ if (title)
+ {
+ BuildTitle(info, activePlaylist.GetOriginalFileName(), title, GETFILEINFO_TITLE_LENGTH);
+ }
+ if (info)
+ if (length_in_ms) *length_in_ms = info->GetLengthMilliseconds();
+ else
+ if (length_in_ms) *length_in_ms = -1000;
+
+ //if (isURL)
+ winamp.GetStatus(title, 256, activePlaylist.GetOriginalFileName());
+ }
+}
+
+int WMDRM::InfoBox(const wchar_t *fn, HWND hwndParent)
+{
+ /* CUT> we're now using the unified file info dialogue
+ FileInfoDialog dialog(WASABI_API_LNG_HINST, hwndParent, fn);
+ if (dialog.WasEdited())
+ return 0;
+ else
+ return 1;
+ */
+ return 0;
+}
+
+int WMDRM::IsOurFile(const in_char *fn)
+{
+ // if (!reader)
+ // return 0;
+ if (wcsstr(fn, L".asx")) // TODO: need something WAY better than this
+ return 1;
+ return fileTypes.IsSupportedURL(fn);
+}
+
+int WMDRM::Play(const wchar_t * fn)
+{
+ InitWM();
+
+ if (!reader)
+ return -1;
+ if (network)
+ network->SetBufferingTime((QWORD)config_buffer_time*10000LL);
+
+ ResetEvent(killswitch);
+ wait->ResetForOpen();
+
+ activePlaylist.Clear();
+ activePlaylist.playlistFilename = _wcsdup(fn);
+ if (playlistManager->Load(fn, &activePlaylist) != PLAYLISTMANAGER_SUCCESS)
+ activePlaylist.OnFile(fn, 0, -1, 0); // add it manually (TODO: need a better way to do this)
+
+ winamp.GetVideoOutput();
+ playing = true;
+ startTime = winamp.GetStart() * 1000;
+ if (!startAtMilliseconds)
+ startAtMilliseconds = startTime;
+
+ endTime = winamp.GetEnd() * 1000;
+ clock->SetLastOutputTime(startAtMilliseconds); // normally 0, but set when metadata editor needs to stop / restart a file
+ AssignOutput();
+ clock->SetStartTimeMilliseconds(startAtMilliseconds); // normally 0, but set when metadata editor needs to stop / restart a file
+ startAtMilliseconds = 0;
+ return seek->Open(activePlaylist.GetFileName(), &callback);
+}
+
+void WMDRM::ReOpen()
+{
+ if (opened)
+ seek->Stop();
+ seek->Open(activePlaylist.GetFileName(), &callback);
+}
+
+void WMDRM::Pause()
+{
+ paused = true;
+ if (seek)
+ seek->Pause();
+}
+
+void WMDRM::UnPause()
+{
+ paused = false;
+ if (seek)
+ seek->Unpause();
+}
+
+int WMDRM::IsPaused()
+{
+ return (int)paused;
+}
+
+void WMDRM::Stop()
+{
+ if (!playing)
+ return ;
+
+ playing = false;
+ SetEvent(killswitch);
+ if (paused)
+ UnPause();
+ if (seek)
+ seek->Stop();
+}
+
+
+void WMDRM::Closed()
+{
+ opened = false;
+ WMHandler::Closed();
+}
+
+int WMDRM::GetLength()
+{
+ if (info)
+ return info->GetLengthMilliseconds();
+ else
+ return 0;
+}
+
+int WMDRM::GetOutputTime()
+{
+ if (!opened)
+ {
+ //if (winamp.bufferCount)
+ return winamp.bufferCount;
+ //return 0;
+ }
+ return clock->GetOutputTime();
+}
+
+void WMDRM::SetOutputTime(int time_in_ms)
+{
+ if (startTime || endTime)
+ {
+ unsigned int seektime = time_in_ms;
+ if (endTime && seektime > endTime)
+ seektime = endTime;
+ if (startTime && seektime < startTime)
+ seektime = startTime;
+ seek->SeekTo(seektime);
+ return ;
+ }
+ seek->SeekTo(time_in_ms);
+}
+
+void WMDRM::SetVolume(int volume)
+{
+ this->volume = volume;
+ if (out)
+ out->SetVolume(volume);
+}
+
+void WMDRM::SetPan(int pan)
+{
+ this->pan = pan;
+ if (out)
+ out->SetPan(pan);
+}
+
+void WMDRM::EQSet(int on, char data[10], int preamp)
+{}
+
+void WMDRM::BuildBuffers()
+{
+ remaining.Allocate(audio->AudioSamplesToBytes(SAMPLES_PER_BLOCK));
+
+ // TODO: check against old size
+ delete [] dspBuffer;
+ delete [] vizBuffer;
+ dspBuffer = new unsigned char[audio->AudioSamplesToBytes(SAMPLES_PER_BLOCK) * 2];
+ vizBuffer = new unsigned char[audio->AudioSamplesToBytes(SAMPLES_PER_BLOCK) * 2];
+}
+
+void WMDRM::AudioDataReceived(void *_data, unsigned long sizeBytes, DWORD timestamp)
+{
+ // TODO: apply replaygain first
+ // but if we change bitdepth, we'll have to be careful about calling audio->AudioSamplesToBytes() and similiar functions
+
+ unsigned char *data = (unsigned char *)_data;
+ if (!remaining.Empty())
+ {
+ if (WaitForSingleObject(killswitch, 0) == WAIT_OBJECT_0)
+ {
+ remaining.Flush();
+ return ;
+ }
+ remaining.UpdatingWrite(data, sizeBytes);
+ if (remaining.Full())
+ {
+ OutputAudioSamples(remaining.GetData(), SAMPLES_PER_BLOCK, timestamp);
+ remaining.Flush();
+ }
+ }
+
+ long samplesLeft = audio->AudioBytesToSamples(sizeBytes);
+
+ while (samplesLeft)
+ {
+ if (WaitForSingleObject(killswitch, 0) == WAIT_OBJECT_0)
+ {
+ remaining.Flush();
+ return ;
+ }
+
+ if (samplesLeft >= SAMPLES_PER_BLOCK)
+ {
+ OutputAudioSamples(data, SAMPLES_PER_BLOCK, timestamp);
+ data += audio->AudioSamplesToBytes(SAMPLES_PER_BLOCK);
+ samplesLeft -= SAMPLES_PER_BLOCK;
+ }
+ else
+ {
+ unsigned long bytesLeft = audio->AudioSamplesToBytes(samplesLeft);
+ remaining.UpdatingWrite(data, bytesLeft);
+ samplesLeft = audio->AudioBytesToSamples(bytesLeft); // should always be 0
+ assert(samplesLeft == 0);
+ }
+ }
+}
+
+
+void WMDRM::QuantizedViz(void *data, long sizeBytes, DWORD timestamp)
+{
+ if (drmProtected)
+ {
+ assert(sizeBytes == audio->Channels() *(audio->BitSize() / 8) * SAMPLES_PER_BLOCK);
+ memset(vizBuffer, 0, sizeBytes);
+ ptrdiff_t stride = audio->BitSize() / 8;
+ size_t position = stride - 1;
+ unsigned char *origData = (unsigned char *)data;
+ for (int i = 0;i < SAMPLES_PER_BLOCK*audio->Channels();i++) // winamp hardcodes this ...
+ {
+ vizBuffer[position] = (origData[position] & 0xFC); // 6 bits of precision, enough for viz.
+ position += stride;
+ }
+
+ plugin.SAAddPCMData((char *) vizBuffer, audio->Channels(), audio->ValidBits(), timestamp);
+ plugin.VSAAddPCMData((char *) vizBuffer, audio->Channels(), audio->ValidBits(), timestamp);
+ }
+ else
+ {
+ plugin.SAAddPCMData((char *) data, audio->Channels(), audio->ValidBits(), timestamp);
+ plugin.VSAAddPCMData((char *) data, audio->Channels(), audio->ValidBits(), timestamp);
+ }
+}
+
+long WMDRM::GetPosition()
+{
+ if (!opened)
+ return 0;
+ return out->GetWrittenTime();
+}
+
+void WMDRM::OutputAudioSamples(void *data, long samples)
+{
+ DWORD timestamp = out->GetWrittenTime();
+ OutputAudioSamples(data, samples, timestamp);
+}
+
+void WMDRM::OutputAudioSamples(void *data, long samples, DWORD &timestamp)
+{
+ clock->SetLastOutputTime(timestamp);
+ timestamp += audio->AudioSamplesToMilliseconds(samples);
+
+
+ //clock->SetLastOutputTime(winamp.GetWrittenTime());
+
+ //in theory, we could check mod->dsp_isactive(), but that opens up a potential race condition ...
+ memcpy(dspBuffer, data, audio->AudioSamplesToBytes(samples));
+ int dspSize = samples;
+ if (!drmProtected)
+ dspSize = plugin.dsp_dosamples((short *)dspBuffer, samples, audio->BitSize(), audio->Channels(), audio->SampleRate());
+ dspSize = audio->AudioSamplesToBytes(dspSize);
+ if (samples == SAMPLES_PER_BLOCK)
+ QuantizedViz(dspBuffer, dspSize, timestamp);
+ while (out->CanWrite() <= dspSize)
+ {
+ if (WaitForSingleObject(killswitch, 10) == WAIT_OBJECT_0)
+ {
+ remaining.Flush();
+ return ;
+ }
+ }
+ out->Write((char *)dspBuffer, dspSize);
+ /*long bytesAvail = */out->CanWrite();
+}
+
+void WMDRM::Opened()
+{
+ //winamp.ResetBuffering();
+ drmProtected = info->IsAttribute(g_wszWMProtected);
+ ResetEvent(killswitch);
+ if (!audio->IsOpen())
+ {
+ if (video->IsOpen())
+ {
+ winamp.SetStatus(WASABI_API_LNGSTRINGW(IDS_REALTIME));
+ clock->GoRealTime();
+ plugin.is_seekable = info->IsSeekable() ? 1 : 0;
+ winamp.SetAudioInfo(info->GetBitrate() / 1000, 0, 0);
+ //out->SetVolume( -666); // set default volume
+ }
+ else
+ {
+ // no audio or video!!
+ seek->Stop();
+ First().OpenFailed();
+ return ;
+ }
+ }
+ else
+ {
+ BuildBuffers();
+ plugin.is_seekable = info->IsSeekable() ? 1 : 0;
+ winamp.SetAudioInfo(info->GetBitrate() / 1000, audio->SampleRate() / 1000, audio->Channels());
+ out->SetVolume(volume); // set default volume
+ out->SetPan(pan);
+ winamp.SetVizInfo(audio->SampleRate(), audio->Channels());
+ }
+ opened = true;
+ winamp.ClearStatus();
+ reader->Start(clock->GetStartTime(), 0, 1.0f, NULL);
+ WMHandler::Opened();
+
+}
+
+void WMDRM::Started()
+{
+ ResetEvent(killswitch);
+ winamp.ResetBuffering();
+ winamp.ClearStatus();
+ WMHandler::Started();
+}
+void WMDRM::EndOfFile()
+{
+ if (audio->IsOpen())
+ {
+ if (WaitForSingleObject(killswitch, 0) != WAIT_OBJECT_0)
+ {
+ if (remaining.used)
+ {
+ OutputAudioSamples(remaining.GetData(), audio->AudioBytesToSamples(remaining.used));
+ remaining.Flush();
+ }
+ out->Write(0, 0);
+ while (out->IsPlaying())
+ {
+ if (WaitForSingleObject(killswitch, 10) == WAIT_OBJECT_0)
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ // TODO: if we have a playlist, start the next track instead of telling winamp to go to the next track
+ if (playing)
+ winamp.EndOfFile();
+ WMHandler::EndOfFile();
+}
+
+void WMDRM::NewMetadata()
+{
+ winamp.RefreshTitle();
+ WMHandler::NewMetadata();
+}
+
+void WMDRM::Error()
+{
+ // wait 200 ms for the killswitch (aka hitting stop)
+ // this allows the user to hit "stop" and not have to continue cycling through songs if there are a whole bunch of bad/missing WMAs in the playlist
+ if (playing && WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0)
+ winamp.EndOfFile();
+}
+
+void WMDRM::OpenFailed()
+{
+ if (playing && WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0) // wait 200 ms for the killswitch (see above notes)
+ winamp.EndOfFile();
+}
+
+void WMDRM::Stopped()
+{
+ remaining.Flush();
+ WMHandler::Stopped();
+}
+
+void WMDRM::Kill()
+{
+ SetEvent(killswitch);
+ WMHandler::Kill();
+}
+
+void WMDRM::NewSourceFlags()
+{
+ plugin.is_seekable = info->IsSeekable() ? 1 : 0;
+}
+
+void WMDRM::Connecting()
+{
+ winamp.SetStatus(WASABI_API_LNGSTRINGW(IDS_CONNECTING));
+ WMHandler::Connecting();
+}
+
+void WMDRM::Locating()
+{
+ winamp.SetStatus(WASABI_API_LNGSTRINGW(IDS_LOCATING));
+ WMHandler::Locating();
+}
+
+void WMDRM::AccessDenied()
+{
+ winamp.SetStatus(WASABI_API_LNGSTRINGW(IDS_ACCESS_DENIED));
+ if (playing && WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0) // wait 200 ms for the killswitch (see above notes)
+ winamp.PressStop();
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/WMDRMModule.h b/Src/Plugins/Input/in_wmvdrm/WMDRMModule.h
new file mode 100644
index 00000000..52812930
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/WMDRMModule.h
@@ -0,0 +1,100 @@
+#ifndef NULLSOFT_WMDRMMODULEH
+#define NULLSOFT_WMDRMMODULEH
+
+#include "Remaining.h"
+#include <wmsdk.h>
+// layers
+#include "AudioLayer.h"
+#include "VideoLayer.h"
+#include "ClockLayer.h"
+#include "WaitLayer.h"
+#include "BufferLayer.h"
+#include "SeekLayer.h"
+#include "GainLayer.h"
+
+#include "WMHandler.h"
+#include "WMCallback.h"
+#include "WMInformation.h"
+
+class WMDRM : public WMHandler
+{
+public:
+ WMDRM();
+ ~WMDRM();
+ void Config(HWND hwndParent);
+ void Init();
+ void Quit();
+ void GetFileInfo(const wchar_t *file, wchar_t *title, int *length_in_ms);
+ int InfoBox(const wchar_t *file, HWND hwndParent);
+ int IsOurFile(const wchar_t *fn);
+ int Play(const wchar_t *fn);
+ void Pause();
+ void UnPause();
+ int IsPaused();
+ void Stop();
+ int GetLength();
+ int GetOutputTime();
+ void SetOutputTime(int time_in_ms);
+ void SetVolume(int volume);
+ void SetPan(int pan);
+ int GetVolume() { return volume; }
+ int GetPan() { return pan; }
+ void EQSet(int on, char data[10], int preamp);
+ void BuildBuffers();
+ void OutputAudioSamples(void *data, long samples, DWORD&);
+ void OutputAudioSamples(void *data, long samples);
+ void QuantizedViz(void *data, long sizeBytes, DWORD);
+ long GetPosition();
+ void EndOfFile();
+ bool OpenVideo(int fourcc, int width, int height, bool flipped);
+ void ReOpen();
+ void NewSourceFlags();
+// const char *GetFile() { return fn.c_str();}
+ bool playing;
+ int startAtMilliseconds;
+ void InitWM();
+protected:
+ //WMHandler
+ void AudioDataReceived(void *_data, unsigned long sizeBytes, DWORD timestamp);
+ void Opened();
+ void NewMetadata();
+ void Closed();
+ void Started();
+ void Error();
+ void OpenFailed();
+ void Stopped();
+ void Kill();
+
+ BufferLayer *buffer;
+ ClockLayer *clock;
+#ifndef NO_DRM
+ DRMLayer *drm;
+#endif
+ AudioLayer *audio;
+ VideoLayer *video;
+ WaitLayer *wait;
+ SeekLayer *seek;
+ GainLayer *gain;
+ WMCallback callback;
+ IWMReader *reader;
+ IWMReaderAdvanced *reader1;
+ IWMReaderAdvanced2 *reader2;
+ IWMReaderNetworkConfig *network;
+ WMInformation *info;
+ Remaining remaining;
+ unsigned char *dspBuffer, *vizBuffer;
+ int volume, pan;
+ bool flushed, paused;
+
+ HANDLE killswitch;
+
+ bool opened;
+ bool drmProtected;
+ void Connecting();
+ void Locating();
+ void AssignOutput();
+ void AccessDenied();
+
+
+};
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/WMHandler.cpp b/Src/Plugins/Input/in_wmvdrm/WMHandler.cpp
new file mode 100644
index 00000000..090e1cd7
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/WMHandler.cpp
@@ -0,0 +1,181 @@
+#include "main.h"
+#include <assert.h>
+
+void WMHandler::OpenFailed()
+{
+ if (next)
+ next->OpenFailed();
+}
+
+void WMHandler::ReOpen()
+{
+ if (next)
+ next->ReOpen();
+}
+
+void WMHandler::Started()
+{
+ if (next)
+ next->Started();
+}
+
+void WMHandler::Stopped()
+{
+ if (next)
+ next->Stopped();
+}
+
+void WMHandler::PreRollComplete()
+{
+ if (next)
+ next->PreRollComplete();
+}
+
+void WMHandler::EndOfFile()
+{
+ if (next)
+ next->EndOfFile();
+}
+
+void WMHandler::Closed()
+{
+ if (next)
+ next->Closed();
+}
+
+void WMHandler::BufferingStarted()
+{
+ if (next)
+ next->BufferingStarted();
+}
+
+void WMHandler::BufferingStopped()
+{
+ if (next)
+ next->BufferingStopped();
+}
+
+void WMHandler::NewMetadata()
+{
+ if (next)
+ next->NewMetadata();
+}
+
+void WMHandler::Individualize()
+{
+ if (next)
+ next->Individualize();
+}
+
+void WMHandler::SignatureState(WMT_DRMLA_TRUST *&state)
+{
+ if (next)
+ next->SignatureState(state);
+}
+
+void WMHandler::NoRightsEx(WM_GET_LICENSE_DATA *&licenseData)
+{
+ if (next)
+ next->NoRightsEx(licenseData);
+}
+
+void WMHandler::AcquireLicense(WM_GET_LICENSE_DATA *&licenseData)
+{
+ if (next)
+ next->AcquireLicense(licenseData);
+}
+
+void WMHandler::AllocateOutput(long outputNum, long bufferSize, INSSBuffer *&buffer)
+{
+ if (next)
+ next->AllocateOutput(outputNum, bufferSize, buffer);
+}
+
+void WMHandler::VideoCatchup(QWORD time)
+{
+ if (next)
+ next->VideoCatchup(time);
+}
+
+void WMHandler::TimeToSync(QWORD timeStamp,__int64 &diff)
+{
+ if (next)
+ next->TimeToSync(timeStamp, diff);
+}
+
+void WMHandler::Error()
+{
+ if (next)
+ next->Error();
+ }
+
+void WMHandler::LicenseRequired()
+{
+ if (next)
+ next->LicenseRequired();
+}
+
+void WMHandler::NoRights(wchar_t *licenseData)
+{
+ if (next)
+ next->NoRights(licenseData);
+}
+
+
+WMHandler::WMHandler() : next(0), prev(0)
+ {}
+ WMHandler::~WMHandler()
+ {
+ if (next)
+ next->prev = prev;
+
+ if (prev)
+ prev->next = next;
+
+ }
+
+ WMHandler &WMHandler::operator << (WMHandler &chain)
+ {
+ assert(chain.next == 0);
+ assert(prev == 0);
+
+ prev = &chain;
+ chain.next = this;
+
+ return chain;
+ }
+
+ WMHandler &WMHandler::operator >> (WMHandler &chain)
+ {
+ if (chain.prev)
+ {
+ operator >>(chain.prev);
+ return chain;
+ }
+
+ assert (next == 0);
+ assert (chain.prev == 0);
+
+ next = &chain;
+ chain.prev = this;
+
+ return chain;
+ }
+ WMHandler&WMHandler::operator << (WMHandler *chain)
+ {
+ return operator <<(*chain);
+ }
+
+ WMHandler &WMHandler::operator >> (WMHandler *chain)
+ {
+ return operator >>(*chain);
+ }
+
+ WMHandler &WMHandler::First()
+ {
+ if (prev)
+ return prev->First();
+ else
+ return *this;
+ }
+
diff --git a/Src/Plugins/Input/in_wmvdrm/WMHandler.h b/Src/Plugins/Input/in_wmvdrm/WMHandler.h
new file mode 100644
index 00000000..b2ac60f9
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/WMHandler.h
@@ -0,0 +1,113 @@
+#ifndef NULLSOFT_WMHANDLERH
+#define NULLSOFT_WMHANDLERH
+#include <wmsdk.h>
+
+#define NEXT(x) { if (next) next->x; }
+
+enum DRM_INDIVIDUALIZATION_STATUS {
+ INDI_UNDEFINED = 0x0000,
+ INDI_BEGIN = 0x0001,
+ INDI_SUCCEED = 0x0002,
+ INDI_FAIL = 0x0004,
+ INDI_CANCEL = 0x0008,
+ INDI_DOWNLOAD = 0x0010,
+ INDI_INSTALL = 0x0020
+};
+
+enum DRM_HTTP_STATUS {
+ HTTP_NOTINITIATED = 0,
+ HTTP_CONNECTING = 1,
+ HTTP_REQUESTING = 2,
+ HTTP_RECEIVING = 3,
+ HTTP_COMPLETED = 4
+};
+
+typedef struct _WMGetLicenseData {
+ DWORD dwSize;
+ HRESULT hr;
+ WCHAR* wszURL;
+ WCHAR* wszLocalFilename;
+ BYTE* pbPostData;
+ DWORD dwPostDataSize;
+} WM_GET_LICENSE_DATA;
+
+
+typedef struct _WMIndividualizeStatus {
+ HRESULT hr;
+ DRM_INDIVIDUALIZATION_STATUS enIndiStatus;
+ LPSTR pszIndiRespUrl;
+ DWORD dwHTTPRequest;
+ DRM_HTTP_STATUS enHTTPStatus;
+ DWORD dwHTTPReadProgress;
+ DWORD dwHTTPReadTotal;
+} WM_INDIVIDUALIZE_STATUS;
+
+class WMHandler //: public Chainable<WMHandler>
+{
+public:
+ WMHandler();
+ ~WMHandler();
+ WMHandler &operator << (WMHandler &chain);
+ WMHandler &operator >> (WMHandler &chain);
+ WMHandler&operator << (WMHandler *chain);
+ WMHandler &operator >> (WMHandler *chain);
+ WMHandler &First();
+
+ virtual void Opened() NEXT(Opened())
+ virtual void OpenFailed();
+ virtual void ReOpen();
+
+ virtual void SampleReceived(QWORD &timeStamp, QWORD &duration, unsigned long &outputNum, unsigned long &flags, INSSBuffer *&sample)
+ NEXT(SampleReceived(timeStamp, duration, outputNum, flags, sample))
+
+ virtual void AudioDataReceived(void *data, unsigned long sizeBytes, DWORD timestamp)
+ NEXT(AudioDataReceived(data, sizeBytes, timestamp))
+
+ virtual void TimeReached(QWORD &timeReached) NEXT(TimeReached(timeReached))
+ virtual void NewSourceFlags() NEXT(NewSourceFlags())
+ virtual void HasVideo(bool &video) NEXT(HasVideo(video))
+ virtual void Started();
+ virtual void Stopped();
+ virtual void Stopping() NEXT(Stopping())
+ virtual void DRMExpired() NEXT(DRMExpired())
+
+ virtual void Error();
+
+ virtual void Kill() NEXT(Kill())
+ virtual void PreRollComplete();
+
+ virtual void EndOfFile();
+ virtual void Closed();
+ virtual void BufferingStarted();
+ virtual void BufferingStopped();
+ virtual void NewMetadata();
+ virtual void Connecting() NEXT(Connecting())
+ virtual void Locating() NEXT(Locating())
+
+ virtual void Individualize();
+ virtual void NeedsIndividualization() NEXT(NeedsIndividualization())
+ virtual void IndividualizeStatus(WM_INDIVIDUALIZE_STATUS *status) NEXT(IndividualizeStatus(status))
+
+ virtual void SignatureState(WMT_DRMLA_TRUST *&state);
+ virtual void NoRights(wchar_t *licenseData);
+ virtual void NoRightsEx(WM_GET_LICENSE_DATA *&licenseData);
+ virtual void AcquireLicense(WM_GET_LICENSE_DATA *&licenseData);
+ virtual void LicenseRequired();
+ virtual void BrowserClosed() NEXT(BrowserClosed())
+ virtual void LicenseAcquired() NEXT(LicenseAcquired())
+ virtual void AllocateOutput(long outputNum, long bufferSize, INSSBuffer *&buffer);
+ virtual void MonitorCancelled() NEXT(MonitorCancelled())
+ virtual void SilentCancelled() NEXT(SilentCancelled())
+
+ virtual void VideoCatchup(QWORD time);
+ virtual void TimeToSync(QWORD timeStamp, __int64 &diff);
+ virtual void OpenCalled() NEXT(OpenCalled())
+
+ virtual void InitPlaylistBurn() NEXT(InitPlaylistBurn())
+ virtual void AccessDenied() NEXT(AccessDenied())
+
+private:
+ WMHandler *next, *prev;
+};
+#undef NEXT
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/WMInformation.cpp b/Src/Plugins/Input/in_wmvdrm/WMInformation.cpp
new file mode 100644
index 00000000..a9c5701b
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/WMInformation.cpp
@@ -0,0 +1,880 @@
+#include "main.h"
+#include "WMInformation.h"
+#include "resource.h"
+#include <exception>
+#include <strsafe.h>
+
+class AutoByte
+{
+public:
+ AutoByte(size_t bytes)
+ : data(0)
+ {
+ data = new BYTE[bytes];
+ }
+ ~AutoByte()
+ {
+ if (data)
+ delete[] data;
+ data = 0;
+ }
+ operator void *()
+ {
+ return (void *)data;
+ }
+
+ BYTE *data;
+};
+
+static void StoreData(WMT_ATTR_DATATYPE type, BYTE *value, DWORD length, wchar_t *valueStr, size_t len)
+{
+ switch (type)
+ {
+ case WMT_TYPE_DWORD:
+ StringCchPrintf(valueStr, len, L"%lu", *(DWORD *)value);
+ break;
+
+ case WMT_TYPE_STRING:
+ lstrcpyn(valueStr, (wchar_t *)value, len);
+ break;
+
+ case -1: // hack // if (attrName == L"WM/Text")
+ StringCchPrintf(valueStr, len, L"%s/%s", UserTextDescription(value, length), UserTextString(value, length));
+ break;
+ case WMT_TYPE_BINARY:
+ BinaryString(value, length, valueStr, len);
+ break;
+ case WMT_TYPE_BOOL:
+ if (*(BOOL *)value)
+ {
+ lstrcpyn(valueStr, L"True", len);
+ }
+ else
+ {
+ lstrcpyn(valueStr, L"False", len);
+ }
+ break;
+ case WMT_TYPE_QWORD:
+ StringCchPrintf(valueStr, len, L"%I64u", *(QWORD *)value);
+ break;
+ case WMT_TYPE_WORD:
+ StringCchPrintf(valueStr, len, L"%hu", *(WORD *)value);
+ break;
+ case WMT_TYPE_GUID:
+ GuidString(*(GUID *)value, valueStr, len);
+ break;
+ default:
+ WASABI_API_LNGSTRINGW_BUF(IDS_UNKNOWN,valueStr,len);
+ break;
+ }
+
+}
+WMInformation::WMInformation(const wchar_t *fileName, bool noBlock)
+ : editor(0), editor2(0), header(0), header3(0), reader(0), header2(0), openError(false)
+{
+ if (fileName && fileName[0]
+ && WMCreateEditor(&editor) == S_OK)
+ {
+ if (SUCCEEDED(editor->QueryInterface(&editor2)))
+ {
+ if (SUCCEEDED(editor2->OpenEx(fileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE)))
+ {
+ // good to go
+ editor2->QueryInterface(&header);
+ editor->QueryInterface(&header2);
+ editor2->QueryInterface(&header3);
+ return ;
+ }
+ }
+ else
+ {
+ editor2 = 0;
+ if (SUCCEEDED(editor->Open(fileName)))
+ {
+ // good to go
+ editor->QueryInterface(&header);
+ editor->QueryInterface(&header2);
+ editor->QueryInterface(&header3);
+ return ;
+ }
+ }
+ // can't open it through the metadata editor interface, let's open a reader
+
+ if (editor)
+ editor->Release();
+ editor = 0;
+ if (editor2)
+ editor2->Release();
+ editor2 = 0;
+ if (FAILED(WMCreateReader(0, WMT_RIGHT_PLAYBACK, &reader)))
+ {
+ reader = 0;
+ return ;
+ }
+ hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ callback >> this;
+
+ if (FAILED(reader->Open(fileName, &callback, 0)))
+ {
+ reader->Release();
+ reader = 0;
+ return ;
+ }
+ if (noBlock)
+ WaitForEvent(hEvent, INFINITE);
+ else
+ WaitForSingleObject(hEvent, INFINITE);
+
+ CloseHandle(hEvent);
+ if (openError)
+ {
+ reader->Release();
+ reader = 0;
+ }
+ else
+ {
+ reader->QueryInterface(&header);
+ reader->QueryInterface(&header2);
+ reader->QueryInterface(&header3);
+ }
+
+ }
+}
+
+WMInformation::WMInformation(IWMReader *_reader)
+ : reader(0), // reader is if we create an internal reader, we don't want to save the passed one (so we don't close it on someone else :)
+ editor(0), editor2(0), header(0),
+ header3(0), header2(0),
+ openError(false), hEvent(NULL)
+{
+ if (FAILED(_reader->QueryInterface(&header)))
+ header = 0;
+ if (FAILED(_reader->QueryInterface(&header2)))
+ header2 = 0;
+ if (FAILED(_reader->QueryInterface(&header3)))
+ header3 = 0; // this error is OK, we can deal with it.
+}
+
+/*
+WMInformation::WMInformation(IWMSyncReader *reader)
+: editor(0), editor2(0), header(0), header3(0)
+{
+reader->QueryInterface(&header);
+reader->QueryInterface(&header3);
+}*/
+
+WMInformation::WMInformation(IWMMetadataEditor *_editor)
+ : editor(_editor), editor2(0), header(0),
+ header3(0), reader(0), header2(0),
+ openError(false), hEvent(NULL)
+{
+ editor->AddRef();
+ editor->QueryInterface(&editor2);
+ editor->QueryInterface(&header);
+ editor->QueryInterface(&header2);
+ editor->QueryInterface(&header3);
+}
+
+
+WMInformation::~WMInformation()
+{
+ if (editor)
+ {
+ editor->Close();
+ editor->Release();
+ editor = 0;
+ }
+ if (editor2)
+ editor2->Release();
+ editor2 = 0;
+ if (header)
+ header->Release();
+ header = 0;
+ if (header2)
+ header2->Release();
+ header2 = 0;
+ if (header3)
+ header3->Release();
+ header3 = 0;
+ if (reader)
+ {
+ reader->Close();
+ reader->Release();
+ reader = 0;
+ }
+}
+
+bool WMInformation::GetDataType(const wchar_t *name, WMT_ATTR_DATATYPE &type)
+{
+ if (!name)
+ return false;
+
+ WORD stream = 0;
+ WORD dataLen = 0;
+ if (header && SUCCEEDED(header->GetAttributeByName(&stream, name, &type, 0, &dataLen)))
+ return true;
+ else
+ return false;
+
+}
+
+void WMInformation::DeleteAttribute(const wchar_t *attrName)
+{
+
+ WORD indexCount = 0;
+ if (header3 && SUCCEEDED(header3->GetAttributeIndices(0, attrName, NULL, 0, &indexCount)))
+ {
+ WORD *indices = new WORD[indexCount];
+ if (SUCCEEDED(header3->GetAttributeIndices(0, attrName, NULL, indices, &indexCount)))
+ {
+ for (size_t i = 0;i != indexCount;i++)
+ {
+ header3->DeleteAttribute(0, indices[i]);
+ }
+ }
+ }
+}
+
+
+void WMInformation::SetAttribute_BinString(const wchar_t *attrName, wchar_t *value)
+{
+ if (!header || !attrName || !value)
+ return ;
+
+ if (!*value)
+ {
+ DeleteAttribute(attrName);
+ return ;
+ }
+
+ AutoChar data(value);
+ header->SetAttribute(0, attrName, WMT_TYPE_BINARY, (BYTE *)(char *)data, (WORD)strlen(data));
+}
+
+void WMInformation::GetAttribute_BinString(const wchar_t attrName[], wchar_t *valueStr, size_t len)
+{
+ if (!header)
+ {
+ valueStr[0]=0;
+ return ;
+ }
+
+ WMT_ATTR_DATATYPE type;
+ WORD length = 0;
+ HRESULT hr;
+ WORD streamNum = 0;
+ if (!header || FAILED(header->GetAttributeByName(&streamNum,
+ attrName,
+ &type,
+ 0,
+ &length)))
+ {
+ valueStr[0]=0;
+ return ;
+ }
+ AutoByte v(length);
+ BYTE *value = v.data;
+
+ hr = header->GetAttributeByName(&streamNum,
+ attrName,
+ &type,
+ value,
+ &length);
+ if (FAILED(hr))
+ {
+ valueStr[0]=0;
+ return ;
+ }
+
+
+ int converted = MultiByteToWideChar(CP_ACP, 0, (const char *)value, length, valueStr, len-1);
+ valueStr[converted]=0;
+}
+
+void WMInformation::SetAttribute(const wchar_t *attrName, wchar_t *value, WMT_ATTR_DATATYPE defaultType)
+{
+ if (!header || !attrName || !value)
+ return ;
+
+ if (!*value)
+ {
+ DeleteAttribute(attrName);
+ return ;
+ }
+
+ WMT_ATTR_DATATYPE type;
+
+ if (!GetDataType(attrName, type))
+ type = defaultType;
+
+ switch (type)
+ {
+ case WMT_TYPE_DWORD:
+ {
+ DWORD dwordValue = wcstoul(value, 0, 10);
+ header->SetAttribute(0, attrName, WMT_TYPE_DWORD, (BYTE *) &dwordValue, sizeof(dwordValue));
+ }
+ break;
+ case WMT_TYPE_STRING:
+ {
+ WORD size = static_cast<WORD>((lstrlen(value) + 1) * sizeof(wchar_t));
+ header->SetAttribute(0, attrName, WMT_TYPE_STRING, (BYTE *)value, size);
+ }
+ break;
+ case WMT_TYPE_BINARY:
+ {
+ // TODO
+ }
+ break;
+ case WMT_TYPE_BOOL:
+ {
+ BOOL boolValue;
+ if (!_wcsicmp(L"true", value))
+ boolValue = TRUE;
+ else
+ boolValue = FALSE;
+
+ header->SetAttribute(0, attrName, WMT_TYPE_BOOL, (BYTE *)&boolValue, sizeof(boolValue));
+ }
+ break;
+ case WMT_TYPE_QWORD:
+ {
+ {
+ QWORD qwordValue = _wcstoui64(value, 0, 10);
+ header->SetAttribute(0, attrName, WMT_TYPE_QWORD, (BYTE *) &qwordValue, sizeof(qwordValue));
+ }
+ }
+ break;
+ case WMT_TYPE_WORD:
+ {
+ {
+ WORD wordValue = static_cast<WORD>(wcstoul(value, 0, 10));
+ header->SetAttribute(0, attrName, WMT_TYPE_WORD, (BYTE *) &wordValue, sizeof(wordValue));
+ }
+ }
+ break;
+ case WMT_TYPE_GUID:
+ {
+ GUID guidValue = StringGUID(value);
+ header->SetAttribute(0, attrName, WMT_TYPE_GUID, (BYTE *) &guidValue, sizeof(guidValue));
+ }
+ break;
+ }
+
+}
+
+bool WMInformation::GetAttributeSize(const wchar_t *name, size_t &size)
+{
+ WORD stream = 0;
+ WORD resultSize;
+ WMT_ATTR_DATATYPE type;
+
+ if (!header || FAILED(header->GetAttributeByName(&stream, name, &type, 0, &resultSize)))
+ {
+ return false;
+ }
+ size = resultSize;
+ return true;
+}
+
+DWORD WMInformation::GetDWORDAttr(const wchar_t name[])
+{
+ WORD stream = 0;
+ DWORD result;
+
+ WORD resultSizeWord = sizeof(result);
+ DWORD resultSize = sizeof(result);
+ WMT_ATTR_DATATYPE type = WMT_TYPE_DWORD;
+ WORD count = 1;
+ WORD indices[1] = {0};
+ if ((!header3
+ || FAILED(header3->GetAttributeIndices(0, name, NULL, indices, &count))
+ || FAILED(header3->GetAttributeByIndexEx(0, indices[0], 0, 0, &type, NULL, (BYTE *) &result, &resultSize)))
+ &&
+ (!header || FAILED(header->GetAttributeByName(&stream, name, &type, (BYTE *)&result, &resultSizeWord))))
+ return 0;
+ else
+ return result;
+
+}
+
+
+long WMInformation::GetLongAttr(const wchar_t name[])
+{
+ WORD stream = 0;
+ long result;
+ WORD resultSize = sizeof(result);
+ WMT_ATTR_DATATYPE type;
+
+ if (!header || FAILED(header->GetAttributeByName(&stream, name, &type, (BYTE *)&result, &resultSize)))
+ {
+ return 0;
+ }
+
+ return result;
+}
+
+bool WMInformation::GetBoolAttr(const wchar_t name[])
+{
+ WORD stream = 0;
+ BOOL result;
+ WORD resultSize = sizeof(result);
+ WMT_ATTR_DATATYPE type;
+
+ if (!header || FAILED(header->GetAttributeByName(&stream, name, &type, (BYTE *)&result, &resultSize)))
+ {
+ return false;
+ }
+
+ return !!result;
+}
+
+bool WMInformation::IsSeekable()
+{
+ return GetBoolAttr(g_wszWMSeekable);
+}
+
+long WMInformation::GetLengthMilliseconds()
+{
+ WORD stream = 0;
+ long long duration = 0;
+ WORD resultSize = sizeof(duration);
+ WMT_ATTR_DATATYPE type;
+
+ if (!header || FAILED(header->GetAttributeByName(&stream, g_wszWMDuration, &type, (BYTE *)&duration, &resultSize)))
+ {
+ return -1000;
+ }
+
+ duration /= 10000LL;
+ return (long)duration;
+}
+
+long WMInformation::GetBitrate()
+{
+ return GetDWORDAttr(g_wszWMCurrentBitrate);
+}
+
+WORD WMInformation::GetNumberAttributes()
+{
+ WORD numAttr = 0;
+ if ((!header3 || FAILED(header3->GetAttributeCountEx(0, &numAttr)))
+ && (!header || FAILED(header->GetAttributeCount(0, &numAttr))))
+ return 0;
+ else
+ return numAttr;
+}
+
+
+
+void WMInformation::GetAttribute(WORD index, wchar_t *attrName, size_t attrLen, wchar_t *valueStr, size_t valueStrLen)
+{
+ wchar_t _attrName[1025] = {0};
+ WORD nameLen = sizeof(_attrName) / sizeof(_attrName[0]);
+ WMT_ATTR_DATATYPE type;
+ WORD lang;
+ WORD stream = 0;
+ DWORD length = 0;
+ WORD lengthWord = 0;
+
+ if ((!header3 || FAILED(header3->GetAttributeByIndexEx(0, index, _attrName, &nameLen, &type, &lang, 0, &length)))
+ && (!header || FAILED(header->GetAttributeByIndex(index, &stream, _attrName, &nameLen, &type, 0, &lengthWord))))
+ {
+ attrName[0]=0;
+ valueStr[0]=0;
+ return ;
+ }
+ if (lengthWord)
+ length = lengthWord;
+
+ AutoByte v(length);
+ BYTE *value = v.data;
+
+ lstrcpyn(attrName, _attrName, attrLen);
+ if ((!header3 || FAILED(header3->GetAttributeByIndexEx(0, index, _attrName, &nameLen, &type, &lang, value, &length)))
+ && (!header || FAILED(header->GetAttributeByIndex(index, &stream, _attrName, &nameLen, &type, value, &lengthWord))))
+ {
+ attrName[0]=0;
+ valueStr[0]=0;
+
+ return ;
+ }
+
+ if (attrName == L"WM/Text")
+ {
+ type = (WMT_ATTR_DATATYPE)-1; // hack
+ StringCchCat(attrName, attrLen, L":");
+ StringCchCat(attrName, attrLen, UserTextDescription(value, length));
+ }
+ StoreData(type, value, length, valueStr, valueStrLen);
+}
+
+void WMInformation::GetAttribute(const wchar_t attrName[], wchar_t *valueStr, size_t len)
+{
+ if (!header)
+ {
+ valueStr[0]=0;
+ return ;
+ }
+
+ WMT_ATTR_DATATYPE type;
+ WORD length = 0;
+ HRESULT hr;
+ WORD streamNum = 0;
+ if (!header || FAILED(header->GetAttributeByName(&streamNum,
+ attrName,
+ &type,
+ 0,
+ &length)))
+ {
+ valueStr[0]=0;
+ return ;
+ }
+ AutoByte v(length);
+ BYTE *value = v.data;
+
+ hr = header->GetAttributeByName(&streamNum,
+ attrName,
+ &type,
+ value,
+ &length);
+ if (FAILED(hr))
+ {
+ valueStr[0]=0;
+ return ;
+ }
+
+ if (attrName == L"WM/Text")
+ type = (WMT_ATTR_DATATYPE)-1; // hack
+ StoreData(type, value, length, valueStr, len);
+}
+
+
+bool WMInformation::MakeWritable(const wchar_t *fileName)
+{
+ if (!editor || !editor2)
+ return false;
+
+ if (FAILED(editor2->OpenEx(fileName, GENERIC_READ | GENERIC_WRITE, 0)))
+ {
+ return false;
+ }
+ return true;
+}
+
+bool WMInformation::Flush()
+{
+ if (!editor2 || FAILED(editor->Flush()))
+ return false;
+
+ return true;
+}
+
+bool WMInformation::IsAttribute(const wchar_t attrName[])
+{
+ WMT_ATTR_DATATYPE type = WMT_TYPE_BOOL;
+ WORD length = sizeof(BOOL);
+ WORD streamNum = 0;
+ BOOL value;
+ if (!header || FAILED(header->GetAttributeByName(&streamNum,
+ attrName,
+ &type,
+ (BYTE *)&value,
+ &length)))
+ {
+ return false;
+ }
+ else
+ {
+ return !!value;
+ }
+}
+
+bool WMInformation::IsNotAttribute(const wchar_t attrName[])
+{
+ if (!header)
+ {
+ return false;
+ }
+
+ WMT_ATTR_DATATYPE type = WMT_TYPE_BOOL;
+ WORD length = sizeof(BOOL);
+ WORD streamNum = 0;
+ BOOL value;
+ if (!header || FAILED(header->GetAttributeByName(&streamNum,
+ attrName,
+ &type,
+ (BYTE *)&value,
+ &length)))
+ {
+ return false;
+ }
+ else
+ {
+ return !value;
+ }
+}
+
+bool WMInformation::MakeReadOnly(const wchar_t *fileName)
+{
+ if (!editor || !editor2)
+ return false;
+
+ //editor->Close();
+ if (FAILED(editor2->OpenEx(fileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE)))
+ {
+ return false;
+ }
+ return true;
+}
+
+
+bool WMInformation::NonWritable()
+{
+ if (!editor2)
+ return true;
+ else
+ return false;
+}
+
+void WMInformation::DeleteUserText(const wchar_t *description)
+{
+ WORD indexCount = 0;
+ WMT_ATTR_DATATYPE type = WMT_TYPE_BOOL;
+ WORD nameLen = 128;
+ if (header3 && SUCCEEDED(header3->GetAttributeIndices(0, L"WM/Text", NULL, 0, &indexCount)))
+ {
+ WORD *indices = new WORD[indexCount];
+ if (SUCCEEDED(header3->GetAttributeIndices(0, L"WM/Text", NULL, indices, &indexCount)))
+ {
+ for (size_t index = 0;index != indexCount;index++)
+ {
+ WMT_ATTR_DATATYPE type = WMT_TYPE_BINARY;
+ WORD lang = 0;
+ DWORD length = 0;
+ wchar_t _attrName[128] = L"WM/Text";
+ if (SUCCEEDED(header3->GetAttributeByIndexEx(0, indices[index], _attrName, &nameLen, &type, &lang, 0, &length)))
+ {
+ AutoByte v(length);
+ BYTE *value = v.data;
+
+ if (SUCCEEDED(header3->GetAttributeByIndexEx(0, indices[index], _attrName, &nameLen, &type, &lang, value, &length)))
+ {
+ if (UserTextDescription(value, length) == description)
+ {
+ header3->DeleteAttribute(0, indices[index]);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void WMInformation::SetUserText(const wchar_t *description, const wchar_t *valueStr)
+{
+ if (!header3 || !description || !valueStr)
+ return;
+
+ WM_USER_TEXT userText;
+ userText.pwszDescription = (LPWSTR)description;
+ userText.pwszText = (LPWSTR) valueStr;
+
+ WORD index;
+ header3->AddAttribute(0, L"WM/Text", &index, WMT_TYPE_BINARY, 0, (BYTE *) &userText, sizeof(userText));
+}
+
+void WMInformation::ClearAllAttributes()
+{
+ WORD numAttrs;
+ header3->GetAttributeCountEx(0xFFFF, &numAttrs);
+ while (numAttrs--)
+ {
+ header3->DeleteAttribute(0xFFFF, numAttrs);
+ }
+}
+
+bool WMInformation::GetCodecName(wchar_t *storage, size_t len)
+{
+ if (!header2)
+ return false;
+
+ DWORD codecs=0;
+ header2->GetCodecInfoCount(&codecs);
+ for (DWORD i=0;i!=codecs;i++)
+ {
+ WORD nameLen=0, descriptionLen=0, infoLen = 0;
+ WMT_CODEC_INFO_TYPE type;
+ header2->GetCodecInfo(i, &nameLen, 0, &descriptionLen, 0, &type, &infoLen, 0);
+ if (type == WMT_CODECINFO_AUDIO)
+ {
+ wchar_t *name = new wchar_t[nameLen];
+ wchar_t *description = new wchar_t[descriptionLen];
+ BYTE *info = new BYTE[infoLen];
+ header2->GetCodecInfo(i, &nameLen, name, &descriptionLen, description, &type, &infoLen, info);
+ lstrcpynW(storage, name, len);
+ delete[] name;
+ delete[]description;
+ delete[] info;
+
+ return true;
+ }
+ }
+ return false;
+}
+
+bool WMInformation::GetPicture(void **data, size_t *len, wchar_t **mimeType, int pictype)
+{
+ WORD indexCount = 0;
+ WMT_ATTR_DATATYPE type = WMT_TYPE_BINARY;
+ WORD nameLen = 128;
+ if (header3 && SUCCEEDED(header3->GetAttributeIndices(0, g_wszWMPicture, NULL, 0, &indexCount)))
+ {
+ WORD *indices = new WORD[indexCount];
+ if (SUCCEEDED(header3->GetAttributeIndices(0, g_wszWMPicture, NULL, indices, &indexCount)))
+ {
+ for (size_t index = 0;index != indexCount;index++)
+ {
+ WMT_ATTR_DATATYPE type = WMT_TYPE_BINARY;
+ WORD lang = 0;
+ DWORD length = 0;
+ wchar_t _attrName[128] = {0};
+ if (SUCCEEDED(header3->GetAttributeByIndexEx(0, indices[index], _attrName, &nameLen, &type, &lang, 0, &length)))
+ {
+ AutoByte v(length);
+ BYTE *value = v.data;
+
+ if (SUCCEEDED(header3->GetAttributeByIndexEx(0, indices[index], _attrName, &nameLen, &type, &lang, value, &length)))
+ {
+ WM_PICTURE *picture = (WM_PICTURE *)value;
+ if (picture->bPictureType == pictype)
+ {
+ *len = picture->dwDataLen;
+ *data = WASABI_API_MEMMGR->sysMalloc(*len);
+ memcpy(*data, picture->pbData, *len);
+ wchar_t *type=0;
+ if (picture->pwszMIMEType)
+ type = wcschr(picture->pwszMIMEType, L'/');
+
+ if (type && *type)
+ {
+ type++;
+
+ wchar_t *type2 = wcschr(type, L'/');
+ if (type2 && *type2) type2++;
+ else type2 = type;
+
+ size_t mimelen = wcslen(type2)+1;
+ *mimeType = (wchar_t *)WASABI_API_MEMMGR->sysMalloc(mimelen*sizeof(wchar_t));
+ StringCchCopyW(*mimeType, mimelen, type2);
+ }
+ else
+ *mimeType = 0; // unknown!
+ delete[] indices;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ delete[] indices;
+ }
+ return false;
+}
+
+bool WMInformation::SetPicture(void *data, size_t len, const wchar_t *mimeType, int type)
+{
+ WM_PICTURE picture;
+ picture.bPictureType = type;
+ picture.dwDataLen = len;
+ picture.pbData = (BYTE *)data;
+ picture.pwszDescription=L"";
+ wchar_t mt[32] = {0};
+ if (wcsstr(mimeType, L"/") != 0)
+ {
+ StringCchCopyW(mt, 32, mimeType);
+ }
+ else
+ {
+ StringCchPrintfW(mt, 32, L"image/%s", mimeType);
+ }
+ picture.pwszMIMEType = mt;
+ return SUCCEEDED(header->SetAttribute(0, g_wszWMPicture, WMT_TYPE_BINARY, (const BYTE *)&picture, sizeof(picture)));
+}
+
+bool WMInformation::HasPicture(int pictype)
+{
+ WORD indexCount = 0;
+ WMT_ATTR_DATATYPE type = WMT_TYPE_BINARY;
+ WORD nameLen = 128;
+ if (header3 && SUCCEEDED(header3->GetAttributeIndices(0, g_wszWMPicture, NULL, 0, &indexCount)))
+ {
+ WORD *indices = new WORD[indexCount];
+ if (SUCCEEDED(header3->GetAttributeIndices(0, g_wszWMPicture, NULL, indices, &indexCount)))
+ {
+ for (size_t index = 0;index != indexCount;index++)
+ {
+ WMT_ATTR_DATATYPE type = WMT_TYPE_BINARY;
+ WORD lang = 0;
+ DWORD length = 0;
+ wchar_t _attrName[128] = {0};
+ if (SUCCEEDED(header3->GetAttributeByIndexEx(0, indices[index], _attrName, &nameLen, &type, &lang, 0, &length)))
+ {
+ AutoByte v(length);
+ BYTE *value = v.data;
+
+ if (SUCCEEDED(header3->GetAttributeByIndexEx(0, indices[index], _attrName, &nameLen, &type, &lang, value, &length)))
+ {
+ WM_PICTURE *picture = (WM_PICTURE *)value;
+ if (picture->bPictureType == pictype)
+ {
+ delete[] indices;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ delete[] indices;
+ }
+ return false;
+}
+
+bool WMInformation::DeletePicture(int pictype)
+{
+ WORD indexCount = 0;
+ WMT_ATTR_DATATYPE type = WMT_TYPE_BINARY;
+ WORD nameLen = 128;
+ if (header3 && SUCCEEDED(header3->GetAttributeIndices(0, g_wszWMPicture, NULL, 0, &indexCount)))
+ {
+ WORD *indices = new WORD[indexCount];
+ if (SUCCEEDED(header3->GetAttributeIndices(0, g_wszWMPicture, NULL, indices, &indexCount)))
+ {
+ for (size_t index = 0;index != indexCount;index++)
+ {
+ WMT_ATTR_DATATYPE type = WMT_TYPE_BINARY;
+ WORD lang = 0;
+ DWORD length = 0;
+ wchar_t _attrName[128] = {0};
+ if (SUCCEEDED(header3->GetAttributeByIndexEx(0, indices[index], _attrName, &nameLen, &type, &lang, 0, &length)))
+ {
+ AutoByte v(length);
+ BYTE *value = v.data;
+
+ if (SUCCEEDED(header3->GetAttributeByIndexEx(0, indices[index], _attrName, &nameLen, &type, &lang, value, &length)))
+ {
+ WM_PICTURE *picture = (WM_PICTURE *)value;
+ if (picture->bPictureType == pictype)
+ {
+ header3->DeleteAttribute(0, indices[index]);
+ delete[] indices;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ delete[] indices;
+ }
+ return false;
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/WMInformation.h b/Src/Plugins/Input/in_wmvdrm/WMInformation.h
new file mode 100644
index 00000000..a9e619b6
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/WMInformation.h
@@ -0,0 +1,89 @@
+#ifndef NULLSOFT_WMINFORMATIONH
+#define NULLSOFT_WMINFORMATIONH
+
+#include <wmsdk.h>
+#include "WMCallback.h"
+
+class WMInformation : public WMHandler
+{
+public:
+ WMInformation(const wchar_t *fileName, bool noBlock=false);
+ WMInformation(IWMReader *reader);
+ //WMInformation(IWMSyncReader *reader);
+ WMInformation(IWMMetadataEditor *_editor);
+ //WMInformation();
+ bool ErrorOpening()
+ {
+ return openError;
+ } // TODO: benski> this is only valid for WMInformation(const wchar_t *fileName, bool noBlock=false)!!!
+ virtual ~WMInformation();
+
+ bool MakeWritable(const wchar_t *fileName);
+ bool NonWritable();
+ bool MakeReadOnly(const wchar_t *fileName);
+ bool Flush();
+ bool IsSeekable();
+ long GetLengthMilliseconds();
+ long GetBitrate();
+ WORD GetNumberAttributes();
+ void ClearAllAttributes();
+ bool IsAttribute(const wchar_t attrName[]); // false might mean "attribute not found", see IsNotAttribute
+ bool IsNotAttribute(const wchar_t attrName[]); // false might mean "attribute not found", see IsAttribute
+ void GetAttribute(WORD index, wchar_t *attrName, size_t attrLen, wchar_t *valueStr, size_t valueStrLen);
+ void GetAttribute(const wchar_t attrName[], wchar_t *valueStr, size_t len);
+ void SetAttribute(const wchar_t *attrName, wchar_t *value, WMT_ATTR_DATATYPE defaultType = WMT_TYPE_STRING);
+ void DeleteAttribute(const wchar_t *attrName);
+ bool GetAttributeSize(const wchar_t *attrName, size_t &size);
+ void LicenseRequired()
+ {
+ First().OpenFailed();
+ }
+ void SetAttribute_BinString(const wchar_t *attrName, wchar_t *value);
+ void GetAttribute_BinString(const wchar_t attrName[], wchar_t *valueStr, size_t len);
+
+ void DeleteUserText(const wchar_t *description);
+ void SetUserText(const wchar_t *description, const wchar_t *valueStr);
+
+ bool GetCodecName(wchar_t *storage, size_t len);
+ bool GetPicture(void **data, size_t *len, wchar_t **mimeType, int type);
+ bool SetPicture(void *data, size_t len, const wchar_t *mimeType, int type);
+ bool DeletePicture(int type);
+ bool HasPicture(int type);
+private:
+ bool GetDataType(const wchar_t *name, WMT_ATTR_DATATYPE &type);
+ long GetLongAttr(const wchar_t name[]);
+ bool GetBoolAttr(const wchar_t name[]);
+ DWORD GetDWORDAttr(const wchar_t name[]);
+ struct IWMMetadataEditor *editor;
+ struct IWMMetadataEditor2 *editor2;
+ struct IWMHeaderInfo *header;
+ struct IWMHeaderInfo2 *header2;
+ struct IWMHeaderInfo3 *header3;
+ struct IWMReader *reader;
+
+ WMCallback callback;
+ HANDLE hEvent;
+ bool openError;
+ void NeedsIndividualization()
+ {
+ First().OpenFailed();
+ }
+ void Opened()
+ {
+ openError=false;
+ SetEvent(hEvent);
+ }
+ void OpenFailed()
+ {
+ openError=true;
+ SetEvent(hEvent);
+ }
+ void Error()
+ {
+ openError=true;
+ SetEvent(hEvent);
+ }
+
+};
+
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/WMPlaylist.cpp b/Src/Plugins/Input/in_wmvdrm/WMPlaylist.cpp
new file mode 100644
index 00000000..7f1d6a20
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/WMPlaylist.cpp
@@ -0,0 +1,45 @@
+#include "main.h"
+#include "WMPlaylist.h"
+
+WMPlaylist activePlaylist;
+
+void WMPlaylist::OnFile( const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info )
+{
+ //if (playstring.empty())
+ if ( playstring )
+ free( playstring );
+
+ playstring = _wcsdup( filename );
+}
+
+const wchar_t *WMPlaylist::GetFileName()
+{
+ return ( playstring ? playstring : L"" );
+}
+
+const wchar_t *WMPlaylist::GetOriginalFileName()
+{
+ return ( playlistFilename ? playlistFilename : L"" );
+}
+
+bool WMPlaylist::IsMe( const char *filename )
+{
+ return IsMe( (const wchar_t *)AutoWide( filename ) );
+}
+
+bool WMPlaylist::IsMe( const wchar_t *filename )
+{
+ if ( playlistFilename && !_wcsicmp( playlistFilename, filename ) )
+ return true;
+
+ if ( playstring && !_wcsicmp( playstring, filename ) )
+ return true;
+
+ return false;
+}
+
+#define CBCLASS WMPlaylist
+START_DISPATCH;
+VCB( IFC_PLAYLISTLOADERCALLBACK_ONFILE, OnFile )
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/WMPlaylist.h b/Src/Plugins/Input/in_wmvdrm/WMPlaylist.h
new file mode 100644
index 00000000..8147e5d0
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/WMPlaylist.h
@@ -0,0 +1,56 @@
+#ifndef NULLSOFT_IN_WMVDRM_WMPLAYLIST_H
+#define NULLSOFT_IN_WMVDRM_WMPLAYLIST_H
+
+#include "../playlist/ifc_playlistloadercallback.h"
+
+class WMPlaylist : public ifc_playlistloadercallback
+{
+public:
+ WMPlaylist() {}
+
+ ~WMPlaylist()
+ {
+ if ( playstring )
+ free( playstring );
+
+ if ( playlistFilename )
+ free( playlistFilename );
+ }
+
+ void Clear()
+ {
+ if ( playstring )
+ free( playstring );
+
+ playstring = 0;
+
+ if ( playlistFilename )
+ free( playlistFilename );
+
+ playlistFilename = 0;
+ }
+
+ void OnFile( const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info );
+
+ const wchar_t *GetFileName();
+ const wchar_t *GetOriginalFileName();
+ /* TODO: need something like these, just not sure exact what yet
+ bool ForceStartTime(int &);
+ bool ForceLength(int &);
+ bool ForceNoSeek();
+ */
+ bool IsMe( const char *filename );
+ bool IsMe( const wchar_t *filename );
+
+
+protected:
+ RECVS_DISPATCH;
+
+public:
+ wchar_t *playstring = 0;
+ wchar_t *playlistFilename = 0;
+};
+
+extern WMPlaylist activePlaylist;
+
+#endif // !NULLSOFT_IN_WMVDRM_WMPLAYLIST_H \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/WPLLoader.cpp b/Src/Plugins/Input/in_wmvdrm/WPLLoader.cpp
new file mode 100644
index 00000000..6978cae8
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/WPLLoader.cpp
@@ -0,0 +1,128 @@
+#include "main.h"
+#include "WPLLoader.h"
+#include <stdio.h>
+#include "../nu/AutoWide.h"
+#include "../xml/ifc_xmlreadercallback.h"
+#include "../xml/obj_xml.h"
+#include "api.h"
+#include <api/service/waservicefactory.h>
+#include <shlwapi.h>
+#include <strsafe.h>
+
+class WPLXML : public ifc_xmlreadercallback
+{
+public:
+ WPLXML(ifc_playlistloadercallback *_playlist, const wchar_t *wplFilename) : playlist(_playlist)
+ {
+ lstrcpynW(rootPath, wplFilename, MAX_PATH);
+ PathRemoveFileSpecW(rootPath);
+ }
+ void StartTag(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params)
+{
+ //not necessary YET, it will be if we register for more things: if (!_wcsicmp(xmlpath, L"smil\fbody\fseq\fmedia"))
+ {
+ const wchar_t *track = params->getItemValue(L"src");
+
+ if (track)
+ {
+ if (PathIsRootW(track) || PathIsURLW(track))
+ {
+ playlist->OnFile(track, 0, -1, 0); // TODO: more info!!!
+ }
+ else
+ {
+ wchar_t fullPath[MAX_PATH] = {0}, canonicalizedPath[MAX_PATH] = {0};
+ PathCombineW(fullPath, rootPath, track);
+ PathCanonicalizeW(canonicalizedPath, fullPath);
+ playlist->OnFile(canonicalizedPath, 0, -1, 0); // TODO: more info!!!
+ }
+ }
+ }
+}
+ifc_playlistloadercallback *playlist;
+wchar_t rootPath[MAX_PATH];
+protected:
+ RECVS_DISPATCH;
+};
+
+#ifdef CBCLASS
+#undef CBCLASS
+#endif
+
+#define CBCLASS WPLXML
+START_DISPATCH;
+VCB(ONSTARTELEMENT, StartTag)
+END_DISPATCH;
+
+
+WPLLoader::WPLLoader()
+{
+}
+
+WPLLoader::~WPLLoader(void)
+{
+ //Close();
+}
+
+int WPLLoader::Load(const wchar_t *filename, ifc_playlistloadercallback *playlist)
+{
+ HANDLE file = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
+
+ if (file == INVALID_HANDLE_VALUE)
+ return IFC_PLAYLISTLOADER_FAILED;
+
+ obj_xml *parser=0;
+ waServiceFactory *parserFactory=0;
+
+ parserFactory = plugin.service->service_getServiceByGuid(obj_xmlGUID);
+ if (parserFactory)
+ parser = (obj_xml *)parserFactory->getInterface();
+
+ if (parser)
+ {
+ WPLXML wplXml(playlist, filename);
+ parser->xmlreader_registerCallback(L"smil\fbody\fseq\fmedia", &wplXml);
+ parser->xmlreader_open();
+ parser->xmlreader_setEncoding(L"UTF-8"); // WPL is always UTF-8, but doesn't explicitly have it
+
+ while (true)
+ {
+ char data[1024] = {0};
+ DWORD bytesRead = 0;
+ if (ReadFile(file, data, 1024, &bytesRead, NULL) && bytesRead)
+ {
+ if (parser->xmlreader_feed(data, bytesRead) != API_XML_SUCCESS)
+ {
+ CloseHandle(file);
+ parser->xmlreader_unregisterCallback(&wplXml);
+ parser->xmlreader_close();
+ parserFactory->releaseInterface(parser);
+ return IFC_PLAYLISTLOADER_FAILED;
+ }
+ }
+ else
+ break;
+ }
+
+ CloseHandle(file);
+ parser->xmlreader_feed(0, 0);
+
+ parser->xmlreader_unregisterCallback(&wplXml);
+ parser->xmlreader_close();
+ parserFactory->releaseInterface(parser);
+ return IFC_PLAYLISTLOADER_SUCCESS;
+ }
+
+ CloseHandle(file);
+ return IFC_PLAYLISTLOADER_FAILED;
+}
+
+
+#ifdef CBCLASS
+#undef CBCLASS
+#endif
+
+#define CBCLASS WPLLoader
+START_DISPATCH;
+CB(IFC_PLAYLISTLOADER_LOAD, Load)
+END_DISPATCH;
diff --git a/Src/Plugins/Input/in_wmvdrm/WPLLoader.h b/Src/Plugins/Input/in_wmvdrm/WPLLoader.h
new file mode 100644
index 00000000..650cc58b
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/WPLLoader.h
@@ -0,0 +1,19 @@
+#ifndef NULLSOFT_PLAYLIST_WPL_LOADER_H
+#define NULLSOFT_PLAYLIST_WPL_LOADER_H
+
+#include "../playlist/ifc_playlistloader.h"
+#include "../playlist/ifc_playlistloadercallback.h"
+#include <stdio.h>
+class WPLLoader : public ifc_playlistloader
+{
+public:
+ int Load(const wchar_t *filename, ifc_playlistloadercallback *playlist);
+
+public:
+ WPLLoader();
+ virtual ~WPLLoader(void);
+
+protected:
+ RECVS_DISPATCH;
+};
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/WaitLayer.cpp b/Src/Plugins/Input/in_wmvdrm/WaitLayer.cpp
new file mode 100644
index 00000000..39192a8e
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/WaitLayer.cpp
@@ -0,0 +1,49 @@
+#include "main.h"
+#include "WaitLayer.h"
+#include "util.h"
+
+WaitLayer::WaitLayer(IWMReader *_reader)
+: reader(_reader), stopEvent(0)
+{
+ reader->AddRef();
+ openEvent = CreateEvent(0, TRUE, FALSE, 0);
+}
+
+WaitLayer::~WaitLayer()
+{
+ reader->Release();
+ reader=0;
+}
+
+void WaitLayer::Opened()
+{
+ SetEvent(openEvent);
+ WMHandler::Opened();
+}
+
+bool WaitLayer::IsOpen()
+{
+ return WaitForSingleObject(openEvent, 0) == WAIT_OBJECT_0;
+}
+
+void WaitLayer::OpenCalled()
+{
+ SetEvent(openEvent);
+ WMHandler::OpenCalled();
+}
+
+void WaitLayer::OpenFailed()
+{
+ SetEvent(openEvent);
+ WMHandler::OpenFailed();
+}
+
+bool WaitLayer::WaitForOpen(int time_ms)
+{
+ return WaitForSingleObject(openEvent, time_ms) == WAIT_OBJECT_0;
+}
+
+void WaitLayer::ResetForOpen()
+{
+ ResetEvent(openEvent);
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/WaitLayer.h b/Src/Plugins/Input/in_wmvdrm/WaitLayer.h
new file mode 100644
index 00000000..5b4c7add
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/WaitLayer.h
@@ -0,0 +1,25 @@
+#ifndef NULLSOFT_WAITLAYERH
+#define NULLSOFT_WAITLAYERH
+
+#include "WMHandler.h"
+
+class WaitLayer : public WMHandler
+{
+public:
+ WaitLayer(IWMReader *_reader);
+ ~WaitLayer();
+
+ void ResetForOpen();
+ bool WaitForOpen(int time_ms);
+ bool IsOpen();
+protected:
+ /* inherited from WMCallback */
+ void OpenCalled();
+ void OpenFailed();
+ void Opened();
+
+ IWMReader *reader; // not ours
+ HANDLE stopEvent, openEvent;
+};
+
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/WinampInterface.cpp b/Src/Plugins/Input/in_wmvdrm/WinampInterface.cpp
new file mode 100644
index 00000000..1dfc8ecf
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/WinampInterface.cpp
@@ -0,0 +1,183 @@
+#include "Main.h"
+#include "WinampInterface.h"
+#include "../Winamp/wa_ipc.h"
+#include <cassert>
+#include "WMDRMModule.h"
+#include <strsafe.h>
+#include "WMPlaylist.h"
+#include "../nu/AutoChar.h"
+WinampInterface winamp;
+
+extern WMDRM mod;
+using namespace Nullsoft::Utility;
+
+#ifndef NO_DRM
+#include "vid_overlay.h"
+#include "vid_ddraw.h"
+
+OverlayVideoOutput overlay;
+DDrawVideoOutput ddraw;
+#endif
+
+
+WinampInterface::WinampInterface()
+ : videoWindow(0), bufferCount(0),
+ statusGuard(GUARDNAME("WinampInterface::statusGuard"))
+{
+ statusFilename[0] = 0;
+ status[0] = 0;
+}
+
+/*
+@returns winamp's video window handle
+*/
+HWND WinampInterface::GetVideoWindow()
+{
+ return (HWND)GetVideoOutput()->extended(VIDUSER_GET_VIDEOHWND, 0, 0); // ask for the video hwnd
+}
+
+IVideoOutput *WinampInterface::GetVideoOutput()
+{
+ if (!videoWindow)
+ videoWindow = (IVideoOutput *)SendMessage(GetWinampWindow(), WM_WA_IPC, 0, IPC_GET_IVIDEOOUTPUT); // ask winamp for an interface to the video output
+ return videoWindow;
+
+}
+
+void WinampInterface::EndOfFile()
+{
+ PostMessage(GetWinampWindow(), WM_WA_MPEG_EOF, 0, 0);
+}
+
+HWND WinampInterface::GetWinampWindow()
+{
+ return plugin.hMainWindow;
+}
+
+void WinampInterface::SetStatus(wchar_t *_status)
+{
+ {
+ AutoLock lock (statusGuard);
+ StringCchCopy(status, 1024, _status);
+ StringCchCopy(statusFilename, FILENAME_SIZE, activePlaylist.GetFileName());
+ }
+ PostMessage(GetWinampWindow(), WM_WA_IPC, 0, IPC_UPDTITLE);
+}
+
+bool WinampInterface::GetStatus(wchar_t *title, size_t titleLen, const wchar_t *filename)
+{
+ AutoLock lock (statusGuard);
+
+ if (status[0] && title && filename && !lstrcmpi(statusFilename, filename))
+ {
+ StringCchPrintf(title, titleLen, L"[%s]%s", status, filename);
+ return true;
+ }
+ else
+ return false;
+}
+
+bool WinampInterface::GetStatusHook(wchar_t *title, size_t titleLen, const wchar_t *filename)
+{
+ AutoLock lock (statusGuard);
+
+ if (status[0] && title && filename && !lstrcmpi(statusFilename, filename))
+ {
+ wchar_t *oldTitle = _wcsdup(title);
+ StringCchPrintf(title, titleLen, L"[%s]%s", status, oldTitle);
+ free(oldTitle);
+ return true;
+ }
+ else
+ return false;
+}
+
+bool WinampInterface::HasStatus(const wchar_t *filename)
+{
+ AutoLock lock (statusGuard);
+ if (status[0] && filename && !lstrcmpi(statusFilename, filename))
+ return true;
+ return false;
+}
+
+void WinampInterface::ClearStatus()
+{
+ {
+ //AutoLock lock (statusGuard); // should be safe not to lock here
+ status[0] = 0;
+ statusFilename[0]=0;
+ }
+ PostMessage(GetWinampWindow(), WM_WA_IPC, 0, IPC_UPDTITLE);
+}
+
+void WinampInterface::EncryptedDrawFrame(void *frame)
+{
+#ifndef NO_DRM
+ overlay.SetFrame(frame);
+ ddraw.SetFrame(frame);
+ SecureZeroMemory(&frame, sizeof(void *));
+ GetVideoOutput()->draw((void *)1);
+#endif
+}
+
+bool WinampInterface::OpenEncryptedVideo(int width, int height, bool flip, double aspect, int fourcc)
+{
+#ifndef NO_DRM
+ VideoOpenStruct openVideo = {width, height, flip, aspect, fourcc};
+
+ bool openedOK = false;
+ if (config_video.overlays())
+ {
+ openedOK = !!GetVideoOutput()->extended(VIDUSER_OPENVIDEORENDERER, (intptr_t)(VideoRenderer *)&overlay, (intptr_t)&openVideo);
+ if (openedOK)
+ return true;
+ }
+
+ openedOK = !!GetVideoOutput()->extended(VIDUSER_OPENVIDEORENDERER, (intptr_t)(VideoRenderer *)&ddraw, (intptr_t)&openVideo);
+ if (openedOK)
+ return true;
+#endif
+
+ return false;
+}
+
+void WinampInterface::CloseEncryptedVideo()
+{
+ GetVideoOutput()->extended(VIDUSER_CLOSEVIDEORENDERER, 0, 0);
+}
+
+void WinampInterface::Buffering(int bufStatus, const wchar_t *displayString)
+{
+ 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);
+
+ 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
+ GetVideoOutput()->notifyBufferState(static_cast<int>(bufStatus*2.55f));
+}
diff --git a/Src/Plugins/Input/in_wmvdrm/WinampInterface.h b/Src/Plugins/Input/in_wmvdrm/WinampInterface.h
new file mode 100644
index 00000000..227b834c
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/WinampInterface.h
@@ -0,0 +1,121 @@
+#ifndef NULLSOFT_WINAMPINTERFACEH
+#define NULLSOFT_WINAMPINTERFACEH
+
+#include "Main.h"
+#include <windows.h>
+#include "../Winamp/wa_ipc.h"
+#include "../Winamp/In2.h"
+#include "../Winamp/strutil.h"
+#include "output/AudioOut.h"
+#include "../nu/AutoLock.h"
+
+extern AudioOut *out;
+extern In_Module plugin;
+
+class WinampInterface
+{
+public:
+ WinampInterface();
+
+ HWND GetVideoWindow();
+
+ IVideoOutput *GetVideoOutput();
+ void EndOfFile();
+ HWND GetWinampWindow();
+
+ void RefreshTitle()
+ {
+ PostMessage(plugin.hMainWindow, WM_USER, 0, IPC_UPDTITLE);
+ }
+
+ const char *GetProxy()
+ {
+ return (const char *)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GET_PROXY_STRING);
+ }
+
+ void ResetBuffering() { bufferCount=0;}
+ void Buffering(int bufStatus, const wchar_t *displayString);
+
+ bool OpenEncryptedVideo(int width, int height, bool flip, double aspect, int fourcc);
+ bool OpenVideo(int width, int height, bool flip, double aspect, int fourcc)
+ {
+ GetVideoOutput()->extended(VIDUSER_SET_THREAD_SAFE, 1, 0);
+ bool video = (GetVideoOutput()->open(width, height, flip ? 1 : 0, aspect, fourcc) == 0);
+ return video;
+ }
+
+ ULONG_PTR GetStart()
+ {
+ return SendMessage(plugin.hMainWindow, WM_WA_IPC,0,IPC_GETPLAYITEM_START);
+ }
+
+ ULONG_PTR GetEnd()
+ {
+ return SendMessage(plugin.hMainWindow, WM_WA_IPC,0,IPC_GETPLAYITEM_END);
+ }
+
+ void PressStop()
+ {
+ SendMessage(plugin.hMainWindow, WM_COMMAND, 40047, 0);
+ }
+
+ void PressPlay()
+ {
+ SendMessage(plugin.hMainWindow, WM_COMMAND,40045, 0);
+ }
+
+ void DrawFrame(void *frame)
+ {
+ GetVideoOutput()->draw(frame);
+ }
+
+ void EncryptedDrawFrame(void *frame);
+ void SetVideoStatusText(char *text)
+ {
+ GetVideoOutput()->extended(VIDUSER_SET_INFOSTRING,(INT_PTR)text,0);
+ }
+ void SetVideoPalette(RGBQUAD *palette)
+ {
+ GetVideoOutput()->extended(VIDUSER_SET_PALETTE,(INT_PTR)palette,0);
+ }
+ void CloseViz()
+ {
+ plugin.SAVSADeInit();
+ }
+ void CloseEncryptedVideo();
+ void CloseVideo()
+ {
+ GetVideoOutput()->close();
+ }
+
+ void SetAudioInfo(int bitRateKiloBits, int sampleRateKiloHertz, int channels)
+ {
+ plugin.SetInfo(bitRateKiloBits, sampleRateKiloHertz, channels, 1);
+ }
+
+ void OpenViz(int maxLatency, int sampleRate)
+ {
+ plugin.SAVSAInit(maxLatency, sampleRate);
+ }
+
+ void SetVizInfo(int sampleRate, int channels)
+ {
+ plugin.VSASetInfo(sampleRate, channels);
+ }
+
+ bool GetStatusHook(wchar_t *title, size_t titleLen, const wchar_t *filename);
+ bool HasStatus(const wchar_t *filename);
+ void SetStatus(wchar_t *_status);
+ bool GetStatus(wchar_t *title, size_t titleLen, const wchar_t *filename);
+
+ void ClearStatus();
+ Nullsoft::Utility::LockGuard statusGuard;
+ int bufferCount;
+
+private:
+ wchar_t status[1024];
+ wchar_t statusFilename[FILENAME_SIZE];
+ IVideoOutput *videoWindow;
+};
+
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/XMLString.cpp b/Src/Plugins/Input/in_wmvdrm/XMLString.cpp
new file mode 100644
index 00000000..bc36a151
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/XMLString.cpp
@@ -0,0 +1,45 @@
+#include "main.h"
+#include "XMLString.h"
+#include <strsafe.h>
+
+ XMLString::XMLString()
+ {
+ data[0]=0;
+ }
+
+ void XMLString::Reset()
+ {
+ data[0]=0;
+ }
+
+ const wchar_t *XMLString::GetString()
+ {
+ return data;
+ }
+
+void XMLString::StartTag(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params)
+{
+ data[0]=0;
+}
+
+
+void XMLString::TextHandler(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *str)
+{
+ StringCchCatW(data, 256, str);
+}
+
+
+void XMLString::ManualSet(const wchar_t *string)
+{
+StringCchCatW(data, 256, string);
+}
+
+#ifdef CBCLASS
+#undef CBCLASS
+#endif
+
+#define CBCLASS XMLString
+START_DISPATCH;
+VCB(ONSTARTELEMENT, StartTag)
+VCB(ONCHARDATA, TextHandler)
+END_DISPATCH;
diff --git a/Src/Plugins/Input/in_wmvdrm/XMLString.h b/Src/Plugins/Input/in_wmvdrm/XMLString.h
new file mode 100644
index 00000000..b80380eb
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/XMLString.h
@@ -0,0 +1,29 @@
+#ifndef NULLSOFT_WINAMP_XMLSTRING_H
+#define NULLSOFT_WINAMP_XMLSTRING_H
+
+
+#include "../xml/ifc_xmlreadercallback.h"
+/*
+this one is an xml callback that just saves the last encountered string
+*/
+
+
+class XMLString : public ifc_xmlreadercallback
+{
+public:
+ XMLString();
+ void Reset();
+ const wchar_t *GetString();
+ void ManualSet(const wchar_t *string);
+private:
+ /* XML callbacks */
+ void StartTag(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params);
+ void EndTag(const wchar_t *xmlpath, const wchar_t *xmltag);
+ void TextHandler(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *str);
+
+ wchar_t data[256]; // for now, we'll make it dynamic later
+
+ RECVS_DISPATCH;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/api.cpp b/Src/Plugins/Input/in_wmvdrm/api.cpp
new file mode 100644
index 00000000..05749577
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/api.cpp
@@ -0,0 +1,56 @@
+#include "main.h"
+#include "api.h"
+#include <windows.h>
+#include "../Winamp/wa_ipc.h"
+#include "FactoryHelper.h"
+#include "MetaTagFactory.h"
+#include "factory_Handler.h"
+#include "AlbumArt.h"
+#include "RawReader.h"
+#include "../nu/Singleton.h"
+MetaTagFactory metaTagFactory;
+
+api_service *serviceManager = 0;
+api_playlistmanager *playlistManager=0;
+api_config *AGAVE_API_CONFIG=0;
+api_application *applicationApi=0;
+
+// wasabi based services for localisation support
+api_language *WASABI_API_LNG = 0;
+HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
+api_memmgr *WASABI_API_MEMMGR = 0;
+WPLHandlerFactory wplHandlerFactory;
+ASXHandlerFactory asxHandlerFactory;
+AlbumArtFactory albumArtFactory;
+static RawMediaReaderService raw_media_reader_service;
+static SingletonServiceFactory<svc_raw_media_reader, RawMediaReaderService> raw_factory;
+
+int LoadWasabi()
+{
+ ServiceBuild(AGAVE_API_CONFIG, AgaveConfigGUID);
+ ServiceBuild(playlistManager, api_playlistmanagerGUID);
+ ServiceBuild(WASABI_API_APP, applicationApiServiceGuid);
+ ServiceBuild(WASABI_API_LNG, languageApiGUID);
+ ServiceBuild(WASABI_API_MEMMGR, memMgrApiServiceGuid);
+ plugin.service->service_register(&metaTagFactory);
+ plugin.service->service_register(&wplHandlerFactory);
+ plugin.service->service_register(&asxHandlerFactory);
+ plugin.service->service_register(&albumArtFactory);
+ raw_factory.Register(plugin.service, &raw_media_reader_service);
+
+ return TRUE;
+}
+
+void UnloadWasabi()
+{
+ plugin.service->service_deregister(&metaTagFactory);
+ plugin.service->service_deregister(&wplHandlerFactory);
+ plugin.service->service_deregister(&asxHandlerFactory);
+ plugin.service->service_deregister(&albumArtFactory);
+ plugin.service->service_deregister(&raw_factory);
+ ServiceRelease(playlistManager, api_playlistmanagerGUID);
+ ServiceRelease(AGAVE_API_CONFIG, AgaveConfigGUID);
+ ServiceRelease(WASABI_API_APP, applicationApiServiceGuid);
+ ServiceRelease(WASABI_API_LNG, languageApiGUID);
+ ServiceRelease(WASABI_API_MEMMGR, memMgrApiServiceGuid);
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/api.h b/Src/Plugins/Input/in_wmvdrm/api.h
new file mode 100644
index 00000000..4e226ea1
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/api.h
@@ -0,0 +1,42 @@
+#ifndef NULLSOFT_API_H
+#define NULLSOFT_API_H
+
+#include <windows.h>
+int LoadWasabi();
+void UnloadWasabi();
+
+#include "../playlist/api_playlistmanager.h"
+extern api_playlistmanager *playlistManager;
+#define AGAVE_API_PLAYLISTMANAGER playlistManager
+
+#include "../Agave/Config/api_config.h"
+extern api_config *config;
+#define AGAVE_API_CONFIG config
+
+#include <api/application/api_application.h>
+extern api_application *applicationApi;
+#define WASABI_API_APP applicationApi
+
+#include <api/memmgr/api_memmgr.h>
+extern api_memmgr *memmgr;
+#define WASABI_API_MEMMGR memmgr
+
+#include "../Agave/Language/api_language.h"
+
+// these are custom defines for the out_wave and out_ds embedded implementations
+extern HINSTANCE WASABI_API_LNG_HINST_WAV, WASABI_API_LNG_HINST_WAV_ORIG;
+extern HINSTANCE WASABI_API_LNG_HINST_DS, WASABI_API_LNG_HINST_DS_ORIG;
+
+#define WASABI_API_LNGSTRING_WAV(uID) WASABI_API_LNGSTR(WASABI_API_LNG_HINST_WAV,WASABI_API_LNG_HINST_WAV_ORIG,uID)
+#define WASABI_API_LNGSTRING_BUF_WAV(uID,buf,len) WASABI_API_LNGSTR(WASABI_API_LNG_HINST_WAV,WASABI_API_LNG_HINST_WAV_ORIG,uID,buf,len)
+
+#define WASABI_API_LNGSTRINGW_WAV(uID) WASABI_API_LNGSTRW(WASABI_API_LNG_HINST_WAV,WASABI_API_LNG_HINST_WAV_ORIG,uID)
+#define WASABI_API_LNGSTRINGW_BUF_WAV(uID,buf,len) WASABI_API_LNGSTRW(WASABI_API_LNG_HINST_WAV,WASABI_API_LNG_HINST_WAV_ORIG,uID,buf,len)
+
+#define WASABI_API_LNGSTRING_DS(uID) WASABI_API_LNGSTR(WASABI_API_LNG_HINST_DS,WASABI_API_LNG_HINST_DS_ORIG,uID)
+#define WASABI_API_LNGSTRING_BUF_DS(uID,buf,len) WASABI_API_LNGSTR(WASABI_API_LNG_HINST_DS,WASABI_API_LNG_HINST_DS_ORIG,uID,buf,len)
+
+#define WASABI_API_LNGSTRINGW_DS(uID) WASABI_API_LNGSTRW(WASABI_API_LNG_HINST_DS,WASABI_API_LNG_HINST_DS_ORIG,uID)
+#define WASABI_API_LNGSTRINGW_BUF_DS(uID,buf,len) WASABI_API_LNGSTRW(WASABI_API_LNG_HINST_DS,WASABI_API_LNG_HINST_DS_ORIG,uID,buf,len)
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/config.cpp b/Src/Plugins/Input/in_wmvdrm/config.cpp
new file mode 100644
index 00000000..f0f93737
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/config.cpp
@@ -0,0 +1,118 @@
+#define WM_DEFINE_CONFIG 1
+#include "config.h"
+#include "loadini.h"
+#include "main.h"
+#include "../nu/Config.h"
+
+bool config_no_video = false;
+extern Nullsoft::Utility::Config wmConfig;
+#pragma warning(disable:4800)
+#define READ(type, name) config_##name = (type)wmConfig.cfg_int(TEXT("config_") TEXT(#name), default_##name)
+#define WRITE(type, name) wmConfig.cfg_int(TEXT("config_") TEXT(#name), default_##name) = (int)config_##name
+#define DEFAULT(name) config_##name = default_##name
+
+void ReadConfig()
+{
+ READ(bool, lowmemory);
+ READ(bool, clock);
+
+ READ(bool, video_dedicated_thread);
+ READ(bool, video_early);
+ READ(int, video_early_pad);
+ READ(bool, video_outoforder);
+ READ(bool, video_catchup);
+ READ(int, video_jitter);
+ READ(int, video_drop_threshold);
+ READ(size_t, video_cache_frames);
+ READ(bool, video_notifylate);
+ READ(bool, video_framedropoffset);
+
+ READ(bool, audio_outoforder);
+ READ(bool, audio_dedicated_thread);
+ READ(int, audio_early_pad);
+ READ(bool, audio_early);
+ READ(size_t, audio_cache_frames);
+ READ(size_t, audio_num_channels);
+
+// READ(bool, no_silent);
+// READ(bool, untrusted_ok);
+
+ READ(bool, http_metadata);
+ READ(size_t, buffer_time);
+
+ READ(bool, extra_asx_extensions);
+
+ READ(int, col1);
+ READ(int, col2);
+}
+
+void WriteConfig()
+{
+ WRITE(bool, lowmemory);
+
+ WRITE(bool, clock);
+
+ WRITE(bool, video_dedicated_thread);
+ WRITE(bool, video_early);
+ WRITE(int, video_early_pad);
+ WRITE(bool, video_outoforder);
+ WRITE(bool, video_catchup);
+ WRITE(int, video_jitter);
+ WRITE(int, video_drop_threshold);
+ WRITE(size_t, video_cache_frames);
+ WRITE(bool, video_notifylate);
+ WRITE(bool, video_framedropoffset);
+
+ WRITE(bool, audio_outoforder);
+ WRITE(bool, audio_dedicated_thread);
+ WRITE(int, audio_early_pad);
+ WRITE(bool, audio_early);
+ WRITE(size_t, audio_cache_frames);
+ WRITE(size_t, audio_num_channels);
+
+// WRITE(bool, no_silent);
+// WRITE(bool, untrusted_ok);
+
+ WRITE(bool, http_metadata);
+ WRITE(size_t, buffer_time);
+
+ WRITE(bool, extra_asx_extensions);
+
+ WRITE(int, col1);
+ WRITE(int, col2);
+}
+
+void DefaultConfig()
+{
+ DEFAULT(http_metadata);
+// DEFAULT(no_silent);
+// DEFAULT(untrusted_ok);
+ DEFAULT(buffer_time);
+ DEFAULT(audio_num_channels);
+
+ DEFAULT(audio_outoforder);
+ DEFAULT(audio_dedicated_thread);
+ DEFAULT(audio_early_pad);
+ DEFAULT(audio_early);
+ DEFAULT(audio_cache_frames);
+
+ DEFAULT(lowmemory);
+
+ DEFAULT(clock);
+
+ DEFAULT(video_dedicated_thread);
+ DEFAULT(video_early);
+ DEFAULT(video_early_pad);
+ DEFAULT(video_outoforder);
+ DEFAULT(video_catchup);
+ DEFAULT(video_jitter);
+ DEFAULT(video_drop_threshold);
+ DEFAULT(video_cache_frames);
+ DEFAULT(video_notifylate);
+ DEFAULT(video_framedropoffset);
+
+ DEFAULT(extra_asx_extensions);
+
+ DEFAULT(col1);
+ DEFAULT(col2);
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/config.h b/Src/Plugins/Input/in_wmvdrm/config.h
new file mode 100644
index 00000000..09920677
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/config.h
@@ -0,0 +1,50 @@
+#ifndef NULLSOFT_CONFIGH
+#define NULLSOFT_CONFIGH
+#include "dsound.h"
+#ifdef WM_DEFINE_CONFIG
+#define DEFVAL(x) =x
+#define CFGEXTERN
+#else
+#define DEFVAL(x)
+#define CFGEXTERN extern
+#endif
+
+#define CFG(type, name, defval) CFGEXTERN type config_##name DEFVAL(defval); CFGEXTERN type default_##name DEFVAL(defval);
+
+CFG(bool, lowmemory, true);
+CFG(bool, clock, true);
+
+CFG(bool, video_dedicated_thread, true);
+CFG(bool, video_early, false);
+CFG(int, video_early_pad, 500);
+CFG(bool, video_outoforder, true);
+CFG(bool, video_catchup, true);
+CFG(int, video_jitter, 5);
+CFG(int, video_drop_threshold, 15);
+CFG(size_t, video_cache_frames, 16);
+CFG(bool, video_notifylate, true);
+CFG(bool, video_framedropoffset, false);
+//CFG(bool, video_flip, false);
+
+CFG(bool, audio_outoforder, false);
+CFG(bool, audio_dedicated_thread, true);
+CFG(int, audio_early_pad, 0);
+CFG(bool, audio_early, false);
+CFG(size_t, audio_cache_frames, 12);
+CFG(DWORD, audio_num_channels, DSSPEAKER_5POINT1);
+
+CFG(bool, no_silent, false);
+CFG(bool, untrusted_ok, false);
+
+CFG(bool, http_metadata, false);
+CFG(size_t, buffer_time, 5000);
+
+CFG(int, col1, -1);
+CFG(int, col2, -1);
+
+extern bool config_no_video;
+
+CFG(bool, extra_asx_extensions, false);
+void ReadConfig(), WriteConfig(), DefaultConfig();
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/directdraw.cpp b/Src/Plugins/Input/in_wmvdrm/directdraw.cpp
new file mode 100644
index 00000000..a62de00e
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/directdraw.cpp
@@ -0,0 +1,23 @@
+#include "main.h"
+#include "directdraw.h"
+
+HRESULT (WINAPI *_DirectDrawCreate)(GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter) = 0;
+
+HRESULT DDrawCreate(GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter)
+{
+ static int a = 0;
+ if (!_DirectDrawCreate && !a)
+ {
+ a++;
+ HINSTANCE h = LoadLibrary(L"ddraw.dll");
+ if (h)
+ {
+ *(void**)&_DirectDrawCreate = (void*)GetProcAddress(h, "DirectDrawCreate");
+ }
+ }
+
+ if (_DirectDrawCreate)
+ return _DirectDrawCreate(lpGUID, lplpDD, pUnkOuter);
+ else
+ return S_OK; // TODO: uhhh no this should be an error :)
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/directdraw.h b/Src/Plugins/Input/in_wmvdrm/directdraw.h
new file mode 100644
index 00000000..caa80038
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/directdraw.h
@@ -0,0 +1,8 @@
+#ifndef NULLSOFT_DDRAWH
+#define NULLSOFT_DDRAWH
+#include <ddraw.h>
+
+ extern HRESULT (WINAPI *_DirectDrawCreate)(GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter);
+
+ HRESULT DDrawCreate(GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter);
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/factory_Handler.cpp b/Src/Plugins/Input/in_wmvdrm/factory_Handler.cpp
new file mode 100644
index 00000000..ffb825dc
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/factory_Handler.cpp
@@ -0,0 +1,80 @@
+#include "main.h"
+#include "api.h"
+#include "factory_Handler.h"
+#include "PlaylistHandler.h"
+#include <api/service/services.h>
+
+WPLHandler wplHandler;
+ASXHandler asxHandler;
+
+
+#define DEFINE_HANDLER_FACTORY(CLASSNAME, className) const char *CLASSNAME ## HandlerFactory::GetServiceName() { return #CLASSNAME ## "Playlist Handler"; }\
+ GUID CLASSNAME ## HandlerFactory::GetGUID(){ return className ## HandlerGUID;}\
+ void *CLASSNAME ## HandlerFactory::GetInterface(int global_lock){ return &className ## Handler;}
+
+
+DEFINE_HANDLER_FACTORY(WPL, wpl);
+DEFINE_HANDLER_FACTORY(ASX, asx);
+
+/* --------------------------------------------------------------------- */
+FOURCC CommonHandlerFactory::GetServiceType()
+{
+ return WaSvc::PLAYLISTHANDLER;
+}
+
+int CommonHandlerFactory::SupportNonLockingInterface()
+{
+ return 1;
+}
+
+int CommonHandlerFactory::ReleaseInterface(void *ifc)
+{
+ return 1;
+}
+
+const char *CommonHandlerFactory::GetTestString()
+{
+ return NULL;
+}
+
+int CommonHandlerFactory::ServiceNotify(int msg, int param1, int param2)
+{
+ return 1;
+}
+
+
+
+
+#undef CBCLASS
+#define CBCLASS WPLHandlerFactory
+START_DISPATCH;
+CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName)
+CB(WASERVICEFACTORY_GETGUID, GetGUID)
+CB(WASERVICEFACTORY_GETINTERFACE, GetInterface)
+#undef CBCLASS
+#define CBCLASS CommonHandlerFactory
+CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType)
+CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface)
+CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface)
+CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString)
+CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify)
+END_DISPATCH;
+
+#undef CBCLASS
+#define CBCLASS ASXHandlerFactory
+START_DISPATCH;
+CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName)
+CB(WASERVICEFACTORY_GETGUID, GetGUID)
+CB(WASERVICEFACTORY_GETINTERFACE, GetInterface)
+#undef CBCLASS
+#define CBCLASS CommonHandlerFactory
+CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType)
+CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface)
+CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface)
+CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString)
+CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify)
+END_DISPATCH;
+
+
+
+
diff --git a/Src/Plugins/Input/in_wmvdrm/factory_Handler.h b/Src/Plugins/Input/in_wmvdrm/factory_Handler.h
new file mode 100644
index 00000000..16215543
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/factory_Handler.h
@@ -0,0 +1,30 @@
+#ifndef NULLSOFT_FACTORY_HANDLER_H
+#define NULLSOFT_FACTORY_HANDLER_H
+
+#include <api/service/waservicefactory.h>
+#include <api/service/services.h>
+
+class CommonHandlerFactory : public waServiceFactory
+{
+public:
+
+ FOURCC GetServiceType();
+ int SupportNonLockingInterface();
+ int ReleaseInterface(void *ifc);
+ const char *GetTestString();
+ int ServiceNotify(int msg, int param1, int param2);
+
+};
+
+#define DECLARE_HANDLER_FACTORY(CLASSNAME) class CLASSNAME : public CommonHandlerFactory {\
+public:\
+ const char *GetServiceName();\
+ GUID GetGUID();\
+ void *GetInterface(int global_lock);\
+protected:\
+ RECVS_DISPATCH;}
+
+DECLARE_HANDLER_FACTORY(ASXHandlerFactory);
+DECLARE_HANDLER_FACTORY(WPLHandlerFactory);
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/in_wm.rc b/Src/Plugins/Input/in_wmvdrm/in_wm.rc
new file mode 100644
index 00000000..1b57130b
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/in_wm.rc
@@ -0,0 +1,331 @@
+// 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
+
+#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
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_CONFIG DIALOGEX 0, 0, 344, 142
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Windows Media Decoder Settings"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ GROUPBOX "General",IDC_STATIC,4,4,156,59
+ CONTROL "Retrieve metadata for HTTP streams",IDC_HTTPMETA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,16,133,10
+ LTEXT "Streaming prebuffer: ",IDC_STATIC,10,30,70,8
+ EDITTEXT IDC_BUFFER_TIME,81,28,41,12,ES_AUTOHSCROLL
+ LTEXT "ms",IDC_STATIC,126,30,10,8
+ LTEXT "Speaker Configuration:",IDC_STATIC,10,43,75,12,SS_CENTERIMAGE
+ COMBOBOX IDC_AUDIO_SPEAKER_COUNT,88,43,66,83,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP
+ GROUPBOX "Advanced Settings",IDC_STATIC,4,67,156,52
+ LTEXT "Warning: For advanced users only!",IDC_STATIC,10,83,128,10
+ PUSHBUTTON "Advanced ...",IDC_ADVANCED,52,98,50,13
+/* GROUPBOX "Protected Media",IDC_STATIC,4,80,156,42
+ CONTROL "Try to acquire licenses silently",IDC_SILENT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,93,111,10
+ CONTROL "Always accept untrusted certificates",IDC_UNTRUSTED,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,106,133,10
+*/
+ GROUPBOX "Filetypes",IDC_STATIC,164,4,176,134
+ CONTROL "",IDC_TYPELIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,172,17,161,85
+ PUSHBUTTON "Add ...",IDC_ADDTYPE,172,107,51,13
+ PUSHBUTTON "Edit ...",IDC_EDITTYPE,227,107,50,13
+ PUSHBUTTON "Remove",IDC_REMOVETYPE,281,107,52,13
+ CONTROL "Enable WAX/WMX/WVX playlist extensions",IDC_EXTRA_ASX,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,172,124,151,10
+ PUSHBUTTON "Save",IDOK,4,125,46,13
+ PUSHBUTTON "Cancel",IDCANCEL,54,125,48,13
+ PUSHBUTTON "Defaults",IDC_DEFAULTTYPE,110,125,50,13
+END
+
+IDD_ADDTYPE DIALOGEX 0, 0, 168, 124
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Add File Type"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ LTEXT "Type",IDC_STATIC_TYPE,4,9,36,8,NOT WS_GROUP
+ EDITTEXT IDC_TYPE,44,7,120,12,ES_AUTOHSCROLL
+ LTEXT "Description",IDC_STATIC,4,25,36,8,NOT WS_GROUP
+ EDITTEXT IDC_DESCRIPTION,44,23,120,12,ES_AUTOHSCROLL
+ GROUPBOX "Extension Type",IDC_STATIC,4,40,160,30
+ CONTROL "File Extension",IDC_FILEEXTENSION,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,11,53,60,10
+ CONTROL "Protocol",IDC_PROTOCOL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,84,53,42,10
+ GROUPBOX "Media Type",IDC_STATIC,4,73,160,30
+ CONTROL "Audio Only",IDC_TYPE_AUDIO,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,11,86,51,10
+ CONTROL "Audio/Video",IDC_TYPE_VIDEO,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,84,86,54,10
+ DEFPUSHBUTTON "OK",IDOK,60,107,50,13
+ PUSHBUTTON "Cancel",IDCANCEL,114,107,50,13
+END
+
+IDD_ADVANCED DIALOGEX 0, 0, 190, 214
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Windows Media Decoder Advanced Settings"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ GROUPBOX "Audio",IDC_STATIC,4,4,182,60
+ CONTROL "Dedicated Delivery Thread",IDC_AUDIO_THREAD,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,204,15,101,10
+ CONTROL "Deliver samples early",IDC_AUDIO_EARLY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,18,80,10
+ LTEXT "and add",IDC_STATIC,96,18,30,10,SS_CENTERIMAGE
+ EDITTEXT IDC_AUDIO_EARLYPAD,126,17,40,12,ES_AUTOHSCROLL
+ LTEXT "ms",IDC_STATIC,168,18,11,10,SS_CENTERIMAGE
+ CONTROL "Allow out-of-order frames",IDC_AUDIO_OUTOFORDER,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,204,25,99,10
+ LTEXT "cache",IDC_STATIC,12,32,21,12,SS_CENTERIMAGE
+ EDITTEXT IDC_AUDIO_CACHE_FRAMES,37,32,40,12,ES_AUTOHSCROLL
+ LTEXT "frames",IDC_STATIC,81,32,30,12,SS_CENTERIMAGE
+ CONTROL "Drop audio frames instead of video frames",IDC_AUDIO_DROP,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,48,152,10
+ GROUPBOX "General",IDC_STATIC,4,164,182,28
+ CONTROL "Low Memory",IDC_LOWMEMORY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,175,56,10
+ CONTROL "Real Time",IDC_REALTIME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,108,175,47,10
+ GROUPBOX "Video",IDC_STATIC,4,68,182,93
+ CONTROL "Dedicated Delivery Thread",IDC_VIDEO_THREAD,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,205,113,101,10
+ CONTROL "Deliver samples early",IDC_VIDEO_EARLY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,81,80,10
+ LTEXT "by",IDC_STATIC,96,81,10,10,SS_CENTERIMAGE
+ EDITTEXT IDC_VIDEO_EARLYPAD,108,81,40,12,ES_AUTOHSCROLL
+ LTEXT "ms",IDC_STATIC,150,81,11,10,SS_CENTERIMAGE
+ CONTROL "Allow out-of-order frames",IDC_VIDEO_OUTOFORDER,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,205,126,99,10
+ LTEXT "Cache",IDC_STATIC,14,97,21,12,SS_CENTERIMAGE
+ EDITTEXT IDC_VIDEO_CACHE_FRAMES,42,97,40,12,ES_AUTOHSCROLL
+ LTEXT "frames",IDC_STATIC,88,97,30,12,SS_CENTERIMAGE
+ LTEXT "Jitter:",IDC_STATIC,14,113,20,12,SS_CENTERIMAGE
+ EDITTEXT IDC_VIDEO_JITTER,42,113,40,12,ES_AUTOHSCROLL
+ LTEXT "ms",IDC_STATIC,88,113,10,12,SS_CENTERIMAGE
+ LTEXT "Frame drop threshold:",IDC_STATIC,12,131,72,10,SS_CENTERIMAGE
+ EDITTEXT IDC_VIDEO_DROP_THRESHOLD,88,129,40,12,ES_AUTOHSCROLL
+ LTEXT "ms",IDC_STATIC,132,131,10,10,SS_CENTERIMAGE
+ CONTROL "Report dropped frames to decoder",IDC_VIDEO_NOTIFY,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,145,127,10
+ DEFPUSHBUTTON "OK",IDOK,82,197,50,13
+ PUSHBUTTON "Cancel",IDCANCEL,136,197,50,13
+END
+
+IDD_INFO 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",-1,0,0,341,164
+ CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,6,13,327,143
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_CONFIG, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ RIGHTMARGIN, 340
+ TOPMARGIN, 4
+ BOTTOMMARGIN, 138
+ END
+
+ IDD_ADDTYPE, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ RIGHTMARGIN, 164
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 120
+ END
+
+ IDD_ADVANCED, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ RIGHTMARGIN, 186
+ TOPMARGIN, 4
+ BOTTOMMARGIN, 210
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_WINDOWS_MEDIA_DECODER "Nullsoft Windows Media Decoder v%s"
+ 65535 "{C5B78F09-3222-4a64-AA98-F1ABC5A9E355}"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_WINDOWS_MEDIA_DECODER_OLD "Nullsoft Windows Media Decoder"
+ IDS_REALTIME "Realtime"
+ IDS_CONNECTING "Connecting"
+ IDS_LOCATING "Locating"
+ IDS_ACCESS_DENIED "Access Denied"
+ IDS_STEREO "Stereo"
+ IDS_QUADROPHONIC "Quadrophonic"
+ IDS_SURROUND "Surround"
+ IDS_5_1 "5.1"
+ IDS_7_1 "7.1"
+ IDS_EXT "Ext"
+ IDS_DESCRIPTION "Description"
+ IDS_PROTOCOL "Protocol"
+ IDS_EXTENSION "Extension"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_EDIT_FILE_TYPE "Edit File Type"
+ IDS_WMA_AUDIO_FILE "Windows Media Audio File (*.WMA)"
+ IDS_WMA_VIDEO_FILE "Windows Media Video File (*.WMV)"
+ IDS_ASF_STREAM "Advanced Streaming Format (*.ASF)"
+ IDS_WINDOWS_MEDIA_STREAM "Windows Media Stream"
+ IDS_UNKNOWN_ERROR "unknown error: %x"
+ IDS_ATTRIBUTE "Attribute"
+ IDS_VALUE "Value"
+ IDS_CLOSE "Close"
+ IDS_UNABLE_TO_WRITE_TO_FILE_DOES_FILE_EXIST
+ "Unable to write to file\nDoes the file still exist?"
+ IDS_UNABLE_TO_WRITE_TO_FILE "Unable to write to file"
+ IDS_UNABLE_TO_WRITE_TO_FILE_MAY_NOT_HAVE_ACCESS
+ "Unable to write to file\nYou may not have access to modify this file."
+ IDS_SAVE_FAILED "Save failed"
+ IDS_TRUE "True"
+ IDS_FALSE "False"
+ IDS_UNKNOWN "Unknown"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_WINDOWS_MEDIA_XXX "Windows Media: %dx%d %c%c%c%c"
+ IDS_WINDOWS_MEDIA_PROTECTED_CONTENT_NEED_LICENSE
+ "Windows Media Protected Content\n\nIn order to play this file, you must acquire a license.\nA website will launch to perform this process.\n\nProceed?"
+ IDS_LICENSE_REQUIRED "License required"
+ IDS_BEGINNING_UPDATE "Beginning Update"
+ IDS_UPDATE_FAILED "Update Failed"
+ IDS_UPDATE_CANCELLED "Update Cancelled"
+ IDS_IDLE "Idle"
+ IDS_REQUESTING "Requesting"
+ IDS_RECEIVING_DATA "Receiving Data"
+ IDS_COMPLETED "Completed"
+ IDS_INSTALLING "Installing"
+ IDS_FILE_PROTECTED_CANNOT_PLAY_IN_WINAMP
+ "This file is protected, but cannot be played in Winamp."
+ IDS_LICENSE_ACQUIRED "License Acquired"
+ IDS_WINDOWS_MEDIA_PROTECTED_CONTENT_UNTRUSTED_SOURCE
+ "Windows Media Protected Content\n\nThe license for this license is from an untrusted source.\n\nProceed?"
+ IDS_UNTRUSTED_LICENSE "Untrusted License"
+ IDS_WINDOWS_MEDIA_PROTECTED_CONTENT_TAMPERED_LICENSE
+ "Windows Media Protected Content\n\nThe license for this license appeared to have been tampered.\nIt is recommended that you answer ""No"".\n\nProceed?"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_TAMPERED_LICENSE "Tampered License"
+ IDS_WINDOWS_MEDIA_PROTECTED_CONTENT_SECURITY_UPDATE
+ "Windows Media Protected Content\n\nIn order to play this file, a security update is required.\n\nProceed?"
+ IDS_SECURITY_UPDATE_REQUIRED "Security Update Required"
+ IDS_UPDATING "Updating"
+ IDS_ENCRYPTED_IN_OP_LEVEL_HIGHER_THAN_SUPPORTED
+ "This file is encrypted with an Output Protection Level higher than supported in Winamp."
+ IDS_WINDOWS_MEDIA_PLAYBACK_FAILURE "Windows Media Playback Failure"
+ IDS_UPDATE_REQUIRED "Update required"
+ IDS_ACQUIRING_LICENSE "Acquiring License"
+ IDS_DRM_LICENSE_EXPIRED_VISIT_WINAMP_COM
+ "DRM License has expired! Please visit Winamp.com"
+ IDS_ERROR "Error"
+ IDS_BUFFERING "Buffering"
+ IDS_UNKNOWN_ERROR_WAV "Unknown Error."
+ IDS_CODEC "Codec"
+ IDS_DURATION "Duration"
+ IDS_BITRATE "Bitrate"
+ IDS_FILESIZE "File Size"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_YES "Yes"
+ IDS_NO "No"
+ IDS_WMVER "WM Version"
+ IDS_SEEKABLE "Seekable"
+ IDS_STRIDABLE "Stridable"
+ IDS_BROADCAST "Broadcast"
+ IDS_PROTECTED "Protected"
+ IDS_TRUSTED "Trusted"
+ IDS_CONTAINS "Contains"
+ IDS_NAME "Name"
+ IDS_KBPS "kbps"
+ IDS_ABOUT_TEXT "%s\n© 2005-2023 Winamp SA\nWritten by: Ben Allison\nBuild date: %s\n\nWindows Media is a trademark\nof Microsoft Corporation."
+END
+
+STRINGTABLE
+BEGIN
+ IDS_AUDIO "Audio"
+ IDS_VIDEO "Video"
+ IDS_IMAGE "Image"
+ IDS_SCRIPT "Script"
+ IDS_NONE "None"
+ IDS_WINDOWS_MEDIA_PLAYLIST "Windows Media Playlist"
+ IDS_ASX_PLAYLIST "Advanced Streaming Redirector (ASX)"
+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_wmvdrm/in_wmvdrm.sln b/Src/Plugins/Input/in_wmvdrm/in_wmvdrm.sln
new file mode 100644
index 00000000..af0ece5c
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/in_wmvdrm.sln
@@ -0,0 +1,30 @@
+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_wm", "in_wmvdrm.vcxproj", "{9362E6C2-EFCC-4104-8671-78222E0A5FCC}"
+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
+ {9362E6C2-EFCC-4104-8671-78222E0A5FCC}.Debug|Win32.ActiveCfg = Debug|Win32
+ {9362E6C2-EFCC-4104-8671-78222E0A5FCC}.Debug|Win32.Build.0 = Debug|Win32
+ {9362E6C2-EFCC-4104-8671-78222E0A5FCC}.Debug|x64.ActiveCfg = Debug|x64
+ {9362E6C2-EFCC-4104-8671-78222E0A5FCC}.Debug|x64.Build.0 = Debug|x64
+ {9362E6C2-EFCC-4104-8671-78222E0A5FCC}.Release|Win32.ActiveCfg = Release|Win32
+ {9362E6C2-EFCC-4104-8671-78222E0A5FCC}.Release|Win32.Build.0 = Release|Win32
+ {9362E6C2-EFCC-4104-8671-78222E0A5FCC}.Release|x64.ActiveCfg = Release|x64
+ {9362E6C2-EFCC-4104-8671-78222E0A5FCC}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {3A0E00EA-C715-4273-A005-BCCCC6158254}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/Plugins/Input/in_wmvdrm/in_wmvdrm.vcxproj b/Src/Plugins/Input/in_wmvdrm/in_wmvdrm.vcxproj
new file mode 100644
index 00000000..e4d8d491
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/in_wmvdrm.vcxproj
@@ -0,0 +1,361 @@
+<?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">
+ <ProjectName>in_wm</ProjectName>
+ <ProjectGuid>{9362E6C2-EFCC-4104-8671-78222E0A5FCC}</ProjectGuid>
+ <RootNamespace>in_wmvdrm</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>
+ <GenerateManifest>false</GenerateManifest>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <GenerateManifest>false</GenerateManifest>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnabled>false</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;_USRDLL;IN_WMVDRM_EXPORTS;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;NO_DRM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalOptions>/ignore:4253 /ignore:4254 %(AdditionalOptions)</AdditionalOptions>
+ <AdditionalDependencies>wmvcore.lib;comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <TargetMachine>MachineX86</TargetMachine>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </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;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;_USRDLL;IN_WMVDRM_EXPORTS;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;NO_DRM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalOptions>/ignore:4253 /ignore:4254 %(AdditionalOptions)</AdditionalOptions>
+ <AdditionalDependencies>wmvcore.lib;comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </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>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;_USRDLL;IN_WMVDRM_EXPORTS;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;NO_DRM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4005;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalOptions>/ignore:4253 /ignore:4254 %(AdditionalOptions)</AdditionalOptions>
+ <AdditionalDependencies>wmvcore.lib;comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <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>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </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>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;_USRDLL;IN_WMVDRM_EXPORTS;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;NO_DRM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4005;4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalOptions>/ignore:4253 /ignore:4254 %(AdditionalOptions)</AdditionalOptions>
+ <AdditionalDependencies>wmvcore.lib;comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <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>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </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>
+ <ClCompile Include="..\..\..\nu\CCVersion.cpp" />
+ <ClCompile Include="..\..\..\nu\listview.cpp" />
+ <ClCompile Include="..\..\..\Winamp\strutil.cpp" />
+ <ClCompile Include="AlbumArt.cpp" />
+ <ClCompile Include="api.cpp" />
+ <ClCompile Include="ASXLoader.cpp" />
+ <ClCompile Include="AudioFormat.cpp" />
+ <ClCompile Include="AudioLayer.cpp" />
+ <ClCompile Include="AudioThread.cpp" />
+ <ClCompile Include="BufferLayer.cpp" />
+ <ClCompile Include="ClockLayer.cpp" />
+ <ClCompile Include="config.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">WM_DEFINE_CONFIG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">WM_DEFINE_CONFIG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="ConfigDialog.cpp" />
+ <ClCompile Include="directdraw.cpp" />
+ <ClCompile Include="ExtendedFileInfo.cpp" />
+ <ClCompile Include="ExtendedRead.cpp" />
+ <ClCompile Include="factory_Handler.cpp" />
+ <ClCompile Include="FileInfoDialog.cpp" />
+ <ClCompile Include="FileTypes.cpp" />
+ <ClCompile Include="GainLayer.cpp" />
+ <ClCompile Include="loadini.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="MediaThread.cpp" />
+ <ClCompile Include="MetaTag.cpp" />
+ <ClCompile Include="MetaTagFactory.cpp" />
+ <ClCompile Include="output\OutPlugin.cpp" />
+ <ClCompile Include="PlaylistHandler.cpp" />
+ <ClCompile Include="RawReader.cpp" />
+ <ClCompile Include="SeekLayer.cpp" />
+ <ClCompile Include="StatusHook.cpp" />
+ <ClCompile Include="TagAlias.cpp" />
+ <ClCompile Include="util.cpp" />
+ <ClCompile Include="VideoDataConverter.cpp" />
+ <ClCompile Include="VideoLayer.cpp" />
+ <ClCompile Include="VideoOutputChildDDraw.cpp" />
+ <ClCompile Include="VideoThread.cpp" />
+ <ClCompile Include="vidutils.cpp" />
+ <ClCompile Include="vid_ddraw.cpp" />
+ <ClCompile Include="vid_overlay.cpp" />
+ <ClCompile Include="WaitLayer.cpp" />
+ <ClCompile Include="WinampInterface.cpp" />
+ <ClCompile Include="WMCallback.cpp" />
+ <ClCompile Include="WMDRMModule.cpp" />
+ <ClCompile Include="WMHandler.cpp" />
+ <ClCompile Include="WMInformation.cpp" />
+ <ClCompile Include="WMPlaylist.cpp" />
+ <ClCompile Include="WPLLoader.cpp" />
+ <ClCompile Include="XMLString.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\..\nu\listview.h" />
+ <ClInclude Include="..\..\Output\out_ds\res_wa2\resource.h" />
+ <ClInclude Include="..\..\..\Winamp\strutil.h" />
+ <ClInclude Include="AlbumArt.h" />
+ <ClInclude Include="api.h" />
+ <ClInclude Include="ASXLoader.h" />
+ <ClInclude Include="AudioFormat.h" />
+ <ClInclude Include="AudioLayer.h" />
+ <ClInclude Include="AudioThread.h" />
+ <ClInclude Include="AutoChar.h" />
+ <ClInclude Include="AutoWide.h" />
+ <ClInclude Include="BufferLayer.h" />
+ <ClInclude Include="ClockLayer.h" />
+ <ClInclude Include="config.h" />
+ <ClInclude Include="ConfigDialog.h" />
+ <ClInclude Include="directdraw.h" />
+ <ClInclude Include="ExtendedRead.h" />
+ <ClInclude Include="factory_Handler.h" />
+ <ClInclude Include="FileInfoDialog.h" />
+ <ClInclude Include="FileTypes.h" />
+ <ClInclude Include="GainLayer.h" />
+ <ClInclude Include="loadini.h" />
+ <ClInclude Include="Main.h" />
+ <ClInclude Include="MediaThread.h" />
+ <ClInclude Include="MetaTag.h" />
+ <ClInclude Include="MetaTagFactory.h" />
+ <ClInclude Include="OutputStream.h" />
+ <ClInclude Include="output\AudioOut.h" />
+ <ClInclude Include="output\OutPlugin.h" />
+ <ClInclude Include="PlaylistHandler.h" />
+ <ClInclude Include="RawReader.h" />
+ <ClInclude Include="Remaining.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="SeekLayer.h" />
+ <ClInclude Include="StatusHook.h" />
+ <ClInclude Include="TagAlias.h" />
+ <ClInclude Include="util.h" />
+ <ClInclude Include="VideoDataConverter.h" />
+ <ClInclude Include="VideoLayer.h" />
+ <ClInclude Include="VideoOutputChildDDraw.h" />
+ <ClInclude Include="VideoThread.h" />
+ <ClInclude Include="vidutils.h" />
+ <ClInclude Include="vid_ddraw.h" />
+ <ClInclude Include="vid_overlay.h" />
+ <ClInclude Include="WaitLayer.h" />
+ <ClInclude Include="WinampInterface.h" />
+ <ClInclude Include="WMCallback.h" />
+ <ClInclude Include="WMDRMModule.h" />
+ <ClInclude Include="WMHandler.h" />
+ <ClInclude Include="WMInformation.h" />
+ <ClInclude Include="WMPlaylist.h" />
+ <ClInclude Include="WPLLoader.h" />
+ <ClInclude Include="XMLString.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="in_wm.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Text Include="DESIGN.txt" />
+ <Text Include="TODO.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
+ <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
+ </ProjectReference>
+ </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_wmvdrm/in_wmvdrm.vcxproj.filters b/Src/Plugins/Input/in_wmvdrm/in_wmvdrm.vcxproj.filters
new file mode 100644
index 00000000..98ac6298
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/in_wmvdrm.vcxproj.filters
@@ -0,0 +1,330 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="AlbumArt.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="api.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ASXLoader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="AudioFormat.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="AudioLayer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="AudioThread.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="BufferLayer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ClockLayer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="config.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ConfigDialog.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="directdraw.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="factory_Handler.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FileInfoDialog.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FileTypes.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="GainLayer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="loadini.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MediaThread.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MetaTag.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MetaTagFactory.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="output\OutPlugin.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="PlaylistHandler.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="RawReader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SeekLayer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="StatusHook.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TagAlias.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="util.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="vid_ddraw.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="vid_overlay.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VideoDataConverter.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VideoLayer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VideoOutputChildDDraw.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VideoThread.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="vidutils.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WaitLayer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WinampInterface.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WMCallback.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WMDRMModule.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WMHandler.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WMInformation.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WMPlaylist.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WPLLoader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="XMLString.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\CCVersion.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\listview.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Winamp\strutil.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="AlbumArt.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="api.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ASXLoader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="AudioFormat.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="AudioLayer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="output\AudioOut.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="AudioThread.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="AutoChar.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="AutoWide.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="BufferLayer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ClockLayer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="config.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ConfigDialog.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="directdraw.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ExtendedRead.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="factory_Handler.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="FileInfoDialog.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="FileTypes.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="GainLayer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="loadini.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MediaThread.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MetaTagFactory.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MetaTag.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="output\OutPlugin.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="OutputStream.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="PlaylistHandler.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="RawReader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Remaining.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="SeekLayer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="StatusHook.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="TagAlias.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="util.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="vid_ddraw.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="vid_overlay.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="VideoDataConverter.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="VideoLayer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="VideoOutputChildDDraw.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="VideoThread.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="vidutils.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="WaitLayer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="WinampInterface.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="WMCallback.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="WMDRMModule.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="WMHandler.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="WMInformation.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="WMPlaylist.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="WPLLoader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="XMLString.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nu\listview.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Output\out_ds\res_wa2\resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Winamp\strutil.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Text Include="DESIGN.txt" />
+ <Text Include="TODO.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{c26aaf40-61a0-4d01-b985-d9da875ad265}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{e22165b9-a669-4451-bca6-7d77a42f2d99}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{375a8628-880b-412e-b643-d12761afdc4b}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="in_wm.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/loadini.cpp b/Src/Plugins/Input/in_wmvdrm/loadini.cpp
new file mode 100644
index 00000000..482abeb7
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/loadini.cpp
@@ -0,0 +1,12 @@
+#include "main.h"
+#include "loadini.h"
+#include "AutoWide.h"
+#include "../Winamp/wa_ipc.h"
+extern wchar_t INI_FILE[MAX_PATH];
+void IniFile(HWND hMainWindow)
+{
+ if (!INI_FILE[0])
+ {
+ lstrcpyn(INI_FILE, (wchar_t *)SendMessage(hMainWindow, WM_WA_IPC, 0, IPC_GETINIFILEW), MAX_PATH);
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/loadini.h b/Src/Plugins/Input/in_wmvdrm/loadini.h
new file mode 100644
index 00000000..4d57eb6b
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/loadini.h
@@ -0,0 +1,8 @@
+#ifndef NULLSOFT_LOADINIH
+#define NULLSOFT_LOADINIH
+
+#include <windows.h>
+extern wchar_t INI_FILE[MAX_PATH];
+void IniFile(HWND hMainWindow);
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/main.cpp b/Src/Plugins/Input/in_wmvdrm/main.cpp
new file mode 100644
index 00000000..af26d4e0
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/main.cpp
@@ -0,0 +1,136 @@
+#include "Main.h"
+#include "api.h"
+#include "loadini.h"
+#include "FileTypes.h"
+#include <commctrl.h>
+#include "../nu/Config.h"
+#include "../nu/CCVersion.h"
+#include "resource.h"
+
+wchar_t INI_FILE[MAX_PATH] = L"";
+IDispatch *winampExternal = 0;
+Nullsoft::Utility::Config wmConfig;
+WMDRM mod;
+HINSTANCE WASABI_API_LNG_HINST_WAV = 0;
+HINSTANCE WASABI_API_LNG_HINST_DS = 0;
+
+int Init()
+{
+ if (!IsWindow(plugin.hMainWindow))
+ return IN_INIT_FAILURE;
+
+ if (!LoadWasabi())
+ return IN_INIT_FAILURE;
+
+ plugin.UsesOutputPlug |= 8;
+
+ // need to have this initialised before we try to do anything with localisation features
+ WASABI_API_START_LANG(plugin.hDllInstance,InWmLangGUID);
+
+ static wchar_t szDescription[256];
+ swprintf(szDescription,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_WINDOWS_MEDIA_DECODER),WMDRM_VERSION);
+ plugin.description = (char*)szDescription;
+
+ IniFile(plugin.hMainWindow);
+ wmConfig.SetFile(INI_FILE, L"in_wm");
+ ReadConfig();
+ fileTypes.ReadConfig();
+ if (NULL == winampExternal)
+ {
+ winampExternal = (IDispatch *)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GET_DISPATCH_OBJECT); // ask for winamp's
+ if (winampExternal == (IDispatch *)1)
+ winampExternal = 0;
+ }
+
+ mod.Init();
+ return IN_INIT_SUCCESS;
+}
+
+void Quit()
+{
+ mod.Quit();
+ UnloadWasabi();
+ fileTypes.types.clear();
+
+ if (NULL != winampExternal)
+ {
+ winampExternal->Release();
+ winampExternal = NULL;
+ }
+}
+
+void Config(HWND parent)
+{
+ mod.Config(parent);
+ fileTypes.SaveConfig();
+ WriteConfig();
+}
+
+void About(HWND parent)
+{
+ wchar_t message[1024] = {0}, text[1024] = {0};
+ WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_WINDOWS_MEDIA_DECODER_OLD,text,1024);
+ wsprintfW(message, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),
+ plugin.description, TEXT(__DATE__));
+ DoAboutMessageBox(parent,text,message);
+}
+
+void GetFileInfo(const in_char *file, wchar_t *title, int *length_in_ms) { mod.GetFileInfo(file, title, length_in_ms); }
+int InfoDialog(const in_char *file, HWND parent) { return mod.InfoBox(file, parent); }
+int IsOurFile(const in_char *fn) { return mod.IsOurFile(fn); }
+int Play(const in_char *fn) {return mod.Play(fn); }
+void Pause() { mod.Pause(); }
+void Resume() { mod.UnPause(); }
+int IsPaused() { return mod.IsPaused(); }
+void Stop() { mod.Stop(); }
+int GetLength() { return mod.GetLength(); }
+int GetOutputTime() { return mod.GetOutputTime(); }
+void SetOutputTime(int time_in_ms) { return mod.SetOutputTime(time_in_ms); }
+void SetVolume(int volume) { mod.SetVolume(volume); }
+void SetPan(int pan) { mod.SetPan(pan); }
+void EQSet(int on, char data[10], int preamp) { mod.EQSet(on, data, preamp); }
+
+In_Module plugin =
+{
+ IN_VER_RET, // defined in IN2.H
+ "nullsoft(in_wm.dll)",
+ 0, // hMainWindow (filled in by winamp)
+ 0, // hDllInstance (filled in by winamp)
+ 0, // this is a double-null limited list. "EXT\0Description\0EXT\0Description\0" etc.
+ 0, // is_seekable
+ 1, // uses output plug-in system
+ Config,
+ About,
+ Init,
+ Quit,
+ GetFileInfo,
+ InfoDialog,
+ IsOurFile,
+ Play,
+ Pause,
+ Resume,
+ IsPaused,
+ Stop,
+
+ GetLength,
+ GetOutputTime,
+ SetOutputTime,
+
+ SetVolume,
+ SetPan,
+
+ 0,0,0,0,0,0,0,0,0, // visualization calls filled in by winamp
+
+ 0,0, // dsp calls filled in by winamp
+
+ EQSet,
+
+ NULL, // setinfo call filled in by winamp
+
+ 0, // out_mod filled in by winamp
+};
+
+extern "C" __declspec( dllexport ) In_Module * winampGetInModule2()
+{
+ return &plugin;
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/output/AudioOut.h b/Src/Plugins/Input/in_wmvdrm/output/AudioOut.h
new file mode 100644
index 00000000..90d36202
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/output/AudioOut.h
@@ -0,0 +1,38 @@
+#ifndef NULLSOFT_AUDIOOUTH
+#define NULLSOFT_AUDIOOUTH
+
+#include <windows.h>
+#include "../../../../Winamp/out.h"
+
+enum InitState
+{
+ StatusNone = 0,
+ StatusInit,
+ StatusQuit
+};
+
+class AudioOut
+{
+public:
+ AudioOut() : dllInstance(0), winampWnd(NULL) {}
+ virtual void Init() = 0;
+ virtual void Quit() = 0;
+ virtual int CanWrite() = 0;
+ virtual int GetWrittenTime() = 0;
+ virtual int IsPlaying() = 0;
+ virtual int Open(int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms) = 0;
+ virtual void Close() = 0;
+ virtual int Write(char *buf, int len) = 0;
+ virtual void Flush(int t) = 0;
+ virtual void SetVolume(int _volume) = 0;
+ virtual int Pause(int new_state) = 0;
+ virtual int GetOutputTime() = 0;
+ virtual void SetPan(int _pan) = 0;
+ virtual void About(HWND p) = 0;
+ virtual void Config(HWND w) = 0;
+
+ HINSTANCE dllInstance;
+ HWND winampWnd;
+};
+
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/output/OutPlugin.cpp b/Src/Plugins/Input/in_wmvdrm/output/OutPlugin.cpp
new file mode 100644
index 00000000..3bf43ea6
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/output/OutPlugin.cpp
@@ -0,0 +1,70 @@
+#include "OutPlugin.h"
+#include "../Winamp/In2.h"
+
+#include "WMDRMModule.h"
+extern In_Module plugin;
+OutPlugin pluginOut;
+
+OutPlugin::OutPlugin()
+{}
+
+void OutPlugin::Init()
+{
+ plugin.outMod->Init();
+}
+void OutPlugin::Quit()
+{
+ plugin.outMod->Quit();
+}
+int OutPlugin::CanWrite()
+{
+ return plugin.outMod->CanWrite();
+}
+int OutPlugin::GetWrittenTime()
+{
+ return plugin.outMod->GetWrittenTime();
+}
+int OutPlugin::IsPlaying()
+{
+ return plugin.outMod->IsPlaying();
+}
+int OutPlugin::Open(int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms)
+{
+ return plugin.outMod->Open(samplerate, numchannels, bitspersamp, bufferlenms, prebufferms);
+}
+void OutPlugin::Close()
+{
+ plugin.outMod->Close();
+}
+int OutPlugin::Write(char *buf, int len)
+{
+ return plugin.outMod->Write(buf, len);
+}
+void OutPlugin::Flush(int t)
+{
+ plugin.outMod->Flush(t);
+}
+void OutPlugin::SetVolume(int _volume)
+{
+ plugin.outMod->SetVolume(_volume);
+}
+int OutPlugin::Pause(int new_state)
+{
+ return plugin.outMod->Pause(new_state);
+}
+int OutPlugin::GetOutputTime()
+{
+ return plugin.outMod->GetOutputTime();
+}
+void OutPlugin::SetPan(int _pan)
+{
+ plugin.outMod->SetPan(_pan);
+}
+void OutPlugin::About(HWND p)
+{
+ plugin.outMod->About(p);
+}
+void OutPlugin::Config(HWND w)
+{
+ plugin.outMod->Config(w);
+}
diff --git a/Src/Plugins/Input/in_wmvdrm/output/OutPlugin.h b/Src/Plugins/Input/in_wmvdrm/output/OutPlugin.h
new file mode 100644
index 00000000..10d0ead7
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/output/OutPlugin.h
@@ -0,0 +1,28 @@
+#ifndef NULLSOFT_OUTPLUGINH
+#define NULLSOFT_OUTPLUGINH
+
+#include "AudioOut.h"
+
+class OutPlugin : public AudioOut
+{
+public:
+ OutPlugin();
+ void Init();
+ void Quit();
+ int CanWrite();
+ int GetWrittenTime();
+ int IsPlaying();
+ int Open(int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms);
+ void Close();
+ int Write(char *buf, int len);
+ void Flush(int t);
+ void SetVolume(int _volume);
+ int Pause(int new_state);
+ int GetOutputTime();
+ void SetPan(int _pan);
+ void About(HWND p);
+ void Config(HWND w);
+};
+
+extern OutPlugin pluginOut;
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/res_wav/resource.h b/Src/Plugins/Input/in_wmvdrm/res_wav/resource.h
new file mode 100644
index 00000000..479d2ebf
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/res_wav/resource.h
@@ -0,0 +1,60 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by out_wave.rc
+//
+#define IDS_NULLSOFT_WAVEOUT_OLD 0
+#define IDS_NOT_ACTIVE 1
+#define IDS_UNKNOWN_MMSYSTEM_ERROR 2
+#define IDC_RESET 3
+#define IDS_UNSUPPORTED_PCM_FORMAT 3
+#define IDS_ANOTHER_PROGRAM_IS_USING_THE_SOUNDCARD 4
+#define IDS_NO_SOUND_DEVICES_FOUND 5
+#define IDS_INTERNAL_DRIVER_ERROR 6
+#define IDS_REINSTALL_SOUNDCARD_DRIVERS 7
+#define IDS_ERROR_CODE_WINDOWS_ERROR_MESSAGE 8
+#define IDS_ERROR_CODE 9
+#define IDS_DATA_FORMAT 10
+#define IDS_BUFFER_STATUS 11
+#define IDS_LATENCY 12
+#define IDS_ABOUT 13
+#define IDS_PREFS_TITLE 15
+#define IDS_ERROR 16
+#define IDS_WAVE_U_MS 17
+#define IDS_ABOUT_STRING 18
+#define IDS_ABOUT_TEXT 18
+#define IDD_CONFIG 300
+#define IDD_WAVE_CONFIG 300
+#define IDC_GAPLESS 1000
+#define IDC_BUF_SIZE 1001
+#define IDC_SPIN1 1002
+#define IDC_PRIMARY 1003
+#define IDC_PREBUF_SIZE 1003
+#define IDC_DEV 1004
+#define IDC_HACK 1005
+#define IDC_EXCLUSIVE 1006
+#define IDC_PREBUFFER 1007
+#define IDC_SPIN2 1008
+#define IDC_VOL_ENABLE 1009
+#define IDC_ALT_VOL 1010
+#define IDC_VOL_RESET 1011
+#define IDC_PREB_TEXT 1012
+#define IDC_STATE 1013
+#define IDC_PREBUFFER_1 1014
+#define IDC_PREBUFFER_2 1015
+#define IDC_PREBUF_DISP_1 1016
+#define IDC_PREBUF_DISP_2 1017
+#define IDC_BLAH 1018
+#define IDC_BUFFER 1020
+#define IDC_BUF_DISP 1021
+#define IDS_NULLSOFT_WAVEOUT 65534
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 301
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1019
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/resource.h b/Src/Plugins/Input/in_wmvdrm/resource.h
new file mode 100644
index 00000000..87c2538f
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/resource.h
@@ -0,0 +1,266 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by fileinfo.rc
+//
+#define IDS_NULLSOFT_WINDOWS_MEDIA_DECODER_OLD 0
+#define IDS_WMDRM_ABOUT 1
+#define IDS_ABOUT 2
+#define IDS_REALTIME 3
+#define IDS_CONNECTING 4
+#define IDS_LOCATING 5
+#define IDS_ACCESS_DENIED 6
+#define IDS_STEREO 7
+#define IDS_QUADROPHONIC 8
+#define IDS_SURROUND 9
+#define IDS_5_1 10
+#define IDS_7_1 11
+#define IDS_EXT 12
+#define IDS_DESCRIPTION 13
+#define IDS_PROTOCOL 14
+#define IDS_EXTENSION 15
+#define IDS_EDIT_FILE_TYPE 16
+#define IDS_WMA_AUDIO_FILE 17
+#define IDS_WMA_VIDEO_FILE 18
+#define IDS_ASF_STREAM 19
+#define IDS_WINDOWS_MEDIA_STREAM 20
+#define IDS_UNKNOWN_ERROR 21
+#define IDS_ATTRIBUTE 22
+#define IDS_VALUE 23
+#define IDS_CLOSE 24
+#define IDS_UNABLE_TO_WRITE_TO_FILE_DOES_FILE_EXIST 25
+#define IDS_UNABLE_TO_WRITE_TO_FILE 26
+#define IDS_UNABLE_TO_WRITE_TO_FILE_MAY_NOT_HAVE_ACCESS 27
+#define IDS_SAVE_FAILED 28
+#define IDS_TRUE 29
+#define IDS_FALSE 30
+#define IDS_UNKNOWN 31
+#define IDS_WINDOWS_MEDIA_XXX 32
+#define IDS_WINDOWS_MEDIA_PROTECTED_CONTENT_NEED_LICENSE 33
+#define IDS_LICENSE_REQUIRED 34
+#define IDS_BEGINNING_UPDATE 35
+#define IDS_UPDATE_FAILED 36
+#define IDS_UPDATE_CANCELLED 37
+#define IDS_IDLE 38
+#define IDS_REQUESTING 39
+#define IDS_RECEIVING_DATA 40
+#define IDS_COMPLETED 41
+#define IDS_INSTALLING 42
+#define IDS_FILE_PROTECTED_CANNOT_PLAY_IN_WINAMP 43
+#define IDS_LICENSE_ACQUIRED 44
+#define IDS_WINDOWS_MEDIA_PROTECTED_CONTENT_UNTRUSTED_SOURCE 45
+#define IDS_UNTRUSTED_LICENSE 46
+#define IDS_WINDOWS_MEDIA_PROTECTED_CONTENT_TAMPERED_LICENSE 47
+#define IDS_TAMPERED_LICENSE 48
+#define IDS_WINDOWS_MEDIA_PROTECTED_CONTENT_SECURITY_UPDATE 49
+#define IDS_SECURITY_UPDATE_REQUIRED 50
+#define IDS_UPDATING 51
+#define IDS_ENCRYPTED_IN_OP_LEVEL_HIGHER_THAN_SUPPORTED 52
+#define IDS_WINDOWS_MEDIA_PLAYBACK_FAILURE 53
+#define IDS_UPDATE_REQUIRED 54
+#define IDS_ACQUIRING_LICENSE 55
+#define IDS_DRM_LICENSE_EXPIRED_VISIT_WINAMP_COM 56
+#define IDS_ERROR 57
+#define IDS_STRING58 58
+#define IDS_BUFFERING 58
+#define IDS_STRING59 59
+#define IDS_UNKNOWN_ERROR_WAV 59
+#define IDS_CODEC 60
+#define IDS_DURATION 61
+#define IDS_BITRATE 62
+#define IDS_FILESIZE 63
+#define IDS_YES 64
+#define IDS_NO 65
+#define IDS_WMVER 66
+#define IDS_SEEKABLE 67
+#define IDS_STRIDABLE 68
+#define IDS_BROADCAST 69
+#define IDS_PROTECTED 70
+#define IDS_TRUSTED 71
+#define IDS_CONTAINS 72
+#define IDS_NAME 73
+#define IDS_STRING74 74
+#define IDS_KBPS 74
+#define IDS_ABOUT_TEXT 75
+#define IDD_CONFIG 105
+#define IDD_CONFIG_STATUS 112
+#define IDD_ADDTYPE 113
+#define IDD_ADVANCED 114
+#define IDS_AUDIO 117
+#define IDS_VIDEO 118
+#define IDS_IMAGE 119
+#define IDS_SCRIPT 120
+#define IDS_NONE 121
+#define IDS_WINDOWS_MEDIA_PLAYLIST 122
+#define IDS_STRING124 123
+#define IDS_ASX_PLAYLIST 123
+#define IDD_INFO 131
+#define IDC_EDIT 1003
+#define IDC_DEV 1004
+#define IDC_REVERT 1006
+#define IDC_BUTTON5 1007
+#define IDC_DELETE 1007
+#define IDC_REMOVETYPE 1007
+#define IDC_ADD 1008
+#define IDC_APPLY 1009
+#define IDC_TEMP 1009
+#define IDC_VOL_ENABLE 1009
+#define IDC_METADATALIST 1010
+#define IDC_ALT_VOL 1010
+#define IDC_VOL_RESET 1011
+#define IDC_EDIT1 1011
+#define IDC_AUDIO_EARLY_PAD 1011
+#define IDC_VIDEO_EARLYPAD 1011
+#define IDC_BUFFER_TIME 1011
+#define IDC_VIDEO_EARLY_PAD 1012
+#define IDC_EDIT2 1012
+#define IDC_AUDIO_EARLYPAD 1012
+#define IDC_STATE 1013
+#define IDC_FILENAME 1014
+#define IDC_STATIC_TITLE 1015
+#define IDC_STATIC_ARTIST 1016
+#define IDC_STATIC_ALBUM 1017
+#define IDC_STATIC_TRACK 1018
+#define IDC_BLAH 1018
+#define IDC_STATIC_YEAR 1019
+#define IDC_STATIC_GENRE 1020
+#define IDC_STATIC_COMMENTS 1021
+#define IDC_EDIT_TITLE 1022
+#define IDC_EDIT_ARTIST 1023
+#define IDC_EDIT_ALBUM 1024
+#define IDC_EDIT_COMMENTS 1025
+#define IDC_EDIT_GENRE 1026
+#define IDC_EDIT_YEAR 1027
+#define IDC_EDIT_TRACK 1028
+#define IDC_BUTTON_DELALL 1028
+#define IDC_LOWMEMORY 1029
+#define IDC_EDIT_GENRE2 1029
+#define IDC_EDIT_PUBLISHER 1029
+#define IDC_STATIC_GENRE2 1030
+#define IDC_AUDIO_OUTOFORDER 1031
+#define IDC_EDIT_GENRE3 1031
+#define IDC_EDIT_ALBUMARTIST 1031
+#define IDC_AUDIO_DEDICATED_THREAD 1032
+#define IDC_VIDEO_OUTOFORDER 1033
+#define IDC_AUDIO_EARLY 1034
+#define IDC_VIDEO_DEDICATED_THREAD 1035
+#define IDC_VIDEO_EARLY 1036
+#define IDC_VIDEO_JITTER 1039
+#define IDC_VIDEO_CACHE_FRAMES 1040
+#define IDC_VIDEO_CATCHUP 1042
+#define IDC_VIDEO_NOTIFYLATE 1043
+#define IDC_CHECK3 1044
+#define IDC_VIDEO_FRAMEDROPOFFSET 1044
+#define IDC_SILENT 1044
+#define IDC_REALTIME 1044
+#define IDC_BROWSER 1045
+#define IDC_EDIT5 1049
+#define IDC_RESET 1052
+#define IDC_GLOBAL_FADES 1053
+#define IDC_CUSTOM_FADE_SPIN 1054
+#define IDC_LOGVOL_SPIN 1055
+#define IDC_REFRESH_SPIN 1056
+#define IDC_FADE 1057
+#define IDC_FADE_SPIN 1058
+#define IDC_CREATE_PRIMARY 1059
+#define IDC_PAUSEFADE2 1060
+#define IDC_DEVICE 1061
+#define IDC_WAITx 1062
+#define IDC_PREBUFFER_2 1063
+#define IDC_KILLSIL 1064
+#define IDC_DB 1065
+#define IDC_DB_DISPLAY 1066
+#define IDC_PREBUFFER_1 1067
+#define IDC_LIST 1068
+#define IDC_PREBUF_DISP_1 1069
+#define IDC_CUSTOM_FADE 1070
+#define IDC_PREBUF_DISP_2 1071
+#define IDC_USE_CUSTOM_FADE 1072
+#define IDC_BUFFER 1073
+#define IDC_BUF_DISP 1074
+#define IDC_STATIC_MS 1075
+#define IDC_BUF_RESET 1076
+#define IDC_VOLUME 1077
+#define IDC_TAB1 1078
+#define IDC_TAB 1079
+#define IDC_DEVICE_INFO 1080
+#define IDC_PDS_FAQ 1081
+#define IDC_FADE_GROUP 1082
+#define IDC_FADE_ENABLED 1083
+#define IDC_REFRESH 1084
+#define IDC_STAT_COPY 1085
+#define IDC_LOGVOL_MIN 1086
+#define IDC_LOGVOL_STATIC 1087
+#define IDC_LOGVOL_STATIC2 1088
+#define IDC_FADEVOL 1089
+#define IDC_PREBUF_AUTO 1090
+#define IDC_STATIC_BLEH 1091
+#define IDC_WAVE_GAPLESS 1092
+#define IDC_WAVE_RESET 1093
+#define IDC_WAVE_BUF_SIZE 1093
+#define IDC_WAVE_SPIN1 1094
+#define IDC_WAVE_PRIMARY 1095
+#define IDC_WAVE_PREBUF_SIZE 1095
+#define IDC_WAVE_DEV 1096
+#define IDC_WAVE_HACK 1097
+#define IDC_WAVE_EXCLUSIVE 1098
+#define IDC_WAVE_PREBUFFER 1099
+#define IDC_WAVE_SPIN2 1100
+#define IDC_WAVE_VOL_ENABLE 1101
+#define IDC_WAVE_ALT_VOL 1102
+#define IDC_WAVE_VOL_RESET 1103
+#define IDC_WAVE_PREB_TEXT 1104
+#define IDC_WAVE_STATE 1105
+#define IDC_WAVE_PREBUFFER_1 1106
+#define IDC_WAVE_PREBUFFER_2 1107
+#define IDC_WAVE_PREBUF_DISP_1 1108
+#define IDC_WAVE_PREBUF_DISP_2 1109
+#define IDC_WAVE_BLAH 1110
+#define IDC_WAVE_BUFFER 1111
+#define IDC_WAVE_BUF_DISP 1112
+#define IDC_VOLMODE 1113
+#define IDC_LOGFADES 1114
+#define IDC_TEXT1 1115
+#define IDC_HW_MIX 1116
+#define IDC_VER 1117
+#define IDC_LIST2 1119
+#define IDC_FLIP 1120
+#define IDC_HTTPMETA 1121
+#define IDC_ADVANCED 1122
+#define IDC_DEFAULTTYPE 1123
+#define IDC_ADDTYPE 1124
+#define IDC_EDITTYPE 1125
+#define IDC_TYPELIST 1126
+#define IDC_UNTRUSTED 1127
+#define IDC_TYPE 1129
+#define IDC_DESCRIPTION 1130
+#define IDC_FILEEXTENSION 1131
+#define IDC_PROTOCOL 1132
+#define IDC_VIDEO_THREAD 1135
+#define IDC_AUDIO_DROP 1138
+#define IDC_CHECK8 1139
+#define IDC_VIDEO_NOTIFY 1139
+#define IDC_AUDIO_THREAD 1140
+#define IDC_EDIT4 1145
+#define IDC_EDIT7 1146
+#define IDC_AUDIO_CACHE_FRAMES 1146
+#define IDC_EDIT6 1148
+#define IDC_VIDEO_DROP_THRESHOLD 1148
+#define IDC_EDIT9 1149
+#define IDC_TYPE_AUDIO 1151
+#define IDC_TYPE_VIDEO 1152
+#define IDC_STATIC_TYPE 1153
+#define IDC_TYPE_AUDIO2 1154
+#define IDC_EXTRA_ASX 1154
+#define IDC_AUDIO_SPEAKER_COUNT 10000
+#define IDS_NULLSOFT_WINDOWS_MEDIA_DECODER 65534
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 127
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1155
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/util.cpp b/Src/Plugins/Input/in_wmvdrm/util.cpp
new file mode 100644
index 00000000..a783cea7
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/util.cpp
@@ -0,0 +1,169 @@
+#include "main.h"
+#include "resource.h"
+#include <stdio.h>
+#include <strsafe.h>
+
+//static const GUID INVALID_GUID =
+//{ 0x00000000, 0x0000, 0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
+void WaitForEvent(HANDLE hEvent, DWORD msMaxWaitTime)
+{
+ // DWORD i;
+ MSG msg;
+ const unsigned long eachWait = 10;
+ unsigned long totalWait = 0;
+
+ while (WaitForSingleObject(hEvent, eachWait) == WAIT_TIMEOUT)
+ {
+ while (PeekMessage(&msg, (HWND) NULL, 0, 0, PM_REMOVE))
+ {
+ //TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ totalWait += eachWait;
+ if (totalWait >= msMaxWaitTime)
+ break;
+
+ }
+}
+
+char *HRErrorCode(HRESULT hr)
+{
+#define HR_ERROR_CODE(x) case x: return #x
+ switch (hr)
+ {
+ HR_ERROR_CODE(E_OUTOFMEMORY);
+ HR_ERROR_CODE(E_UNEXPECTED);
+ HR_ERROR_CODE(S_OK);
+ HR_ERROR_CODE(S_FALSE);
+ HR_ERROR_CODE(E_NOINTERFACE);
+ HR_ERROR_CODE(NS_E_PROTECTED_CONTENT);
+ HR_ERROR_CODE(E_INVALIDARG);
+ HR_ERROR_CODE(NS_E_DRM_NO_RIGHTS);
+ HR_ERROR_CODE(NS_E_DRM_LICENSE_NOTACQUIRED);
+ HR_ERROR_CODE(NS_E_DRM_ACQUIRING_LICENSE);
+ HR_ERROR_CODE(NS_S_DRM_ACQUIRE_CANCELLED);
+ HR_ERROR_CODE(NS_E_LICENSE_OUTOFDATE);
+ HR_ERROR_CODE(NS_E_LICENSE_INCORRECT_RIGHTS);
+ HR_ERROR_CODE(NS_E_DRM_REOPEN_CONTENT);
+ HR_ERROR_CODE(NS_E_DRM_LICENSE_APPSECLOW);
+ HR_ERROR_CODE(E_ABORT);
+ HR_ERROR_CODE(NS_E_INVALID_REQUEST);
+ HR_ERROR_CODE(NS_S_DRM_LICENSE_ACQUIRED);
+ HR_ERROR_CODE(NS_S_DRM_MONITOR_CANCELLED);
+ HR_ERROR_CODE(NS_E_FILE_NOT_FOUND);
+ HR_ERROR_CODE(NS_E_FILE_OPEN_FAILED);
+ HR_ERROR_CODE(NS_E_SERVER_NOT_FOUND);
+ HR_ERROR_CODE(NS_E_UNRECOGNIZED_STREAM_TYPE);
+ HR_ERROR_CODE(NS_E_NO_STREAM);
+ HR_ERROR_CODE(E_ACCESSDENIED);
+ HR_ERROR_CODE(NS_S_DRM_BURNABLE_TRACK);
+ HR_ERROR_CODE(NS_S_DRM_BURNABLE_TRACK_WITH_PLAYLIST_RESTRICTION);
+ HR_ERROR_CODE(NS_E_DRM_TRACK_EXCEEDED_PLAYLIST_RESTICTION);
+ HR_ERROR_CODE(NS_E_DRM_TRACK_EXCEEDED_TRACKBURN_RESTRICTION);
+ }
+ static char temp[50];
+ StringCchPrintfA(temp, 50, WASABI_API_LNGSTRING(IDS_UNKNOWN_ERROR), hr);
+ return temp;
+
+}
+
+void GuidString(GUID guid, wchar_t *target, size_t len)
+{
+ StringCchPrintf( target, len, L"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
+ (int)guid.Data1, (int)guid.Data2, (int)guid.Data3,
+ (int)guid.Data4[0], (int)guid.Data4[1],
+ (int)guid.Data4[2], (int)guid.Data4[3],
+ (int)guid.Data4[4], (int)guid.Data4[5],
+ (int)guid.Data4[6], (int)guid.Data4[7] );
+}
+
+ const wchar_t *UserTextDescription(unsigned char *binary, size_t size)
+{
+ WM_USER_TEXT *userText = (WM_USER_TEXT *)binary;
+
+ return userText->pwszDescription;
+}
+
+const wchar_t *UserTextString(unsigned char *binary, size_t size)
+{
+ WM_USER_TEXT *userText = (WM_USER_TEXT *)binary;
+
+ return userText->pwszText;
+}
+
+void BinaryString(unsigned char *binary, size_t size, wchar_t *final, size_t len)
+{
+ wchar_t * const start = new wchar_t[2 + size*2 + 1]; // 0x + 2 hex per byte + null terminator
+ wchar_t *target = start;
+ *target++='0';
+ *target++='x';
+
+ size_t i;
+ for (i = 0;i!=size;i++)
+ {
+ wchar_t temp[3] = {0};
+ _itow(binary[i], temp, 16);
+ if (!temp[0])
+ {
+ *target++ = '0';
+ *target++ = '0';
+ }
+ else if (!temp[1])
+ {
+ *target++ = '0';
+ *target++ = temp[0];
+ }
+ else
+ {
+ *target++ = temp[0];
+ *target++ = temp[1];
+ }
+ }
+ *target = 0;
+ lstrcpyn(final, start, len);
+ delete [] start;
+}
+
+GUID StringGUID(const wchar_t *source)
+{
+ if (source == NULL) return INVALID_GUID;
+
+ GUID guid = GUID_NULL;
+ int Data1, Data2, Data3;
+ int Data4[8] = {0};
+
+ // {1B3CA60C-DA98-4826-B4A9-D79748A5FD73}
+ int n = swscanf(source, L" { %08x - %04x - %04x - %02x%02x - %02x%02x%02x%02x%02x%02x } ",
+ &Data1, &Data2, &Data3, Data4 + 0, Data4 + 1,
+ Data4 + 2, Data4 + 3, Data4 + 4, Data4 + 5, Data4 + 6, Data4 + 7 );
+
+ if (n != 11) return INVALID_GUID;
+
+ // Cross assign all the values
+ guid.Data1 = Data1;
+ guid.Data2 = Data2;
+ guid.Data3 = Data3;
+ guid.Data4[0] = Data4[0];
+ guid.Data4[1] = Data4[1];
+ guid.Data4[2] = Data4[2];
+ guid.Data4[3] = Data4[3];
+ guid.Data4[4] = Data4[4];
+ guid.Data4[5] = Data4[5];
+ guid.Data4[6] = Data4[6];
+ guid.Data4[7] = Data4[7];
+
+ return guid;
+}
+
+int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message)
+{
+ MSGBOXPARAMS msgbx = {sizeof(MSGBOXPARAMS),0};
+ msgbx.lpszText = message;
+ msgbx.lpszCaption = title;
+ msgbx.lpszIcon = MAKEINTRESOURCE(102);
+ msgbx.hInstance = GetModuleHandle(0);
+ msgbx.dwStyle = MB_USERICON;
+ msgbx.hwndOwner = parent;
+ return MessageBoxIndirect(&msgbx);
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/util.h b/Src/Plugins/Input/in_wmvdrm/util.h
new file mode 100644
index 00000000..8c8890da
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/util.h
@@ -0,0 +1,20 @@
+#ifndef NULLSOFT_UTILH
+#define NULLSOFT_UTILH
+
+#include <windows.h>
+
+void WaitForEvent(HANDLE hEvent, DWORD msMaxWaitTime);
+char *HRErrorCode(HRESULT hr);
+
+void GuidString(GUID guid, wchar_t *target, size_t len);
+
+GUID StringGUID(const wchar_t *source);
+
+void BinaryString(unsigned char *binary, size_t size, wchar_t *final, size_t len);
+
+const wchar_t *UserTextDescription(unsigned char *binary, size_t size);
+const wchar_t *UserTextString(unsigned char *binary, size_t size);
+
+int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message);
+
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/version.rc2 b/Src/Plugins/Input/in_wmvdrm/version.rc2
new file mode 100644
index 00000000..ade63c0d
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../../../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 3,9,5,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,9,5,0"
+ VALUE "InternalName", "Nullsoft Windows Media Decoder"
+ VALUE "LegalCopyright", "Copyright © 2005-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "in_wm.dll"
+ VALUE "ProductName", "Winamp"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/Src/Plugins/Input/in_wmvdrm/vid_ddraw.cpp b/Src/Plugins/Input/in_wmvdrm/vid_ddraw.cpp
new file mode 100644
index 00000000..7aed89de
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/vid_ddraw.cpp
@@ -0,0 +1,771 @@
+#include "main.h"
+#include "vid_ddraw.h"
+#include "directdraw.h"
+
+ void *frame;
+ int width, height, flip;
+ int needchange;
+ unsigned int type;
+ LPDIRECTDRAW lpDD;
+ LPDIRECTDRAWSURFACE lpddsOverlay, lpddsPrimary;
+ DDCAPS capsDrv;
+ unsigned int uDestSizeAlign, uSrcSizeAlign;
+ DWORD dwUpdateFlags;
+ RECT rs, rd;
+ RECT lastresizerect;
+ bool initing;
+
+ LPDIRECTDRAWCLIPPER lpddsClipper;
+ DDPIXELFORMAT m_ddpf;
+ int m_depth;
+ RGBQUAD *m_palette;
+
+ RECT winRect;
+ RECT m_monRect;
+
+void getViewport(RECT *r, HWND wnd, int full, RECT *sr);
+#define INIT_DIRECTDRAW_STRUCT(x) (ZeroMemory(&x, sizeof(x)), x.dwSize=sizeof(x))
+DDrawVideoOutput::DDrawVideoOutput() : frame(0), width(0), height(0),
+ flip(0), type(0), uDestSizeAlign(0),
+ uSrcSizeAlign(0), dwUpdateFlags(0),
+ m_depth(0)
+{
+ lpDD = NULL;
+ lpddsOverlay = NULL;
+ lastresizerect.bottom = 0;
+ lastresizerect.top = 0;
+ lastresizerect.left = 0;
+ lastresizerect.right = 0;
+
+ lpddsPrimary = NULL;
+ lpddsClipper = NULL;
+
+ initing = false;
+ needchange = 0;
+ m_palette = NULL;
+ memset(&winRect, 0, sizeof(winRect));
+}
+
+void DDrawVideoOutput::close()
+{
+ // LPDIRECTDRAWSURFACE o=lpddsOverlay;
+ lpddsOverlay = NULL;
+ // if(o) o->Release();
+ //if(lpddsPrimary) lpddsPrimary->Release();
+ // if(lpddsClipper) lpddsClipper->Release();
+ if (lpDD) lpDD->Release(); lpDD = 0;// BU added NULL check in response to talkback
+// removeFullScreen();
+}
+
+DDrawVideoOutput::~DDrawVideoOutput()
+{
+ DDrawVideoOutput::close();
+}
+
+void DDrawVideoOutput::drawSubtitle(SubsItem *item)
+{
+}
+
+int DDrawVideoOutput::create(HWND parent, VideoAspectAdjuster *_adjuster, int w, int h, unsigned int ptype, int flipit, double aspectratio)
+{
+ this->parent = parent;
+ type = ptype;
+ width = w;
+ height = h;
+ flip = flipit;
+ adjuster = _adjuster;
+
+ initing = true;
+ HWND hwnd = this->parent;
+
+ if (lpDD) lpDD->Release();
+ lpDD = NULL;
+
+ update_monitor_coords();
+
+ if (!foundGUID) DDrawCreate(NULL, &lpDD, NULL);
+ else DDrawCreate(&m_devguid, &lpDD, NULL);
+
+
+ if (!lpDD)
+ {
+ initing = false;
+ return 0;
+ }
+
+ lpDD->SetCooperativeLevel(hwnd, DDSCL_NOWINDOWCHANGES | DDSCL_NORMAL);
+
+ DDSURFACEDESC ddsd;
+ INIT_DIRECTDRAW_STRUCT(ddsd);
+ ddsd.dwFlags = DDSD_CAPS;
+ ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+ HRESULT ddrval = lpDD->CreateSurface(&ddsd, &lpddsPrimary, NULL );
+
+ if (FAILED(ddrval) || !lpddsPrimary)
+ {
+ initing=false;
+ return 0;
+ }
+
+ HRESULT v = -1;
+ DDSURFACEDESC DDsd = {sizeof(DDsd), };
+ lpddsPrimary->GetSurfaceDesc(&ddsd);
+ DDsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; //create the surface at screen depth
+ DDsd.dwWidth = w;
+ DDsd.dwHeight = h;
+ DDsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY;
+ if (config_video.ddraw()) v = lpDD->CreateSurface(&DDsd, &lpddsOverlay, NULL);
+ if (!config_video.ddraw() || FAILED(v))
+ {
+ // fall back to system memory if video mem doesn't work
+ DDsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY;
+ v = lpDD->CreateSurface(&DDsd, &lpddsOverlay, NULL);
+ }
+ if (FAILED(v))
+ {
+ // this video card sucks then :)
+ lpddsOverlay = NULL;
+ initing = false;
+ return 0;
+ }
+
+ // get the depth
+ m_depth = 8;
+ INIT_DIRECTDRAW_STRUCT(m_ddpf);
+ if (lpddsOverlay->GetPixelFormat(&m_ddpf) >= 0)
+ {
+ m_depth = m_ddpf.dwRGBBitCount;
+ if (m_depth == 16 && m_ddpf.dwGBitMask == 0x03e0) m_depth = 15;
+ }
+
+ if (lpDD->CreateClipper(0, &lpddsClipper, NULL) != DD_OK)
+ {
+ lpddsOverlay = NULL;
+ initing = false;
+ return 0;
+ }
+
+ lpddsClipper->SetHWnd(0, hwnd);
+ lpddsPrimary->SetClipper(lpddsClipper);
+ initing = false;
+
+ //get current monitor
+ getViewport(&m_monRect, hwnd, 1, NULL);
+
+ return 1;
+}
+
+int DDrawVideoOutput::onPaint(HWND hwnd)
+{
+ return 0;
+}
+
+void DDrawVideoOutput::displayFrame(const char * /*buf*/, int size, int time)
+{
+ DDSURFACEDESC dd = {sizeof(dd), };
+ if (config_video.vsync()) lpDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 0);
+ if (lpddsOverlay->Lock(NULL, &dd, DDLOCK_WAIT, NULL) != DD_OK)
+ {
+ needchange = 1;
+ return ;
+ }
+ if (type == VIDEO_MAKETYPE('Y', 'V', '1', '2'))
+ {
+ const YV12_PLANES *planes = (YV12_PLANES *)frame;
+ // convert yv12 to rgb
+ int bytes = m_depth >> 3;
+ if (m_depth == 15) bytes = 2;
+ int i, j, y00, y01, y10, y11, u, v;
+ unsigned char *pY = (unsigned char *)planes->y.baseAddr;
+ unsigned char *pU = (unsigned char *)planes->u.baseAddr;
+ unsigned char *pV = (unsigned char *)planes->v.baseAddr;
+ unsigned char *pOut = (unsigned char*)dd.lpSurface;
+ const int rvScale = (int) (2.017 * 65536.0); //91881;
+ const int gvScale = - (int) (0.392 * 65536.0); // -22553;
+ const int guScale = - (int) (0.813 * 65536.0); // -46801;
+ const int buScale = (int) (1.596 * 65536.0); //116129;
+ const int yScale = (int)( 1.164 * 65536.0 ); //(1.164*65536.0);
+ int addOut = dd.lPitch * 2 - width * bytes;
+ int yrb = planes->y.rowBytes;
+ int addL = dd.lPitch;
+
+ /* LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. */
+#define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16)))
+
+ if (flip)
+ {
+ pOut += (dd.lPitch) * (height - 1);
+ addOut = -dd.lPitch * 2 - width * bytes;
+ addL = -addL;
+ }
+
+ for (j = 0; j <= height - 2; j += 2)
+ {
+ for (i = 0; i <= width - 2; i += 2)
+ {
+ y00 = *pY - 16;
+ y01 = *(pY + 1) - 16;
+ y10 = *(pY + yrb) - 16;
+ y11 = *(pY + yrb + 1) - 16;
+ u = (*pU++) - 128;
+ v = (*pV++) - 128;
+
+ {
+ int r, g, b;
+
+ g = guScale * v + gvScale * u;
+ r = buScale * v;
+ b = rvScale * u;
+
+ y00 *= yScale; y01 *= yScale;
+ y10 *= yScale; y11 *= yScale;
+
+ switch (m_depth)
+ {
+ case 15:
+ {
+ unsigned short *rgb = (unsigned short *)pOut;
+ rgb[0] = ((LIMIT(r + y00) >> 3) << 10) | ((LIMIT(g + y00) >> 3) << 5) | (LIMIT(b + y00) >> 3);
+ rgb[1] = ((LIMIT(r + y01) >> 3) << 10) | ((LIMIT(g + y01) >> 3) << 5) | (LIMIT(b + y01) >> 3);
+ rgb += addL / 2;
+ rgb[0] = ((LIMIT(r + y10) >> 3) << 10) | ((LIMIT(g + y10) >> 3) << 5) | (LIMIT(b + y10) >> 3);
+ rgb[1] = ((LIMIT(r + y11) >> 3) << 10) | ((LIMIT(g + y11) >> 3) << 5) | (LIMIT(b + y11) >> 3);
+ }
+ break;
+ case 16:
+ {
+ unsigned short *rgb = (unsigned short *)pOut;
+ rgb[0] = ((LIMIT(r + y00) >> 3) << 11) | ((LIMIT(g + y00) >> 2) << 5) | (LIMIT(b + y00) >> 3);
+ rgb[1] = ((LIMIT(r + y01) >> 3) << 11) | ((LIMIT(g + y01) >> 2) << 5) | (LIMIT(b + y01) >> 3);
+ rgb += addL / 2;
+ rgb[0] = ((LIMIT(r + y10) >> 3) << 11) | ((LIMIT(g + y10) >> 2) << 5) | (LIMIT(b + y10) >> 3);
+ rgb[1] = ((LIMIT(r + y11) >> 3) << 11) | ((LIMIT(g + y11) >> 2) << 5) | (LIMIT(b + y11) >> 3);
+ }
+ break;
+ case 24:
+ {
+ unsigned char *rgb = pOut;
+ /* Write out top two pixels */
+ rgb[0] = LIMIT(b + y00); rgb[1] = LIMIT(g + y00); rgb[2] = LIMIT(r + y00);
+ rgb[3] = LIMIT(b + y01); rgb[4] = LIMIT(g + y01); rgb[5] = LIMIT(r + y01);
+
+ /* Skip down to next line to write out bottom two pixels */
+ rgb += addL;
+ rgb[0] = LIMIT(b + y10); rgb[1] = LIMIT(g + y10); rgb[2] = LIMIT(r + y10);
+ rgb[3] = LIMIT(b + y11); rgb[4] = LIMIT(g + y11); rgb[5] = LIMIT(r + y11);
+ }
+ break;
+ case 32:
+ {
+ unsigned char *rgb = pOut;
+ /* Write out top two pixels */
+ rgb[0] = LIMIT(b + y00); rgb[1] = LIMIT(g + y00); rgb[2] = LIMIT(r + y00);
+ rgb[4] = LIMIT(b + y01); rgb[5] = LIMIT(g + y01); rgb[6] = LIMIT(r + y01);
+
+ /* Skip down to next line to write out bottom two pixels */
+ rgb += addL;
+ rgb[0] = LIMIT(b + y10); rgb[1] = LIMIT(g + y10); rgb[2] = LIMIT(r + y10);
+ rgb[4] = LIMIT(b + y11); rgb[5] = LIMIT(g + y11); rgb[6] = LIMIT(r + y11);
+ }
+ break;
+ }
+ }
+
+ pY += 2;
+ pOut += 2 * bytes;
+ }
+ pY += yrb + yrb - width;
+ pU += planes->u.rowBytes - width / 2;
+ pV += planes->v.rowBytes - width / 2;
+ pOut += addOut;
+ }
+ }
+ else if (type == VIDEO_MAKETYPE('R', 'G', '3', '2'))
+ {
+ //FUCKO: do we need to support 8bits depth?
+ switch (m_depth)
+ {
+ case 15:
+ { // convert RGB32 -> RGB16 (555)
+ const char *a = (const char *)frame;
+ char *b = (char *)dd.lpSurface;
+ int l = width * 4, l2 = dd.lPitch;
+ int ladj = l;
+ if (flip) { a += l * (height - 1); ladj = -ladj; }
+ for (int i = 0;i < height;i++)
+ {
+ short *dest = (short *)b;
+ int *src = (int *)a;
+ for (int j = 0;j < width;j++)
+ {
+ int c = *(src++);
+ int r = c >> 16;
+ int g = (c >> 8) & 0xff;
+ int b = (c) & 0xff;
+ *(dest++) = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
+ }
+ a += ladj; b += l2;
+ }
+ }
+ break;
+ case 16:
+ { // convert RGB32 -> RGB16
+ //FUCKO: this assumes 565
+ const char *a = (const char *)frame;
+ char *b = (char *)dd.lpSurface;
+ int l = width * 4, l2 = dd.lPitch;
+ int ladj = l;
+ if (flip) { a += l * (height - 1); ladj = -ladj; }
+ for (int i = 0;i < height;i++)
+ {
+ short *dest = (short *)b;
+ int *src = (int *)a;
+ for (int j = 0;j < width;j++)
+ {
+ //FUCKO: optimize here
+ int c = *(src++);
+ int r = c >> 16;
+ int g = (c >> 8) & 0xff;
+ int b = (c) & 0xff;
+ *(dest++) = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
+ }
+ a += ladj; b += l2;
+ }
+ }
+ break;
+ case 24:
+ { // convert RGB32 -> RGB24
+ const char *a = (const char *)frame;
+ char *b = (char *)dd.lpSurface;
+ int l = width * 4, l2 = dd.lPitch;
+ int ladj = l;
+ if (flip) { a += l * (height - 1); ladj = -ladj; }
+ for (int i = 0;i < height;i++)
+ {
+ char *dest = (char *)b;
+ int *src = (int *)a;
+ for (int j = 0;j < width;j++)
+ {
+ //FUCKO: optimize here
+ int c = *(src++);
+ int r = c >> 16;
+ int g = (c >> 8) & 0xff;
+ int b = (c) & 0xff;
+ *dest++ = b;
+ *dest++ = g;
+ *dest++ = r;
+ }
+ a += ladj; b += l2;
+ }
+ }
+ break;
+ case 32:
+ { // straight RGB32 copy
+ const char *a = (const char *)frame;
+ char *b = (char *)dd.lpSurface;
+ int l = width * 4, l2 = dd.lPitch;
+ int ladj = l;
+ if (flip) { a += l * (height - 1); ladj = -ladj; }
+ for (int i = 0;i < height;i++)
+ {
+ memcpy(b, a, l);
+ a += ladj; b += l2;
+ }
+ }
+ break;
+ }
+ }
+ else if (type == VIDEO_MAKETYPE('Y', 'U', 'Y', '2') || type == VIDEO_MAKETYPE('U', 'Y', 'V', 'Y'))
+ {
+ //const char *a = (const char *)frame;
+ //char *b = (char *)dd.lpSurface;
+ int /*l = width * 2, */l2 = dd.lPitch;
+ if (flip)
+ {
+ //b += (height - 1) * l2;
+ l2 = -l2;
+ }
+ switch (m_depth)
+ {
+ case 15:
+ {
+ // yuy2->rgb16 (555) conversion
+ unsigned char *src = (unsigned char *)frame;
+ unsigned short *dst = (unsigned short *)dd.lpSurface;
+ int line, col; //, linewidth;
+ int y, yy;
+ int u, v;
+ int vr, ug, vg, ub;
+ unsigned char *py, *pu, *pv;
+
+ //linewidth = width - (width >> 1);
+ py = src;
+ pu = src + 1;
+ pv = src + 3;
+
+ int pitchadd = dd.lPitch / 2 - width;
+
+ for (line = 0; line < height; line++)
+ {
+ for (col = 0; col < width; col++)
+ {
+#undef LIMIT
+ #define LIMIT(x) ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) )
+
+ y = *py;
+ yy = y << 8;
+ u = *pu - 128;
+ ug = 88 * u;
+ ub = 454 * u;
+ v = *pv - 128;
+ vg = 183 * v;
+ vr = 359 * v;
+
+ unsigned char b = LIMIT(yy + ub );
+ unsigned char g = LIMIT(yy - ug - vg);
+ unsigned char r = LIMIT(yy + vr);
+ *(dst++) = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
+
+ py += 2;
+ if ( (col & 1) == 1)
+ {
+ pu += 4; // skip yvy every second y
+ pv += 4; // skip yuy every second y
+ }
+ } // ..for col
+ dst += pitchadd;
+ } /* ..for line */
+ }
+ break;
+ case 16:
+ {
+ // yuy2->rgb16 conversion
+ //FUCKO: only supports 565
+ unsigned char *src = (unsigned char *)frame;
+ unsigned short *dst = (unsigned short *)dd.lpSurface;
+ int line, col; //, linewidth;
+ int y, yy;
+ int u, v;
+ int vr, ug, vg, ub;
+ unsigned char *py, *pu, *pv;
+
+ //linewidth = width - (width >> 1);
+ py = src;
+ pu = src + 1;
+ pv = src + 3;
+
+ int pitchadd = dd.lPitch / 2 - width;
+
+ for (line = 0; line < height; line++)
+ {
+ for (col = 0; col < width; col++)
+ {
+#undef LIMIT
+ #define LIMIT(x) ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) )
+
+ y = *py;
+ yy = y << 8;
+ u = *pu - 128;
+ ug = 88 * u;
+ ub = 454 * u;
+ v = *pv - 128;
+ vg = 183 * v;
+ vr = 359 * v;
+
+ unsigned char b = LIMIT(yy + ub );
+ unsigned char g = LIMIT(yy - ug - vg);
+ unsigned char r = LIMIT(yy + vr);
+ *(dst++) = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
+
+ py += 2;
+ if ( (col & 1) )
+ {
+ pu += 4; // skip yvy every second y
+ pv += 4; // skip yuy every second y
+ }
+ } // ..for col
+ dst += pitchadd;
+ } /* ..for line */
+ }
+ break;
+ case 24:
+ {
+ // yuy2->rgb24 conversion
+ unsigned char *src = (unsigned char *)frame;
+ unsigned char *dst = (unsigned char *)dd.lpSurface;
+ int line, col; //, linewidth;
+ int y, yy;
+ int u, v;
+ int vr, ug, vg, ub;
+ unsigned char *py, *pu, *pv;
+
+ //linewidth = width - (width >> 1);
+ py = src;
+ pu = src + 1;
+ pv = src + 3;
+
+ int pitchadd = dd.lPitch - (width * 3);
+
+ for (line = 0; line < height; line++)
+ {
+ for (col = 0; col < width; col++)
+ {
+#undef LIMIT
+ #define LIMIT(x) ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) )
+
+ y = *py;
+ yy = y << 8;
+ u = *pu - 128;
+ ug = 88 * u;
+ ub = 454 * u;
+ v = *pv - 128;
+ vg = 183 * v;
+ vr = 359 * v;
+
+ *(dst++) = LIMIT(yy + ub );
+ *(dst++) = LIMIT(yy - ug - vg);
+ *(dst++) = LIMIT(yy + vr);
+
+ py += 2;
+ if ( (col & 1) == 1)
+ {
+ pu += 4; // skip yvy every second y
+ pv += 4; // skip yuy every second y
+ }
+ } // ..for col
+ dst += pitchadd;
+ } /* ..for line */
+ }
+ break;
+ case 32:
+ {
+ // yuy2->rgb32 conversion
+ unsigned char *src = (unsigned char *)frame;
+ unsigned char *dst = (unsigned char *)dd.lpSurface;
+ int line, col; //, linewidth;
+ int y, yy;
+ int u, v;
+ int vr, ug, vg, ub;
+ unsigned char *py, *pu, *pv;
+
+ //linewidth = width - (width >> 1);
+ py = src;
+ pu = src + 1;
+ pv = src + 3;
+
+ int pitchadd = dd.lPitch - (width * 4);
+
+ for (line = 0; line < height; line++)
+ {
+ for (col = 0; col < width; col++)
+ {
+#undef LIMIT
+ #define LIMIT(x) ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) )
+
+ y = *py;
+ yy = y << 8;
+ u = *pu - 128;
+ ug = 88 * u;
+ ub = 454 * u;
+ v = *pv - 128;
+ vg = 183 * v;
+ vr = 359 * v;
+
+ *dst++ = LIMIT(yy + ub ); // b
+ *dst++ = LIMIT(yy - ug - vg); // g
+ *dst++ = LIMIT(yy + vr); // r
+ dst++;
+
+ py += 2;
+ if ( (col & 1) == 1)
+ {
+ pu += 4; // skip yvy every second y
+ pv += 4; // skip yuy every second y
+ }
+ } // ..for col
+ dst += pitchadd;
+ } /* ..for line */
+ }
+ break;
+ }
+ }
+ else if (type == VIDEO_MAKETYPE('R', 'G', '2', '4'))
+ {
+ //FUCKO: only ->RGB32 conversion supported
+ switch (m_depth)
+ {
+ case 32:
+ {
+ const char *a = (const char *)frame;
+ char *b = (char *)dd.lpSurface;
+ int l = width, l2 = dd.lPitch;
+ int ladj = l * 3;
+ if (!flip) ladj = 0;
+ if (flip) { a += (l * 3) * (height - 1); ladj = -(ladj + l * 3); }
+ l2 -= l * 4;
+ for (int i = 0;i < height;i++)
+ {
+ //memcpy(b,a,l);
+ for (int j = 0;j < l;j++)
+ {
+ b[0] = a[0];
+ b[1] = a[1];
+ b[2] = a[2];
+ b += 4; a += 3;
+ }
+ a += ladj; b += l2;
+ }
+ }
+ break;
+ }
+ }
+ else if (type == VIDEO_MAKETYPE('R', 'G', 'B', '8') && m_palette)
+ {
+ unsigned char *d = (unsigned char *)dd.lpSurface;
+ int pitch = dd.lPitch;
+ unsigned char *src = (unsigned char *)frame;
+ int newwidth = (width + 3) & 0xfffc;
+ src += newwidth * height - 1;
+ for (int j = 0;j < height;j++)
+ {
+ switch (m_depth)
+ {
+ case 15:
+ case 16:
+ {
+ unsigned short *dest = (unsigned short *)d;
+ for (int i = 0;i < newwidth;i++)
+ {
+ unsigned char c = src[ -newwidth + 1 + i];
+ RGBQUAD *rgb = &m_palette[c];
+ switch (m_depth)
+ {
+ case 15: *(dest++) = ((rgb->rgbRed >> 3) << 10) | ((rgb->rgbGreen >> 3) << 5) | (rgb->rgbBlue >> 3); break;
+ case 16: *(dest++) = ((rgb->rgbRed >> 3) << 11) | ((rgb->rgbGreen >> 2) << 5) | (rgb->rgbBlue >> 3); break;
+ }
+ }
+ }
+ break;
+ case 24:
+ case 32:
+ {
+ unsigned char *dest = d;
+ for (int i = 0;i < newwidth;i++)
+ {
+ unsigned char c = src[ -newwidth + 1 + i];
+ RGBQUAD *rgb = &m_palette[c];
+ *dest++ = rgb->rgbBlue;
+ *dest++ = rgb->rgbGreen;
+ *dest++ = rgb->rgbRed;
+ if (m_depth == 32) dest++;
+ }
+ }
+ break;
+ }
+ d += pitch;
+ src -= newwidth;
+ }
+ }
+
+ lpddsOverlay->Unlock(&dd);
+
+
+ RECT r;
+ HWND hwnd = this->parent;
+ if (!IsWindow(hwnd)) return ;
+
+ // if(GetParent(hwnd)) hwnd=GetParent(hwnd);
+
+ GetClientRect(hwnd, &r);
+ RECT fullr = r;
+ adjuster->adjustAspect(r);
+ if (r.left != lastresizerect.left || r.right != lastresizerect.right || r.top != lastresizerect.top ||
+ r.bottom != lastresizerect.bottom)
+ {
+ if (r.left != 0)
+ {
+ RECT tmp = {0, 0, r.left, fullr.bottom};
+ InvalidateRect(hwnd, &tmp, TRUE);
+ }
+
+ if (r.right != fullr.right)
+ {
+ RECT tmp = {r.right, 0, fullr.right, fullr.bottom};
+ InvalidateRect(hwnd, &tmp, TRUE);
+ }
+ if (r.top != 0)
+ {
+ RECT tmp = {r.left, 0, r.right, r.top};
+ InvalidateRect(hwnd, &tmp, TRUE);
+ }
+ if (r.bottom != fullr.bottom)
+ {
+ RECT tmp = {r.left, r.bottom, r.right, fullr.bottom};
+ InvalidateRect(hwnd, &tmp, TRUE);
+ }
+
+ lastresizerect = r;
+ }
+
+ ClientToScreen(hwnd, (LPPOINT)&r);
+ ClientToScreen(hwnd, ((LPPOINT)&r) + 1);
+
+ // transform coords from windows desktop coords (where 0,0==upper-left corner of box encompassing all monitors)
+ // to the coords for the monitor we're displaying on:
+ r.left -= m_mon_x;
+ r.right -= m_mon_x;
+ r.top -= m_mon_y;
+ r.bottom -= m_mon_y;
+
+ HDC hdc = NULL;
+ HDC inhdc = NULL;
+
+ RECT *pSrcRect = NULL;
+
+ {
+ if (!config_video.ddraw() || lpddsPrimary->Blt(&r, lpddsOverlay, pSrcRect, DDBLT_WAIT, 0) != DD_OK)
+ {
+ // as a last resort, BitBlt().
+ if (lpddsOverlay->GetDC(&inhdc) != DD_OK)
+ {
+ needchange = 1;
+ return ;
+ }
+ if (lpddsPrimary->GetDC(&hdc) != DD_OK)
+ {
+ lpddsOverlay->ReleaseDC(inhdc); inhdc = NULL;
+ needchange = 1;
+ return ;
+ }
+
+ int src_w = width;
+ int src_h = pSrcRect ? (pSrcRect->bottom - pSrcRect->top) : height;
+ if (r.right - r.left == src_w && r.bottom - r.top == src_h)
+ BitBlt(hdc, r.left, r.top, r.right - r.left, r.bottom - r.top, inhdc, 0, 0, SRCCOPY);
+ else
+ StretchBlt(hdc, r.left, r.top, r.right - r.left, r.bottom - r.top, inhdc, 0, 0, src_w, src_h, SRCCOPY);
+ }
+ }
+
+ if (hdc) { lpddsPrimary->ReleaseDC(hdc); hdc = NULL; }
+ if (inhdc) { lpddsOverlay->ReleaseDC(inhdc); inhdc = NULL; }
+}
+
+void DDrawVideoOutput::timerCallback()
+{
+ //check if the video has been dragged to another monitor
+ RECT curRect;
+
+ getViewport(&curRect, parent, 1, NULL);
+ if (memcmp(&curRect, &m_monRect, sizeof(RECT)))
+ needchange = 1;
+}
+
+void DDrawVideoOutput::resetSubtitle()
+{
+}
+
+void DDrawVideoOutput::Refresh()
+{
+ // do nothing, we'll refresh on the next frame
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/vid_ddraw.h b/Src/Plugins/Input/in_wmvdrm/vid_ddraw.h
new file mode 100644
index 00000000..184d5817
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/vid_ddraw.h
@@ -0,0 +1,50 @@
+#ifndef NULLSOFT_IN_WMVDRM_VID_DDRAW_H
+#define NULLSOFT_IN_WMVDRM_VID_DDRAW_H
+
+#include <ddraw.h>
+#include "VideoOutputChildDDraw.h"
+
+class SubsItem;
+
+class DDrawVideoOutput : public VideoOutputChildDDraw
+{
+public:
+ DDrawVideoOutput();
+ virtual ~DDrawVideoOutput();
+ int create(HWND parent, VideoAspectAdjuster *_adjuster, int w, int h, unsigned int ptype, int flipit, double aspectratio);
+ int needChange() { return needchange; }
+ int onPaint(HWND hwnd);
+ void displayFrame(const char *buf, int size, int time);
+ void timerCallback();
+ void setPalette(RGBQUAD *pal) { m_palette = pal; }
+ void drawSubtitle(SubsItem *item);
+ void resetSubtitle();
+ void setVFlip(int on) { flip = on; }
+ void Refresh();
+ void close();
+ void SetFrame(void *_frame) { frame = _frame; }
+private:
+ void *frame;
+ int width, height, flip;
+ int needchange;
+ unsigned int type;
+ LPDIRECTDRAW lpDD;
+ LPDIRECTDRAWSURFACE lpddsOverlay, lpddsPrimary;
+ DDCAPS capsDrv;
+ unsigned int uDestSizeAlign, uSrcSizeAlign;
+ DWORD dwUpdateFlags;
+ RECT rs, rd;
+ RECT lastresizerect;
+ bool initing;
+
+ LPDIRECTDRAWCLIPPER lpddsClipper;
+ DDPIXELFORMAT m_ddpf;
+ int m_depth;
+ RGBQUAD *m_palette;
+
+ RECT winRect;
+ RECT m_monRect;
+};
+
+extern DDrawVideoOutput ddraw;
+#endif
diff --git a/Src/Plugins/Input/in_wmvdrm/vid_overlay.cpp b/Src/Plugins/Input/in_wmvdrm/vid_overlay.cpp
new file mode 100644
index 00000000..dd686489
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/vid_overlay.cpp
@@ -0,0 +1,708 @@
+#include "main.h"
+#include <windows.h>
+#include <multimon.h>
+#include "vid_overlay.h"
+#include "directdraw.h"
+#include <api.h>
+#include "../Winamp/wa_ipc.h"
+#include <assert.h>
+void getViewport(RECT *r, HWND wnd, int full, RECT *sr);
+
+
+void YV12_to_YUY2(unsigned char *output, const YV12_PLANES *planes, int pitch, int width, int height, int flip)
+{
+ const unsigned char *yi = planes->y.baseAddr;
+ const unsigned char *ui = planes->u.baseAddr;
+ const unsigned char *vi = planes->v.baseAddr;
+ if (flip)
+ output += pitch * (height - 1);
+ while (height > 0)
+ {
+ int x = width;
+ unsigned char *oo = output;
+
+ while (x > 0)
+ {
+ output[0] = *yi++; output[1] = *ui++; output[2] = *yi++; output[3] = *vi++;
+ output += 4; x -= 2;
+ }
+ ui -= width / 2;
+ vi -= width / 2;
+ yi += planes->y.rowBytes - width;
+ x = width;
+ if (flip) output = oo - pitch;
+ else output += pitch - width * 2;
+ oo = output;
+ while (x > 0)
+ {
+ output[0] = *yi++; output[1] = *ui++; output[2] = *yi++; output[3] = *vi++;
+ output += 4; x -= 2;
+ }
+ if (flip) output = oo - pitch;
+ else output += pitch - width * 2;
+ ui += planes->u.rowBytes - (width / 2);
+ vi += planes->v.rowBytes - (width / 2);
+ yi += planes->y.rowBytes - width;
+ height -= 2;
+ }
+}
+
+void YV12_to_UYVY(unsigned char *output, const YV12_PLANES *planes, int pitch, int width, int height, int flip)
+{
+ const unsigned char *yi = planes->y.baseAddr;
+ const unsigned char *ui = planes->u.baseAddr;
+ const unsigned char *vi = planes->v.baseAddr;
+
+ if (flip) output += pitch * (height - 1);
+ while (height > 0)
+ {
+ int x = width;
+ unsigned char *oo = output;
+
+ while (x > 0)
+ {
+ output[0] = *ui++; output[1] = *yi++; output[2] = *vi++; output[3] = *yi++;
+ output += 4; x -= 2;
+ }
+
+ ui -= width / 2;
+ vi -= width / 2;
+ yi += planes->y.rowBytes - width;
+ x = width;
+ if (flip) output = oo - pitch;
+ else output += pitch - width * 2;
+ oo = output;
+ while (x > 0)
+ {
+ output[0] = *ui++; output[1] = *yi++; output[2] = *vi++; output[3] = *yi++;
+ output += 4; x -= 2;
+ }
+
+ if (flip) output = oo - pitch;
+ else output += pitch - width * 2;
+ ui += planes->u.rowBytes - (width / 2);
+ vi += planes->v.rowBytes - (width / 2);
+ yi += planes->y.rowBytes - width;
+ height -= 2;
+ }
+}
+
+void YV12_to_YV12(unsigned char *output, const YV12_PLANES *planes, int pitch, int width, int height, int flip)
+{ // woo native YV12 copy
+ int f = !!flip;
+ unsigned char *o = output + (f * height * pitch);
+ const char *i = (const char*)planes->y.baseAddr;
+
+
+ int d_o = pitch;
+ if (f) d_o = -d_o;
+ else o -= d_o;
+
+ int h2 = height;
+ while (h2--)
+ {
+ o += d_o; memcpy(o, i, width); i += planes->y.rowBytes;
+ }
+
+ d_o /= 2;
+
+ int w2 = width / 2;
+ h2 = height / 2;
+ i = (const char*)planes->v.baseAddr;
+ o = output + (height * pitch * (f + 4)) / 4;
+
+ if (!f) o -= d_o;
+ while (h2--)
+ {
+ o += d_o; memcpy(o, i, w2); i += planes->v.rowBytes;
+ }
+ o = output + (height * pitch * (f + 5)) / 4;
+ i = (const char*)planes->u.baseAddr;
+ h2 = height / 2;
+
+ if (!f) o -= d_o;
+ while (h2--)
+ {
+ o += d_o; memcpy(o, i, w2);i += planes->u.rowBytes;
+ }
+}
+
+
+void YUY2_to_YUY2(unsigned char *output, const char *buf, int pitch, int width, int height, int flip)
+{
+ const char *a = buf;
+ unsigned char *b = output;
+ int l = width * 2, l2 = pitch;
+ if (flip)
+ {
+ b += (height - 1) * l2;
+ l2 = -l2;
+ }
+
+
+ //wee straight YUY2 copy
+ for (int i = 0;i < height;i++)
+ {
+ memcpy(b, a, l);
+ b += l2;
+ a += l;
+ }
+
+}
+
+void YUY2_to_UYVY(unsigned char *output, const char *buf, int pitch, int width, int height, int flip)
+{
+ const char *a = buf;
+ unsigned char *b = output;
+ int l = width * 2, l2 = pitch;
+ if (flip)
+ {
+ b += (height - 1) * l2;
+ l2 = -l2;
+ }
+
+ for (int i = 0;i < height;i++)
+ {
+ int x = width / 2;
+ while (x-- > 0)
+ {
+ b[0] = a[1];
+ b[1] = a[0];
+ b[2] = a[3];
+ b[3] = a[2];
+ a += 4;
+ b += 4;
+ }
+ memcpy(b, a, l);
+ b += l2;
+ a += l;
+ }
+}
+
+#define INIT_DIRECTDRAW_STRUCT(x) (ZeroMemory(&x, sizeof(x)), x.dwSize=sizeof(x))
+// I like to set these to 255,0,255 to test that we arent drawing this color too many playces
+#define OV_COL_R 16
+#define OV_COL_G 0
+#define OV_COL_B 16
+
+OverlayVideoOutput::OverlayVideoOutput() : frame(NULL), width(0), height(0),
+ flip(0), type(0), uDestSizeAlign(0), uSrcSizeAlign(0), dwUpdateFlags(0)
+{
+ lpDD = NULL;
+ m_closed = 0;
+ overlay_color = RGB(OV_COL_R, OV_COL_G, OV_COL_B);
+ lpddsOverlay = NULL;
+ lpddsPrimary = NULL;
+ lpBackBuffer = NULL;
+
+ yuy2_output = uyvy_output = 0;
+ initing = false;
+ needchange = 0;
+ memset(&m_oldrd, 0, sizeof(m_oldrd));
+ memset(&winRect, 0, sizeof(winRect));
+ m_fontsize = 0;
+}
+
+OverlayVideoOutput::~OverlayVideoOutput()
+{
+ OverlayVideoOutput::close();
+}
+
+
+static DWORD DD_ColorMatch(LPDIRECTDRAWSURFACE pdds, COLORREF rgb)
+{
+ COLORREF rgbT;
+ HDC hdc;
+ DWORD dw = CLR_INVALID;
+ DDSURFACEDESC ddsd;
+ HRESULT hres;
+
+ //
+ // use GDI SetPixel to color match for us
+ //
+ if (rgb != CLR_INVALID && pdds->GetDC(&hdc) == DD_OK)
+ {
+ rgbT = GetPixel(hdc, 0, 0); // save current pixel value
+ SetPixel(hdc, 0, 0, rgb); // set our value
+ pdds->ReleaseDC(hdc);
+ }
+
+ // now lock the surface so we can read back the converted color
+ ddsd.dwSize = sizeof(ddsd);
+ while ((hres = pdds->Lock(NULL, &ddsd, 0, NULL)) ==
+ DDERR_WASSTILLDRAWING)
+ ;
+
+ if (hres == DD_OK)
+ {
+ dw = *(DWORD *)ddsd.lpSurface; // get DWORD
+ if (ddsd.ddpfPixelFormat.dwRGBBitCount < 32)
+ dw &= (1 << ddsd.ddpfPixelFormat.dwRGBBitCount) - 1; // mask it to bpp
+ pdds->Unlock(NULL);
+ }
+
+
+ // now put the color that was there back.
+ if (rgb != CLR_INVALID && pdds->GetDC(&hdc) == DD_OK)
+ {
+ SetPixel(hdc, 0, 0, rgbT);
+ pdds->ReleaseDC(hdc);
+ }
+
+ return dw;
+}
+
+int OverlayVideoOutput::create(HWND parent, VideoAspectAdjuster *_adjuster, int w, int h, unsigned int ptype, int flipit, double aspectratio)
+{
+ OverlayVideoOutput::close();
+ this->parent = parent;
+ type = ptype;
+ width = w;
+ height = h;
+ flip = flipit;
+
+ adjuster = _adjuster;
+
+ initing = true;
+ HWND hwnd = this->parent;
+
+ if (lpDD) lpDD->Release();
+ lpDD = NULL;
+
+ update_monitor_coords();
+
+ if (!foundGUID) DDrawCreate(NULL, &lpDD, NULL);
+ else DDrawCreate(&m_devguid, &lpDD, NULL);
+
+
+ if (!lpDD)
+ {
+ initing = false;
+ return 0;
+ }
+
+ lpDD->SetCooperativeLevel(hwnd, DDSCL_NOWINDOWCHANGES | DDSCL_NORMAL);
+
+ DDSURFACEDESC ddsd;
+ INIT_DIRECTDRAW_STRUCT(ddsd);
+ ddsd.dwFlags = DDSD_CAPS;
+ ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+ lpDD->CreateSurface(&ddsd, &lpddsPrimary, NULL );
+
+ if (!lpddsPrimary)
+ {
+ if (lpDD) lpDD->Release();
+ lpDD = NULL;
+ initing=false;
+ return 0;
+ }
+
+ // init overlay
+ DDSURFACEDESC ddsdOverlay;
+ INIT_DIRECTDRAW_STRUCT(ddsdOverlay);
+ //ddsdOverlay.ddsCaps.dwCaps=DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
+ //ddsdOverlay.dwFlags= DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT|DDSD_PITCH;
+ //ddsdOverlay.dwBackBufferCount=0;
+ ddsdOverlay.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
+ ddsdOverlay.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT | DDSD_PITCH | DDSD_BACKBUFFERCOUNT;
+ ddsdOverlay.dwBackBufferCount = 1;
+ ddsdOverlay.dwWidth = w;
+ ddsdOverlay.dwHeight = h;
+ ddsdOverlay.lPitch = w * 4;
+ DDPIXELFORMAT pf[] =
+ {
+ {sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('Y', 'U', 'Y', '2'), 0, 0, 0, 0, 0},
+ {sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('U', 'Y', 'V', 'Y'), 0, 0, 0, 0, 0}, // UYVY
+ {sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('Y', 'V', '1', '2'), 0, 0, 0, 0, 0},
+ };
+ int tab[5] = {0};
+ if (type == VIDEO_MAKETYPE('Y', 'U', 'Y', '2'))
+ {
+ tab[0] = 0; // default is YUY2
+ tab[1] = 1;
+ tab[2] = -1;
+ }
+ else if (type == VIDEO_MAKETYPE('U', 'Y', 'V', 'Y'))
+ {
+ tab[0] = 1; // make UYVY default
+ tab[1] = 0;
+ tab[2] = -1;
+ }
+ else if (type == VIDEO_MAKETYPE('Y', 'V', '1', '2'))
+ {
+ if (config_video.yv12())
+ {
+ tab[0] = 2;
+ tab[1] = 0;
+ tab[2] = 1;
+ tab[3] = -1;
+ }
+ else
+ {
+ //use YUY2
+ tab[0] = 0; // default is YUY2
+ tab[1] = 1;
+ tab[2] = -1;
+ }
+ }
+ else
+ {
+ tab[0] = -1; // default is RGB
+ }
+
+ int x = 4096;
+ HRESULT v = -1;
+ for (x = 0; x < sizeof(tab) / sizeof(tab[0]) && tab[x] >= 0; x ++)
+ {
+ ddsdOverlay.ddpfPixelFormat = pf[tab[x]];
+ v = lpDD->CreateSurface(&ddsdOverlay, &lpddsOverlay, NULL);
+ if (!FAILED(v)) break;
+ }
+ if (FAILED(v) || x >= sizeof(tab) / sizeof(tab[0]) || tab[x] < 0)
+ {
+ initing = false;
+ return 0;
+ }
+
+ yuy2_output = (tab[x] == 0);
+ uyvy_output = (tab[x] == 1);
+
+ //get the backbuffer surface
+ DDSCAPS ddscaps;
+ ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
+ v = lpddsOverlay->GetAttachedSurface(&ddscaps, &lpBackBuffer);
+ if (v != DD_OK || lpBackBuffer == 0)
+ {
+ // make it use normal vsync
+ lpBackBuffer=0;
+ initing = FALSE;
+ return 0;
+ }
+
+ INIT_DIRECTDRAW_STRUCT(capsDrv);
+ lpDD->GetCaps(&capsDrv, NULL);
+
+ uDestSizeAlign = capsDrv.dwAlignSizeDest;
+ uSrcSizeAlign = capsDrv.dwAlignSizeSrc;
+
+ dwUpdateFlags = DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE;
+
+ DEVMODE d;
+ d.dmSize = sizeof(d);
+ d.dmDriverExtra = 0;
+ EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &d);
+
+ int rv = OV_COL_R, gv = OV_COL_G, bv = OV_COL_B;
+ overlay_color = RGB(rv, gv, bv);
+
+ if (d.dmBitsPerPel == 8)
+ {
+ overlay_color = RGB(255, 0, 255);
+ }
+
+ INIT_DIRECTDRAW_STRUCT(ovfx);
+ ovfx.dwDDFX = 0;
+ switch (d.dmBitsPerPel)
+ {
+ case 8:
+ ovfx.dckDestColorkey.dwColorSpaceLowValue = 253;
+ break;
+ case 16:
+ ovfx.dckDestColorkey.dwColorSpaceLowValue = ((rv >> 3) << 11) | ((gv >> 2) << 5) | (bv >> 3);
+ break;
+ case 15:
+ ovfx.dckDestColorkey.dwColorSpaceLowValue = ((rv >> 3) << 10) | ((gv >> 3) << 5) | (bv >> 3);
+ break;
+ case 24:
+ case 32:
+ ovfx.dckDestColorkey.dwColorSpaceLowValue = (rv << 16) | (gv << 8) | bv;
+ break;
+ }
+
+ //try to get the correct bit depth thru directdraw (for fucked up 16 bits displays for ie.)
+ {
+ DDSURFACEDESC DDsd = {sizeof(DDsd), };
+ lpddsPrimary->GetSurfaceDesc(&ddsd);
+ DDsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; //create the surface at screen depth
+ DDsd.dwWidth = 8;
+ DDsd.dwHeight = 8;
+ DDsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY;
+ LPDIRECTDRAWSURFACE tempsurf;
+ if (lpDD->CreateSurface(&DDsd, &tempsurf, NULL) == DD_OK)
+ {
+ int res = DD_ColorMatch(tempsurf, overlay_color);
+ if (res != CLR_INVALID) ovfx.dckDestColorkey.dwColorSpaceLowValue = res;
+ tempsurf->Release();
+ }
+ }
+
+ ovfx.dckDestColorkey.dwColorSpaceHighValue = ovfx.dckDestColorkey.dwColorSpaceLowValue;
+
+ getRects(&rs, &rd);
+ if (FAILED(lpddsOverlay->UpdateOverlay(&rs, lpddsPrimary, &rd, dwUpdateFlags, &ovfx)))
+ {
+ initing = false;
+ return 0;
+ }
+ initing = false;
+
+ DDSURFACEDESC dd = {sizeof(dd), };
+ if (lpddsOverlay->Lock(NULL, &dd, DDLOCK_WAIT, NULL) != DD_OK) return 0;
+ unsigned char *o = (unsigned char*)dd.lpSurface;
+ if (uyvy_output || yuy2_output)
+ {
+ int x = dd.lPitch * height / 2;
+ while (x--)
+ {
+ if (uyvy_output)
+ {
+ *o++ = 128;
+ *o++ = 0;
+ }
+ else
+ {
+ *o++ = 0;
+ *o++ = -128;
+ }
+ }
+ }
+ else
+ {
+ memset(o, 0, dd.lPitch*height); o += dd.lPitch * height;
+ memset(o, 128, dd.lPitch*height / 2);
+ }
+ lpddsOverlay->Unlock(&dd);
+
+ m_closed=0;
+ needchange = 0;
+ InvalidateRect(hwnd, NULL, TRUE);
+
+ return 1;
+}
+
+void OverlayVideoOutput::close()
+{
+ m_closed = 1;
+ if (lpddsOverlay) lpddsOverlay->UpdateOverlay(NULL, lpddsPrimary, NULL, DDOVER_HIDE , NULL);
+ if (lpBackBuffer) lpBackBuffer->Release(); lpBackBuffer=0;
+ if (lpddsOverlay) lpddsOverlay->Release(); lpddsOverlay=0;
+ if (lpddsPrimary) lpddsPrimary->Release(); lpddsPrimary=0;
+ if (lpDD) lpDD->Release(); lpDD=0; // BU added NULL check in response to talkback
+}
+
+void OverlayVideoOutput::getRects(RECT *drs, RECT *drd, int fixmultimon) const
+{
+ //if(GetParent(hwnd)) hwnd=GetParent(hwnd);
+
+ RECT rd, rs;
+ GetClientRect(parent, &rd);
+ ClientToScreen(parent, (LPPOINT)&rd);
+ ClientToScreen(parent, ((LPPOINT)&rd) + 1);
+
+ adjuster->adjustAspect(rd);
+
+ rd.left -= m_mon_x;
+ rd.right -= m_mon_x;
+ rd.top -= m_mon_y;
+ rd.bottom -= m_mon_y;
+
+ memset(&rs, 0, sizeof(rs));
+ rs.right = width;
+ rs.bottom = height;
+
+ if (fixmultimon)
+ {
+ //resize overlay for off-screen
+ RECT rfull;
+ getViewport(&rfull, parent, 1, NULL);
+
+ rfull.left -= m_mon_x;
+ rfull.right -= m_mon_x;
+ rfull.top -= m_mon_y;
+ rfull.bottom -= m_mon_y;
+
+ if (rd.right > rfull.right)
+ {
+ int diff = rd.right - rfull.right;
+ float sc = (float)(width) / (float)(rd.right - rd.left);
+ rd.right = rfull.right;
+ rs.right = width - (int)(diff * sc);
+ }
+ if (rd.left < rfull.left)
+ {
+ int diff = rfull.left - rd.left;
+ float sc = (float)(width) / (float)(rd.right - rd.left);
+ rd.left = rfull.left;
+ rs.left = (int)(diff * sc);
+ }
+ if (rd.bottom > rfull.bottom)
+ {
+ int diff = rd.bottom - rfull.bottom;
+ float sc = (float)(height) / (float)(rd.bottom - rd.top);
+ rd.bottom = rfull.bottom;
+ rs.bottom = height - (int)(diff * sc);
+ }
+ if (rd.top < rfull.top)
+ {
+ int diff = rfull.top - rd.top;
+ float sc = (float)(height) / (float)(rd.bottom - rd.top);
+ rd.top = rfull.top;
+ rs.top = (int)(diff * sc);
+ }
+ }
+
+ if (capsDrv.dwCaps & DDCAPS_ALIGNSIZESRC && uDestSizeAlign)
+ {
+ rs.left = (int)((rs.left + uDestSizeAlign - 1) / uDestSizeAlign) * uDestSizeAlign;
+ rs.right = (int)((rs.right + uDestSizeAlign - 1) / uDestSizeAlign) * uDestSizeAlign;
+ }
+ if (capsDrv.dwCaps & DDCAPS_ALIGNSIZEDEST && uDestSizeAlign)
+ {
+ rd.left = (int)((rd.left + uDestSizeAlign - 1) / uDestSizeAlign) * uDestSizeAlign;
+ rd.right = (int)((rd.right + uDestSizeAlign - 1) / uDestSizeAlign) * uDestSizeAlign;
+ }
+
+ *drd = rd;
+ *drs = rs;
+}
+
+void OverlayVideoOutput::timerCallback()
+{
+ if (!adjuster)
+ return;
+ RECT rd, rs;
+ getRects(&rs, &rd);
+
+ if (memcmp(&m_oldrd, &rd, sizeof(RECT)))
+ {
+ m_oldrd = rd;
+ if (!initing && lpddsOverlay)
+ if (FAILED(lpddsOverlay->UpdateOverlay(&rs, lpddsPrimary, &rd, dwUpdateFlags, &ovfx)))
+ {
+ needchange = 1;
+ }
+ InvalidateRect(parent, NULL, FALSE);
+ }
+}
+
+int OverlayVideoOutput::onPaint(HWND hwnd)
+{
+ if (!adjuster)
+ return 0;
+ PAINTSTRUCT p;
+ BeginPaint(hwnd, &p);
+
+ if (!m_closed)
+ {
+ RECT r, rs, rfull, clientRect;
+ RECT drawRect;
+ getRects(&rs, &r, 0); // we don't just fill the entire client rect, cause that looks gross
+
+ getViewport(&rfull, hwnd, 1, NULL);
+
+ // go from this screen coords to global coords
+ r.left += rfull.left;
+ r.top += rfull.top;
+ r.right += rfull.left;
+ r.bottom += rfull.top;
+
+ // go from global back to client
+ ScreenToClient(hwnd, (LPPOINT)&r);
+ ScreenToClient(hwnd, ((LPPOINT)&r) + 1);
+
+
+ HBRUSH br = (HBRUSH) GetStockObject(BLACK_BRUSH);
+ GetClientRect(hwnd, &clientRect);
+
+ // left black box
+ drawRect.left = clientRect.left;
+ drawRect.right = r.left;
+ drawRect.top = clientRect.top;
+ drawRect.bottom = clientRect.bottom;
+ FillRect(p.hdc, &drawRect, br);
+
+ // right black box
+ drawRect.left = r.right;
+ drawRect.right = clientRect.right;
+ drawRect.top = clientRect.top;
+ drawRect.bottom = clientRect.bottom;
+ FillRect(p.hdc, &drawRect, br);
+
+ // top black box
+ drawRect.left = clientRect.left;
+ drawRect.right = clientRect.right;
+ drawRect.top = clientRect.top;
+ drawRect.bottom = r.top;
+ FillRect(p.hdc, &drawRect, br);
+
+ // bottom black box
+ drawRect.left = clientRect.left;
+ drawRect.right = clientRect.right;
+ drawRect.top = r.bottom;
+ drawRect.bottom = clientRect.bottom;
+ FillRect(p.hdc, &drawRect, br);
+
+ LOGBRUSH lb = {BS_SOLID, (COLORREF)overlay_color, };
+ br = CreateBrushIndirect(&lb);
+
+ FillRect(p.hdc, &r, br);
+ DeleteObject(br);
+ }
+
+ EndPaint(hwnd, &p);
+
+ return 1;
+}
+
+void OverlayVideoOutput::displayFrame(const char * /*buf*/, int size, int time)
+{
+ if (m_closed)
+ return;
+ DDSURFACEDESC dd = {sizeof(dd), };
+ //CT> vsync wait not used anymore
+ //if (config_video.vsync()) lpDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN,0);
+ HRESULT result;
+ //if ((result=lpddsOverlay->Lock(NULL,&dd,DDLOCK_WAIT,NULL)) != DD_OK) {
+ if ((result = lpBackBuffer->Lock(NULL, &dd, DDLOCK_WAIT, NULL)) != DD_OK)
+ {
+ if (result == DDERR_SURFACELOST) needchange = 1;
+ return ;
+ }
+ if (type == VIDEO_MAKETYPE('Y', 'V', '1', '2'))
+ {
+ const YV12_PLANES *planes = (YV12_PLANES *)frame;
+ if (uyvy_output)
+ YV12_to_UYVY((unsigned char*)dd.lpSurface, planes, dd.lPitch, width, height, flip);
+ else if (yuy2_output)
+ YV12_to_YUY2((unsigned char*)dd.lpSurface, planes, dd.lPitch, width, height, flip);
+ else
+ YV12_to_YV12((unsigned char*)dd.lpSurface, planes, dd.lPitch, width, height, flip);
+ }
+ else if (type == VIDEO_MAKETYPE('Y', 'U', 'Y', '2'))
+ {
+ if (yuy2_output)
+ YUY2_to_YUY2((unsigned char*)dd.lpSurface, (const char *)frame, dd.lPitch, width, height, flip);
+ else if (uyvy_output)
+ YUY2_to_UYVY((unsigned char*)dd.lpSurface, (const char *)frame, dd.lPitch, width, height, flip);
+ else
+ YUY2_to_YUY2((unsigned char*)dd.lpSurface, (const char *)frame, dd.lPitch, width, height, flip); // is this right?
+ }
+ else if (type == VIDEO_MAKETYPE('U', 'Y', 'V', 'Y'))
+ {
+ if (yuy2_output)
+ YUY2_to_UYVY((unsigned char*)dd.lpSurface, (const char *)frame, dd.lPitch, width, height, flip);
+ else
+ YUY2_to_YUY2((unsigned char*)dd.lpSurface, (const char *)frame, dd.lPitch, width, height, flip);
+ }
+
+ lpBackBuffer->Unlock(&dd);
+ lpddsOverlay->Flip(lpBackBuffer, DDFLIP_WAIT);
+}
+
+void OverlayVideoOutput::drawSubtitle(SubsItem *item)
+{
+}
+
+void OverlayVideoOutput::resetSubtitle()
+{
+}
diff --git a/Src/Plugins/Input/in_wmvdrm/vid_overlay.h b/Src/Plugins/Input/in_wmvdrm/vid_overlay.h
new file mode 100644
index 00000000..d7d811a6
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/vid_overlay.h
@@ -0,0 +1,55 @@
+#ifndef NULLSOFT_VIDEO_OVERLAY_H
+#define NULLSOFT_VIDEO_OVERLAY_H
+
+#include <ddraw.h>
+#include <multimon.h>
+#include "VideoOutputChildDDraw.h"
+
+class SubsItem;
+
+class OverlayVideoOutput : public VideoOutputChildDDraw {
+
+public:
+ OverlayVideoOutput();
+ virtual ~OverlayVideoOutput();
+ int create(HWND parent,VideoAspectAdjuster *_adjuster, int w, int h, unsigned int type, int flipit, double aspectratio); //return 1 if ok
+ int needChange() { return needchange; }
+ int onPaint(HWND hwnd);
+ void displayFrame(const char *buf, int size, int time);
+ void timerCallback();
+ void close();
+ void drawSubtitle(SubsItem *item);
+ void resetSubtitle();
+ void setVFlip(int on) { flip=on; }
+ void Refresh() { if (parent) InvalidateRect(parent, NULL, TRUE); }
+ void SetFrame(void *_frame) { frame = _frame; }
+private:
+ void *frame;
+ int width, height, flip;
+ int m_closed;
+ int needchange;
+ unsigned int type;
+ LPDIRECTDRAW lpDD;
+ LPDIRECTDRAWSURFACE lpddsOverlay, lpddsPrimary;
+ LPDIRECTDRAWSURFACE lpBackBuffer;
+
+ DDCAPS capsDrv;
+ unsigned int uDestSizeAlign, uSrcSizeAlign;
+ DWORD dwUpdateFlags;
+ DDOVERLAYFX ovfx;
+ RECT rs,rd;
+ RECT m_oldrd;
+ RECT winRect;
+
+ int overlay_color;
+ bool initing;
+ int yuy2_output, uyvy_output;
+
+ void getRects(RECT *drs, RECT *drd, int fixmultimon=1) const;
+
+ int m_fontsize;
+};
+
+extern OverlayVideoOutput overlay;
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_wmvdrm/vidutils.cpp b/Src/Plugins/Input/in_wmvdrm/vidutils.cpp
new file mode 100644
index 00000000..6361afe3
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/vidutils.cpp
@@ -0,0 +1,130 @@
+#include "main.h"
+#include "api.h"
+#include <multimon.h>
+
+#undef GetSystemMetrics
+void getViewport(RECT *r, HWND wnd, int full, RECT *sr)
+{
+ POINT *p = NULL;
+ if (p || sr || wnd)
+ {
+ static int initted = 0;
+ static HMONITOR (WINAPI *Mfp)(POINT pt, DWORD dwFlags);
+ static HMONITOR (WINAPI *Mfr)(LPCRECT lpcr, DWORD dwFlags);
+ static HMONITOR (WINAPI *Mfw)(HWND wnd, DWORD dwFlags);
+ static BOOL (WINAPI *Gmi)(HMONITOR mon, LPMONITORINFOEX lpmi);
+ if (!initted)
+ {
+ HINSTANCE h = LoadLibraryW(L"user32.dll");
+ if (h)
+ {
+ Mfp = (HMONITOR (WINAPI *)(POINT, DWORD)) GetProcAddress(h, "MonitorFromPoint");
+ Mfr = (HMONITOR (WINAPI *)(LPCRECT, DWORD)) GetProcAddress(h, "MonitorFromRect");
+ Mfw = (HMONITOR (WINAPI *)(HWND, DWORD)) GetProcAddress(h, "MonitorFromWindow");
+ Gmi = (BOOL (WINAPI *)(HMONITOR, LPMONITORINFOEX)) GetProcAddress(h, "GetMonitorInfoW");
+ }
+ initted = 1;
+ }
+
+ if (Mfp && Mfr && Mfw && Gmi)
+ {
+ HMONITOR hm = NULL;
+
+ if (sr)
+ hm = Mfr(sr, MONITOR_DEFAULTTONEAREST);
+ else if (wnd)
+ hm = Mfw(wnd, MONITOR_DEFAULTTONEAREST);
+ else if (p)
+ hm = Mfp(*p, MONITOR_DEFAULTTONEAREST);
+
+ if (hm)
+ {
+ MONITORINFOEXW mi;
+ memset(&mi, 0, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+
+ if (Gmi(hm, &mi))
+ {
+ if (!full)
+ *r = mi.rcWork;
+ else
+ *r = mi.rcMonitor;
+
+ return ;
+ }
+ }
+ }
+ }
+ if (full)
+ { // this might be borked =)
+ r->top = r->left = 0;
+ r->right = GetSystemMetrics(SM_CXSCREEN);
+ r->bottom = GetSystemMetrics(SM_CYSCREEN);
+ }
+ else
+ {
+ SystemParametersInfoW(SPI_GETWORKAREA, 0, r, 0);
+ }
+}
+
+VideoConfig::VideoConfig() : group(0),
+itemYV12(0), itemOverlay(0), itemVsync(0), itemDDraw(0)
+{}
+
+bool VideoConfig::yv12()
+{
+ GetGroup();
+ if (!itemYV12)
+ return false;
+
+ return itemYV12->GetBool();
+}
+
+bool VideoConfig::overlays()
+{
+ GetGroup();
+ if (!itemOverlay)
+ return true; // overlay by default
+
+ return itemOverlay->GetBool();
+}
+
+bool VideoConfig::vsync()
+{
+ GetGroup();
+ if (!itemVsync)
+ return false; // no vsync by default
+
+ return itemVsync->GetBool();
+}
+
+bool VideoConfig::ddraw()
+{
+ GetGroup();
+ if (!itemDDraw)
+ return true;
+
+ return itemDDraw->GetBool();
+}
+
+void VideoConfig::GetGroup()
+{
+ if (group)
+ return ;
+
+ // {2135E318-6919-4bcf-99D2-62BE3FCA8FA6}
+ static const GUID videoConfigGroupGUID =
+ { 0x2135e318, 0x6919, 0x4bcf, { 0x99, 0xd2, 0x62, 0xbe, 0x3f, 0xca, 0x8f, 0xa6 } };
+
+ group = AGAVE_API_CONFIG->GetGroup(videoConfigGroupGUID);
+ if (group)
+ {
+ itemYV12 = group->GetItem(L"YV12");
+ itemOverlay = group->GetItem(L"overlay");
+ itemVsync = group->GetItem(L"vsync");
+ itemDDraw = group->GetItem(L"ddraw");
+ }
+}
+
+
+VideoConfig config_video;
diff --git a/Src/Plugins/Input/in_wmvdrm/vidutils.h b/Src/Plugins/Input/in_wmvdrm/vidutils.h
new file mode 100644
index 00000000..34b83a90
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/vidutils.h
@@ -0,0 +1,24 @@
+#ifndef NULLSOFT_IN_WMVDRM_VID_UTILS_H
+#define NULLSOFT_IN_WMVDRM_VID_UTILS_H
+
+#include "../Agave/Config/ifc_configgroup.h"
+
+class VideoConfig
+{
+public:
+ VideoConfig();
+ bool yv12();
+ bool overlays();
+ bool vsync();
+ bool ddraw();
+
+private:
+ void GetGroup();
+
+ ifc_configgroup *group;
+ ifc_configitem *itemYV12, *itemOverlay, *itemVsync, *itemDDraw;
+};
+
+extern VideoConfig config_video;
+
+#endif \ No newline at end of file