aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Library/ml_disc
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Library/ml_disc')
-rw-r--r--Src/Plugins/Library/ml_disc/M3UWriter.cpp91
-rw-r--r--Src/Plugins/Library/ml_disc/M3UWriter.h25
-rw-r--r--Src/Plugins/Library/ml_disc/PLSWriter.cpp65
-rw-r--r--Src/Plugins/Library/ml_disc/PLSWriter.h23
-rw-r--r--Src/Plugins/Library/ml_disc/ReplayGain.cpp95
-rw-r--r--Src/Plugins/Library/ml_disc/ReplayGain.h16
-rw-r--r--Src/Plugins/Library/ml_disc/api__ml_disc.h15
-rw-r--r--Src/Plugins/Library/ml_disc/banner.cpp222
-rw-r--r--Src/Plugins/Library/ml_disc/banner.h44
-rw-r--r--Src/Plugins/Library/ml_disc/cdburn.cpp2155
-rw-r--r--Src/Plugins/Library/ml_disc/cdrip.cpp1061
-rw-r--r--Src/Plugins/Library/ml_disc/cmdbar_data.cpp485
-rw-r--r--Src/Plugins/Library/ml_disc/commandbar.cpp119
-rw-r--r--Src/Plugins/Library/ml_disc/commandbar.h48
-rw-r--r--Src/Plugins/Library/ml_disc/config.cpp129
-rw-r--r--Src/Plugins/Library/ml_disc/config.h44
-rw-r--r--Src/Plugins/Library/ml_disc/copyfiles.cpp613
-rw-r--r--Src/Plugins/Library/ml_disc/copyfiles.h27
-rw-r--r--Src/Plugins/Library/ml_disc/copyfiles_post.cpp138
-rw-r--r--Src/Plugins/Library/ml_disc/copyinternal.h125
-rw-r--r--Src/Plugins/Library/ml_disc/copyprep.cpp426
-rw-r--r--Src/Plugins/Library/ml_disc/copyprogress.cpp360
-rw-r--r--Src/Plugins/Library/ml_disc/discInfo.cpp328
-rw-r--r--Src/Plugins/Library/ml_disc/discInfo.h61
-rw-r--r--Src/Plugins/Library/ml_disc/drive.cpp138
-rw-r--r--Src/Plugins/Library/ml_disc/drive.h16
-rw-r--r--Src/Plugins/Library/ml_disc/driveListBox.cpp255
-rw-r--r--Src/Plugins/Library/ml_disc/driveListBox.h50
-rw-r--r--Src/Plugins/Library/ml_disc/drivemngr.cpp1910
-rw-r--r--Src/Plugins/Library/ml_disc/drivemngr.h239
-rw-r--r--Src/Plugins/Library/ml_disc/drives.cpp243
-rw-r--r--Src/Plugins/Library/ml_disc/drives.h44
-rw-r--r--Src/Plugins/Library/ml_disc/formatfilename.cpp269
-rw-r--r--Src/Plugins/Library/ml_disc/helpwnd.cpp350
-rw-r--r--Src/Plugins/Library/ml_disc/infoBox.cpp145
-rw-r--r--Src/Plugins/Library/ml_disc/infoBox.h42
-rw-r--r--Src/Plugins/Library/ml_disc/main.cpp1273
-rw-r--r--Src/Plugins/Library/ml_disc/main.h222
-rw-r--r--Src/Plugins/Library/ml_disc/medium.cpp136
-rw-r--r--Src/Plugins/Library/ml_disc/medium.h17
-rw-r--r--Src/Plugins/Library/ml_disc/menu.cpp25
-rw-r--r--Src/Plugins/Library/ml_disc/menu.h13
-rw-r--r--Src/Plugins/Library/ml_disc/ml_disc.rc939
-rw-r--r--Src/Plugins/Library/ml_disc/ml_disc.sln30
-rw-r--r--Src/Plugins/Library/ml_disc/ml_disc.vcxproj382
-rw-r--r--Src/Plugins/Library/ml_disc/ml_disc.vcxproj.filters250
-rw-r--r--Src/Plugins/Library/ml_disc/png.rc45
-rw-r--r--Src/Plugins/Library/ml_disc/prefs.cpp351
-rw-r--r--Src/Plugins/Library/ml_disc/primosdk_helper.cpp109
-rw-r--r--Src/Plugins/Library/ml_disc/primosdk_helper.h31
-rw-r--r--Src/Plugins/Library/ml_disc/questionwnd.cpp279
-rw-r--r--Src/Plugins/Library/ml_disc/resource.h455
-rw-r--r--Src/Plugins/Library/ml_disc/resources/cdrom.pngbin0 -> 202 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/cdrom_32x32_24.bmpbin0 -> 3126 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/eject1.pngbin0 -> 249 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/eject2.pngbin0 -> 261 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/eject3.pngbin0 -> 132 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/eject4.pngbin0 -> 113 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/eject_d.pngbin0 -> 345 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/eject_n.pngbin0 -> 361 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/eject_p.pngbin0 -> 325 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/enqueue_d.pngbin0 -> 285 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/enqueue_menu_16x16.pngbin0 -> 199 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/enqueue_n.pngbin0 -> 270 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/enqueue_p.pngbin0 -> 249 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/enqueuem_d.pngbin0 -> 250 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/enqueuem_n.pngbin0 -> 259 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/enqueuem_p.pngbin0 -> 257 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/filecopy.pngbin0 -> 2903 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/listbox_back_2x68x24.bmpbin0 -> 598 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/play_d.pngbin0 -> 340 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/play_menu_16x16.pngbin0 -> 348 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/play_n.pngbin0 -> 352 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/play_p.pngbin0 -> 352 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/playm_d.pngbin0 -> 373 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/playm_n.pngbin0 -> 380 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/playm_p.pngbin0 -> 386 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/rip&burn_logo_228x25x16.bmpbin0 -> 11456 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/resources/sonic_powered_44x22.bmpbin0 -> 694 bytes
-rw-r--r--Src/Plugins/Library/ml_disc/settings.cpp573
-rw-r--r--Src/Plugins/Library/ml_disc/settings.h108
-rw-r--r--Src/Plugins/Library/ml_disc/spti.cpp239
-rw-r--r--Src/Plugins/Library/ml_disc/spti.h35
-rw-r--r--Src/Plugins/Library/ml_disc/version.rc239
-rw-r--r--Src/Plugins/Library/ml_disc/view_cdrom.cpp1014
-rw-r--r--Src/Plugins/Library/ml_disc/view_container.cpp573
-rw-r--r--Src/Plugins/Library/ml_disc/view_data.cpp675
-rw-r--r--Src/Plugins/Library/ml_disc/view_info.cpp127
-rw-r--r--Src/Plugins/Library/ml_disc/view_ripburn.cpp567
-rw-r--r--Src/Plugins/Library/ml_disc/view_wait.cpp140
90 files changed, 19063 insertions, 0 deletions
diff --git a/Src/Plugins/Library/ml_disc/M3UWriter.cpp b/Src/Plugins/Library/ml_disc/M3UWriter.cpp
new file mode 100644
index 00000000..d4ec2a13
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/M3UWriter.cpp
@@ -0,0 +1,91 @@
+#include "M3UWriter.h"
+#include <shlwapi.h>
+
+static void MakeRelativePathName( const char *filename, char *outFile, const char *path )
+{
+ char outPath[ MAX_PATH ] = { 0 };
+
+ int common = PathCommonPrefixA( path, filename, outPath );
+ if ( common && common == lstrlenA( path ) )
+ {
+ PathAddBackslashA( outPath );
+ const char *p = filename + lstrlenA( outPath );
+ lstrcpyA( outFile, p );
+ }
+ else if ( !PathIsUNCA( filename ) && PathIsSameRootA( filename, path ) )
+ {
+ lstrcpyA( outFile, filename + 2 );
+ }
+}
+
+M3UWriter::M3UWriter()
+{
+ memset( basePath, 0, sizeof( basePath ) );
+}
+
+M3UWriter::~M3UWriter()
+{
+ Close();
+}
+
+int M3UWriter::Open( char *filename, int extendedMode )
+{
+ fp = fopen( filename, "wt" );
+ if ( !fp )
+ return 0;
+
+ if ( extendedMode )
+ fprintf( fp, "#EXTM3U\n" );
+
+ extended = extendedMode;
+
+ lstrcpynA( basePath, filename, MAX_PATH );
+ PathRemoveFileSpecA( basePath );
+
+ return 1;
+}
+
+int M3UWriter::Open( FILE *_fp, char *filename, int extendedMode )
+{
+ fp = _fp;
+ if ( !fp )
+ return 0;
+
+ if ( extendedMode )
+ fprintf( fp, "#EXTM3U\n" );
+
+ extended = extendedMode;
+
+ lstrcpynA( basePath, filename, MAX_PATH );
+ PathRemoveFileSpecA( basePath );
+
+ return 1;
+}
+
+void M3UWriter::SetFilename( char *filename )
+{
+ //char temp[ MAX_PATH ] = { 0 };
+ //MakeRelativePathName( filename, temp, basePath );
+ fprintf( fp, "%s\n", filename );
+}
+
+void M3UWriter::SetExtended( char *filename, char *title, int length )
+{
+ if ( !extended )
+ SetFilename( filename );
+ else
+ {
+ //char temp[ MAX_PATH ] = { 0 };
+ //MakeRelativePathName( filename, temp, basePath );
+ fprintf( fp, "#EXTINF:%d,%s\n%s\n", length, title, filename );
+ }
+}
+
+void M3UWriter::Close()
+{
+ if ( fp != NULL )
+ {
+ fclose( fp );
+ fp = NULL;
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/M3UWriter.h b/Src/Plugins/Library/ml_disc/M3UWriter.h
new file mode 100644
index 00000000..2f4335d5
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/M3UWriter.h
@@ -0,0 +1,25 @@
+#ifndef NULLSOFT_M3UWRITERH
+#define NULLSOFT_M3UWRITERH
+
+#include <stdio.h>
+#include <windows.h>
+
+class M3UWriter
+{
+public:
+ M3UWriter();
+ virtual ~M3UWriter();
+
+ int Open(char *filename, int extendedMode);
+ int Open( FILE *_fp, char *filename, int extendedMode );
+ void SetFilename( char *filename );
+ void SetExtended( char *filename, char *title, int length );
+ void Close();
+
+private:
+ char basePath[MAX_PATH];
+ int extended = 0;
+ FILE *fp = NULL;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/PLSWriter.cpp b/Src/Plugins/Library/ml_disc/PLSWriter.cpp
new file mode 100644
index 00000000..8231d5b7
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/PLSWriter.cpp
@@ -0,0 +1,65 @@
+#include "PLSWriter.h"
+#include <windows.h>
+
+PLSWriter::PLSWriter() : numEntries(0), entryUsed(0)
+{
+ memset(plsFile, 0, sizeof(plsFile));
+}
+
+void PLSWriter::Open(char *filename)
+{
+ lstrcpynA(plsFile, filename, MAX_PATH);
+}
+
+void PLSWriter::SetFilename(char *filename)
+{
+ char fieldbuf[32] = {0};
+ BeforeSet();
+ wsprintfA(fieldbuf,"File%u",numEntries);
+ WritePrivateProfileStringA("playlist",fieldbuf,filename,plsFile);
+}
+
+void PLSWriter::SetTitle(char *title)
+{
+ char fieldbuf[32] = {0};
+ BeforeSet();
+ wsprintfA(fieldbuf,"Title%u",numEntries);
+ WritePrivateProfileStringA("playlist",fieldbuf,title,plsFile);
+}
+
+void PLSWriter::SetLength(int length)
+{
+ char fieldbuf[32] = {0};
+ char lenStr[32] = {0};
+ BeforeSet();
+ wsprintfA(fieldbuf,"Length%u",numEntries);
+ wsprintfA(lenStr,"%d", length);
+ WritePrivateProfileStringA("playlist",fieldbuf,lenStr,plsFile);
+}
+
+void PLSWriter::BeforeSet()
+{
+ if (!entryUsed)
+ {
+ entryUsed=1;
+ numEntries++;
+ }
+}
+
+void PLSWriter::Next()
+{
+ entryUsed=0;
+}
+
+void PLSWriter::Close()
+{
+ if (numEntries)
+ {
+ char temp[32] = {0};
+ wsprintfA(temp,"%u",numEntries);
+ WritePrivateProfileStringA("playlist","NumberOfEntries",temp,plsFile);
+ WritePrivateProfileStringA("playlist","Version","2",plsFile);
+ }
+ numEntries=0;
+ entryUsed=0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/PLSWriter.h b/Src/Plugins/Library/ml_disc/PLSWriter.h
new file mode 100644
index 00000000..d97ddd47
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/PLSWriter.h
@@ -0,0 +1,23 @@
+#ifndef NULLSOFT_PLSWRITERH
+#define NULLSOFT_PLSWRITERH
+
+#include <windows.h>
+
+class PLSWriter
+{
+public:
+ PLSWriter();
+ void Open(char *filename);
+ void SetFilename(char *filename);
+ void SetTitle(char *title);
+ void SetLength(int length);
+ void Next(); // tells the pls writer to start writing info for the next item
+ void Close();
+private:
+ void BeforeSet();
+ unsigned int numEntries;
+ int entryUsed;
+ char plsFile[MAX_PATH];
+
+};
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/ReplayGain.cpp b/Src/Plugins/Library/ml_disc/ReplayGain.cpp
new file mode 100644
index 00000000..fdbd973c
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/ReplayGain.cpp
@@ -0,0 +1,95 @@
+#include "main.h"
+#include "ReplayGain.h"
+#include <api/service/waservicefactory.h>
+static obj_replaygain *replayGain = 0;
+HANDLE rgThread = 0;
+static HANDLE killSwitch = 0;
+extern HWND m_extract_wnd;
+
+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 = reinterpret_cast<api_T *>( factory->getInterface() );
+ }
+}
+
+template <class api_T>
+void ServiceRelease(api_T *api_t, GUID factoryGUID_t)
+{
+ if (plugin.service && api_t)
+ {
+ waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);
+ if (factory)
+ factory->releaseInterface(api_t);
+ }
+ api_t = NULL;
+}
+
+DWORD WINAPI RGProc(void *data)
+{
+ while (WaitForSingleObjectEx(killSwitch, INFINITE, TRUE) != WAIT_OBJECT_0);
+
+ return 0;
+}
+
+void CreateGain()
+{
+ killSwitch = CreateEvent(0, FALSE, FALSE, 0);
+ DWORD dummy;
+ rgThread = CreateThread(0, 0, RGProc, 0, 0, &dummy);
+}
+
+void CALLBACK StartGain(ULONG_PTR data)
+{
+ int mode = (int)data;
+
+ ServiceBuild(replayGain, RGGUID);
+ if (replayGain)
+ replayGain->Open(mode);
+}
+
+void CALLBACK WriteGain(ULONG_PTR data)
+{
+ if (replayGain)
+ replayGain->Write();
+
+ HANDLE notifyHandle =(HANDLE)data;
+ if (notifyHandle)
+ SetEvent(notifyHandle);
+
+ PostMessage(m_extract_wnd, WM_APP+4, 0, 0);
+}
+
+void CALLBACK CalculateGain(ULONG_PTR data)
+{
+ wchar_t *lastfn = (wchar_t *)data;
+ if (replayGain)
+ {
+ PostMessage(m_extract_wnd, WM_APP+2, 0, 0);
+ replayGain->ProcessTrack(lastfn);
+ }
+ free(lastfn);
+ PostMessage(m_extract_wnd, WM_APP+3, 0, 0);
+}
+
+void CALLBACK CloseGain(ULONG_PTR data)
+{
+ if (replayGain)
+ {
+ replayGain->Close();
+ ServiceRelease(replayGain, RGGUID);
+ replayGain = 0;
+ }
+}
+
+void CALLBACK QuitThread(ULONG_PTR data)
+{
+ if (rgThread)
+ {
+ SetEvent(killSwitch);
+ }
+}
diff --git a/Src/Plugins/Library/ml_disc/ReplayGain.h b/Src/Plugins/Library/ml_disc/ReplayGain.h
new file mode 100644
index 00000000..3e79e8c4
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/ReplayGain.h
@@ -0,0 +1,16 @@
+#ifndef NULLSOFT_ML_DISC_REPLAYGAIN_H
+#define NULLSOFT_ML_DISC_REPLAYGAIN_H
+
+#include <windows.h>
+#include "../ml_rg/obj_replaygain.h"
+
+void CALLBACK StartGain(ULONG_PTR data);
+void CALLBACK WriteGain(ULONG_PTR data);
+void CALLBACK CalculateGain(ULONG_PTR data);
+void CALLBACK CloseGain(ULONG_PTR data);
+void CALLBACK QuitThread(ULONG_PTR data);
+void CreateGain();
+
+extern HANDLE rgThread;
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/api__ml_disc.h b/Src/Plugins/Library/ml_disc/api__ml_disc.h
new file mode 100644
index 00000000..ac88b8ff
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/api__ml_disc.h
@@ -0,0 +1,15 @@
+#ifndef NULLSOFT_ML_DISC_API_H
+#define NULLSOFT_ML_DISC_API_H
+
+#include <api/service/waServiceFactory.h>
+
+#include <api/application/api_application.h>
+#define WASABI_API_APP applicationApi
+
+#include "../Agave/Language/api_language.h"
+
+#include "../Winamp/api_stats.h"
+extern api_stats *statsApi;
+#define AGAVE_API_STATS statsApi
+
+#endif // !NULLSOFT_ML_DISC_API_H \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/banner.cpp b/Src/Plugins/Library/ml_disc/banner.cpp
new file mode 100644
index 00000000..5d0b1b15
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/banner.cpp
@@ -0,0 +1,222 @@
+#include "main.h"
+#include ".\banner.h"
+#include "..\gen_ml\graphics.h"
+
+
+MLBanner::MLBanner(void)
+{
+ bmpBck = NULL;
+ bmpLogo = NULL;
+ bmpLogoMask = NULL;
+ bmpBanner = NULL;
+ m_hwnd = NULL;
+ oldWndProc = NULL;
+
+ color1 = RGB(0,0,0);
+ color2 = RGB(255,255,255);
+
+ hInstance = NULL;
+ logoResId = 0;
+ bgndResId = 0;
+
+ SetRect(&rcBanner, 0,0,0,0);
+}
+MLBanner::~MLBanner(void)
+{
+ DestroyImages();
+ SetWindowLong(m_hwnd, GWL_WNDPROC, (LONG)oldWndProc);
+ oldWndProc = NULL;
+}
+
+void MLBanner::SetColors(int color1, int color2)
+{
+ this->color1 = color1;
+ this->color2 = color2;
+ ReloadImages();
+}
+
+void MLBanner::SetImages(HINSTANCE hInstance, int bgndResId, int logoResId)
+{
+ this->hInstance = hInstance;
+ this->logoResId = logoResId;
+ this->bgndResId = bgndResId;
+ ReloadImages();
+}
+
+void MLBanner::ReloadImages(void)
+{
+ DestroyImages();
+ if (hInstance)
+ {
+ if (bgndResId)
+ {
+ bmpBck = (HBITMAP)LoadImage(hInstance, MAKEINTRESOURCE(bgndResId), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
+ if (bmpBck) bmpBck = PatchBitmapColors24(bmpBck, color1, color2, Filter1);
+ }
+ if (logoResId)
+ {
+ bmpLogo = (HBITMAP)LoadImage(hInstance, MAKEINTRESOURCE(logoResId), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
+ if (bmpLogo)
+ {
+ bmpLogoMask = CreateBitmapMask(bmpLogo, 1,1);
+ }
+ }
+ }
+
+}
+
+void MLBanner::DestroyImages(void)
+{
+ if (bmpBck) DeleteObject(bmpBck);
+ bmpBck = NULL;
+
+ if (bmpLogo) DeleteObject(bmpLogo);
+ bmpLogo = NULL;
+
+ if (bmpLogoMask) DeleteObject(bmpLogoMask);
+ bmpLogoMask = NULL;
+
+ if (bmpBanner) DeleteObject(bmpBanner);
+ bmpBanner = NULL;
+}
+
+
+
+void MLBanner::UpdateBunnerBmp(void)
+{
+ if (bmpBanner) DeleteObject(bmpBanner);
+
+ HDC hdc = GetDC(m_hwnd);
+
+ bmpBanner = CreateCompatibleBitmap(hdc, rcBanner.right, rcBanner.bottom);
+ HDC memDstDC = CreateCompatibleDC (hdc);
+ HDC memSrcDC = CreateCompatibleDC (hdc);
+ HBITMAP obmp1 = (HBITMAP)SelectObject(memDstDC, bmpBanner);
+ HBITMAP obmp2 = (HBITMAP)SelectObject(memSrcDC, bmpBck);
+
+ for (int i = 0; i < rcBanner.right; i++)
+ {
+ BitBlt(memDstDC,
+ i,0,
+ 1, rcBanner.bottom,
+ memSrcDC,
+ 0,0,
+ SRCCOPY);
+
+ }
+
+ BITMAP bm;
+ GetObject(bmpLogo, sizeof(BITMAP), &bm);
+
+ SelectObject(memSrcDC, bmpLogoMask);
+ BitBlt(memDstDC,
+ 6,
+ max(2, (rcBanner.bottom - bm.bmHeight) / 2),
+ min(rcBanner.right - 4, bm.bmWidth),
+ min(rcBanner.bottom - 2, bm.bmHeight),
+ memSrcDC,
+ 0,0,
+ SRCAND);
+
+ SelectObject(memSrcDC, bmpLogo);
+ BitBlt(memDstDC,
+ 6,
+ max(2, (rcBanner.bottom - bm.bmHeight) / 2),
+ min(rcBanner.right - 4, bm.bmWidth),
+ min(rcBanner.bottom - 2, bm.bmHeight),
+ memSrcDC,
+ 0,0,
+ SRCPAINT);
+
+ ReleaseDC(m_hwnd, hdc);
+
+ SelectObject(memDstDC, obmp1);
+ SelectObject(memSrcDC, obmp2);
+
+ DeleteDC(memDstDC);
+ DeleteDC(memSrcDC);
+
+}
+
+void MLBanner::Init(HWND hwnd)
+{
+ m_hwnd = hwnd;
+ SetWindowLong(hwnd,GWL_USERDATA,(LONG)this);
+ oldWndProc= (WNDPROC) SetWindowLong(hwnd, GWL_WNDPROC, (LONG)newWndProc);
+ UpdateBunnerBmp();
+}
+
+BOOL CALLBACK MLBanner::newWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
+{
+ MLBanner *banner = (MLBanner*)GetWindowLong(hwndDlg, GWL_USERDATA);
+
+ switch(uMsg)
+ {
+ case WM_SIZE:
+ if (SIZE_MINIMIZED != wParam)
+ {
+ SetRect(&banner->rcBanner, 0,0,LOWORD(lParam),HIWORD(lParam));
+ banner->UpdateBunnerBmp();
+ }
+ break;
+ case WM_ERASEBKGND:
+ {
+ HDC hdc = GetDC(hwndDlg);
+ if (banner->bmpBanner)
+ {
+ HDC memSrcDC = CreateCompatibleDC (hdc);
+ HBITMAP obmp = (HBITMAP)SelectObject(memSrcDC, banner->bmpBanner);
+ StretchBlt( hdc,
+ banner->rcBanner.left,
+ banner->rcBanner.top,
+ banner->rcBanner.right - banner->rcBanner.left,
+ banner->rcBanner.bottom - banner->rcBanner.top,
+ memSrcDC,
+ banner->rcBanner.left,
+ banner->rcBanner.top,
+ banner->rcBanner.right - banner->rcBanner.left,
+ banner->rcBanner.bottom - banner->rcBanner.top,
+ SRCCOPY);
+ SelectObject(memSrcDC, obmp);
+ DeleteDC(memSrcDC);
+ }
+ ReleaseDC(hwndDlg, hdc);
+ }
+ return TRUE;
+ case WM_PAINT:
+ {
+ PAINTSTRUCT pt;
+ HDC hdc = BeginPaint(hwndDlg, &pt);
+ if (!banner->bmpBanner)
+ {
+ SetRect(&banner->rcBanner, 0,0,pt.rcPaint.right - pt.rcPaint.left, pt.rcPaint.bottom - pt.rcPaint.top);
+ banner->UpdateBunnerBmp();
+ }
+ if (banner->bmpBanner)
+ {
+ HDC memSrcDC = CreateCompatibleDC (hdc);
+ HBITMAP obmp = (HBITMAP)SelectObject(memSrcDC, banner->bmpBanner);
+ StretchBlt( hdc,
+ pt.rcPaint.left,
+ pt.rcPaint.top,
+ pt.rcPaint.right - pt.rcPaint.left,
+ pt.rcPaint.bottom - pt.rcPaint.top,
+ memSrcDC,
+ pt.rcPaint.left,
+ pt.rcPaint.top,
+ pt.rcPaint.right - pt.rcPaint.left,
+ pt.rcPaint.bottom - pt.rcPaint.top,
+ SRCCOPY);
+ SelectObject(memSrcDC, obmp);
+ DeleteDC(memSrcDC);
+ ValidateRect(hwndDlg, &pt.rcPaint);
+ }
+ EndPaint(hwndDlg, &pt);
+ }
+ break;
+ }
+
+ return CallWindowProc(banner->oldWndProc, hwndDlg, uMsg, wParam, lParam);
+}
+
+ \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/banner.h b/Src/Plugins/Library/ml_disc/banner.h
new file mode 100644
index 00000000..9b7409cf
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/banner.h
@@ -0,0 +1,44 @@
+#ifndef NULLSOFT_ML_BANNER_HEADER
+#define NULLSOFT_ML_BANNER_HEADER
+
+#include <windows.h>
+
+class MLBanner
+{
+public:
+ MLBanner(void);
+ ~MLBanner(void);
+
+public:
+
+ void SetColors(int color1, int color2);
+ void SetImages(HINSTANCE hInstance, int bgndResId, int logoResId);
+ void Init(HWND hwnd);
+ void ReloadImages(void);
+
+protected:
+ void DestroyImages(void);
+ void UpdateBunnerBmp(void);
+ static BOOL CALLBACK newWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
+
+private:
+
+ HWND m_hwnd;
+ HBITMAP bmpBck;
+ HBITMAP bmpLogo;
+ HBITMAP bmpLogoMask;
+ HBITMAP bmpBanner;
+
+ WNDPROC oldWndProc;
+
+ HINSTANCE hInstance;
+ int logoResId;
+ int bgndResId;
+
+ int color1;
+ int color2;
+
+ RECT rcBanner;
+};
+
+#endif // NULLSOFT_ML_BANNER_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/cdburn.cpp b/Src/Plugins/Library/ml_disc/cdburn.cpp
new file mode 100644
index 00000000..6f05d06a
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/cdburn.cpp
@@ -0,0 +1,2155 @@
+#include "main.h"
+#include <stdio.h>
+#include "../nu/ns_wc.h"
+#include "resource.h"
+#include "../nu/listview.h"
+#include "../nu/DialogSkinner.h"
+#include "../nu/ChildSizer.h"
+#include "config.h"
+#include "../../General/gen_ml/gaystring.h"
+#include "../Winamp/burn.h"
+#include "../Winamp/strutil.h"
+#include <std::vector>
+#include "../nu/AutoChar.h"
+#include "../nu/AutoWide.h"
+#include <api/service/waServiceFactory.h>
+#include "../playlist/ifc_playlistloadercallback.h"
+#include "../playlist/api_playlistmanager.h"
+#include <imapi.h>
+#include <imapierror.h>
+#include <shlwapi.h>
+#include <strsafe.h>
+
+//shit to finish:
+//-erase CDRWs
+//-cache the veritas handle
+//-recurse add folders
+//-check for available space on HD before burning
+//-the resampling in convert
+
+
+#define WM_EX_OPCOMPLETED (WM_USER + 0x100)
+
+class PLCallBack : ifc_playlistloadercallback
+{
+public:
+ PLCallBack(void) : fileList(0) {};
+ ~PLCallBack(void) {};
+public:
+ void OnFile(const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info)
+ {
+ fileList->push_back(new GayString(AutoChar(filename)));
+ }
+ RECVS_DISPATCH;
+public:
+ std::vector<GayString*> *fileList;
+};
+
+#define CBCLASS PLCallBack
+START_DISPATCH;
+VCB(IFC_PLAYLISTLOADERCALLBACK_ONFILE, OnFile)
+END_DISPATCH;
+#undef CBCLASS
+
+class PLCallBackW : ifc_playlistloadercallback
+{
+public:
+ PLCallBackW(void) : fileList(0) {};
+ ~PLCallBackW(void) {};
+public:
+ void OnFile(const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info)
+ {
+ fileList->push_back(new GayStringW(filename));
+ }
+ RECVS_DISPATCH;
+public:
+ std::vector<GayStringW*> *fileList;
+};
+
+#define CBCLASS PLCallBackW
+START_DISPATCH;
+VCB(IFC_PLAYLISTLOADERCALLBACK_ONFILE, OnFile)
+END_DISPATCH;
+#undef CBCLASS
+
+static INT_PTR WINAPI DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+#include "../burnlib/burnlib.h"
+
+static W_ListView m_statuslist;
+
+static HWND m_hwndstatus;
+static char m_cdrom;
+static int m_is_cdrw, m_availsecs;
+static int m_max_speed;
+static int m_dragging, m_drag_item;
+static HWND prevWnd = NULL;
+itemRecordListW itemCache[100] = {0};
+static int percentCompleted = 0;
+static DWORD pidBurner = 0;
+
+static HFONT hPLFont = NULL;
+
+static int LETTERTOINDEX(char c)
+{
+ c = (char)toupper(c);
+ if (c < 'A') c = 'A';
+ if (c > 'Z') c = 'Z';
+ return c -'A';
+}
+
+#include "../winamp/wa_ipc.h"
+
+
+#define TIMER_NOTIFYINFO_ID 1985
+#define TIMER_NOTIFYINFO_DELAY 200
+
+
+static ChildWndResizeItem burnwnd_rlist[] =
+{
+ {IDC_LIST2, 0x0011},
+ {IDC_CDINFO, 0x0010},
+ {IDC_ADD, 0x0101},
+ {IDC_BURN, 0x0101},
+ {IDC_CLEAR, 0x0101},
+ {IDC_BURN_OPTS, 0x0101},
+ {IDC_CANCEL_BURN, 0x0101},
+ {IDC_LOGO, 0x1010},
+ {IDC_BTN_SHOWINFO, 0x1111},
+};
+static _inline void code(long* v, long* k)
+{
+ unsigned long y = v[0], z = v[1], sum = 0, /* set up */
+ delta = 0x9e3779b9, n = 32 ; /* key schedule constant*/
+
+ while (n-- > 0)
+ { /* basic cycle start */
+ sum += delta;
+ y += ((z << 4) + k[0]) ^(z + sum) ^((z >> 5) + k[1]);
+ z += ((y << 4) + k[2]) ^(y + sum) ^((y >> 5) + k[3]); /* end cycle */
+ }
+ v[0] = y; v[1] = z;
+
+}
+
+static void startBurn(HWND hwndDlg, char driveletter)
+{
+ g_config->WriteInt(L"cdburnmaxspeed", m_max_speed);
+
+ //write the temp playlist to disk
+ FILE *fp;
+ char filename[MAX_PATH] = {0}, tp[MAX_PATH] = {0};
+
+ pidBurner = 0;
+
+ if (!GetTempPathA(sizeof(tp), tp)) lstrcpynA(tp, ".", MAX_PATH);
+ if (GetTempFileNameA(tp, "BRN", 0, filename))
+ {
+ unlink(filename);
+ StringCchCatA(filename, MAX_PATH, ".m3u8");
+ }
+ else lstrcpynA(filename, "brn_tmp.m3u8", MAX_PATH);
+
+ fp = fopen(filename, "wt");
+ if (!fp)
+ {
+ wchar_t title[16] = {0};
+ MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(IDS_ERROR_WRITING_TEMP_BURN_LIST),
+ WASABI_API_LNGSTRINGW_BUF(IDS_ERROR,title,16), MB_OK);
+ return ;
+ }
+ int idx = LETTERTOINDEX(driveletter);
+ fprintf(fp, "#EXTM3U\n");
+ for (int i = 0;i < itemCache[idx].Size;i++)
+ {
+ fprintf(fp, "#EXTINF:%d,%s\n", itemCache[idx].Items[i].length, (char *)AutoChar(itemCache[idx].Items[i].title, CP_UTF8));
+ fprintf(fp, "%s\n", (char *)AutoChar(itemCache[idx].Items[i].filename, CP_UTF8));
+ }
+ fclose(fp);
+
+ burnCDStruct bcds =
+ {
+ m_cdrom,
+ filename,
+ hwndDlg,
+ "",
+ };
+ pidBurner = (DWORD)(INT_PTR)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM) & bcds, IPC_BURN_CD);
+ if (!pidBurner)
+ {
+ wchar_t title[16] = {0};
+ unlink(filename);
+ MessageBox(hwndDlg, AutoWide(bcds.error), WASABI_API_LNGSTRINGW_BUF(IDS_ERROR,title,16), MB_OK);
+ }
+}
+
+static void link_handledraw(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if (uMsg == WM_DRAWITEM)
+ {
+ DRAWITEMSTRUCT *di = (DRAWITEMSTRUCT *)lParam;
+ if (di->CtlType == ODT_BUTTON)
+ {
+ wchar_t wt[123] = {0};
+ int y;
+ RECT r;
+ HPEN hPen, hOldPen;
+ GetDlgItemText(hwndDlg, (int)wParam, wt, 123);
+
+ // draw text
+ SetTextColor(di->hDC, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220));
+ r = di->rcItem;
+ r.left += 2;
+ DrawText(di->hDC, wt, -1, &r, DT_VCENTER | DT_SINGLELINE);
+
+ memset(&r, 0, sizeof(r));
+ DrawText(di->hDC, wt, -1, &r, DT_SINGLELINE | DT_CALCRECT);
+
+ // draw underline
+ y = di->rcItem.bottom - ((di->rcItem.bottom - di->rcItem.top) - (r.bottom - r.top)) / 2 - 1;
+ hPen = CreatePen(PS_SOLID, 0, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220));
+ hOldPen = (HPEN) SelectObject(di->hDC, hPen);
+ MoveToEx(di->hDC, di->rcItem.left + 2, y, NULL);
+ LineTo(di->hDC, di->rcItem.right + 2 - ((di->rcItem.right - di->rcItem.left) - (r.right - r.left)), y);
+ SelectObject(di->hDC, hOldPen);
+ DeleteObject(hPen);
+
+ }
+ }
+}
+
+static void refreshList()
+{
+ if (!m_hwndstatus) return ;
+
+ ListView_SetItemCount(m_statuslist.getwnd(), 0);
+ int idx = LETTERTOINDEX(m_cdrom);
+ ListView_SetItemCount(m_statuslist.getwnd(), itemCache[idx].Size);
+ if (itemCache[idx].Size > 0) ListView_RedrawItems(m_statuslist.getwnd(), 0, itemCache[idx].Size - 1);
+}
+static int m_last_trackpos;
+
+
+typedef struct _MEDIAINFO
+{
+ CHAR cLetter;
+ BOOL bInserted;
+ BOOL bRecordable;
+ BOOL bRewritable;
+ BOOL bBlank;
+ ULONG nSectorsFree;
+ ULONG nSectorsUsed;
+} MEDIAINFO;
+
+static HRESULT GetMediaInfoFromSonic(MEDIAINFO *pmi)
+{
+ HRESULT hr;
+
+ hr = S_OK;
+
+ char name[]= "cda://X.cda";
+ char buf2[64] = "";
+ char buf3[64] = "";
+
+ name[6] = pmi->cLetter;
+
+ pmi->bInserted = FALSE;
+ pmi->bRewritable = FALSE;
+ pmi->nSectorsFree = 0;
+ pmi->nSectorsUsed = 0;
+
+ pmi->bRecordable = TRUE;
+ getFileInfo(name, "cdtype", buf3, sizeof(buf3));
+ if (buf3[0] && 0 == lstrcmpA(buf3, "CDRW")) pmi->bRewritable = TRUE;
+
+ getFileInfo(name, "cdlengths", buf2, sizeof(buf2));
+ if (buf2[0])
+ {
+ pmi->bInserted = TRUE;
+ pmi->nSectorsFree = atoi(buf2);
+ }
+
+ return hr;
+}
+
+static void CALLBACK FreeAsyncParam(DM_NOTIFY_PARAM *phdr)
+{
+ switch(phdr->opCode)
+ {
+ case DMOP_IMAPIINFO:
+ break;
+ }
+ free(phdr);
+}
+static void FinishSetStatus(HWND hwndDlg, MEDIAINFO *pmi)
+{
+ int freesecs;
+ if(pmi->bInserted)
+ {
+ freesecs = (pmi->nSectorsFree * 2048) / (150 * 1024); //150kb/s as its considered DATA CD at this point in veritas
+ }
+ else
+ {
+ freesecs = 74 * 60; //Default to 74mns CDR
+ }
+
+ m_availsecs = freesecs;
+
+ int idx = LETTERTOINDEX(m_cdrom);
+ int usedlen = 0;
+ int truncpos = 0;
+ for (int i = 0;i < itemCache[idx].Size;i++)
+ {
+ usedlen += itemCache[idx].Items[i].length;
+ if (usedlen > m_availsecs)
+ truncpos++;
+ }
+ m_availsecs -= usedlen;
+
+ wchar_t status[256] = {0};
+ if (!pmi->bInserted)
+ WASABI_API_LNGSTRINGW_BUF(IDS_NO_BLANK_CDR_IN_DRIVE,status,512);
+ else
+ {
+ StringCchPrintf(status, 512, WASABI_API_LNGSTRINGW(IDS_X_CAPACITY_DETAILS),
+ (pmi->bRewritable) ? L"CD-RW" : L"CD-R" , freesecs / 60, freesecs % 60);
+ }
+
+ wchar_t temp[16] = {0};
+ StringCchPrintf(status + wcslen(status), 256,
+ WASABI_API_LNGSTRINGW(IDS_USED_X_X_TRACKS),
+ usedlen / 60,
+ usedlen % 60,
+ itemCache[idx].Size,
+ WASABI_API_LNGSTRINGW_BUF(itemCache[idx].Size == 1 ? IDS_TRACK : IDS_TRACKS,temp,16));
+
+ if (freesecs && pmi->bInserted)
+ {
+ if (m_availsecs >= 0) StringCchPrintf(status + wcslen(status), 256, WASABI_API_LNGSTRINGW(IDS_AVAILABLE_X_X),
+ m_availsecs / 60, m_availsecs % 60);
+ else StringCchPrintf(status + wcslen(status), 256, WASABI_API_LNGSTRINGW(IDS_X_OVER_CAPACITY_REMOVE_X_TRACKS),
+ -m_availsecs / 60, -m_availsecs % 60, truncpos);
+ }
+ SetDlgItemText(hwndDlg, IDC_CDINFO, status);
+ m_last_trackpos = -1;
+
+ m_is_cdrw = pmi->bRewritable;
+ ListView_RedrawItems(m_statuslist.getwnd(), 0, itemCache[idx].Size - 1);
+}
+
+static void SetStatus(HWND hwndDlg, CHAR cLetter)
+{
+ if (DM_MODE_BURNING == DriveManager_GetDriveMode(cLetter) &&
+ NULL != (m_burning_other_wnd = cdburn_FindBurningHWND(cLetter)))
+ {
+ prevWnd = (HWND)SendMessage(m_burning_other_wnd, WM_BURNUPDATEOWNER, 0, (LPARAM)hwndDlg);
+ if (prevWnd == hwndDlg) prevWnd = NULL;
+ DWORD state = (DWORD)(INT_PTR)SendMessage(m_burning_other_wnd, WM_BURNGETSTATUS, BURNSTATUS_STATE, 0);
+ if (state)
+ {
+ SetDlgItemText(hwndDlg, IDC_CDINFO, WASABI_API_LNGSTRINGW(IDS_BURNING));
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CLEAR), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_ADD), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_BURN), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_BURN_OPTS), SW_SHOWNA);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CANCEL_BURN), SW_SHOWNA);
+ SetDlgItemText(hwndDlg, IDC_CANCEL_BURN, WASABI_API_LNGSTRINGW(IDS_CANCEL_BURN));
+ m_availsecs = 0;
+ m_last_trackpos = -1;
+ m_is_cdrw = 0;
+ percentCompleted = 0;
+ UpdateWindow(hwndDlg);
+ }
+
+ SendMessage(hwndDlg, WM_BURNNOTIFY, BURN_STATECHANGED, state);
+ ShowWindow(m_burning_other_wnd, g_config->ReadInt(L"cdburnstatuswnd", 1) ? SW_SHOWNA : SW_HIDE);
+ PostMessage(m_burning_other_wnd, WM_BURNCONFIGCHANGED, BURNCFG_HIDEVIEW, !g_config->ReadInt(L"cdburnstatuswnd", 1));
+ PostMessage(m_burning_other_wnd, WM_BURNCONFIGCHANGED, BURNCFG_AUTOEJECT, g_config->ReadInt(L"cdburnautoeject", 1));
+ PostMessage(m_burning_other_wnd, WM_BURNCONFIGCHANGED, BURNCFG_ADDTODB, g_config->ReadInt(L"cdburnautoadddb", 0));
+ PostMessage(m_burning_other_wnd, WM_BURNCONFIGCHANGED, BURNCFG_AUTOCLOSE, g_config->ReadInt(L"cdburnautoclose", 0));
+ }
+ else
+ {
+ BOOL br;
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CLEAR), SW_SHOWNA);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_ADD), SW_SHOWNA);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_BURN), SW_SHOWNA);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_BURN_OPTS), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CANCEL_BURN), SW_HIDE);
+
+ SetDlgItemText(hwndDlg, IDC_CDINFO, WASABI_API_LNGSTRINGW(IDS_CALCULATING));
+
+ UpdateWindow(hwndDlg);
+
+ DM_IMAPI_PARAM *pIMAPI = (DM_IMAPI_PARAM*)calloc(1, sizeof(DM_IMAPI_PARAM));
+ if (pIMAPI)
+ {
+ pIMAPI->header.cLetter = cLetter;
+ pIMAPI->header.callback = (INT_PTR)hwndDlg;
+ pIMAPI->header.uMsg = WM_EX_OPCOMPLETED;
+ pIMAPI->header.fnFree = FreeAsyncParam;
+ pIMAPI->header.fFlags = DMF_QUERYMEDIATYPE | DMF_QUERYMEDIAINFO;
+ br = DriveManager_GetIMAPIInfo(pIMAPI);
+ }
+ else br = FALSE;
+ if (!br) SetDlgItemText(hwndDlg, IDC_CDINFO, WASABI_API_LNGSTRINGW(IDS_DISC_READ_ERROR));
+ }
+}
+
+static void deleteSelectedItems(HWND hwndDlg, CHAR cLetter)
+{
+ int idx = LETTERTOINDEX(cLetter);
+ for (int i = itemCache[idx].Size - 1;i >= 0;i--)
+ {
+ if (m_statuslist.GetSelected(i))
+ {
+ freeRecord(&itemCache[idx].Items[i]);
+ int l = itemCache[idx].Size - i - 1;
+ if (l > 0) memcpy(&itemCache[idx].Items[i], &itemCache[idx].Items[i + 1], sizeof(itemRecordW)*l);
+ itemCache[idx].Size--;
+ }
+ }
+ SetStatus(hwndDlg, cLetter);
+ refreshList();
+}
+
+static void selectAll()
+{
+ int l = m_statuslist.GetCount();
+ for (int i = 0;i < l;i++) m_statuslist.SetSelected(i);
+}
+
+static void playSelectedItems(HWND hwndDlg, int enqueue)
+{
+ int idx = LETTERTOINDEX(m_cdrom);
+ if (!enqueue) SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_DELETE);
+
+ for (int i = 0;i < itemCache[idx].Size;i++)
+ {
+ if (!m_statuslist.GetSelected(i)) continue;
+
+ //send the file to winamp
+ COPYDATASTRUCT cds;
+ cds.dwData = IPC_PLAYFILEW;
+ cds.lpData = (void *)itemCache[idx].Items[i].filename;
+ cds.cbData = (DWORD)(sizeof(wchar_t *) * (wcslen(itemCache[idx].Items[i].filename) + 1)); // include space for null char
+ SendMessageW(plugin.hwndWinampParent, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds);
+ }
+
+ if (!enqueue) SendMessageW(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_STARTPLAY);
+}
+BOOL CALLBACK CantBurnProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ wchar_t *message = (wchar_t *)lParam;
+
+ // due to quirks with the more common resource editors, is easier to just store the string
+ // internally only with \n and post-process to be \r\n (as here) so it will appear correctly
+ // on new lines as is wanted (silly multiline edit controls)
+ wchar_t tmp2[1024] = {0}, *t2 = tmp2;
+ while(message && *message && (t2 - tmp2 < 1024))
+ {
+ if(*message == L'\n')
+ {
+ *t2 = L'\r';
+ t2 = CharNextW(t2);
+ }
+ *t2 = *message;
+ message = CharNextW(message);
+ t2 = CharNextW(t2);
+ }
+
+ SetDlgItemText(hwnd, IDC_MESSAGE2, tmp2);
+ }
+ return 0;
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ EndDialog(hwnd, 0);
+ break;
+ case IDCANCEL:
+ EndDialog(hwnd, -1);
+ break;
+ }
+ }
+ return 0;
+}
+
+HRESULT ResolveShortCut(HWND hwnd, LPCSTR pszShortcutFile, LPSTR pszPath)
+{
+ HRESULT hres;
+ IShellLinkA* psl;
+ WIN32_FIND_DATAA wfd;
+
+ *pszPath = 0; // assume failure
+
+ hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ IID_IShellLinkA, (void **) & psl);
+ if (SUCCEEDED(hres))
+ {
+ IPersistFile* ppf;
+
+ hres = psl->QueryInterface(IID_IPersistFile, (void **) & ppf); // OLE 2! Yay! --YO
+ if (SUCCEEDED(hres))
+ {
+ wchar_t wsz[MAX_PATH] = {0};
+ MultiByteToWideCharSZ(CP_ACP, 0, pszShortcutFile, -1, wsz, MAX_PATH);
+
+ hres = ppf->Load(wsz, STGM_READ);
+ if (SUCCEEDED(hres))
+ {
+ hres = psl->Resolve(hwnd, SLR_ANY_MATCH);
+ if (SUCCEEDED(hres))
+ {
+ char szGotPath[MAX_PATH] = {0};
+ lstrcpynA(szGotPath, pszShortcutFile, MAX_PATH);
+ hres = psl->GetPath(szGotPath, MAX_PATH, (WIN32_FIND_DATAA *) & wfd,
+ SLGP_SHORTPATH);
+ lstrcpynA(pszPath, szGotPath, MAX_PATH);
+ }
+ }
+ ppf->Release();
+ }
+ psl->Release();
+ }
+ return SUCCEEDED(hres);
+}
+
+HRESULT ResolveShortCut(HWND hwnd, LPCWSTR pszShortcutFile, LPWSTR pszPath)
+{
+ HRESULT hres;
+ IShellLinkW* psl;
+ WIN32_FIND_DATAW wfd;
+
+ *pszPath = 0; // assume failure
+
+ hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ IID_IShellLinkW, (void **) & psl);
+ if (SUCCEEDED(hres))
+ {
+ IPersistFile* ppf;
+
+ hres = psl->QueryInterface(IID_IPersistFile, (void **) & ppf); // OLE 2! Yay! --YO
+ if (SUCCEEDED(hres))
+ {
+ /*wchar_t wsz[MAX_PATH] = {0};
+ MultiByteToWideCharSZ(CP_ACP, 0, pszShortcutFile, -1, wsz, MAX_PATH);*/
+
+ hres = ppf->Load(pszShortcutFile/*wsz*/, STGM_READ);
+ if (SUCCEEDED(hres))
+ {
+ hres = psl->Resolve(hwnd, SLR_ANY_MATCH);
+ if (SUCCEEDED(hres))
+ {
+ wchar_t szGotPath[MAX_PATH] = {0};
+ wcsncpy(szGotPath, pszShortcutFile, MAX_PATH);
+ hres = psl->GetPath(szGotPath, MAX_PATH, (WIN32_FIND_DATAW *) & wfd,
+ SLGP_SHORTPATH);
+ wcsncpy(pszPath, szGotPath, MAX_PATH);
+ }
+ }
+ ppf->Release();
+ }
+ psl->Release();
+ }
+ return SUCCEEDED(hres);
+}
+
+static int checkFile(const char *file)
+{
+ //check if the file is supported by winamp
+ const char *ext = extension(file);
+ if (!ext || !ext[0]) return 0;
+ if (strstr(file, "://") && !strstr(file, "cda://")) return 0;
+
+#if 0 // benski> this would be neat to have, but will fail with unicode filenames (which in_mp3 can open anyway)... TODO: make it workable later
+ HANDLE hFile = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (INVALID_HANDLE_VALUE == hFile && GetLastError() != ERROR_FILE_NOT_FOUND)
+ {
+ wchar_t message[1024] = {0};
+ StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_FILE_X_CANNOT_BE_BURNED_REASON_NOT_FOUND), AutoWide(file), AutoWide(ext));
+ return WASABI_API_DIALOGBOXPARAM(IDD_NOBURN, g_hwnd, CantBurnProc, (LPARAM)message);
+ }
+ CloseHandle(hFile);
+#endif
+
+ char *m_extlist = (char*)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_EXTLIST);
+ {
+ int found = 0;
+ char *a = m_extlist;
+ while (a && *a)
+ {
+ if (!lstrcmpiA(a, ext))
+ {
+ found = 1;
+ break;
+ }
+ a += lstrlenA(a) + 1;
+ }
+ GlobalFree((HGLOBAL)m_extlist);
+ if (!found)
+ {
+ wchar_t message[1024] = {0};
+ StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_FILE_X_CANNOT_BE_BURNED_REASON_FILETYPE_NOT_REGISTERED), AutoWide(file), AutoWide(ext));
+ return (int)(INT_PTR)WASABI_API_DIALOGBOXPARAM(IDD_NOBURN, plugin.hwndLibraryParent, CantBurnProc, (LPARAM)message);
+ }
+ }
+
+ //check for type
+ char tmp[64] = {0, };
+ getFileInfo(file, "type", tmp, sizeof(tmp) - 1);
+ if (tmp[0] && tmp[0] != '0')
+ {
+ wchar_t message[1024], temp[128] = {0};
+ StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_FILE_X_CANNOT_BE_BURNED_REASON_X),AutoWide(file),
+ WASABI_API_LNGSTRINGW_BUF((tmp[0] == '1' ? IDS_VIDEO_FILES_CANNOT_BE_BURNED : IDS_NOT_AN_AUDIO_FILE),temp,128));
+ return (int)(INT_PTR)WASABI_API_DIALOGBOXPARAM(IDD_NOBURN, plugin.hwndLibraryParent, CantBurnProc, (LPARAM)message);
+
+ }
+
+ // note: this check is NOT meant as any sort of protection.. It simply saves the user the hassle of an error later
+ if (getFileInfo(file, "burnable", tmp, 64) // most plugins don't support this extended file info, so failure is OK
+ && tmp[0] == '0') // if it does support it, then we can check whether or not it's burnable
+ {
+ wchar_t message[1024] = {0};
+ StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_FILE_CANNOT_BE_BURNED), AutoWide(file));
+ if (getFileInfo(file, "noburnreason", tmp, 64))
+ {
+ StringCchPrintfW(message, 1024,
+ WASABI_API_LNGSTRINGW(IDS_FILE_X_CANNOT_BE_BURNED_REASON_X),
+ AutoWide(file), AutoWide(tmp));
+ }
+ return (int)(INT_PTR)WASABI_API_DIALOGBOXPARAM(IDD_NOBURN, plugin.hwndLibraryParent, CantBurnProc, (LPARAM)message);
+ }
+
+ return 1;
+}
+
+
+static int checkFile(const wchar_t *file)
+{
+ //check if the file is supported by winamp
+ const wchar_t *ext = PathFindExtension(file);
+ if (!ext || !ext[0]) return 0;
+ ext++;
+ if (wcsstr(file, L"://") && !wcsstr(file, L"cda://")) return 0;
+
+#if 0 // benski> this would be neat to have, but will fail with unicode filenames (which in_mp3 can open anyway)... TODO: make it workable later
+ HANDLE hFile = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (INVALID_HANDLE_VALUE == hFile && GetLastError() == ERROR_FILE_NOT_FOUND)
+ {
+ wchar_t message[1024] = {0};
+ StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_FILE_X_CANNOT_BE_BURNED_REASON_NOT_FOUND), file, ext);
+ return WASABI_API_DIALOGBOXPARAM(IDD_NOBURN, g_hwnd, CantBurnProc, (LPARAM)message);
+ }
+ CloseHandle(hFile);
+#endif
+ wchar_t *m_extlist = (wchar_t*)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_EXTLISTW);
+ {
+ int found = 0;
+ wchar_t *a = m_extlist;
+ while (a && *a)
+ {
+ if (!lstrcmpiW(a, ext))
+ {
+ found = 1;
+ break;
+ }
+ a += lstrlenW(a) + 1;
+ }
+ GlobalFree((HGLOBAL)m_extlist);
+ if (!found)
+ {
+ wchar_t message[1024] = {0};
+ StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_FILE_X_CANNOT_BE_BURNED_REASON_FILETYPE_NOT_REGISTERED), file, ext);
+ return (int)(INT_PTR)WASABI_API_DIALOGBOXPARAM(IDD_NOBURN, plugin.hwndLibraryParent, CantBurnProc, (LPARAM)message);
+ }
+ }
+
+ //check for type
+ wchar_t tmp[64] = {0, };
+ getFileInfoW(file, L"type", tmp, 64);
+ if (tmp[0] && tmp[0] != '0')
+ {
+ wchar_t message[1024] = {0}, temp[128] = {0};
+ StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_FILE_X_CANNOT_BE_BURNED_REASON_X), file,
+ WASABI_API_LNGSTRINGW_BUF((tmp[0] == '1' ? IDS_VIDEO_FILES_CANNOT_BE_BURNED : IDS_NOT_AN_AUDIO_FILE),temp,128));
+ return (int)(INT_PTR)WASABI_API_DIALOGBOXPARAM(IDD_NOBURN, plugin.hwndLibraryParent, CantBurnProc, (LPARAM)message);
+
+ }
+
+ // note: this check is NOT meant as any sort of protection.. It simply saves the user the hassle of an error later
+ if (getFileInfoW(file, L"burnable", tmp, 64) // most plugins don't support this extended file info, so failure is OK
+ && tmp[0] == '0') // if it does support it, then we can check whether or not it's burnable
+ {
+ wchar_t message[1024] = {0};
+ StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_FILE_CANNOT_BE_BURNED), file);
+ if (getFileInfoW(file, L"noburnreason", tmp, 64))
+ {
+ StringCchPrintfW(message, 1024,
+ WASABI_API_LNGSTRINGW(IDS_FILE_X_CANNOT_BE_BURNED_REASON_X),
+ file, tmp);
+ }
+ return (int)(INT_PTR)WASABI_API_DIALOGBOXPARAM(IDD_NOBURN, plugin.hwndLibraryParent, CantBurnProc, (LPARAM)message);
+ }
+
+ return 1;
+}
+
+
+void cdburn_clearBurner(char driveletter)
+{
+ emptyRecordList(&itemCache[LETTERTOINDEX(driveletter)]);
+}
+void cdburn_addfile(char* file, std::vector<GayString*> *files, api_playlistmanager* plManager, ifc_playlistloadercallback *plCB);
+void cdburn_addfile(wchar_t* file, std::vector<GayStringW*> *files, api_playlistmanager* plManager, ifc_playlistloadercallback *plCB);
+void cdburn_addfolder(const char* folder, std::vector<GayString*> *files, api_playlistmanager* plManager, ifc_playlistloadercallback *plCB);
+void cdburn_addfolder(const wchar_t* folder, std::vector<GayStringW*> *files, api_playlistmanager* plManager, ifc_playlistloadercallback *plCB);
+
+void cdburn_appendFile(char *file, char cLetter)
+{
+ std::vector<GayString*> files;
+ waServiceFactory *plmFactory = plugin.service->service_getServiceByGuid(api_playlistmanagerGUID);
+ api_playlistmanager *plManager = (plmFactory) ? (api_playlistmanager*)plmFactory->getInterface() : NULL;
+
+ int idx = LETTERTOINDEX(cLetter);
+ int validFile = 1;
+
+ if (itemCache[idx].Size > 255) return;
+ itemRecordListW newItems = {0, 0, 0};
+ PLCallBack plCB;
+ plCB.fileList = &files;
+
+ cdburn_addfile(file, &files, (api_playlistmanager*)plManager, (ifc_playlistloadercallback*)&plCB);
+
+ size_t x;
+
+ for (x = 0; x < files.size(); x ++) // temp record . replace it !!!
+ {
+ char *fn = files.at(x)->Get();
+
+ validFile = checkFile(fn);
+ // can't use switch here cause break won't work
+ if (validFile == -1) // bad file and user cancelled
+ break;
+ if (validFile) // bad file, user skipped
+ {
+ allocRecordList(&newItems, newItems.Size + 1);
+ if (!newItems.Alloc) break;
+
+ char title[2048] = {0};
+ basicFileInfoStruct bfis = {fn, 0, 0, title, sizeof(title) - 1,};
+ SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&bfis, IPC_GET_BASIC_FILE_INFO);
+ if (bfis.length > 0)
+ {
+ memset((void *)&(newItems.Items[newItems.Size]), 0, sizeof(itemRecordW));
+ newItems.Items[newItems.Size].filename = AutoWideDup(fn);
+ newItems.Items[newItems.Size].title = AutoWideDup(title);
+ newItems.Items[newItems.Size].length = bfis.length;
+ newItems.Size++;
+ }
+ }
+ delete(files.at(x)->Get());
+ }
+
+ if (validFile != -1)
+ copyRecordList(&itemCache[idx], &newItems);
+ refreshList();
+ if (m_hwndstatus) SetStatus(m_hwndstatus, cLetter);
+ if (plManager) plmFactory->releaseInterface(plManager);
+}
+
+void cdburn_appendFile(wchar_t *file, char cLetter)
+{
+ std::vector<GayStringW*> files;
+ waServiceFactory *plmFactory = plugin.service->service_getServiceByGuid(api_playlistmanagerGUID);
+ api_playlistmanager *plManager = (plmFactory) ? (api_playlistmanager*)plmFactory->getInterface() : NULL;
+
+ int idx = LETTERTOINDEX(cLetter);
+ int validFile = 1;
+
+ if (itemCache[idx].Size > 255) return;
+ itemRecordListW newItems = {0, 0, 0};
+ PLCallBackW plCB;
+ plCB.fileList = &files;
+
+ cdburn_addfile(file, &files, (api_playlistmanager*)plManager, (ifc_playlistloadercallback*)&plCB);
+
+ size_t x;
+
+ for (x = 0; x < files.size(); x ++) // temp record . replace it !!!
+ {
+ const wchar_t *fn = files.at(x)->Get();
+
+ validFile = checkFile(fn);
+ // can't use switch here cause break won't work
+ if (validFile == -1) // bad file and user cancelled
+ break;
+ if (validFile) // bad file, user skipped
+ {
+ allocRecordList(&newItems, newItems.Size + 1);
+ if (!newItems.Alloc) break;
+
+ wchar_t title[2048] = {0};
+ basicFileInfoStructW bfis = {fn, 0, 0, title, ARRAYSIZE(title) - 1,};
+ SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&bfis, IPC_GET_BASIC_FILE_INFOW);
+ if (bfis.length > 0)
+ {
+ memset((void *)&(newItems.Items[newItems.Size]), 0, sizeof(itemRecordW));
+ newItems.Items[newItems.Size].filename = _wcsdup(fn);
+ newItems.Items[newItems.Size].title = _wcsdup(title);
+ newItems.Items[newItems.Size].length = bfis.length;
+ newItems.Size++;
+ }
+ }
+ delete(files.at(x)->Get());
+ }
+
+ if (validFile != -1)
+ copyRecordList(&itemCache[idx], &newItems);
+ refreshList();
+ if (m_hwndstatus) SetStatus(m_hwndstatus, cLetter);
+ if (plManager) plmFactory->releaseInterface(plManager);
+}
+void cdburn_addfile(char* file, std::vector<GayString*> *files, api_playlistmanager* plManager, ifc_playlistloadercallback *plCB)
+{
+ if (!_stricmp(extension(file), "lnk"))
+ {
+ char temp2[MAX_PATH] = {0};
+ if (ResolveShortCut(plugin.hwndLibraryParent, file, temp2)) lstrcpynA(file, temp2, MAX_PATH);
+ else return;
+ }
+
+ if (!_strnicmp(file, "cda://", 6))
+ {
+ if (strlen(file) == 7)
+ {
+ int n = 0;
+ char buf2[32] = {0};
+ getFileInfo(file, "ntracks", buf2, sizeof(buf2));
+ n = atoi(buf2);
+ if (n > 0 && n < 256)
+ {
+ for (int x = 0; x < n; x ++)
+ {
+ char s[64] = {0};
+ StringCchPrintfA(s, 64, "%s,%d.cda", file, x + 1);
+ files->push_back(new GayString(s));
+ }
+ }
+ }
+ else files->push_back(new GayString(file));
+ }
+
+ else if (strstr(file, "://"))
+ {
+ if (plManager && PLAYLISTMANAGER_SUCCESS != plManager->Load(AutoWide(file), plCB))
+ {
+ files->push_back(new GayString(file));
+ }
+ }
+ else if (!lstrcmpA(file + 1, ":\\"))
+ {
+ cdburn_addfolder(file, files, plManager, plCB);
+ }
+ else
+ {
+ WIN32_FIND_DATAA d = {0};
+ HANDLE h = FindFirstFileA(file, &d);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ FindClose(h);
+ if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ cdburn_addfolder(file, files, plManager, plCB);
+ }
+ if (plManager && PLAYLISTMANAGER_SUCCESS != plManager->Load(AutoWide(file), plCB))
+ {
+ files->push_back(new GayString(file));
+ }
+ }
+ else files->push_back(new GayString(file));
+ }
+}
+void cdburn_addfile(wchar_t* file, std::vector<GayStringW*> *files, api_playlistmanager* plManager, ifc_playlistloadercallback *plCB)
+{
+ if (!_wcsicmp(extensionW(file), L"lnk"))
+ {
+ wchar_t temp2[MAX_PATH] = {0};
+ if (ResolveShortCut(plugin.hwndLibraryParent, file, temp2)) lstrcpyn(file, temp2, MAX_PATH);
+ else return;
+ }
+
+ if (!_wcsnicmp(file, L"cda://", 6))
+ {
+ if (wcslen(file) == 7)
+ {
+ int n = 0;
+ wchar_t buf2[32] = {0};
+ getFileInfoW(file, L"ntracks", buf2, sizeof(buf2));
+ n = _wtoi(buf2);
+ if (n > 0 && n < 256)
+ {
+ for (int x = 0; x < n; x ++)
+ {
+ wchar_t s[64] = {0};
+ StringCchPrintfW(s, 64, L"%s,%d.cda", file, x + 1);
+ files->push_back(new GayStringW(s));
+ }
+ }
+ }
+ else files->push_back(new GayStringW(file));
+ }
+
+ else if (wcsstr(file, L"://"))
+ {
+ if (plManager && PLAYLISTMANAGER_SUCCESS != plManager->Load(file, plCB))
+ {
+ files->push_back(new GayStringW(file));
+ }
+ }
+ else if (!lstrcmpW(file + 1, L":\\"))
+ {
+ cdburn_addfolder(file, files, plManager, plCB);
+ }
+ else
+ {
+ WIN32_FIND_DATAW d = {0};
+ HANDLE h = FindFirstFileW(file, &d);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ FindClose(h);
+ if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ cdburn_addfolder(file, files, plManager, plCB);
+ }
+ if (plManager && PLAYLISTMANAGER_SUCCESS != plManager->Load(file, plCB))
+ {
+ files->push_back(new GayStringW(file));
+ }
+ }
+ else files->push_back(new GayStringW(file));
+ }
+}
+void cdburn_addfolder(const char* folder, std::vector<GayString*> *files, api_playlistmanager* plManager, ifc_playlistloadercallback *plCB)
+{
+ WIN32_FIND_DATAA d = {0};
+ char path[MAX_PATH] = {0};
+ PathCombineA(path, folder, "*");
+
+ HANDLE h = FindFirstFileA(path, &d);
+ if (h == INVALID_HANDLE_VALUE) return;
+ do
+ {
+ if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ if (0 == lstrcmpA(d.cFileName, ".") || 0 == lstrcmpA(d.cFileName, "..")) continue;
+ GayString pathNew(folder);
+ pathNew.Append("\\");
+ pathNew.Append(d.cFileName);
+ cdburn_addfolder(pathNew.Get(), files, plManager, plCB);
+ }
+ else
+ {
+ GayString file(folder);
+ file.Append("\\");
+ file.Append(d.cFileName);
+ cdburn_addfile(file.Get(), files, plManager, plCB);
+ }
+ }
+ while (FindNextFileA(h, &d));
+ FindClose(h);
+}
+void cdburn_addfolder(const wchar_t* folder, std::vector<GayStringW*> *files, api_playlistmanager* plManager, ifc_playlistloadercallback *plCB)
+{
+ WIN32_FIND_DATAW d = {0};
+ wchar_t path[MAX_PATH] = {0};
+ PathCombineW(path, folder, L"*");
+
+ HANDLE h = FindFirstFileW(path, &d);
+ if (h == INVALID_HANDLE_VALUE) return;
+ do
+ {
+ if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ if (0 == lstrcmpW(d.cFileName, L".") || 0 == lstrcmpW(d.cFileName, L"..")) continue;
+ GayStringW pathNew(folder);
+ pathNew.Append(L"\\");
+ pathNew.Append(d.cFileName);
+ cdburn_addfolder(pathNew.Get(), files, plManager, plCB);
+ }
+ else
+ {
+ GayStringW file(folder);
+ file.Append(L"\\");
+ file.Append(d.cFileName);
+ cdburn_addfile((wchar_t*)file.Get(), files, plManager, plCB);
+ }
+ }
+ while (FindNextFileW(h, &d));
+ FindClose(h);
+}
+void cdburn_appendItemRecord(itemRecordList *obj, char cLetter)
+{
+ int idx = LETTERTOINDEX(cLetter);
+ int validFile = 1;
+ itemRecordListW newItems = {0, 0, 0};
+ BurnAddStatus_Create(obj->Size);
+
+ for (int i = 0;i < obj->Size;i++)
+ {
+ validFile = checkFile(obj->Items[i].filename);
+ if (validFile == -1)
+ break;
+ if (validFile)
+ {
+ if (newItems.Size > 255) break;
+
+ allocRecordList(&newItems, newItems.Size + 1);
+ if (!newItems.Alloc) return ;
+
+ memset((void *)&(newItems.Items[newItems.Size]), 0, sizeof(itemRecordW));
+ newItems.Items[newItems.Size].filename = AutoWideDup(obj->Items[i].filename);
+
+ GayString title;
+ if (obj->Items[i].artist) title.Append(obj->Items[i].artist);
+ if (title.Get() && title.Get()[0] && obj->Items[i].title && obj->Items[i].title[0])
+ title.Append(" - ");
+ if (obj->Items[i].title) title.Append(obj->Items[i].title);
+
+ newItems.Items[newItems.Size].title = AutoWideDup(title.Get());
+
+ newItems.Items[newItems.Size].length = obj->Items[i].length;
+ newItems.Size++;
+ BurnAddStatus_Step(&newItems);
+ }
+ }
+ BurnAddStatus_Done();
+ if (validFile != -1)
+ copyRecordList(&itemCache[idx], &newItems);
+ refreshList();
+ if (m_hwndstatus) SetStatus(m_hwndstatus, cLetter);
+}
+
+void cdburn_appendItemRecord(itemRecordListW *obj, char cLetter)
+{
+ int idx = LETTERTOINDEX(cLetter);
+ int validFile = 1;
+ itemRecordListW newItems = {0, 0, 0};
+ BurnAddStatus_Create(obj->Size);
+
+ for (int i = 0;i < obj->Size;i++)
+ {
+ validFile = checkFile(obj->Items[i].filename);
+ if (validFile == -1)
+ break;
+ if (validFile)
+ {
+ if (newItems.Size > 255) break;
+
+ allocRecordList(&newItems, newItems.Size + 1);
+ if (!newItems.Alloc) return ;
+
+ memset((void *)&(newItems.Items[newItems.Size]), 0, sizeof(itemRecordW));
+ newItems.Items[newItems.Size].filename = _wcsdup(obj->Items[i].filename);
+
+ GayStringW title;
+ if (obj->Items[i].artist) title.Append(obj->Items[i].artist);
+ if (title.Get() && title.Get()[0] && obj->Items[i].title && obj->Items[i].title[0])
+ title.Append(L" - ");
+ if (obj->Items[i].title) title.Append(obj->Items[i].title);
+
+ newItems.Items[newItems.Size].title = _wcsdup(title.Get());
+
+ newItems.Items[newItems.Size].length = obj->Items[i].length;
+ newItems.Size++;
+ BurnAddStatus_Step(&newItems);
+ }
+ }
+ BurnAddStatus_Done();
+ if (validFile != -1)
+ copyRecordList(&itemCache[idx], &newItems);
+ refreshList();
+ if (m_hwndstatus) SetStatus(m_hwndstatus, cLetter);
+}
+
+static void Shell_Free(void *p)
+{
+ IMalloc *m;
+ SHGetMalloc(&m);
+ m->Free(p);
+}
+
+HWND cdburn_FindBurningHWND(char cLetter)
+{
+ HWND h = 0;
+ while (NULL != (h = FindWindowExW(NULL, h, L"#32770", NULL)))
+ {
+ if (!GetPropW(h, L"WABURNER")) continue;
+ if (((char)(INT_PTR)GetPropW(h, L"DRIVE")) == cLetter) return h;
+ }
+ return NULL;
+}
+
+CHAR cdburn_IsMeBurning(void)
+{
+ if (pidBurner)
+ {
+ HWND h = NULL;
+ DWORD pid;
+ while (NULL != (h = FindWindowExW(NULL, h, L"#32770", NULL)))
+ {
+ if (GetPropW(h, L"WABURNER") && GetWindowThreadProcessId(h, &pid) && pid == pidBurner)
+ return (CHAR)(INT_PTR)GetPropW(h, L"DRIVE");
+ }
+ }
+ return 0;
+}
+
+static void NotifyInfoWindow(HWND hwnd, LPCWSTR pszFileName, BOOL bForceRefresh)
+{
+ HWND hwndParent;
+ hwndParent = GetParent(hwnd);
+ if (hwndParent) SendMessageW(hwndParent, WM_SHOWFILEINFO,
+ (WPARAM)((bForceRefresh) ? WISF_FORCE : WISF_NORMAL),
+ (LPARAM)pszFileName);
+}
+
+static void moveSelItemsUp()
+{
+ if (DM_MODE_BURNING == DriveManager_GetDriveMode(m_cdrom)) return;
+
+ int idx = LETTERTOINDEX(m_cdrom);
+ for (int i = 0;i < itemCache[idx].Size;i++)
+ {
+ if (m_statuslist.GetSelected(i))
+ {
+ //swap the 2 items
+ if (i > 0)
+ {
+ itemRecordW tmp = itemCache[idx].Items[i];
+ itemCache[idx].Items[i] = itemCache[idx].Items[i - 1];
+ itemCache[idx].Items[i - 1] = tmp;
+ ListView_SetItemState(m_statuslist.getwnd(), i - 1, LVIS_SELECTED, LVIS_SELECTED);
+ ListView_SetItemState(m_statuslist.getwnd(), i, 0, LVIS_SELECTED);
+ ListView_RedrawItems(m_statuslist.getwnd(), i - 1, i);
+ if (ListView_GetItemState(m_statuslist.getwnd(), i, LVIS_FOCUSED))
+ {
+ ListView_SetItemState(m_statuslist.getwnd(), i - 1, LVIS_FOCUSED, LVIS_FOCUSED);
+ }
+ }
+ }
+ }
+}
+
+static void moveSelItemsDown()
+{
+ if (DM_MODE_BURNING == DriveManager_GetDriveMode(m_cdrom)) return ;
+
+ int idx = LETTERTOINDEX(m_cdrom);
+ for (int i = itemCache[idx].Size - 1;i >= 0;i--)
+ {
+ if (m_statuslist.GetSelected(i))
+ {
+ //swap the 2 items
+ if (i < (itemCache[idx].Size - 1))
+ {
+ itemRecordW tmp = itemCache[idx].Items[i];
+ itemCache[idx].Items[i] = itemCache[idx].Items[i + 1];
+ itemCache[idx].Items[i + 1] = tmp;
+ ListView_SetItemState(m_statuslist.getwnd(), i + 1, LVIS_SELECTED, LVIS_SELECTED);
+ ListView_SetItemState(m_statuslist.getwnd(), i, 0, LVIS_SELECTED);
+ ListView_RedrawItems(m_statuslist.getwnd(), i, i + 1);
+ if (ListView_GetItemState(m_statuslist.getwnd(), i, LVIS_FOCUSED))
+ {
+ ListView_SetItemState(m_statuslist.getwnd(), i + 1, LVIS_FOCUSED, LVIS_FOCUSED);
+ }
+ }
+ }
+ }
+}
+
+
+int g_burn_hack_startburn;
+
+void OnBurnDlgInit(HWND hwndDlg, LPARAM lParam)
+{
+ m_hwndstatus = hwndDlg;
+ m_is_cdrw = 0;
+ m_dragging = 0;
+
+ m_cdrom = (char)lParam;
+
+ SendMessageW(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(IDC_BTN_SHOWINFO, BN_EX_GETTEXT), (LPARAM)GetDlgItem(hwndDlg, IDC_BTN_SHOWINFO));
+
+ m_statuslist.setwnd(GetDlgItem(hwndDlg, IDC_LIST2));
+ m_statuslist.AddCol(WASABI_API_LNGSTRINGW(IDS_TRACK_NUMBER), g_view_metaconf->ReadInt(L"col_track", 60));
+ m_statuslist.AddCol(WASABI_API_LNGSTRINGW(IDS_TITLE), g_view_metaconf->ReadInt(L"col_title", 200));
+ m_statuslist.AddCol(WASABI_API_LNGSTRINGW(IDS_LENGTH), g_view_metaconf->ReadInt(L"col_len", 80));
+ m_statuslist.AddCol(WASABI_API_LNGSTRINGW(IDS_STATUS), g_view_metaconf->ReadInt(L"col_status", 200));
+
+ childSizer.Init(hwndDlg, burnwnd_rlist, sizeof(burnwnd_rlist) / sizeof(burnwnd_rlist[0]));
+
+ if(m_statuslist.getwnd())
+ {
+ MLSKINWINDOW sw;
+
+ sw.hwndToSkin = m_statuslist.getwnd();
+ sw.skinType = SKINNEDWND_TYPE_LISTVIEW;
+ sw.style = SWLVS_FULLROWSELECT | SWLVS_DOUBLEBUFFER | SWLVS_ALTERNATEITEMS | SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS;
+ MLSkinWindow(plugin.hwndLibraryParent, &sw);
+ }
+
+ refreshList();
+
+ // this will make sure that we've got the cddb logo shown even when using a localised version
+ HANDLE hPrev = (HANDLE) SendDlgItemMessage(hwndDlg,IDC_LOGO,STM_SETIMAGE,IMAGE_BITMAP,
+ (LPARAM)LoadImage(plugin.hDllInstance,MAKEINTRESOURCE(IDB_LISTITEM_CDDRIVE),
+ IMAGE_BITMAP,0,0, 0));
+ if (hPrev) DeleteObject(hPrev);
+
+ NotifyInfoWindow(hwndDlg, NULL, TRUE); // ignore cache
+ SetStatus(hwndDlg, m_cdrom);
+
+ if (g_burn_hack_startburn)
+ {
+ g_burn_hack_startburn = 0;
+ PostMessage(hwndDlg, WM_COMMAND, IDC_BURN, 0);
+ }
+
+}
+
+void OnBurnNotify(HWND hwndDlg, DWORD notification, DWORD param)
+{
+ switch (notification)
+ {
+ case BURN_READY:
+ SetStatus(hwndDlg, m_cdrom);
+ break;
+ case BURN_STATECHANGED:
+ {
+ wchar_t title[512] = {0};
+ const wchar_t *buf = NULL;
+ switch (param)
+ {
+ case BURNERPLAYLIST_BURNCANCELING:
+ SetDlgItemText(hwndDlg, IDC_CANCEL_BURN, WASABI_API_LNGSTRINGW(IDS_CANCELLING));
+ buf = WASABI_API_LNGSTRINGW_BUF(IDS_BURNING_AUDIO_CANCELLING,title,512);
+ break;
+ case BURNERPLAYLIST_BURNFINISHING:
+ buf = WASABI_API_LNGSTRINGW_BUF(IDS_BURNING_AUDIO_FINISHING,title,512);
+ break;
+ case BURNERPLAYLIST_DECODEFINISHED:
+ buf = WASABI_API_LNGSTRINGW_BUF(IDS_BURNING_AUDIO_DATA_PREP_FINISHED,title,512);
+ break;
+ case BURNERPLAYLIST_LICENSINGSTARTING:
+ buf = WASABI_API_LNGSTRINGW_BUF(IDS_BURNING_AUDIO_VERIFYING_FILES,title,512);
+ break;
+ case BURNERPLAYLIST_LICENSINGFINISHED:
+ buf = WASABI_API_LNGSTRINGW_BUF(IDS_BURNING_AUDIO_VERIFICATION_COMPLETED,title,512);
+ break;
+ case BURNERPLAYLIST_BURNPROGRESS:
+ wchar_t buf2[256] = {0};
+ switch (SendMessage(m_burning_other_wnd, WM_BURNGETSTATUS, BURNSTATUS_ERROR, 0))
+ {
+ case BURNERPLAYLIST_WRITELEADIN:
+ buf = WASABI_API_LNGSTRINGW_BUF(IDS_OPENING_DISC_WRITING_LEAD_IN,buf2,256);
+ break;
+ case BURNERPLAYLIST_WRITELEADOUT:
+ buf = WASABI_API_LNGSTRINGW_BUF(IDS_CLOSING_DISC_WRITING_LEAD_OUT,buf2,256);
+ break;
+ default: break;
+ }
+ if (buf)
+ {
+ int percent = (int)(INT_PTR)SendMessage(m_burning_other_wnd, WM_BURNGETSTATUS, BURNSTATUS_PROGRESS, 0);
+ percentCompleted = max(percent, percentCompleted);
+ StringCchPrintf(title, 512, WASABI_API_LNGSTRINGW(IDS_BURNING_AUDIO_CURRENT_OPERATION), percentCompleted, buf);
+ }
+ break;
+ }
+ if (buf) SetDlgItemText(hwndDlg, IDC_CDINFO, title);
+ }
+ break;
+ case BURN_ITEMSTATECHANGED:
+ ListView_RedrawItems(m_statuslist.getwnd(), param, param);
+ break;
+ case BURN_ITEMDECODEPROGRESS:
+ ListView_RedrawItems(m_statuslist.getwnd(), param, param);
+ {
+ wchar_t title[512] = {0};
+ int percent = (int)(INT_PTR)SendMessage(m_burning_other_wnd, WM_BURNGETSTATUS, BURNSTATUS_PROGRESS, 0);
+ percentCompleted = max(percent, percentCompleted);
+ StringCchPrintf(title, 512, WASABI_API_LNGSTRINGW(IDS_BURNING_AUDIO_CD_PREP_DATA), percentCompleted);
+ SetDlgItemText(hwndDlg, IDC_CDINFO, title);
+ }
+ break;
+ case BURN_ITEMBURNPROGRESS:
+ ListView_RedrawItems(m_statuslist.getwnd(), param, param);
+ {
+ wchar_t title[512] = {0};
+ int percent = (int)(INT_PTR)SendMessage(m_burning_other_wnd, WM_BURNGETSTATUS, BURNSTATUS_PROGRESS, 0);
+ percentCompleted = max(percent, percentCompleted);
+ StringCchPrintf(title, 512, WASABI_API_LNGSTRINGW(IDS_BURNING_AUDIO_BURNING_DATA), percentCompleted);
+ SetDlgItemText(hwndDlg, IDC_CDINFO, title);
+ }
+ break;
+ case BURN_WORKING:
+ ListView_RedrawItems(m_statuslist.getwnd(), 0, ListView_GetItemCount(m_statuslist.getwnd()));
+ break;
+ case BURN_FINISHED:
+ {
+ wchar_t buf1[128] = {0}, closeStr[16] = {0};
+ GetDlgItemText(hwndDlg, IDC_CANCEL_BURN, buf1, ARRAYSIZE(buf1));
+ if (lstrcmpi(buf1, WASABI_API_LNGSTRINGW_BUF(IDS_CLOSE,closeStr,16)))
+ SetDlgItemText(hwndDlg, IDC_CANCEL_BURN, closeStr);
+ wchar_t buf[128] = {0};
+ switch (param)
+ {
+ case BURNERPLAYLIST_SUCCESS:
+ WASABI_API_LNGSTRINGW_BUF(IDS_AUDIO_CD_BURNED_SUCCESSFULLY,buf,128);
+ break;
+ case BURNERPLAYLIST_ABORTED:
+ WASABI_API_LNGSTRINGW_BUF(IDS_BURN_ABORTED_BY_USER,buf,128);
+ break;
+ default:
+ WASABI_API_LNGSTRINGW_BUF(IDS_BURNING_FAILED,buf,128);
+ break;
+ }
+ StringCchPrintf(buf1, 128, WASABI_API_LNGSTRINGW(IDS_BURNING_COMPLETED_STATUS_X), buf);
+ SetDlgItemText(hwndDlg, IDC_CDINFO, buf1);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BURN), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_BURN), TRUE);
+ }
+ break;
+ case BURN_DESTROYED:
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BURN), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_BURN), TRUE);
+ m_burning_other_wnd = NULL;
+ SetStatus(hwndDlg, m_cdrom);
+ break;
+ case BURN_CONFIGCHANGED:
+ switch (LOWORD(param))
+ {
+ case BURNCFG_AUTOCLOSE:
+ g_config->WriteInt(L"cdburnautoclose", HIWORD(param));
+ break;
+ case BURNCFG_AUTOEJECT:
+ g_config->WriteInt(L"cdburnautoeject", HIWORD(param));
+ break;
+ case BURNCFG_ADDTODB:
+ g_config->WriteInt(L"cdburnautoadddb", HIWORD(param));
+ break;
+ case BURNCFG_HIDEVIEW:
+ g_config->WriteInt(L"cdburnstatuswnd", !HIWORD(param));
+ break;
+ }
+ break;
+ }
+}
+
+static int CALLBACK WINAPI BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
+{
+ switch (uMsg)
+ {
+ case BFFM_INITIALIZED:
+ {
+ SendMessageW(hwnd, BFFM_SETSELECTIONW, 1, (LPARAM)WASABI_API_APP->path_getWorkingPath());
+
+ // this is not nice but it fixes the selection not working correctly on all OSes
+ EnumChildWindows(hwnd, browseEnumProc, 0);
+ }
+ }
+ return 0;
+}
+
+wchar_t* BuildFilterList(void)
+{
+ static wchar_t fileExtensionsString[128] = {L"*.*"}; // "All files\0*.*\0\0"
+ wchar_t *temp=fileExtensionsString+lstrlenW(fileExtensionsString) +1;
+ lstrcpynW(temp, WASABI_API_LNGSTRINGW(IDS_ALL_FILES), 128);
+ *(temp = temp + lstrlenW(temp) + 1) = 0;
+ return fileExtensionsString;
+}
+
+
+static void CALLBACK Window_TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ HWND hwndList;
+ int index, driveIdx;
+ wchar_t *pszFileName;
+
+ switch(idEvent)
+ {
+ case TIMER_NOTIFYINFO_ID:
+ KillTimer(hwnd, TIMER_NOTIFYINFO_ID);
+ hwndList = GetDlgItem(hwnd, IDC_LIST2);
+
+ driveIdx = LETTERTOINDEX(m_cdrom);
+ index = (hwndList) ? (INT)SendMessage(hwndList, LVM_GETNEXTITEM, (WPARAM)-1, (LPARAM)LVNI_FOCUSED) : -1;
+ pszFileName = (index >= 0 && index < itemCache[driveIdx].Size) ? itemCache[driveIdx].Items[index].filename : NULL;
+ NotifyInfoWindow(hwnd, pszFileName, FALSE);
+ break;
+ }
+}
+
+static void ListView_OnItemChanged(HWND hwndDlg, NMLISTVIEW *pnmv)
+{
+ if (LVIF_STATE & pnmv->uChanged)
+ {
+ if ((LVIS_FOCUSED & pnmv->uOldState) != (LVIS_FOCUSED & pnmv->uNewState))
+ {
+ KillTimer(hwndDlg, TIMER_NOTIFYINFO_ID);
+ SetTimer(hwndDlg, TIMER_NOTIFYINFO_ID, TIMER_NOTIFYINFO_DELAY, Window_TimerProc);
+ }
+ }
+}
+static void Window_OnQueryInfo(HWND hwnd)
+{
+ KillTimer(hwnd, TIMER_NOTIFYINFO_ID);
+ NotifyInfoWindow(hwnd, NULL, TRUE);
+ SetTimer(hwnd, TIMER_NOTIFYINFO_ID, TIMER_NOTIFYINFO_DELAY, Window_TimerProc);
+}
+
+static void Window_OnOperationCompleted(HWND hwnd, DM_NOTIFY_PARAM *phdr)
+{
+ MEDIAINFO mediaInfo;
+
+ if (phdr->cLetter != m_cdrom) return;
+
+ ZeroMemory(&mediaInfo, sizeof(MEDIAINFO));
+ mediaInfo.cLetter = m_cdrom;
+
+ switch(phdr->opCode)
+ {
+ case DMOP_IMAPIINFO:
+ if (S_OK == phdr->result)
+ {
+ DM_IMAPI_PARAM *pIMAPI = (DM_IMAPI_PARAM*)phdr;
+
+ if ((0 != pIMAPI->fMediaType && 0 != pIMAPI->fMediaFlags))
+ {
+ mediaInfo.bInserted = TRUE;
+ if (MEDIA_WRITABLE & pIMAPI->fMediaFlags) mediaInfo.bRecordable = TRUE;
+ if (MEDIA_RW & pIMAPI->fMediaFlags) mediaInfo.bRewritable = TRUE;
+ if (MEDIA_BLANK & pIMAPI->fMediaFlags) mediaInfo.bBlank = TRUE;
+ mediaInfo.nSectorsFree = pIMAPI->ulFreeBlocks;
+ mediaInfo.nSectorsUsed = pIMAPI->ulNextWritable;
+ }
+ }
+ else GetMediaInfoFromSonic(&mediaInfo);
+ FinishSetStatus(hwnd, &mediaInfo);
+ return;
+ }
+}
+
+static INT_PTR CALLBACK DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+
+ INT_PTR a = dialogSkinner.Handle(hwndDlg, uMsg, wParam, lParam); if (a) return a;
+ switch (uMsg)
+ {
+ case WM_SIZE:
+ if (wParam != SIZE_MINIMIZED)
+ {
+ childSizer.Resize(hwndDlg, burnwnd_rlist, sizeof(burnwnd_rlist) / sizeof(burnwnd_rlist[0]));
+ }
+ break;
+ case WM_BURNNOTIFY:
+ OnBurnNotify(hwndDlg, (DWORD)wParam, (DWORD)lParam);
+ PostMessage(prevWnd, uMsg, wParam, lParam);
+ break;
+ case WM_INITDIALOG: OnBurnDlgInit(hwndDlg, lParam); return 0;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ // link is dead so disabling for the time being
+ /*case IDC_LOGO:
+ if (HIWORD(wParam) == BN_CLICKED)
+ ShellExecute(hwndDlg, L"open", L"http://estore.sonic.com/redirect.asp?id=spaol110103", NULL, L".", 0);
+ break;*/
+ case IDC_BURN_OPTS:
+ {
+ RECT r;
+ HMENU menu = GetSubMenu(g_context_menus, 6);
+ GetWindowRect((HWND)lParam, &r);
+ CheckMenuItem(menu, ID_RIPOPTIONS_RIPPINGSTATUSWINDOW, g_config->ReadInt(L"cdburnstatuswnd", 0) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(menu, ID_RIPOPTIONS_EJECTCDWHENCOMPLETED, g_config->ReadInt(L"cdburnautoeject", 1) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(menu, ID_BURNOPTIONS_ADDCDTITLESTOLOCALCDDBCACHE, g_config->ReadInt(L"cdburnautoadddb", 1) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(menu, ID_RIPOPTIONS_CLOSEVIEWWHENCOMPLETE, g_config->ReadInt(L"cdburnautoclose", 0) ? MF_CHECKED : MF_UNCHECKED);
+
+ int x = Menu_TrackPopup(plugin.hwndLibraryParent, menu,
+ TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_BOTTOMALIGN |
+ TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RETURNCMD,
+ r.left, r.top, hwndDlg, NULL);
+ int val = 0, msgid;
+ switch (x)
+ {
+ case ID_RIPOPTIONS_RIPPINGSTATUSWINDOW:
+ val = g_config->ReadInt(L"cdburnstatuswnd", 0);
+ g_config->WriteInt(L"cdburnstatuswnd", !val);
+ msgid = BURNCFG_HIDEVIEW;
+ break;
+ case ID_RIPOPTIONS_EJECTCDWHENCOMPLETED:
+ val = !g_config->ReadInt(L"cdburnautoeject", 1);
+ g_config->WriteInt(L"cdburnautoeject", val);
+ msgid = BURNCFG_AUTOEJECT;
+ break;
+ case ID_BURNOPTIONS_ADDCDTITLESTOLOCALCDDBCACHE:
+ val = !g_config->ReadInt(L"cdburnautoadddb", 0);
+ g_config->WriteInt(L"cdburnautoadddb", val);
+ msgid = BURNCFG_ADDTODB;
+ break;
+ case ID_RIPOPTIONS_CLOSEVIEWWHENCOMPLETE:
+ val = !g_config->ReadInt(L"cdburnautoclose", 0);
+ g_config->WriteInt(L"cdburnautoclose", val);
+ msgid = BURNCFG_AUTOCLOSE;
+ break;
+ default: msgid = 0; break;
+ }
+ if (msgid)
+ {
+ HWND h;
+ h = cdburn_FindBurningHWND(m_cdrom);
+ if (h)
+ {
+ PostMessage(h, WM_BURNCONFIGCHANGED, msgid, val);
+ if (BURNCFG_HIDEVIEW == msgid) ShowWindow(h, val ? SW_HIDE : SW_SHOW);
+ }
+ }
+ Sleep(100);
+ MSG msg;
+ while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)); //eat return
+ }
+ return 0;
+ case IDC_ADD:
+ if (DM_MODE_BURNING != DriveManager_GetDriveMode(m_cdrom))
+ {
+ RECT r;
+ GetWindowRect((HWND)lParam, &r);
+ int x = Menu_TrackPopup(plugin.hwndLibraryParent, GetSubMenu(g_context_menus, 3),
+ TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_BOTTOMALIGN |
+ TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RETURNCMD,
+ r.left, r.top, hwndDlg, NULL);
+ switch (x)
+ {
+ case ID_BURNADDMENU_FILES:
+ {
+ OPENFILENAMEW l = {sizeof(l), };
+ wchar_t *temp;
+ const int len = 256 * 1024 - 128;
+ wchar_t *m_extlist = 0;
+ m_extlist = (wchar_t*)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 1, IPC_GET_EXTLISTW);
+ if ((int)(INT_PTR)m_extlist == 1) m_extlist = 0;
+
+ temp = (wchar_t *)GlobalAlloc(GPTR, len);
+ l.hwndOwner = hwndDlg;
+ l.lpstrFilter = m_extlist ? m_extlist : BuildFilterList();
+ l.lpstrFile = temp;
+ l.nMaxFile = len - 1;
+ l.lpstrTitle = WASABI_API_LNGSTRINGW(IDS_ADD_FILES_TO_BURNING_LIST);
+ l.lpstrDefExt = L"";
+ l.lpstrInitialDir = WASABI_API_APP->path_getWorkingPath();
+
+ l.Flags = OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ALLOWMULTISELECT;
+ if (GetOpenFileNameW(&l))
+ {
+ wchar_t newCurPath[MAX_PATH] = {0};
+ GetCurrentDirectoryW(MAX_PATH, newCurPath);
+ WASABI_API_APP->path_setWorkingPath(newCurPath);
+
+ if (temp[wcslen(temp) + 1])
+ {
+ wchar_t buf[MAX_PATH] = {0};
+ wchar_t *p = temp;
+ wchar_t *path = p;
+ p += wcslen(p) + 1;
+ while (p && *p)
+ {
+ if (*path)
+ StringCchPrintfW(buf, MAX_PATH, L"%s%s%s", path, path[wcslen(path) - 1] == '\\' ? L"" : L"\\" , p);
+ else
+ StringCchPrintfW(buf, MAX_PATH, L"%s", p);
+
+ cdburn_appendFile(buf, m_cdrom);
+ p += wcslen(p) + 1;
+ }
+ }
+ else
+ cdburn_appendFile(temp, m_cdrom);
+ }
+ GlobalFree(temp);
+ if (m_extlist) GlobalFree((HGLOBAL)m_extlist);
+ SetStatus(hwndDlg, m_cdrom);
+ }
+ break;
+ case ID_BURNADDMENU_FOLDER:
+ {
+ BROWSEINFOW bi = {0};
+ wchar_t name[MAX_PATH] = {0};
+ bi.hwndOwner = hwndDlg;
+ bi.pszDisplayName = name;
+ bi.lpszTitle = WASABI_API_LNGSTRINGW(IDS_CHOOSE_A_FOLDER_TO_ADD_TO_BURNING_LIST);
+ bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
+ bi.lpfn = BrowseCallbackProc;
+ ITEMIDLIST *idlist = SHBrowseForFolderW(&bi);
+ if (idlist)
+ {
+ wchar_t path[MAX_PATH] = {0};
+ SHGetPathFromIDListW(idlist, path);
+ Shell_Free(idlist);
+ cdburn_appendFile(path, m_cdrom);
+ SetStatus(hwndDlg, m_cdrom);
+ }
+ }
+ break;
+ case ID_BURNADDMENU_CURRENTPLAYLIST:
+ {
+ int plsize = (int)(INT_PTR)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETLISTLENGTH);
+ for (int i = 0;i < plsize;i++)
+ {
+ wchar_t *name = (wchar_t *)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, i, IPC_GETPLAYLISTFILEW);
+ cdburn_appendFile(name, m_cdrom);
+ }
+ SetStatus(hwndDlg, m_cdrom);
+ }
+ break;
+ }
+ Sleep(100);
+ MSG msg;
+ while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)); //eat return
+ }
+ break;
+ case IDC_CLEAR:
+ if (DM_MODE_BURNING != DriveManager_GetDriveMode(m_cdrom))
+ {
+ wchar_t title[32] = {0};
+ if (MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(IDS_SURE_YOU_WANT_TO_CLEAR_BURNING_LIST),
+ WASABI_API_LNGSTRINGW_BUF(IDS_CONFIRMATION,title,32), MB_YESNO) != IDYES)
+ break;
+ emptyRecordList(&itemCache[LETTERTOINDEX(m_cdrom)]);
+ SetStatus(hwndDlg, m_cdrom);
+ refreshList();
+ }
+ break;
+ case IDC_CANCEL_BURN:
+ case IDC_BURN:
+ {
+ HWND h;
+ if (NULL != (h = cdburn_FindBurningHWND(m_cdrom)))
+ {
+ PostMessage(h, WM_COMMAND, MAKEWPARAM(IDCANCEL, BN_CLICKED), 0);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BURN), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_BURN), FALSE);
+
+ }
+ else doBurnDialog(hwndDlg);
+ }
+ break;
+ case IDC_BTN_SHOWINFO:
+ switch(HIWORD(wParam))
+ {
+ case BN_CLICKED: SendMessageW(GetParent(hwndDlg), WM_COMMAND, wParam, lParam); break;
+ }
+ break;
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ {
+ if (DM_MODE_BURNING == DriveManager_GetDriveMode(m_cdrom) || m_statuslist.GetCount() == 0) return 0;
+
+ HMENU menu = GetSubMenu(g_context_menus, 4);
+
+ POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
+
+ if (pt.x == -1 || pt.y == -1) // x and y are -1 if the user invoked a shift-f10 popup menu
+ {
+ RECT itemRect = {0};
+ int selected = m_statuslist.GetNextSelected();
+ if (selected != -1) // if something is selected we'll drop the menu from there
+ {
+ m_statuslist.GetItemRect(selected, &itemRect);
+ ClientToScreen(m_statuslist.getwnd(), (POINT *)&itemRect);
+ }
+ else // otherwise we'll drop it from the top-left corner of the listview, adjusting for the header location
+ {
+ GetWindowRect(m_statuslist.getwnd(), &itemRect);
+
+ HWND hHeader = (HWND)SNDMSG((HWND)wParam, LVM_GETHEADER, 0, 0L);
+ RECT headerRect;
+ if ((WS_VISIBLE & GetWindowLongPtr(hHeader, GWL_STYLE)) && GetWindowRect(hHeader, &headerRect))
+ {
+ itemRect.top += (headerRect.bottom - headerRect.top);
+ }
+ }
+ pt.x = itemRect.left;
+ pt.y = itemRect.top;
+ }
+
+ HWND hHeader = (HWND)SNDMSG((HWND)wParam, LVM_GETHEADER, 0, 0L);
+ RECT headerRect;
+ if (0 == (WS_VISIBLE & GetWindowLongPtr(hHeader, GWL_STYLE)) || FALSE == GetWindowRect(hHeader, &headerRect))
+ {
+ SetRectEmpty(&headerRect);
+ }
+
+ if (FALSE != PtInRect(&headerRect, pt))
+ {
+ return 0;
+ }
+
+ int r = Menu_TrackPopup(plugin.hwndLibraryParent, menu,
+ TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_NONOTIFY,
+ pt.x, pt.y, hwndDlg, NULL);
+ switch (r)
+ {
+ case ID_BURNCONTEXTMENU_PLAYSELECTEDITEMS:
+ playSelectedItems(hwndDlg, 0);
+ break;
+ case ID_BURNCONTEXTMENU_ENQUEUESELECTEDITEMS:
+ playSelectedItems(hwndDlg, 1);
+ break;
+ case ID_BURNCONTEXTMENU_SELECTALL:
+ selectAll();
+ break;
+ case ID_BURNCONTEXTMENU_REMOVESELECTEDITEMS:
+ if (DM_MODE_BURNING != DriveManager_GetDriveMode(m_cdrom)) deleteSelectedItems(hwndDlg, m_cdrom);
+ break;
+ case ID_BURNCONTEXTMENU_BURN:
+ doBurnDialog(hwndDlg);
+ break;
+ case ID_BURNCONTEXTMENU_MOVESELECTEDITEMSUP:
+ moveSelItemsUp();
+ break;
+ case ID_BURNCONTEXTMENU_MOVESELECTEDITEMSDOWN:
+ moveSelItemsDown();
+ break;
+ }
+ Sleep(100);
+ MSG msg;
+ while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)); //eat return
+ return 1;
+ }
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR l = (LPNMHDR)lParam;
+ if (l->idFrom == IDC_LIST2)
+ {
+ if (l->code == LVN_KEYDOWN)
+ {
+ LPNMLVKEYDOWN pnkd = (LPNMLVKEYDOWN) lParam;
+ switch (pnkd->wVKey)
+ {
+ case 38: //up
+ if (GetAsyncKeyState(VK_LMENU))
+ {
+ moveSelItemsUp();
+ return 1;
+ }
+ break;
+ case 40: //down
+ if (GetAsyncKeyState(VK_LMENU))
+ {
+ moveSelItemsDown();
+ return 1;
+ }
+ break;
+ case 46: //delete
+ if (DM_MODE_BURNING != DriveManager_GetDriveMode(m_cdrom)) deleteSelectedItems(hwndDlg, m_cdrom);
+ break;
+ case 'A':
+ if (GetAsyncKeyState(VK_CONTROL))
+ selectAll();
+ break;
+ }
+ }
+ else if (l->code == NM_DBLCLK)
+ {
+ if (DM_MODE_BURNING == DriveManager_GetDriveMode(m_cdrom)) return 0;
+ playSelectedItems(hwndDlg, (!!g_config->ReadInt(L"enqueuedef", 0)) ^(!!(GetAsyncKeyState(VK_SHIFT)&0x8000)));
+ }
+ else if (l->code == NM_RETURN)
+ {
+ if (DM_MODE_BURNING == DriveManager_GetDriveMode(m_cdrom)) return 0;
+ playSelectedItems(hwndDlg, 0 ^(!!(GetAsyncKeyState(VK_SHIFT)&0x8000)));
+ }
+ else if (l->code == LVN_GETDISPINFO)
+ {
+ NMLVDISPINFO *lpdi = (NMLVDISPINFO*) lParam;
+ int item = lpdi->item.iItem;
+ int idx = LETTERTOINDEX(m_cdrom);
+ if (item < 0 || item >= itemCache[idx].Size) return 0;
+
+ itemRecordW *thisitem = itemCache[idx].Items + item;
+
+ if (lpdi->item.mask & (LVIF_TEXT | /*LVIF_IMAGE*/0)) // we can always do images too :)
+ {
+ if (lpdi->item.mask & LVIF_TEXT)
+ {
+ wchar_t tmpbuf[128] = {0};
+ wchar_t *nameptr = 0;
+ switch (lpdi->item.iSubItem)
+ {
+ case 0:
+ //track #
+ StringCchPrintfW(tmpbuf, 128, L"%d", item + 1);
+ nameptr = tmpbuf;
+ break;
+ case 1:
+ //title
+ lstrcpynW(tmpbuf, thisitem->title, 128);
+ nameptr = tmpbuf;
+ break;
+ case 2:
+ //length
+ StringCchPrintfW(tmpbuf, 128, L"%01d:%02d", thisitem->length / 60, thisitem->length % 60);
+ nameptr = tmpbuf;
+ break;
+ case 3:
+ DWORD state = (DWORD) SendMessage(m_burning_other_wnd, WM_BURNGETITEMSTATUS, BURNSTATUS_STATE, (LPARAM)item);
+ switch (state)
+ {
+ case BURNERITEM_BURNING:
+ case BURNERITEM_DECODING:
+ StringCchPrintfW(tmpbuf, 128, L"%s (%d%%)",
+ WASABI_API_LNGSTRINGW((BURNERITEM_BURNING == state) ? IDS_BURNING_ : IDS_PREPARING),
+ (DWORD)SendMessage(m_burning_other_wnd, WM_BURNGETITEMSTATUS, BURNSTATUS_PROGRESS, (LPARAM)item));
+ nameptr = tmpbuf;
+ break;
+ case BURNERITEM_SUCCESS: break;
+ case BURNERITEM_BURNED:
+ nameptr = WASABI_API_LNGSTRINGW_BUF(IDS_FINISHED,tmpbuf,128);
+ break;
+ case BURNERITEM_DECODED:
+ nameptr = WASABI_API_LNGSTRINGW_BUF(IDS_PREPARED,tmpbuf,128);
+ break;
+ case BURNERITEM_SKIPPED:
+ nameptr = WASABI_API_LNGSTRINGW_BUF(IDS_SKIPPED,tmpbuf,128);
+ break;
+ case BURNERITEM_READY:
+ nameptr = WASABI_API_LNGSTRINGW_BUF(IDS_SCHEDULED,tmpbuf,128);
+ break;
+ case BURNERITEM_LICENSING:
+ nameptr = WASABI_API_LNGSTRINGW_BUF(IDS_CHECKING_LICENSE,tmpbuf,128);
+ break;
+ case BURNERITEM_LICENSED:
+ nameptr = WASABI_API_LNGSTRINGW_BUF(IDS_LICENSED,tmpbuf,128);
+ break;
+ case BURNERITEM_ABORTED:
+ nameptr = WASABI_API_LNGSTRINGW_BUF(IDS_CANCELLED,tmpbuf,128);
+ break;
+ case BURNERITEM_FAILED:
+ nameptr = WASABI_API_LNGSTRINGW_BUF(IDS_FAILED,tmpbuf,128);
+ break;
+ case BURNERITEM_CANCELING:
+ nameptr = WASABI_API_LNGSTRINGW_BUF(IDS_CANCELLING,tmpbuf,128);
+ break;
+ case BURNERITEM_BADFILENAME:
+ nameptr = WASABI_API_LNGSTRINGW_BUF(IDS_BAD_FILENAME,tmpbuf,128);
+ break;
+ case BURNERITEM_UNABLEOPENFILE:
+ nameptr = WASABI_API_LNGSTRINGW_BUF(IDS_UNABLE_TO_OPEN_FILE,tmpbuf,128);
+ break;
+ case BURNERITEM_WRITEERROR:
+ nameptr = WASABI_API_LNGSTRINGW_BUF(IDS_CACHE_WRITE_FAILED,tmpbuf,128);
+ break;
+ case BURNERITEM_DECODEERROR:
+ nameptr = WASABI_API_LNGSTRINGW_BUF(IDS_UNABLE_TO_FIND_DECODER,tmpbuf,128);
+ break;
+ case BURNERITEM_ADDSTREAMFAILED:
+ nameptr = WASABI_API_LNGSTRINGW_BUF(IDS_CANNOT_ADD_TO_THE_DISC,tmpbuf,128);
+ break;
+ case BURNERITEM_READSTREAMERROR:
+ nameptr = WASABI_API_LNGSTRINGW_BUF(IDS_CACHE_READ_FAILED,tmpbuf,128);
+ break;
+ default:
+ nameptr = WASABI_API_LNGSTRINGW_BUF(IDS_UNKNOWN_ERROR,tmpbuf,128);
+ break;
+ }
+ //status
+ break;
+ }
+ if (nameptr)
+ lstrcpynW(lpdi->item.pszText, nameptr, lpdi->item.cchTextMax);
+ else
+ lpdi->item.pszText[0] = 0;
+ }
+ }
+ return 0;
+ }
+ else if (l->code == LVN_BEGINDRAG)
+ {
+ SetCapture(hwndDlg);
+ m_dragging = 1;
+ LPNMLISTVIEW nlv = (LPNMLISTVIEW)lParam;
+ m_drag_item = nlv->iItem;
+ }
+ else if (l->code == LVN_ITEMCHANGED) ListView_OnItemChanged(hwndDlg, (NMLISTVIEW*)l);
+ }
+ }
+ break;
+
+ case WM_MOUSEMOVE:
+ if (m_dragging)
+ {
+ POINT p;
+ p.x = GET_X_LPARAM(lParam);
+ p.y = GET_Y_LPARAM(lParam);
+ ClientToScreen(hwndDlg, &p);
+ ScreenToClient(m_statuslist.getwnd(), &p);
+ int i = m_statuslist.FindItemByPoint(p.x, p.y);
+ if (i != -1 && i != m_drag_item)
+ {
+ if (i > m_drag_item)
+ {
+ for (int j = 0;j < (i - m_drag_item);j++)
+ moveSelItemsDown();
+ }
+ else
+ {
+ for (int j = 0;j < (m_drag_item - i);j++)
+ moveSelItemsUp();
+ }
+ m_drag_item = i;
+ }
+ }
+ break;
+
+ case WM_LBUTTONUP:
+ if (GetCapture() == hwndDlg)
+ {
+ ReleaseCapture();
+ m_dragging = 0;
+ }
+ break;
+
+ case WM_DESTROY:
+ if (m_statuslist.getwnd())
+ {
+ g_view_metaconf->WriteInt(L"col_track", m_statuslist.GetColumnWidth(0));
+ g_view_metaconf->WriteInt(L"col_title", m_statuslist.GetColumnWidth(1));
+ g_view_metaconf->WriteInt(L"col_len", m_statuslist.GetColumnWidth(2));
+ g_view_metaconf->WriteInt(L"col_status", m_statuslist.GetColumnWidth(3));
+ }
+
+ if (m_burning_other_wnd && IsWindow(m_burning_other_wnd))
+ {
+ PostMessage(m_burning_other_wnd, WM_BURNUPDATEOWNER, 0, (LPARAM)prevWnd);
+ prevWnd = NULL;
+ }
+ m_hwndstatus = 0;
+
+ if (hPLFont)
+ {
+ DeleteObject(hPLFont);
+ hPLFont = NULL;
+ }
+
+ {
+ HANDLE hPrev = (HANDLE) SendDlgItemMessage(hwndDlg,IDC_LOGO,STM_SETIMAGE,IMAGE_BITMAP, 0L);
+ if (hPrev) DeleteObject(hPrev);
+ }
+ return 0;
+ case WM_ML_CHILDIPC:
+ if (lParam == ML_CHILDIPC_DROPITEM && wParam)
+ {
+ mlDropItemStruct *dis = (mlDropItemStruct *)wParam;
+ if (DM_MODE_BURNING == DriveManager_GetDriveMode(m_cdrom))
+ {
+ dis->result = -1;
+ return 0;
+ }
+
+ if (dis->type != ML_TYPE_ITEMRECORDLISTW && dis->type != ML_TYPE_ITEMRECORDLIST &&
+ dis->type != ML_TYPE_FILENAMES && dis->type != ML_TYPE_FILENAMESW)
+ {
+ dis->result = -1;
+ }
+ else
+ {
+ if (dis->data)
+ {
+ dis->result = 1;
+ if (dis->type == ML_TYPE_ITEMRECORDLIST)
+ {
+ itemRecordList *obj = (itemRecordList *)dis->data;
+ cdburn_appendItemRecord(obj, m_cdrom);
+ }
+ else if (dis->type == ML_TYPE_ITEMRECORDLISTW)
+ {
+ itemRecordListW *obj = (itemRecordListW *)dis->data;
+ cdburn_appendItemRecord(obj, m_cdrom);
+ }
+ else if (dis->type == ML_TYPE_FILENAMES) // playlist
+ {
+ char *p = (char*)dis->data;
+ while (p && *p)
+ {
+ cdburn_appendFile(p, m_cdrom);
+ p += strlen(p) + 1;
+ }
+ }
+ else if (dis->type == ML_TYPE_FILENAMESW)
+ {
+ wchar_t *p = (wchar_t*)dis->data;
+ while (p && *p)
+ {
+ cdburn_appendFile(p, m_cdrom);
+ p += wcslen(p) + 1;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+
+ case WM_DROPFILES:
+ {
+ char temp[2048] = {0};
+ HDROP h = (HDROP) wParam;
+ int y = DragQueryFileA(h, 0xffffffff, temp, sizeof(temp));
+
+ if (DM_MODE_BURNING == DriveManager_GetDriveMode(m_cdrom))
+ {
+// MessageBoxA(hwndDlg,"Cannot add files while burning","CD Burner",MB_OK);
+ }
+ else for (int x = 0; x < y; x ++)
+ {
+ DragQueryFileA(h, x, temp, sizeof(temp));
+ cdburn_appendFile(temp, m_cdrom);
+ }
+
+ DragFinish(h);
+ }
+ return 0;
+ case WM_PAINT:
+ {
+ int tab[] = { IDC_LIST2 | DCW_SUNKENBORDER, IDC_LOGO | DCW_SUNKENBORDER};
+ dialogSkinner.Draw(hwndDlg, tab, 2);
+ }
+ return 0;
+ case WM_ERASEBKGND: return 1; //handled by WADlg_DrawChildWindowBorders in WM_PAINT
+ case WM_QUERYFILEINFO: Window_OnQueryInfo(hwndDlg); break;
+ case WM_EX_OPCOMPLETED: Window_OnOperationCompleted(hwndDlg, (DM_NOTIFY_PARAM*)lParam);
+ }
+ return FALSE;
+}
+
+static HWND BurnAddStatus_wnd;
+
+static BOOL CALLBACK BurnAddStatus_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if (uMsg == WM_CLOSE)
+ DestroyWindow(hwndDlg);
+ return 0;
+}
+
+void BurnAddStatus_Create(int num)
+{
+ if (BurnAddStatus_wnd && IsWindow(BurnAddStatus_wnd)) DestroyWindow(BurnAddStatus_wnd);
+ BurnAddStatus_wnd = WASABI_API_CREATEDIALOGW(IDD_BURN_ADD_STATUS, plugin.hwndLibraryParent, BurnAddStatus_proc);
+ if (!BurnAddStatus_wnd) return ;
+
+ SetTimer(BurnAddStatus_wnd, 1, 100, NULL);
+ SendDlgItemMessage(BurnAddStatus_wnd, IDC_PROGRESS1, PBM_SETRANGE, 0, MAKELPARAM(0, num));
+
+ unsigned int start_t = GetTickCount();
+ if (start_t >= 0xffffff00) start_t = 0;
+
+ MSG msg;
+ while (GetTickCount() < start_t + 100 && GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+}
+
+void BurnAddStatus_Step(itemRecordListW *items)
+{
+ if (!BurnAddStatus_wnd || !items || items && !items->Size || items && !items->Items) return ;
+ SendDlgItemMessage(BurnAddStatus_wnd, IDC_PROGRESS1, PBM_DELTAPOS, 1, 0);
+
+ int l = 0;
+ for (int i = 0;i < items->Size;i++)
+ {
+ l += items->Items[i].length;
+ }
+ wchar_t buf[512] = {0};
+ StringCchPrintf(buf, 512, WASABI_API_LNGSTRINGW(IDS_ADDING_TRACKS_TO_BURNER_TOTAL_LENGTH_X), l / 60, l % 60);
+ SetDlgItemText(BurnAddStatus_wnd, IDC_STAT, buf);
+}
+
+void BurnAddStatus_Done()
+{
+ if (!BurnAddStatus_wnd) return ;
+ unsigned int start_t = GetTickCount();
+ if (start_t >= 0xffffff00) start_t = 0;
+
+ MSG msg;
+ while (GetTickCount() < start_t + 1000 && IsWindow(BurnAddStatus_wnd) && GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ DestroyWindow(BurnAddStatus_wnd);
+ BurnAddStatus_wnd = 0;
+}
+
+static bool cdrFound(char letter)
+{
+ wchar_t name[]= L"cda://X.cda";
+ wchar_t info[16] = L"";
+ name[6] = letter;
+ getFileInfoW(name, L"cdtype", info, sizeof(info)/sizeof(wchar_t));
+ return !lstrcmpW(info, L"CDR") || !lstrcmpW(info, L"CDRW");
+}
+
+static char m_burnwait_letter;
+static BOOL CALLBACK BurnWaitProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ SetTimer(hwndDlg, 1, 2000, NULL);
+ {
+ wchar_t buf[512] = {0};
+ StringCchPrintf(buf, 512, WASABI_API_LNGSTRINGW(IDS_PLEASE_INSERT_BLANK_RECORDABLE_CD), toupper(m_burnwait_letter));
+ SetDlgItemText(hwndDlg, IDC_TEXT, buf);
+ }
+ return 0;
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDCANCEL) EndDialog(hwndDlg, 1);
+ return 0;
+ case WM_TIMER:
+ if (cdrFound(m_burnwait_letter)) EndDialog(hwndDlg, 0);
+ return 0;
+ }
+ return 0;
+}
+
+int Burn_WaitForCDR(HWND hwndParent, char driveletter) // returns 0 on CD-R found, 1 on cancel
+{
+ CHAR cMode;
+ if (!driveletter) return 1;
+ cMode = DriveManager_GetDriveMode(driveletter);
+ if (DM_MODE_BURNING == cMode || DM_MODE_RIPPING == cMode)
+ {
+ return 1; // if burning or ripping, don't fuck with it
+ }
+
+ if (cdrFound(driveletter)) return 0;
+ if (m_burnwait_letter) return 1;
+
+ m_burnwait_letter = (char)toupper(driveletter);
+ int x = (int)(INT_PTR)WASABI_API_DIALOGBOXW(IDD_WAITFORCDR, hwndParent, BurnWaitProc);
+ m_burnwait_letter = 0;
+ return x;
+}
diff --git a/Src/Plugins/Library/ml_disc/cdrip.cpp b/Src/Plugins/Library/ml_disc/cdrip.cpp
new file mode 100644
index 00000000..58192596
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/cdrip.cpp
@@ -0,0 +1,1061 @@
+#include "main.h"
+#include <windowsx.h>
+#include "resource.h"
+
+#include "..\nu\listview.h"
+#include "../nu/DialogSkinner.h"
+#include "../nu/ChildSizer.h"
+
+#include "config.h"
+#include "../winamp/wa_ipc.h"
+
+#include "..\..\General\gen_ml/gaystring.h"
+
+#include <stdio.h>
+#include <shlobj.h>
+#include <time.h>
+#include "../nu/AutoChar.h"
+#include "../nu/AutoCharFn.h"
+#include "../nu/AutoWide.h"
+
+#include "ReplayGain.h"
+
+#include "M3UWriter.h"
+#include "PLSWriter.h"
+#include "./settings.h"
+#include <shlwapi.h>
+#include <windows.h>
+#include <strsafe.h>
+
+extern unsigned int FileTimeToUnixTime(FILETIME *ft);
+
+static UINT uMsgRipperNotify = 0;
+
+
+static INT_PTR WINAPI DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+HWND CreateCDRipWindow(HWND hwndParent, CHAR cLetter)
+{
+ return WASABI_API_CREATEDIALOGPARAMW(IDD_VIEW_CDROM_EX2, hwndParent, DlgProc, (LPARAM)cLetter);
+}
+//physically update metadata in a given file
+int updateFileInfo(char *filename, char *metadata, char *data)
+{
+ extendedFileInfoStruct efis = {
+ filename,
+ metadata,
+ data ? data : "",
+ data ? strlen(data) : 0,
+ };
+ return (INT)(INT_PTR)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&efis, IPC_SET_EXTENDED_FILE_INFO);
+}
+
+//physically update metadata in a given file
+int updateFileInfoW(wchar_t *filename, const wchar_t *metadata, const wchar_t *data)
+{
+ extendedFileInfoStructW efis = {
+ filename,
+ metadata,
+ data ? data : L"",
+ data ? lstrlenW(data) : 0,
+ };
+ return (INT)(INT_PTR)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&efis, IPC_SET_EXTENDED_FILE_INFOW);
+}
+
+
+static int m_extract_curtrack, m_extract_nb, m_extract_nb_total;
+static int m_db_has_upd;
+static convertFileStructW m_fcs;
+static wchar_t m_extract_src[64];
+static DWORD m_extract_time;
+static int m_extracting;
+static cdrip_params *m_rip_params;
+HWND m_extract_wnd = 0;
+static wchar_t m_last_total_status[512];
+static wchar_t m_last_item_status[512];
+static int m_cur_rg = 0;
+static int done = 0;
+
+bool RegisteredEncoder(DWORD fourcc)
+{
+ if (fourcc == mmioFOURCC('M', 'P', '3', 'l')
+ || fourcc == mmioFOURCC('A', 'A', 'C', 'H')
+ || fourcc == mmioFOURCC('M', '4', 'A', 'H'))
+ return true;
+ else
+ return false;
+}
+
+static void createDirForFile(char *str)
+{
+ char *p = str;
+ if ((p[0] == '\\' || p[0] == '/') && (p[1] == '\\' || p[1] == '/'))
+ {
+ p += 2;
+ while (p && *p && *p != '\\' && *p != '/') p++;
+ if (!p || !*p) return ;
+ p++;
+ while (p && *p && *p != '\\' && *p != '/') p++;
+ }
+ else
+ {
+ while (p && *p && *p != '\\' && *p != '/') p++;
+ }
+
+ while (p && *p)
+ {
+ while (p && *p != '\\' && *p != '/' && *p) p = CharNextA(p);
+ if (p && *p)
+ {
+ char lp = *p;
+ *p = 0;
+ CreateDirectoryA(str, NULL);
+ *p++ = lp;
+ }
+ }
+}
+
+static void createDirForFileW(wchar_t *str)
+{
+ wchar_t *p = str;
+ if ((p[0] ==L'\\' || p[0] ==L'/') && (p[1] ==L'\\' || p[1] ==L'/'))
+ {
+ p += 2;
+ while (p && *p && *p !=L'\\' && *p !=L'/') p++;
+ if (!p || !*p) return ;
+ p++;
+ while (p && *p && *p !=L'\\' && *p !=L'/') p++;
+ }
+ else
+ {
+ while (p && *p && *p !=L'\\' && *p !=L'/') p++;
+ }
+
+ while (p && *p)
+ {
+ while (p && *p !=L'\\' && *p !=L'/' && *p) p = CharNextW(p);
+ if (p && *p)
+ {
+ wchar_t lp = *p;
+ *p = 0;
+ CreateDirectoryW(str, NULL);
+ *p++ = lp;
+ }
+ }
+}
+
+static void LockCD(char cLetter, BOOL bLock)
+{
+ wchar_t info[32] = {0};
+ wchar_t name[] = L"cda://X";
+ name[6] = cLetter;
+ getFileInfoW(name, (bLock) ? L"cdlock" : L"cdunlock", info, sizeof(info)/sizeof(wchar_t));
+}
+
+static int m_pstat_bytesdone;
+static int m_pstat_bytesout;
+static int m_pstat_timedone;
+
+static int m_rip_done;
+
+static HWND m_hwndstatus;
+static W_ListView m_statuslist;
+
+static void NotifyInfoWindow(HWND hwnd, BOOL bForceRefresh)
+{
+ HWND hwndParent;
+ hwndParent = GetParent(hwnd);
+ if (hwndParent) SendMessageW(hwndParent, WM_SHOWFILEINFO,
+ (WPARAM) WISF_MESSAGE | ((bForceRefresh) ? WISF_FORCE : WISF_NORMAL),
+ (LPARAM)WASABI_API_LNGSTRINGW(IDS_INFO_RIPPING));
+}
+
+static void ListView_OnItemChanged(HWND hwndDlg, NMLISTVIEW *pnmv)
+{
+ if (LVIF_STATE & pnmv->uChanged)
+ {
+ if ((LVIS_FOCUSED & pnmv->uOldState) != (LVIS_FOCUSED & pnmv->uNewState))
+ {
+ NotifyInfoWindow(hwndDlg, TRUE);
+ }
+ }
+}
+
+static void Window_OnQueryInfo(HWND hwnd)
+{
+ NotifyInfoWindow(hwnd, FALSE);
+}
+
+static INT_PTR Window_OnNotify(HWND hwndDlg, INT ctrlId, LPNMHDR phdr)
+{
+ switch(phdr->idFrom)
+ {
+ case IDC_LIST2:
+ switch(phdr->code)
+ {
+ case LVN_ITEMCHANGED: ListView_OnItemChanged(hwndDlg, (NMLISTVIEW*)phdr); break;
+ }
+ break;
+ }
+ return 0;
+}
+
+INT_PTR WINAPI DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ static ChildWndResizeItem ripwnd_rlist[] = {
+ {IDC_LIST2, 0x0011},
+ {IDC_CDINFO, 0x0000},
+ {IDC_RIPOPTS, 0x0101},
+ {IDC_CANCEL_RIP, 0x0101},
+ {IDC_BTN_SHOWINFO, 0x1111},
+ };
+
+ INT_PTR a = (INT_PTR)dialogSkinner.Handle(hwndDlg, uMsg, wParam, lParam); if (a) return a;
+ switch (uMsg)
+ {
+ case WM_SIZE:
+ if (wParam != SIZE_MINIMIZED)
+ {
+ childSizer.Resize(hwndDlg, ripwnd_rlist, sizeof(ripwnd_rlist) / sizeof(ripwnd_rlist[0]));
+ }
+ break;
+ case WM_PAINT:
+ {
+ int tab[] = { IDC_LIST2 | DCW_SUNKENBORDER};
+ dialogSkinner.Draw(hwndDlg, tab, 1);
+ }
+ return 0;
+ case WM_INITDIALOG:
+
+ m_hwndstatus = hwndDlg;
+
+ SendMessageW(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(IDC_BTN_SHOWINFO, BN_EX_GETTEXT), (LPARAM)GetDlgItem(hwndDlg, IDC_BTN_SHOWINFO));
+
+ m_statuslist.setwnd(GetDlgItem(hwndDlg, IDC_LIST2));
+ m_statuslist.AddCol(WASABI_API_LNGSTRINGW(IDS_TRACK_NUMBER), g_view_metaconf->ReadInt(L"col_track", 60));
+ m_statuslist.AddCol(WASABI_API_LNGSTRINGW(IDS_TITLE), g_view_metaconf->ReadInt(L"col_title", 200));
+ m_statuslist.AddCol(WASABI_API_LNGSTRINGW(IDS_LENGTH), g_view_metaconf->ReadInt(L"col_len", 80));
+ m_statuslist.AddCol(WASABI_API_LNGSTRINGW(IDS_STATUS), g_view_metaconf->ReadInt(L"col_status", 200));
+
+ SetDlgItemText(hwndDlg, IDC_CDINFO, m_last_total_status);
+ {
+ int l = m_rip_params->ntracks;
+ int x = 0;
+ for (int i = 0;i < l;i++) if (m_rip_params->tracks[i])
+ {
+ wchar_t buf[1024] = {0};
+ StringCchPrintf(buf, 1024, L"%d", i + 1);
+ m_statuslist.InsertItem(x, buf, 0);
+ m_statuslist.SetItemText(x, 1, m_rip_params->tracks[i]);
+
+ StringCchPrintf(buf, 512, L"%d:%02d", m_rip_params->lengths[i] / 60, m_rip_params->lengths[i] % 60);
+ m_statuslist.SetItemText(x, 2, buf);
+
+ if (i < m_extract_curtrack || m_rip_done)
+ m_statuslist.SetItemText(x, 3, WASABI_API_LNGSTRINGW(IDS_COMPLETED));
+ else if (i > m_extract_curtrack)
+ m_statuslist.SetItemText(x, 3, WASABI_API_LNGSTRINGW(IDS_QUEUED));
+ else
+ {
+ m_statuslist.SetItemText(x, 3, m_last_item_status[0] ? m_last_item_status : WASABI_API_LNGSTRINGW(IDS_RIPPING));
+ }
+ x++;
+ }
+ }
+
+ if (!m_extract_wnd || m_rip_done)
+ {
+ SetDlgItemText(hwndDlg, IDC_CANCEL_RIP, WASABI_API_LNGSTRINGW(IDS_CLOSE));
+ }
+
+ if (g_config->ReadInt(L"cdripautoeject", 0)) CheckDlgButton(hwndDlg, IDC_CHECK2, BST_CHECKED);
+ if (g_config->ReadInt(L"cdripautoplay", 0)) CheckDlgButton(hwndDlg, IDC_CHECK3, BST_CHECKED);
+ if (g_config->ReadInt(L"cdripautoclose", 1)) CheckDlgButton(hwndDlg, IDC_CHECK1, BST_CHECKED);
+
+ childSizer.Init(hwndDlg, ripwnd_rlist, sizeof(ripwnd_rlist) / sizeof(ripwnd_rlist[0]));
+
+ ListView_SetTextColor(m_statuslist.getwnd(), dialogSkinner.Color(WADLG_ITEMFG));
+ ListView_SetBkColor(m_statuslist.getwnd(), dialogSkinner.Color(WADLG_ITEMBG));
+ ListView_SetTextBkColor(m_statuslist.getwnd(), dialogSkinner.Color(WADLG_ITEMBG));
+
+ if(m_statuslist.getwnd())
+ {
+ MLSKINWINDOW sw;
+ sw.hwndToSkin = m_statuslist.getwnd();
+ sw.skinType = SKINNEDWND_TYPE_LISTVIEW;
+ sw.style = SWLVS_FULLROWSELECT | SWLVS_DOUBLEBUFFER | SWLVS_ALTERNATEITEMS | SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS;
+ MLSkinWindow(plugin.hwndLibraryParent, &sw);
+ }
+ NotifyInfoWindow(hwndDlg, TRUE);
+ return 0;
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_RIPOPTS:
+ {
+ RECT r;
+ HMENU menu = GetSubMenu(g_context_menus, 5);
+ GetWindowRect((HWND)lParam, &r);
+ CheckMenuItem(menu, ID_RIPOPTIONS_RIPPINGSTATUSWINDOW, g_config->ReadInt(L"cdripstatuswnd", 0) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(menu, ID_RIPOPTIONS_EJECTCDWHENCOMPLETED, g_config->ReadInt(L"cdripautoeject", 0) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(menu, ID_RIPOPTIONS_PLAYTRACKSWHENCOMPLETED, g_config->ReadInt(L"cdripautoplay", 0) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(menu, ID_RIPOPTIONS_CLOSEVIEWWHENCOMPLETE, g_config->ReadInt(L"cdripautoclose", 0) ? MF_CHECKED : MF_UNCHECKED);
+
+ int prio = g_config->ReadInt(L"extractprio", THREAD_PRIORITY_NORMAL);
+ CheckMenuItem(menu, ID_RIPOPTIONS_PRIORITY_IDLE, prio == THREAD_PRIORITY_IDLE ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(menu, ID_RIPOPTIONS_PRIORITY_LOWEST, prio == THREAD_PRIORITY_LOWEST ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(menu, ID_RIPOPTIONS_PRIORITY_BELOWNORMAL, prio == THREAD_PRIORITY_BELOW_NORMAL ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(menu, ID_RIPOPTIONS_PRIORITY_NORMAL, prio == THREAD_PRIORITY_NORMAL ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(menu, ID_RIPOPTIONS_PRIORITY_ABOVENORMAL, prio == THREAD_PRIORITY_ABOVE_NORMAL ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(menu, ID_RIPOPTIONS_PRIORITY_HIGH, prio == THREAD_PRIORITY_HIGHEST ? MF_CHECKED : MF_UNCHECKED);
+
+ int x = Menu_TrackPopup(plugin.hwndLibraryParent, menu,
+ TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_BOTTOMALIGN |
+ TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RETURNCMD,
+ r.left, r.top, hwndDlg, NULL);
+ switch (x)
+ {
+ case ID_RIPOPTIONS_RIPPINGSTATUSWINDOW:
+ {
+ int x = g_config->ReadInt(L"cdripstatuswnd", 0);
+ g_config->WriteInt(L"cdripstatuswnd", !x);
+ ShowWindow(m_extract_wnd, x ? SW_HIDE : SW_SHOW);
+ }
+ break;
+ case ID_RIPOPTIONS_EJECTCDWHENCOMPLETED:
+ g_config->WriteInt(L"cdripautoeject", !g_config->ReadInt(L"cdripautoeject", 0));
+ break;
+ case ID_RIPOPTIONS_PLAYTRACKSWHENCOMPLETED:
+ g_config->WriteInt(L"cdripautoplay", !g_config->ReadInt(L"cdripautoplay", 0));
+ break;
+ case ID_RIPOPTIONS_CLOSEVIEWWHENCOMPLETE:
+ g_config->WriteInt(L"cdripautoclose", !g_config->ReadInt(L"cdripautoclose", 0));
+ break;
+ case ID_RIPOPTIONS_PRIORITY_IDLE:
+ case ID_RIPOPTIONS_PRIORITY_LOWEST:
+ case ID_RIPOPTIONS_PRIORITY_BELOWNORMAL:
+ case ID_RIPOPTIONS_PRIORITY_NORMAL:
+ case ID_RIPOPTIONS_PRIORITY_ABOVENORMAL:
+ case ID_RIPOPTIONS_PRIORITY_HIGH:
+ {
+ int prio = THREAD_PRIORITY_NORMAL;
+ if (x == ID_RIPOPTIONS_PRIORITY_IDLE) prio = THREAD_PRIORITY_IDLE;
+ if (x == ID_RIPOPTIONS_PRIORITY_LOWEST) prio = THREAD_PRIORITY_LOWEST;
+ if (x == ID_RIPOPTIONS_PRIORITY_BELOWNORMAL) prio = THREAD_PRIORITY_BELOW_NORMAL;
+ if (x == ID_RIPOPTIONS_PRIORITY_ABOVENORMAL) prio = THREAD_PRIORITY_ABOVE_NORMAL;
+ if (x == ID_RIPOPTIONS_PRIORITY_HIGH) prio = THREAD_PRIORITY_HIGHEST;
+ g_config->WriteInt(L"extractprio", prio);
+ convertSetPriorityW csp = {
+ &m_fcs,
+ prio,
+ };
+ SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&csp, IPC_CONVERT_SET_PRIORITYW);
+ }
+ break;
+ }
+ Sleep(100);
+ MSG msg;
+ while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)); //eat return
+ }
+ return 0;
+ case IDC_CANCEL_RIP:
+ {
+ wchar_t title[64] = {0};
+ if (!m_extract_wnd ||
+ m_rip_done ||
+ MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(IDS_CANCEL_RIP),
+ WASABI_API_LNGSTRINGW_BUF(IDS_CD_RIP_QUESTION,title,64),
+ MB_YESNO | MB_ICONQUESTION) == IDYES)
+ {
+ if (m_rip_params) LockCD(m_rip_params->drive_letter, FALSE);
+ if (m_extract_wnd) DestroyWindow(m_extract_wnd);
+ DestroyWindow(hwndDlg);
+ }
+ return 0;
+ }
+ case IDC_BTN_SHOWINFO:
+ switch(HIWORD(wParam))
+ {
+ case BN_CLICKED:
+ SendMessageW(GetParent(hwndDlg), WM_COMMAND, wParam, lParam);
+ NotifyInfoWindow(hwndDlg, TRUE);
+ break;
+ }
+ break;
+ }
+ break;
+ case WM_DESTROY:
+ if (m_statuslist.getwnd())
+ {
+ g_view_metaconf->WriteInt(L"col_track", m_statuslist.GetColumnWidth(0));
+ g_view_metaconf->WriteInt(L"col_title", m_statuslist.GetColumnWidth(1));
+ g_view_metaconf->WriteInt(L"col_len", m_statuslist.GetColumnWidth(2));
+ g_view_metaconf->WriteInt(L"col_status", m_statuslist.GetColumnWidth(3));
+ }
+
+ m_hwndstatus = 0;
+ return 0;
+
+ case WM_ERASEBKGND: return 1; //handled by WADlg_DrawChildWindowBorders in WM_PAINT
+ case WM_QUERYFILEINFO: Window_OnQueryInfo(hwndDlg); break;
+ case WM_NOTIFY: return Window_OnNotify(hwndDlg, (INT)wParam, (LPNMHDR) lParam);
+
+
+ }
+ return 0;
+}
+
+static BOOL CALLBACK extract_dialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ m_extract_wnd = hwndDlg;
+ m_rip_done = 0;
+ SetDlgItemText(hwndDlg, IDC_STATUS, WASABI_API_LNGSTRINGW(IDS_INITIALIZING));
+ m_pstat_bytesdone = 0;
+ m_pstat_bytesout = 0;
+ m_pstat_timedone = 0;
+ m_extract_nb = 0;
+ m_extract_curtrack = -1;
+ m_cur_rg = 0;
+ {
+ m_extract_nb_total = 0;
+ int l = m_rip_params->ntracks;
+ for (int i = 0;i < l;i++) if (m_rip_params->tracks[i]) m_extract_nb_total++;
+ }
+
+ LockCD(m_rip_params->drive_letter, TRUE);
+
+ SetPropW(hwndDlg, L"WARIPPER", (HANDLE)hwndDlg);
+ SetPropW(hwndDlg, L"DRIVE", (HANDLE)(INT_PTR)(0xFF & m_rip_params->drive_letter));
+
+ if (!uMsgRipperNotify) uMsgRipperNotify = RegisterWindowMessageA("WARIPPER_BROADCAST_MSG");
+ if (uMsgRipperNotify) SendNotifyMessage(HWND_BROADCAST, uMsgRipperNotify, (WPARAM)m_rip_params->drive_letter, (LPARAM)TRUE);
+ SendDlgItemMessage(hwndDlg, IDC_PROGRESS1, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
+ SendDlgItemMessage(hwndDlg, IDC_PROGRESS2, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
+ PostMessage(hwndDlg, WM_APP + 1, 0, 0);
+ if (g_config->ReadInt(L"cdripstatuswnd", 0)) ShowWindow(hwndDlg, SW_SHOW);
+
+ INT bVal;
+
+ if (S_OK == Settings_GetBool(C_EXTRACT, EF_CALCULATERG, &bVal) && bVal)
+ {
+ CreateGain();
+ QueueUserAPC(StartGain, rgThread,
+ (ULONG_PTR)((m_rip_params->ntracks == m_extract_nb_total) ? RG_ALBUM : RG_INDIVIDUAL_TRACKS));
+ }
+ break;
+ case WM_APP + 2: // track is starting to be RG scanned
+ m_statuslist.SetItemText(m_cur_rg, 3, WASABI_API_LNGSTRINGW(IDS_CALCULATING_REPLAY_GAIN));
+ break;
+ case WM_APP + 3: // track is starting to be RG scanned
+ m_statuslist.SetItemText(m_cur_rg, 3, WASABI_API_LNGSTRINGW(IDS_COMPLETED));
+ m_cur_rg++;
+ break;
+ case WM_APP + 1:
+ {
+ INT trackOffset, cchDest;
+ TCHAR szDestination[MAX_PATH] = {0}, szFormat[MAX_PATH] = {0};
+ int l = m_rip_params->ntracks;
+ done = 1;
+
+ Settings_GetInt(C_EXTRACT, EF_TRACKOFFSET, &trackOffset);
+ Settings_ReadString(C_EXTRACT, EF_TITLEFMT, szFormat, ARRAYSIZE(szFormat));
+ Settings_ReadString(C_EXTRACT, EF_PATH, szDestination, ARRAYSIZE(szDestination));
+ CleanupDirectoryString(szDestination);
+
+ cchDest = lstrlen(szDestination);
+
+ for (int i = m_extract_curtrack + 1;i < l;i++)
+ {
+ if (m_rip_params->tracks[i])
+ {
+ StringCchPrintfW(m_extract_src, 64, L"cda://%c,%d.cda", m_rip_params->drive_letter, i + 1);
+ szDestination[cchDest] = TEXT('\0');
+ if (cchDest) PathAddBackslash(szDestination);
+
+ wchar_t tmp1[32] = {0}, tmp2[32] = {0}, tmp3[32] = {0}, tmp4[32] = {0}, tmp5[32] = {0};
+ FormatFileName(szDestination,
+ ARRAYSIZE(szDestination)-11, // ensure we're leaving enough room for the extension
+ szFormat,
+ i + trackOffset,
+ (m_rip_params->artist && *(m_rip_params->artist)) ? (m_rip_params->artist) : WASABI_API_LNGSTRINGW_BUF(IDS_UNKNOWN_ARTIST,tmp1,32),
+ (m_rip_params->album && *(m_rip_params->album)) ? (m_rip_params->album) : WASABI_API_LNGSTRINGW_BUF(IDS_UNKNOWN_ALBUM,tmp2,32),
+ (m_rip_params->tracks[i] && *(m_rip_params->tracks[i])) ? (m_rip_params->tracks[i]) : L"0",
+ (m_rip_params->genre && *(m_rip_params->genre)) ? (m_rip_params->genre) : WASABI_API_LNGSTRINGW_BUF(IDS_UNKNOWN,tmp3,32),
+ (m_rip_params->year && *(m_rip_params->year)) ? (m_rip_params->year) : WASABI_API_LNGSTRINGW_BUF(IDS_UNKNOWN,tmp4,32),
+ (m_rip_params->trackArtists[i] && m_rip_params->trackArtists[i][0])?m_rip_params->trackArtists[i] : WASABI_API_LNGSTRINGW_BUF(IDS_UNKNOWN,tmp5,32),
+ NULL, (m_rip_params->disc && *(m_rip_params->disc)) ? (m_rip_params->disc) : L"");
+ memset(&m_fcs, 0, sizeof(m_fcs));
+ m_fcs.sourcefile = m_extract_src;
+ INT fourcc;
+ Settings_GetInt(C_EXTRACT, EF_FOURCC, &fourcc);
+ m_fcs.destformat[0]=fourcc;
+ if (m_fcs.destformat[0] == OLD_AAC_CODEC) Settings_GetDefault(C_EXTRACT, EF_FOURCC, &m_fcs.destformat[0]);
+
+ // now determine the extension
+ wchar_t fmt[10] = {0};
+ GetExtensionString(fmt, ARRAYSIZE(fmt), (DWORD)m_fcs.destformat[0]);
+ BOOL upperCase;
+ if (SUCCEEDED(Settings_GetBool(C_EXTRACT, EF_UPPEREXTENSION, &upperCase)) && FALSE != upperCase)
+ CharUpper(fmt);
+ else
+ CharLower(fmt);
+
+ StringCchCat(szDestination, ARRAYSIZE(szDestination), TEXT("."));
+ StringCchCat(szDestination, ARRAYSIZE(szDestination), fmt);
+
+ if (m_rip_params->filenames[i]) free(m_rip_params->filenames[i]);
+ m_rip_params->filenames[i] = _wcsdup(szDestination);
+
+ wchar_t tempFile[MAX_PATH] = {0};
+ wchar_t tmppath[MAX_PATH] = {0};
+ GetTempPath(MAX_PATH,tmppath);
+ GetTempFileName(tmppath,L"rip",0,tempFile);
+
+ m_rip_params->tempFilenames[i] = _wcsdup(tempFile);
+ createDirForFileW(m_rip_params->filenames[i]);
+ m_fcs.destfile = _wcsdup(tempFile);
+ createDirForFileW(m_fcs.destfile);
+ m_fcs.callbackhwnd = hwndDlg;
+ m_fcs.error = L"";
+ m_extract_time = 0;
+ m_extract_curtrack = i;
+
+ wchar_t *ptr = m_rip_params->filenames[i];
+ if (cchDest && cchDest < (int)lstrlenW(ptr)) ptr += (cchDest + 1);
+ SetDlgItemText(hwndDlg, IDC_CURTRACK, ptr);
+
+ if (SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&m_fcs, IPC_CONVERTFILEW) != 1)
+ {
+ wchar_t tmp[512] = {0};
+ StringCchPrintf(tmp, 512, WASABI_API_LNGSTRINGW(IDS_ERROR_RIPPING_TRACK), i + 1, m_fcs.error ? m_fcs.error : L"");
+ MessageBox(hwndDlg, tmp, WASABI_API_LNGSTRINGW(IDS_ERROR), MB_OK);
+ done = -1;
+ break;
+ }
+ convertSetPriorityW csp = {
+ &m_fcs,
+ g_config->ReadInt(L"extractprio", THREAD_PRIORITY_NORMAL),
+ };
+ SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&csp, IPC_CONVERT_SET_PRIORITYW);
+ m_extracting = 1;
+ done = 0;
+ PostMessage(hwndDlg, WM_WA_IPC , 0, IPC_CB_CONVERT_STATUS);
+ break;
+ }
+ }
+ if (done && m_rip_params)
+ {
+ LockCD(m_rip_params->drive_letter, FALSE);
+
+ if (g_config->ReadInt(L"cdripautoeject", 0) && done > 0)
+ {
+ char buf[64] = {0};
+ StringCchPrintfA(buf, 64, "cda://%c.cda", m_rip_params->drive_letter);
+ char buf2[32] = {0};
+ getFileInfo(buf, "<eject>", buf2, sizeof(buf2));
+ }
+
+ if (g_config->ReadInt(L"cdripautoplay", 0) && done > 0)
+ {
+ SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_DELETE);
+ for (int i = 0;i < m_rip_params->ntracks;i++)
+ {
+ if (m_rip_params->tracks[i])
+ {
+ COPYDATASTRUCT cds;
+ cds.dwData = IPC_PLAYFILEW;
+ cds.lpData = (void *) m_rip_params->filenames[i];
+ cds.cbData = sizeof(wchar_t) * (lstrlenW(m_rip_params->filenames[i]) + 1); // include space for null char
+ SendMessage(plugin.hwndWinampParent, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds);
+ }
+ }
+ SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_STARTPLAY);
+ }
+
+
+ if (m_rip_params->ntracks == m_extract_nb_total && done > 0)
+ {
+ INT bVal;
+ if ((S_OK == Settings_GetBool(C_EXTRACT, EF_CREATEM3U, &bVal) && bVal) ||
+ (S_OK == Settings_GetBool(C_EXTRACT, EF_CREATEPLS, &bVal) && bVal))
+ {
+
+ wchar_t str[MAX_PATH] = {0}, fmt[MAX_PATH] = {0};
+ Settings_ReadString(C_EXTRACT, EF_PLAYLISTFMT, fmt, ARRAYSIZE(fmt));
+ Settings_ReadString(C_EXTRACT, EF_PATH, str, ARRAYSIZE(str));
+
+ int l = lstrlenW(str);
+ if (l)
+ PathAddBackslash(str);
+
+ FormatFileName(str, ARRAYSIZE(str)-5, // ensure we're leaving enough room for the extension
+ fmt, 0xdeadbeef,
+ m_rip_params->artist ? m_rip_params->artist : L"",
+ m_rip_params->album ? m_rip_params->album : L"",
+ NULL,
+ m_rip_params->genre ? m_rip_params->genre : L"",
+ m_rip_params->year ? m_rip_params->year : L"",
+ NULL,
+ NULL,
+ m_rip_params->disc ? m_rip_params->disc : L"");
+
+ if (S_OK == Settings_GetBool(C_EXTRACT, EF_CREATEM3U, &bVal) && bVal)
+ {
+ wchar_t str2[MAX_PATH] = {0};
+ lstrcpynW(str2, str, MAX_PATH);
+ StringCchCatW(str2, MAX_PATH, L".m3u");
+ createDirForFileW(str2);
+ M3UWriter w;
+ FILE *fp=_wfopen(str2, L"wt");
+ w.Open(fp, AutoCharFn(str2), TRUE);
+ BOOL ext;
+ Settings_GetBool(C_EXTRACT, EF_USEM3UEXT, &ext);
+ for (int i = 0;i < m_rip_params->ntracks;i++)
+ {
+ if (m_rip_params->tracks[i] && m_rip_params->filenames[i])
+ {
+ GayString str;
+ str.Set(AutoChar(m_rip_params->artist));
+ str.Append(" - ");
+ str.Append(AutoChar(m_rip_params->tracks[i]));
+ if (ext)
+ w.SetExtended(AutoCharFn(m_rip_params->filenames[i]), str.Get(), m_rip_params->lengths[i]);
+ else
+ w.SetFilename(AutoCharFn(m_rip_params->filenames[i]));
+
+ }
+ }
+ w.Close();
+ }
+
+ if (S_OK == Settings_GetBool(C_EXTRACT, EF_CREATEPLS, &bVal) && bVal)
+ {
+ char str2[MAX_PATH] = {0};
+ lstrcpynA(str2, AutoChar(str), MAX_PATH);
+ StringCchCatA(str2, MAX_PATH, ".pls");
+ createDirForFile(str2);
+ // TODO: check for bad unicode conversion
+ PLSWriter w;
+ w.Open(str2);
+ for (int i = 0;i < m_rip_params->ntracks;i++)
+ {
+ if (m_rip_params->tracks[i] && m_rip_params->filenames[i])
+ {
+ GayString str;
+ str.Set(AutoChar(m_rip_params->artist));
+ str.Append(" - ");
+ str.Append(AutoChar(m_rip_params->tracks[i]));
+ w.SetFilename(AutoCharFn(m_rip_params->filenames[i]));
+ w.SetTitle(str.Get());
+ w.SetLength(m_rip_params->lengths[i]);
+ w.Next();
+ }
+ }
+ w.Close();
+ }
+ }
+
+ if (S_OK == Settings_GetBool(C_EXTRACT, EF_CREATEMLPL, &bVal) && bVal)
+ {
+ itemRecordListW irl = {0, };
+ allocRecordList(&irl, m_rip_params->ntracks, 0);
+ for (int i = 0;i < m_rip_params->ntracks;i++)
+ {
+ if (m_rip_params->tracks[i])
+ {
+ int n = irl.Size;
+ memset(&irl.Items[n], 0, sizeof(itemRecordW));
+ irl.Items[n].filename = _wcsdup(m_rip_params->filenames[i]);
+ irl.Items[n].album = _wcsdup(m_rip_params->album);
+ irl.Items[n].artist = _wcsdup(m_rip_params->artist);
+ irl.Items[n].title = _wcsdup(m_rip_params->tracks[i]);
+ irl.Items[n].genre = _wcsdup(m_rip_params->genre);
+ irl.Items[n].year = _wtoi(m_rip_params->year);
+ irl.Items[n].length = m_rip_params->lengths[i];
+ irl.Size++;
+ }
+ }
+
+ GayString str;
+ str.Set(AutoChar(m_rip_params->artist));
+ str.Append(" - ");
+ str.Append(AutoChar(m_rip_params->album));
+ AutoWide name(str.Get());
+ mlMakePlaylist pl = {sizeof(mlMakePlaylist), (const wchar_t*)name, ML_TYPE_ITEMRECORDLISTW, (void *) & irl, 0x01};
+ SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&pl, ML_IPC_PLAYLIST_MAKE);
+ freeRecordList(&irl);
+ }
+ } // playlist creation
+
+ if (rgThread)
+ QueueUserAPC(WriteGain, rgThread, 0);
+ else
+ PostMessage(m_extract_wnd, WM_APP + 4, 0, 0);
+ }
+ }
+ break;
+ case WM_APP + 4:
+ if (m_db_has_upd)
+ {
+ // TODO: benski> does mldb read metadata from this call or the 'add' call - because it won't have replaygain tags until now
+ PostMessage(plugin.hwndLibraryParent, WM_ML_IPC, 0, ML_IPC_DB_SYNCDB);
+ }
+
+ m_rip_done = 1;
+ if (g_config->ReadInt(L"cdripautoclose", 1))
+ {
+ DestroyWindow(hwndDlg);
+ }
+ else
+ {
+ SetWindowText(hwndDlg, WASABI_API_LNGSTRINGW(done > 0 ? IDS_RIP_COMPLETE : IDS_RIP_FAILED));
+ SetDlgItemText(hwndDlg, IDC_BUTTON1, WASABI_API_LNGSTRINGW(IDS_CLOSE));
+ if (m_hwndstatus)
+ {
+ SetDlgItemText(m_hwndstatus, IDC_CANCEL_RIP, WASABI_API_LNGSTRINGW(IDS_CLOSE));
+ }
+ SendDlgItemMessage(hwndDlg, IDC_PROGRESS1, PBM_SETPOS, 100, 0);
+ SendDlgItemMessage(hwndDlg, IDC_PROGRESS2, PBM_SETPOS, 100, 0);
+
+
+ int now = m_pstat_timedone;
+
+ int bytesout = m_pstat_bytesout;
+
+ double extracted_time = (double) m_pstat_bytesdone * (1.0 / 44100.0 / 4.0);
+ if (extracted_time < 1.0) extracted_time = 1.0;
+
+ int br = (int) (((double)bytesout * 8.0 / extracted_time) / 1000.0 + 0.5);
+
+ if (done > 0)
+ {
+ wchar_t sstr[16] = {0};
+ StringCchPrintf(m_last_total_status, 512,
+ WASABI_API_LNGSTRINGW(IDS_X_TRACKS_RIPPED_IN_X),
+ m_extract_nb_total,
+ WASABI_API_LNGSTRINGW_BUF(m_extract_nb_total == 1 ? IDS_TRACK : IDS_TRACKS,sstr,16),
+ now / 1000 / 60, (now / 1000) % 60,
+ extracted_time / (now / 1000.0),
+ br, (double)bytesout * (1.0 / (1024.0*1024.0))
+ );
+ }
+ else
+ {
+ WASABI_API_LNGSTRINGW_BUF(IDS_RIP_FAILED,m_last_total_status,512);
+ }
+
+ SetDlgItemText(hwndDlg, IDC_STATUS2, m_last_total_status);
+ SetDlgItemText(hwndDlg, IDC_CURTRACK, WASABI_API_LNGSTRINGW(IDS_COMPLETED));
+ SetDlgItemText(hwndDlg, IDC_STATUS, L"");
+
+ if (m_hwndstatus && IsWindow(m_hwndstatus))
+ {
+ SetDlgItemText(m_hwndstatus, IDC_CDINFO, m_last_total_status);
+ SetDlgItemText(m_hwndstatus, IDC_CANCEL_RIP, WASABI_API_LNGSTRINGW(IDS_DONE));
+ }
+ }
+ break;
+ case WM_WA_IPC:
+ switch (lParam)
+ {
+ case IPC_CB_CONVERT_STATUS:
+ {
+ if (!m_extract_time)
+ {
+ m_extract_time = GetTickCount();
+ break;
+ }
+ DWORD now = GetTickCount() - m_extract_time;
+ if (!now) now = 1000; //safety
+ wchar_t tmp[512 + 128] = {0};
+
+ {
+ int total_t = 0;
+ if (wParam) total_t = MulDiv(100, now, (int)wParam);
+ int rem_t = total_t - now;
+
+ double extracted_time = (double) m_fcs.bytes_done * (1.0 / 44100.0 / 4.0);
+ if (extracted_time < 1.0) extracted_time = 1.0;
+ int br = (int) (((double)m_fcs.bytes_out * 8.0 / extracted_time) / 1000.0 + 0.5);
+
+ int estsize = 0;
+ if (m_fcs.bytes_total > 0) estsize = MulDiv(m_fcs.bytes_out, m_fcs.bytes_total, m_fcs.bytes_done);
+
+ if (rem_t < 0) rem_t = 0;
+ if (total_t < 0) total_t = 0;
+
+ StringCchPrintf(tmp, 640,
+ WASABI_API_LNGSTRINGW(IDS_ELAPSED_X_REMAINING_X_TOTAL_X),
+ now / 1000 / 60, (now / 1000) % 60,
+ rem_t / 1000 / 60, (rem_t / 1000) % 60,
+ total_t / 1000 / 60, (total_t / 1000) % 60,
+ extracted_time / ((double)now / 1000.0),
+ br, (double)estsize * (1.0 / (1024.0*1024.0))
+ );
+ SetDlgItemText(hwndDlg, IDC_STATUS, tmp);
+
+ if (m_hwndstatus && IsWindow(m_hwndstatus))
+ {
+ StringCchPrintf(m_last_item_status, 512,
+ WASABI_API_LNGSTRINGW(IDS_X_KBPS_AT_X_REALTIME),
+ wParam,
+ br,
+ extracted_time / ((double)now / 1000.0)
+ );
+ m_statuslist.SetItemText(m_extract_nb, 3, m_last_item_status);
+ }
+ }
+
+ {
+ int total_in_bytes_calc = m_rip_params->total_length_bytes;
+
+ now += m_pstat_timedone;
+
+ int total_t = 0;
+ int bytesdone = m_fcs.bytes_done + m_pstat_bytesdone;
+ int bytesout = m_fcs.bytes_out + m_pstat_bytesout;
+ if (bytesdone) total_t = MulDiv(total_in_bytes_calc, now, bytesdone);
+
+ int rem_t = total_t - now;
+
+ double extracted_time = (double) bytesdone * (1.0 / 44100.0 / 4.0);
+ if (extracted_time < 1.0) extracted_time = 1.0;
+ int br = (int) (((double)bytesout * 8.0 / extracted_time) / 1000.0 + 0.5);
+
+ int estsize = 0;
+ if (total_in_bytes_calc > 0) estsize = MulDiv(bytesout, total_in_bytes_calc, bytesdone);
+
+ if (rem_t < 0) rem_t = 0;
+ if (total_t < 0) total_t = 0;
+
+ StringCchPrintf(m_last_total_status, 512,
+ WASABI_API_LNGSTRINGW(IDS_X_OF_X_ELAPSED_X_REMAINING_X),
+ m_extract_nb + 1, m_extract_nb_total,
+ now / 1000 / 60, (now / 1000) % 60,
+ rem_t / 1000 / 60, (rem_t / 1000) % 60,
+ total_t / 1000 / 60, (total_t / 1000) % 60,
+ extracted_time / ((double)now / 1000.0),
+ br, (double)estsize * (1.0 / (1024.0*1024.0))
+ );
+
+ if (m_hwndstatus && IsWindow(m_hwndstatus))
+ {
+ SetDlgItemText(m_hwndstatus, IDC_CDINFO, m_last_total_status);
+ }
+ SetDlgItemText(hwndDlg, IDC_STATUS2, m_last_total_status);
+
+ int a = 0;
+ if (total_in_bytes_calc) a = MulDiv(bytesdone, 100, total_in_bytes_calc);
+ SendDlgItemMessage(hwndDlg, IDC_PROGRESS2, PBM_SETPOS, a, 0);
+
+ StringCchPrintf(tmp, 640, WASABI_API_LNGSTRINGW(IDS_X_PERCENT_RIPPING_FROM_CD), a);
+ SetWindowText(hwndDlg, tmp);
+ }
+
+ SendDlgItemMessage(hwndDlg, IDC_PROGRESS1, PBM_SETPOS, wParam, 0);
+ }
+ break;
+ case IPC_CB_CONVERT_DONE:
+
+ SendMessage(plugin.hwndWinampParent , WM_WA_IPC, (WPARAM)&m_fcs, IPC_CONVERTFILEW_END);
+ free(m_fcs.destfile);
+ m_pstat_bytesdone += m_fcs.bytes_done;
+ m_pstat_bytesout += m_fcs.bytes_out;
+ if (m_extract_time) m_pstat_timedone += GetTickCount() - m_extract_time;
+
+ CopyFileW(m_rip_params->tempFilenames[m_extract_curtrack], m_rip_params->filenames[m_extract_curtrack], FALSE);
+ DeleteFileW(m_rip_params->tempFilenames[m_extract_curtrack]);
+ if (AGAVE_API_STATS)
+ {
+ AGAVE_API_STATS->IncrementStat(api_stats::RIP_COUNT);
+ AGAVE_API_STATS->SetStat(api_stats::RIP_FORMAT, m_fcs.destformat[0]);
+ }
+ wchar_t *lastfn = m_rip_params->filenames[m_extract_curtrack];
+
+ if (g_config->ReadInt(L"extracttag", 1))
+ {
+ // add metadata to this file
+ if (updateFileInfoW(lastfn, L"title", m_rip_params->tracks[m_extract_curtrack]))
+ {
+ updateFileInfoW(lastfn, L"conductor", m_rip_params->conductors[m_extract_curtrack]);
+ updateFileInfoW(lastfn, L"composer", m_rip_params->composers[m_extract_curtrack]);
+ updateFileInfoW(lastfn, L"GracenoteFileID", m_rip_params->gracenoteFileIDs[m_extract_curtrack]);
+ updateFileInfoW(lastfn, L"GracenoteExtData", m_rip_params->gracenoteExtData[m_extract_curtrack]);
+ updateFileInfoW(lastfn, L"artist", m_rip_params->trackArtists[m_extract_curtrack]);
+ //if (lstrcmpiW(m_rip_params->trackArtists[m_extract_curtrack], m_rip_params->artist)) // only write albumartist if they're different
+ updateFileInfoW(lastfn, L"albumartist", m_rip_params->artist);
+ updateFileInfoW(lastfn, L"album", m_rip_params->album);
+ updateFileInfoW(lastfn, L"genre", m_rip_params->genre);
+ updateFileInfoW(lastfn, L"year", m_rip_params->year);
+ updateFileInfoW(lastfn, L"disc", m_rip_params->disc);
+ updateFileInfoW(lastfn, L"publisher", m_rip_params->publisher);
+ if (m_rip_params->comment && m_rip_params->comment[0])
+ updateFileInfoW(lastfn, L"comment", m_rip_params->comment);
+ else
+ {
+ TCHAR szComment[8192] = {0};
+ Settings_ReadString(C_EXTRACT, EF_COMMENTTEXT, szComment, ARRAYSIZE(szComment));
+ updateFileInfoW(lastfn, L"comment", szComment);
+ }
+
+ wchar_t buf[32] = {0};
+ if (m_extract_curtrack >= 0)
+ {
+ if (g_config->ReadInt(L"total_tracks", 0))
+ StringCchPrintfW(buf, 32, L"%d/%d", m_extract_curtrack + g_config->ReadInt(L"trackoffs", 1), m_rip_params->ntracks);
+ else
+ StringCchPrintfW(buf, 32, L"%d", m_extract_curtrack + g_config->ReadInt(L"trackoffs", 1));
+ }
+ else buf[0] = 0;
+ updateFileInfoW(lastfn, L"track", buf);
+
+ if (WASABI_API_APP)
+ updateFileInfoW(lastfn, L"tool", WASABI_API_APP->main_getVersionString());
+ SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_WRITE_EXTENDED_FILE_INFO);
+ }
+ }
+
+ if (g_config->ReadInt(L"extractaddml", 1))
+ {
+ LMDB_FILE_ADD_INFOW fi = {lastfn, -1, -1};
+ SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_DB_ADDORUPDATEFILEW, (WPARAM)&fi);
+ m_db_has_upd = 1;
+ }
+
+ if (m_hwndstatus && IsWindow(m_hwndstatus))
+ {
+ m_statuslist.SetItemText(m_extract_nb, 3, WASABI_API_LNGSTRING(IDS_WAITING));
+ }
+
+ if (rgThread)
+ QueueUserAPC(CalculateGain, rgThread, (ULONG_PTR)_wcsdup(lastfn));
+ else
+ PostMessage(m_extract_wnd, WM_APP + 3, 0, 0);
+
+
+ m_extract_nb++;
+ m_extracting = 0;
+ PostMessage(hwndDlg, WM_APP + 1, 0, 0);
+ break;
+ }
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_BUTTON1:
+ {
+ wchar_t title[64] = {0};
+ if (m_rip_done ||
+ MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(IDS_CANCEL_RIP),
+ WASABI_API_LNGSTRINGW_BUF(IDS_CD_RIP_QUESTION,title,64),
+ MB_YESNO | MB_ICONQUESTION) == IDYES)
+ {
+ LockCD(m_rip_params->drive_letter, FALSE);
+ DestroyWindow(hwndDlg);
+ }
+ return 0;
+ }
+ case IDCANCEL:
+ g_config->WriteInt(L"cdripstatuswnd", 0);
+ ShowWindow(hwndDlg, SW_HIDE);
+ break;
+ }
+ break;
+ case WM_CLOSE:
+ return 0;
+ case WM_DESTROY:
+ if (m_extracting)
+ {
+ SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&m_fcs, IPC_CONVERTFILEW_END);
+ // make sure we clean up on cancel!
+ m_extracting = 0;
+ DeleteFileW(m_rip_params->tempFilenames[m_extract_curtrack]);
+ }
+ if (uMsgRipperNotify) SendNotifyMessage(HWND_BROADCAST, uMsgRipperNotify, (WPARAM)(m_rip_params) ? m_rip_params->drive_letter : 0, (LPARAM)FALSE);
+ if (m_rip_params)
+ {
+ int i;
+ for (i = 0; i < m_rip_params->ntracks; i++)
+ {
+ free(m_rip_params->tracks[i]);
+ free(m_rip_params->trackArtists[i]);
+ free(m_rip_params->composers[i]);
+ free(m_rip_params->gracenoteFileIDs[i]);
+ free(m_rip_params->gracenoteExtData[i]);
+ free(m_rip_params->conductors[i]);
+ free(m_rip_params->filenames[i]);
+ free(m_rip_params->tempFilenames[i]);
+ }
+ free(m_rip_params->gracenoteFileIDs);
+ free(m_rip_params->gracenoteExtData);
+ free(m_rip_params->composers);
+ free(m_rip_params->conductors);
+ free(m_rip_params->tracks);
+ free(m_rip_params->trackArtists);
+ free(m_rip_params->filenames);
+ free(m_rip_params->tempFilenames);
+ free(m_rip_params->lengths);
+
+ free(m_rip_params->album);
+ free(m_rip_params->artist);
+ free(m_rip_params->genre);
+ free(m_rip_params->year);
+ free(m_rip_params->publisher);
+ free(m_rip_params->comment);
+ free(m_rip_params->disc);
+
+ free(m_rip_params);
+ m_rip_params = 0;
+ }
+ m_extract_wnd = 0;
+
+ if (rgThread)
+ QueueUserAPC(CloseGain, rgThread, 0);
+
+ break;
+ }
+ return 0;
+}
+
+void cdrip_stop_all_extracts()
+{
+ if (m_rip_params) LockCD(m_rip_params->drive_letter, FALSE);
+ if (m_extract_wnd) DestroyWindow(m_extract_wnd);
+ if (m_hwndstatus) DestroyWindow(m_hwndstatus);
+}
+
+int cdrip_isextracting(char drive)
+{
+ if (!m_rip_params) return 0;
+ if (drive == -1 && m_rip_done)
+ {
+ if (m_extract_wnd && IsWindow(m_extract_wnd)) DestroyWindow(m_extract_wnd);
+ if (m_hwndstatus && IsWindow(m_hwndstatus)) DestroyWindow(m_hwndstatus);
+ return 0;
+ }
+ if (drive == 0 || drive == -1) return toupper(m_rip_params->drive_letter);
+ return toupper(m_rip_params->drive_letter) == toupper(drive);
+}
+
+HWND cdrip_FindBurningHWND(char cLetter)
+{
+ HWND h = 0;
+ while (NULL != (h = FindWindowExW(NULL, h, L"#32770", NULL)))
+ {
+ if (!GetPropW(h, L"WARIPPER")) continue;
+ if (((char)(INT_PTR)GetPropW(h, L"DRIVE")) == cLetter) return h;
+ }
+ return NULL;
+}
+
+void cdrip_extractFiles(cdrip_params *parms)
+{
+ WASABI_API_LNGSTRINGW_BUF(IDS_INITIALIZING,m_last_total_status,512);
+ m_last_item_status[0] = 0;
+ m_rip_params = parms;
+ WASABI_API_CREATEDIALOGW(IDD_VIEW_CDROM_EXTRACT, plugin.hwndWinampParent, extract_dialogProc);
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/cmdbar_data.cpp b/Src/Plugins/Library/ml_disc/cmdbar_data.cpp
new file mode 100644
index 00000000..1cd8b511
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/cmdbar_data.cpp
@@ -0,0 +1,485 @@
+#include "main.h"
+#include <windowsx.h>
+#include "./resource.h"
+#include "./commandbar.h"
+#include <shlwapi.h>
+#include <strsafe.h>
+
+static HMLIMGLST hmlilButton = NULL;
+
+static LPCTSTR GetImageTagStr(INT resId)
+{
+ switch(resId)
+ {
+ case IDB_PLAY_NORMAL: return TEXT("button.play");
+ case IDB_PLAY_HIGHLIGHTED: return TEXT("button.play.highlighted");
+ case IDB_PLAY_PRESSED: return TEXT("button.play.pressed");
+ case IDB_PLAY_DISABLED: return TEXT("button.play.disabled");
+ case IDB_ENQUEUE_NORMAL: return TEXT("button.enqueue");
+ case IDB_ENQUEUE_HIGHLIGHTED: return TEXT("button.enqueue.highlighted");
+ case IDB_ENQUEUE_PRESSED: return TEXT("button.enqueue.pressed");
+ case IDB_ENQUEUE_DISABLED: return TEXT("button.enqueue.disabled");
+ case IDB_EJECT2_NORMAL: return TEXT("button.eject");
+ case IDB_EJECT2_HIGHLIGHTED: return TEXT("button.eject.highlighted");
+ case IDB_EJECT2_PRESSED: return TEXT("button.eject.pressed");
+ case IDB_EJECT2_DISABLED: return TEXT("button.eject.disabled");
+ }
+ return NULL;
+}
+
+static HMLIMGLST DataCmdBar_CreateImageList()
+{
+ HMLIMGLST hmlil;
+ MLIMAGELISTCREATE mlilCreate;
+ MLIMAGESOURCE mlis;
+ MLIMAGELISTITEM mlilItem;
+
+ mlilCreate.cx = g_view_metaconf->ReadIntEx(TEXT("artwork"), TEXT("button.icon.cx"), 12);
+ mlilCreate.cy = g_view_metaconf->ReadIntEx(TEXT("artwork"), TEXT("button.icon.cy"), 12);
+ mlilCreate.cInitial = 12;
+ mlilCreate.cGrow = 3;
+ mlilCreate.cCacheSize = 4;
+ mlilCreate.flags = MLILC_COLOR32;
+
+ hmlil = MLImageList_Create(plugin.hwndLibraryParent, &mlilCreate);
+ if (NULL == hmlil) return NULL;
+
+
+ ZeroMemory(&mlilItem, sizeof(MLIMAGELISTITEM));
+ mlilItem.cbSize = sizeof(MLIMAGELISTITEM);
+ mlilItem.hmlil = hmlil;
+ mlilItem.filterUID = MLIF_BUTTONBLENDPLUSCOLOR_UID;
+ mlilItem.pmlImgSource = &mlis;
+
+ ZeroMemory(&mlis, sizeof(MLIMAGESOURCE));
+ mlis.cbSize = sizeof(MLIMAGESOURCE);
+ mlis.type = SRC_TYPE_PNG;
+ mlis.hInst = plugin.hDllInstance;
+
+
+ INT imageList[] =
+ { IDB_PLAY_NORMAL, IDB_PLAY_PRESSED, IDB_PLAY_HIGHLIGHTED, IDB_PLAY_DISABLED,
+ IDB_ENQUEUE_NORMAL, IDB_ENQUEUE_PRESSED, IDB_ENQUEUE_HIGHLIGHTED, IDB_ENQUEUE_DISABLED,
+ IDB_EJECT2_NORMAL, IDB_EJECT2_PRESSED, IDB_EJECT2_HIGHLIGHTED, IDB_EJECT2_DISABLED,
+ };
+
+
+ TCHAR szResource[MAX_PATH] = {0}, szPath[MAX_PATH] = {0}, szFullPath[MAX_PATH] = {0};
+ g_view_metaconf->ReadCchStringEx(szPath, ARRAYSIZE(szPath), TEXT("artwork"), TEXT("path"), NULL);
+ for(int i = 0; i < sizeof(imageList)/sizeof(imageList[0]); i++)
+ {
+ mlilItem.nTag = imageList[i];
+ g_view_metaconf->ReadCchStringEx(szResource, ARRAYSIZE(szResource), TEXT("artwork"), GetImageTagStr(imageList[i]), NULL);
+ if (TEXT('\0') != szResource[0])
+ {
+ PathCombine(szFullPath, szPath, szResource);
+ mlis.lpszName = szFullPath;
+ mlis.flags |= ISF_LOADFROMFILE;
+ }
+ else
+ {
+ mlis.lpszName = MAKEINTRESOURCE(imageList[i]);
+ mlis.flags &= ~ISF_LOADFROMFILE;
+ }
+
+ MLImageList_Add(plugin.hwndLibraryParent, &mlilItem);
+ }
+ return hmlil;
+}
+
+static HMLIMGLST DataCmdBar_CreateDropDownImageList(HMENU hMenu)
+{
+ HMLIMGLST hmlil;
+ MLIMAGELISTCREATE mlilCreate;
+ MLIMAGESOURCE mlis;
+
+ if (!hMenu) return NULL;
+
+ mlilCreate.cx = 16;
+ mlilCreate.cy = 16;
+ mlilCreate.cInitial = 2;
+ mlilCreate.cGrow = 1;
+ mlilCreate.cCacheSize = 3;
+ mlilCreate.flags = MLILC_COLOR32;
+
+ hmlil = MLImageList_Create(plugin.hwndLibraryParent, &mlilCreate);
+ if (NULL == hmlil) return NULL;
+
+ ZeroMemory(&mlis, sizeof(MLIMAGESOURCE));
+ mlis.cbSize = sizeof(MLIMAGESOURCE);
+ mlis.type = SRC_TYPE_PNG;
+ mlis.hInst = plugin.hDllInstance;
+
+
+ INT imageList[] = { IDB_PLAY_MENU, IDB_ENQUEUE_MENU, };
+ MENUITEMINFOW mii = { sizeof(MENUITEMINFOW), };
+ mii.fMask = MIIM_ID;
+ for(int i = 0; i < sizeof(imageList)/sizeof(imageList[0]); i++)
+ {
+ if (GetMenuItemInfoW(hMenu, i, TRUE, &mii))
+ {
+ mlis.lpszName = MAKEINTRESOURCEW(imageList[i]);
+ MLImageList_Add2(plugin.hwndLibraryParent, hmlil, MLIF_FILTER1_UID, &mlis, mii.wID);
+ }
+ }
+ return hmlil;
+
+}
+
+
+
+static void DataCmdBar_SetButtonImages(HWND hButton, HMLIMGLST hmlil, INT normal, INT hover, INT pressed, INT disabled)
+{
+ MLBUTTONIMAGELIST bil;
+ MLIMAGELISTTAG t;
+ bil.hmlil = hmlil;
+ t.hmlil = bil.hmlil;
+
+ t.nTag = normal;
+ bil.normalIndex = (MLImageList_GetIndexFromTag(plugin.hwndLibraryParent, &t)) ? t.mlilIndex : -1;
+
+ if (disabled == normal) bil.disabledIndex = bil.normalIndex;
+ else
+ {
+ t.nTag = disabled;
+ bil.disabledIndex = (MLImageList_GetIndexFromTag(plugin.hwndLibraryParent, &t)) ? t.mlilIndex : bil.normalIndex;
+ }
+
+ if (hover == normal) bil.hoverIndex = bil.normalIndex;
+ else if (hover == disabled) bil.hoverIndex = bil.disabledIndex;
+ else
+ {
+ t.nTag = hover;
+ bil.hoverIndex = (MLImageList_GetIndexFromTag(plugin.hwndLibraryParent, &t)) ? t.mlilIndex : bil.normalIndex;
+ }
+
+ if (pressed == normal) bil.pressedIndex = bil.normalIndex;
+ else if (pressed == disabled) bil.pressedIndex = bil.disabledIndex;
+ else if (pressed == hover) bil.pressedIndex = bil.hoverIndex;
+ else
+ {
+ t.nTag = pressed;
+ bil.pressedIndex = (MLImageList_GetIndexFromTag(plugin.hwndLibraryParent, &t)) ? t.mlilIndex : bil.normalIndex;
+ }
+
+ SENDMLIPC(hButton, ML_IPC_SKINNEDBUTTON_SETIMAGELIST, (LPARAM)&bil);
+}
+
+static void PlayEx_Initialize(HWND hdlg)
+{
+ HWND hButton = GetDlgItem(hdlg, IDC_BTN_PLAYEX);
+ if (!hButton) return;
+
+ HWND hFileView = (HWND)CommandBar_GetData(hdlg);
+ if (NULL == hFileView) return;
+
+ HMENU hMenu = FileView_GetMenu(hFileView, FVMENU_PLAY);
+ if (!hMenu) return;
+
+ BOOL bPlay = (!hFileView || 0 == (FVS_ENQUEUE & FileView_GetStyle(hFileView)));
+
+ WCHAR szBuffer[256] = {0};
+ MENUITEMINFOW mii = { sizeof(MENUITEMINFOW), };
+ mii.fMask = MIIM_STRING;
+ mii.dwTypeData = szBuffer;
+ mii.cch = sizeof(szBuffer)/sizeof(szBuffer[0]);
+ if (GetMenuItemInfoW(hMenu, (bPlay) ? 0 : 1, TRUE, &mii))
+ {
+ while(mii.cch && L'\t' != szBuffer[mii.cch]) mii.cch--;
+ if (mii.cch > 0) szBuffer[mii.cch] = L'\0';
+ SetWindowTextW(hButton, szBuffer);
+ }
+
+ if (bPlay)
+ {
+ DataCmdBar_SetButtonImages(hButton, hmlilButton, IDB_PLAY_NORMAL,
+ IDB_PLAY_PRESSED, IDB_PLAY_HIGHLIGHTED, IDB_PLAY_DISABLED);
+ }
+ else
+ {
+ DataCmdBar_SetButtonImages(hButton, hmlilButton, IDB_ENQUEUE_NORMAL,
+ IDB_ENQUEUE_PRESSED, IDB_ENQUEUE_HIGHLIGHTED, IDB_ENQUEUE_DISABLED);
+ }
+}
+
+static void DataCmdBar_UpdateControls(HWND hdlg, BOOL bRedraw)
+{
+ INT buttonList[] = { IDC_BTN_PLAYEX, IDC_BTN_COPY, };
+ HDWP hdwp;
+ DWORD flags, size;
+ INT w;
+
+ flags = SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | ((bRedraw) ? 0 : SWP_NOREDRAW);
+ hdwp = BeginDeferWindowPos(sizeof(buttonList)/sizeof(buttonList[0]));
+ if (!hdwp) return;
+
+ for(int i =0; i < sizeof(buttonList)/sizeof(buttonList[0]); i++)
+ {
+ HWND hctrl = GetDlgItem(hdlg, buttonList[i]);
+ if (NULL != hctrl)
+ {
+ size = 0;
+ switch(buttonList[i])
+ {
+ case IDC_BTN_PLAYEX:
+ {
+ HWND hFileView = (HWND)CommandBar_GetData(hdlg);
+ if (NULL == hFileView) return;
+
+ HMENU hMenu = FileView_GetMenu(hFileView, FVMENU_PLAY);
+ if (hMenu)
+ {
+ WCHAR szText[256] = {0};
+ INT count = GetMenuItemCount(hMenu);
+ MENUITEMINFO mii = {0};
+
+ mii.cbSize = sizeof(MENUITEMINFO);
+ mii.fMask = MIIM_STRING;
+ mii.dwTypeData = szText;
+
+ w = 0;
+ for (int i = 0; i < count; i++)
+ {
+ mii.cch = sizeof(szText)/sizeof(szText[0]);
+ if (GetMenuItemInfo(hMenu, i, TRUE, &mii))
+ {
+ while(mii.cch && L'\t' != szText[mii.cch]) mii.cch--;
+ if (mii.cch > 0) szText[mii.cch] = L'\0';
+ size = MLSkinnedButton_GetIdealSize(hctrl, szText);
+ if (w < LOWORD(size)) w = LOWORD(size);
+ }
+ }
+ size = MAKELONG(w + 8, HIWORD(size));
+ }
+ }
+ break;
+ default:
+ size = MLSkinnedButton_GetIdealSize(hctrl, NULL);
+ break;
+ }
+
+ INT width = LOWORD(size), height = HIWORD(size);
+ if (width < 82) width = 82;
+ if (height < 14) height = 14;
+
+ hdwp = DeferWindowPos(hdwp, hctrl, NULL, 0, 0, width, height, flags);
+ }
+ }
+
+ EndDeferWindowPos(hdwp);
+
+ SetWindowPos(hdlg, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE |
+ SWP_NOZORDER | SWP_FRAMECHANGED | ((bRedraw) ? 0 : SWP_NOREDRAW));
+}
+
+static BOOL DataCmdBar_OnInitDialog(HWND hdlg, HWND hwndFocus, LPARAM lParam)
+{
+ MLSKINWINDOW sw;
+ sw.skinType = SKINNEDWND_TYPE_AUTO;
+ sw.style = SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWS_USESKINFONT;
+ sw.hwndToSkin = hdlg;
+ MLSkinWindow(plugin.hwndLibraryParent, &sw);
+
+
+ sw.hwndToSkin = GetDlgItem(hdlg, IDC_BTN_EJECT);
+ MLSkinWindow(plugin.hwndLibraryParent, &sw);
+
+ sw.style |= SWBS_SPLITBUTTON;
+ sw.hwndToSkin = GetDlgItem(hdlg, IDC_BTN_PLAYEX);
+ MLSkinWindow(plugin.hwndLibraryParent, &sw);
+
+ sw.style = SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWS_USESKINFONT;
+ sw.hwndToSkin = GetDlgItem(hdlg, IDC_BTN_COPY);
+ MLSkinWindow(plugin.hwndLibraryParent, &sw);
+
+
+ sw.skinType = SKINNEDWND_TYPE_STATIC;
+ sw.style = SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWS_USESKINFONT;
+ sw.hwndToSkin = GetDlgItem(hdlg, IDC_LBL_STATUS);
+ MLSkinWindow(plugin.hwndLibraryParent, &sw);
+
+ hmlilButton = DataCmdBar_CreateImageList();
+
+ PlayEx_Initialize(hdlg);
+
+ DataCmdBar_SetButtonImages(GetDlgItem(hdlg, IDC_BTN_EJECT), hmlilButton, IDB_EJECT2_NORMAL,
+ IDB_EJECT2_PRESSED, IDB_EJECT2_HIGHLIGHTED, IDB_EJECT2_DISABLED);
+
+ DataCmdBar_UpdateControls(hdlg, FALSE);
+ return FALSE;
+}
+
+static void DataCmdBar_OnDestroy(HWND hdlg)
+{
+ if (hmlilButton)
+ {
+ MLImageList_Destroy(plugin.hwndLibraryParent, hmlilButton);
+ hmlilButton = NULL;
+ }
+}
+
+static void DataCmdBar_OnWindowPosChanged(HWND hdlg, WINDOWPOS *pwp)
+{
+ if (0 == (SWP_NOSIZE & pwp->flags) || 0 != (SWP_FRAMECHANGED & pwp->flags))
+ {
+ HWND hctrl;
+ HDWP hdwp;
+ RECT rc, rw;
+ DWORD flags;
+
+ if (!GetClientRect(hdlg, &rc)) return;
+ InflateRect(&rc, -2, 0);
+ LONG left = rc.left-2;
+ LONG right = rc.right;
+
+ hdwp = BeginDeferWindowPos(4);
+ if (!hdwp) return;
+
+ flags = SWP_NOACTIVATE | SWP_NOZORDER | ((SWP_NOREDRAW | SWP_NOCOPYBITS) & pwp->flags);
+
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDC_BTN_PLAYEX)) && GetWindowRect(hctrl, &rw))
+ {
+ hdwp = DeferWindowPos(hdwp, hctrl, NULL, left, rc.top, rw.right - rw.left, rc.bottom - rc.top, flags);
+ left += ((rw.right - rw.left) + 8);
+ }
+
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDC_BTN_COPY)) && GetWindowRect(hctrl, &rw))
+ {
+ hdwp = DeferWindowPos(hdwp, hctrl, NULL, left, rc.top, rw.right - rw.left, rc.bottom - rc.top, flags);
+ left += ((rw.right - rw.left) + 8);
+ }
+
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDC_BTN_EJECT)) && GetWindowRect(hctrl, &rw))
+ {
+ right -= (rw.right - rw.left);
+ if (right < (left + 16)) right = left + 16;
+
+ hdwp = DeferWindowPos(hdwp, hctrl, NULL, right, rc.top, rw.right - rw.left, rc.bottom - rc.top, flags);
+ right -= 4;
+ }
+
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDC_LBL_STATUS)) && GetWindowRect(hctrl, &rw))
+ {
+ hdwp = DeferWindowPos(hdwp, hctrl, NULL, left, rc.top, right - left, rc.bottom - rc.top, flags);
+ }
+ EndDeferWindowPos(hdwp);
+ }
+ if (0 == (SWP_NOREDRAW & pwp->flags)) InvalidateRect(GetDlgItem(hdlg, IDC_LBL_STATUS), NULL, TRUE);
+}
+
+static void DataCmdBar_OnPlayDropDown(HWND hdlg, HWND hctrl)
+{
+ RECT r;
+ if (!GetWindowRect(hctrl, &r)) return;
+
+ HWND hFileView = (HWND)CommandBar_GetData(hdlg);
+ if (NULL == hFileView) return;
+
+
+ HMENU hMenu = FileView_GetMenu(hFileView, FVMENU_PLAY);
+ if (!hMenu) return;
+
+
+ MLSkinnedButton_SetDropDownState(hctrl, TRUE);
+
+ HMLIMGLST hmlilDropDown = DataCmdBar_CreateDropDownImageList(hMenu);
+
+ MLTrackSkinnedPopupMenuEx(plugin.hwndLibraryParent, hMenu,
+ TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_BOTTOMALIGN | TPM_LEFTALIGN | TPM_NONOTIFY,
+ r.left, r.top - 2, hFileView, NULL, hmlilDropDown, r.right - r.left,
+ SMS_USESKINFONT, NULL, 0L);
+
+ MLSkinnedButton_SetDropDownState(hctrl, FALSE);
+ MLImageList_Destroy(plugin.hwndLibraryParent, hmlilDropDown);
+}
+
+static void DataCmdBar_OnPlayClick(HWND hdlg, HWND hButton)
+{
+ EnableWindow(hButton, FALSE);
+
+ HWND hFileView = (HWND)CommandBar_GetData(hdlg);
+ if (NULL != hFileView)
+ {
+ HMENU hMenu = FileView_GetMenu(hFileView, FVMENU_PLAY);
+ if (NULL != hMenu)
+ {
+ UINT uCmd = (FVS_ENQUEUE & FileView_GetStyle(hFileView)) ? FVA_ENQUEUE : FVA_PLAY;
+ SendMessageW(hFileView, WM_COMMAND, MAKEWPARAM(FileView_GetActionCommand(hFileView, uCmd), 0), 0L);
+ }
+ }
+
+ EnableWindow(hButton, TRUE);
+
+}
+
+static void DataCmdBar_OnCommand(HWND hdlg, INT eventId, INT ctrlId, HWND hwndCtrl)
+{
+ switch (ctrlId)
+ {
+ case IDC_BTN_PLAYEX:
+ switch(eventId)
+ {
+ case MLBN_DROPDOWN: DataCmdBar_OnPlayDropDown(hdlg, hwndCtrl); break;
+ case BN_CLICKED: DataCmdBar_OnPlayClick(hdlg, hwndCtrl); break;
+ }
+ break;
+ case IDC_BTN_EJECT:
+ switch(eventId)
+ {
+ case BN_CLICKED: SendMessageW(GetParent(hdlg), WM_COMMAND, MAKEWPARAM(ID_EJECT_DISC, 0), 0L); break; // straight to container...
+ }
+ break;
+ case IDC_BTN_COPY:
+ switch(eventId)
+ {
+ case BN_CLICKED: SendMessageW(hdlg, WM_COMMAND, MAKEWPARAM(ID_COPY_SELECTION, 0), 0L); break;
+ }
+ break;
+ }
+}
+
+
+static INT DataCmdBar_OnGetBestHeight(HWND hdlg)
+{
+ INT h, height = 0;
+ INT buttonList[] = { IDC_BTN_PLAYEX, };
+
+ for(int i =0; i < sizeof(buttonList)/sizeof(buttonList[0]); i++)
+ {
+ HWND hctrl = GetDlgItem(hdlg, buttonList[i]);
+ if (NULL != hctrl)
+ {
+ DWORD sz = MLSkinnedButton_GetIdealSize(hctrl, NULL);
+ h = HIWORD(sz);
+ if (height < h) height = h;
+ }
+ }
+
+ return height;
+}
+
+INT_PTR WINAPI DataCmdBar_DialogProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG: return DataCmdBar_OnInitDialog(hdlg, (HWND)wParam, lParam);
+ case WM_DESTROY: DataCmdBar_OnDestroy(hdlg); break;
+ case WM_WINDOWPOSCHANGED: DataCmdBar_OnWindowPosChanged(hdlg, (WINDOWPOS*)lParam); return TRUE;
+ case WM_COMMAND: DataCmdBar_OnCommand(hdlg, HIWORD(wParam), LOWORD(wParam), (HWND)lParam); break;
+ case CBM_GETBESTHEIGHT: SetWindowLongPtrW(hdlg, DWLP_MSGRESULT, DataCmdBar_OnGetBestHeight(hdlg)); return TRUE;
+ case WM_DISPLAYCHANGE:
+ PlayEx_Initialize(hdlg);
+ break;
+ case WM_SETFONT:
+ DataCmdBar_UpdateControls(hdlg, LOWORD(lParam));
+ return 0;
+ case WM_SETTEXT:
+ case WM_GETTEXT:
+ case WM_GETTEXTLENGTH:
+ SetWindowLongPtrW(hdlg, DWLP_MSGRESULT, (LONGX86)(LONG_PTR)SendDlgItemMessageW(hdlg, IDC_LBL_STATUS, uMsg, wParam, lParam));
+ return TRUE;
+
+ }
+
+ return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/commandbar.cpp b/Src/Plugins/Library/ml_disc/commandbar.cpp
new file mode 100644
index 00000000..ef560c68
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/commandbar.cpp
@@ -0,0 +1,119 @@
+#include "main.h"
+#include "./commandbar.h"
+
+typedef struct _CMDBAR
+{
+ HWND hwndOwner;
+ DLGPROC fnDlgProc;
+ ULONG_PTR uData;
+} CMDBAR;
+
+#define GetBarData(/*HWND*/ __hwndCmdBar) ((CMDBAR*)GetPropW((__hwndCmdBar), L"MLDISCCMDBAR"))
+
+
+static BOOL CommandBar_OnInitDialog(HWND hdlg, HWND hwndFocus, LPARAM lParam)
+{
+ CMDBARCREATESTRUCT *pcbcs = (CMDBARCREATESTRUCT*)lParam;
+ if (pcbcs && pcbcs->fnDialogProc)
+ {
+ CMDBAR *pcb = (CMDBAR*)calloc(1, sizeof(CMDBAR));
+ if (pcb)
+ {
+ pcb->fnDlgProc = pcbcs->fnDialogProc;
+ pcb->hwndOwner = pcbcs->hwndOwner;
+ pcb->uData = pcbcs->uData;
+ if (!SetPropW(hdlg, L"MLDISCCMDBAR", (HANDLE)pcb))
+ {
+ free(pcb);
+ DestroyWindow(hdlg);
+ }
+ else return pcbcs->fnDialogProc(hdlg, WM_INITDIALOG, (WPARAM)hwndFocus, pcbcs->uData);
+ }
+ }
+ return FALSE;
+}
+
+static void CommandBar_OnDestroy(HWND hdlg)
+{
+ CMDBAR *pcb = GetBarData(hdlg);
+ if (pcb)
+ {
+ RemovePropW(hdlg, L"MLDISCCMDBAR");
+ free(pcb);
+ }
+}
+
+static BOOL CALLBACK CommandBar_EnumChildHeight(HWND hwnd, LPARAM param)
+{
+ if (!param) return FALSE;
+ HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE);
+ if (hdc)
+ {
+ HFONT hf = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0L);
+ if (NULL == hf) hf = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+ if (NULL != hf)
+ {
+ TEXTMETRICW tm = {0};
+ HFONT hfo = (HFONT)SelectObject(hdc, hf);
+ if (GetTextMetricsW(hdc, &tm))
+ {
+ INT *pmh = (INT*)param;
+ if (tm.tmHeight > *pmh) *pmh = tm.tmHeight;
+ }
+ SelectObject(hdc, hfo);
+ }
+ ReleaseDC(hwnd, hdc);
+ }
+ return TRUE;
+
+}
+static INT CommandBar_OnGetBestHeight(HWND hwnd)
+{
+ INT maxHeight = 0;
+ EnumChildWindows(hwnd, CommandBar_EnumChildHeight, (LPARAM)&maxHeight);
+ if (maxHeight != 0) maxHeight += 2;
+ return maxHeight;
+}
+
+INT_PTR CALLBACK CommandBar_DialogProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ CMDBAR *pcb = GetBarData(hdlg);
+ if (!pcb)
+ {
+ if (WM_INITDIALOG == uMsg) return CommandBar_OnInitDialog(hdlg, (HWND)wParam, lParam);
+ return 0;
+ }
+ if (pcb->fnDlgProc(hdlg, uMsg, wParam, lParam))
+ {
+ if (WM_DESTROY == uMsg) CommandBar_OnDestroy(hdlg);
+ return TRUE;
+ }
+
+ switch(uMsg)
+ {
+ case WM_DESTROY: CommandBar_OnDestroy(hdlg); return TRUE;
+ case WM_COMMAND:
+ if (pcb->hwndOwner) SendMessageW(pcb->hwndOwner, uMsg, wParam, lParam); return TRUE;
+ break;
+
+ case CBM_GETBESTHEIGHT: MSGRESULT(hdlg, CommandBar_OnGetBestHeight(hdlg));
+ case CBM_GETOWNER: MSGRESULT(hdlg, pcb->hwndOwner);
+ case CBM_SETOWNER:
+ SetWindowLongPtrW(hdlg, DWLP_MSGRESULT, (LONGX86)(LONG_PTR)pcb->hwndOwner);
+ pcb->hwndOwner = (HWND)lParam;
+ return TRUE;
+ case CBM_GETDATA:
+ {
+ CMDBAR *pBar = GetBarData(hdlg);
+ MSGRESULT(hdlg, (pBar) ? pBar->uData : NULL);
+ }
+ case CBM_SETDATA:
+ {
+ CMDBAR *pBar = GetBarData(hdlg);
+ if (pBar) pBar->uData = (ULONG_PTR)lParam;
+ MSGRESULT(hdlg, (NULL != pBar));
+ }
+
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/commandbar.h b/Src/Plugins/Library/ml_disc/commandbar.h
new file mode 100644
index 00000000..6ff90583
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/commandbar.h
@@ -0,0 +1,48 @@
+#ifndef NULLOSFT_MEDIALIBRARY_COMMANDBAR_CONTROL_HEADER
+#define NULLOSFT_MEDIALIBRARY_COMMANDBAR_CONTROL_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <windows.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef CBM_FIRST
+#define CBM_FIRST (WM_APP + 100)
+#endif
+
+#define CBM_GETBESTHEIGHT (CBM_FIRST + 1)
+#define CBM_GETOWNER (CBM_FIRST + 2)
+#define CBM_SETOWNER (CBM_FIRST + 3)
+#define CBM_GETDATA (CBM_FIRST + 4)
+#define CBM_SETDATA (CBM_FIRST + 5)
+
+#define CommandBar_GetBestHeight(/*HWND*/ __hwndCB)\
+ ((INT)(INT_PTR)SENDMSG((__hwndCB), CBM_GETBESTHEIGHT, 0, 0L))
+
+#define CommandBar_GetOwner(/*HWND*/ __hwndCB)\
+ ((HWND)SENDMSG((__hwndCB), CBM_GETOWNER, 0, 0L))
+
+#define CommandBar_SetOwner(/*HWND*/ __hwndCB, /*HWND*/ __hwndNewOwner)\
+ ((BOOL)SENDMSG((__hwndCB), CBM_SETOWNER, 0, (LPARAM)(__hwndNewOwner)))
+
+#define CommandBar_GetData(/*HWND*/ __hwndCB)\
+ ((HWND)SENDMSG((__hwndCB), CBM_GETDATA, 0, 0L))
+
+#define CommandBar_SetData(/*HWND*/ __hwndCB, /*ULONG_PTR*/ __userData)\
+ ((BOOL)SENDMSG((__hwndCB), CBM_SETDATA, 0, (LPARAM)(__userData)))
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+#endif // NULLOSFT_MEDIALIBRARY_COMMANDBAR_CONTROL_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/config.cpp b/Src/Plugins/Library/ml_disc/config.cpp
new file mode 100644
index 00000000..71474145
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/config.cpp
@@ -0,0 +1,129 @@
+#include "main.h"
+#include "./config.h"
+#include <shlwapi.h>
+#include <strsafe.h>
+
+#define DEFAULT_SECTION TEXT("gen_ml_config")
+
+#define STRCOMP_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
+
+C_Config::~C_Config()
+{
+ if (m_inifile) CoTaskMemFree(m_inifile);
+}
+
+C_Config::C_Config(LPCTSTR pszIniFile)
+{
+ if (S_OK != SHStrDup(pszIniFile, &m_inifile))
+ m_inifile = NULL;
+}
+
+void C_Config::WriteIntEx(LPCTSTR pszSection, LPCTSTR pszKey, int nValue)
+{
+ TCHAR buf[32] = {0};
+ StringCchPrintf(buf, ARRAYSIZE(buf), TEXT("%d"), nValue);
+ WriteStringEx(pszSection, pszKey, buf);
+}
+
+int C_Config::ReadIntEx(LPCTSTR pszSection, LPCTSTR pszKey, int nDefault)
+{
+ return GetPrivateProfileInt(pszSection, pszKey, nDefault, m_inifile);
+}
+
+void C_Config::WriteStringEx(LPCTSTR pszSection, LPCTSTR pszKey, LPCTSTR pszValue)
+{
+ WritePrivateProfileString(pszSection, pszKey, pszValue, m_inifile);
+ }
+
+LPTSTR C_Config::ReadCbStringEx(LPTSTR pszBuffer, INT cbBuffer, LPCTSTR pszSection, LPCTSTR pszKey, LPCTSTR pszDefault)
+{
+ return ReadCchStringEx(pszBuffer, cbBuffer/sizeof(TCHAR), pszSection, pszKey, pszDefault);
+}
+
+LPTSTR C_Config::ReadCchStringEx(LPTSTR pszBuffer, INT cchBuffer, LPCTSTR pszSection, LPCTSTR pszKey, LPCTSTR pszDefault)
+{
+ const static TCHAR foobuf[] = TEXT("___________gen_ml_lameness___________");
+ pszBuffer[0] = TEXT('\0');
+ GetStringEx(pszSection, pszKey, foobuf, pszBuffer, cchBuffer);
+ pszBuffer[cchBuffer -1] = TEXT('\0');
+ if (CSTR_EQUAL == CompareString(STRCOMP_INVARIANT, 0, foobuf, -1, pszBuffer, -1))
+ {
+ if (S_OK != StringCchCopyEx(pszBuffer, cchBuffer, pszDefault, NULL, NULL, STRSAFE_IGNORE_NULLS))
+ return NULL;
+ }
+ return pszBuffer;
+}
+
+LPTSTR C_Config::ReadCchQuotedString(LPTSTR pszBuffer, INT cchBuffer, LPCTSTR pszSection, LPCTSTR pszKey, LPCTSTR pszDefault)
+{
+ LPTSTR p = ReadCchStringEx(pszBuffer, cchBuffer, pszSection, pszKey, pszDefault);
+ if (p) PathUnquoteSpaces(pszBuffer);
+ return p;
+}
+
+LPTSTR C_Config::ReadCbQuotedString(LPTSTR pszBuffer, INT cbBuffer, LPCTSTR pszSection, LPCTSTR pszKey, LPCTSTR pszDefault)
+{
+ return ReadCchQuotedString(pszBuffer, cbBuffer/sizeof(TCHAR), pszSection, pszKey, pszDefault);
+}
+
+void C_Config::WriteQuotedString(LPCTSTR pszSection, LPCTSTR pszKey, LPCTSTR pszValue)
+{
+ if (!pszValue) return;
+ INT cch = lstrlen(pszValue);
+ if (cch < MAX_PATH)
+ {
+ TCHAR szBuffer[MAX_PATH] = {0};
+ if (S_OK == StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), pszValue))
+ {
+ PathQuoteSpaces(szBuffer);
+ WriteStringEx(pszSection, pszKey, szBuffer);
+ return;
+ }
+ }
+ else
+ {
+ LPTSTR pszBuffer = (LPTSTR)calloc((cch + 4), sizeof(TCHAR));
+ if (pszBuffer)
+ {
+ if (S_OK == StringCchCopy(pszBuffer, cch + 4, pszValue))
+ {
+ PathQuoteSpaces(pszBuffer);
+ WriteStringEx(pszSection, pszKey, pszBuffer);
+ free(pszBuffer);
+ return;
+ }
+ free(pszBuffer);
+ }
+ }
+ WriteStringEx(pszSection, pszKey, pszValue);
+}
+
+DWORD C_Config::GetStringEx(LPCTSTR pszSection, LPCTSTR pszKey, LPCTSTR pszDefault, LPTSTR pszReturnedValue, DWORD nSize)
+{
+ return GetPrivateProfileString(pszSection, pszKey, pszDefault, pszReturnedValue, nSize, m_inifile);
+}
+
+void C_Config::WriteInt(LPCTSTR pszKey, int nValue)
+{
+ WriteIntEx(DEFAULT_SECTION, pszKey, nValue);
+}
+
+int C_Config::ReadInt(LPCTSTR pszKey, int nDefault)
+{
+ return ReadIntEx(DEFAULT_SECTION, pszKey, nDefault);
+}
+
+void C_Config::WriteString(LPCTSTR pszKey, LPCTSTR pszValue)
+{
+ WriteStringEx(DEFAULT_SECTION, pszKey, pszValue);
+}
+
+LPCTSTR C_Config::ReadString(LPCTSTR pszKey, LPCTSTR pszDefault)
+{
+ static TCHAR szBuffer[4096];
+ return ReadCchStringEx(szBuffer, ARRAYSIZE(szBuffer), DEFAULT_SECTION, pszKey, pszDefault);
+}
+DWORD C_Config::GetString(LPCTSTR pszKey, LPCTSTR pszDefault, LPTSTR pszReturnedValue, DWORD nSize)
+{
+ return GetStringEx(DEFAULT_SECTION, pszKey, pszDefault, pszReturnedValue, nSize);
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/config.h b/Src/Plugins/Library/ml_disc/config.h
new file mode 100644
index 00000000..6ec17685
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/config.h
@@ -0,0 +1,44 @@
+#ifndef _C_CONFIG_H_
+#define _C_CONFIG_H_
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#define C_CONFIG_WIN32NATIVE
+#include <windows.h>
+
+class C_Config
+{
+ public:
+ C_Config(LPCTSTR pszIniFile);
+ ~C_Config();
+
+ void WriteIntEx(LPCTSTR pszSection, LPCTSTR pszKey, int nValue);
+ void WriteStringEx(LPCTSTR pszSection, LPCTSTR pszKey, LPCTSTR pszValue);
+ int ReadIntEx(LPCTSTR pszSection, LPCTSTR pszKey, int nDefault);
+ LPTSTR ReadCchStringEx(LPTSTR pszBuffer, INT cchBuffer, LPCTSTR pszSection, LPCTSTR pszKey, LPCTSTR pszDefault);
+ LPTSTR ReadCbStringEx(LPTSTR pszBuffer, INT cbBuffer, LPCTSTR pszSection, LPCTSTR pszKey, LPCTSTR pszDefault);
+ DWORD GetStringEx(LPCTSTR pszSection, LPCTSTR pszKey, LPCTSTR pszDefault, LPTSTR pszReturnedValue, DWORD nSize);
+
+ void WriteBoolEx(LPCTSTR pszSection, LPCTSTR pszKey, int nValue) { WriteIntEx(pszSection, pszKey, ( 0 != nValue)); }
+ BOOL ReadBoolEx(LPCTSTR pszSection, LPCTSTR pszKey, int nDefault) { return (0 != ReadIntEx(pszSection, pszKey, nDefault)); }
+
+ LPTSTR ReadCchQuotedString(LPTSTR pszBuffer, INT cchBuffer, LPCTSTR pszSection, LPCTSTR pszKey, LPCTSTR pszDefault);
+ LPTSTR ReadCbQuotedString(LPTSTR pszBuffer, INT cbBuffer, LPCTSTR pszSection, LPCTSTR pszKey, LPCTSTR pszDefault);
+ void WriteQuotedString(LPCTSTR pszSection, LPCTSTR pszKey, LPCTSTR pszValue);
+
+ void WriteInt(LPCTSTR pszKey, int nValue);
+ void WriteString(LPCTSTR pszKey, LPCTSTR pszValue);
+ int ReadInt(LPCTSTR pszKey, int nDefault);
+ LPCTSTR ReadString(LPCTSTR pszKey, LPCTSTR pszDefault);
+ DWORD GetString(LPCTSTR pszKey, LPCTSTR pszDefault, LPTSTR pszReturnedValue, DWORD nSize);
+
+ void WriteBool(LPCTSTR pszKey, int nValue) { WriteInt(pszKey, ( 0 != nValue)); }
+ BOOL ReadBool(LPCTSTR pszKey, int nDefault) { return (0 != ReadInt(pszKey, nDefault)); }
+
+ private:
+ TCHAR *m_inifile;
+};
+
+#endif//_C_CONFIG_H_
diff --git a/Src/Plugins/Library/ml_disc/copyfiles.cpp b/Src/Plugins/Library/ml_disc/copyfiles.cpp
new file mode 100644
index 00000000..426bd79d
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/copyfiles.cpp
@@ -0,0 +1,613 @@
+#include "main.h"
+#include "./copyfiles.h"
+#include "./copyinternal.h"
+#include "./resource.h"
+#include "./settings.h"
+#include "../nu/trace.h"
+#include <api/service/waServiceFactory.h>
+#include <shlwapi.h>
+#include <strsafe.h>
+
+static LONG szBusyDrive[26] = {0, };
+static CRITICAL_SECTION cs_copy = { 0,};
+
+static void NotifyDialog(COPYDATA *pcd, UINT uTask, UINT uOperation, LPARAM lParam)
+{
+ if(pcd->hDialog) PostMessage(pcd->hDialog, CFM_NOTIFY, MAKEWPARAM(uTask, uOperation), lParam);
+}
+
+static INT_PTR QueryDialog(COPYDATA *pcd, UINT uTask, UINT uOperation, LPARAM lParam)
+{
+ return (pcd->hDialog) ? SendMessage(pcd->hDialog, CFM_NOTIFY, MAKEWPARAM(uTask, uOperation), lParam) : FALSE;
+}
+
+static void CopyFiles_MarkDrivesBusy(LPCTSTR *ppsz, INT count, BOOL bBusy)
+{
+ INT i, n;
+ DWORD driveMask = 0x00;
+
+ EnterCriticalSection(&cs_copy);
+ if (bBusy)
+ {
+ for(i = 0; i < count; i++)
+ {
+ n = PathGetDriveNumber(ppsz[i]);
+ if (-1 != n)
+ {
+ if (0 == szBusyDrive[n]) driveMask |= (((DWORD)0x01) << n);
+ szBusyDrive[n]++;
+ }
+ }
+ }
+ else
+ {
+ for(i = 0; i < count; i++)
+ {
+ n = PathGetDriveNumber(ppsz[i]);
+ if (-1 != n)
+ {
+ if (szBusyDrive[n] <= 0) continue;
+ szBusyDrive[n]--;
+ if (0 == szBusyDrive[n]) driveMask |= (((DWORD)0x01) << n);
+ }
+ }
+ }
+ LeaveCriticalSection(&cs_copy);
+
+ if (0x00 != driveMask)
+ {
+ static UINT uMsgCopyNotify = 0;
+ if (!uMsgCopyNotify) uMsgCopyNotify = RegisterWindowMessageA("WACOPY_BROADCAST_MSG");
+ if (uMsgCopyNotify)
+ {
+ for (CHAR c = 'A'; 0x00 != driveMask; c++, driveMask>>=1)
+ {
+ if (0 == (0x01 & driveMask)) continue;
+ if (bBusy) SendNotifyMessage(HWND_BROADCAST, uMsgCopyNotify, MAKEWPARAM(c, 0), (LPARAM)TRUE);
+ else
+ {
+ SendNotifyMessage(HWND_BROADCAST, uMsgCopyNotify, MAKEWPARAM(c, 0), (LPARAM)FALSE);
+ SendNotifyMessage(HWND_BROADCAST, uMsgCopyNotify, MAKEWPARAM(0, 0xffff), (LPARAM)plugin.hwndWinampParent);
+ }
+ }
+ }
+ }
+}
+static BOOL ReadCopyParameters(COPYDATA *pcd)
+{
+ BOOL bVal;
+ HRESULT hr = S_OK;
+ pcd->uFlags = 0;
+ if (S_OK == hr) hr = Settings_ReadString(C_COPY, CF_PATH, pcd->szDestination, ARRAYSIZE(pcd->szDestination));
+ if (S_OK == hr) hr = Settings_ReadString(C_COPY, CF_TITLEFMT, pcd->szTitleFormat, ARRAYSIZE(pcd->szTitleFormat));
+ if (S_OK == hr && S_OK == (hr = Settings_GetBool(C_COPY, CF_ADDTOMLDB, &bVal)) && bVal) pcd->uFlags |= FCF_ADDTOMLDB;
+ if (S_OK == hr && S_OK == (hr = Settings_GetBool(C_COPY, CF_USETITLEFMT, &bVal)) && bVal) pcd->uFlags |= FCF_USETITLEFMT;
+ CleanupDirectoryString(pcd->szDestination);
+
+ return (S_OK == hr);
+
+}
+
+void MLDisc_InitializeCopyData()
+{
+ InitializeCriticalSection(&cs_copy);
+}
+
+void MLDisc_ReleaseCopyData()
+{
+ DeleteCriticalSection(&cs_copy);
+}
+
+BOOL MLDisc_IsDiscCopying(CHAR cLetter)
+{
+ if (cLetter >= 'a' && cLetter <= 'z') cLetter -= 0x20;
+ return (cLetter >= 'A' && cLetter <= 'Z' && szBusyDrive[cLetter - 'A'] > 0);
+}
+
+
+BOOL MLDisc_CopyFiles(HWND hParent, LPWSTR *ppszFiles, ULONGLONG *pFSizes, INT count)
+{
+ if (!ppszFiles || !count) return FALSE;
+ if (NULL == hParent) hParent = plugin.hwndLibraryParent;
+
+ COPYDATA *pcd = (COPYDATA*)CoTaskMemAlloc(sizeof(COPYDATA));
+ if (!pcd) return FALSE;
+ ZeroMemory(pcd, sizeof(COPYDATA));
+
+ CopyFiles_AddRef(pcd);
+
+ pcd->hOwner = hParent;
+ pcd->ppszFiles = ppszFiles;
+ pcd->pFSizes = pFSizes;
+ pcd->count = count;
+ pcd->bCancel = FALSE;
+
+ waServiceFactory *factory = plugin.service->service_getServiceByGuid(api_metadataGUID);
+ if (factory) pcd->pMetaReader = (api_metadata*) factory->getInterface();
+ factory = plugin.service->service_getServiceByGuid(mldbApiGuid);
+ if (factory) pcd->pMlDb = (api_mldb*) factory->getInterface();
+
+ HWND hRoot = GetAncestor(hParent, GA_ROOT);
+ if (NULL == hRoot) hRoot = hParent;
+
+ INT_PTR result = WASABI_API_DIALOGBOXPARAMW(IDD_FILECOPY_PREPARE, hRoot, CopyPrepare_DialogProc, (LPARAM)pcd);
+ if (IDCANCEL != result)
+ {
+ if (ReadCopyParameters(pcd))
+ {
+ HWND hCopy = WASABI_API_CREATEDIALOGPARAMW(IDD_FILECOPY_PROGRESS, hRoot, CopyProgress_DialogProc, (LPARAM)pcd);
+ if (hCopy)
+ {
+ pcd->hDialog = hCopy;
+ ShowWindow(hCopy, SW_SHOWNORMAL);
+ RedrawWindow(hCopy, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ }
+ CopyFiles_StartCopy(pcd);
+ }
+ }
+
+ CopyFiles_Release(pcd);
+ return TRUE;
+}
+
+BOOL CopyFiles_CancelCopy(COPYDATA *pcd)
+{
+ if (pcd) pcd->bCancel = TRUE;
+ return (NULL != pcd);
+}
+
+LONG CopyFiles_AddRef(COPYDATA *pcd)
+{
+ return (pcd) ? InterlockedIncrement(&pcd->ref) : 0;
+
+}
+LONG CopyFiles_Release(COPYDATA *pcd)
+{
+ if (pcd && pcd->ref > 0)
+ {
+ LONG r = InterlockedDecrement(&pcd->ref);
+ if ( 0 == r)
+ {
+ if (pcd->ppszFiles)
+ {
+ for (int i = 0; i < pcd->count; i++) CoTaskMemFree(pcd->ppszFiles[i]);
+ CoTaskMemFree(pcd->ppszFiles);
+ }
+ if (pcd->pFSizes) CoTaskMemFree(pcd->pFSizes);
+ if (pcd->hThread) CloseHandle(pcd->hThread);
+
+ if(pcd->pMetaReader)
+ {
+ waServiceFactory *factory = plugin.service->service_getServiceByGuid(api_metadataGUID);
+ if (factory) factory->releaseInterface(pcd->pMetaReader);
+ }
+ if(pcd->pMlDb)
+ {
+ waServiceFactory *factory = plugin.service->service_getServiceByGuid(mldbApiGuid);
+ if (factory) factory->releaseInterface(pcd->pMlDb);
+ }
+ CoTaskMemFree(pcd);
+ }
+ return r;
+ }
+ return 0;
+}
+
+static ULONGLONG CopyFiles_CalculateTotalSize(COPYDATA *pcd)
+{
+ ULONGLONG total = 0;
+ if (!pcd->pFSizes)
+ {
+ pcd->pFSizes = (ULONGLONG*)CoTaskMemAlloc(sizeof(ULONGLONG)*pcd->count);
+ if (!pcd->pFSizes)
+ {
+ pcd->errorCode = ERROR_NOT_ENOUGH_MEMORY;
+ return 0;
+ }
+ LARGE_INTEGER fs;
+ for(int i = 0; i < pcd->count; i++)
+ {
+ HANDLE hFile = CreateFile(pcd->ppszFiles[i], FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
+ if (INVALID_HANDLE_VALUE == hFile)
+ {
+ pcd->errorCode = GetLastError();
+ return 0;
+ }
+ if (!GetFileSizeEx(hFile, &fs)) { pcd->errorCode = GetLastError(); CloseHandle(hFile); return 0; }
+ CloseHandle(hFile);
+ pcd->pFSizes[i] = fs.QuadPart;
+ }
+ }
+ for(int i = 0; i < pcd->count; i++) total += pcd->pFSizes[i];
+ return total;
+}
+
+BOOL CopyFiles_CreateDirectory(LPCTSTR pszDirectory)
+{
+ DWORD ec = ERROR_SUCCESS;
+ if (!CreateDirectory(pszDirectory, NULL))
+ {
+ ec = GetLastError();
+ if (ERROR_PATH_NOT_FOUND == ec)
+ {
+ LPCTSTR pszBlock = pszDirectory;
+ TCHAR szBuffer[MAX_PATH] = {0};
+
+ LPCTSTR pszCursor = PathFindNextComponent(pszBlock);
+ ec = (pszCursor == pszBlock || S_OK != StringCchCopyN(szBuffer, ARRAYSIZE(szBuffer), pszBlock, (pszCursor - pszBlock))) ?
+ ERROR_INVALID_NAME : ERROR_SUCCESS;
+
+ pszBlock = pszCursor;
+
+ while (ERROR_SUCCESS == ec && NULL != (pszCursor = PathFindNextComponent(pszBlock)))
+ {
+ if (pszCursor == pszBlock || S_OK != StringCchCatN(szBuffer, ARRAYSIZE(szBuffer), pszBlock, (pszCursor - pszBlock)))
+ ec = ERROR_INVALID_NAME;
+
+ if (ERROR_SUCCESS == ec && !CreateDirectory(szBuffer, NULL))
+ {
+ ec = GetLastError();
+ if (ERROR_ALREADY_EXISTS == ec) ec = ERROR_SUCCESS;
+ }
+ pszBlock = pszCursor;
+ }
+ }
+
+ if (ERROR_ALREADY_EXISTS == ec) ec = ERROR_SUCCESS;
+ }
+ SetLastError(ec);
+ return (ERROR_SUCCESS == ec);
+}
+
+
+static BOOL CopyFiles_CheckDestination(COPYDATA *pcd, ULONGLONG needSize)
+{
+ TCHAR szRoot[MAX_PATH] = {0};
+ if (S_OK != StringCchCopy(szRoot, ARRAYSIZE(szRoot), pcd->szDestination))
+ {
+ pcd->errorCode = ERROR_OUTOFMEMORY;
+ return FALSE;
+ }
+ PathStripToRoot(szRoot);
+ ULARGE_INTEGER free, total;
+ if (!GetDiskFreeSpaceEx(szRoot, &free, &total, NULL))
+ {
+ pcd->errorCode = GetLastError();
+ return FALSE;
+ }
+ if (needSize > free.QuadPart)
+ {
+ pcd->errorCode = ERROR_DISK_FULL;
+ return FALSE;
+ }
+
+ if (INVALID_FILE_ATTRIBUTES == GetFileAttributes(pcd->szDestination))
+ {
+ DWORD ec = GetLastError();
+ if (ERROR_PATH_NOT_FOUND == ec || ERROR_FILE_NOT_FOUND == ec)
+ {
+ if (TRUE == QueryDialog(pcd, CFT_CONFLICT, CFO_DESTNOTEXIST, (LPARAM)pcd->szDestination))
+ pcd->errorCode = ERROR_REQUEST_ABORTED;
+ }
+ else pcd->errorCode = ec;
+
+ if (ERROR_SUCCESS != pcd->errorCode)
+ {
+ pcd->errorMsgId = IDS_COPY_ERRMSG_DIRECTORYCREATE_FAILED;
+ return FALSE;
+ }
+
+ }
+
+ if (!CopyFiles_CreateDirectory(pcd->szDestination))
+ {
+ pcd->errorMsgId = IDS_COPY_ERRMSG_DIRECTORYCREATE_FAILED;
+ pcd->errorCode = ERROR_CANNOT_MAKE;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static BOOL CopyFiles_FixCdAttributes(LPCTSTR pszFileName)
+{
+ DWORD attr = GetFileAttributes(pszFileName);
+ if (INVALID_FILE_ATTRIBUTES == attr) return FALSE;
+ return SetFileAttributes(pszFileName, (attr & ~FILE_ATTRIBUTE_READONLY) | FILE_ATTRIBUTE_ARCHIVE);
+}
+
+
+
+static BOOL CopyFiles_DeleteFile(LPCTSTR pszFileName, COPYDATA *pcd)
+{
+ DWORD attr = GetFileAttributes(pszFileName);
+ if (INVALID_FILE_ATTRIBUTES == attr) return FALSE;
+ if (FILE_ATTRIBUTE_READONLY & attr)
+ {
+ BOOL bReset = FALSE;
+ if (FCF_DELETEREADONLY & pcd->uFlags) bReset = TRUE;
+ else
+ {
+ INT_PTR r = QueryDialog(pcd, CFT_CONFLICT, CFO_READONLY, (LPARAM)pszFileName);
+ switch(r)
+ {
+ case READONLY_CANCELCOPY:
+ pcd->errorMsgId = IDS_COPY_ERRMSG_COPYFILE_USERABORT;
+ SetLastError(ERROR_REQUEST_ABORTED);
+ return FALSE;
+ case READONLY_DELETEALL: pcd->uFlags |= FCF_DELETEREADONLY; // no break
+ case READONLY_DELETE: bReset = TRUE; break;
+ }
+ }
+
+ if (bReset)
+ {
+ if (!SetFileAttributes(pszFileName, (attr & ~FILE_ATTRIBUTE_READONLY)))
+ return FALSE;
+ }
+ }
+ return DeleteFile(pszFileName);
+}
+
+#define NOT_EXIST 0
+#define EXIST_AND_SKIP 1
+#define EXIST_AND_OVERWRITE 2
+#define EXIST_AND_CANCEL 3
+
+static DWORD CopyFiles_CheckIfExist(LPCTSTR pszFileNameDest, LPCTSTR pszFileNameSource, COPYDATA *pcd)
+{
+ if (INVALID_FILE_ATTRIBUTES == GetFileAttributes(pszFileNameDest))
+ {
+ return NOT_EXIST;
+ }
+
+
+ if (FCF_SKIPFILE & pcd->uFlags) return EXIST_AND_SKIP;
+ if (FCF_OVERWRITEFILE & pcd->uFlags) return EXIST_AND_OVERWRITE;
+
+ FILECONFLICT conflict;
+ ZeroMemory(&conflict, sizeof(FILECONFLICT));
+
+ pcd->uFlags &= ~(FCF_SKIPFILE | FCF_OVERWRITEFILE);
+
+ conflict.pszNameExisting = pszFileNameDest;
+ conflict.pszNameNew = pszFileNameSource;
+
+ INT_PTR r = QueryDialog(pcd, CFT_CONFLICT, CFO_FILEALREDYEXIST, (LPARAM)&conflict);
+
+ switch(0xFF & r)
+ {
+ case EXISTFILE_CANCELCOPY: return EXIST_AND_CANCEL;
+ case EXISTFILE_SKIP:
+ if (EXISTFILE_APPLY_TO_ALL & r) pcd->uFlags |= FCF_SKIPFILE;
+ return EXIST_AND_SKIP;
+ case EXISTFILE_OVERWRITE:
+ if (EXISTFILE_APPLY_TO_ALL & r) pcd->uFlags |= FCF_OVERWRITEFILE;
+ return EXIST_AND_OVERWRITE;
+ }
+
+ return NOT_EXIST;
+}
+
+typedef struct _COPYPROGRESS
+{
+ ULONGLONG total;
+ ULONGLONG completed;
+ HWND hDialog;
+ INT percent;
+} COPYPROGRESS;
+
+static DWORD CALLBACK CopyFiles_ProgressRoutine(LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred, LARGE_INTEGER StreamSize,
+ LARGE_INTEGER StreamBytesTransferred, DWORD dwStreamNumber, DWORD dwCallbackReason,
+ HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID lpData)
+{
+ COPYPROGRESS *pProgress = (COPYPROGRESS*)lpData;
+ if (pProgress)
+ {
+ switch(dwCallbackReason)
+ {
+ case CALLBACK_STREAM_SWITCH:
+ pProgress->completed += TotalFileSize.QuadPart;
+ break;
+
+ }
+ INT p = (INT)((pProgress->completed - TotalFileSize.QuadPart + TotalBytesTransferred.QuadPart) * 100 / pProgress->total);
+ if (p != pProgress->percent)
+ {
+ pProgress->percent = p;
+ if (pProgress->hDialog)
+ PostMessage(pProgress->hDialog, CFM_NOTIFY, MAKEWPARAM(CFT_COPYING, CFO_PROGRESS), p);
+ }
+ }
+ return PROGRESS_CONTINUE;
+};
+
+static DWORD WINAPI CopyFiles_ThreadProc(LPVOID param)
+{
+
+ COPYDATA *pcd = (COPYDATA*)param;
+ if (!pcd) return 1;
+
+ CopyFiles_MarkDrivesBusy((LPCTSTR*)pcd->ppszFiles, pcd->count, TRUE);
+
+ ULONGLONG needSize;
+ NotifyDialog(pcd, CFT_INITIALIZING, CFO_INIT, 0L);
+ NotifyDialog(pcd, CFT_INITIALIZING, CFO_CACLSIZE, 0L);
+ needSize = CopyFiles_CalculateTotalSize(pcd);
+ BOOL bSuccess = (ERROR_SUCCESS == pcd->errorCode);
+ if (bSuccess)
+ {
+ NotifyDialog(pcd, CFT_INITIALIZING, CFO_CHECKDESTINATION, 0L);
+ bSuccess = CopyFiles_CheckDestination(pcd, needSize);
+ }
+
+ if (!bSuccess)
+ {
+ if (0 == pcd->errorMsgId)
+ pcd->errorMsgId = IDS_COPY_ERRMSG_INITIALIZATION_FAILED;
+ NotifyDialog(pcd, CFT_FINISHED, CFO_FAILED, pcd->errorCode);
+ CopyFiles_MarkDrivesBusy((LPCTSTR*)pcd->ppszFiles, pcd->count, FALSE);
+ CopyFiles_Release(pcd);
+ return 0;
+ }
+
+ COPYPROGRESS progress;
+ ZeroMemory(&progress, sizeof(COPYPROGRESS));
+ progress.total = needSize;
+ progress.hDialog = pcd->hDialog;
+
+ NotifyDialog(pcd, CFT_COPYING, CFO_INIT, 0L);
+
+ TCHAR szFile[MAX_PATH] = {0};
+
+ for (int i = 0; i < pcd->count && ERROR_SUCCESS == pcd->errorCode; i++)
+ {
+ if (pcd->bCancel)
+ {
+ pcd->errorMsgId = IDS_COPY_ERRMSG_COPYFILE_USERABORT;
+ pcd->errorCode = ERROR_REQUEST_ABORTED;
+ }
+
+ LPCTSTR pszOrigFileName = PathFindFileName(pcd->ppszFiles[i]);
+
+ NotifyDialog(pcd, CFT_COPYING, CFO_NEXTFILE, MAKELPARAM(i, pcd->count));
+
+ if (NULL == PathCombine(szFile, pcd->szDestination, pszOrigFileName))
+ {
+ pcd->errorMsgId = IDS_COPY_ERRMSG_COPYFILE_FAILED;
+ pcd->errorCode = ERROR_BAD_PATHNAME;
+ }
+
+ DWORD r = CopyFiles_CheckIfExist(szFile, pcd->ppszFiles[i], pcd);
+ switch(r)
+ {
+ case EXIST_AND_SKIP:
+ continue;
+ break;
+ case EXIST_AND_OVERWRITE:
+ if (!CopyFiles_DeleteFile(szFile, pcd))
+ {
+ pcd->errorMsgId = IDS_COPY_ERRMSG_DELETEFILE_FAILED;
+ pcd->errorCode = GetLastError();
+ }
+ break;
+ case EXIST_AND_CANCEL:
+ pcd->errorMsgId = IDS_COPY_ERRMSG_COPYFILE_USERABORT;
+ pcd->errorCode = ERROR_REQUEST_ABORTED;
+ break;
+ }
+
+ // copy
+ if (ERROR_SUCCESS == pcd->errorCode && !CopyFileEx(pcd->ppszFiles[i], szFile, CopyFiles_ProgressRoutine, &progress, &pcd->bCancel, 0))
+ {
+ pcd->errorMsgId = IDS_COPY_ERRMSG_COPYFILE_FAILED;
+ pcd->errorCode = GetLastError();
+ }
+
+ if (pcd->bCancel)
+ {
+ pcd->errorMsgId = IDS_COPY_ERRMSG_COPYFILE_USERABORT;
+ pcd->errorCode = ERROR_REQUEST_ABORTED;
+ }
+
+ if (ERROR_SUCCESS == pcd->errorCode) // post copy
+ {
+ // fix attributes
+ if (ERROR_SUCCESS == pcd->errorCode && !CopyFiles_FixCdAttributes(szFile))
+ {
+ pcd->errorMsgId = IDS_COPY_ERRMSG_SETATTRIBUTES_FAILED;
+ pcd->errorCode = GetLastError();
+ }
+
+ // format title & rename
+ if (ERROR_SUCCESS == pcd->errorCode && (FCF_USETITLEFMT & pcd->uFlags) && pcd->pMetaReader)
+ {
+ TCHAR szBuffer[MAX_PATH] = {0};
+ if (!CopyFiles_FormatFileName(szBuffer, ARRAYSIZE(szBuffer), szFile, pszOrigFileName,
+ pcd->szDestination, pcd->szTitleFormat, pcd->pMetaReader))
+ {
+ pcd->errorMsgId = IDS_COPY_ERRMSG_TITLEFORMAT_FAILED;
+ pcd->errorCode = GetLastError();
+ }
+
+ if (pcd->bCancel)
+ {
+ pcd->errorMsgId = IDS_COPY_ERRMSG_COPYFILE_USERABORT;
+ pcd->errorCode = ERROR_REQUEST_ABORTED;
+ }
+
+ if (ERROR_SUCCESS == pcd->errorCode &&
+ CSTR_EQUAL != CompareString(STRCOMP_INVARIANT, 0, szBuffer, -1, szFile, -1))
+ {
+
+ DWORD r = CopyFiles_CheckIfExist(szBuffer, szFile, pcd);
+ switch(r)
+ {
+ case EXIST_AND_SKIP:
+ CopyFiles_DeleteFile(szFile, pcd);
+ continue;
+ break;
+ case EXIST_AND_OVERWRITE:
+ if (!CopyFiles_DeleteFile(szBuffer, pcd))
+ {
+ pcd->errorMsgId = IDS_COPY_ERRMSG_DELETEFILE_FAILED;
+ pcd->errorCode = GetLastError();
+ }
+ break;
+ case EXIST_AND_CANCEL:
+ pcd->errorMsgId = IDS_COPY_ERRMSG_COPYFILE_USERABORT;
+ pcd->errorCode = ERROR_REQUEST_ABORTED;
+ break;
+ }
+
+ if (ERROR_SUCCESS == pcd->errorCode)
+ {
+ if (!MoveFileEx(szFile, szBuffer, MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED))
+ {
+ pcd->errorMsgId = IDS_COPY_ERRMSG_TITLEFORMAT_FAILED;
+ pcd->errorCode = GetLastError();
+ }
+ else StringCchCopy(szFile, ARRAYSIZE(szFile), szBuffer);
+ }
+
+ }
+ }
+
+
+ if (ERROR_SUCCESS != pcd->errorCode)
+ {
+ CopyFiles_DeleteFile(szFile, pcd);
+ }
+ else if ((FCF_ADDTOMLDB & pcd->uFlags) && pcd->pMlDb)
+ {
+ if (0 == pcd->pMlDb->AddFile(szFile))
+ {
+ pcd->errorMsgId = IDS_COPY_ERRMSG_ADDTOMLDB_FAILED;
+ pcd->errorCode = ERROR_FILE_NOT_FOUND;
+ }
+ }
+ }
+
+ }
+
+ NotifyDialog(pcd, CFT_FINISHED, (ERROR_SUCCESS == pcd->errorCode) ? CFO_SUCCESS : CFO_FAILED, pcd->errorCode);
+ if ((FCF_ADDTOMLDB & pcd->uFlags) && pcd->pMlDb) pcd->pMlDb->Sync();
+ CopyFiles_MarkDrivesBusy((LPCTSTR*)pcd->ppszFiles, pcd->count, FALSE);
+ CopyFiles_Release(pcd);
+ return 0;
+}
+
+BOOL CopyFiles_StartCopy(COPYDATA *pcd)
+{
+ DWORD threadId;
+
+ CopyFiles_AddRef(pcd);
+
+ pcd->hThread = CreateThread(NULL, 0, CopyFiles_ThreadProc, pcd, 0, &threadId);
+ if (pcd->hThread) return TRUE;
+
+
+ pcd->errorCode = GetLastError();
+ NotifyDialog(pcd, CFT_FINISHED, CFO_FAILED, pcd->errorCode);
+ CopyFiles_Release(pcd);
+ return FALSE;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/copyfiles.h b/Src/Plugins/Library/ml_disc/copyfiles.h
new file mode 100644
index 00000000..bd762f53
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/copyfiles.h
@@ -0,0 +1,27 @@
+#ifndef NULLOSFT_MEDIALIBRARY_MLDISC_COPYFILES_HEADER
+#define NULLOSFT_MEDIALIBRARY_MLDISC_COPYFILES_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <windows.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+void MLDisc_InitializeCopyData();
+void MLDisc_ReleaseCopyData();
+
+// use CoTaskMemAlloc/CoTackMemFree to allocate buffers and each string. pszFSize can be NULL. if return TRUE do not free data.
+BOOL MLDisc_CopyFiles(HWND hParent, LPWSTR *ppszFiles, ULONGLONG *pFSizes, INT count);
+BOOL MLDisc_IsDiscCopying(CHAR cLetter);
+#ifdef __cplusplus
+}
+#endif
+
+
+
+#endif // NULLOSFT_MEDIALIBRARY_MLDISC_COPYFILES_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/copyfiles_post.cpp b/Src/Plugins/Library/ml_disc/copyfiles_post.cpp
new file mode 100644
index 00000000..44893398
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/copyfiles_post.cpp
@@ -0,0 +1,138 @@
+#include "main.h"
+#include "./copyfiles.h"
+#include "./copyinternal.h"
+#include "./resource.h"
+#include "../nu/trace.h"
+#include <api/service/waServiceFactory.h>
+#include <shlwapi.h>
+#include <strsafe.h>
+
+typedef struct _FILEMETA
+{
+ LPWSTR pszArtist;
+ LPWSTR pszAlbum;
+ LPWSTR pszTitle;
+ LPWSTR pszGenre;
+ LPWSTR pszAlbumArtist;
+ INT nYear;
+ INT nTrackNum;
+ INT nTrackCount;
+ LPWSTR pszDisc;
+} FILEMETA;
+
+// sets part and parts to -1 or 0 on fail/missing (e.g. parts will be -1 on "1", but 0 on "1/")
+static void ParseIntSlashInt(wchar_t *string, int *part, int *parts)
+{
+ *part = -1;
+ *parts = -1;
+
+ if (string && string[0])
+ {
+ *part = _wtoi(string);
+ while (string && *string && *string != '/')
+ {
+ string++;
+ }
+ if (*string == '/')
+ {
+ string++;
+ *parts = _wtoi(string);
+ }
+ }
+}
+
+#define READFILEINFO(__fileName, __tag, __result, __pszBuffer, __cchBuffer)\
+ (pReader->GetExtendedFileInfo((__fileName), (__tag), (__pszBuffer), (__cchBuffer)) && L'\0' != *(__pszBuffer))
+
+
+static void CopyFiles_ReadFileMeta(FILEMETA *pMeta, api_metadata *pReader, LPCWSTR pszFileName)
+{
+ WCHAR szBuffer[2048] = {0};
+
+ #define GETFILEINFO_STR(__tag, __result, __resId)\
+ { szBuffer[0] = L'\0';\
+ READFILEINFO(pszFileName, __tag, __result, szBuffer, ARRAYSIZE(szBuffer));\
+ if (TEXT('\0') == *szBuffer)\
+ { if (IS_INTRESOURCE(__resId)) WASABI_API_LNGSTRINGW_BUF(((UINT)(UINT_PTR)(__resId)), szBuffer, ARRAYSIZE(szBuffer));\
+ else StringCchCopyEx(szBuffer, ARRAYSIZE(szBuffer), (__resId), NULL, NULL, STRSAFE_IGNORE_NULLS);\
+ }\
+ (__result) = _wcsdup(szBuffer); }
+ #define GETFILEINFO_INT(__tag, __result) { szBuffer[0] = L'\0';\
+ if (READFILEINFO(pszFileName, __tag, __result, szBuffer, ARRAYSIZE(szBuffer)))\
+ {(__result) = _wtoi(szBuffer); }}
+ #define GETFILEINFO_INTINT(__tag, __result1, __result2) { szBuffer[0] = L'\0';\
+ if (READFILEINFO(pszFileName, __tag, __result, szBuffer, ARRAYSIZE(szBuffer)))\
+ {ParseIntSlashInt(szBuffer, (__result1), (__result2)); }}
+
+ if (!pMeta) return;
+ ZeroMemory(pMeta, sizeof(FILEMETA));
+
+#pragma warning(push)
+#pragma warning(disable : 4127)
+
+ GETFILEINFO_STR(L"artist", pMeta->pszArtist, MAKEINTRESOURCE(IDS_UNKNOWN_ARTIST));
+ GETFILEINFO_STR(L"album", pMeta->pszAlbum, MAKEINTRESOURCE(IDS_UNKNOWN_ALBUM));
+ GETFILEINFO_STR(L"title", pMeta->pszTitle, MAKEINTRESOURCE(IDS_UNKNOWN));
+ GETFILEINFO_STR(L"albumartist", pMeta->pszAlbumArtist, pMeta->pszArtist);
+ GETFILEINFO_STR(L"genre", pMeta->pszGenre, MAKEINTRESOURCE(IDS_UNKNOWN_GENRE));
+ GETFILEINFO_INT(L"year", pMeta->nYear);
+ GETFILEINFO_INTINT(L"track", &pMeta->nTrackNum, &pMeta->nTrackCount);
+ GETFILEINFO_STR(L"disc", pMeta->pszDisc, MAKEINTRESOURCE(IDS_UNKNOWN));
+#pragma warning(pop)
+}
+
+static void CopyFiles_ReleaseFileMeta(FILEMETA *pMeta)
+{
+ if (pMeta->pszArtist) free(pMeta->pszArtist);
+ if (pMeta->pszAlbum) free(pMeta->pszAlbum);
+ if (pMeta->pszTitle) free(pMeta->pszTitle);
+ if (pMeta->pszGenre) free(pMeta->pszGenre);
+ if (pMeta->pszDisc) free(pMeta->pszDisc);
+ if (pMeta->pszAlbumArtist) free(pMeta->pszAlbumArtist);
+ ZeroMemory(pMeta, sizeof(FILEMETA));
+}
+
+static BOOL CopyFiles_GetFormattedName(LPTSTR pszBuffer, INT cchBufferMax, LPCTSTR pszFileToFormat, LPCTSTR pszOrigFileName, LPCTSTR pszFormat, api_metadata *pMetaReader)
+{
+ HRESULT hr;
+ FILEMETA meta;
+ CopyFiles_ReadFileMeta(&meta, pMetaReader, pszFileToFormat);
+
+ WCHAR szYear[16] = {0};
+ StringCchPrintf(szYear, ARRAYSIZE(szYear), TEXT("%d"), meta.nYear);
+
+ pszBuffer[0] = TEXT('\0');
+ hr = FormatFileName(pszBuffer, cchBufferMax, pszFormat,
+ meta.nTrackNum, meta.pszAlbumArtist,
+ meta.pszAlbum, meta.pszTitle,
+ meta.pszGenre, szYear, meta.pszArtist,
+ pszOrigFileName, meta.pszDisc);
+
+ CopyFiles_ReleaseFileMeta(&meta);
+
+ if (S_OK != hr)
+ {
+ SetLastError(ERROR_OUTOFMEMORY);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL CopyFiles_FormatFileName(LPTSTR pszNewFileName, INT cchBufferMax, LPCTSTR pszFileToRename, LPCTSTR pszOrigFileName, LPCTSTR pszDestination, LPCTSTR pszFormat, api_metadata *pMetaReader)
+{
+ StringCchCopy(pszNewFileName, cchBufferMax, pszDestination);
+ INT l = lstrlen(pszNewFileName);
+ if (l) { pszNewFileName[l] = TEXT('\\'); pszNewFileName[l + 1] = TEXT('\0'); l++; }
+ if (!CopyFiles_GetFormattedName(pszNewFileName + l, cchBufferMax - l, pszFileToRename, pszOrigFileName, pszFormat, pMetaReader))
+ return FALSE;
+
+ LPTSTR p = PathFindFileName(pszNewFileName);
+ if (p && p > pszNewFileName)
+ {
+ *(p - 1) = TEXT('\0');
+ if (!CopyFiles_CreateDirectory(pszNewFileName)) return FALSE;
+ *(p - 1) = TEXT('\\');
+ }
+ return TRUE;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/copyinternal.h b/Src/Plugins/Library/ml_disc/copyinternal.h
new file mode 100644
index 00000000..b59be7a6
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/copyinternal.h
@@ -0,0 +1,125 @@
+#ifndef NULLOSFT_MEDIALIBRARY_MLDISC_COPYFILES_INTERNAL_HEADER
+#define NULLOSFT_MEDIALIBRARY_MLDISC_COPYFILES_INTERNAL_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <windows.h>
+#include "../Agave/Metadata/api_metadata.h"
+#include "../ml_local/api_mldb.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define STRCOMP_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
+
+#define CPM_UPDATEDISKSIZE (WM_APP + 2)
+
+
+typedef struct _COPYDATA
+{
+ LONG ref;
+ HWND hDialog;
+ HWND hOwner;
+ HANDLE hThread;
+ BOOL bCancel;
+ DWORD errorCode;
+ UINT errorMsgId;
+ LPWSTR *ppszFiles;
+ ULONGLONG *pFSizes;
+ INT count;
+ UINT uFlags;
+ api_metadata *pMetaReader;
+ api_mldb *pMlDb;
+ WCHAR szDestination[MAX_PATH];
+ WCHAR szTitleFormat[128];
+} COPYDATA;
+
+#define FCF_ADDTOMLDB 0x00000002L
+#define FCF_USETITLEFMT 0x00000004L
+#define FCF_SKIPFILE 0x00010000L
+#define FCF_OVERWRITEFILE 0x00020000L
+#define FCF_DELETEREADONLY 0x00040000L
+
+
+
+INT_PTR CALLBACK CopyPrepare_DialogProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK CopyProgress_DialogProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+
+LONG CopyFiles_AddRef(COPYDATA *pcd);
+LONG CopyFiles_Release(COPYDATA *pcd);
+BOOL CopyFiles_StartCopy(COPYDATA *pcd);
+BOOL CopyFiles_CancelCopy(COPYDATA *pcd);
+
+BOOL CopyFiles_CreateDirectory(LPCTSTR pszDirectory);
+BOOL CopyFiles_FormatFileName(LPTSTR pszNewFileName, INT cchBufferMax, LPCTSTR pszFileToRename, LPCTSTR pszOrigFileName, LPCTSTR pszDestination, LPCTSTR pszFormat, api_metadata *pMetaReader);
+
+HBITMAP CopyFiles_LoadResourcePng(LPCTSTR pszResource);
+
+#define CFM_NOTIFY (WM_APP + 3)
+
+// notify task
+#define CFT_INITIALIZING 0x0001
+#define CFT_COPYING 0x0002
+#define CFT_FINISHED 0x0003
+#define CFT_CONFLICT 0x0004 // conflicts always use SendMessage
+
+
+// init task operations code
+#define CFO_INIT 0x0000
+#define CFO_CACLSIZE 0x0001
+#define CFO_CHECKDESTINATION 0x0002
+
+
+// copy task operations code
+#define CFO_INIT 0x0000 // time to set tast text
+#define CFO_NEXTFILE 0x0001 // lParam - MAKELPARAM(file index, total count)
+#define CFO_PROGRESS 0x0002 // lParam - percent
+#define CFO_POSTCOPY 0x0003
+
+
+// conflicts
+
+#define EXISTFILE_CANCELCOPY 0x0001 // almost like return FALSE but will not produce error
+#define EXISTFILE_SKIP 0x0002 // skip
+#define EXISTFILE_OVERWRITE 0x0003 // overwrite
+#define EXISTFILE_APPLY_ONCE 0x0000 // apply only once
+#define EXISTFILE_APPLY_TO_ALL 0x0100 // apply to all files with the same conflict
+
+#define READONLY_CANCELCOPY 0x0001
+#define READONLY_DELETE 0x0002
+#define READONLY_DELETEALL 0x0003
+
+typedef struct _FILECONFLICT
+{
+ LPCTSTR pszNameExisting;
+ LPCTSTR pszNameNew;
+} FILECONFLICT;
+
+
+#define CFO_DESTNOTEXIST 0x0000 // return FALSE to create destination or TRUE to cancel copy operation. param -pszDestionation
+#define CFO_FILEALREDYEXIST 0x0001 // return FALSE to fail with access denied, or EXISTFILE_XXX, param = (FILECONFLICT*)
+#define CFO_READONLY 0x0002 // return FALSE to fail, or RADONLY_XXX, param = (LPCTSTR)pszFileName
+
+
+
+
+
+
+// finished task operations code
+#define CFO_FAILED 0x0001
+#define CFO_SUCCESS 0x0002
+#define CFO_CANCELLED 0x0003
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+#endif // NULLOSFT_MEDIALIBRARY_MLDISC_COPYFILES_INTERNAL_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/copyprep.cpp b/Src/Plugins/Library/ml_disc/copyprep.cpp
new file mode 100644
index 00000000..7425ef39
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/copyprep.cpp
@@ -0,0 +1,426 @@
+#include "main.h"
+#include "./copyfiles.h"
+#include "./copyinternal.h"
+#include "./resource.h"
+#include "./settings.h"
+#include "../nu/trace.h"
+#include <shlwapi.h>
+#include <strsafe.h>
+
+typedef struct _PREPDLG
+{
+ HFONT hfItalic;
+ HWND hActiveHelp;
+ HBITMAP hbmpLogo;
+ IAutoComplete *pac;
+ IACList2 *pacl2;
+ COPYDATA *pCopyData;
+ TCHAR szCurrentRoot[MAX_PATH];
+} PREPDLG;
+
+typedef struct _CALCDISKSIZE
+{
+ HWND hCallback;
+ DWORD dwError;
+ ULARGE_INTEGER bytesFree;
+ ULARGE_INTEGER bytesTotal;
+ TCHAR szRoot[MAX_PATH];
+} CALCDISKSIZE;
+
+#define PREPDLG_PROP TEXT("PREPDLG")
+#define GetPrepDlg(__hdlg) ((PREPDLG*)GetProp((__hdlg), PREPDLG_PROP))
+
+#define TID_UPDATEDISKSIZE 1985
+#define DELAY_UPDATEDISKSIZE 100
+
+static void DisplayFormatExample(HWND hdlg, INT nItemId)
+{
+ TCHAR szBuffer[MAX_PATH*2], szFormat[MAX_PATH] = {0};
+
+ Settings_ReadString(C_COPY, CF_TITLEFMT, szFormat, ARRAYSIZE(szFormat));
+ szBuffer[0] = TEXT('\0');
+ FormatFileName(szBuffer, ARRAYSIZE(szBuffer), szFormat, 10,
+ TEXT("U2"),
+ TEXT("The Joshua Tree"),
+ TEXT("Exit"),
+ TEXT("Rock"),
+ TEXT("1987"),
+ TEXT("U2"),
+ TEXT("u2_The_Joshua_Tree.Mp3"),
+ TEXT(""));
+ SetDlgItemText(hdlg, nItemId, szBuffer);
+}
+
+static DWORD WINAPI DiskFreeSpace_ThreadProc(LPVOID param)
+{
+ CALCDISKSIZE *pcs = (CALCDISKSIZE*)param;
+ if (!pcs) return 0;
+ pcs->dwError = 0;
+ SetLastError(0);
+ if (!GetDiskFreeSpaceEx(pcs->szRoot, &pcs->bytesFree, &pcs->bytesTotal, NULL))
+ pcs->dwError = GetLastError();
+ PostMessage(pcs->hCallback, CPM_UPDATEDISKSIZE, 0, (LPARAM)pcs);
+ return 0;
+}
+
+static void CopyPrepare_UpdateMessage(HWND hdlg)
+{
+ TCHAR szBuffer[MAX_PATH*2] = {0};
+ PREPDLG *ppd = GetPrepDlg(hdlg);
+
+ szBuffer[0] = TEXT('\0');
+ if (ppd && ppd->pCopyData)
+ {
+ TCHAR szPath[MAX_PATH] = {0}, szFormat[256] = {0};
+ if (S_OK != Settings_ReadString(C_COPY, CF_PATH, szPath, ARRAYSIZE(szPath))) *szPath = TEXT('\0');
+ else CleanupDirectoryString(szPath);
+
+ if (1 == ppd->pCopyData->count)
+ {
+ WASABI_API_LNGSTRINGW_BUF(IDS_COPY_PREP_MESSAGE_SINGLE_FILE, szFormat, ARRAYSIZE(szFormat));
+ StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), szFormat, PathFindFileName(ppd->pCopyData->ppszFiles[0]), szPath);
+ }
+ else
+ {
+ WASABI_API_LNGSTRINGW_BUF(IDS_COPY_PREP_MESSAGE_MULTIPLE_FILES, szFormat, ARRAYSIZE(szFormat));
+ StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), szFormat, ppd->pCopyData->count, szPath);
+ }
+ }
+ SetDlgItemText(hdlg, IDC_LBL_MESSAGE, szBuffer);
+}
+
+HBITMAP CopyFiles_LoadResourcePng(LPCTSTR pszResource)
+{
+ HBITMAP hbmp;
+ MLIMAGESOURCE src = { sizeof(MLIMAGESOURCE), };
+ src.lpszName = pszResource;
+ src.type = SRC_TYPE_PNG;
+ src.flags = 0;
+
+ src.hInst = WASABI_API_LNG_HINST;
+ hbmp = MLImageLoader_LoadDib(plugin.hwndLibraryParent, &src);
+ if(!hbmp)
+ {
+ src.hInst = WASABI_API_ORIG_HINST;
+ hbmp = MLImageLoader_LoadDib(plugin.hwndLibraryParent, &src);
+ }
+
+ DIBSECTION dibsec;
+
+ if (hbmp && sizeof(DIBSECTION) == GetObjectW(hbmp, sizeof(DIBSECTION), &dibsec) &&
+ BI_RGB == dibsec.dsBmih.biCompression && 1 == dibsec.dsBmih.biPlanes && 32 == dibsec.dsBm.bmBitsPixel)
+ {
+ MLIMAGEFILTERAPPLYEX filter = { sizeof(MLIMAGEFILTERAPPLYEX), };
+ filter.filterUID = MLIF_BLENDONBK_UID;
+ filter.cx = dibsec.dsBm.bmWidth;
+ filter.cy = dibsec.dsBm.bmHeight;
+ filter.bpp = dibsec.dsBm.bmBitsPixel;
+ filter.pData = (LPBYTE)dibsec.dsBm.bmBits;
+ filter.rgbBk = GetSysColor(COLOR_3DFACE);
+ MLImageFilter_ApplyEx(plugin.hwndLibraryParent, &filter);
+ }
+ return hbmp;
+}
+
+static INT_PTR CopyPrepare_OnInitDialog(HWND hdlg, HWND hFocus, LPARAM lParam)
+{
+ HWND hctrl;
+ PREPDLG *ppd = (PREPDLG*)calloc(1, sizeof(PREPDLG));
+ if (!ppd) return 0;
+
+ SetProp(hdlg, PREPDLG_PROP, ppd);
+ ppd->pCopyData = (COPYDATA*)lParam;
+
+ hctrl = GetDlgItem(hdlg, IDOK);
+ if (hctrl) SendMessageW(hdlg, WM_NEXTDLGCTL, (WPARAM)hctrl, (LPARAM)TRUE);
+ SendMessageW(hdlg, WM_COMMAND, MAKEWPARAM(IDC_BTN_OPTIONS, BN_CLICKED), (LPARAM)GetDlgItem(hdlg, IDC_BTN_OPTIONS));
+
+ hctrl = GetDlgItem(hdlg, IDC_LBL_EXAMPLE);
+ if (hctrl)
+ {
+ LOGFONT lf;
+ HFONT hf = (HFONT)SendMessage(hctrl, WM_GETFONT, 0, 0L);
+ if (NULL == hf) hf = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+ if (GetObject(hf, sizeof(LOGFONT), &lf))
+ {
+ lf.lfItalic = TRUE;
+ ppd->hfItalic = CreateFontIndirect(&lf);
+ if (ppd->hfItalic)
+ {
+ UINT szIdList[] = { IDC_LBL_EXAMPLE_TITLE, IDC_LBL_EXAMPLE, IDC_LBL_FREE_TITLE, IDC_LBL_FREE, IDC_LBL_REQUIRED_TITLE, IDC_LBL_REQUIRED, };
+ for (int i = 0; i < sizeof(szIdList)/sizeof(szIdList[0]); i++) SendDlgItemMessage(hdlg, szIdList[i], WM_SETFONT, (WPARAM)ppd->hfItalic, FALSE);
+ }
+ }
+ }
+
+ if (ppd->pCopyData && ppd->pCopyData->pFSizes)
+ {
+ TCHAR szBuffer[128] = {0};
+ ULONGLONG total = 0;
+ for(int i = 0; i < ppd->pCopyData->count; i++) total += ppd->pCopyData->pFSizes[i];
+ StrFormatByteSize64(total, szBuffer, ARRAYSIZE(szBuffer));
+ SetDlgItemText(hdlg, IDC_LBL_REQUIRED, szBuffer);
+ }
+ else
+ {
+ ShowWindow(GetDlgItem(hdlg, IDC_LBL_REQUIRED_TITLE), SW_HIDE);
+ ShowWindow(GetDlgItem(hdlg, IDC_LBL_REQUIRED), SW_HIDE);
+ }
+
+ if (ppd->pCopyData && ppd->pCopyData->hOwner)
+ {
+ RECT rw;
+ if (!GetWindowRect(ppd->pCopyData->hOwner, &rw)) SetRect(&rw, 0, 0, 0, 0);
+ if (hdlg && rw.left != rw.right)
+ {
+ RECT rw2;
+ GetWindowRect(hdlg, &rw2);
+ SetWindowPos(hdlg, HWND_TOP,
+ rw.left + ((rw.right - rw.left) - (rw2.right - rw2.left))/2,
+ rw.top + ((rw.bottom - rw.top) - (rw2.bottom - rw2.top))/2,
+ 0, 0, SWP_NOACTIVATE | SWP_NOSIZE);
+ }
+ }
+
+ CopyPrepare_UpdateMessage(hdlg);
+
+ SendMessage(hdlg, DM_REPOSITION, 0, 0L);
+
+ ppd->hbmpLogo = CopyFiles_LoadResourcePng(MAKEINTRESOURCE(IDB_FILECOPY));
+ if (ppd->hbmpLogo) SendDlgItemMessage(hdlg, IDC_PIC_LOGO, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)ppd->hbmpLogo);
+ else ShowWindow(GetDlgItem(hdlg, IDC_PIC_LOGO), SW_HIDE);
+
+ return FALSE;
+}
+
+static void CopyPrepare_OnDestroy(HWND hdlg)
+{
+ PREPDLG *ppd = GetPrepDlg(hdlg);
+ RemoveProp(hdlg, PREPDLG_PROP);
+ if (ppd)
+ {
+ if (ppd->hActiveHelp) DestroyWindow(ppd->hActiveHelp);
+ if (ppd->hfItalic) DeleteObject(ppd->hfItalic);
+ if (ppd->pacl2) ppd->pacl2->Release();
+ if (ppd->pac) ppd->pac->Release();
+
+ if (ppd->hbmpLogo)
+ {
+ HBITMAP hbmp = (HBITMAP)SendDlgItemMessage(hdlg, IDC_PIC_LOGO, STM_GETIMAGE, IMAGE_BITMAP, 0L);
+ if (hbmp != ppd->hbmpLogo) DeleteObject(hbmp);
+ DeleteObject(ppd->hbmpLogo);
+ }
+ free(ppd);
+ }
+}
+static void CopyPrepare_OnParentNotify(HWND hdlg, UINT uMsg, LPARAM lParam)
+{
+ PREPDLG *ppd = GetPrepDlg(hdlg);
+ if (ppd && WM_DESTROY == uMsg && ppd->hActiveHelp && ppd->hActiveHelp == (HWND)lParam)
+ ppd->hActiveHelp = NULL;
+}
+
+static void CALLBACK CopyPrepare_OnUpdateDiskSizeTimer(HWND hdlg, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ KillTimer(hdlg, idEvent);
+
+ CopyPrepare_UpdateMessage(hdlg);
+
+ PREPDLG *ppd = GetPrepDlg(hdlg);
+ if (!ppd) return;
+ CALCDISKSIZE *pcs = (CALCDISKSIZE*)calloc(1, sizeof(CALCDISKSIZE));
+ if (!pcs) return;
+
+ pcs->hCallback = hdlg;
+
+ if (S_OK == Settings_ReadString(C_COPY, CF_PATH, pcs->szRoot, ARRAYSIZE(pcs->szRoot)))
+ {
+ PathStripToRoot(pcs->szRoot);
+
+ if (TEXT('\0') != *pcs->szRoot &&
+ (TEXT('\0') == *ppd->szCurrentRoot ||
+ CSTR_EQUAL != CompareString(STRCOMP_INVARIANT, NORM_IGNORECASE, ppd->szCurrentRoot, -1, pcs->szRoot, -1)))
+ {
+ DWORD threadId;
+ SetDlgItemText(hdlg, IDC_LBL_FREE, WASABI_API_LNGSTRINGW(IDS_CALCULATING));
+ HANDLE ht = CreateThread(NULL, 0, DiskFreeSpace_ThreadProc, pcs, 0, &threadId);
+ if (NULL != ht)
+ {
+ CloseHandle(ht);
+ StringCchCopy(ppd->szCurrentRoot, ARRAYSIZE(ppd->szCurrentRoot), pcs->szRoot);
+ return;
+ }
+ SetDlgItemText(hdlg, IDC_LBL_FREE, TEXT(""));
+ }
+ }
+ if (TEXT('\0') == *pcs->szRoot)
+ {
+ pcs->dwError = ERROR_INVALID_NAME;
+ StringCchCopy(ppd->szCurrentRoot, ARRAYSIZE(ppd->szCurrentRoot), pcs->szRoot);
+ PostMessage(pcs->hCallback, CPM_UPDATEDISKSIZE, 0, (LPARAM)pcs);
+ return;
+ }
+ free(pcs);
+
+
+}
+
+static void CopyPrepare_OnUpdateDiskSizeResult(HWND hdlg, CALCDISKSIZE *pcs)
+{
+ if (!pcs) return;
+ PREPDLG *ppd = GetPrepDlg(hdlg);
+ if (ppd && CSTR_EQUAL == CompareString(STRCOMP_INVARIANT, NORM_IGNORECASE, ppd->szCurrentRoot, -1, pcs->szRoot, -1))
+ {
+ TCHAR szBuffer[128] = {0};
+ szBuffer[0] = TEXT('\0');
+
+ if (ERROR_SUCCESS == pcs->dwError) StrFormatByteSize64(pcs->bytesFree.QuadPart, szBuffer, ARRAYSIZE(szBuffer));
+ else WASABI_API_LNGSTRINGW_BUF(IDS_UNKNOWN, szBuffer, sizeof(szBuffer));
+
+ SetDlgItemText(hdlg, IDC_LBL_FREE, szBuffer);
+ }
+ free(pcs);
+}
+
+
+static void CopyPrepare_OnOptionsClick(HWND hdlg)
+{
+ RECT rw, rw2;
+ BOOL bEnable;
+ INT height;
+
+ PREPDLG *ppd = GetPrepDlg(hdlg);
+
+ HWND hctrl = GetDlgItem(hdlg, IDC_GRP_OPTIONS);
+ if (!hctrl || !GetWindowRect(hctrl, &rw)) return;
+
+ GetWindowRect(hdlg, &rw2);
+ OffsetRect(&rw, -rw2.left, -rw2.top);
+
+ if (WS_DISABLED & GetWindowLongPtrW(hctrl, GWL_STYLE))
+ {
+ height = rw.bottom + 8;
+ bEnable = TRUE;
+ Settings_SetDirectoryCtrl(C_COPY, CF_PATH, hdlg, IDC_EDT_PATH);
+ Settings_SetCheckBox(C_COPY, CF_ADDTOMLDB, hdlg, IDC_CHK_ADDTOMLDB);
+ Settings_SetCheckBox(C_COPY, CF_USETITLEFMT, hdlg, IDC_CHK_CUSTOMNAME);
+ Settings_SetDlgItemText(C_COPY, CF_TITLEFMT, hdlg, IDC_EDT_NAMEFORMAT);
+ SetDlgItemText(hdlg, IDC_BTN_OPTIONS, WASABI_API_LNGSTRINGW(IDS_OPTIONS_HIDE));
+
+ if (ppd && NULL == ppd->pac)
+ {
+ HRESULT hr;
+ hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, IID_IAutoComplete, (LPVOID*)&ppd->pac);
+ if (S_OK == hr)
+ {
+ IAutoComplete2 *pac2;
+ if (SUCCEEDED(ppd->pac->QueryInterface(IID_IAutoComplete2, (LPVOID*)&pac2)))
+ {
+ pac2->SetOptions(ACO_AUTOSUGGEST | ACO_AUTOAPPEND | 0x00000020/*ACF_UPDOWNKEYDROPSLIST*/);
+ pac2->Release();
+ }
+
+ hr = CoCreateInstance(CLSID_ACListISF, NULL, CLSCTX_INPROC_SERVER, IID_IACList2, (LPVOID*)&ppd->pacl2);
+ if (S_OK == hr) ppd->pacl2->SetOptions(ACLO_FILESYSDIRS);
+ }
+ if(ppd->pac) ppd->pac->Init(GetDlgItem(hdlg, IDC_EDT_PATH), ppd->pacl2, NULL, NULL);
+ }
+ }
+ else
+ {
+ height = rw.top;
+ bEnable = FALSE;
+ SetDlgItemText(hdlg, IDC_BTN_OPTIONS, WASABI_API_LNGSTRINGW(IDS_OPTIONS_SHOW));
+ }
+
+ SetWindowPos(hdlg, NULL, 0, 0, rw2.right - rw2.left, height, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
+ EnableWindow(hctrl, bEnable);
+
+ UINT szIdList[] = { IDC_EDT_PATH, IDC_BTN_BROWSE, IDC_CHK_ADDTOMLDB,
+ IDC_CHK_CUSTOMNAME, IDC_EDT_NAMEFORMAT, IDC_BTN_HELP, };
+
+ for (int i = 0; i < sizeof(szIdList)/sizeof(szIdList[0]); i++) EnableWindow(GetDlgItem(hdlg, szIdList[i]), bEnable);
+ if (bEnable && BST_UNCHECKED == IsDlgButtonChecked(hdlg, IDC_CHK_CUSTOMNAME))
+ {
+ EnableWindow(GetDlgItem(hdlg, IDC_EDT_NAMEFORMAT), FALSE);
+ ShowWindow(GetDlgItem(hdlg, IDC_LBL_EXAMPLE), SW_HIDE);
+ ShowWindow(GetDlgItem(hdlg, IDC_LBL_EXAMPLE_TITLE), SW_HIDE);
+ }
+}
+
+static INT_PTR CopyPrepare_OnHelp(HWND hdlg, HELPINFO *phi)
+{
+ PREPDLG *ppd = GetPrepDlg(hdlg);
+ if (ppd && 0 == (WS_DISABLED & GetWindowLongPtrW(GetDlgItem(hdlg, IDC_GRP_OPTIONS), GWL_STYLE)))
+ {
+ if (ppd->hActiveHelp) SetWindowPos(ppd->hActiveHelp, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
+ else ppd->hActiveHelp = MLDisc_ShowHelp(hdlg, MAKEINTRESOURCE(IDS_COPY_FILENAME_FORMAT_TITLE),
+ MAKEINTRESOURCE(IDS_COPY_FILENAME_FORMAT_CAPTION), MAKEINTRESOURCE(IDS_COPY_FILENAME_FORMAT), HF_ALLOWRESIZE);
+ SetWindowLongPtrW(hdlg, DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+INT_PTR CALLBACK CopyPrepare_DialogProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG: return CopyPrepare_OnInitDialog(hdlg, (HWND)wParam, lParam);
+ case WM_DESTROY: CopyPrepare_OnDestroy(hdlg); break;
+ case WM_PARENTNOTIFY: CopyPrepare_OnParentNotify(hdlg, LOWORD(wParam), lParam); break;
+ case WM_HELP: return CopyPrepare_OnHelp(hdlg, (HELPINFO*)lParam);
+ case CPM_UPDATEDISKSIZE: CopyPrepare_OnUpdateDiskSizeResult(hdlg, (CALCDISKSIZE*)lParam); break;
+ case WM_COMMAND:
+ switch(LOWORD(wParam))
+ {
+ case IDOK:
+ case IDCANCEL:
+ EndDialog(hdlg, LOWORD(wParam));
+ break;
+ case IDC_BTN_BROWSE: if (HIWORD(wParam) == BN_CLICKED) Settings_BrowseForFolder(C_COPY, CF_PATH, hdlg, IDC_EDT_PATH); break;
+ case IDC_BTN_OPTIONS: if (HIWORD(wParam) == BN_CLICKED) CopyPrepare_OnOptionsClick(hdlg); break;
+ case IDC_CHK_ADDTOMLDB: if (BN_CLICKED == HIWORD(wParam)) Settings_FromCheckBox(C_COPY, CF_ADDTOMLDB, hdlg, IDC_CHK_ADDTOMLDB); break;
+ case IDC_CHK_CUSTOMNAME:
+ if (BN_CLICKED == HIWORD(wParam))
+ {
+ Settings_FromCheckBox(C_COPY, CF_USETITLEFMT, hdlg, IDC_CHK_CUSTOMNAME);
+ BOOL bEnable = (BST_UNCHECKED != IsDlgButtonChecked(hdlg, IDC_CHK_CUSTOMNAME));
+ EnableWindow(GetDlgItem(hdlg, IDC_EDT_NAMEFORMAT), bEnable);
+ ShowWindow(GetDlgItem(hdlg, IDC_LBL_EXAMPLE_TITLE), (bEnable) ? SW_SHOWNA : SW_HIDE);
+ ShowWindow(GetDlgItem(hdlg, IDC_LBL_EXAMPLE), (bEnable) ? SW_SHOWNA : SW_HIDE);
+
+ }
+ break;
+
+ case IDC_EDT_PATH:
+ if (EN_CHANGE == HIWORD(wParam))
+ {
+ Settings_FromDirectoryCtrl(C_COPY, CF_PATH, hdlg, IDC_EDT_PATH);
+ SetTimer(hdlg, TID_UPDATEDISKSIZE, DELAY_UPDATEDISKSIZE, CopyPrepare_OnUpdateDiskSizeTimer);
+ }
+ break;
+ case IDC_EDT_NAMEFORMAT:
+ if (EN_CHANGE == HIWORD(wParam))
+ {
+ Settings_FromDlgItemText(C_COPY, CF_TITLEFMT, hdlg, IDC_EDT_NAMEFORMAT);
+ DisplayFormatExample(hdlg, IDC_LBL_EXAMPLE);
+ }
+ break;
+
+ case IDC_BTN_HELP:
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ HELPINFO hi = {sizeof(HELPINFO), };
+ hi.dwContextId = HELPINFO_WINDOW;
+ hi.iCtrlId = IDC_EDT_NAMEFORMAT;
+ hi.hItemHandle = GetDlgItem(hdlg, IDC_EDT_NAMEFORMAT);
+ hi.iContextType = 0;
+ hi.MousePos.x = 0; hi.MousePos.y = 0;
+ SendMessageW(hdlg, WM_HELP, 0, (LPARAM)&hi);
+ }
+ break;
+ }
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/copyprogress.cpp b/Src/Plugins/Library/ml_disc/copyprogress.cpp
new file mode 100644
index 00000000..36bd36a7
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/copyprogress.cpp
@@ -0,0 +1,360 @@
+#include "main.h"
+#include "./copyfiles.h"
+#include "./copyinternal.h"
+#include "./resource.h"
+#include "./settings.h"
+#include "../nu/trace.h"
+#include <shlwapi.h>
+#include <strsafe.h>
+
+typedef struct _PROGDLG
+{
+ COPYDATA *pCopyData;
+ HBITMAP hbmpLogo;
+} PROGDLG;
+
+#define PROGDLG_PROP TEXT("PROGDLG")
+#define GetProgDlg(__hdlg) ((PROGDLG*)GetProp((__hdlg), PROGDLG_PROP))
+
+#define SetControlText(__hwnd, __ctrlId, __pszText)\
+ SetDlgItemText((__hwnd), (__ctrlId), (IS_INTRESOURCE(__pszText) ? WASABI_API_LNGSTRINGW((UINT)(UINT_PTR)(__pszText)) : (__pszText)))
+
+#define SetTaskText(__hwnd, __pszText) SetControlText(__hwnd, IDC_LBL_TASK, __pszText)
+#define SetOperationText(__hwnd, __pszText) SetControlText(__hwnd, IDC_LBL_OPERATION, __pszText)
+
+
+static INT_PTR CopyProgress_OnInitDialog(HWND hdlg, HWND hFocus, LPARAM lParam)
+{
+ HWND hctrl;
+ PROGDLG *ppd = (PROGDLG*)calloc(1, sizeof(PROGDLG));
+ if (!ppd) return 0;
+
+ SetProp(hdlg, PROGDLG_PROP, ppd);
+ ppd->pCopyData = (COPYDATA*)lParam;
+ CopyFiles_AddRef(ppd->pCopyData);
+ if (ppd->pCopyData && ppd->pCopyData->hOwner)
+ {
+ RECT rw;
+ if (!GetWindowRect(ppd->pCopyData->hOwner, &rw)) SetRect(&rw, 0, 0, 0, 0);
+ if (hdlg && rw.left != rw.right)
+ {
+ RECT rw2;
+ GetWindowRect(hdlg, &rw2);
+ SetWindowPos(hdlg, HWND_TOP,
+ rw.left + ((rw.right - rw.left) - (rw2.right - rw2.left))/2,
+ rw.top + ((rw.bottom - rw.top) - (rw2.bottom - rw2.top))/2,
+ 0, 0, SWP_NOACTIVATE | SWP_NOSIZE);
+ }
+ }
+
+ hctrl = GetDlgItem(hdlg, IDC_PRG_TOTAL);
+ if (NULL != hctrl)
+ {
+ SendMessage(hctrl, PBM_SETRANGE32, (WPARAM)0, (LPARAM)100);
+ SendMessage(hctrl, PBM_SETPOS, (WPARAM)0, 0L);
+ SendMessage(hctrl, PBM_SETSTEP, (WPARAM)1, 0L);
+ }
+
+ SetTaskText(hdlg, MAKEINTRESOURCE(IDS_COPY_TASK_PREPARE));
+ SetOperationText(hdlg, TEXT(""));
+
+ SendMessage(hdlg, DM_REPOSITION, 0, 0L);
+
+ ppd->hbmpLogo = CopyFiles_LoadResourcePng(MAKEINTRESOURCE(IDB_FILECOPY));
+ if (ppd->hbmpLogo) SendDlgItemMessage(hdlg, IDC_PIC_LOGO, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)ppd->hbmpLogo);
+ else ShowWindow(GetDlgItem(hdlg, IDC_PIC_LOGO), SW_HIDE);
+
+ return FALSE;
+}
+
+static void CopyProgress_OnDestroy(HWND hdlg)
+{
+ PROGDLG *ppd = GetProgDlg(hdlg);
+ RemoveProp(hdlg, PROGDLG_PROP);
+ if (ppd)
+ {
+ if (ppd->pCopyData) CopyFiles_Release(ppd->pCopyData);
+
+ if (ppd->hbmpLogo)
+ {
+ HBITMAP hbmp = (HBITMAP)SendDlgItemMessage(hdlg, IDC_PIC_LOGO, STM_GETIMAGE, IMAGE_BITMAP, 0L);
+ if (hbmp != ppd->hbmpLogo) DeleteObject(hbmp);
+ DeleteObject(ppd->hbmpLogo);
+ }
+
+ free(ppd);
+ }
+}
+
+static void ShowErrorBox(HWND hdlg)
+{
+ PROGDLG *ppd = GetProgDlg(hdlg);
+ if (!ppd || !ppd->pCopyData) return;
+
+
+ TCHAR szBuffer[2048] = {0}, szFormat[256] = {0}, szUnknown[64] = {0};
+ LPTSTR pszMessage;
+
+ if (ERROR_REQUEST_ABORTED == ppd->pCopyData->errorCode) return; // ignore user aborts
+
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
+ ppd->pCopyData->errorCode, 0, (LPTSTR)&pszMessage, 0, NULL);
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_UNKNOWN, szUnknown, ARRAYSIZE(szUnknown));
+ WASABI_API_LNGSTRINGW_BUF(IDS_COPY_ERROR_MESSAGE, szFormat, ARRAYSIZE(szFormat));
+
+ StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), szFormat,
+ ppd->pCopyData->szDestination,
+ (ppd->pCopyData->errorMsgId) ? WASABI_API_LNGSTRINGW(ppd->pCopyData->errorMsgId) : szUnknown,
+ ppd->pCopyData->errorCode,
+ (pszMessage) ? pszMessage : szUnknown);
+
+ MessageBox(hdlg, szBuffer, WASABI_API_LNGSTRINGW(IDS_COPY_ERROR_CAPTION), MB_OK | MB_ICONERROR);
+ if (pszMessage) LocalFree(pszMessage);
+}
+
+static void CopyProgress_OnDisplayNextFile(HWND hdlg, INT iFile, INT iCount)
+{
+ PROGDLG *ppd = GetProgDlg(hdlg);
+ if (!ppd || !ppd->pCopyData) return;
+ SetOperationText(hdlg, PathFindFileName(ppd->pCopyData->ppszFiles[iFile]));
+
+}
+
+
+static INT_PTR CopyProgress_OnDestiantionNotExist(HWND hdlg, LPCTSTR pszDestination)
+{
+ PROGDLG *ppd = GetProgDlg(hdlg);
+ if (!ppd || !ppd->pCopyData) return FALSE;
+ QUESTIONBOX qb = {0};
+ TCHAR szFormat[MAX_PATH] = {0}, szMessage[MAX_PATH*2] = {0};
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_DESTINATION_NOT_EXIST_FORMAT, szFormat, ARRAYSIZE(szFormat));
+ StringCchPrintf(szMessage, ARRAYSIZE(szMessage), szFormat, pszDestination);
+
+ qb.hParent =hdlg;
+ qb.pszIcon = IDI_QUESTION;
+ qb.pszTitle = MAKEINTRESOURCE(IDS_CONFIRM_CREATE_DESTINATION);
+ qb.pszMessage = szMessage;
+ qb.pszBtnOkText = MAKEINTRESOURCE(IDS_YES);
+ qb.pszBtnCancelText = MAKEINTRESOURCE(IDS_NO);
+ qb.uBeepType = MB_ICONEXCLAMATION;
+ qb.uFlags = QBF_DEFAULT_OK | QBF_SETFOREGROUND | QBF_BEEP;
+ return (IDCANCEL == MLDisc_ShowQuestionBox(&qb));
+}
+
+static INT_PTR CopyProgress_OnReadOnly(HWND hdlg, LPCTSTR pszFile)
+{
+ PROGDLG *ppd = GetProgDlg(hdlg);
+ if (!ppd || !ppd->pCopyData) return FALSE;
+ QUESTIONBOX qb = {0};
+ TCHAR szFormat[MAX_PATH] = {0}, szMessage[MAX_PATH*2] = {0};
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_READONLY_FILE_DELETE_FORMAT, szFormat, ARRAYSIZE(szFormat));
+ StringCchPrintf(szMessage, ARRAYSIZE(szMessage), szFormat, pszFile);
+
+ qb.hParent =hdlg;
+ qb.pszIcon = IDI_QUESTION;
+ qb.pszTitle = MAKEINTRESOURCE(IDS_CONFIRM_FILE_DELETE);
+ qb.pszMessage = szMessage;
+ qb.pszBtnOkText = MAKEINTRESOURCE(IDS_YES);
+ qb.pszBtnCancelText = MAKEINTRESOURCE(IDS_CANCEL);
+ qb.pszCheckboxText = MAKEINTRESOURCE(IDS_APPLY_TO_ALL_FILES);
+ qb.uBeepType = MB_ICONEXCLAMATION;
+ qb.uFlags = QBF_DEFAULT_OK | QBF_SETFOREGROUND | QBF_BEEP | QBF_SHOW_CHECKBOX;
+
+ switch(MLDisc_ShowQuestionBox(&qb))
+ {
+ case IDCANCEL: return READONLY_CANCELCOPY;
+ case IDOK: return (qb.checkboxChecked) ? READONLY_DELETEALL : READONLY_DELETE;
+ }
+
+ return FALSE;
+}
+
+static LPTSTR FormatFileInfo(LPTSTR pszBuffer, size_t cchBufferMax, LPCTSTR pszFilePath)
+{
+ HANDLE hFile;
+ HRESULT hr;
+ BY_HANDLE_FILE_INFORMATION fi;
+
+ pszBuffer[0] = TEXT('\0');
+
+ hFile = CreateFile(pszFilePath, FILE_READ_ATTRIBUTES | FILE_READ_EA,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL);
+
+ if (INVALID_HANDLE_VALUE != hFile &&
+ GetFileInformationByHandle(hFile, &fi))
+ {
+ TCHAR szTemp[1024] = {0}, szKeyword[64] = {0};
+ SYSTEMTIME st = {0};
+
+ LONGLONG fsize = (LONGLONG)(((__int64)fi.nFileSizeHigh<< 32) | fi.nFileSizeLow);
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_SIZE, szKeyword, ARRAYSIZE(szKeyword));
+ hr = StringCchPrintfEx(pszBuffer, cchBufferMax, &pszBuffer, &cchBufferMax, STRSAFE_IGNORE_NULLS, TEXT("\n %s: %s"), szKeyword, StrFormatByteSize64(fsize, szTemp, ARRAYSIZE(szTemp)));
+
+ if (S_OK == hr && FileTimeToSystemTime(&fi.ftCreationTime, &st) &&
+ GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, szTemp, ARRAYSIZE(szTemp)))
+ {
+ WASABI_API_LNGSTRINGW_BUF(IDS_CREATED, szKeyword, ARRAYSIZE(szKeyword));
+ hr = StringCchPrintfEx(pszBuffer, cchBufferMax, &pszBuffer, &cchBufferMax, STRSAFE_IGNORE_NULLS, TEXT("\n %s: %s"), szKeyword, szTemp);
+ if (S_OK == hr && GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, szTemp, ARRAYSIZE(szTemp)))
+ hr = StringCchPrintfEx(pszBuffer, cchBufferMax, &pszBuffer, &cchBufferMax, STRSAFE_IGNORE_NULLS, TEXT(", %s"), szTemp);
+
+ }
+
+
+ if (S_OK == hr && FileTimeToSystemTime(&fi.ftLastWriteTime, &st) &&
+ GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, szTemp, ARRAYSIZE(szTemp)))
+ {
+ WASABI_API_LNGSTRINGW_BUF(IDS_MODIFIED, szKeyword, ARRAYSIZE(szKeyword));
+ hr = StringCchPrintfEx(pszBuffer, cchBufferMax, &pszBuffer, &cchBufferMax, STRSAFE_IGNORE_NULLS, TEXT("\n %s: %s"), szKeyword, szTemp);
+ if (S_OK == hr && GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, szTemp, ARRAYSIZE(szTemp)))
+ hr = StringCchPrintfEx(pszBuffer, cchBufferMax, &pszBuffer, &cchBufferMax, STRSAFE_IGNORE_NULLS, TEXT(", %s"), szTemp);
+
+ }
+ if (S_OK == hr)
+ {
+ hr = StringCchCopyEx(pszBuffer, cchBufferMax, TEXT("\n\n"), &pszBuffer, &cchBufferMax, STRSAFE_IGNORE_NULLS);
+ }
+ }
+ else hr = S_FALSE;
+
+ if (S_OK != hr) pszBuffer[0] = TEXT('\0');
+
+ if (INVALID_HANDLE_VALUE != hFile) CloseHandle(hFile);
+ return pszBuffer;
+
+}
+static INT_PTR CopyProgress_OnFileAlreadyExist(HWND hdlg, FILECONFLICT *pConflict)
+{
+ PROGDLG *ppd = GetProgDlg(hdlg);
+ if (!ppd || !ppd->pCopyData) return FALSE;
+ QUESTIONBOX qb = {0};
+ TCHAR szFormat[128] = {0}, szMessage[MAX_PATH*2] = {0}, szPath[MAX_PATH] = {0}, szFileInfo1[128] = {0}, szFileInfo2[128] = {0};
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_FILE_REPLACE_FORMAT, szFormat, ARRAYSIZE(szFormat));
+
+ StringCchCopy(szPath, ARRAYSIZE(szPath), pConflict->pszNameExisting);
+ LPTSTR pszFileName = PathFindFileName(szPath);
+ if (pszFileName && pszFileName > szPath) *(pszFileName - 1) = TEXT('\0');
+
+ FormatFileInfo(szFileInfo1, ARRAYSIZE(szFileInfo1), pConflict->pszNameExisting);
+ FormatFileInfo(szFileInfo2, ARRAYSIZE(szFileInfo2), pConflict->pszNameNew);
+
+ StringCchPrintf(szMessage, ARRAYSIZE(szMessage), szFormat, szPath, pszFileName, szFileInfo1, szFileInfo2);
+
+ qb.hParent = hdlg;
+ qb.pszIcon = IDI_QUESTION;
+ qb.pszTitle = MAKEINTRESOURCE(IDS_CONFIRM_FILE_REPLACE);
+ qb.pszMessage = szMessage;
+
+ qb.pszBtnExtraText = MAKEINTRESOURCE(IDS_SKIP);
+ qb.pszBtnOkText = MAKEINTRESOURCE(IDS_OVERWRITE);
+ qb.pszBtnCancelText = MAKEINTRESOURCE(IDS_CANCEL);
+ qb.pszCheckboxText = MAKEINTRESOURCE(IDS_APPLY_TO_ALL_FILES);
+ qb.uBeepType = MB_ICONEXCLAMATION;
+
+ qb.uFlags = QBF_DEFAULT_OK | QBF_SETFOREGROUND | QBF_BEEP | QBF_SHOW_CHECKBOX | QBF_SHOW_EXTRA_BUTTON;
+
+ INT_PTR qbr = MLDisc_ShowQuestionBox(&qb);
+ INT r = 0;
+ switch(qbr)
+ {
+ case IDCANCEL: r = EXISTFILE_CANCELCOPY; break;
+ case IDOK: r = EXISTFILE_OVERWRITE; break;
+ case IDC_BTN_EXTRA1: r = EXISTFILE_SKIP; break;
+ }
+
+ if (qb.checkboxChecked) r |= EXISTFILE_APPLY_TO_ALL;
+ return r;
+}
+
+static INT_PTR CopyProgress_OnCopyNotify(HWND hdlg, UINT uTask, UINT uOperation, LPARAM lParam)
+{
+ switch(uTask)
+ {
+ case CFT_INITIALIZING:
+
+ switch(uOperation)
+ {
+ case CFO_INIT:
+ SetTaskText(hdlg, MAKEINTRESOURCE(IDS_COPY_TASK_PREPARE));
+ SetOperationText(hdlg, TEXT(""));
+ break;
+ case CFO_CACLSIZE:
+ SetOperationText(hdlg, MAKEINTRESOURCE(IDS_COPY_OP_CALCULATESIZE));
+ break;
+ case CFO_CHECKDESTINATION:
+ SetOperationText(hdlg, MAKEINTRESOURCE(IDS_COPY_OP_CHECKDESTINATION));
+ break;
+ }
+ break;
+ case CFT_COPYING:
+ switch(uOperation)
+ {
+ case CFO_INIT:
+ SetTaskText(hdlg, MAKEINTRESOURCE(IDS_COPY_TASK_COPY));
+ SetOperationText(hdlg, TEXT(""));
+ break;
+ case CFO_NEXTFILE:
+ CopyProgress_OnDisplayNextFile(hdlg, LOWORD(lParam), HIWORD(lParam));
+ break;
+ case CFO_PROGRESS:
+ SendDlgItemMessage(hdlg, IDC_PRG_TOTAL, PBM_SETPOS, (WPARAM)lParam, 0L);
+ break;
+ }
+ break;
+ case CFT_FINISHED:
+ SetTaskText(hdlg, MAKEINTRESOURCE(IDS_COPY_TASK_FINISHED));
+ switch(uOperation)
+ {
+ case CFO_SUCCESS: SetOperationText(hdlg, MAKEINTRESOURCE(IDS_COMPLETED)); break;
+ case CFO_CANCELLED: SetOperationText(hdlg, MAKEINTRESOURCE(IDS_CANCELLED)); break;
+ case CFO_FAILED:
+ SetOperationText(hdlg, MAKEINTRESOURCE(IDS_FAILED));
+ ShowErrorBox(hdlg);
+ break;
+ }
+ DestroyWindow(hdlg);
+ break;
+ case CFT_CONFLICT:
+ switch(uOperation)
+ {
+ case CFO_DESTNOTEXIST: return CopyProgress_OnDestiantionNotExist(hdlg, (LPCTSTR)lParam);
+ case CFO_FILEALREDYEXIST: return CopyProgress_OnFileAlreadyExist(hdlg, (FILECONFLICT*)lParam);
+ case CFO_READONLY: return CopyProgress_OnReadOnly(hdlg, (LPCTSTR)lParam);
+ }
+ break;
+ }
+ return FALSE;
+}
+INT_PTR CALLBACK CopyProgress_DialogProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ PROGDLG *ppd = GetProgDlg(hdlg);
+
+ switch(uMsg)
+ {
+ case WM_INITDIALOG: return CopyProgress_OnInitDialog(hdlg, (HWND)wParam, lParam);
+ case WM_DESTROY: CopyProgress_OnDestroy(hdlg); break;
+ case WM_COMMAND:
+ switch(LOWORD(wParam))
+ {
+ case IDOK:
+ case IDCANCEL:
+ if (ppd && ppd->pCopyData)
+ {
+ SetOperationText(hdlg, MAKEINTRESOURCE(IDS_CANCELLING));
+ SendDlgItemMessage(hdlg, IDCANCEL, BM_SETSTATE, (WPARAM)TRUE, 0L);
+ EnableWindow(GetDlgItem(hdlg, IDCANCEL), FALSE);
+ CopyFiles_CancelCopy(ppd->pCopyData);
+ }
+ else DestroyWindow(hdlg);
+ break;
+ }
+ case CFM_NOTIFY: MSGRESULT(hdlg, CopyProgress_OnCopyNotify(hdlg, LOWORD(wParam), HIWORD(wParam), lParam));
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/discInfo.cpp b/Src/Plugins/Library/ml_disc/discInfo.cpp
new file mode 100644
index 00000000..9aff366d
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/discInfo.cpp
@@ -0,0 +1,328 @@
+#include "main.h"
+#include "discinfo.h"
+#include "resource.h"
+#include <strsafe.h>
+
+// indices
+#define DD_PARSED 0x0000
+#define DD_INDEX_MEDIUM_TYPE 0x0001
+#define DD_INDEX_MEDIUM_FORMAT 0x0002
+#define DD_INDEX_MEDIUM 0x0003
+#define DD_INDEX_PROTECTED_DVD 0x0004
+#define DD_INDEX_ERASABLE 0x0005
+#define DD_INDEX_TRACKS_NUM 0x0006
+#define DD_INDEX_SECTOR_USED 0x0007
+#define DD_INDEX_SECTOR_FREE 0x0008
+
+#define TEXT_UNKNOWN L"Unknown"
+#define TEXT_TRUE L"Yes"
+#define TEXT_FALSE L"No"
+
+// MediumType Values
+#define PRIMOSDK_SILVER 0x00000301 /* A disc that is not recordable. It may be a stamped (silver) disc or a gold (recordable) disc that has been recorded Disc-At-Once. */
+#define PRIMOSDK_COMPLIANTGOLD 0x00000302 /* A gold disc or rewritable disc that contains data but remains open, allowing the appending of additional data. */
+#define PRIMOSDK_OTHERGOLD 0x00000303 /* A gold disc to which it is not possible for PrimoSDK to append additional data. */
+#define PRIMOSDK_BLANK 0x00000304 /* A blank gold disc or blank rewritable disc. */
+
+// MediumFormat Values
+#define PRIMOSDK_B1 0x000000B1 /* Blank disc */
+#define PRIMOSDK_D1 0x000000D1 /* Data Mode 1 DAO (e.g. most data CD-ROMs or typical DOS games) */
+#define PRIMOSDK_D2 0x000000D2 /* Kodak Photo CD: Data multisession Mode 2 TAO */
+#define PRIMOSDK_D3 0x000000D3 /* Gold Data Mode 1: Data multisession Mode 1, closed */
+#define PRIMOSDK_D4 0x000000D4 /* Gold Data Mode 2: Data multisession Mode 2, closed */
+#define PRIMOSDK_D5 0x000000D5 /* Data Mode 2 DAO (silver mastered from Corel or Toast gold) */
+#define PRIMOSDK_D6 0x000000D6 /* CDRFS: Fixed packet (from Sony packet-writing solution) */
+#define PRIMOSDK_D7 0x000000D7 /* Packet writing */
+#define PRIMOSDK_D8 0x000000D8 /* Gold Data Mode 1: Data multisession Mode 1, open */
+#define PRIMOSDK_D9 0x000000D9 /* Gold Data Mode 2: Data multisession Mode 2, open */
+#define PRIMOSDK_A1 0x000000A1 /* Audio DAO/SAO/TAO (like most silver music discs) or closed gold audio */
+#define PRIMOSDK_A2 0x000000A2 /* Audio Gold disc with session not closed (TAO or SAO) */
+#define PRIMOSDK_A3 0x000000A3 /* First type of Enhanced CD (aborted) */
+#define PRIMOSDK_A4 0x000000A4 /* CD Extra, Blue Book standard */
+#define PRIMOSDK_A5 0x000000A5 /* Audio TAO with session not written (in-progress compilation) */
+#define PRIMOSDK_M1 0x000000E1 /* First track data, others audio */
+#define PRIMOSDK_M2 0x000000E2 /* Mixed-mode made TAO */
+#define PRIMOSDK_M3 0x000000E3 /* Kodak Portfolio (as per the Kodak standard) */
+#define PRIMOSDK_M4 0x000000E4 /* Video CD (as the White Book standard) */
+#define PRIMOSDK_M5 0x000000E5 /* CD-i (as the Green Book standard) */
+#define PRIMOSDK_M6 0x000000E6 /* PlayStation (Sony games) */
+#define PRIMOSDK_F1 0x000000F1 /* Obsolete */
+#define PRIMOSDK_F2 0x000000F2 /* Obsolete for restricted overwrite DVD (DLA DVD-RW) */
+#define PRIMOSDK_F3 0x000000F3 /* Completed (non-appendable) DVD (DVD-ROM or closed recordable) */
+#define PRIMOSDK_F4 0x000000F4 /* Incremental DVD with appendable zone (DLA DVD-R and DVD+RW) */
+#define PRIMOSDK_F8 0x000000F8 /* Appendable DVD of any type (single border or multiborder) */
+#define PRIMOSDK_FA 0x000000FA /* DVD-RAM cartridge */
+#define PRIMOSDK_GENERICCD 0x000000C1 /* Other type of CD. */
+
+// Medium and Unit Values
+#define PRIMOSDK_CDROM 0x00000201 /* CD-ROM */
+#define PRIMOSDK_CDR 0x00000202 /* CD-R */
+#define PRIMOSDK_CDRW 0x00000203 /* CD-RW */
+#define PRIMOSDK_DVDR 0x00000204 /* DVD-R */
+#define PRIMOSDK_DVDROM 0x00000205 /* DVD-ROM (any type) */
+#define PRIMOSDK_DVDRAM 0x00000206 /* DVD-RAM */
+#define PRIMOSDK_DVDRW 0x00000207 /* DVD-RW */
+#define PRIMOSDK_DVDPRW 0x00000209 /* DVD+RW */
+#define PRIMOSDK_DVDPR 0x00000210 /* DVD+R */
+#define PRIMOSDK_DDCDROM 0x00000211 /* Double-density CD-ROM */
+#define PRIMOSDK_DDCDR 0x00000212 /* Double-density CD-R */
+#define PRIMOSDK_DDCDRW 0x00000213 /* Double-density CD-RW */
+#define PRIMOSDK_DVDPR9 0x00000214 /* dual-layer DVD+R */
+#define PRIMOSDK_OTHER 0x00000220 /* other types */
+
+
+int mediumTypeText[] = {IDS_STAMPED_DISC_OR_RECORDABLE_THAT_HAS_BEEN_RECORDED,
+ IDS_REWRITEABLE_DISC_HAS_DATA_BUT_KEPT_OPEN_FOR_APPEND,
+ IDS_REWRITEABLE_DISC_NOT_POSSIBLE_TO_APPEND_DATA,
+ IDS_BLANK_REWRITEABLE_DISC,
+ };
+
+const wchar_t *mediumText[] = {L"CD-ROM", L"CD-R", L"CD-RW", L"DVD-ROM",
+ L"DVD-R",L"DVD-RW", L"DVD+R", L"DVD+RW", L"DVD-RAM",
+ L"DDCD-ROM", L"DDCD-R", L"DDCD-RW", L"DL DVD+R"};
+
+int mediumFormatText[] = { IDS_MEDIA_BLANK_DISC,
+ IDS_MEDIA_DATA_MODE_1_DAO,
+ IDS_MEDIA_KODAK_PHOTO_CD,
+ IDS_MEDIA_DATA_MULTISESSION_MODE_1_CLOSED,
+ IDS_MEDIA_DATA_MULTISESSION_MODE_2_CLOSED,
+ IDS_MEDIA_DATA_MODE_2_DAO,
+ IDS_MEDIA_CDRFS,
+ IDS_MEDIA_PACKET_WRITING,
+ IDS_MEDIA_DATA_MULTISESSION_MODE_1_OPEN,
+ IDS_MEDIA_DATA_MULTISESSION_MODE_2_OPEN,
+ IDS_MEDIA_AUDIO_DAO_SAO_TAO,
+ IDS_MEDIA_AUDIO_REWRITEABLE_DISC_WITH_SESSION_NOT_CLOSED,
+ IDS_MEDIA_FIRST_TYPE_OF_ENHANCED_CD_ABORTED,
+ IDS_MEDIA_CD_EXTRA,
+ IDS_MEDIA_AUDIO_TAO_WITH_SESSION_NOT_WRITTEN,
+ IDS_MEDIA_FIRST_TRACK_DATA_OTHERS_AUDIO,
+ IDS_MEDIA_MIXED_MODE_MADE_TAO,
+ IDS_MEDIA_KODAK_PORTFOLIO,
+ IDS_MEDIA_VIDEO_CD,
+ IDS_MEDIA_CDi,
+ IDS_MEDIA_PLAYSTATION_SONY_GAMES,
+ IDS_MEDIA_OBSOLETE,
+ IDS_MEDIA_OBSOLETE_FOR_RESTRICTED_OVERWRITE_DVD,
+ IDS_MEDIA_DVDROM_OR_CLOSED_RECORDABLE,
+ IDS_MEDIA_INCREMENTAL_DVD_WITH_APPENDABLE_ZONE,
+ IDS_MEDIA_APPENDABLE_DVD_OF_ANY_TYPE,
+ IDS_MEDIA_DVDRAM_CARTRIDGE,
+ IDS_MEDIA_CD_OTHER_TYPE
+ };
+
+DiscInfo::DiscInfo(void)
+{
+ serialNum = 0;
+ memset(buffer, 0, sizeof(buffer));
+ strData = NULL;
+ ResetData();
+}
+
+DiscInfo::DiscInfo(const wchar_t *info)
+{
+ serialNum = 0;
+ memset(buffer, 0, sizeof(buffer));
+ strData = NULL;
+ SetStringInfo(info);
+}
+
+
+DiscInfo::~DiscInfo(void)
+{
+ ResetData();
+}
+
+void DiscInfo::ResetData(void)
+{
+ if (strData) free(strData);
+ strData = NULL;
+
+ data[DD_PARSED]= FALSE;
+ for (int i = 1; i < DISC_DATA_COUNT; i++) data[i] = -1;
+}
+
+BOOL DiscInfo::SetStringInfo(const wchar_t *info)
+{
+ ResetData();
+
+ int strLen = lstrlenW(info) + 1;
+ strData = (wchar_t*) malloc(strLen * sizeof(wchar_t));
+ StringCchCopyW(strData, strLen, info);
+
+ const wchar_t *start = info;
+ const wchar_t *end = info;
+ BOOL cont;
+ int i = 1;
+ do
+ {
+ while(end[0] != L';' && end[0] != 0x00) end = CharNextW(end);
+ cont = (end[0] == L';');
+
+ data[i++] = _wtoi(start);
+
+ if(cont)
+ {
+ end = CharNextW(end);
+ start = end;
+ }
+ }
+ while(cont && i < DISC_DATA_COUNT);
+
+ data[DD_PARSED] = (i == DISC_DATA_COUNT);
+ return data[DD_PARSED];
+}
+const wchar_t* DiscInfo::GetStringInfo(void)
+{
+ return strData;
+}
+DWORD DiscInfo::GetMedium(void)
+{
+ return data[DD_INDEX_MEDIUM];
+}
+DWORD DiscInfo::GetMediumType(void)
+{
+ return data[DD_INDEX_MEDIUM_TYPE];
+}
+DWORD DiscInfo::GetMediumFormat(void)
+{
+ return data[DD_INDEX_MEDIUM_FORMAT];
+}
+BOOL DiscInfo::GetProtectedDVD(void)
+{
+ return data[DD_INDEX_PROTECTED_DVD];
+}
+BOOL DiscInfo::GetErasable(void)
+{
+ return data[DD_INDEX_ERASABLE];
+}
+DWORD DiscInfo::GetTracksNumber(void)
+{
+ return data[DD_INDEX_TRACKS_NUM];
+}
+DWORD DiscInfo::GetSectorsUsed(void)
+{
+ return data[DD_INDEX_SECTOR_USED];
+}
+DWORD DiscInfo::GetSectorsFree(void)
+{
+ return data[DD_INDEX_SECTOR_FREE];
+}
+
+BOOL DiscInfo::GetRecordable(void)
+{
+ return (data[DD_INDEX_MEDIUM_TYPE] == PRIMOSDK_COMPLIANTGOLD || data[DD_INDEX_MEDIUM_TYPE] == PRIMOSDK_BLANK);
+}
+
+int DiscInfo::GetSerialNumber(void)
+{
+ return serialNum;
+}
+
+void DiscInfo::SetSerialNumber(int serialNumber)
+{
+ serialNum = serialNumber;
+}
+
+const wchar_t* DiscInfo::GetMediumText(void)
+{
+ int index = -1;
+ switch(data[DD_INDEX_MEDIUM])
+ {
+ case PRIMOSDK_CDROM: index = 0; break;
+ case PRIMOSDK_CDR: index = 1; break;
+ case PRIMOSDK_CDRW: index = 2; break;
+ case PRIMOSDK_DVDR: index = 4; break;
+ case PRIMOSDK_DVDROM: index = 3; break;
+ case PRIMOSDK_DVDRAM: index = 8; break;
+ case PRIMOSDK_DVDRW: index = 5; break;
+ case PRIMOSDK_DVDPRW: index = 7; break;
+ case PRIMOSDK_DVDPR: index = 6; break;
+ case PRIMOSDK_DDCDROM: index = 9; break;
+ case PRIMOSDK_DDCDR: index = 10; break;
+ case PRIMOSDK_DDCDRW: index = 11; break;
+ case PRIMOSDK_DVDPR9: index = 12; break;
+ default: return TEXT_UNKNOWN;
+ }
+ return mediumText[index];
+}
+const wchar_t* DiscInfo::GetMediumTypeText(void)
+{
+ static wchar_t tmp[256];
+ int index = -1;
+ switch(data[DD_INDEX_MEDIUM_TYPE])
+ {
+ case PRIMOSDK_SILVER: index = 0; break;
+ case PRIMOSDK_COMPLIANTGOLD: index = 1; break;
+ case PRIMOSDK_OTHERGOLD: index = 2; break;
+ case PRIMOSDK_BLANK: index = 3; break;
+ default: return WASABI_API_LNGSTRINGW_BUF(plugin.hDllInstance,IDS_UNKNOWN,tmp,256);
+ }
+ return WASABI_API_LNGSTRINGW_BUF(plugin.hDllInstance,mediumTypeText[index],tmp,256);
+}
+const wchar_t* DiscInfo::GetMediumFormatText(void)
+{
+ static wchar_t tmpM[256];
+ int index = -1;
+ switch(data[DD_INDEX_MEDIUM_FORMAT])
+ {
+ case PRIMOSDK_B1: index = 0; break;
+ case PRIMOSDK_D1: index = 1; break;
+ case PRIMOSDK_D2: index = 2; break;
+ case PRIMOSDK_D3: index = 3; break;
+ case PRIMOSDK_D4: index = 4; break;
+ case PRIMOSDK_D5: index = 5; break;
+ case PRIMOSDK_D6: index = 6; break;
+ case PRIMOSDK_D7: index = 7; break;
+ case PRIMOSDK_D8: index = 8; break;
+ case PRIMOSDK_D9: index = 9; break;
+ case PRIMOSDK_A1: index = 10; break;
+ case PRIMOSDK_A2: index = 11; break;
+ case PRIMOSDK_A3: index = 12; break;
+ case PRIMOSDK_A4: index = 13; break;
+ case PRIMOSDK_A5: index = 14; break;
+ case PRIMOSDK_M1: index = 15; break;
+ case PRIMOSDK_M2: index = 16; break;
+ case PRIMOSDK_M3: index = 17; break;
+ case PRIMOSDK_M4: index = 18; break;
+ case PRIMOSDK_M5: index = 19; break;
+ case PRIMOSDK_M6: index = 20; break;
+ case PRIMOSDK_F1: index = 21; break;
+ case PRIMOSDK_F2: index = 22; break;
+ case PRIMOSDK_F3: index = 23; break;
+ case PRIMOSDK_F4: index = 24; break;
+ case PRIMOSDK_F8: index = 25; break;
+ case PRIMOSDK_FA: index = 26; break;
+ case PRIMOSDK_GENERICCD: index = 27; break;
+ default: return WASABI_API_LNGSTRINGW_BUF(plugin.hDllInstance,IDS_UNKNOWN,tmpM,256);
+ }
+ return WASABI_API_LNGSTRINGW_BUF(plugin.hDllInstance,mediumFormatText[index],tmpM,256);
+}
+const wchar_t* DiscInfo::GetProtectedDVDText(void)
+{
+ return data[DD_INDEX_PROTECTED_DVD] ? TEXT_TRUE : TEXT_FALSE ;
+}
+const wchar_t* DiscInfo::GetErasableText(void)
+{
+ return data[DD_INDEX_ERASABLE] ? TEXT_TRUE: TEXT_FALSE ;
+}
+const wchar_t* DiscInfo::GetTracksNumberText(void)
+{
+ StringCchPrintfW(buffer, TEXT_BUFFER_SIZE, L"%d", data[DD_INDEX_TRACKS_NUM]);
+ return buffer;
+}
+const wchar_t* DiscInfo::GetSectorsUsedText(void)
+{
+ StringCchPrintfW(buffer, TEXT_BUFFER_SIZE, L"%d", data[DD_INDEX_SECTOR_USED]);
+ return buffer;
+}
+const wchar_t* DiscInfo::GetSectorsFreeText(void)
+{
+ StringCchPrintfW(buffer, TEXT_BUFFER_SIZE, L"%d", data[DD_INDEX_SECTOR_FREE]);
+ return buffer;
+}
+
+const wchar_t* DiscInfo::GetRecordableText(void)
+{
+ return GetRecordable() ? TEXT_TRUE : TEXT_FALSE;
+}
diff --git a/Src/Plugins/Library/ml_disc/discInfo.h b/Src/Plugins/Library/ml_disc/discInfo.h
new file mode 100644
index 00000000..89f6d30e
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/discInfo.h
@@ -0,0 +1,61 @@
+#ifndef NULLSOFT_DISCINFO_HEADER
+#define NULLSOFT_DISCINFO_HEADER
+
+#include <windows.h>
+
+
+// disc data array size (see decalrations in discInfo.cpp)
+#define DISC_DATA_COUNT 0x0009
+
+#define TEXT_BUFFER_SIZE 64
+
+
+
+class DiscInfo
+{
+public:
+ DiscInfo(void);
+ DiscInfo(const wchar_t *info);
+ ~DiscInfo(void);
+
+public:
+
+ BOOL SetStringInfo(const wchar_t *info);
+ const wchar_t* GetStringInfo(void);
+
+ DWORD GetMedium(void);
+ DWORD GetMediumType(void);
+ DWORD GetMediumFormat(void);
+ BOOL GetProtectedDVD(void);
+ BOOL GetErasable(void);
+ DWORD GetTracksNumber(void);
+ DWORD GetSectorsUsed(void);
+ DWORD GetSectorsFree(void);
+ void SetSerialNumber(int serialNumber);
+ int GetSerialNumber(void);
+
+ BOOL GetRecordable(void);
+
+ const wchar_t* GetMediumText(void);
+ const wchar_t* GetMediumTypeText(void);
+ const wchar_t* GetMediumFormatText(void);
+ const wchar_t* GetProtectedDVDText(void);
+ const wchar_t* GetErasableText(void);
+ const wchar_t* GetTracksNumberText(void);
+ const wchar_t* GetSectorsUsedText(void);
+ const wchar_t* GetSectorsFreeText(void);
+
+ const wchar_t* GetRecordableText(void);
+
+protected:
+ void ResetData(void);
+
+private:
+ wchar_t *strData;
+ int serialNum;
+ wchar_t buffer[TEXT_BUFFER_SIZE];
+ DWORD data[DISC_DATA_COUNT];
+
+};
+
+#endif // NULLSOFT_DISCINFO_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/drive.cpp b/Src/Plugins/Library/ml_disc/drive.cpp
new file mode 100644
index 00000000..e38e9d4f
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/drive.cpp
@@ -0,0 +1,138 @@
+#include "./main.h"
+#include "./drive.h"
+#include "./resource.h"
+//#include <primosdk.h>
+#include <strsafe.h>
+
+static LPCWSTR pszBusType[] =
+{
+ L"ATAPI",
+ L"SCSI",
+ L"1394",
+ L"USB",
+ L"USB2"
+};
+
+static LPCWSTR pszType[] =
+{
+ L"CD-ROM",
+ L"CD-R",
+ L"CD-RW",
+ L"DVD-ROM",
+ L"DVD-R",
+ L"DVD-RW",
+ L"DVD+R",
+ L"DVD+RW",
+ L"DVD-RAM",
+ L"DDCD-ROM",
+ L"DDCD-R",
+ L"DDCD-RW",
+ L"DL DVD+R",
+ L"DL DVD-R",
+ L"BD-RW",
+ L"BD-R",
+ L"BD-ROM",
+ L"HDDVD-RW",
+ L"HDDVD-R",
+ L"HDDVD-ROM",
+};
+
+static wchar_t buffer[64];
+
+LPCWSTR Drive_GetBusTypeString(DWORD nBusType)
+{
+ int index = -1;
+#if 0
+ switch (nBusType)
+ {
+ case PRIMOSDK_ATAPI: index = 0; break;
+ case PRIMOSDK_SCSI: index = 1; break;
+ case PRIMOSDK_1394: index = 2; break;
+ case PRIMOSDK_USB: index = 3; break;
+ case PRIMOSDK_USB2: index = 4; break;
+ }
+#endif
+ return (-1 != index) ? pszBusType[index] :
+ WASABI_API_LNGSTRINGW_BUF(IDS_UNKNOWN, buffer, sizeof(buffer)/sizeof(wchar_t));
+}
+
+LPCWSTR Drive_GetTypeString(DWORD nType)
+{
+ int index = -1;
+#if 0
+ switch (nType)
+ {
+ case PRIMOSDK_CDROM: index = 0; break;
+ case PRIMOSDK_CDR: index = 1; break;
+ case PRIMOSDK_CDRW: index = 2; break;
+ case PRIMOSDK_DVDROM: index = 3; break;
+ case PRIMOSDK_DVDR: index = 4; break;
+ case PRIMOSDK_DVDRW: index = 5; break;
+ case PRIMOSDK_DVDPR: index = 6; break;
+ case PRIMOSDK_DVDPRW: index = 7; break;
+ case PRIMOSDK_DVDRAM: index = 8; break;
+ case PRIMOSDK_DDCDROM: index = 9; break;
+ case PRIMOSDK_DDCDR: index = 10; break;
+ case PRIMOSDK_DDCDRW: index = 11; break;
+ case PRIMOSDK_DVDPR9: index = 12; break;
+ case PRIMOSDK_DVDR9: index = 13; break;
+ case PRIMOSDK_BDRE: index = 14; break;
+ case PRIMOSDK_BDR: index = 15; break;
+ case PRIMOSDK_BDROM: index = 16; break;
+ case PRIMOSDK_HDDVDRW: index = 17; break;
+ case PRIMOSDK_HDDVDR: index = 18; break;
+ case PRIMOSDK_HDDVDROM: index = 19; break;
+ }
+#endif
+ return (-1 != index) ? pszType[index] :
+ WASABI_API_LNGSTRINGW_BUF(IDS_UNKNOWN, buffer, sizeof(buffer)/sizeof(wchar_t));
+}
+
+BOOL Drive_IsRecorderType(DWORD nType)
+{
+#if 0
+ switch(nType)
+ {
+ case PRIMOSDK_CDR:
+ case PRIMOSDK_CDRW:
+ case PRIMOSDK_DVDR:
+ case PRIMOSDK_DVDRW:
+ case PRIMOSDK_DVDPR:
+ case PRIMOSDK_DVDPRW:
+ case PRIMOSDK_DVDRAM:
+ case PRIMOSDK_DDCDR:
+ case PRIMOSDK_DDCDRW:
+ case PRIMOSDK_DVDPR9:
+ case PRIMOSDK_DVDR9:
+ case PRIMOSDK_BDRE:
+ case PRIMOSDK_BDR:
+ case PRIMOSDK_HDDVDRW:
+ case PRIMOSDK_HDDVDR:
+ return TRUE;
+ }
+#endif
+ return FALSE;
+}
+
+BOOL Drive_IsRecorder(CHAR cLetter)
+{
+#if 0
+ wchar_t info[128] = {0};
+ wchar_t name[] = L"cda://X.cda";
+ DWORD result;
+ BOOL reloaded = FALSE;
+
+ name[6] = cLetter;
+
+ for(;;)
+ {
+ result = getFileInfoW(name, L"cdinfo", info, sizeof(info)/sizeof(wchar_t));
+ if (result || reloaded || !getFileInfoW(name, L"reloadsonic", NULL, 0)) break;
+ reloaded = TRUE;
+ }
+
+ return (result) ? Drive_IsRecorderType(_wtoi(info)) : FALSE;
+#else
+ return FALSE;
+#endif
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/drive.h b/Src/Plugins/Library/ml_disc/drive.h
new file mode 100644
index 00000000..3bfe5b92
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/drive.h
@@ -0,0 +1,16 @@
+#ifndef NULLSOFT_MLDISC_DRIVE_HEADER
+#define NULLSOFT_MLDISC_DRIVE_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <windows.h>
+
+LPCWSTR Drive_GetTypeString(DWORD nType);
+LPCWSTR Drive_GetBusTypeString(DWORD nBusType);
+BOOL Drive_IsRecorderType(DWORD nType);
+BOOL Drive_IsRecorder(CHAR cLetter);
+
+#endif // NULLSOFT_MLDISC_DRIVE_HEADER
+
diff --git a/Src/Plugins/Library/ml_disc/driveListBox.cpp b/Src/Plugins/Library/ml_disc/driveListBox.cpp
new file mode 100644
index 00000000..8f5fbdf3
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/driveListBox.cpp
@@ -0,0 +1,255 @@
+#include "main.h"
+#include ".\DriveListBox.h"
+#include "resource.h"
+#include "..\..\General\gen_ml\graphics.h"
+#include <strsafe.h>
+
+DriveListBox::DriveListBox(int controlId)
+{
+ m_hwnd = NULL;
+ m_parentHwnd= NULL;
+ hInstance = NULL;
+ bmpNormal = NULL;
+ bmpSelected = NULL;
+ driveResId = NULL;
+ bgndResId = NULL;
+
+ this->controlId = controlId;
+
+ SetRect(&rcItem, 0,0,0,68); // hardcoded height
+
+ clrNormalBG = RGB(0,0,0);
+ clrSelected1 = RGB(0,0,0);
+ clrSelected2 = RGB(255,255,255);
+ clrTextSel = RGB(255,255,255);
+ clrTextNorm = RGB(0,255,0);
+}
+
+DriveListBox::~DriveListBox(void)
+{
+ DestroyImages();
+}
+
+void DriveListBox::DestroyImages(void)
+{
+ if (bmpNormal) DeleteObject(bmpNormal);
+ bmpNormal = NULL;
+
+ if (bmpSelected) DeleteObject(bmpSelected);
+ bmpSelected = NULL;
+}
+
+void DriveListBox::SetColors(COLORREF clrNormalBG, COLORREF clrSelected1, COLORREF clrSelected2, COLORREF clrTextSel, COLORREF clrTextNorm)
+{
+ this->clrNormalBG = clrNormalBG;
+ this->clrSelected1 = clrSelected1;
+ this->clrSelected2 = clrSelected2;
+ this->clrTextSel = clrTextSel;
+ this->clrTextNorm = clrTextNorm;
+ ReloadImages();
+}
+
+void DriveListBox::SetImages(HINSTANCE hInstance, int bgndResId, int driveResId)
+{
+ this->hInstance = hInstance;
+ this->bgndResId = bgndResId;
+ this->driveResId = driveResId;
+ ReloadImages();
+}
+
+HWND DriveListBox::GetHWND(void)
+{
+ return m_hwnd;
+}
+
+void DriveListBox::ReloadImages(void)
+{
+ DestroyImages();
+ if (!hInstance) return;
+
+ HBITMAP bmpBck = NULL, bmpDrive = NULL;
+ if (bgndResId)
+ {
+ bmpBck = (HBITMAP)LoadImage(hInstance, MAKEINTRESOURCE(bgndResId), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
+ if (bmpBck) bmpBck = PatchBitmapColors24(bmpBck, clrSelected1, clrSelected2, Filter1);
+ }
+ if (driveResId)
+ {
+ bmpDrive = (HBITMAP)LoadImage(hInstance, MAKEINTRESOURCE(driveResId), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
+ }
+
+ CreateBitmaps(bmpBck, bmpDrive);
+ if (bmpBck) DeleteObject(bmpBck);
+ if (bmpDrive) DeleteObject(bmpDrive);
+}
+
+void DriveListBox::CreateBitmaps(HBITMAP bmpBck, HBITMAP bmpDrive)
+{
+ if (rcItem.right == 0 || rcItem.bottom == 0) return;
+
+ HBITMAP bmpDriveMask = NULL;
+ if (bmpDrive) bmpDriveMask = CreateBitmapMask(bmpDrive, RGB(255, 0, 255));
+
+ HDC hdc = GetDC(m_hwnd);
+ HBITMAP bmp;
+
+ for (int i = 0; i < 2; i++)
+ {
+ bmp = CreateCompatibleBitmap(hdc, rcItem.right, rcItem.bottom);
+ HDC memDstDC = CreateCompatibleDC (hdc);
+ HDC memSrcDC = CreateCompatibleDC (hdc);
+ HBITMAP obmp1 = (HBITMAP)SelectObject(memDstDC, bmp);
+ HBITMAP obmp2 = (HBITMAP)SelectObject(memSrcDC, bmpBck);
+
+ if (i == 0 )
+ {
+ for (int i = 0; i < rcItem.right; i++)
+ {
+ BitBlt(memDstDC, i, 0, 2, rcItem.bottom, memSrcDC, 0, 0, SRCCOPY);
+ }
+ }
+ else
+ {
+ HBRUSH hb = CreateSolidBrush(clrNormalBG);
+ FillRect(memDstDC, &rcItem, hb);
+ DeleteObject(hb);
+ }
+
+ BITMAP bm;
+ GetObject(bmpDrive, sizeof(BITMAP), &bm);
+ RECT r1 = {
+ max(2, (rcItem.right - bm.bmWidth) / 2),
+ max(2, (rcItem.bottom - 16 - bm.bmHeight) / 2),
+ min(rcItem.right - 4, bm.bmWidth),
+ min(rcItem.bottom -18, bm.bmHeight)
+ };
+ SelectObject(memSrcDC, bmpDriveMask);
+ BitBlt(memDstDC, r1.left, r1.top, r1.right, r1.bottom, memSrcDC, 0,0, SRCAND);
+
+ SelectObject(memSrcDC, bmpDrive);
+ BitBlt(memDstDC, r1.left, r1.top, r1.right, r1.bottom, memSrcDC, 0,0, SRCPAINT);
+
+ SelectObject(memDstDC, obmp1);
+ SelectObject(memSrcDC, obmp2);
+
+ DeleteDC(memDstDC);
+ DeleteDC(memSrcDC);
+ if (i == 0) bmpSelected = bmp;
+ else bmpNormal = bmp;
+ }
+
+ ReleaseDC(m_hwnd, hdc);
+ DeleteObject(bmpDriveMask);
+}
+
+void DriveListBox::Init(HWND hwnd)
+{
+ m_hwnd = hwnd;
+ m_parentHwnd = GetParent(hwnd);
+}
+
+int DriveListBox::HandleMsgProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_DRAWITEM:
+ if (wParam == (WPARAM)controlId) DrawItem((DRAWITEMSTRUCT *)lParam);
+ break;
+ case WM_MEASUREITEM:
+ if (wParam == (WPARAM)controlId && MeasureItem((LPMEASUREITEMSTRUCT)lParam)) return 1;
+ break;
+
+ case WM_CTLCOLORLISTBOX:
+ SetBkColor((HDC)wParam, clrNormalBG);
+ return NULL;
+ }
+ return FALSE;
+}
+
+void DriveListBox::DrawItem(LPDRAWITEMSTRUCT di)
+{
+ if(di->CtlType == ODT_LISTBOX)
+ {
+ if (di->itemID == -1) return;
+
+ RECT r;
+ r=di->rcItem;
+
+ if (!bmpSelected || !bmpNormal || ((r.right - r.left) != rcItem.right))
+ {
+ SetRect(&rcItem, 0,0,r.right - r.left, rcItem.bottom);
+ ReloadImages();
+ }
+
+ HBITMAP bmp;
+ int color;
+ if (di->itemState & ODS_SELECTED)
+ {
+ bmp = bmpSelected;
+ color = clrTextSel;
+ }
+ else
+ {
+ bmp = bmpNormal;
+ color = clrTextNorm;
+ }
+
+ RECT rc;
+ GetClientRect(di->hwndItem, &rc);
+
+ HRGN rgnW = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
+ // HRGN rgn = CreateRoundRectRgn(r.left +2, r.top + 2, r.right - 2, r.bottom -2, 10, 8);
+ HRGN rgn = CreateRectRgn(r.left +2, r.top + 2, r.right - 2, r.bottom -2);
+
+ CombineRgn(rgn, rgn, rgnW, RGN_AND);
+ SelectClipRgn(di->hDC, rgn);
+ DeleteObject(rgn);
+ DeleteObject(rgnW);
+
+ if (bmp)
+ {
+ HDC hdcbmp = CreateCompatibleDC(di->hDC);
+ HBITMAP obmp = (HBITMAP)SelectObject(hdcbmp,bmp);
+ StretchBlt(di->hDC,
+ r.left,
+ r.top,
+ rcItem.right,
+ rcItem.bottom,
+ hdcbmp,
+ 0,
+ 0,
+ rcItem.right,
+ rcItem.bottom,
+ SRCCOPY);
+ SelectObject(hdcbmp,obmp);
+ DeleteDC(hdcbmp);
+ }
+
+ InflateRect(&r, -2, -2);
+
+ if ( (di->itemState & ODS_SELECTED) && GetFocus() == di->hwndItem)
+ {
+ DrawFocusRect(di->hDC, &r);
+ }
+
+ SetBkMode(di->hDC, TRANSPARENT);
+ SetTextColor(di->hDC, color);
+
+ RECT textRect = {r.left + 2, r.bottom - 20, r.right - 2, r.bottom - 6};
+
+ wchar_t str[256] = {0};
+ INT nType;
+ nType = 0xFFFF & ((DWORD)di->itemData >> 16);
+ StringCchPrintfW(str, 256, WASABI_API_LNGSTRINGW(IDS_X_DRIVE_X),
+ (nType) ? Drive_GetTypeString(nType) : L"",
+ (CHAR)(0xFF & di->itemData));
+ DrawTextW(di->hDC, str,-1,&textRect,DT_BOTTOM|DT_SINGLELINE|DT_CENTER);
+ }
+ return;
+}
+
+int DriveListBox::MeasureItem(LPMEASUREITEMSTRUCT mi)
+{
+ mi->itemHeight = rcItem.bottom;
+ return 1;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/driveListBox.h b/Src/Plugins/Library/ml_disc/driveListBox.h
new file mode 100644
index 00000000..95d0c4e1
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/driveListBox.h
@@ -0,0 +1,50 @@
+#ifndef NULLSOFT_DRIVE_COMBOBOX_HEADER
+#define NULLSOFT_DRIVE_COMBOBOX_HEADER
+
+#include "windows.h"
+
+class DriveListBox
+{
+public:
+ DriveListBox(int controlId);
+ ~DriveListBox(void);
+
+public:
+ void SetColors(COLORREF clrNormalBG, COLORREF clrSelected1, COLORREF clrSelected2, COLORREF clrTextSel, COLORREF clrTextNorm);
+ void SetImages(HINSTANCE hInstance, int bgndResId, int driveResId);
+ void Init(HWND hwnd);
+ int HandleMsgProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
+ void ReloadImages(void);
+ HWND GetHWND(void);
+
+private:
+ void DestroyImages(void);
+ void CreateBitmaps(HBITMAP bmpBck, HBITMAP bmpDrive);
+
+ void DrawItem(LPDRAWITEMSTRUCT di);
+ int MeasureItem(LPMEASUREITEMSTRUCT mi);
+
+private:
+
+ HWND m_hwnd, m_parentHwnd;
+
+ HINSTANCE hInstance;
+
+ HBITMAP bmpNormal;
+ HBITMAP bmpSelected;
+
+ int driveResId;
+ int bgndResId;
+
+ RECT rcItem;
+
+ int controlId;
+
+ COLORREF clrNormalBG;
+ COLORREF clrSelected1;
+ COLORREF clrSelected2;
+ COLORREF clrTextSel;
+ COLORREF clrTextNorm;
+};
+
+#endif // NULLSOFT_DRIVE_COMBOBOX_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/drivemngr.cpp b/Src/Plugins/Library/ml_disc/drivemngr.cpp
new file mode 100644
index 00000000..b2d87fcc
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/drivemngr.cpp
@@ -0,0 +1,1910 @@
+#include "main.h"
+#include "./drivemngr.h"
+#include "./primosdk_helper.h"
+#include "./resource.h"
+#include "../nu/trace.h"
+#include "./dbt.h"
+#include "./spti.h"
+#include <setupapi.h>
+
+#include <imapi.h>
+#include <strsafe.h>
+
+#define LISTENER_CLASSNAME L"MLDISCLISTENER"
+#define LISTENER_WINDOWNAME L""
+
+#define POLLMEDIUMCHANGE_INTERVAL 2000
+#define POLLMEDIUMVALIDATE_INTERVAL 6000
+
+#define DMS_SUSPENDED 0x0000
+#define DMS_ACTIVE 0x0001
+
+#define WM_EX_QUIT (WM_APP + 1)
+
+typedef struct _MEDIUMINFO_I
+{
+ UINT msLastPolled; // last time medium info was polled
+ UINT serialNumber; // medium serialnumber
+} MEDIUMINFO_I;
+
+typedef struct _DRIVEINFO_I
+{
+ char cLetter; // drive letter
+ INT deviceNumber; // system assigned device number (unique till next reboot)
+ BOOL bMediumInserted; // if TRUE mediumInfo contains valid data
+ CHAR cMode; // drive mode
+ DWORD dwType; // drive type
+ LPWSTR pszDevName; // device name
+ HANDLE hThread; // device info thread
+ DWORD dwThreadId;
+ MEDIUMINFO_I mediumInfo;
+} DRIVEINFO_I;
+
+typedef struct _DRIVEMNGR
+{
+ HWND hwndListener;
+ DMNPROC callback;
+ UINT fState;
+ CRITICAL_SECTION csLock;
+
+ DRIVEINFO_I *pDrives;
+ INT nCount;
+ INT nAlloc;
+
+ HANDLE hPollingThread;
+ DWORD dwPollingThread;
+} DRIVEMNGR;
+
+typedef struct _DEVICEINFO
+{
+ CHAR cLetter;
+ LPWSTR pszDevName;
+ WCHAR szTargetPath[128];
+ WCHAR szVolumeName[64];
+ DWORD dwType;
+ INT deviceNumber;
+ INT opCode;
+} DEVICEINFO;
+
+static DRIVEMNGR *pMngr = NULL;
+
+
+static void CALLBACK APC_CheckDrives(ULONG_PTR param);
+static void CALLBACK APC_IsMediumChanged(ULONG_PTR param);
+
+static void CALLBACK APC_GetUnitInfo(ULONG_PTR param);
+static void CALLBACK APC_GetUnitInfo2(ULONG_PTR param);
+static void CALLBACK APC_GetDiscInfoEx(ULONG_PTR param);
+static void CALLBACK APC_GetDiscInfo2(ULONG_PTR param);
+static void CALLBACK APC_GetTitle(ULONG_PTR param);
+static void CALLBACK APC_DriveScan(ULONG_PTR param);
+static void CALLBACK APC_GetMCIInfo(ULONG_PTR param);
+static void CALLBACK APC_GetIMAPIInfo(ULONG_PTR param);
+static void CALLBACK APC_Eject(ULONG_PTR param);
+
+static DWORD CALLBACK InfoThread(LPVOID param);
+
+static DWORD CALLBACK PollingThread(LPVOID param);
+
+static void CALLBACK PollMediumInfo(ULONG_PTR param);
+
+static LRESULT WINAPI ListenerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+static CHAR CheckLetter(CHAR cLetter)
+{
+ if (cLetter < 'A' || cLetter > 'Z')
+ {
+ if (cLetter >= 'a' && cLetter <= 'z') return (cLetter - 0x20);
+ return 0;
+ }
+ return cLetter;
+}
+
+static LPCWSTR GetDeviceName(CHAR cLetter)
+{
+ LPCWSTR pszDevName;
+ if (!pMngr) return NULL;
+ pszDevName = NULL;
+
+ EnterCriticalSection(&pMngr->csLock);
+ for (int i = 0; i < pMngr->nCount; i++)
+ {
+ if (pMngr->pDrives[i].cLetter == cLetter)
+ {
+ pszDevName = pMngr->pDrives[i].pszDevName;
+ break;
+ }
+ }
+ LeaveCriticalSection(&pMngr->csLock);
+
+ return pszDevName;
+}
+
+static BOOL IsPollingRequired(void)
+{
+ HKEY hKey;
+ LONG result;
+ BOOL bAutoRunEnabled;
+
+ bAutoRunEnabled = FALSE;
+
+ result = RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\Cdrom"), &hKey);
+ if (ERROR_SUCCESS == result)
+ {
+ DWORD value;
+ DWORD size;
+ size = sizeof(DWORD);
+ result = RegQueryValueEx(hKey, TEXT("AutoRun"), NULL, NULL, (LPBYTE)&value, &size);
+ if (ERROR_SUCCESS == result) bAutoRunEnabled = (0 != value);
+
+ RegCloseKey(hKey);
+ }
+ return !bAutoRunEnabled;
+
+}
+static CHAR Drive_LetterFromMask(ULONG unitmask)
+{
+ char i;
+ for (i = 0; i < 26; ++i)
+ {
+ if (unitmask & 0x1) break;
+ unitmask = unitmask >> 1;
+ }
+ return (i + 'A');
+}
+
+static BOOL Drive_Add(DEVICEINFO *pDevInfo)
+{
+ DRIVEINFO_I *pDrive;
+ if (!pMngr) return FALSE;
+
+ EnterCriticalSection(&pMngr->csLock);
+
+ INT index, opCode;
+
+ opCode = 0;
+ for (index = 0; index < pMngr->nCount && pMngr->pDrives[index].cLetter != pDevInfo->cLetter; index++);
+ if (index != pMngr->nCount)
+ {
+ pDrive = &pMngr->pDrives[index];
+ if (pDrive->deviceNumber != pDevInfo->deviceNumber || pDrive->dwType != pDevInfo->dwType) opCode = 1;
+ }
+ else
+ {
+ if (pMngr->nCount == pMngr->nAlloc)
+ {
+ LPVOID data;
+ data = realloc(pMngr->pDrives, sizeof(DRIVEINFO_I)*(pMngr->nCount + 2));
+ if (!data)
+ {
+ LeaveCriticalSection(&pMngr->csLock);
+ return FALSE;
+ }
+ pMngr->pDrives = (DRIVEINFO_I*)data;
+ pMngr->nAlloc += 2;
+ }
+ pDrive = &pMngr->pDrives[pMngr->nCount];
+ pMngr->nCount++;
+
+ ZeroMemory(pDrive, sizeof(DRIVEINFO_I));
+ pDrive->cLetter = pDevInfo->cLetter;
+ opCode = 2;
+ }
+
+ if (opCode)
+ {
+ pDrive->deviceNumber = pDevInfo->deviceNumber;
+ pDrive->dwType = pDevInfo->dwType;
+ if (pDrive->pszDevName) free(pDrive->pszDevName);
+ pDrive->pszDevName = _wcsdup(pDevInfo->pszDevName);
+ }
+
+ LeaveCriticalSection(&pMngr->csLock);
+
+ if (opCode && pMngr->callback) pMngr->callback((2 == opCode) ? DMW_DRIVEADDED : DMW_DRIVECHANGED, pDevInfo->cLetter);
+
+ return TRUE;
+}
+
+static BOOL Drive_Remove(CHAR cLetter)
+{
+ INT index;
+ BOOL bReportChanges;
+ if (!pMngr) return FALSE;
+
+ EnterCriticalSection(&pMngr->csLock);
+
+ bReportChanges = FALSE;
+ index = pMngr->nCount;
+ while (index-- && pMngr->pDrives[index].cLetter != cLetter);
+
+ if (-1 != index)
+ {
+ if (pMngr->pDrives[index].pszDevName) free(pMngr->pDrives[index].pszDevName);
+ if (index != pMngr->nCount - 1) MoveMemory(&pMngr->pDrives[index], &pMngr->pDrives[index + 1], sizeof(DRIVEINFO_I)*(pMngr->nCount - index));
+ pMngr->nCount--;
+ bReportChanges = TRUE;
+ }
+ LeaveCriticalSection(&pMngr->csLock);
+
+ if (bReportChanges && pMngr->callback) pMngr->callback(DMW_DRIVEREMOVED, cLetter);
+ return TRUE;
+}
+
+static HRESULT QueueInfoAPC(CHAR cLetter, PAPCFUNC pfnAPC, ULONG_PTR param)
+{
+ DWORD *pdwThreadId(NULL);
+ HRESULT hr(S_FALSE);
+ HANDLE *phThread(NULL);
+ static HANDLE hDrivesInfoThread = NULL;
+
+ if (NULL == pMngr) return E_FAIL;
+
+ EnterCriticalSection(&pMngr->csLock);
+ if (cLetter)
+ {
+ INT index = pMngr->nCount;
+ while (index-- && pMngr->pDrives[index].cLetter != cLetter);
+ if (-1 != index)
+ {
+ phThread = &pMngr->pDrives[index].hThread;
+ pdwThreadId = &pMngr->pDrives[index].dwThreadId;
+ }
+ }
+ else
+ {
+ phThread = &hDrivesInfoThread;
+ }
+
+ if (phThread)
+ {
+ if (!*phThread)
+ {
+ DWORD tid;
+ *phThread = CreateThread(NULL, 0, InfoThread, NULL, 0, &tid);
+ if (pdwThreadId) *pdwThreadId = tid;
+ Sleep(100);
+ }
+ if (*phThread)
+ {
+ if (0 == QueueUserAPC(pfnAPC, *phThread, param))
+ {
+ TRACE_LINE(TEXT("queue user apc failed"));
+ }
+ else hr = S_OK;
+ }
+ }
+
+ LeaveCriticalSection(&pMngr->csLock);
+
+ return hr;
+}
+
+static BOOL Medium_Add(CHAR cLetter, DWORD serial)
+{
+ INT index;
+ if (!pMngr) return FALSE;
+
+ EnterCriticalSection(&pMngr->csLock);
+
+ index = pMngr->nCount;
+ while (index-- && pMngr->pDrives[index].cLetter != cLetter);
+ if (-1 != index)
+ {
+ pMngr->pDrives[index].bMediumInserted = TRUE;
+ pMngr->pDrives[index].mediumInfo.msLastPolled = 0;
+ pMngr->pDrives[index].mediumInfo.serialNumber = serial;
+ }
+
+ LeaveCriticalSection(&pMngr->csLock);
+
+ if (-1 != index)
+ {
+ if (-1 == serial) QueueInfoAPC(cLetter, APC_IsMediumChanged, (ULONG_PTR)cLetter);
+ if (pMngr->callback) pMngr->callback(DMW_MEDIUMARRIVED, cLetter);
+ }
+
+ return TRUE;
+}
+
+static BOOL Medium_Remove(CHAR cLetter)
+{
+ INT index;
+ if (!pMngr) return FALSE;
+
+ EnterCriticalSection(&pMngr->csLock);
+
+ index = pMngr->nCount;
+ while (index-- && pMngr->pDrives[index].cLetter != cLetter);
+ if (-1 != index) pMngr->pDrives[index].bMediumInserted = FALSE;
+
+ LeaveCriticalSection(&pMngr->csLock);
+
+ if (-1 != index && pMngr->callback) pMngr->callback(DMW_MEDIUMREMOVED, cLetter);
+
+ return TRUE;
+}
+
+BOOL DriveManager_Initialize(DMNPROC DMNProc, BOOL bSuspended)
+{
+ WNDCLASSW wc = {0};
+ HINSTANCE hInstance;
+
+ if (pMngr || !DMNProc) return FALSE;
+
+ pMngr = (DRIVEMNGR*)calloc(1, sizeof(DRIVEMNGR));
+ if (!pMngr) return FALSE;
+
+ hInstance = GetModuleHandle(NULL);
+
+ if (!GetClassInfoW(hInstance, LISTENER_CLASSNAME, &wc))
+ {
+ wc.hInstance = hInstance;
+ wc.lpfnWndProc = ListenerWndProc;
+ wc.lpszClassName = LISTENER_CLASSNAME;
+ if (!RegisterClassW(&wc))
+ {
+ DriveManager_Uninitialize(0);
+ return FALSE;
+ }
+ }
+ pMngr->hwndListener = CreateWindowW(LISTENER_CLASSNAME, LISTENER_WINDOWNAME, WS_DISABLED, 0,0,0,0, HWND_DESKTOP, NULL, hInstance, 0L);
+ if (!pMngr->hwndListener)
+ {
+ DriveManager_Uninitialize(0);
+ return FALSE;
+ }
+ InitializeCriticalSection(&pMngr->csLock);
+ pMngr->callback = DMNProc;
+
+ return TRUE;
+}
+
+BOOL DriveManager_Uninitialize(INT msExitWaitTime)
+{
+ if (pMngr)
+ {
+ WNDCLASSW wc;
+ HINSTANCE hInstance;
+
+ if (pMngr->hwndListener) DestroyWindow(pMngr->hwndListener);
+
+ hInstance = GetModuleHandle(NULL);
+ if (GetClassInfoW(hInstance, LISTENER_CLASSNAME, &wc)) UnregisterClassW(LISTENER_CLASSNAME, hInstance);
+
+ EnterCriticalSection(&pMngr->csLock);
+
+ for (int index =0; index < pMngr->nCount; index++)
+ {
+ if (pMngr->pDrives[index].hThread)
+ {
+ PostThreadMessage(pMngr->pDrives[index].dwThreadId, WM_EX_QUIT, 1, 0);
+ INT result = WaitForSingleObject(pMngr->pDrives[index].hThread, msExitWaitTime);
+ if (WAIT_TIMEOUT == result) TerminateThread(pMngr->pDrives[index].hThread, 1);
+ CloseHandle(pMngr->pDrives[index].hThread);
+ pMngr->pDrives[index].hThread = NULL;
+ }
+ }
+ LeaveCriticalSection(&pMngr->csLock);
+
+ if (pMngr->hPollingThread)
+ {
+ PostThreadMessage(pMngr->dwPollingThread, WM_EX_QUIT, 1, 0);
+ INT result = WaitForSingleObject(pMngr->hPollingThread, msExitWaitTime);
+ if (WAIT_TIMEOUT == result) TerminateThread(pMngr->hPollingThread, 1);
+ CloseHandle(pMngr->hPollingThread);
+ pMngr->hPollingThread = NULL;
+ }
+
+ EnterCriticalSection(&pMngr->csLock);
+
+ if (pMngr->pDrives)
+ {
+ free(pMngr->pDrives);
+ }
+
+ DRIVEMNGR *managerInstance = pMngr;
+ pMngr = NULL;
+
+ LeaveCriticalSection(&managerInstance->csLock);
+ DeleteCriticalSection(&managerInstance->csLock);
+
+ free(managerInstance);
+
+ PrimoSDKHelper_Uninitialize();
+
+ }
+ return TRUE;
+}
+
+BOOL DriveManager_Suspend(void)
+{
+ if (!pMngr) return FALSE;
+
+ pMngr->fState = DMS_SUSPENDED;
+ if (pMngr->hPollingThread)
+ {
+ PostThreadMessage(pMngr->dwPollingThread, WM_EX_QUIT, 1, 0);
+ pMngr->hPollingThread = NULL;
+ }
+ return TRUE;
+}
+
+BOOL DriveManager_Update(BOOL bAsync)
+{
+ if (bAsync) return (QueueInfoAPC(0, APC_DriveScan, 0) && QueueInfoAPC(0, PollMediumInfo, 0));
+ else
+ {
+ APC_DriveScan(0);
+ QueueInfoAPC(0, PollMediumInfo, 0);
+ }
+ return TRUE;
+}
+
+BOOL DriveManager_Resume(BOOL bUpdate)
+{
+ if (!pMngr) return FALSE;
+ pMngr->fState = DMS_ACTIVE;
+
+ EnterCriticalSection(&pMngr->csLock);
+ for (int index =0; index < pMngr->nCount; index++) pMngr->pDrives[index].mediumInfo.msLastPolled = 0;
+ LeaveCriticalSection(&pMngr->csLock);
+
+ APC_DriveScan(0);
+ QueueInfoAPC(0, PollMediumInfo, 0);
+
+ if (NULL == pMngr->hPollingThread && IsPollingRequired())
+ {
+ pMngr->hPollingThread = CreateThread(NULL, 0, PollingThread, NULL, 0, &pMngr->dwPollingThread);
+ }
+
+ return TRUE;
+}
+
+BOOL DriveManager_SetDriveMode(CHAR cLetter, CHAR cMode)
+{
+ BOOL report;
+ INT index;
+
+ index = -1;
+ report = FALSE;
+ cLetter = CheckLetter(cLetter);
+
+ if (pMngr && cLetter)
+ {
+ EnterCriticalSection(&pMngr->csLock);
+ for (index =0; index < pMngr->nCount; index++)
+ {
+ if (pMngr->pDrives[index].cLetter == cLetter)
+ {
+ if (pMngr->pDrives[index].cMode != cMode)
+ {
+ pMngr->pDrives[index].cMode = cMode;
+ report = TRUE;
+ }
+ break;
+ }
+ }
+ if (index == pMngr->nCount) index = -1;
+ LeaveCriticalSection(&pMngr->csLock);
+ if (report && pMngr->callback) pMngr->callback(DMW_MODECHANGED, MAKEWORD(cLetter, cMode));
+ }
+
+ return (-1 != index);
+}
+
+CHAR DriveManager_GetDriveMode(CHAR cLetter)
+{
+ CHAR result;
+
+ result = DM_MODE_ERROR;
+ cLetter = CheckLetter(cLetter);
+
+ if (pMngr && cLetter)
+ {
+ INT index;
+ EnterCriticalSection(&pMngr->csLock);
+ for (index =0; index < pMngr->nCount; index++)
+ {
+ if (pMngr->pDrives[index].cLetter == cLetter)
+ {
+ result = pMngr->pDrives[index].cMode;
+ break;
+ }
+ }
+ LeaveCriticalSection(&pMngr->csLock);
+ }
+ return result;
+}
+
+DWORD DriveManager_GetDriveType(CHAR cLetter)
+{
+ DWORD type;
+
+ type = DRIVE_TYPE_UNKNOWN | DRIVE_CAP_UNKNOWN;
+ cLetter = CheckLetter(cLetter);
+
+ if (pMngr && cLetter)
+ {
+ INT index;
+ EnterCriticalSection(&pMngr->csLock);
+ for (index =0; index < pMngr->nCount; index++)
+ {
+ if (pMngr->pDrives[index].cLetter == cLetter)
+ {
+ type = pMngr->pDrives[index].dwType;
+ break;
+ }
+ }
+ LeaveCriticalSection(&pMngr->csLock);
+ }
+ return type;
+}
+
+BOOL DriveManager_IsMediumInserted(CHAR cLetter)
+{
+ BOOL result;
+
+ result = FALSE;
+ cLetter = CheckLetter(cLetter);
+
+ if (pMngr && cLetter)
+ {
+ INT index;
+ EnterCriticalSection(&pMngr->csLock);
+ for (index =0; index < pMngr->nCount; index++)
+ {
+ if (pMngr->pDrives[index].cLetter == cLetter)
+ {
+ result = pMngr->pDrives[index].bMediumInserted;
+ break;
+ }
+ }
+ LeaveCriticalSection(&pMngr->csLock);
+ }
+ return result;
+}
+
+INT DriveManager_GetDriveList(CHAR *pLetters, INT cchSize)
+{
+ INT r = 0;
+ if (!pLetters || !pMngr) return -1;
+ EnterCriticalSection(&pMngr->csLock);
+ for (int index =0; index < pMngr->nCount; index++)
+ {
+ *pLetters = pMngr->pDrives[index].cLetter;
+ pLetters++;
+ cchSize--;
+ r++;
+ if (0 == cchSize) break;
+ }
+ LeaveCriticalSection(&pMngr->csLock);
+ return r;
+}
+
+static BOOL QueueInfoJob(PAPCFUNC pfnAPC, DM_NOTIFY_PARAM *pHeader)
+{
+ BOOL result(TRUE);
+ if (!pMngr || !pHeader) result = FALSE;
+
+ if (result)
+ {
+ HANDLE hProc = GetCurrentProcess();
+ pHeader->hReserved = 0;
+ result = (BOOL)DuplicateHandle(hProc, GetCurrentThread(), hProc, &pHeader->hReserved,
+ 0, FALSE, DUPLICATE_SAME_ACCESS);
+ if (!result) pHeader->hReserved = 0;
+ }
+
+ if (result)
+ {
+ CHAR cLetter = CheckLetter(pHeader->cLetter);
+ result = (cLetter && S_OK == QueueInfoAPC(cLetter, pfnAPC, (ULONG_PTR)pHeader));
+ }
+
+ if(!result && pHeader && pHeader->fnFree)
+ {
+ if (pHeader->hReserved) CloseHandle(pHeader->hReserved);
+ pHeader->fnFree(pHeader);
+ }
+ return result;
+}
+
+BOOL DriveManager_GetUnitInfo(DM_UNITINFO_PARAM *puip)
+{
+ return QueueInfoJob(APC_GetUnitInfo, (DM_NOTIFY_PARAM*)puip);
+}
+
+BOOL DriveManager_GetUnitInfo2(DM_UNITINFO2_PARAM *puip)
+{
+ return QueueInfoJob(APC_GetUnitInfo2, (DM_NOTIFY_PARAM*)puip);
+}
+
+BOOL DriveManager_GetDiscInfoEx(DM_DISCINFOEX_PARAM *pdip)
+{
+ return QueueInfoJob(APC_GetDiscInfoEx, (DM_NOTIFY_PARAM*)pdip);
+}
+BOOL DriveManager_GetDiscInfo2(DM_DISCINFO2_PARAM *pdip)
+{
+ return QueueInfoJob(APC_GetDiscInfo2, (DM_NOTIFY_PARAM*)pdip);
+}
+
+BOOL DriveManager_QueryTitle(DM_TITLE_PARAM *pdtp)
+{
+ return QueueInfoJob(APC_GetTitle, (DM_NOTIFY_PARAM*)pdtp);
+}
+
+BOOL DriveManager_GetMCIInfo(DM_MCI_PARAM *pmcip)
+{
+ return QueueInfoJob(APC_GetMCIInfo, (DM_NOTIFY_PARAM*)pmcip);
+}
+
+BOOL DriveManager_GetIMAPIInfo(DM_IMAPI_PARAM *pIMAPI)
+{
+ return QueueInfoJob(APC_GetIMAPIInfo, (DM_NOTIFY_PARAM*)pIMAPI);
+}
+BOOL DriveManager_Eject(CHAR cLetter, INT nCmd)
+{
+ if (!pMngr) return FALSE;
+ CHAR cLetter1 = CheckLetter(cLetter);
+
+ return (cLetter1 && QueueInfoAPC(cLetter1, APC_Eject, (ULONG_PTR)MAKELONG(cLetter, nCmd)));
+}
+
+BOOL DriveManager_IsUnitReady(CHAR cLetter, BOOL *pbReady)
+{
+ BYTE sc, asc, ascq;
+
+ BOOL bSuccess;
+ HANDLE hDevice;
+
+ *pbReady = FALSE;
+ hDevice = CreateFileW(GetDeviceName(cLetter), GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
+
+ if (INVALID_HANDLE_VALUE == hDevice) return FALSE;
+
+ bSuccess = SPTI_TestUnitReady(hDevice, &sc, &asc, &ascq, 3);
+ if (!bSuccess)
+ {
+ if (ERROR_SEM_TIMEOUT == GetLastError()) bSuccess = TRUE;
+ }
+ else if (0x00 == sc || (0x02 == sc && 0x3A == asc)) *pbReady = TRUE;
+
+ CloseHandle(hDevice);
+
+ return bSuccess;
+}
+
+static BOOL GetVolumeNameForVolumeMountPoint_DL(LPCWSTR lpszVolumeMountPoint, LPWSTR lpszVolumeName, DWORD cchBufferLength)
+{
+ static BOOL (WINAPI *func)(LPCWSTR, LPWSTR, DWORD) = NULL;
+ if (!func)
+ {
+ UINT prevErrorMode;
+ HMODULE hModule;
+ prevErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
+ hModule = LoadLibraryW(L"Kernel32.dll");
+ SetErrorMode(prevErrorMode);
+ if (hModule)
+ {
+ func = (BOOL (WINAPI*)(LPCWSTR, LPWSTR, DWORD))GetProcAddress(hModule, "GetVolumeNameForVolumeMountPointW");
+ FreeLibrary(hModule);
+ }
+ }
+ return (func) ? func(lpszVolumeMountPoint, lpszVolumeName, cchBufferLength) : FALSE;
+}
+
+static DWORD GetDeviceNames(DEVICEINFO *pDevInfo, INT count)
+{
+ HANDLE hDevInfo;
+ SP_DEVICE_INTERFACE_DATA spiData;
+ SP_DEVICE_INTERFACE_DETAIL_DATA_W *pspiDetailData;
+ DWORD dwErrorCode, dwReqSize, dwDetailSize;
+ wchar_t volume[128], szDosName[] = L"X:\\", szDosName1[] = L"X:\\";
+
+ if (!pDevInfo || !count) return ERROR_INVALID_DATA;
+
+ for (int i = 0; i < count; i++)
+ {
+ szDosName[0] = pDevInfo[i].cLetter;
+ GetVolumeNameForVolumeMountPoint_DL(szDosName, pDevInfo[i].szVolumeName, sizeof(pDevInfo[i].szVolumeName)/sizeof(wchar_t));
+ szDosName1[0] = pDevInfo[i].cLetter;
+ QueryDosDeviceW(szDosName1, pDevInfo[i].szTargetPath, sizeof(pDevInfo[i].szTargetPath)/sizeof(wchar_t));
+ }
+
+ hDevInfo = SetupDiGetClassDevs((LPGUID)&CdRomClassGuid, NULL, NULL, (DIGCF_PRESENT | DIGCF_INTERFACEDEVICE));
+ if (INVALID_HANDLE_VALUE == hDevInfo) return GetLastError();
+
+ dwDetailSize = 0;
+ pspiDetailData = NULL;
+ spiData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+ dwErrorCode = 0;
+
+ for (int index = 0; !dwErrorCode; index++)
+ {
+ BOOL bResult = SetupDiEnumDeviceInterfaces(hDevInfo, 0, (LPGUID)&CdRomClassGuid, index, &spiData);
+ if (!bResult)
+ {
+ dwErrorCode = GetLastError();
+ break;
+ }
+
+ bResult = SetupDiGetDeviceInterfaceDetailW(hDevInfo, &spiData, NULL, 0, &dwReqSize, NULL);
+ if (!bResult)
+ {
+ dwErrorCode = GetLastError();
+ if (ERROR_INSUFFICIENT_BUFFER != dwErrorCode) break;
+ dwErrorCode = 0;
+ }
+ dwReqSize += 2*sizeof(wchar_t);
+ if (dwReqSize > dwDetailSize)
+ {
+ LPVOID data;
+ data = realloc(pspiDetailData, dwReqSize);
+ if (!data) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; break; }
+ pspiDetailData = (SP_DEVICE_INTERFACE_DETAIL_DATA*)data;
+ dwDetailSize = dwReqSize;
+ }
+
+ pspiDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+ bResult = SetupDiGetDeviceInterfaceDetailW(hDevInfo, &spiData, pspiDetailData, dwDetailSize, NULL, NULL);
+ if (!bResult)
+ {
+ dwErrorCode = GetLastError();
+ break;
+ }
+
+ INT cchName;
+ cchName = lstrlenW(pspiDetailData->DevicePath);
+ pspiDetailData->DevicePath[cchName] = L'\\';
+ pspiDetailData->DevicePath[cchName + 1] = 0x00;
+
+ if(GetVolumeNameForVolumeMountPoint_DL(pspiDetailData->DevicePath, volume, sizeof(volume)/sizeof(wchar_t)))
+ {
+ for (int i = 0; i < count; i++)
+ {
+ if (!pDevInfo[i].pszDevName && 0 == lstrcmpW(volume, pDevInfo[i].szVolumeName))
+ {
+ pDevInfo[i].pszDevName = (LPWSTR)calloc((cchName + 1), sizeof(wchar_t));
+ if (pDevInfo[i].pszDevName) StringCchCopyNW(pDevInfo[i].pszDevName, cchName + 1, pspiDetailData->DevicePath, cchName);
+ break;
+ }
+ }
+ }
+ }
+ if (pspiDetailData) free(pspiDetailData);
+ SetupDiDestroyDeviceInfoList(hDevInfo);
+
+ for (int i = 0; i < count; i++)
+ {
+ if (!pDevInfo[i].pszDevName)
+ {
+ wchar_t szDevName[] = L"\\\\.\\x:";
+ szDevName[4] = pDevInfo[i].cLetter;
+ pDevInfo[i].pszDevName = (LPWSTR)calloc(sizeof(szDevName) + 2, sizeof(wchar_t));
+ if (pDevInfo[i].pszDevName) StringCbCopyW(pDevInfo[i].pszDevName, sizeof(szDevName) + 2, szDevName);
+ }
+ }
+
+ return dwErrorCode;
+}
+
+static void GetDeviceCaps(DEVICEINFO *pDevInfo, INT count)
+{
+ for( int i = 0; i < count; i++)
+ {
+ pDevInfo[i].dwType = ((pDevInfo[i].dwType & 0x0000FFFF) | DRIVE_CAP_UNKNOWN);
+ }
+
+ // TODO come back to this later on, but for the moment not seeing any noticeable issues
+ // with disabling this and instead and instead it helps prevent random trk****.tmp
+ // files being generated and also seems to fix the crash on start people have here
+ /*IDiscMaster *pdm;
+ IDiscRecorder *pdr;
+ IEnumDiscRecorders *per;
+ ULONG nActual;
+ HRESULT hr = CoCreateInstance(CLSID_MSDiscMasterObj, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_IDiscMaster, (void**)&pdm);
+ if (SUCCEEDED(hr))
+ {
+ // TODO determine why this is causing trk*.tmp files to be created when called
+ // which ends up spamming the %temp% folder everytime Winamp starts :o(
+ hr = pdm->Open();
+ if (SUCCEEDED(hr))
+ {
+ IEnumDiscMasterFormats *pef;
+ hr = pdm->EnumDiscMasterFormats(&pef);
+ if (SUCCEEDED(hr))
+ {
+ IID pFormats[2];
+ hr = pef->Next(sizeof(pFormats)/sizeof(IID), pFormats, &nActual);
+ if (SUCCEEDED(hr))
+ {
+ while(nActual--) { if (IID_IRedbookDiscMaster == pFormats[nActual]) break; }
+ if (nActual != ((ULONG)-1))
+ {
+ IRedbookDiscMaster *pdf;
+ hr = pdm->SetActiveDiscMasterFormat(IID_IRedbookDiscMaster, (void**)&pdf);
+ if (SUCCEEDED(hr))
+ {
+ pdf->Release();
+ hr = pdm->EnumDiscRecorders(&per);
+ if (SUCCEEDED(hr))
+ {
+ while (S_OK== per->Next(1, &pdr, &nActual) && nActual > 0)
+ {
+ BSTR bstrPath;
+ hr = pdr->GetPath(&bstrPath);
+ if (SUCCEEDED(hr))
+ {
+ for (int i = 0; i < count; i++)
+ {
+ if (0 == lstrcmpW(pDevInfo[i].szTargetPath, bstrPath))
+ {
+ LONG type;
+ if (SUCCEEDED(pdr->GetRecorderType(&type)))
+ {
+ pDevInfo[i].dwType &= 0x0000FFFF;
+ switch(type)
+ {
+ case RECORDER_CDR: pDevInfo[i].dwType |= DRIVE_CAP_R; break;
+ case RECORDER_CDRW: pDevInfo[i].dwType |= DRIVE_CAP_RW; break;
+ }
+ }
+ break;
+ }
+ }
+ if (bstrPath) SysFreeString(bstrPath);
+ }
+ pdr->Release();
+ }
+ per->Release();
+ }
+ }
+ }
+ }
+ pef->Release();
+ }
+ pdm->Close();
+ }
+ pdm->Release();
+ }
+ else
+ {
+ }*/
+}
+
+static void Listener_OnDeviceChange(HWND hwnd, UINT nType, DWORD_PTR dwData)
+{
+ DEV_BROADCAST_HDR *phdr;
+
+ switch(nType)
+ {
+ case DBT_DEVICEARRIVAL:
+ phdr = (DEV_BROADCAST_HDR*)dwData;
+ if (DBT_DEVTYP_VOLUME == phdr->dbch_devicetype)
+ {
+ DEV_BROADCAST_VOLUME *pvol = (DEV_BROADCAST_VOLUME*)phdr;
+ if (DBTF_MEDIA == pvol->dbcv_flags) Medium_Add(Drive_LetterFromMask(pvol->dbcv_unitmask), (DWORD)-1);
+ else if (0 == pvol->dbcv_flags)
+ {
+ char root[] = "X:\\";
+ root[0] = Drive_LetterFromMask(pvol->dbcv_unitmask);
+ if (DRIVE_CDROM == GetDriveTypeA(root)) QueueInfoAPC(0, APC_CheckDrives, (ULONG_PTR)root[0]);
+ }
+ }
+ break;
+ case DBT_DEVICEREMOVECOMPLETE:
+ phdr = (DEV_BROADCAST_HDR*)dwData;
+ if (DBT_DEVTYP_VOLUME == phdr->dbch_devicetype)
+ {
+ DEV_BROADCAST_VOLUME *pvol = (DEV_BROADCAST_VOLUME*)phdr;
+ if (DBTF_MEDIA == pvol->dbcv_flags) Medium_Remove(Drive_LetterFromMask(pvol->dbcv_unitmask));
+ else if (0 == pvol->dbcv_flags)
+ {
+ char root[] = "X:\\";
+ root[0] = Drive_LetterFromMask(pvol->dbcv_unitmask);
+ if (DRIVE_CDROM == GetDriveTypeA(root)) Drive_Remove(root[0]);
+ }
+ }
+ break;
+ }
+}
+
+static DWORD CALLBACK InfoThread(LPVOID param)
+{
+ MSG msg;
+ DWORD start, status, timeout, result(0);
+ BOOL bComInit, run(TRUE);
+ HANDLE hTemp(NULL);
+
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
+ bComInit = ( S_OK == CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
+
+ timeout = 20000; // 20 seconds delay
+ start = GetTickCount();
+
+ while(run)
+ {
+ DWORD elapsed = GetTickCount() - start;
+ if (elapsed < timeout)
+ status = MsgWaitForMultipleObjectsEx(0, NULL, timeout - elapsed,
+ QS_ALLINPUT, MWMO_WAITALL | MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
+ else status = WAIT_TIMEOUT;
+
+ switch(status)
+ {
+ case WAIT_FAILED:
+ if (bComInit) CoUninitialize();
+ return (DWORD)-1;
+ case WAIT_TIMEOUT:
+ if (NULL != pMngr)
+ {
+ EnterCriticalSection(&pMngr->csLock);
+ start = GetCurrentThreadId();
+ hTemp = NULL;
+
+ for (int i = pMngr->nCount - 1; i >= 0; i--)
+ {
+ if (pMngr->pDrives[i].dwThreadId == start)
+ {
+ pMngr->pDrives[i].dwThreadId = 0;
+ hTemp = pMngr->pDrives[i].hThread;
+ pMngr->pDrives[i].hThread = NULL;
+ }
+ }
+ LeaveCriticalSection(&pMngr->csLock);
+
+ //while (WAIT_IO_COMPLETION == WaitForMultipleObjectsEx(0, NULL, TRUE, 0, TRUE));
+ while (SleepEx(0, TRUE) == WAIT_IO_COMPLETION);
+ }
+ result = 2;
+ run = FALSE;
+ break;
+ case WAIT_IO_COMPLETION: start = GetTickCount(); break;
+ case WAIT_OBJECT_0:
+ while (run && PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ switch(msg.message)
+ {
+ case WM_QUIT:
+ result = (DWORD)msg.wParam;
+ run = FALSE;
+ break;
+ case WM_EX_QUIT:
+ PostQuitMessage((INT)msg.wParam);
+ break;
+ default:
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ if (bComInit) CoUninitialize();
+ if (2 == result && hTemp) CloseHandle(hTemp);
+ hTemp = NULL;
+ return result;
+}
+
+static DWORD CALLBACK PollingThread(LPVOID param)
+{
+ MSG msg;
+ DWORD status, timeout;
+ BOOL bComInit;
+
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE);
+ bComInit = ( S_OK == CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
+
+ timeout = POLLMEDIUMCHANGE_INTERVAL;
+
+ for(;;)
+ {
+ DWORD elapsed, start = GetTickCount();
+ while ((elapsed = GetTickCount() - start) < timeout)
+ {
+ status = MsgWaitForMultipleObjectsEx(0, NULL, timeout - elapsed,
+ QS_ALLINPUT, MWMO_WAITALL | MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
+ switch(status)
+ {
+ case WAIT_FAILED:
+ if (bComInit) CoUninitialize();
+ return (DWORD)-1;
+ case WAIT_TIMEOUT: PollMediumInfo(0); break;
+ case WAIT_OBJECT_0:
+ while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ switch(msg.message)
+ {
+ case WM_QUIT:
+ if (bComInit) CoUninitialize();
+ return (DWORD)msg.wParam;
+ case WM_EX_QUIT:
+ PostQuitMessage((INT)msg.wParam);
+ break;
+ default:
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+static void CALLBACK PollMediumInfo(ULONG_PTR param)
+{
+ char letters[32] = {0};
+ LPCWSTR pszDevName[32] = {0};
+ INT index, count;
+ if (!pMngr) return;
+
+ count = 0;
+ EnterCriticalSection(&pMngr->csLock);
+ for (index =0; index < pMngr->nCount; index++)
+ {
+ if (DM_MODE_BURNING != pMngr->pDrives[index].cMode && DM_MODE_RIPPING != pMngr->pDrives[index].cMode)
+ {
+ letters[count] = pMngr->pDrives[index].cLetter;
+ pszDevName[count] = pMngr->pDrives[index].pszDevName;
+ count++;
+ }
+ }
+
+ LeaveCriticalSection(&pMngr->csLock);
+
+ while(count--)
+ {
+ HANDLE hDevice = CreateFileW(pszDevName[count], GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
+
+ if (INVALID_HANDLE_VALUE != hDevice)
+ {
+ BYTE sc, asc, ascq;
+ BOOL bReady, bReportChanges, bNeedRecheck;
+ DWORD ticks;
+
+ bReportChanges = FALSE;
+ bNeedRecheck = FALSE;
+
+ if(!SPTI_TestUnitReady(hDevice, &sc, &asc, &ascq, 2))
+ {
+ bReady = FALSE;
+ }
+ else bReady = (0x00 == sc || (0x02 == sc && 0x3A == asc));
+
+ CloseHandle(hDevice);
+
+ EnterCriticalSection(&pMngr->csLock);
+ for (index =0; index < pMngr->nCount; index++)
+ {
+ if (pMngr->pDrives[index].cLetter == letters[count])
+ {
+ ticks = GetTickCount();
+ if (pMngr->pDrives[index].bMediumInserted &&
+ (ticks - pMngr->pDrives[index].mediumInfo.msLastPolled) > POLLMEDIUMVALIDATE_INTERVAL) bNeedRecheck = TRUE;
+ pMngr->pDrives[index].mediumInfo.msLastPolled = ticks;
+
+ if (bReady && ((0x00 == sc) != pMngr->pDrives[index].bMediumInserted)) bReportChanges = TRUE;
+ break;
+ }
+ }
+ LeaveCriticalSection(&pMngr->csLock);
+
+ if (bReportChanges)
+ {
+ if (0 == sc) Medium_Add(letters[count], (DWORD)-1);
+ else Medium_Remove(letters[count]);
+ }
+ else if (bNeedRecheck)
+ {
+ QueueInfoAPC(letters[count], APC_IsMediumChanged, (DWORD_PTR)letters[count]);
+ }
+
+ }
+ }
+}
+
+
+static LRESULT WINAPI ListenerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_DEVICECHANGE:
+ Listener_OnDeviceChange(hwnd, (UINT)wParam, (DWORD_PTR)lParam);
+ break;
+ }
+ return DefWindowProcW(hwnd, uMsg, wParam, lParam);
+}
+
+static void CALLBACK APC_CheckDrives(ULONG_PTR param)
+{
+ INT index, result, count;
+ DWORD unitmask, dwOutput;
+ STORAGE_DEVICE_NUMBER sdn;
+ DEVICEINFO *pDevInfo;
+ BYTE buffer[4096] = {0};
+
+ if (!pMngr) return;
+
+ unitmask = (DWORD)param;
+ count = 0;
+ for (int i = 0; i < 26; i++) {if (0x1 & (unitmask >> i)) count++;}
+ if (!count) return;
+
+ pDevInfo = (DEVICEINFO*)calloc(count, sizeof(DEVICEINFO));
+ if (!pDevInfo) return;
+
+ index = 0;
+ for (int i = 0; i < 26; i++)
+ {
+ if (0x1 & unitmask)
+ {
+ pDevInfo[index].cLetter = (CHAR)(('A' + i));
+ index++;
+ if (index == count) break;
+ }
+ unitmask = unitmask >> 1;
+ }
+
+ GetDeviceNames(pDevInfo, count);
+
+ for (int i = 0; i < count; i++)
+ {
+ HANDLE hDevice = CreateFileW(pDevInfo[i].pszDevName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
+
+ if (INVALID_HANDLE_VALUE != hDevice)
+ {
+ ZeroMemory(&sdn, sizeof(STORAGE_DEVICE_NUMBER));
+ result = DeviceIoControl(hDevice, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(STORAGE_DEVICE_NUMBER), &dwOutput, NULL);
+ pDevInfo->deviceNumber = (result) ? sdn.DeviceNumber : -1;
+
+ ZeroMemory(&buffer, sizeof(buffer)/sizeof(BYTE));
+ result = DeviceIoControl(hDevice, IOCTL_STORAGE_GET_MEDIA_TYPES_EX, NULL, 0, buffer, sizeof(buffer)/sizeof(BYTE), &dwOutput, NULL);
+ if (result && buffer && (FILE_DEVICE_DVD & ((GET_MEDIA_TYPES*)buffer)->DeviceType)) pDevInfo[i].dwType = DRIVE_TYPE_DVD;
+ else pDevInfo[i].dwType = DRIVE_TYPE_CD;
+
+ CloseHandle(hDevice);
+ }
+ }
+
+ GetDeviceCaps(pDevInfo, count);
+
+ EnterCriticalSection(&pMngr->csLock);
+
+ for (int i = 0; i < count; i++)
+ {
+ pDevInfo[i].opCode = 0;
+ for (index =0; index < pMngr->nCount; index++)
+ {
+ if (pMngr->pDrives[index].cLetter == pDevInfo[i].cLetter)
+ {
+ if (-1 == pMngr->pDrives[index].deviceNumber || pMngr->pDrives[index].deviceNumber != pDevInfo[i].deviceNumber)
+ pDevInfo[i].opCode = 1;
+ break;
+ }
+ }
+ if (pMngr->nCount == index) pDevInfo[i].opCode = 2;
+ }
+ LeaveCriticalSection(&pMngr->csLock);
+
+ for (int i = 0; i < count; i++)
+ {
+ if (pDevInfo[i].opCode) Drive_Add(&pDevInfo[i]);
+ }
+
+ if (pDevInfo)
+ {
+ for (int i = 0; i<count; i++)
+ {
+ if (pDevInfo[i].pszDevName) free(pDevInfo[i].pszDevName);
+ }
+ free(pDevInfo);
+ }
+}
+
+static void CALLBACK APC_IsMediumChanged(ULONG_PTR param)
+{
+ INT opCode;
+ DWORD serial;
+
+ wchar_t devname[4] = L"X:\\";
+
+ if (!pMngr) return;
+
+ opCode = 0;
+ devname[0] = (char)(0xFF & param);
+ if (devname[0])
+ {
+ BOOL result;
+
+ serial = 0;
+ result = GetVolumeInformationW(devname, NULL, 0, &serial, NULL, NULL, NULL, 0);
+ if (!result) serial = 0; // perhaps this is empty recordable disc
+
+ EnterCriticalSection(&pMngr->csLock);
+
+ for (INT index =0; index < pMngr->nCount; index++)
+ {
+ if (pMngr->pDrives[index].cLetter == (char)param )
+ {
+ pMngr->pDrives[index].mediumInfo.msLastPolled = GetTickCount();
+ if (!pMngr->pDrives[index].bMediumInserted && result) opCode = 0x02;
+ else if (pMngr->pDrives[index].mediumInfo.serialNumber != serial)
+ {
+ if (-1 == pMngr->pDrives[index].mediumInfo.serialNumber) pMngr->pDrives[index].mediumInfo.serialNumber = serial;
+ else opCode = 0x03;
+ }
+ break;
+ }
+ }
+ LeaveCriticalSection(&pMngr->csLock);
+
+ if (0x01 & opCode) Medium_Remove((char)param);
+ if (0x02 & opCode) Medium_Add((char)param, serial);
+ }
+}
+
+static void CALLBACK APC_AsyncOp_Complete(ULONG_PTR param)
+{
+ DM_NOTIFY_PARAM *phdr = (DM_NOTIFY_PARAM*)param;
+ if (phdr->hReserved)
+ {
+ CloseHandle(phdr->hReserved);
+ phdr->hReserved = NULL;
+ }
+
+ if (phdr->callback)
+ {
+ if (phdr->uMsg)
+ {
+ if (IsWindow((HWND)phdr->callback)) SendMessageW((HWND)phdr->callback, phdr->uMsg, (WPARAM)DMW_OPCOMPLETED, (LPARAM)phdr);
+ }
+ else ((DMNPROC)phdr->callback)(DMW_OPCOMPLETED, (INT_PTR)param);
+ }
+
+ if (phdr->fnFree)
+ {
+ phdr->fnFree(phdr);
+ }
+}
+
+static void AsycOp_Complete(DM_NOTIFY_PARAM *param)
+{
+ if (param) QueueUserAPC(APC_AsyncOp_Complete, param->hReserved, (ULONG_PTR)param);
+}
+
+static void CALLBACK APC_GetUnitInfo(ULONG_PTR param)
+{
+ DWORD unit;
+ DM_UNITINFO_PARAM *puip;
+ puip = (DM_UNITINFO_PARAM*)param;
+
+ puip->header.opCode = DMOP_UNITINFO;
+
+ unit = CheckLetter(puip->header.cLetter);
+
+ if (!unit || (!PrimoSDKHelper_IsInitialized() && !PrimoSDKHelper_Initialize())) puip->header.result = PRIMOSDK_CMDSEQUENCE;
+ else
+ {
+ DWORD ready = 0;
+ CHAR buffer[512] = {0};
+
+ puip->header.result = PrimoSDKHelper_UnitInfo(&unit, &puip->dwType,
+ ((DMF_DESCRIPTION & puip->header.fFlags) || (DMF_FIRMWARE & puip->header.fFlags)) ? (BYTE*)buffer: NULL,
+ (DMF_READY & puip->header.fFlags) ? &ready : NULL);
+
+ if (PRIMOSDK_OK == puip->header.result)
+ {
+ if (DMF_READY & puip->header.fFlags) puip->bReady = (0 != ready);
+
+ if (DMF_DESCRIPTION & puip->header.fFlags)
+ {
+ INT len = lstrlenA(buffer);
+ if (len > 5) len -= 5;
+ if (!puip->pszDesc || puip->cchDesc < (len + 1)) puip->cchDesc = -(len + 1);
+ else
+ {
+ StringCchCopyNA(puip->pszDesc, puip->cchDesc, buffer, len);
+ puip->cchDesc = len;
+ }
+ }
+ if (DMF_FIRMWARE & puip->header.fFlags)
+ {
+ LPSTR p;
+ INT len = lstrlenA(buffer);
+ p = buffer + (len - ((len > 5) ? 4 : 0));
+ if (!puip->pszFirmware || puip->cchFirmware < 4) puip->cchFirmware = -4;
+ else
+ {
+ StringCchCopyA(puip->pszFirmware, puip->cchFirmware, p);
+ puip->cchFirmware = 4;
+ }
+ }
+ }
+ }
+ AsycOp_Complete(&puip->header);
+}
+
+static void CALLBACK APC_GetUnitInfo2(ULONG_PTR param)
+{
+ DWORD unit;
+ DM_UNITINFO2_PARAM *puip;
+
+ puip = (DM_UNITINFO2_PARAM*)param;
+ puip->header.opCode = DMOP_UNITINFO2;
+ unit = CheckLetter(puip->header.cLetter);
+
+ if (!unit || (!PrimoSDKHelper_IsInitialized() && !PrimoSDKHelper_Initialize())) puip->header.result = PRIMOSDK_CMDSEQUENCE;
+ else
+ {
+ BOOL bReady;
+ DWORD szTypes[32], rfu;
+
+ if (DriveManager_IsUnitReady((char)unit, &bReady) && !bReady)
+ {
+ SleepEx(1000, TRUE);
+ QueueUserAPC(APC_GetUnitInfo2, GetCurrentThread(), param);
+ return;
+ }
+
+ puip->header.result = PrimoSDKHelper_UnitInfo2(&unit, szTypes, &puip->dwClassId, &puip->dwBusType, &rfu);
+ if (PRIMOSDK_OK == puip->header.result)
+ {
+ if (DMF_TYPES & puip->header.fFlags)
+ {
+ INT len;
+ for (len = 0; szTypes[len] != 0xFFFFFFFF; len++);
+
+ if (!puip->pdwTypes || puip->nTypes < len) puip->nTypes = -len;
+ else
+ {
+ puip->nTypes = len;
+ if (len) CopyMemory(puip->pdwTypes, szTypes, sizeof(DWORD)*len);
+ }
+ }
+ }
+ }
+ AsycOp_Complete(&puip->header);
+}
+static void CALLBACK APC_GetDiscInfoEx(ULONG_PTR param)
+{
+ DWORD unit;
+ DM_DISCINFOEX_PARAM *pdip;
+
+ pdip = (DM_DISCINFOEX_PARAM*)param;
+ pdip->header.opCode = DMOP_DISCINFO;
+ unit = CheckLetter(pdip->header.cLetter);
+
+ if (!unit || (!PrimoSDKHelper_IsInitialized() && !PrimoSDKHelper_Initialize())) pdip->header.result = PRIMOSDK_CMDSEQUENCE;
+ else
+ {
+ BOOL bReady;
+ DWORD dwFlags, dwErasable;
+
+ if (DriveManager_IsUnitReady((char)unit, &bReady) && !bReady)
+ {
+ SleepEx(1000, TRUE);
+ QueueUserAPC(APC_GetDiscInfoEx, GetCurrentThread(), param);
+ return;
+ }
+
+ dwFlags = (DMF_DRIVEMODE_TAO & pdip->header.fFlags);
+ pdip->header.result = PrimoSDKHelper_DiscInfoEx(&unit, dwFlags,
+ (DMF_MEDIUMTYPE & pdip->header.fFlags) ? &pdip->dwMediumType : NULL,
+ (DMF_MEDIUMFORMAT & pdip->header.fFlags) ? &pdip->dwMediumFormat : NULL,
+ &dwErasable,
+ (DMF_TRACKS & pdip->header.fFlags) ? &pdip->dwTracks: NULL,
+ (DMF_USED & pdip->header.fFlags) ? &pdip->dwUsed : NULL,
+ (DMF_FREE & pdip->header.fFlags) ? &pdip->dwFree : NULL);
+
+ if (PRIMOSDK_OK == pdip->header.result) pdip->bErasable = (0 != dwErasable);
+ }
+
+ AsycOp_Complete(&pdip->header);
+}
+
+static void CALLBACK APC_GetDiscInfo2(ULONG_PTR param)
+{
+ DWORD unit;
+ DM_DISCINFO2_PARAM *pdip;
+
+ pdip = (DM_DISCINFO2_PARAM*)param;
+ pdip->header.opCode = DMOP_DISCINFO2;
+ unit = CheckLetter(pdip->header.cLetter);
+
+ if (!unit || (!PrimoSDKHelper_IsInitialized() && !PrimoSDKHelper_Initialize())) pdip->header.result = PRIMOSDK_CMDSEQUENCE;
+ else
+ {
+ DWORD rfu, medium, protectedDVD, flags;
+
+ BOOL bReady;
+ if (DriveManager_IsUnitReady((char)unit, &bReady) && !bReady)
+ {
+ SleepEx(1000, TRUE);
+ QueueUserAPC(APC_GetDiscInfo2, GetCurrentThread(), param);
+ return;
+ }
+
+ pdip->header.result = PrimoSDKHelper_DiscInfo2(&unit,
+ (DMF_MEDIUM & pdip->header.fFlags) ? &pdip->dwMedium : (DMF_MEDIUMEX & pdip->header.fFlags) ? &medium : NULL,
+ (DMF_PROTECTEDDVD & pdip->header.fFlags) ? &protectedDVD : NULL,
+ (DMF_PACKETWRITTEN & pdip->header.fFlags) ? &flags : NULL,
+ (DMF_MEDIUMEX & pdip->header.fFlags) ? &pdip->dwMediumEx : NULL,
+ &rfu);
+ if (PRIMOSDK_OK == pdip->header.result)
+ {
+ if (DMF_PROTECTEDDVD & pdip->header.fFlags) pdip->bProtectedDVD = (0 != protectedDVD);
+ if (DMF_PACKETWRITTEN & pdip->header.fFlags) pdip->bPacketWritten = (0 != (PRIMOSDK_PACKETWRITTEN & protectedDVD));
+ }
+
+ }
+ AsycOp_Complete(&pdip->header);
+}
+
+static void CALLBACK APC_GetTitle(ULONG_PTR param)
+{
+ CHAR cLetter;
+ DM_TITLE_PARAM *pdtp;
+
+ pdtp = (DM_TITLE_PARAM*)param;
+ pdtp->header.opCode = DMOP_TITLE;
+ cLetter = CheckLetter(pdtp->header.cLetter);
+
+ pdtp->header.result = PRIMOSDK_CMDSEQUENCE;
+ if (cLetter && pdtp->pszTitle)
+ {
+ wchar_t name[] = L"X:\\";
+ MCIDEVICEID devId;
+ MCI_OPEN_PARMS op = {0};
+ MCI_GENERIC_PARMS gp = {0};
+ MCI_STATUS_PARMS sp = {0};
+
+ name[0] = cLetter;
+
+ op.lpstrDeviceType = (LPWSTR)MCI_DEVTYPE_CD_AUDIO;
+ op.lpstrElementName = name;
+
+ if (!mciSendCommandW(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE_ID | MCI_OPEN_ELEMENT, (DWORD_PTR)&op))
+ {
+ HRESULT hr;
+
+ devId = op.wDeviceID;
+ sp.dwItem = MCI_STATUS_MEDIA_PRESENT;
+ INT present = (!mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)&sp)) ? (BOOL)sp.dwReturn : 0;
+
+ if (present)
+ {
+ INT nTracks;
+ BOOL bAudio;
+ wchar_t szVolume[256] = {0};
+ // check if we have at least one audio track
+ sp.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
+ nTracks = (!mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)&sp)) ? (INT)sp.dwReturn : -1;
+ bAudio = FALSE;
+
+ if (nTracks > 0)
+ {
+ sp.dwItem = MCI_CDA_STATUS_TYPE_TRACK;
+ for (sp.dwTrack = 1; sp.dwTrack <= (UINT)nTracks && !bAudio; sp.dwTrack++)
+ {
+ mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD_PTR)&sp);
+ bAudio = (MCI_CDA_TRACK_AUDIO == sp.dwReturn);
+ }
+ if (bAudio) WASABI_API_LNGSTRINGW_BUF(IDS_CD_AUDIO, szVolume, sizeof(szVolume)/sizeof(wchar_t));
+ else
+ {
+ INT result;
+ wchar_t devname[4] = L"X:\\";
+ devname[0] = cLetter;
+ result = GetVolumeInformationW(devname, szVolume, sizeof(szVolume)/sizeof(wchar_t), NULL, NULL, NULL, NULL, 0);
+ if (!result) WASABI_API_LNGSTRINGW_BUF(IDS_DISC_DATA, szVolume, sizeof(szVolume)/sizeof(wchar_t));
+ }
+ }
+ else WASABI_API_LNGSTRINGW_BUF(IDS_DISC_BLANK, szVolume, sizeof(szVolume)/sizeof(wchar_t));
+
+ hr = StringCchPrintfW(pdtp->pszTitle, pdtp->cchTitle, L"%s (%c:)", szVolume, cLetter);
+ }
+ else
+ {
+ INT nDriveType, nDriveCap;
+ DWORD type;
+ wchar_t szDriveType[32] = {0}, szDriveCap[64] = {0};
+
+ type = DriveManager_GetDriveType(cLetter);
+ if ((DRIVE_TYPE_UNKNOWN | DRIVE_CAP_UNKNOWN) == type) type = DRIVE_TYPE_CD;
+
+ nDriveCap = ((DRIVE_CAP_R | DRIVE_CAP_RW) & type) ? IDS_RECORDER_CAP : IDS_DRIVE_CAP;
+ nDriveType = (IDS_DRIVE_CAP == nDriveCap && (DRIVE_TYPE_DVD & type)) ? IDS_DVD : IDS_CD;
+
+ WASABI_API_LNGSTRINGW_BUF(nDriveType, szDriveType, sizeof(szDriveType)/sizeof(wchar_t));
+ WASABI_API_LNGSTRINGW_BUF(nDriveCap, szDriveCap, sizeof(szDriveCap)/sizeof(wchar_t));
+ hr = StringCchPrintfW(pdtp->pszTitle, pdtp->cchTitle, L"%s %s (%C:)", szDriveType, szDriveCap, cLetter);
+ }
+ pdtp->header.result = hr;
+ mciSendCommandW(devId, MCI_CLOSE, MCI_WAIT, (DWORD_PTR)&gp);
+ }
+ }
+ AsycOp_Complete(&pdtp->header);
+}
+
+static void CALLBACK APC_DriveScan(ULONG_PTR param)
+{
+ char i;
+ char root[] = "A:\\";
+ DWORD unitmask;
+ DEVICEINFO di = {0};
+
+ /// detect drives
+ unitmask = GetLogicalDrives();
+
+ di.deviceNumber = -1;
+ di.dwType = DRIVE_TYPE_CD;
+
+ for (i = 0; i < 26; ++i)
+ {
+ if (0x1 & (unitmask >> i))
+ {
+ root[0] = ('A' + i);
+ if(DRIVE_CDROM != GetDriveTypeA(root)) unitmask &= ~(1 << i);
+ else
+ {
+ di.cLetter = root[0];
+ Drive_Add(&di);
+ }
+ }
+ }
+ APC_CheckDrives((ULONG_PTR)unitmask);
+}
+
+#define MAX_TEST_ATTEMPT 20
+
+static void CALLBACK APC_Eject(ULONG_PTR param)
+{
+ INT nCmd;
+ CHAR cLetter;
+ BYTE sc(0), asc(0), ascq(0);
+
+ nCmd = HIWORD(param);
+ cLetter = CheckLetter((CHAR)param);
+
+ if (cLetter && DM_MODE_READY == DriveManager_GetDriveMode(cLetter))
+ {
+ BOOL bSuccess;
+ HANDLE hDevice;
+
+ hDevice = CreateFileW(GetDeviceName(cLetter), GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
+ if (INVALID_HANDLE_VALUE != hDevice)
+ {
+ DWORD dwOutput;
+ LARGE_INTEGER start, finish;
+
+ bSuccess = SPTI_TestUnitReady(hDevice, &sc, &asc, &ascq, 3);
+ if (!bSuccess && ERROR_SEM_TIMEOUT == GetLastError())
+ {
+ bSuccess = TRUE;
+ sc = 0xFF;
+ }
+
+ if (bSuccess && (0 == sc || (0x02 == sc && 0x3A == asc)))
+ {
+ INT opCode;
+ opCode = (DM_EJECT_REMOVE == nCmd || 0x00 == sc || (0x3A == asc && 0x01 == ascq)) ?
+ IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA;
+
+ QueryPerformanceCounter(&start);
+ bSuccess = DeviceIoControl(hDevice, opCode, NULL, 0, NULL, 0, &dwOutput, NULL);
+ QueryPerformanceCounter(&finish);
+
+ if (bSuccess && DM_EJECT_CHANGE == nCmd && 0x00 != sc && 0x00 == ascq)
+ {
+ finish.QuadPart -= start.QuadPart;
+
+ if (finish.QuadPart < freq.QuadPart && (finish.QuadPart*100000 / freq.QuadPart) < 200)
+ {
+ // test unit redy
+ INT i;
+ sc = 0x02; asc = 0x04; ascq = 0x01;
+ for (i = 0; i < MAX_TEST_ATTEMPT && 0x02 == sc && 0x04 == asc && 0x01 == ascq; i++)
+ {
+ Sleep(50);
+ if (!SPTI_TestUnitReady(hDevice, &sc, &asc, &ascq, 3) && ERROR_SEM_TIMEOUT == GetLastError())
+ i = MAX_TEST_ATTEMPT;
+ }
+ if (i < MAX_TEST_ATTEMPT && 0x02 == sc && 0x3A ==asc)
+ {
+ DeviceIoControl(hDevice, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &dwOutput, NULL);
+ }
+ sc = 0x00;
+ }
+ }
+ }
+ CloseHandle(hDevice);
+ }
+ else bSuccess = FALSE;
+
+ if (!bSuccess)
+ { // we can try MCI
+
+ }
+ else if (0x00 != sc && !(0x02 == sc && 0x3A == asc))
+ {
+ SleepEx(200, TRUE);
+ QueueUserAPC(APC_Eject, GetCurrentThread(), param);
+ return;
+ }
+ }
+}
+
+static void CALLBACK APC_GetMCIInfo(ULONG_PTR param)
+{
+ CHAR cLetter;
+ MCI_OPEN_PARMS op = {0};
+ DM_MCI_PARAM *pmcip;
+
+ pmcip = (DM_MCI_PARAM*)param;
+ pmcip->header.opCode = DMOP_MCIINFO;
+ cLetter = CheckLetter(pmcip->header.cLetter);
+
+ pmcip->header.result = PRIMOSDK_CMDSEQUENCE;
+ if (cLetter)
+ {
+ wchar_t name[] = L"X:\\";
+ MCIDEVICEID devId;
+ MCI_INFO_PARMS ip = {0};
+ MCI_GENERIC_PARMS gp = {0};
+ MCI_STATUS_PARMS sp = {0};
+
+ name[0] = cLetter;
+
+ op.lpstrDeviceType = (LPWSTR)MCI_DEVTYPE_CD_AUDIO;
+ op.lpstrElementName = name;
+
+ if (!mciSendCommandW(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE_ID | MCI_OPEN_ELEMENT, (DWORD_PTR)&op))
+ {
+ WCHAR buffer[512] = {0};
+ INT nMaxTracks = pmcip->nTracks;
+
+ devId = op.wDeviceID;
+
+ if ((DMF_TRACKCOUNT | DMF_TRACKSINFO) & pmcip->header.fFlags)
+ {
+ sp.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
+ pmcip->nTracks = (!mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)&sp)) ? (INT)sp.dwReturn : -1;
+ }
+ if (DMF_READY & pmcip->header.fFlags)
+ {
+ sp.dwItem = MCI_STATUS_READY;
+ pmcip->bReady = (!mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)&sp)) ? (BOOL)sp.dwReturn : 0;
+ }
+ if (DMF_MODE & pmcip->header.fFlags)
+ {
+ sp.dwItem = MCI_STATUS_MODE;
+ pmcip->uMode = (!mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)&sp)) ? (UINT)sp.dwReturn : 0;
+ }
+ if (DMF_MEDIUMPRESENT & pmcip->header.fFlags)
+ {
+ sp.dwItem = MCI_STATUS_MEDIA_PRESENT;
+ pmcip->bMediumPresent = (!mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)&sp)) ? (BOOL)sp.dwReturn : 0;
+ }
+ if (DMF_MEDIUMUID & pmcip->header.fFlags)
+ {
+ ip.dwRetSize = sizeof(buffer)/sizeof(wchar_t);
+ ip.lpstrReturn= buffer;
+ if (!mciSendCommandW(devId, MCI_INFO, MCI_WAIT | MCI_INFO_MEDIA_IDENTITY, (DWORD_PTR)&ip))
+ {
+ INT len;
+ len = lstrlenW(ip.lpstrReturn);
+ if (S_OK == StringCchCopyW(pmcip->pszMediumUID, pmcip->cchMediumUID, ip.lpstrReturn))
+ {
+ pmcip->cchMediumUID = len;
+ }
+ else pmcip->cchMediumUID = 0 - (len + 1);
+ }
+ else pmcip->cchMediumUID = -1;
+ }
+ if (DMF_MEDIUMUPC & pmcip->header.fFlags)
+ {
+ ip.dwCallback = NULL;
+ ip.dwRetSize = sizeof(buffer)/sizeof(wchar_t);
+ ip.lpstrReturn = buffer;
+ if (!mciSendCommandW(devId, MCI_INFO, MCI_WAIT | MCI_INFO_MEDIA_UPC, (DWORD_PTR)&ip))
+ {
+ INT len;
+ len = lstrlenW(ip.lpstrReturn);
+ if (S_OK == StringCchCopyW(pmcip->pszMediumUPC, pmcip->cchMediumUPC, ip.lpstrReturn))
+ {
+ pmcip->cchMediumUPC = len;
+ }
+ else pmcip->cchMediumUPC = 0 - (len + 1);
+ }
+ else pmcip->cchMediumUPC = -1;
+ }
+
+ if (DMF_TRACKSINFO & pmcip->header.fFlags)
+ {
+ MCI_SET_PARMS setp;
+
+ if (nMaxTracks < pmcip->nTracks) pmcip->nTracks = (0 - pmcip->nTracks);
+ else
+ {
+ INT prevPos(0), length(0);
+ setp.dwTimeFormat = MCI_FORMAT_MILLISECONDS;
+ mciSendCommandW(devId, MCI_SET, MCI_WAIT | MCI_SET_TIME_FORMAT, (DWORD_PTR)&setp);
+
+ for (int i = pmcip->nTracks; i > 0; i--)
+ {
+ sp.dwItem = MCI_CDA_STATUS_TYPE_TRACK;
+ sp.dwTrack = i;
+ mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD_PTR)&sp);
+ BOOL bAudio = (MCI_CDA_TRACK_AUDIO == sp.dwReturn);
+
+ sp.dwItem = MCI_STATUS_POSITION;
+ sp.dwTrack = i;
+ mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD_PTR)&sp);
+
+ if (i != pmcip->nTracks) length = prevPos - (INT)sp.dwReturn;
+ prevPos = (INT)sp.dwReturn;
+
+ if (i == pmcip->nTracks)
+ {
+ sp.dwItem = MCI_STATUS_LENGTH;
+ sp.dwTrack = i;
+ mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD_PTR)&sp);
+ length = (INT)sp.dwReturn;
+ }
+
+ pmcip->pTracks[i- 1] = (0x7FFFFFF & length) | ((bAudio) ? 0x80000000 : 0);
+ }
+
+ setp.dwTimeFormat = MCI_FORMAT_TMSF;
+ mciSendCommandW(devId, MCI_SET, MCI_WAIT | MCI_SET_TIME_FORMAT, (DWORD_PTR)&setp);
+ }
+ }
+
+ mciSendCommandW(devId, MCI_CLOSE, MCI_WAIT, (DWORD_PTR)&gp);
+ pmcip->header.result = PRIMOSDK_OK;
+ }
+ }
+
+ AsycOp_Complete(&pmcip->header);
+}
+
+static void CALLBACK APC_GetIMAPIInfo(ULONG_PTR param)
+{
+ CHAR cLetter;
+ BOOL bReady;
+ HRESULT hr(S_FALSE);
+ IDiscMaster *pdm;
+ IDiscRecorder *pdr;
+ IEnumDiscRecorders *per;
+ ULONG nActual;
+
+ wchar_t szDevName[] = L"X:\\";
+ wchar_t szTargetName[128] = {0};
+ DM_IMAPI_PARAM *pIMAPI;
+
+ pIMAPI = (DM_IMAPI_PARAM*)param;
+ cLetter = CheckLetter(pIMAPI->header.cLetter);
+
+ if (DriveManager_IsUnitReady(cLetter, &bReady) && !bReady)
+ {
+ SleepEx(1000, TRUE);
+ QueueUserAPC(APC_GetIMAPIInfo, GetCurrentThread(), param);
+ return;
+ }
+
+ pIMAPI->header.opCode = DMOP_IMAPIINFO;
+
+ pIMAPI->bRecorder = FALSE;
+ pIMAPI->header.result = (DWORD)E_INVALIDARG;
+
+ szDevName[0] = cLetter;
+ if (cLetter && QueryDosDeviceW(szDevName, szTargetName, sizeof(szTargetName)/sizeof(wchar_t)))
+ {
+ hr = CoCreateInstance(CLSID_MSDiscMasterObj, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_IDiscMaster, (void**)&pdm);
+ if (SUCCEEDED(hr))
+ {
+ hr = pdm->Open();
+ if (SUCCEEDED(hr))
+ {
+ IEnumDiscMasterFormats *pef;
+ hr = pdm->EnumDiscMasterFormats(&pef);
+ if (SUCCEEDED(hr))
+ {
+ IID pFormats[2];
+ hr = pef->Next(sizeof(pFormats)/sizeof(IID), pFormats, &nActual);
+ if (SUCCEEDED(hr))
+ {
+ while(nActual--) { if (IID_IRedbookDiscMaster == pFormats[nActual]) break; }
+ if (nActual != ((ULONG)-1))
+ {
+ IRedbookDiscMaster *pdf;
+ hr = pdm->SetActiveDiscMasterFormat(IID_IRedbookDiscMaster, (void**)&pdf);
+ if (SUCCEEDED(hr))
+ {
+ pdf->Release();
+ hr = pdm->EnumDiscRecorders(&per);
+ if (SUCCEEDED(hr))
+ {
+ while (S_OK== per->Next(1, &pdr, &nActual) && nActual > 0)
+ {
+ BSTR bstrPath;
+ hr = pdr->GetPath(&bstrPath);
+ if (SUCCEEDED(hr))
+ {
+ if (0 == lstrcmp(szTargetName, bstrPath))
+ {
+ pIMAPI->bRecorder = TRUE;
+ if ((DMF_BASEPNPID & pIMAPI->header.fFlags) && FAILED(pdr->GetBasePnPID(&pIMAPI->bstrBasePnPID))) pIMAPI->bstrBasePnPID = NULL;
+ if ((DMF_DISPLAYNAMES & pIMAPI->header.fFlags) && FAILED(pdr->GetDisplayNames(&pIMAPI->bstrVendorID, &pIMAPI->bstrProductID, &pIMAPI->bstrRevision)))
+ {
+ pIMAPI->bstrVendorID = NULL;
+ pIMAPI->bstrProductID = NULL;
+ pIMAPI->bstrRevision = NULL;
+ }
+ if (DMF_PATH & pIMAPI->header.fFlags)
+ {
+ pIMAPI->bstrPath = bstrPath;
+ bstrPath = NULL;
+ }
+ if ((DMF_DRIVESTATE & pIMAPI->header.fFlags) && FAILED(pdr->GetRecorderState(&pIMAPI->ulDriveState))) pIMAPI->ulDriveState = (ULONG)-1;
+ if ((DMF_DRIVETYPE & pIMAPI->header.fFlags) && FAILED(pdr->GetRecorderType(&pIMAPI->fDriveType))) pIMAPI->fDriveType = 0;
+ if ((DMF_QUERYMEDIATYPE | DMF_QUERYMEDIAINFO) & pIMAPI->header.fFlags)
+ {
+ BOOL bTypeOk(FALSE), bInfoOk(FALSE);
+ if (SUCCEEDED(pdr->OpenExclusive()))
+ {
+ if (0 == (DMF_QUERYMEDIATYPE & pIMAPI->header.fFlags) ||
+ SUCCEEDED(pdr->QueryMediaType(&pIMAPI->fMediaType, &pIMAPI->fMediaFlags))) bTypeOk = TRUE;
+ if (0 == (DMF_QUERYMEDIAINFO & pIMAPI->header.fFlags) ||
+ SUCCEEDED(pdr->QueryMediaInfo(&pIMAPI->bSessions, &pIMAPI->bLastTrack, &pIMAPI->ulStartAddress,
+ &pIMAPI->ulNextWritable, &pIMAPI->ulFreeBlocks))) bInfoOk = TRUE;
+ pdr->Close();
+ }
+
+
+ if (!bTypeOk)
+ {
+ pIMAPI->fMediaType = -1;
+ pIMAPI->fMediaFlags = -1;
+ }
+ if (!bInfoOk)
+ {
+ pIMAPI->bLastTrack = 0;
+ pIMAPI->bSessions = 0;
+ pIMAPI->ulFreeBlocks = 0;
+ pIMAPI->ulNextWritable = 0;
+ pIMAPI->ulStartAddress = 0;
+ }
+
+
+ }
+ break;
+ }
+ if (bstrPath) SysFreeString(bstrPath);
+ }
+ pdr->Release();
+ }
+ per->Release();
+ }
+ }
+ }
+ }
+ pef->Release();
+ }
+ pdm->Close();
+ }
+ pdm->Release();
+ }
+ }
+ pIMAPI->header.result = hr;
+ AsycOp_Complete(&pIMAPI->header);
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/drivemngr.h b/Src/Plugins/Library/ml_disc/drivemngr.h
new file mode 100644
index 00000000..c5e9c2e9
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/drivemngr.h
@@ -0,0 +1,239 @@
+#ifndef NULLSOFT_MLDISC_DRIVEMANAGER_HEADER
+#define NULLSOFT_MLDISC_DRIVEMANAGER_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <windows.h>
+//#include "../primo/obj_primo.h"
+
+// drive types
+
+#define DRIVE_TYPE_UNKNOWN 0x00000000
+#define DRIVE_TYPE_CD 0x00000010
+#define DRIVE_TYPE_DVD 0x00000020
+#define DRIVE_CAP_UNKNOWN 0x80000000
+#define DRIVE_CAP_R 0x00010000
+#define DRIVE_CAP_RW 0x00020000
+#define DRIVE_CDR (DRIVE_TYPE_CD | DRIVE_CAP_R )
+#define DRIVE_CDRW (DRIVE_TYPE_CD | DRIVE_CAP_RW)
+#define DRIVE_DVDR (DRIVE_TYPE_DVD | DRIVE_CAP_R)
+#define DRIVE_DVDRW (DRIVE_TYPE_DVD | DRIVE_CAP_RW)
+
+#define DMW_DRIVEADDED 0x0001 // param contains drive letter
+#define DMW_DRIVEREMOVED 0x0002 // param contains drive letter
+#define DMW_DRIVECHANGED 0x0003 // param contains drive letter
+#define DMW_MEDIUMARRIVED 0x0004 // param contains drive letter
+#define DMW_MEDIUMREMOVED 0x0005 // param contains drive letter
+#define DMW_OPCOMPLETED 0x0006 // one of the async opetations completed
+
+#define DMW_MODECHANGED 0x0010 // LOWORD(param) = MAKEWORD(cLetter, cMode)
+
+typedef void* HDRVMNGR;
+
+typedef struct _DM_NOTIFY_PARAM DM_NOTIFY_PARAM;
+
+typedef void (CALLBACK *DMNPROC)(WORD/*wCode*/, INT_PTR/*param*/);
+typedef void (CALLBACK *DMFREEPROC)(DM_NOTIFY_PARAM *phdr);
+
+
+#define DM_EJECT_REMOVE 0
+#define DM_EJECT_LOAD 1
+#define DM_EJECT_CHANGE 2
+
+// valid with DM_UNITINFO_PARAM
+#define DMF_DESCRIPTION 0x00000001
+#define DMF_FIRMWARE 0x00000002
+#define DMF_READY 0x00000004
+
+// valid with DM_UNITINFO2_PARAM
+#define DMF_TYPES 0x00000001
+
+// valid with DM_DISCINFOEX_PARAM
+#define DMF_DRIVEMODE_DAO 0x00000000
+#define DMF_DRIVEMODE_TAO 0x00010000
+#define DMF_MEDIUMTYPE 0x00000001
+#define DMF_MEDIUMFORMAT 0x00000002
+#define DMF_TRACKS 0x00000004
+#define DMF_USED 0x00000008
+#define DMF_FREE 0x00000010
+
+// valid with DM_DISCINFO2_PARAM
+#define DMF_MEDIUM 0x00000001
+#define DMF_PROTECTEDDVD 0x00000002
+#define DMF_PACKETWRITTEN 0x00000004
+#define DMF_MEDIUMEX 0x00000008
+
+// valid with DM_FANCYTITLE_PARAM
+#define DMF_VOLUMELABEL 0x00000001 // volume label
+#define DMF_CDTEXT 0x00000002 // if set and medium inserted will try to get info from cdtext
+#define DMF_CDDB 0x00000004 // if set and medium inserted will try to get info from gracenote
+#define DMF_DRIVEDESCRIPTION 0x00000010 // will use PrimoSDK to get drive info
+
+// valid with DM_MCI_PARAM
+#define DMF_READY 0x00000004 //
+#define DMF_MEDIUMPRESENT 0x00000002 //
+#define DMF_MODE 0x00000001 //
+#define DMF_TRACKCOUNT 0x00000008 //
+#define DMF_TRACKSINFO 0x00000010 //
+#define DMF_MEDIUMUID 0x00000020 //
+#define DMF_MEDIUMUPC 0x00000040 //
+
+// valid with DM_IMAPI_PARAM
+#define DMF_BASEPNPID 0x00000001
+#define DMF_DISPLAYNAMES 0x00000002
+#define DMF_PATH 0x00000004
+#define DMF_DRIVESTATE 0x00000008
+#define DMF_DRIVETYPE 0x00000010
+#define DMF_QUERYMEDIATYPE 0x00000020
+#define DMF_QUERYMEDIAINFO 0x00000040
+
+
+// Operation Codes
+#define DMOP_GENERAL 0x0000
+#define DMOP_UNITINFO 0x0001
+#define DMOP_UNITINFO2 0x0002
+#define DMOP_DISCINFO 0x0003
+#define DMOP_DISCINFO2 0x0004
+#define DMOP_TITLE 0x0005
+#define DMOP_MCIINFO 0x0006
+#define DMOP_IMAPIINFO 0x0007
+
+// Drive modes
+#define DM_MODE_ERROR ((CHAR)(0 - 1))
+
+#define DM_MODE_READY 0x00
+#define DM_MODE_BURNING 0x01
+#define DM_MODE_RIPPING 0x02
+#define DM_MODE_COPYING 0x03
+
+
+typedef struct _DM_NOTIFY_PARAM
+{
+ INT_PTR callback; // pointer to the callback. If uMsg != 0 callback is HWND, otherwise it is DMNPROC
+ UINT uMsg; // specify message code to post notification. if 0 callback points to DMNPROC.
+ CHAR cLetter; // drive letter.
+ UINT fFlags; // DMF_XXX
+ DWORD result; // result code. Set by async func.
+ WORD opCode; // completed opCode (DMOP_XXX). Set by async func.
+ DMFREEPROC fnFree; // you can specify function that need to be called to free data
+ HANDLE hReserved; // reserved;
+} DM_NOTIFY_PARAM;
+
+
+typedef struct _DM_UNITINFO_PARAM
+{
+ DM_NOTIFY_PARAM header;
+ DWORD dwType; // unit type
+ BOOL bReady; // unit ready flag
+ LPSTR pszDesc; // pointer to the buffer with unit description.
+ INT cchDesc; // [in] length of the decription buffer in chars. [out] number of characters written. If error value is negative and show minimum required buffer
+ LPSTR pszFirmware; // pointer to the buffer with FirmWare ( firmware version is always 4 chars)
+ INT cchFirmware; // [in] length of the firmware buffer in chars. [out] number of characters written. If error value is negative and show minimum required buffer
+} DM_UNITINFO_PARAM;
+
+typedef struct _DM_UNITINFO2_PARAM
+{
+ DM_NOTIFY_PARAM header;
+ DWORD *pdwTypes; // unit types vector
+ INT nTypes; // vector length (in DWORDS)
+ DWORD dwClassId; // class identifier assigned to the unit.
+ DWORD dwBusType; // type of bus to which the device is connected.
+} DM_UNITINFO2_PARAM;
+
+
+typedef struct _DM_DISCINFOEX_PARAM
+{
+ DM_NOTIFY_PARAM header;
+ DWORD dwMediumType; // type of the medium.
+ DWORD dwMediumFormat; // format of the media
+ BOOL bErasable; //
+ DWORD dwTracks; // number of tracks in the disc.
+ DWORD dwUsed; // total number of sectors used on the disc.
+ DWORD dwFree; // total number of free sectors on the disc.
+
+} DM_DISCINFOEX_PARAM;
+
+typedef struct _DM_DISCINFO2_PARAM
+{
+ DM_NOTIFY_PARAM header;
+ DWORD dwMedium; // physical type of the media.
+ BOOL bProtectedDVD; // DVD containing copy-protected content.
+ BOOL bPacketWritten; // if the media is formatted by packet writing software.
+ DWORD dwMediumEx; // physical type of the medium.
+} DM_DISCINFO2_PARAM;
+
+
+typedef struct _DM_TITLE_PARAM
+{
+ DM_NOTIFY_PARAM header;
+ LPWSTR pszTitle;
+ INT cchTitle;
+
+} DM_TITLE_PARAM;
+
+typedef struct _DM_MCI_PARAM
+{
+ DM_NOTIFY_PARAM header;
+ BOOL bReady;
+ BOOL bMediumPresent;
+ UINT uMode;
+ DWORD* pTracks; // this contains track info (first bit set to '1' if track is audio, other bits track length
+ INT nTracks;
+ LPWSTR pszMediumUID;
+ INT cchMediumUID;
+ LPWSTR pszMediumUPC;
+ INT cchMediumUPC;
+} DM_MCI_PARAM;
+
+// you responsible for freeing all bstrs (use SysFreeString())
+typedef struct _DM_IMAPI_PARAM
+{
+ DM_NOTIFY_PARAM header; // header result contains HRESULT
+ BOOL bRecorder; // Set to TRUE if IMAPI fond drive
+ BSTR bstrBasePnPID; // DMF_BASEPNPID
+ BSTR bstrVendorID; // DMF_DISPLAYNAMES
+ BSTR bstrProductID; // DMF_DISPLAYNAMES
+ BSTR bstrRevision; // DMF_DISPLAYNAMES
+ BSTR bstrPath; // DMF_PATH
+ ULONG ulDriveState; // DMF_DRIVESTATE
+ LONG fDriveType; // DMF_DRIVETYPE
+ LONG fMediaType; // DMF_QUERYMEDIATYPE
+ LONG fMediaFlags; // DMF_QUERYMEDIATYPE
+ BYTE bSessions; // DMF_QUERYMEDIAINFO
+ BYTE bLastTrack; // DMF_QUERYMEDIAINFO
+ ULONG ulStartAddress; // DMF_QUERYMEDIAINFO
+ ULONG ulNextWritable; // DMF_QUERYMEDIAINFO
+ ULONG ulFreeBlocks; // DMF_QUERYMEDIAINFO
+
+} DM_IMAPI_PARAM;
+
+BOOL DriveManager_Initialize(DMNPROC DMNProc, BOOL bSuspended);
+BOOL DriveManager_Uninitialize(INT msExitWaitTime);
+BOOL DriveManager_Suspend(void);
+BOOL DriveManager_Resume(BOOL bUpdate);
+INT DriveManager_GetDriveList(CHAR *pLetters, INT cchSize);
+BOOL DriveManager_Update(BOOL bAsync); // check all drives and discs
+BOOL DriveManager_SetDriveMode(CHAR cLetter, CHAR cMode);
+CHAR DriveManager_GetDriveMode(CHAR cLetter);
+DWORD DriveManager_GetDriveType(CHAR cLetter);
+BOOL DriveManager_Eject(CHAR cLetter, INT nCmd); // nCmd = DM_EJECT_XXX
+BOOL DriveManager_IsUnitReady(BOOL *pbReady);
+BOOL DriveManager_IsMediumInserted(CHAR cLetter);
+// PrimoSDK async calls
+BOOL DriveManager_GetUnitInfo(DM_UNITINFO_PARAM *puip);
+BOOL DriveManager_GetUnitInfo2(DM_UNITINFO2_PARAM *puip);
+BOOL DriveManager_GetDiscInfoEx(DM_DISCINFOEX_PARAM *pdip);
+BOOL DriveManager_GetDiscInfo2(DM_DISCINFO2_PARAM *pdip);
+
+BOOL DriveManager_QueryTitle(DM_TITLE_PARAM *pdtp);
+
+// MCI async
+BOOL DriveManager_GetMCIInfo(DM_MCI_PARAM *pmcip);
+
+//IMAPI async
+BOOL DriveManager_GetIMAPIInfo(DM_IMAPI_PARAM *pIMAPI);
+
+
+#endif //NULLSOFT_MLDISC_DRIVEMANAGER_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/drives.cpp b/Src/Plugins/Library/ml_disc/drives.cpp
new file mode 100644
index 00000000..cdc29c27
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/drives.cpp
@@ -0,0 +1,243 @@
+#include "main.h"
+#include <Windows.h>
+#include "resource.h"
+#include "drives.h"
+#include <strsafe.h>
+
+//
+// UNIT TYPES AND MEDIA TYPES
+//
+#define PRIMOSDK_CDROM 0x00000201
+#define PRIMOSDK_CDR 0x00000202
+#define PRIMOSDK_CDRW 0x00000203
+#define PRIMOSDK_DVDR 0x00000204
+#define PRIMOSDK_DVDROM 0x00000205
+#define PRIMOSDK_DVDRAM 0x00000206
+#define PRIMOSDK_DVDRW 0x00000207
+#define PRIMOSDK_ROBOTICS 0x00000208
+#define PRIMOSDK_DVDPRW 0x00000209
+#define PRIMOSDK_DVDPR 0x00000210
+#define PRIMOSDK_DDCDROM 0x00000211
+#define PRIMOSDK_DDCDR 0x00000212
+#define PRIMOSDK_DDCDRW 0x00000213
+#define PRIMOSDK_DVDPR9 0x00000214
+#define PRIMOSDK_DVDR9 0x00000215
+
+//
+
+#define PRIMOSDK_OTHER 0x00000220
+
+// bus type
+#define PRIMOSDK_UNKNOWN 0
+#define PRIMOSDK_ATAPI 1
+#define PRIMOSDK_SCSI 2
+#define PRIMOSDK_1394 3
+#define PRIMOSDK_USB 4
+#define PRIMOSDK_USB2 5
+
+const wchar_t *typeText[] = {L"UNKNOWN", L"CD-ROM", L"CD-R", L"CD-RW", L"DVD-ROM", L"DVD-R", L"DVD-RW", L"DVD+R", L"DVD+RW", L"DVD-RAM", L"DDCD", L"DDCD-R", L"DDCD-RW", L"DL DVD+R", L"DL DVD-R"};
+const wchar_t *busText[] = {L"UNKNOWN", L"ATAPI", L"SCSI", L"1394", L"USB", L"USB2"};
+
+Drives::Drives(void)
+{
+}
+
+Drives::~Drives(void)
+{
+ Clear();
+}
+
+void Drives::AddDrive(wchar_t letter, unsigned int typeCode, wchar_t* description, const wchar_t *extInfo)
+{
+ OPTICAL_DRIVE drive;
+ drive.letter = (wchar_t)CharUpperW((wchar_t*)letter);
+ drive.typeCode = typeCode;
+ drive.modelInfo = (NULL == description) ? NULL : _wcsdup(description);
+ drive.busType = 0;
+ drive.disc = NULL;
+ if (extInfo)
+ { // extInfo format: bysType;typeCode1;typeCode2;...;typeCoden
+ const wchar_t *desc = extInfo;
+ drive.nTypeList = 0;
+
+ while(desc[0] != 0x00) {desc = CharNextW(desc); if (desc[0] == ';') drive.nTypeList ++;}
+ if (drive.nTypeList) drive.nTypeList;
+
+ drive.pTypeList = (int*) malloc(sizeof(int) * drive.nTypeList);
+ int *list = drive.pTypeList;
+ const wchar_t *start = extInfo;
+ const wchar_t *end = extInfo;
+ BOOL cont;
+ do
+ {
+ while(end[0] != ';' && end[0] != 0x00) end = CharNextW(end);
+
+ cont = (end[0] == ';') ;
+
+ if (start == extInfo)
+ drive.busType = _wtoi(start);
+ else
+ {
+ *list = _wtoi(start);
+ list++;
+ }
+
+ if(cont)
+ {
+ end = CharNextW(end);
+ start = end;
+ }
+ }
+ while(cont);
+ }
+ else
+ {
+ drive.nTypeList = 1;
+ drive.pTypeList = (int*) malloc(sizeof(int) * drive.nTypeList);
+ drive.pTypeList[0] = typeCode;
+ }
+
+ Map<wchar_t, OPTICAL_DRIVE>::MapPair insert_pair(drive.letter, drive);
+ driveList.insert(insert_pair);
+}
+
+void Drives::Clear(void)
+{
+ for (c_iter = driveList.begin(); c_iter != driveList.end(); c_iter++)
+ {
+ if (c_iter->second.modelInfo) free(c_iter->second.modelInfo);
+ if (c_iter->second.pTypeList) free(c_iter->second.pTypeList);
+ if (c_iter->second.disc) delete(c_iter->second.disc);
+ }
+ driveList.clear();
+}
+
+unsigned int Drives::GetCount(void)
+{
+ return (unsigned int)driveList.size();
+}
+
+const OPTICAL_DRIVE* Drives::GetFirst(void)
+{
+ c_iter = driveList.begin();
+ return (c_iter == driveList.end()) ? NULL : &c_iter->second;
+}
+
+const OPTICAL_DRIVE* Drives::GetNext(void)
+{
+ return (++c_iter == driveList.end()) ? NULL : &c_iter->second;
+}
+
+BOOL Drives::IsRecorder(const OPTICAL_DRIVE *drive)
+{
+ BOOL recorder = FALSE;
+ switch (drive->typeCode)
+ {
+ case PRIMOSDK_CDR:
+ case PRIMOSDK_CDRW:
+ case PRIMOSDK_DVDR:
+ case PRIMOSDK_DVDRW:
+ case PRIMOSDK_DVDPR:
+ case PRIMOSDK_DVDPRW:
+ case PRIMOSDK_DVDRAM:
+ case PRIMOSDK_DDCDR:
+ case PRIMOSDK_DDCDRW:
+ case PRIMOSDK_DVDPR9:
+ case PRIMOSDK_DVDR9:
+ recorder = TRUE;
+ break;
+ }
+ return recorder;
+}
+
+const wchar_t* Drives::GetTypeString(int typeCode)
+{
+ int index = 0;
+ switch (typeCode)
+ {
+ case PRIMOSDK_CDROM:
+ index = 1;
+ break;
+ case PRIMOSDK_CDR:
+ index = 2;
+ break;
+ case PRIMOSDK_CDRW:
+ index = 3;
+ break;
+ case PRIMOSDK_DVDROM:
+ index = 4;
+ break;
+ case PRIMOSDK_DVDR:
+ index = 5;
+ break;
+ case PRIMOSDK_DVDRW:
+ index = 6;
+ break;
+ case PRIMOSDK_DVDPR:
+ index = 7;
+ break;
+ case PRIMOSDK_DVDPRW:
+ index = 8;
+ break;
+ case PRIMOSDK_DVDRAM:
+ index = 9;
+ break;
+ case PRIMOSDK_DDCDROM:
+ index = 10;
+ break;
+ case PRIMOSDK_DDCDR:
+ index = 11;
+ break;
+ case PRIMOSDK_DDCDRW:
+ index = 12;
+ break;
+ case PRIMOSDK_DVDPR9:
+ index = 13;
+ break;
+ case PRIMOSDK_DVDR9:
+ index = 14;
+ break;
+ default:
+ static wchar_t tmp2[64];
+ return WASABI_API_LNGSTRINGW_BUF(plugin.hDllInstance,IDS_UNKNOWN,tmp2,64);
+ }
+ return typeText[index];
+}
+
+const wchar_t* Drives::GetBusString(int busCode)
+{
+ int index = 0;
+ switch (busCode)
+ {
+ case PRIMOSDK_ATAPI:
+ index = 1;
+ break;
+ case PRIMOSDK_SCSI:
+ index = 2;
+ break;
+ case PRIMOSDK_1394:
+ index = 3;
+ break;
+ case PRIMOSDK_USB:
+ index = 4;
+ break;
+ case PRIMOSDK_USB2:
+ index = 5;
+ break;
+ default:
+ static wchar_t tmp3[64];
+ return WASABI_API_LNGSTRINGW_BUF(plugin.hDllInstance,IDS_UNKNOWN,tmp3,64);
+ }
+ return busText[index];
+}
+
+const wchar_t* Drives::GetFormatedString(const OPTICAL_DRIVE *drv, wchar_t *buffer, size_t size, BOOL useFullName)
+{
+ StringCchPrintfW(buffer, size, WASABI_API_LNGSTRINGW(plugin.hDllInstance,IDS_X_DRIVE_BRACKET_X),
+ GetTypeString(drv->typeCode), drv->letter);
+ if (useFullName && drv->modelInfo)
+ {
+ StringCchPrintfW(buffer, size, L"%s - %s", buffer, drv->modelInfo);
+ }
+ return buffer;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/drives.h b/Src/Plugins/Library/ml_disc/drives.h
new file mode 100644
index 00000000..4984607e
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/drives.h
@@ -0,0 +1,44 @@
+#ifndef NULLSOFT_DRIVEINFO_HEADER
+#define NULLSOFT_DRIVEINFO_HEADER
+
+#include "../nu/Map.h"
+#include ".\discinfo.h"
+
+
+typedef struct
+{
+ wchar_t letter;
+ int typeCode;
+ wchar_t* modelInfo;
+ int busType;
+ int *pTypeList; // all supported types
+ int nTypeList; // number of supported tpyes
+ DiscInfo *disc; // inserted disc info;
+} OPTICAL_DRIVE;
+
+#define MAX_FORMAT_DRIVE_STRING 512
+
+class Drives
+{
+public:
+ Drives(void);
+ ~Drives(void);
+
+public:
+ void AddDrive(wchar_t letter, unsigned int typeCode, wchar_t* description, const wchar_t *extInfo);
+ void Clear(void);
+ unsigned int GetCount(void);
+ const OPTICAL_DRIVE* GetFirst(void);
+ const OPTICAL_DRIVE* GetNext(void); // if returns NULL - means no more
+
+ static BOOL IsRecorder(const OPTICAL_DRIVE *drive);
+
+ static const wchar_t* GetTypeString(int typeCode);
+ static const wchar_t* GetBusString(int busCode);
+ static const wchar_t* GetFormatedString(const OPTICAL_DRIVE *drv, wchar_t *buffer, size_t size, BOOL useFullName = TRUE);
+private:
+ Map<wchar_t, OPTICAL_DRIVE> driveList;
+ Map<wchar_t, OPTICAL_DRIVE>::const_iterator c_iter;
+};
+
+#endif //NULLSOFT_DRIVEINFO_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/formatfilename.cpp b/Src/Plugins/Library/ml_disc/formatfilename.cpp
new file mode 100644
index 00000000..cca31507
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/formatfilename.cpp
@@ -0,0 +1,269 @@
+#include "main.h"
+#include "../nu/ns_wc.h"
+#include "../winamp/wa_ipc.h"
+#include <shlwapi.h>
+#include <strsafe.h>
+
+#define TID_UNKNOWN 0
+#define TID_ARTIST 1
+#define TID_ALBUM 2
+#define TID_TITLE 3
+#define TID_GENRE 4
+#define TID_YEAR 5
+#define TID_TRACKARTIST 6
+#define TID_FILENAME 7
+#define TID_EXTENSION 8
+#define TID_DISC 9
+#define TID_DISCS 10
+
+#define TOKEN_ARTIST TEXT("<artist>")
+#define TOKEN_ALBUM TEXT("<album>")
+#define TOKEN_TITLE TEXT("<title>")
+#define TOKEN_GENRE TEXT("<genre>")
+#define TOKEN_YEAR TEXT("<year>")
+#define TOKEN_TRACKARTIST TEXT("<trackartist>")
+#define TOKEN_FILENAME TEXT("<filename>")
+#define TOKEN_EXTENSION TEXT("<extension>")
+#define TOKEN_DISC TEXT("<disc>")
+#define TOKEN_DISCS TEXT("<discs>")
+
+#define TOKEN_LEN_ARTIST 8
+#define TOKEN_LEN_ALBUM 7
+#define TOKEN_LEN_TITLE 7
+#define TOKEN_LEN_GENRE 7
+#define TOKEN_LEN_YEAR 6
+#define TOKEN_LEN_TRACKARTIST 13
+#define TOKEN_LEN_FILENAME 10
+#define TOKEN_LEN_EXTENSION 11
+#define TOKEN_LEN_DISC 6
+#define TOKEN_LEN_DISCS 7
+
+#define STRCOMP_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
+
+#define CHECK_TOKEN(token, format) (CSTR_EQUAL == CompareString(STRCOMP_INVARIANT, NORM_IGNORECASE, TOKEN_##token##, TOKEN_LEN_##token##, format, TOKEN_LEN_##token##))
+
+
+static TCHAR PathValidateChar(TCHAR cVal, BOOL bAllowBackSlash)
+{
+ switch(cVal)
+ {
+ case TEXT('\\'): if (!bAllowBackSlash) return TEXT('-');
+ break;
+ case TEXT('/'): if (!bAllowBackSlash) return TEXT('-');
+ return TEXT('\\');
+
+ case TEXT(':'): return TEXT('-');
+ case TEXT('*'): return TEXT('_');
+ case TEXT('?'): return TEXT('_');
+ case TEXT('\"'): return TEXT('\'');
+ case TEXT('<'): return TEXT('(');
+ case TEXT('>'): return TEXT(')');
+ case TEXT('|'): return TEXT('_');
+ }
+ return cVal;
+}
+
+void CleanupDirectoryString(LPTSTR pszDirectory)
+{
+ PathUnquoteSpaces(pszDirectory);
+ PathRemoveBlanks(pszDirectory);
+
+ LPTSTR pc = pszDirectory;
+ while (TEXT('\0') != *pc) { if (TEXT('/') == *pc) *pc = TEXT('\\'); pc++; }
+
+ if (pc > pszDirectory)
+ {
+ pc--;
+ while (pszDirectory != pc &&
+ (TEXT('.') == *pc || TEXT(' ') == *pc || TEXT('\\') == *pc))
+ {
+ *pc = TEXT('\0');
+ pc--;
+ }
+ }
+}
+
+LPWSTR GetExtensionString(LPWSTR pszBuffer, INT cchBufferMax, DWORD fourcc)
+{
+ char configExt[10] = { '\0', };
+ pszBuffer[0] = L'\0';
+ convertConfigItem cfi;
+ cfi.configfile = 0;
+ cfi.data = configExt;
+ cfi.format = fourcc;
+ cfi.item = "extension";
+ cfi.len = 10;
+ SENDWAIPC(plugin.hwndWinampParent, IPC_CONVERT_CONFIG_GET_ITEM, (WPARAM)&cfi);
+ if ('\0' != *configExt)
+ {
+ if (!MultiByteToWideCharSZ(CP_ACP, 0, configExt, -1, pszBuffer, cchBufferMax))
+ return NULL;
+ }
+ else
+ {
+ if (cchBufferMax < 5) return NULL;
+ pszBuffer[0] = (TCHAR)((fourcc) & 0xff);
+ pszBuffer[1] = (TCHAR)((fourcc >> 8) & 0xff);
+ pszBuffer[2] = (TCHAR)((fourcc >> 16) & 0xff);
+ pszBuffer[3] = TEXT('\0');
+ for (LPTSTR p = &pszBuffer[2]; p >= pszBuffer && TEXT(' ') == *p; p--) *p = TEXT('\0');
+ }
+ return pszBuffer;
+}
+
+// if trackno is 0xdeadbeef, or title is 0, they are both ignored
+// TODO: use ATF instead
+HRESULT FormatFileName(LPTSTR pszTextOut, INT cchTextMax, LPCTSTR pszFormat,
+ INT nTrackNo, LPCTSTR pszArtist,
+ LPCTSTR pszAlbum, LPCTSTR pszTitle,
+ LPCTSTR pszGenre, LPCTSTR pszYear,
+ LPCTSTR pszTrackArtist,
+ LPCTSTR pszFileName, LPCTSTR pszDisc)
+{
+ HRESULT hr = S_OK;
+ TCHAR szBuffer[MAX_PATH] = {0};
+ LPTSTR pszStart = pszTextOut + lstrlen(pszTextOut);
+
+ while (pszFormat && TEXT('\0') != *pszFormat)
+ {
+ int whichstr = TID_UNKNOWN;
+ if (*pszFormat == TEXT('#') && nTrackNo != 0xdeadbeef)
+ {
+ int cnt = 0;
+ while (pszFormat && *pszFormat == TEXT('#')) { pszFormat++; cnt++; }
+ if (cnt > 8) cnt = 8;
+
+ TCHAR szFormat[32] = {0};
+ hr = StringCchPrintf(szFormat, ARRAYSIZE(szFormat), TEXT("%%%02dd"), cnt);
+ if (S_OK == hr) StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), szFormat, nTrackNo);
+ if (S_OK == hr) StringCchCat(pszTextOut, cchTextMax, szBuffer);
+ if (S_OK != hr) return hr;
+ }
+ else if (pszArtist && CHECK_TOKEN(ARTIST, pszFormat)) whichstr = TID_ARTIST;
+ else if (pszAlbum && CHECK_TOKEN(ALBUM, pszFormat)) whichstr = TID_ALBUM;
+ else if (pszTitle && CHECK_TOKEN(TITLE, pszFormat)) whichstr = TID_TITLE;
+ else if (pszGenre && CHECK_TOKEN(GENRE, pszFormat)) whichstr = TID_GENRE;
+ else if (pszYear && CHECK_TOKEN(YEAR, pszFormat)) whichstr = TID_YEAR;
+ else if (pszTrackArtist && CHECK_TOKEN(TRACKARTIST, pszFormat)) whichstr = TID_TRACKARTIST;
+ else if (pszFileName && CHECK_TOKEN(FILENAME, pszFormat)) whichstr = TID_FILENAME;
+ else if (pszFileName && CHECK_TOKEN(EXTENSION, pszFormat)) whichstr = TID_EXTENSION;
+ else if (pszDisc && CHECK_TOKEN(DISC, pszFormat)) whichstr = TID_DISC;
+ else if (pszDisc && CHECK_TOKEN(DISCS, pszFormat)) whichstr = TID_DISCS;
+ else
+ {
+ INT l = lstrlen(pszTextOut);
+ pszTextOut += l;
+ cchTextMax -= l;
+ pszTextOut[0] = *pszFormat++;
+ if (cchTextMax < 2) return STRSAFE_E_INSUFFICIENT_BUFFER;
+ pszTextOut[0] = PathValidateChar(pszTextOut[0], TRUE);
+ if (TEXT('\\') == *pszTextOut)
+ {
+ // remove end spaces and dots
+ while (pszTextOut > pszStart &&
+ (TEXT('\\') == *(pszTextOut - 1) || TEXT(' ') == *(pszTextOut - 1) || TEXT('.') == *(pszTextOut - 1)))
+ {
+ pszTextOut--;
+ cchTextMax--;
+ }
+
+ if (pszTextOut == pszStart)
+ {
+ pszTextOut--;
+ cchTextMax--;
+ }
+ else *pszTextOut = TEXT('\\');
+ }
+ pszTextOut++;
+ cchTextMax--;
+ *pszTextOut = TEXT('\0');
+ if (S_OK != hr) return hr;
+ }
+ if (whichstr != TID_UNKNOWN)
+ {
+ LPCTSTR pszSrc = NULL;
+ int islow = IsCharLower(pszFormat[1]) && IsCharLower(pszFormat[2]);
+ int ishi = IsCharUpper(pszFormat[1]) && IsCharUpper(pszFormat[2]);
+ switch(whichstr)
+ {
+ case TID_ARTIST: pszSrc = pszArtist; pszFormat += TOKEN_LEN_ARTIST; break;
+ case TID_ALBUM: pszSrc = pszAlbum; pszFormat += TOKEN_LEN_ALBUM; break;
+ case TID_TITLE: pszSrc = pszTitle; pszFormat += TOKEN_LEN_TITLE; break;
+ case TID_GENRE: pszSrc = pszGenre; pszFormat += TOKEN_LEN_GENRE; break;
+ case TID_YEAR: pszSrc = pszYear; pszFormat += TOKEN_LEN_YEAR; break;
+ case TID_TRACKARTIST: pszSrc = pszTrackArtist; pszFormat += TOKEN_LEN_TRACKARTIST; break;
+ case TID_DISC:
+ if (pszDisc && *pszDisc && TEXT('\0') == *pszDisc) pszSrc = L"1";
+ else
+ {
+ // default to 1 when we've not got a proper value passed to us
+ int disc = _wtoi(pszDisc);
+ if(disc <= 0) disc = 1;
+ StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), L"%d", disc);
+ pszSrc = szBuffer;
+ }
+ pszFormat += TOKEN_LEN_DISC;
+ break;
+ case TID_DISCS:
+ if (pszDisc && *pszDisc && TEXT('\0') == *pszDisc) pszSrc = L"1";
+ else
+ {
+ LPTSTR pszTemp = wcschr((LPTSTR)pszDisc, L'/');
+ if(pszTemp == NULL) pszTemp = wcschr((LPTSTR)pszDisc, L'\\');
+ if(pszTemp == NULL)
+ {
+ pszTemp = (LPTSTR)pszDisc;
+ }
+ else
+ {
+ pszTemp = CharNext(pszTemp);
+ }
+ int disc = _wtoi(pszTemp);
+ if(disc <= 0) disc = 1;
+ StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), L"%d", disc);
+ pszSrc = szBuffer;
+ }
+ pszFormat += TOKEN_LEN_DISCS;
+ break;
+ case TID_FILENAME:
+ pszSrc = PathFindExtension(pszFileName);
+ if (TEXT('\0') == *pszSrc) pszSrc = pszFileName;
+ else
+ {
+ StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), pszFileName);
+ PathRemoveExtension(szBuffer);
+ pszSrc = szBuffer;
+ }
+ pszFormat += TOKEN_LEN_FILENAME;
+ break;
+ case TID_EXTENSION:
+ pszSrc = PathFindExtension(pszFileName); pszFormat += TOKEN_LEN_EXTENSION;
+ while (pszTextOut > pszStart && TEXT('.') == *(pszTextOut - 1))
+ {
+ pszTextOut--;
+ *pszTextOut = TEXT('\0');
+ cchTextMax++;
+ }
+ break;
+ }
+
+ INT l = lstrlen(pszTextOut);
+ pszTextOut += l;
+ cchTextMax -= l;
+
+ while (pszSrc && TEXT('\0') != *pszSrc && cchTextMax > 0)
+ {
+ pszTextOut[0] = pszSrc[0];
+ if (ishi) CharUpperBuffW(pszTextOut, 1);
+ else if (islow) CharLowerBuffW(pszTextOut, 1);
+ pszTextOut[0] = PathValidateChar(pszTextOut[0], FALSE);
+ cchTextMax--;
+ if (cchTextMax) pszTextOut++;
+ pszSrc++;
+ }
+ pszTextOut[0] = TEXT('\0');
+ if (0 == cchTextMax) return STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ }
+ return hr;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/helpwnd.cpp b/Src/Plugins/Library/ml_disc/helpwnd.cpp
new file mode 100644
index 00000000..3bbce8be
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/helpwnd.cpp
@@ -0,0 +1,350 @@
+#include "main.h"
+#include "./resource.h"
+#include "../nu/trace.h"
+#include <strsafe.h>
+
+#define MLHELP_PROP TEXT("MLHELP")
+
+typedef struct __SIMPLEHELP
+{
+ LPCWSTR pszTitle;
+ LPCWSTR pszCaption;
+ LPCWSTR pszText;
+ HWND hOwner;
+ UINT uFlags;
+} SIMPLEHELP;
+
+typedef struct __MLHELP
+{
+ HWND hOwner;
+ UINT uFlags;
+ LONG width;
+ LONG height;
+} MLHELP;
+
+#define GetHelp(__hwnd) ((MLHELP*)GetProp((__hwnd), MLHELP_PROP))
+
+#define CLIENT_MIN_WIDTH 280
+#define CLIENT_MIN_HEIGHT 200
+#define CLIENT_MAX_WIDTH 800
+#define CLIENT_MAX_HEIGHT 600
+#define BORDER_SPACE 10
+
+static INT_PTR CALLBACK SimpleHelp_DialogProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+HWND MLDisc_ShowHelp(HWND hOwner, LPCWSTR pszWindowTitle, LPCWSTR pszCaption, LPCWSTR pszText, UINT uFlags)
+{
+ SIMPLEHELP help;
+ help.pszTitle = pszWindowTitle;
+ help.pszCaption = pszCaption;
+ help.pszText = pszText;
+ help.uFlags = uFlags;
+ help.hOwner = hOwner;
+
+ if (HF_DOMODAL & uFlags)
+ {
+ WASABI_API_DIALOGBOXPARAMW(IDD_SIMPLEHELP, hOwner, SimpleHelp_DialogProc, (LPARAM)&help);
+ return NULL;
+ }
+
+ return WASABI_API_CREATEDIALOGPARAMW(IDD_SIMPLEHELP, hOwner, SimpleHelp_DialogProc, (LPARAM)&help);
+}
+
+static BOOL FindPrefferedSizeEx(HDC hdc, LPCTSTR pszText, LPCTSTR pszNewLine, SIZE *pSize)
+{
+ if (!pSize) return FALSE;
+ pSize->cx = 0; pSize->cy = 0;
+ if (!hdc || !pszText || !pszNewLine) return FALSE;
+ LPCTSTR pszBlock = pszText;
+ LPCTSTR pszCursor = pszBlock;
+ INT cchSep = lstrlenW(pszNewLine);
+ INT matched = 0;
+ for(;;)
+ {
+ if (*pszCursor)
+ {
+ if (*pszCursor == pszNewLine[matched]) matched++;
+ else matched = 0;
+ pszCursor++;
+ }
+ if (matched == cchSep || TEXT('\0') == *pszCursor)
+ {
+ SIZE sz;
+
+ INT l = (INT)(size_t)((pszCursor - pszBlock) - matched);
+ if (l > 0)
+ {
+ if (!GetTextExtentPoint32(hdc, pszBlock, l, &sz)) return FALSE;
+ }
+ else
+ {
+ if (!GetTextExtentPoint32(hdc, TEXT("\n"), 1, &sz)) return FALSE;
+ sz.cx = 0;
+ }
+
+
+ if (pSize->cx < sz.cx) pSize->cx= sz.cx;
+ pSize->cy += sz.cy;
+
+ if (TEXT('\0') == *pszCursor) break;
+ else
+ {
+ matched = 0;
+ pszBlock = pszCursor;
+ }
+ }
+ }
+ return TRUE;
+}
+
+static BOOL FindPrefferedSize(HWND hwnd, LPCTSTR pszText, LPCTSTR pszNewLine, SIZE *pSize)
+{
+ HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_PARENTCLIP);
+ if (!hdc) return FALSE;
+ HFONT hf, hfo;
+ hf = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0L);
+ if (NULL == hf) hf = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+ hfo = (NULL != hf) ? (HFONT)SelectObject(hdc, hf) : NULL;
+
+ BOOL br = FindPrefferedSizeEx(hdc, pszText, pszNewLine, pSize);
+
+ if (hfo) SelectObject(hdc, hfo);
+ ReleaseDC(hwnd, hdc);
+
+ return br;
+}
+
+static INT_PTR SimpleHlp_OnInitDialog(HWND hdlg, HWND hFocus, LPARAM lParam)
+{
+ SIMPLEHELP *pHelp = (SIMPLEHELP*)lParam;
+ SIZE sizeCaption = { 0, 0 };
+ SIZE sizeText = { 0, 0 };
+ SIZE sizeClient = {0, 0};
+ SIZE sizeButton = {0, 0};
+
+ MLHELP *pmlh = (MLHELP*)calloc(1, sizeof(MLHELP));
+ if (pmlh)
+ {
+ pmlh->hOwner = pHelp->hOwner;
+ pmlh->uFlags = pHelp->uFlags;
+ pmlh->height = 0;
+ pmlh->width = 0;
+ }
+ SetProp(hdlg, MLHELP_PROP, (HANDLE)pmlh);
+
+ HWND hctrl;
+ if(pHelp)
+ {
+
+ WCHAR szBuffer[4096] = {0};
+ if (pHelp->pszTitle)
+ {
+ if (IS_INTRESOURCE(pHelp->pszCaption))
+ {
+ WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)pHelp->pszTitle, szBuffer, ARRAYSIZE(szBuffer));
+ pHelp->pszTitle = szBuffer;
+ }
+ SetWindowText(hdlg, pHelp->pszTitle);
+ }
+
+ if (pHelp->pszCaption)
+ {
+ if (IS_INTRESOURCE(pHelp->pszCaption))
+ {
+ WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)pHelp->pszCaption, szBuffer, ARRAYSIZE(szBuffer));
+ pHelp->pszCaption = szBuffer;
+ }
+
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDC_LBL_CAPTION)))
+ {
+ FindPrefferedSize(hctrl, pHelp->pszCaption, TEXT("\n"), &sizeCaption);
+ SetWindowText(hctrl, pHelp->pszCaption);
+ }
+ }
+
+ if (pHelp->pszText)
+ {
+ if (IS_INTRESOURCE(pHelp->pszText))
+ {
+ WCHAR form_szBuffer[4096] = {0}, *fszB = form_szBuffer, *szB = szBuffer;
+ WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)pHelp->pszText, form_szBuffer, ARRAYSIZE(form_szBuffer));
+ while(fszB && *fszB){
+ if(*fszB == L'\n' && *CharPrevW(form_szBuffer,fszB) != L'\r'){
+ *szB = L'\r';
+ szB = CharNextW(szB);
+ }
+ *szB = *fszB;
+ szB = CharNextW(szB);
+ fszB = CharNextW(fszB);
+ }
+ *szB = 0;
+ pHelp->pszText = szBuffer;
+ }
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDC_EDT_TEXT)))
+ {
+ FindPrefferedSize(hctrl, pHelp->pszText, TEXT("\r\n"), &sizeText);
+ SetWindowText(hctrl, pHelp->pszText);
+ }
+ }
+
+ if (0 == (HF_ALLOWRESIZE & pHelp->uFlags))
+ {
+ SetWindowLongPtrW(hdlg, GWL_EXSTYLE, GetWindowLongPtrW(hdlg, GWL_EXSTYLE) | WS_EX_DLGMODALFRAME);
+ SetWindowLongPtrW(hdlg, GWL_STYLE, (GetWindowLongPtrW(hdlg, GWL_STYLE) & ~WS_THICKFRAME) | DS_MODALFRAME);
+ }
+ }
+
+ if (sizeText.cx > 0) sizeText.cx += GetSystemMetrics(SM_CXVSCROLL);
+
+ sizeClient.cx = ((sizeText.cx > sizeCaption.cx) ? sizeText.cx : sizeCaption.cx) + 8;
+ if (sizeClient.cx < CLIENT_MIN_WIDTH) sizeClient.cx = CLIENT_MIN_WIDTH;
+ if (sizeClient.cx > CLIENT_MAX_WIDTH) sizeClient.cx = CLIENT_MAX_WIDTH;
+ sizeText.cx = sizeClient.cx;
+ sizeCaption.cx = sizeClient.cx;
+ sizeClient.cx += BORDER_SPACE * 2;
+
+ if (sizeCaption.cy > 0) sizeCaption.cy += 16;
+ if (sizeCaption.cy > CLIENT_MAX_HEIGHT/3) sizeCaption.cy = CLIENT_MAX_HEIGHT/3;
+
+ if (sizeText.cy > 0) sizeText.cy += 16;
+ if (sizeText.cy < (CLIENT_MIN_HEIGHT - sizeCaption.cy)) sizeText.cy = (CLIENT_MIN_HEIGHT - sizeCaption.cy);
+ if (sizeText.cy > (CLIENT_MAX_HEIGHT - sizeCaption.cy)) sizeText.cy = (CLIENT_MAX_HEIGHT - sizeCaption.cy);
+
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDCANCEL)))
+ {
+ RECT rw;
+ if (GetWindowRect(hctrl, &rw)) { sizeButton.cx = rw.right - rw.left; sizeButton.cy = rw.bottom - rw.top; }
+ }
+
+ LONG top = BORDER_SPACE;
+ sizeClient.cy = BORDER_SPACE +sizeCaption.cy + sizeText.cy + 8 + sizeButton.cy + BORDER_SPACE;
+
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDC_LBL_CAPTION)))
+ {
+ if (0 == sizeCaption.cy) EnableWindow(hctrl, FALSE);
+ SetWindowPos(hctrl, NULL, BORDER_SPACE, top, sizeCaption.cx, sizeCaption.cy, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
+ top += sizeCaption.cy;
+ }
+
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDC_EDT_TEXT)))
+ {
+ if (0 == sizeText.cy) EnableWindow(hctrl, FALSE);
+ SetWindowPos(hctrl, NULL, BORDER_SPACE, top, sizeText.cx, sizeText.cy, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
+ }
+
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDCANCEL)))
+ {
+ SetWindowPos(hctrl, NULL, sizeClient.cx - BORDER_SPACE - sizeButton.cx, sizeClient.cy - BORDER_SPACE - sizeButton.cy,
+ sizeButton.cx, sizeButton.cy, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
+ }
+
+ RECT rw, rc;
+ if (GetClientRect(hdlg, &rc) && GetWindowRect(hdlg, &rw))
+ {
+ sizeClient.cx += ((rw.right - rw.left) - (rc.right - rc.left));
+ sizeClient.cy += ((rw.bottom - rw.top) - (rc.bottom - rc.top));
+ }
+
+ SetRect(&rw, 0, 0, 0, 0);
+
+ if (pHelp->hOwner && GetWindowRect(pHelp->hOwner, &rw))
+ {
+ rw.left += ((rw.right - rw.left) - sizeClient.cx)/2;
+ rw.top += ((rw.bottom - rw.top) - sizeClient.cy)/2;
+ }
+ UINT swpFlags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | ((HF_DOMODAL & pHelp->uFlags) ? SWP_NOZORDER : 0);
+ pmlh->width = sizeClient.cx;
+ pmlh->height = sizeClient.cy;
+ SetWindowPos(hdlg, HWND_TOP, rw.left, rw.top, sizeClient.cx, sizeClient.cy, swpFlags);
+ return FALSE;
+}
+
+static void SimpleHelp_OnDestroy(HWND hdlg)
+{
+ MLHELP *pHelp = GetHelp(hdlg);
+ RemoveProp(hdlg, MLHELP_PROP);
+
+ if (pHelp && 0 == (HF_DOMODAL & pHelp->uFlags) && pHelp->hOwner && IsWindow(pHelp->hOwner))
+ SendMessageW(pHelp->hOwner, WM_PARENTNOTIFY, MAKEWPARAM(WM_DESTROY, 0), (LPARAM)hdlg);
+ if (pHelp) free(pHelp);
+}
+
+static void SimpleHlp_OnCommand(HWND hdlg, INT ctrlId, INT eventId, HWND hctrl)
+{
+ MLHELP *pHelp = GetHelp(hdlg);
+ switch(ctrlId)
+ {
+ case IDCANCEL:
+ case IDOK:
+ if (!pHelp || (HF_DOMODAL & pHelp->uFlags)) EndDialog(hdlg, ctrlId);
+ else DestroyWindow(hdlg);
+ break;
+ }
+}
+
+/*static void SimpleHlp_OnWindowPosChanging(HWND hdlg, WINDOWPOS *pwp)
+{
+ MLHELP *pHelp = GetHelp(hdlg);
+ if (!pHelp) return;
+
+ if (0 == (SWP_NOSIZE & pwp->flags))
+ {
+ if (pwp->cx < CLIENT_MIN_WIDTH) pwp->cx = CLIENT_MIN_WIDTH;
+ if (pwp->cx > CLIENT_MAX_WIDTH) pwp->cx = CLIENT_MAX_WIDTH;
+ if (pwp->cy < CLIENT_MIN_HEIGHT) pwp->cy = CLIENT_MIN_HEIGHT;
+ if (pwp->cy > CLIENT_MAX_HEIGHT) pwp->cy = CLIENT_MAX_HEIGHT;
+ }
+}
+
+static void SimpleHlp_OnWindowPosChanged(HWND hdlg, WINDOWPOS *pwp)
+{
+ MLHELP *pHelp = GetHelp(hdlg);
+ if (!pHelp) return;
+ if (0 == (SWP_NOSIZE & pwp->flags))
+ {
+ RECT rw;
+ GetWindowRect(hdlg, &rw);
+ LONG dx = (rw.right - rw.left) - pHelp->width;
+ LONG dy = (rw.bottom - rw.top) - pHelp->height;
+ pHelp->width = rw.right - rw.left;
+ pHelp->height = rw.bottom - rw.top;
+
+ HDWP hdwp = BeginDeferWindowPos(3);
+ HWND hctrl;
+ if (hdwp && 0 != dx && NULL != (hctrl = GetDlgItem(hdlg, IDC_LBL_CAPTION)) && GetWindowRect(hctrl, &rw))
+ {
+ hdwp = DeferWindowPos(hdwp, hctrl, NULL, 0, 0,
+ (rw.right - rw.left) + dx, (rw.bottom - rw.top), SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOREDRAW);
+ InvalidateRect(hctrl, NULL, TRUE);
+ }
+ if (hdwp && (0 != dx || 0 != dy) &&
+ NULL != (hctrl = GetDlgItem(hdlg, IDC_EDT_TEXT)) && GetWindowRect(hctrl, &rw))
+ {
+ hdwp = DeferWindowPos(hdwp, hctrl, NULL, 0, 0,
+ (rw.right - rw.left) + dx, (rw.bottom - rw.top) + dy, SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOREDRAW);
+ }
+ if (hdwp && (0 != dx || 0 != dy) &&
+ NULL != (hctrl = GetDlgItem(hdlg, IDCANCEL)) && GetWindowRect(hctrl, &rw))
+ {
+ MapWindowPoints(HWND_DESKTOP, hdlg, (POINT*)&rw, 2);
+ hdwp = DeferWindowPos(hdwp, hctrl, NULL, rw.left + dx, rw.top + dy, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOREDRAW);
+ }
+ if (hdwp && EndDeferWindowPos(hdwp))
+ {
+ RedrawWindow(hdlg, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_INTERNALPAINT | RDW_ALLCHILDREN | RDW_UPDATENOW | RDW_ERASENOW);
+ }
+ }
+}*/
+
+static INT_PTR CALLBACK SimpleHelp_DialogProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG: return SimpleHlp_OnInitDialog(hdlg, (HWND)wParam, lParam);
+ case WM_DESTROY: SimpleHelp_OnDestroy(hdlg); break;
+ case WM_COMMAND: SimpleHlp_OnCommand(hdlg, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); break;
+ /*case WM_WINDOWPOSCHANGING: SimpleHlp_OnWindowPosChanging(hdlg, (WINDOWPOS*)lParam); return 0;
+ case WM_WINDOWPOSCHANGED: SimpleHlp_OnWindowPosChanged(hdlg, (WINDOWPOS*)lParam); return 0;*/
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/infoBox.cpp b/Src/Plugins/Library/ml_disc/infoBox.cpp
new file mode 100644
index 00000000..23ea2313
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/infoBox.cpp
@@ -0,0 +1,145 @@
+#include "main.h"
+#include ".\infoBox.h"
+
+
+MLInfoBox::MLInfoBox(void)
+{
+ oldWndProc = NULL;
+ m_hwnd = NULL;
+ bodyBrush = NULL;
+ headerBrush = NULL;
+ headerText[0] = 0;
+
+ SetColors(RGB(0,0,0), RGB(255,255,255), RGB(0,60,0));
+
+ SetRect(&rcBody, 0,0,0,0);
+
+ drawHeader = TRUE;
+ SetRect(&rcHeader, 0,0,0,20); // default height
+
+ headerFont = NULL;
+
+}
+MLInfoBox::~MLInfoBox(void)
+{
+ SetWindowLong(m_hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)oldWndProc);
+ oldWndProc = NULL;
+
+ if (headerBrush) DeleteObject(headerBrush);
+ headerBrush = NULL;
+
+ if (bodyBrush) DeleteObject(bodyBrush);
+ bodyBrush = NULL;
+
+ if (headerFont) DeleteObject(headerFont);
+ headerFont = NULL;
+
+}
+
+void MLInfoBox::SetColors(COLORREF bodyBG, COLORREF headerFG, COLORREF headerBG)
+{
+ this->bodyBG = bodyBG;
+ this->headerFG = headerFG;
+ this->headerBG = headerBG;
+
+ if (headerBrush) DeleteObject(headerBrush);
+ headerBrush = NULL;
+ headerBrush = CreateSolidBrush(headerBG);
+
+ if (bodyBrush) DeleteObject(bodyBrush);
+ bodyBrush = NULL;
+ bodyBrush = CreateSolidBrush(bodyBG);
+
+}
+
+void MLInfoBox::Init(HWND hwnd)
+{
+ m_hwnd = hwnd;
+
+ HDC hdc = GetDC(hwnd);
+ long lfHeight;
+ lfHeight = -MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+ headerFont = CreateFontW(lfHeight, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"Arial");
+ ReleaseDC(hwnd, hdc);
+
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONGX86)(LONG_PTR)this);
+ oldWndProc= (WNDPROC)(LONG_PTR)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)newWndProc);
+ RECT rc;
+ GetWindowRect(hwnd, &rc);
+ SetSize(rc.right - rc.left, rc.bottom - rc.top);
+}
+
+void MLInfoBox::SetSize(int cx, int cy)
+{
+ int offset = 0;
+ if (drawHeader)
+ {
+ SetRect(&rcHeader, 0,0, cx, rcHeader.bottom);
+ offset = rcHeader.bottom;
+ }
+ SetRect(&rcBody, 0, offset, cx, cy);
+}
+LRESULT CALLBACK MLInfoBox::newWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
+{
+ MLInfoBox *box = (MLInfoBox*)(LONG_PTR)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch(uMsg)
+ {
+ case WM_SIZE:
+ if (SIZE_MINIMIZED != wParam)
+ {
+ box->SetSize(LOWORD(lParam), HIWORD(lParam));
+ }
+ break;
+ case WM_ERASEBKGND:
+ {
+ HDC hdc = GetDC(hwndDlg);
+ SetTextColor(hdc, box->headerFG);
+ SetBkColor(hdc, box->headerBG);
+ RECT txtRect;
+ SetRect(&txtRect, box->rcHeader.left + 8, box->rcHeader.top + 2, box->rcHeader.right -2, box->rcHeader.bottom -2);
+ HFONT oldFont = (HFONT)SelectObject(hdc, box->headerFont);
+ GetWindowTextW(hwndDlg, box->headerText, CAPTION_LENGTH);
+ DrawTextW(hdc, box->headerText, -1, &txtRect, DT_VCENTER | DT_LEFT | DT_SINGLELINE);
+ SelectObject(hdc, oldFont);
+ ReleaseDC(hwndDlg, hdc);
+ }
+ return TRUE;
+
+
+ break;
+ case WM_PAINT:
+ {
+ PAINTSTRUCT pt;
+ HDC hdc = BeginPaint(hwndDlg, &pt);
+ RECT drawRect ;
+ if(box->drawHeader && IntersectRect(&drawRect, &box->rcHeader, &pt.rcPaint))
+ {
+ FillRect(hdc, &drawRect, box->headerBrush);
+
+ SetTextColor(hdc, box->headerFG);
+ SetBkColor(hdc, box->headerBG);
+ SetRect(&drawRect, box->rcHeader.left + 8, box->rcHeader.top + 2, box->rcHeader.right -2, box->rcHeader.bottom -2);
+ HFONT oldFont = (HFONT)SelectObject(hdc, box->headerFont);
+ GetWindowTextW(hwndDlg, box->headerText, CAPTION_LENGTH);
+ DrawTextW(hdc, box->headerText, -1, &drawRect, DT_VCENTER | DT_LEFT | DT_SINGLELINE);
+ SelectObject(hdc, oldFont);
+ ValidateRect(hwndDlg, &drawRect);
+ }
+
+
+ if(IntersectRect(&drawRect, &box->rcBody, &pt.rcPaint))
+ {
+ FillRect(hdc, &drawRect, box->bodyBrush);
+ ValidateRect(hwndDlg, &drawRect);
+ }
+
+ EndPaint(hwndDlg, &pt);
+ }
+ break;
+ }
+
+ return CallWindowProc(box->oldWndProc, hwndDlg, uMsg, wParam, lParam);
+}
+
+ \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/infoBox.h b/Src/Plugins/Library/ml_disc/infoBox.h
new file mode 100644
index 00000000..b945aab4
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/infoBox.h
@@ -0,0 +1,42 @@
+#ifndef NULLSOFT_ML_INFOBOX_HEADER
+#define NULLSOFT_ML_INFOBOX_HEADER
+
+#include <windows.h>
+
+#define CAPTION_LENGTH 64
+class MLInfoBox
+{
+public:
+ MLInfoBox(void);
+ ~MLInfoBox(void);
+
+public:
+
+ void SetColors(COLORREF bodyBG, COLORREF headerFG, COLORREF headerBG);
+ void Init(HWND hwnd);
+
+protected:
+ static LRESULT CALLBACK newWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
+ void SetSize(int cx, int cy);
+
+private:
+
+ HWND m_hwnd;
+ WNDPROC oldWndProc;
+
+ wchar_t headerText[CAPTION_LENGTH];
+ BOOL drawHeader;
+
+ COLORREF headerBG;
+ COLORREF headerFG;
+ COLORREF bodyBG;
+
+ HFONT headerFont;
+
+ HBRUSH headerBrush;
+ HBRUSH bodyBrush;
+ RECT rcBody;
+ RECT rcHeader;
+};
+
+#endif // NULLSOFT_ML_INFOBOX_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/main.cpp b/Src/Plugins/Library/ml_disc/main.cpp
new file mode 100644
index 00000000..6f39f2f3
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/main.cpp
@@ -0,0 +1,1273 @@
+#include "Main.h"
+#include "ReplayGain.h"
+#include "../nu/AutoChar.h"
+#include "../nu/MediaLibraryInterface.h"
+#include "./resource.h"
+#include "./settings.h"
+#include "./copyfiles.h"
+#include "../winamp/wa_ipc.h"
+//#include <primosdk.h>
+#include <shlwapi.h>
+#include <imapi.h>
+#include <imapierror.h>
+#include "../nu/ns_wc.h"
+#include <vector>
+#include <strsafe.h>
+
+#define VERSION_MAJOR 2
+#define VERSION_MINOR 0
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(x) (sizeof(x)/sizeof(*x))
+#endif
+
+typedef struct _LISTENER
+{
+ HWND hwnd;
+ UINT uMsg;
+ CHAR cLetter;
+} LISTENER;
+
+typedef struct _NAVITEMENUMPARAM
+{
+ NAVITEMENUMPROC callback;
+ LPARAM param;
+} NAVITEMENUMPARAM;
+
+typedef enum _BTNSTATE
+{
+ BTNSTATE_NORMAL = 0,
+ BTNSTATE_HILITED = 1,
+ BTNSTATE_PRESSED = 2,
+ BTNSTATE_DISABLED = 3,
+} BTNSTATE;
+
+#define ICON_SIZE_CX 14
+#define ICON_SIZE_CY 14
+
+#define NAVBUTTON_STATECHECK_DELAY 100
+static int Init();
+static void Quit();
+
+HNAVITEM hniMain = NULL;
+static LRESULT delay_ml_startup;
+static HMLIMGLST hmlilIcons = NULL;
+LARGE_INTEGER freq;
+
+#define NAVITEM_PREFIX L"_ml_disc_"
+#define NAVITEM_PREFIX_SIZE (sizeof(NAVITEM_PREFIX)/sizeof(wchar_t))
+
+#define NCS_EX_SHOWEJECT 0x0100
+
+C_Config *g_config, *g_view_metaconf = NULL;
+HMENU g_context_menus;
+
+prefsDlgRecW myPrefsItemCD = {0};
+INT_PTR imgIndex = 0;
+wchar_t randb[64] = {0};
+static wchar_t cdrip[64];
+
+static DWORD g_navStyle = NCS_FULLROWSELECT | NCS_SHOWICONS;
+static DWORD riphash = 0;
+
+static std::vector<LPARAM> driveList;
+
+static LISTENER activeListener = { NULL, 0, };
+static WNDPROC oldWinampWndProc = NULL;
+
+api_application *WASABI_API_APP = 0;
+api_stats *AGAVE_API_STATS = 0;
+
+// wasabi based services for localisation support
+api_language *WASABI_API_LNG = 0;
+HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
+
+static UINT uMsgBurnerNotify = 0;
+static UINT uMsgRipperNotify = 0;
+static UINT uMsgNavStyleUpdate = 0;
+static UINT uMsgCopyNotify = 0;
+
+static void CALLBACK Invoke_OnDriveManagerNotify(WORD wCode, INT_PTR param);
+static void Plugin_OnMLVisible(BOOL bVisible);
+void ShowHideRipBurnParent(void);
+
+
+
+static void DriveParam_RegisterDrive(DRIVE *drive)
+{
+ LPARAM param;
+ size_t index;
+
+ param = (LPARAM)drive;
+ if (NULL == param)
+ return;
+
+ index = driveList.size();
+ while(index--)
+ {
+ if(param == driveList[index])
+ return;
+ }
+
+ driveList.push_back(param);
+}
+
+static void DriveParam_UnregisterDrive(DRIVE *drive)
+{
+ LPARAM param;
+ size_t index;
+
+ param = (LPARAM)drive;
+ if (NULL == param)
+ return;
+
+ index = driveList.size();
+ while(index--)
+ {
+ if(param == driveList[index])
+ {
+ driveList.erase(driveList.begin() + index);
+ return;
+ }
+ }
+}
+
+static BOOL DriveParam_IsValid(LPARAM param)
+{
+ if (param > 0x0000FFFF)
+ {
+ size_t index = driveList.size();
+ while(index--)
+ {
+ if(param == driveList[index])
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+DRIVE *Plugin_GetDriveFromNavItem(HNAVITEM hItem)
+{
+ NAVITEM item;
+
+ if (!hItem) return NULL;
+
+ item.cbSize = sizeof(NAVITEM);
+ item.mask = NIMF_PARAM;
+ item.hItem = hItem;
+
+ return (MLNavItem_GetInfo(plugin.hwndLibraryParent, &item) && DriveParam_IsValid(item.lParam)) ?
+ (DRIVE*)item.lParam : NULL;
+}
+
+HNAVITEM Plugin_GetNavItemFromLetter(CHAR cLetter)
+{
+ NAVCTRLFINDPARAMS fp = {0};
+ wchar_t invariant[32] = {0};
+
+ if (S_OK == StringCchPrintfW(invariant, sizeof(invariant)/sizeof(wchar_t), L"%s%c", NAVITEM_PREFIX, cLetter))
+ {
+ fp.cchLength = -1;
+ fp.pszName = invariant;
+ fp.compFlags = NICF_INVARIANT;
+
+ return MLNavCtrl_FindItemByName(plugin.hwndLibraryParent, &fp);
+ }
+ return NULL;
+}
+
+static BOOL CALLBACK EnumerateNavItemsCB(HNAVITEM hItem, LPARAM param)
+{
+ DRIVE *pDrive = Plugin_GetDriveFromNavItem(hItem);
+ return (pDrive) ? ((NAVITEMENUMPARAM*)param)->callback(hItem, pDrive, ((NAVITEMENUMPARAM*)param)->param) : TRUE;
+}
+
+BOOL Plugin_EnumerateNavItems(NAVITEMENUMPROC callback, LPARAM param)
+{
+ NAVITEMENUMPARAM pluginenum;
+ NAVCTRLENUMPARAMS navenum;
+ if (!callback) return FALSE;
+
+ pluginenum.callback = callback;
+ pluginenum.param = param;
+
+ navenum.hItemStart = hniMain;
+ navenum.lParam = (LPARAM)&pluginenum;
+ navenum.enumProc = EnumerateNavItemsCB;
+
+ return MLNavCtrl_EnumItems(plugin.hwndLibraryParent, &navenum);
+}
+
+void Plugin_RegisterListener(HWND hwnd, UINT uMsg, CHAR cLetter)
+{
+ activeListener.hwnd = hwnd;
+ activeListener.uMsg = uMsg;
+ activeListener.cLetter = cLetter;
+}
+
+void Plugin_UnregisterListener(HWND hwnd)
+{
+ ZeroMemory(&activeListener, sizeof(LISTENER));
+}
+
+static BOOL CALLBACK EnumNavItems_OnUIChangeCB(HNAVITEM hItem, DRIVE *pDrive, LPARAM param)
+{
+ if (pDrive) pDrive->textSize = 0;
+ return TRUE;
+}
+
+static void UpdatedNavStyles(void)
+{
+ g_navStyle = MLNavCtrl_GetStyle(plugin.hwndLibraryParent);
+ if (0 != g_view_metaconf->ReadInt(TEXT("showeject"), 1)) g_navStyle |= NCS_EX_SHOWEJECT;
+}
+
+static BOOL CALLBACK EnumerateNavItemsRemoveCB(HNAVITEM hItem, DRIVE *pDrive, LPARAM param)
+{
+ if(pDrive)
+ {
+ MLNavCtrl_DeleteItem(plugin.hwndLibraryParent,hItem);
+ Plugin_EnumerateNavItems(EnumerateNavItemsRemoveCB, 0);
+ }
+ return TRUE;
+}
+
+static BOOL Plugin_QueryOkToQuit()
+{
+ CHAR szLetters[24] = {0};
+ INT c = DriveManager_GetDriveList(szLetters, ARRAYSIZE(szLetters));
+ while(c-- > 0)
+ {
+ INT msgId;
+ if (cdrip_isextracting(szLetters[c])) msgId = IDS_YOU_ARE_CURRENTLY_RIPPING_AUDIO_CD_MUST_CANCEL_TO_CLOSE_WINAMP;
+ else if (MLDisc_IsDiscCopying(szLetters[c])) msgId = IDS_YOU_ARE_CURRENTLY_COPYING_DATA_CD_MUST_CANCEL_TO_CLOSE_WINAMP;
+ else msgId = 0;
+ if (msgId)
+ {
+ wchar_t buffer[512] = {0};
+ StringCchPrintfW(buffer, 512, WASABI_API_LNGSTRINGW(msgId), szLetters[c]);
+ MessageBoxW(plugin.hwndWinampParent, buffer, WASABI_API_LNGSTRINGW(IDS_NOTIFICATION),
+ MB_OK | MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TOPMOST);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+LRESULT CALLBACK WinampWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if(uMsgNavStyleUpdate == uMsg)
+ {
+ if(!wParam)
+ {
+ UpdatedNavStyles();
+ Plugin_EnumerateNavItems(EnumNavItems_OnUIChangeCB, 0);
+ }
+ else
+ {
+ Plugin_EnumerateNavItems(EnumerateNavItemsRemoveCB, 0);
+ ShowHideRipBurnParent();
+ DriveManager_Uninitialize(0);
+ DriveManager_Initialize(Invoke_OnDriveManagerNotify, TRUE);
+ Plugin_OnMLVisible((BOOL)SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_IS_VISIBLE, 0));
+ }
+ }
+ else if (uMsgBurnerNotify == uMsg) // burner broadcast message LOWORD(wParam) = drive letter, lParam = (BOOL)bStarted. if bStarted = TRUE - burning started, otherwise burning finished
+ {
+ if (0 == HIWORD(wParam))
+ {
+ DriveManager_SetDriveMode((CHAR)LOWORD(wParam), (0 != lParam) ? DM_MODE_BURNING : DM_MODE_READY);
+ }
+ }
+ else if (uMsgRipperNotify == uMsg)
+ {
+ if (HIWORD(wParam)) // another instance of winamp quering
+ {
+ if (LOWORD(wParam) && cdrip_isextracting((CHAR)LOWORD(wParam))) SendNotifyMessage((HWND)lParam, uMsgRipperNotify, LOWORD(wParam), (LPARAM)TRUE);
+ else
+ {
+ CHAR cLetter;
+ cLetter = (CHAR)cdrip_isextracting(-1);
+ if (cLetter) SendNotifyMessage((HWND)lParam, uMsgRipperNotify, cLetter, (LPARAM)TRUE);
+ }
+ }
+ else
+ {
+ DriveManager_SetDriveMode((CHAR)LOWORD(wParam), (0 != lParam) ? DM_MODE_RIPPING : DM_MODE_READY);
+ }
+ }
+ else if (uMsgCopyNotify == uMsg)
+ {
+ if (HIWORD(wParam))
+ {
+ if (LOWORD(wParam) && MLDisc_IsDiscCopying((CHAR)LOWORD(wParam))) SendNotifyMessage((HWND)lParam, uMsgCopyNotify, LOWORD(wParam), (LPARAM)TRUE);
+ else
+ {
+ CHAR szLetters[24] = {0};
+ INT c = DriveManager_GetDriveList(szLetters, ARRAYSIZE(szLetters));
+ while(c-- > 0)
+ {
+ if (MLDisc_IsDiscCopying(szLetters[c]))
+ SendNotifyMessage((HWND)lParam, uMsgCopyNotify, szLetters[c], (LPARAM)TRUE);
+ }
+ }
+ }
+ else
+ {
+ DriveManager_SetDriveMode((CHAR)LOWORD(wParam), (0 != lParam) ? DM_MODE_COPYING : DM_MODE_READY);
+ }
+ }
+ else if (WM_WA_IPC == uMsg)
+ {
+ switch(lParam)
+ {
+ case IPC_SKIN_CHANGED:
+ case IPC_CB_RESETFONT:
+ UpdatedNavStyles();
+ Plugin_EnumerateNavItems(EnumNavItems_OnUIChangeCB, 0);
+ break;
+ case IPC_FILE_TAG_MAY_HAVE_UPDATED:
+ case IPC_FILE_TAG_MAY_HAVE_UPDATEDW:
+ if (activeListener.hwnd) SendMessageW(activeListener.hwnd, activeListener.uMsg, (WPARAM)lParam, (LPARAM)wParam);
+ break;
+ }
+ if(lParam == delay_ml_startup)
+ {
+ if(!wParam)
+ {
+ PostMessage(plugin.hwndWinampParent, WM_WA_IPC, 1, delay_ml_startup);
+ }
+ else if(wParam == 1)
+ {
+ // TODO: benski> temp-hack fix for now -- if (SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_IS_VISIBLE, 0))
+ {
+ DriveManager_Initialize(Invoke_OnDriveManagerNotify, TRUE);
+ MLDisc_InitializeCopyData();
+
+ DriveManager_Resume(TRUE);
+ SendNotifyMessage(HWND_BROADCAST, uMsgBurnerNotify, MAKEWPARAM(0, 0xffff), (LPARAM)plugin.hwndWinampParent);
+ SendNotifyMessage(HWND_BROADCAST, uMsgRipperNotify, MAKEWPARAM(0, 0xffff), (LPARAM)plugin.hwndWinampParent);
+ SendNotifyMessage(HWND_BROADCAST, uMsgCopyNotify, MAKEWPARAM(0, 0xffff), (LPARAM)plugin.hwndWinampParent);
+ }
+ }
+ }
+ }
+ return (oldWinampWndProc) ? CallWindowProcW(oldWinampWndProc, hwnd, uMsg, wParam, lParam) : DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
+static DM_UNITINFO2_PARAM unitinfo;
+static char szDesc[1024];
+static DWORD szTypes[64];
+
+static void CALLBACK FreeParam(DM_NOTIFY_PARAM *phdr)
+{
+ if (!phdr) return;
+ switch(phdr->opCode)
+ {
+ case DMOP_TITLE:
+ if (((DM_TITLE_PARAM*)phdr)->pszTitle) free(((DM_TITLE_PARAM*)phdr)->pszTitle);
+ break;
+ }
+ free(phdr);
+}
+static void RegisterIcons()
+{
+ MLIMAGELISTCREATE mlilCreate;
+ MLIMAGESOURCE mlis;
+ MLIMAGELISTITEM mlilItem;
+
+ if (hmlilIcons) return;
+
+ mlilCreate.cx = ICON_SIZE_CX;
+ mlilCreate.cy = ICON_SIZE_CY;
+ mlilCreate.cInitial = 5;
+ mlilCreate.cGrow = 1;
+ mlilCreate.cCacheSize = 3;
+ mlilCreate.flags = MLILC_COLOR24;
+
+ hmlilIcons = MLImageList_Create(plugin.hwndLibraryParent, &mlilCreate);
+ if (!hmlilIcons) return;
+
+ ZeroMemory(&mlilItem, sizeof(MLIMAGELISTITEM));
+ mlilItem.cbSize = sizeof(MLIMAGELISTITEM);
+ mlilItem.hmlil = hmlilIcons;
+ mlilItem.filterUID = MLIF_FILTER1_UID;
+ mlilItem.pmlImgSource = &mlis;
+
+ ZeroMemory(&mlis, sizeof(MLIMAGESOURCE));
+ mlis.cbSize = sizeof(MLIMAGESOURCE);
+ mlis.type = SRC_TYPE_PNG;
+ mlis.hInst = plugin.hDllInstance;
+
+ mlis.lpszName = MAKEINTRESOURCEW(IDB_NAVITEM_CDROM);
+ MLImageList_Add(plugin.hwndLibraryParent, &mlilItem);
+
+ mlis.lpszName = MAKEINTRESOURCEW(IDB_EJECT_NORMAL);
+ MLImageList_Add(plugin.hwndLibraryParent, &mlilItem);
+
+ mlis.lpszName = MAKEINTRESOURCEW(IDB_EJECT_HILITED);
+ MLImageList_Add(plugin.hwndLibraryParent, &mlilItem);
+
+ mlis.lpszName = MAKEINTRESOURCEW(IDB_EJECT_PRESSED);
+ MLImageList_Add(plugin.hwndLibraryParent, &mlilItem);
+
+ mlis.lpszName = MAKEINTRESOURCEW(IDB_EJECT_DISABLED);
+ MLImageList_Add(plugin.hwndLibraryParent, &mlilItem);
+
+}
+
+
+static BOOL UpdateTitle(CHAR cLetter, LPCWSTR pszTitle)
+{
+ HNAVITEM hItem;
+ NAVITEM item;
+ DRIVE *pDrive;
+
+ hItem = Plugin_GetNavItemFromLetter(cLetter);
+ if (!hItem) return FALSE;
+
+ pDrive = Plugin_GetDriveFromNavItem(hItem);
+ if (!pDrive) return FALSE;
+
+ if (S_OK != StringCchCopyW(pDrive->szTitle, sizeof(pDrive->szTitle)/sizeof(wchar_t), (pszTitle) ? pszTitle : L"")) return FALSE;
+
+ pDrive->textSize = 0;
+
+ item.cbSize = sizeof(NAVITEM);
+ item.mask = NIMF_TEXT;
+ item.hItem = hItem;
+ item.pszText = pDrive->szTitle;
+
+ return MLNavItem_SetInfo(plugin.hwndLibraryParent, &item);
+
+}
+static void QueryTitle(CHAR cLetter)
+{
+ DM_TITLE_PARAM *pdtp;
+
+ pdtp = (DM_TITLE_PARAM*)calloc(4, sizeof(DM_TITLE_PARAM));
+ if (pdtp)
+ {
+ pdtp->header.callback = (INT_PTR)Invoke_OnDriveManagerNotify;
+ pdtp->header.cLetter = cLetter;
+ pdtp->header.uMsg = NULL;
+ pdtp->header.fnFree = FreeParam;
+ pdtp->cchTitle = 256;
+ pdtp->pszTitle = (wchar_t*)calloc(pdtp->cchTitle, sizeof(wchar_t));
+
+ DriveManager_QueryTitle(pdtp);
+ }
+}
+static void Drive_OnAdded(CHAR cLetter)
+{
+ wchar_t szInvariant[32] = {0};
+
+ DRIVE *pDrive;
+ NAVINSERTSTRUCT nis = {0};
+ wchar_t szDriveType[32] = {0}, szDriveCap[64] = {0};
+
+ pDrive = (DRIVE*)calloc(1, sizeof(DRIVE));
+ if (!pDrive) return;
+
+ StringCchPrintfW(szInvariant, sizeof(szInvariant)/sizeof(wchar_t), L"%s%c", NAVITEM_PREFIX, cLetter);
+
+ pDrive->cLetter = cLetter;
+ pDrive->cMode = DriveManager_GetDriveMode(cLetter);
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_CD, szDriveType, sizeof(szDriveType)/sizeof(wchar_t));
+ WASABI_API_LNGSTRINGW_BUF(IDS_DRIVE_CAP, szDriveCap, sizeof(szDriveCap)/sizeof(wchar_t));
+ StringCchPrintfW(pDrive->szTitle, sizeof(pDrive->szTitle)/sizeof(wchar_t), L"%s %s (%C:)", szDriveType, szDriveCap, (WCHAR)cLetter);
+
+ if (NULL == hmlilIcons) RegisterIcons();
+
+ ZeroMemory(&nis, sizeof(NAVINSERTSTRUCT));
+ nis.hParent = hniMain;
+ nis.item.cbSize = sizeof(NAVITEM);
+ nis.item.pszText = pDrive->szTitle;
+ nis.item.pszInvariant = szInvariant;
+ nis.item.mask = NIMF_TEXT | NIMF_TEXTINVARIANT | NIMF_PARAM | NIMF_STYLE;
+ nis.item.style = NIS_CUSTOMDRAW | NIS_WANTSETCURSOR | NIS_WANTHITTEST;
+ nis.item.styleMask = nis.item.style;
+ nis.item.lParam = (LPARAM)pDrive;
+
+ if (NULL != MLNavCtrl_InsertItem(plugin.hwndLibraryParent, &nis))
+ {
+ DriveParam_RegisterDrive(pDrive);
+ }
+}
+
+static void Drive_OnChanged(CHAR cLetter)
+{
+ QueryTitle(cLetter);
+}
+
+static void Drive_OnRemoved(CHAR cLetter)
+{
+ HNAVITEM hItem;
+ hItem = Plugin_GetNavItemFromLetter(cLetter);
+ if (hItem) MLNavCtrl_DeleteItem(plugin.hwndLibraryParent, hItem);
+ if (riphash && (0xFF & (riphash >> 24)) == (UCHAR)cLetter) riphash = 0;
+}
+
+static void Drive_OnModeChanged(CHAR cLetter, CHAR cMode)
+{
+ HNAVITEM hItem;
+ DRIVE *pDrive;
+
+ hItem = Plugin_GetNavItemFromLetter(cLetter);
+ if (!hItem) return;
+
+ pDrive = Plugin_GetDriveFromNavItem(hItem);
+ if (pDrive)
+ {
+ NAVITEMINAVLIDATE inv;
+
+ pDrive->cMode = cMode;
+
+ inv.fErase = FALSE;
+ inv.hItem = hItem;
+ inv.prc = NULL;
+ MLNavItem_Invalidate(plugin.hwndLibraryParent, &inv);
+ }
+}
+
+static void Medium_OnAdded(CHAR cLetter)
+{
+ if (riphash && (0xFF & (riphash >> 24)) == (UCHAR)cLetter) riphash = 0;
+ QueryTitle(cLetter);
+}
+
+static void Medium_OnRemoved(CHAR cLetter)
+{
+ if (riphash && (0xFF & (riphash >> 24)) == (UCHAR)cLetter) riphash = 0;
+ QueryTitle(cLetter);
+}
+
+static void OnDriveMangerOpCompleted(DM_NOTIFY_PARAM *phdr)
+{
+ switch(phdr->opCode)
+ {
+ case DMOP_TITLE:
+ if (S_OK == phdr->result) UpdateTitle(phdr->cLetter, ((DM_TITLE_PARAM*)phdr)->pszTitle);
+ break;
+ }
+}
+
+static void CALLBACK OnDriveManagerNotify(ULONG_PTR param)
+{
+ WORD code;
+ CHAR cLetter;
+
+ code = LOWORD(param);
+ cLetter = (CHAR)(0xFF & HIWORD(param));
+
+ switch(code)
+ {
+ case DMW_DRIVEADDED: Drive_OnAdded(cLetter); break;
+ case DMW_DRIVEREMOVED: Drive_OnRemoved(cLetter); break;
+ case DMW_DRIVECHANGED: Drive_OnChanged(cLetter); break;
+ case DMW_MEDIUMARRIVED: Medium_OnAdded(cLetter); break;
+ case DMW_MEDIUMREMOVED: Medium_OnRemoved(cLetter); break;
+ case DMW_MODECHANGED: Drive_OnModeChanged(cLetter, ((CHAR)(HIWORD(param)>>8))); break;
+
+ }
+ if (activeListener.hwnd && (0 == activeListener.cLetter || cLetter == activeListener.cLetter))
+ PostMessageW(activeListener.hwnd, activeListener.uMsg, (WPARAM)code, (LPARAM)HIWORD(param));
+}
+
+static void CALLBACK Invoke_OnDriveManagerNotify(WORD wCode, INT_PTR param)
+{
+ switch(wCode)
+ {
+ case DMW_DRIVEADDED:
+ case DMW_DRIVEREMOVED:
+ case DMW_DRIVECHANGED:
+ case DMW_MEDIUMARRIVED:
+ case DMW_MEDIUMREMOVED:
+ case DMW_MODECHANGED:
+ if (GetCurrentThreadId() != GetWindowThreadProcessId(plugin.hwndLibraryParent, NULL))
+ {
+ HANDLE htWA = (WASABI_API_APP) ? WASABI_API_APP->main_getMainThreadHandle() : NULL;
+ if (htWA)
+ {
+ QueueUserAPC(OnDriveManagerNotify, htWA, MAKELONG(wCode, (WORD)(param)));
+ CloseHandle(htWA);
+ }
+ }
+ else OnDriveManagerNotify(MAKELONG(wCode, (WORD)(param)));
+ break;
+ case DMW_OPCOMPLETED: OnDriveMangerOpCompleted((DM_NOTIFY_PARAM*)param); break;
+ }
+}
+
+void ShowHideRipBurnParent(void)
+{
+ BOOL bVal;
+ if (S_OK == Settings_GetBool(C_GLOBAL, GF_SHOWPARENT, &bVal) && bVal)
+ {
+ if(!hniMain)
+ {
+ NAVINSERTSTRUCT nis;
+ ZeroMemory(&nis, sizeof(NAVITEM));
+ nis.item.cbSize = sizeof(NAVITEM);
+ nis.item.pszText = WASABI_API_LNGSTRINGW_BUF(IDS_RIP_AND_BURN, randb,64);
+ nis.item.pszInvariant = NAVITEM_PREFIX L"main";
+ nis.item.mask = NIMF_TEXT | NIMF_STYLE | NIMF_TEXTINVARIANT | NIMF_PARAM;
+ nis.item.style = NIS_HASCHILDREN;
+ nis.item.styleMask = nis.item.style;
+ nis.item.lParam = 0L;
+ hniMain = MLNavCtrl_InsertItem(plugin.hwndLibraryParent, &nis);
+ }
+ }
+ else
+ {
+ if(hniMain)
+ {
+ MLNavCtrl_DeleteItem(plugin.hwndLibraryParent,hniMain);
+ }
+ hniMain = NULL;
+ }
+}
+
+int Init()
+{
+ QueryPerformanceFrequency(&freq);
+
+ // get the Application service
+ waServiceFactory *sf = plugin.service->service_getServiceByGuid(applicationApiServiceGuid);
+ if (sf) WASABI_API_APP = (api_application *)sf->getInterface();
+
+ // loader so that we can get the localisation service api for use
+ sf = plugin.service->service_getServiceByGuid(languageApiGUID);
+ if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
+
+ sf = plugin.service->service_getServiceByGuid(AnonymousStatsGUID);
+ if (sf) AGAVE_API_STATS = reinterpret_cast<api_stats*>(sf->getInterface());
+
+ // need to have this initialised before we try to do anything with localisation features
+ WASABI_API_START_LANG(plugin.hDllInstance,MlDiscLangGUID);
+
+ mediaLibrary.library = plugin.hwndLibraryParent;
+ mediaLibrary.winamp = plugin.hwndWinampParent;
+ mediaLibrary.instance = plugin.hDllInstance;
+
+ static wchar_t szDescription[256];
+ StringCchPrintf(szDescription, ARRAYSIZE(szDescription),
+ WASABI_API_LNGSTRINGW(IDS_NULLSOFT_RIP_AND_BURN), VERSION_MAJOR, VERSION_MINOR);
+ plugin.description = (char*)szDescription;
+
+ // add to Winamp preferences
+ myPrefsItemCD.dlgID = IDD_PREFSCDRIPFR;
+ myPrefsItemCD.name = WASABI_API_LNGSTRINGW_BUF(IDS_CD_RIPPING,cdrip,64);
+ myPrefsItemCD.proc = (void*)CDRipPrefsProc;
+ myPrefsItemCD.hInst = WASABI_API_LNG_HINST;
+ myPrefsItemCD.where = 6; // media library
+ SENDWAIPC(plugin.hwndWinampParent, IPC_ADD_PREFS_DLGW, (WPARAM)&myPrefsItemCD);
+
+ wchar_t szIniFile[MAX_PATH],
+ *INI_DIR = (wchar_t*)SENDWAIPC(plugin.hwndWinampParent, IPC_GETINIDIRECTORYW, 0);
+
+ PathCombine(szIniFile, INI_DIR, TEXT("Plugins\\gen_ml.ini"));
+ g_config = new C_Config(szIniFile);
+
+ PathCombine(szIniFile, INI_DIR, TEXT("Plugins\\ml\\cdrom.vmd"));
+ g_view_metaconf = new C_Config(szIniFile);
+
+ g_context_menus = WASABI_API_LOADMENU(IDR_CONTEXTMENUS);
+
+ oldWinampWndProc = (WNDPROC)(LONG_PTR)SetWindowLongPtrW(plugin.hwndWinampParent, GWLP_WNDPROC, (LONG)(LONG_PTR)WinampWndProc);
+
+ if (!uMsgBurnerNotify) uMsgBurnerNotify = RegisterWindowMessageA("WABURNER_BROADCAST_MSG");
+ if (!uMsgRipperNotify) uMsgRipperNotify = RegisterWindowMessageA("WARIPPER_BROADCAST_MSG");
+ if (!uMsgCopyNotify) uMsgCopyNotify = RegisterWindowMessageA("WACOPY_BROADCAST_MSG");
+ if (!uMsgNavStyleUpdate) uMsgNavStyleUpdate = RegisterWindowMessageW(L"ripburn_nav_update");
+
+ UpdatedNavStyles();
+ ShowHideRipBurnParent();
+
+ delay_ml_startup = SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"ml_disc_delay", IPC_REGISTER_WINAMP_IPCMESSAGE);
+ PostMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, delay_ml_startup);
+ return ML_INIT_SUCCESS;
+}
+
+void Quit()
+{
+ DriveManager_Uninitialize(4000); // allow to wait for 4 sec.
+ MLDisc_ReleaseCopyData();
+ delete(g_view_metaconf);
+ g_view_metaconf = 0;
+
+ delete(g_config);
+ g_config = NULL;
+
+ if (rgThread)
+ {
+ QueueUserAPC(QuitThread, rgThread, 0);
+ WaitForSingleObject(rgThread, INFINITE);
+ CloseHandle(rgThread);
+ rgThread = 0;
+ }
+
+ waServiceFactory *sf = plugin.service->service_getServiceByGuid(AnonymousStatsGUID);
+ if (sf) sf->releaseInterface(AGAVE_API_STATS);
+}
+
+int getFileInfo(const char *filename, const char *metadata, char *dest, int len)
+{
+ dest[0] = 0;
+ extendedFileInfoStruct efis = { filename, metadata, dest, len, };
+ return (int)(INT_PTR)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM) & efis, IPC_GET_EXTENDED_FILE_INFO); //will return 1 if wa2 supports this IPC call
+}
+
+int getFileInfoW(const wchar_t *filename, const wchar_t *metadata, wchar_t *dest, int len)
+{
+ if (dest && len)
+ dest[0] = 0;
+ extendedFileInfoStructW efis = { filename, metadata, dest, len, };
+ return (int)(INT_PTR)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&efis, IPC_GET_EXTENDED_FILE_INFOW); //will return 1 if wa2 supports this IPC call
+}
+
+
+void Plugin_ShowRippingPreferences(void)
+{
+ SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&myPrefsItemCD, IPC_OPENPREFSTOPAGE);
+}
+BOOL Plugin_IsExtractScheduled(CHAR cLetter)
+{
+ BOOL result;
+ if (riphash && (0xFF & (riphash >> 24)) == (UCHAR)cLetter)
+ {
+ DWORD mediumSN;
+ char devname[] = "X:\\";
+ devname[0] = cLetter;
+ result = (GetVolumeInformationA(devname, NULL, 0, &mediumSN, NULL, NULL, NULL, 0) && (0x00FFFFFF & riphash) == mediumSN);
+ riphash = 0;
+ }
+ else result = FALSE;
+ return result;
+}
+
+
+static int Root_OnContextMenu(HNAVITEM hItem, HWND hHost, POINTS pts)
+{
+ HMENU hMenu = GetSubMenu(g_context_menus, 7);
+ if (!hMenu) return 0;
+
+ POINT pt;
+ POINTSTOPOINT(pt, pts);
+ if (-1 == pt.x || -1 == pt.y)
+ {
+ NAVITEMGETRECT itemRect;
+ itemRect.fItem = FALSE;
+ itemRect.hItem = hItem;
+ if (MLNavItem_GetRect(plugin.hwndLibraryParent, &itemRect))
+ {
+ MapWindowPoints(hHost, HWND_DESKTOP, (POINT*)&itemRect.rc, 2);
+ pt.x = itemRect.rc.left + 2;
+ pt.y = itemRect.rc.top + 2;
+ }
+ }
+
+ int r = Menu_TrackPopup(plugin.hwndLibraryParent, hMenu,
+ TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_NONOTIFY,
+ pt.x, pt.y, hHost, NULL);
+ switch (r)
+ {
+ case ID_NAVIGATION_PREFERENCES: Plugin_ShowRippingPreferences(); return 1;
+ case ID_NAVIGATION_HELP: SENDWAIPC(plugin.hwndWinampParent, IPC_OPEN_URL, L"https://help.winamp.com/hc/articles/8111574760468-CD-Ripping-with-Winamp"); return 1;
+ break;
+ }
+ return 0;
+}
+
+static int Plugin_OnContextMenu(HNAVITEM hItem, HWND hHost, POINTS pts, CHAR cLetter)
+{
+ HMENU hMenu = GetSubMenu(g_context_menus, 2);
+ if (!hMenu) return 0;
+
+ MENUITEMINFO mii = { sizeof(MENUITEMINFO), };
+ mii.fMask = MIIM_STATE;
+ if (GetMenuItemInfo(hMenu, ID_CDROMMENU_EJECTCD, FALSE, &mii))
+ {
+ mii.fState &= ~(MFS_ENABLED | MFS_DISABLED);
+ mii.fState |= ((DM_MODE_READY == DriveManager_GetDriveMode(cLetter)) ? MFS_ENABLED : MFS_DISABLED);
+ SetMenuItemInfo(hMenu, ID_CDROMMENU_EJECTCD, FALSE, &mii);
+ }
+
+ POINT pt;
+ POINTSTOPOINT(pt, pts);
+ if (-1 == pt.x || -1 == pt.y)
+ {
+ NAVITEMGETRECT itemRect;
+ itemRect.fItem = FALSE;
+ itemRect.hItem = hItem;
+ if (MLNavItem_GetRect(plugin.hwndLibraryParent, &itemRect))
+ {
+ MapWindowPoints(hHost, HWND_DESKTOP, (POINT*)&itemRect.rc, 2);
+ pt.x = itemRect.rc.left + 2;
+ pt.y = itemRect.rc.top + 2;
+ }
+ }
+
+ int r = Menu_TrackPopup(plugin.hwndLibraryParent, hMenu,
+ TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_NONOTIFY,
+ pt.x, pt.y, hHost, NULL);
+ switch (r)
+ {
+ case ID_CDROMMENU_EXTRACT_CONFIGURE: Plugin_ShowRippingPreferences(); return 1;
+ case ID_CDROMMENU_EXTRACT_HELP: SENDWAIPC(plugin.hwndWinampParent, IPC_OPEN_URL, L"https://help.winamp.com/hc/articles/8111574760468-CD-Ripping-with-Winamp"); return 1;
+ case ID_CDROMMENU_PLAYALL:
+ case ID_CDROMMENU_ENQUEUEALL:
+ {
+ int enq = r == ID_CDROMMENU_ENQUEUEALL;
+ itemRecordList obj = {0, };
+ saveCDToItemRecordList(cLetter, &obj, NULL);
+ mlSendToWinampStruct p;
+ p.type = ML_TYPE_CDTRACKS;
+ p.enqueue = enq | 2;
+ p.data = &obj;
+ SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_SENDTOWINAMP, (WPARAM)&p);
+ freeRecordList(&obj);
+ }
+ break;
+ case ID_CDROMMENU_EXTRACT_EXTRACTALL:
+ riphash = 0;
+ if (hItem)
+ {
+ if (hItem == MLNavCtrl_GetSelection(plugin.hwndLibraryParent))
+ {
+ HWND hwnd = (HWND)SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_GETCURRENTVIEW, 0);
+ if (hwnd && SendMessageW(hwnd, WM_EXTRACTDISC, cLetter, 0)) break;
+ }
+
+ char devname[] = "X:\\";
+ devname[0] = cLetter;
+ if (!GetVolumeInformationA(devname, NULL, 0, &riphash, NULL, NULL, NULL, 0)) riphash = 0;
+ if (riphash) riphash = ((0x00FFFFFF & riphash) | (cLetter << 24));
+ MLNavItem_Select(plugin.hwndLibraryParent, hItem);
+ }
+ break;
+ case ID_CDROMMENU_EJECTCD:
+ {
+ CHAR cMode;
+ cMode = DriveManager_GetDriveMode(cLetter);
+ if (DM_MODE_READY != cMode)
+ {
+ wchar_t title[32] = {0};
+ MessageBox(plugin.hwndLibraryParent,
+ WASABI_API_LNGSTRINGW((DM_MODE_RIPPING == cMode) ? IDS_ERROR_CD_RIP_IN_PROGRESS : IDS_ERROR_CD_BURN_IN_PROGRESS),
+ WASABI_API_LNGSTRINGW_BUF(IDS_CD_EJECT,title,32), 0);
+ return FALSE;
+ }
+ else DriveManager_Eject(cLetter, DM_EJECT_REMOVE);
+ }
+ break;
+ }
+ Sleep(100);
+ MSG msg;
+ while (PeekMessageW(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)); //eat return
+ return 0;
+}
+
+static DWORD resumeTick = 0; // this is cheating
+static void Plugin_OnMLVisible(BOOL bVisible)
+{
+ if (bVisible)
+ {
+ DriveManager_Resume(TRUE);
+ resumeTick = GetTickCount();
+ SendNotifyMessage(HWND_BROADCAST, uMsgBurnerNotify, MAKEWPARAM(0, 0xffff), (LPARAM)plugin.hwndWinampParent);
+ SendNotifyMessage(HWND_BROADCAST, uMsgRipperNotify, MAKEWPARAM(0, 0xffff), (LPARAM)plugin.hwndWinampParent);
+ SendNotifyMessage(HWND_BROADCAST, uMsgCopyNotify, MAKEWPARAM(0, 0xffff), (LPARAM)plugin.hwndWinampParent);
+ return;
+ }
+ else DriveManager_Suspend();
+}
+
+static HWND Plugin_OnViewCreate(HNAVITEM hItem, HWND hwndParent)
+{
+ if (hItem == hniMain)
+ {
+ return WASABI_API_CREATEDIALOGW(IDD_VIEW_RIPBURN, hwndParent, view_ripburnDialogProc);
+ }
+ else
+ {
+ DRIVE *pDrive = Plugin_GetDriveFromNavItem(hItem);
+ if (pDrive) return CreateContainerWindow(hwndParent, pDrive->cLetter, ((GetTickCount() - resumeTick) > 100));
+ resumeTick = 0;
+ }
+ return NULL;
+}
+static BOOL Plugin_OnNavItemDelete(HNAVITEM hItem)
+{
+ DRIVE *pDrive = Plugin_GetDriveFromNavItem(hItem);
+ if (!pDrive) return FALSE;
+ DriveParam_UnregisterDrive(pDrive);
+ free(pDrive);
+ return TRUE;
+}
+
+static BOOL Plugin_OnNavItemClick(HNAVITEM hItem, UINT nAction, HWND hwndParent)
+{
+ return FALSE;
+}
+
+static INT Plugin_OnNavCustomDraw(HNAVITEM hItem, NAVITEMDRAW *pnicd, LPARAM lParam)
+{
+ static INT indent = 0;
+ DRIVE *pDrive;
+
+ if (FALSE == DriveParam_IsValid(lParam))
+ return FALSE;
+
+ pDrive = (DRIVE*)lParam;
+
+ if (0 == indent) indent = MLNavCtrl_GetIndent(plugin.hwndLibraryParent);
+ switch(pnicd->drawStage)
+ {
+ case NIDS_PREPAINT:
+ if (pnicd->prc->bottom > 0 && pnicd->prc->bottom > pnicd->prc->top)
+ {
+ HIMAGELIST himl;
+ INT realIndex, l, t, r;
+ MLIMAGELISTREALINDEX mlilRealIndex;
+
+ himl = MLImageList_GetRealList(plugin.hwndLibraryParent, hmlilIcons);
+
+ mlilRealIndex.cbSize = sizeof(MLIMAGELISTREALINDEX);
+ mlilRealIndex.hmlil = hmlilIcons;
+ mlilRealIndex.rgbBk = GetBkColor(pnicd->hdc);
+ mlilRealIndex.rgbFg = GetTextColor(pnicd->hdc);
+
+ t = pnicd->prc->top + (pnicd->prc->bottom - pnicd->prc->top - ICON_SIZE_CY)/2;
+ l = pnicd->prc->left + (indent*pnicd->iLevel) + 3;
+ r = pnicd->prc->right;
+
+ mlilRealIndex.mlilIndex = 0;
+ realIndex = ((NCS_SHOWICONS & g_navStyle) && himl && l < pnicd->prc->right) ?
+ MLImageList_GetRealIndex(plugin.hwndLibraryParent, &mlilRealIndex) : -1;
+ if (-1 != realIndex) // draw icon
+ {
+ if (ImageList_Draw(himl, realIndex, pnicd->hdc, l, t, ILD_NORMAL))
+ {
+ ExcludeClipRect(pnicd->hdc, l, t, l + ICON_SIZE_CX, t + ICON_SIZE_CY);
+ l += (ICON_SIZE_CX + 5);
+ }
+ }
+
+ pDrive->bEjectVisible = FALSE;
+ mlilRealIndex.mlilIndex = 1 + ((DM_MODE_READY == pDrive->cMode) ? pDrive->nBtnState : BTNSTATE_DISABLED);
+ realIndex = ((NCS_EX_SHOWEJECT & g_navStyle) && himl && (r - l) > (24 + 6 + ICON_SIZE_CX)) ?
+ MLImageList_GetRealIndex(plugin.hwndLibraryParent, &mlilRealIndex) : -1;
+ if (-1 != realIndex)
+ {
+ if (ImageList_Draw(himl, realIndex, pnicd->hdc, r - (ICON_SIZE_CX + 2), t, ILD_NORMAL))
+ {
+ r -= (ICON_SIZE_CX + 2);
+ ExcludeClipRect(pnicd->hdc, r, t, r + ICON_SIZE_CX, t + ICON_SIZE_CY);
+ r -= 4;
+ pDrive->bEjectVisible = TRUE;
+ }
+ }
+
+ if (*pDrive->szTitle && l < r)
+ {
+ RECT rt;
+ INT textH, textW;
+ COLORREF rgbOld(0), rgbBkOld(0);
+
+ if (!pDrive->textSize || (pDrive->textOrigWidth > r-l-3 && pDrive->itemWidth > (pnicd->prc->right - pnicd->prc->left)) ||
+ (LOWORD(pDrive->textSize) != pDrive->textOrigWidth && pDrive->itemWidth < (pnicd->prc->right - pnicd->prc->left)))
+ {
+ NAVITEM item;
+ item.cbSize = sizeof(NAVITEM);
+ item.mask = NIMF_TEXT;
+ item.hItem = hItem;
+ item.cchTextMax = sizeof(pDrive->szTitle)/sizeof(wchar_t);
+ item.pszText = pDrive->szTitle;
+ MLNavItem_GetInfo(plugin.hwndLibraryParent, &item);
+ {
+ if (pDrive->szTitle != item.pszText)
+ {
+ StringCchCopyW(pDrive->szTitle, sizeof(pDrive->szTitle)/sizeof(wchar_t), item.pszText);
+ }
+ if (!pDrive->textSize)
+ {
+ SetRect(&rt, 0, 0, 1, 1);
+ DrawTextW(pnicd->hdc, pDrive->szTitle, -1, &rt, DT_SINGLELINE | DT_CALCRECT);
+ pDrive->textOrigWidth = rt.right - rt.left;
+ }
+ SetRect(&rt, 0, 0, r - l - 3, 1);
+ textH = DrawTextW(pnicd->hdc, pDrive->szTitle, -1, &rt, DT_SINGLELINE|DT_CALCRECT|DT_END_ELLIPSIS|DT_MODIFYSTRING);
+ textW = rt.right - rt.left;
+ pDrive->textSize = (DWORD)MAKELONG(textW, textH);
+ }
+ }
+ else
+ {
+ textW = LOWORD(pDrive->textSize);
+ textH = HIWORD(pDrive->textSize);
+ }
+
+ if (0 == (NCS_FULLROWSELECT & g_navStyle) && ((NIS_SELECTED | NIS_DROPHILITED) & pnicd->itemState))
+ {
+ rgbOld = SetTextColor(pnicd->hdc, pnicd->clrText);
+ rgbBkOld = SetBkColor(pnicd->hdc, pnicd->clrTextBk);
+ }
+
+ if (r > (l + textW + 7)) r = l + textW + 7;
+
+ SetRect(&rt, l, pnicd->prc->top, r, pnicd->prc->bottom);
+
+ t = pnicd->prc->top + (pnicd->prc->bottom - pnicd->prc->top - textH)/2;
+ ExtTextOutW(pnicd->hdc, rt.left + 2, t, ETO_CLIPPED | ETO_OPAQUE, &rt, pDrive->szTitle, lstrlenW(pDrive->szTitle), 0);
+ if (0 == (NCS_FULLROWSELECT & g_navStyle) && (NIS_FOCUSED & pnicd->itemState) &&
+ 0 == (0x1 /*UISF_HIDEFOCUS*/ & (INT)SendMessageW(MLNavCtrl_GetHWND(plugin.hwndLibraryParent), 0x129 /*WM_QUERYUISTATE*/, 0, 0L)))
+ {
+ DrawFocusRect(pnicd->hdc, &rt);
+ }
+ ExcludeClipRect(pnicd->hdc, rt.left, rt.top, rt.right, rt.bottom);
+
+
+ if (0 == (NCS_FULLROWSELECT & g_navStyle) && ((NIS_SELECTED | NIS_DROPHILITED) & pnicd->itemState))
+ {
+ if (rgbOld != pnicd->clrText) SetTextColor(pnicd->hdc, rgbOld);
+ if (rgbBkOld != pnicd->clrTextBk) SetBkColor(pnicd->hdc, rgbBkOld);
+ }
+ }
+
+ pDrive->itemWidth = (WORD)(pnicd->prc->right - pnicd->prc->left);
+
+ if (NCS_FULLROWSELECT & g_navStyle)
+ {
+ ExtTextOutW(pnicd->hdc, 0, 0, ETO_OPAQUE, pnicd->prc, L"", 0, 0);
+ return NICDRF_SKIPDEFAULT;
+ }
+ else ExcludeClipRect(pnicd->hdc, pnicd->prc->left, pnicd->prc->top, pnicd->prc->right, pnicd->prc->bottom);
+ }
+ break;
+ case NIDS_POSTPAINT:
+ break;
+ }
+ return NICDRF_DODEFAULT;
+}
+
+static HNAVITEM hItemActive = NULL;
+
+static BOOL GetEjectBtnRect(HNAVITEM hItem, RECT *prc)
+{
+ NAVITEMGETRECT navRect;
+ navRect.fItem = FALSE;
+ navRect.hItem = hItem;
+
+ if (!hItem || !prc || !MLNavItem_GetRect(plugin.hwndLibraryParent, &navRect)) return FALSE;
+
+ navRect.rc.right -= 2;
+ navRect.rc.left = navRect.rc.right - ICON_SIZE_CX;
+ navRect.rc.top += (navRect.rc.bottom - navRect.rc.top - ICON_SIZE_CY)/2;
+ navRect.rc.bottom = navRect.rc.top + ICON_SIZE_CY;
+
+ CopyRect(prc, &navRect.rc);
+ return TRUE;
+}
+
+static INT_PTR Plugin_OnNavHitTest(HNAVITEM hItem, NAVHITTEST *pnavHitTest, LPARAM lParam)
+{
+ DRIVE *pDrive;
+
+ if (FALSE == DriveParam_IsValid(lParam))
+ return FALSE;
+
+ pDrive = (DRIVE*)lParam;
+
+ if ((NAVHT_ONITEMRIGHT | NAVHT_ONITEM) & pnavHitTest->flags)
+ {
+ RECT rb;
+
+ if (pDrive->bEjectVisible && GetEjectBtnRect(hItem, &rb) &&
+ pnavHitTest->pt.x >= rb.left && pnavHitTest->pt.x <= rb.right &&
+ pnavHitTest->pt.y >= rb.top && pnavHitTest->pt.y <= rb.bottom)
+ {
+ pnavHitTest->flags = NAVHT_NOWHERE;
+ pnavHitTest->hItem = NULL;
+ }
+ }
+ return 1;
+}
+
+static void CALLBACK NavButton_TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ DRIVE *pDrive;
+ POINT pt;
+ RECT rb;
+
+ pDrive = (hItemActive) ? Plugin_GetDriveFromNavItem(hItemActive) : NULL;
+ if (!pDrive || (BYTE)BTNSTATE_NORMAL == pDrive->nBtnState || !pDrive->bEjectVisible || !GetEjectBtnRect(hItemActive, &rb))
+ {
+ KillTimer(NULL, idEvent);
+ return;
+ }
+
+ GetCursorPos(&pt);
+ MapWindowPoints(HWND_DESKTOP, MLNavCtrl_GetHWND(plugin.hwndLibraryParent), &pt, 1);
+
+
+ if (pt.x < rb.left || pt.x > rb.right || pt.y < rb.top || pt.y > rb.bottom)
+ {
+ NAVITEMINAVLIDATE inv;
+
+ KillTimer(NULL, idEvent);
+
+ inv.fErase = FALSE;
+ inv.hItem = hItemActive;
+ inv.prc = &rb;
+
+ hItemActive = NULL;
+ pDrive->nBtnState = BTNSTATE_NORMAL;
+
+ MLNavItem_Invalidate(plugin.hwndLibraryParent, &inv);
+ }
+}
+
+static INT_PTR Plugin_OnNavSetCursor(HNAVITEM hItem, LPARAM lParam)
+{
+ POINT pt;
+ DRIVE *pDrive;
+ BYTE state;
+ RECT rb;
+
+ if (FALSE == DriveParam_IsValid(lParam))
+ return FALSE;
+
+ pDrive = (DRIVE*)lParam;
+
+ if (DM_MODE_READY != pDrive->cMode || !pDrive->bEjectVisible || !GetEjectBtnRect(hItem, &rb)) return -1;
+
+ GetCursorPos(&pt);
+
+ MapWindowPoints(HWND_DESKTOP, MLNavCtrl_GetHWND(plugin.hwndLibraryParent), &pt, 1);
+ if (pt.x >= rb.left && pt.x <= rb.right && pt.y >= rb.top && pt.y <= rb.bottom)
+ {
+ state = (BYTE)((0x8000 & GetAsyncKeyState( GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON)) ?
+ BTNSTATE_PRESSED : BTNSTATE_HILITED);
+ }
+ else state = BTNSTATE_NORMAL;
+
+ if (pDrive->nBtnState != state)
+ {
+
+ NAVITEMINAVLIDATE inv;
+
+ if ((BYTE)BTNSTATE_PRESSED == pDrive->nBtnState && BTNSTATE_HILITED == state)
+ {
+ DriveManager_Eject(pDrive->cLetter, DM_EJECT_CHANGE);
+ }
+
+ if (pDrive->timerId)
+ {
+ KillTimer(NULL, pDrive->timerId);
+ pDrive->timerId = 0;
+ }
+ if (hItemActive)
+ {
+ DRIVE *pDriveOld = Plugin_GetDriveFromNavItem(hItemActive);
+ if (pDriveOld)
+ {
+ RECT rb2;
+ if (pDriveOld->timerId)
+ {
+ KillTimer(NULL, pDriveOld->timerId);
+ pDriveOld->timerId = NULL;
+ }
+ if ((BYTE)BTNSTATE_NORMAL != pDriveOld->nBtnState && GetEjectBtnRect(hItemActive, &rb2))
+ {
+ pDriveOld->nBtnState = BTNSTATE_NORMAL;
+ inv.fErase = FALSE;
+ inv.hItem = hItemActive;
+ inv.prc = &rb2;
+ MLNavItem_Invalidate(plugin.hwndLibraryParent, &inv);
+ }
+ hItemActive = NULL;
+ }
+ }
+
+ if (BTNSTATE_NORMAL != state)
+ {
+ hItemActive = hItem;
+ pDrive->timerId = SetTimer(NULL, 0, NAVBUTTON_STATECHECK_DELAY, NavButton_TimerProc);
+ }
+
+ pDrive->nBtnState = state;
+
+ inv.fErase = FALSE;
+ inv.hItem = hItem;
+ inv.prc = &rb;
+ MLNavItem_Invalidate(plugin.hwndLibraryParent, &inv);
+
+ }
+
+
+ return -1;
+}
+
+static BOOL Plugin_OnConfig(void)
+{
+ SendMessageW(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&myPrefsItemCD, IPC_OPENPREFSTOPAGE);
+ return TRUE;
+}
+
+static INT_PTR pluginMessageProc(int msg, INT_PTR param1, INT_PTR param2, INT_PTR param3)
+{
+ HNAVITEM hItem;
+
+ if (msg >= ML_MSG_TREE_BEGIN && msg <= ML_MSG_TREE_END)
+ {
+ hItem = (msg < ML_MSG_NAVIGATION_FIRST) ? MLNavCtrl_FindItemById(plugin.hwndLibraryParent, param1) : (HNAVITEM)param1;
+ if (!hItem) return 0;
+ } else hItem = NULL;
+
+ switch (msg)
+ {
+ case ML_MSG_TREE_ONCREATEVIEW: return (INT_PTR)Plugin_OnViewCreate(hItem, (HWND)param2);
+ case ML_MSG_NAVIGATION_ONDELETE: return (INT_PTR)Plugin_OnNavItemDelete(hItem);
+ case ML_MSG_NAVIGATION_ONCUSTOMDRAW: return (INT_PTR)Plugin_OnNavCustomDraw(hItem, (NAVITEMDRAW*)param2, (LPARAM)param3);
+ case ML_MSG_NAVIGATION_ONHITTEST: return (INT_PTR)Plugin_OnNavHitTest(hItem, (NAVHITTEST*)param2, (LPARAM)param3);
+ case ML_MSG_NAVIGATION_ONSETCURSOR: return (INT_PTR)Plugin_OnNavSetCursor(hItem, (LPARAM)param3);
+ case ML_MSG_NAVIGATION_CONTEXTMENU:
+ {
+ DRIVE *pDrive;
+ if (hniMain && (hItem == hniMain))
+ return Root_OnContextMenu(hItem, (HWND)param2, MAKEPOINTS(param3));
+
+ //Plugin Item
+ pDrive = Plugin_GetDriveFromNavItem(hItem);
+ if (pDrive)
+ return Plugin_OnContextMenu(hItem, (HWND)param2, MAKEPOINTS(param3), pDrive->cLetter);
+
+ return 0;
+ }
+ case ML_MSG_TREE_ONCLICK: return (INT_PTR)Plugin_OnNavItemClick(hItem, (UINT)param2, (HWND)param3);
+ case ML_MSG_CONFIG: return (INT_PTR)Plugin_OnConfig();
+ case ML_MSG_MLVISIBLE: Plugin_OnMLVisible((BOOL)param1); break;
+ case ML_MSG_NOTOKTOQUIT: if (!Plugin_QueryOkToQuit()) { return TRUE; } break;
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+extern "C" winampMediaLibraryPlugin plugin =
+{
+ MLHDR_VER,
+ "nullsoft(ml_disc.dll)",
+ Init,
+ Quit,
+ pluginMessageProc,
+ 0,
+ 0,
+ 0,
+};
+
+extern "C" __declspec(dllexport) winampMediaLibraryPlugin *winampGetMediaLibraryPlugin()
+{
+ return &plugin;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/main.h b/Src/Plugins/Library/ml_disc/main.h
new file mode 100644
index 00000000..285b8de6
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/main.h
@@ -0,0 +1,222 @@
+#ifndef NULLSOFT_MAINH
+#define NULLSOFT_MAINH
+
+#include "..\..\General\gen_ml/ml.h"
+#include "..\..\General\gen_ml/ml_ipc_0313.h"
+
+#include "./config.h"
+#include "./drivemngr.h"
+#include "./drive.h"
+#include "./medium.h"
+#include "api__ml_disc.h"
+#include "..\..\General\gen_ml/menu.h"
+
+#include "./copyfiles.h"
+#include "./copyinternal.h"
+
+#include <shlobj.h>
+#include <windows.h>
+#include <commctrl.h>
+
+#ifndef LONGX86
+#ifdef _WIN64
+ #define LONGX86 LONG_PTR
+#else /*_WIN64*/
+ #define LONGX86 LONG
+#endif /*_WIN64*/
+#endif // LONGX86
+
+#define OLD_AAC_CODEC mmioFOURCC('A','A','C',' ')
+
+#define BN_EX_GETTEXT 0x0FFF
+
+#define WM_EXTRACTDISC (WM_APP + 0x010)
+#define WM_SHOWFILEINFO (WM_APP + 0x11) // (wParam)(UINT)WISF_, (lParam)(LPCWSTR)file(track)name (can be NULL to reset)
+#define WM_QUERYFILEINFO (WM_APP + 0x12)
+#define WM_TAGUPDATED (WM_APP + 0x13) // wParam = 0, lParam = (LPCWSTR)pszFileName
+
+#define VCM_CREATECOMMANDBAR (WM_APP + 0x20)
+#define VCM_DESTROYCOMMANDBAR (WM_APP + 0x21)
+#define VCM_GETCOMMANDBAR (WM_APP + 0x22)
+#define VCM_GETMININFOENABLED (WM_APP + 0x23)
+#define VCM_GETMININFOVISIBLE (WM_APP + 0x24)
+
+
+typedef struct __CMDBARCREATESTRUCT
+{
+ HWND hwndOwner;
+ UINT resourceId;
+ DLGPROC fnDialogProc;
+ ULONG_PTR uData;
+} CMDBARCREATESTRUCT;
+
+#define MSGRESULT(__hwnd, __result) { SetWindowLongPtrW((__hwnd), DWLP_MSGRESULT, ((LONGX86)(LONG_PTR)(__result))); return TRUE; }
+
+#define ViewContainer_CreateCmdBar(/*HWND*/ __hwndViewContainer, /*HWND*/ __hwndOwner, /*INT_PTR*/ __resourceId, /*DLGPROC*/ __fnDialogProc, /*ULONG_PTR*/ __uData)\
+ {CMDBARCREATESTRUCT cs; cs.hwndOwner = (__hwndOwner); cs.resourceId = (__resourceId); cs.fnDialogProc = (__fnDialogProc); cs.uData = (__uData);\
+ ((HWND)SNDMSG((__hwndViewContainer), VCM_CREATECOMMANDBAR, 0, (LPARAM)(&cs)));}
+
+#define ViewContainer_DestroyCmdBar(/*HWND*/ __hwndViewContainer)\
+ ((BOOL)SNDMSG((__hwndViewContainer), VCM_DESTROYCOMMANDBAR, 0, 0L))
+
+#define ViewContainer_GetCmdBar(/*HWND*/ __hwndViewContainer)\
+ ((HWND)SNDMSG((__hwndViewContainer), VCM_GETCOMMANDBAR, 0, 0L))
+
+#define ViewContainer_GetMiniInfoEnabled(/*HWND*/ __hwndViewContainer)\
+ ((HWND)SNDMSG((__hwndViewContainer), VCM_GETMININFOENABLED, 0, 0L))
+
+#define ViewContainer_GetMiniInfoVisible(/*HWND*/ __hwndViewContainer)\
+ ((HWND)SNDMSG((__hwndViewContainer), VCM_GETMININFOVISIBLE, 0, 0L))
+
+
+extern winampMediaLibraryPlugin plugin;
+extern LARGE_INTEGER freq;
+
+void CleanupDirectoryString(LPTSTR pszDirectory);
+LPWSTR GetExtensionString(LPWSTR pszBuffer, INT cchBufferMax, DWORD fourcc);
+HRESULT FormatFileName(LPTSTR pszTextOut, INT cchTextMax, LPCTSTR pszFormat,
+ INT nTrackNo, LPCTSTR pszArtist,
+ LPCTSTR pszAlbum, LPCTSTR pszTitle,
+ LPCTSTR pszGenre, LPCTSTR pszYear,
+ LPCTSTR pszTrackArtist,
+ LPCTSTR pszFileName, LPCTSTR pszDisc);
+
+
+bool RegisteredEncoder(DWORD fourcc);
+
+extern C_Config *g_config;
+extern HMENU g_context_menus;
+extern C_Config *g_view_metaconf;
+
+#define DSF_CANRECORD 0x00010000
+
+#define DSF_PLAYING 0x00000001
+#define DSF_RIPPING 0x00000002
+#define DSF_BURNING 0x00000004
+#define DSF_GETTINGINFO 0x00000008
+
+typedef struct _DRIVE
+{
+ CHAR cLetter;
+ CHAR cMode;
+ WCHAR szTitle[64];
+ DWORD textSize;
+ BOOL textOrigWidth;
+ WORD itemWidth;
+ BYTE nBtnState;
+ BOOL bEjectVisible;
+ UINT_PTR timerId;
+} DRIVE;
+
+
+typedef BOOL (CALLBACK *NAVITEMENUMPROC)(HNAVITEM hItem, DRIVE *pDrive, LPARAM param);
+
+DRIVE *Plugin_GetDriveFromNavItem(HNAVITEM hItem);
+HNAVITEM Plugin_GetNavItemFromLetter(CHAR cLetter);
+BOOL Plugin_EnumerateNavItems(NAVITEMENUMPROC callback, LPARAM param);
+void Plugin_RegisterListener(HWND hwnd, UINT uMsg, CHAR cLetter); // active view can register itself to be notified about drive/medium changes if cLetter = 0 you will be notifed for all drives
+void Plugin_UnregisterListener(HWND hwnd);
+void Plugin_ShowRippingPreferences(void);
+BOOL Plugin_IsExtractScheduled(CHAR cLetter);
+
+int getFileInfo(const char *filename, const char *metadata, char *dest, int len);
+int getFileInfoW(const wchar_t *filename, const wchar_t *metadata, wchar_t *dest, int len);
+
+#define HF_DOMODAL 0x0001
+#define HF_ALLOWRESIZE 0x0010
+
+HWND MLDisc_ShowHelp(HWND hParent, LPCWSTR pszWindowTitle, LPCWSTR pszCaption, LPCWSTR pszText, UINT uFlags); // returns hwnd only if not HF_DOMODAL
+
+
+#define QBF_SHOW_CHECKBOX 0x00000001L
+#define QBF_SHOW_EXTRA_BUTTON 0x00000002L
+#define QBF_TOPMOST 0x00000100L
+#define QBF_SETFOREGROUND 0x00000200L
+#define QBF_BEEP 0x00000400L
+#define QBF_FLASH 0x00000800L
+#define QBF_DEFAULT_OK 0x00000000L
+#define QBF_DEFAULT_CANCEL 0x00001000L
+#define QBF_DEFAULT_EXTRA1 0x00002000L
+
+
+typedef struct _QUESTIONBOX
+{
+ HWND hParent; // [in]
+ LPCTSTR pszIcon; // [in]
+ UINT uBeepType; // [in]
+ LPCTSTR pszTitle; // [in] accepts MAKEINTRESOURCE() as parameters.
+ LPCTSTR pszMessage; // [in] accepts MAKEINTRESOURCE() as parameters.
+ UINT uFlags; // [in]
+ LPCTSTR pszBtnOkText; // [in] accepts MAKEINTRESOURCE() as parameters.
+ LPCTSTR pszBtnCancelText; // [in] accepts MAKEINTRESOURCE() as parameters.
+ LPCTSTR pszCheckboxText; // [in] accepts MAKEINTRESOURCE() as parameters.
+ LPCTSTR pszBtnExtraText; // [in] accepts MAKEINTRESOURCE() as parameters.
+ BOOL checkboxChecked; // [in][out]
+} QUESTIONBOX;
+
+INT_PTR MLDisc_ShowQuestionBox(QUESTIONBOX *pQuestionBox); // returns pressed button id;
+
+// cdrip.cpp
+BOOL CALLBACK CDRipPrefsProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
+
+typedef struct
+{
+ char drive_letter;
+
+ wchar_t *album;
+ wchar_t *artist;
+ wchar_t *genre;
+ wchar_t *year;
+ wchar_t *publisher; // record label
+ wchar_t *disc; // disc ##/##
+ wchar_t *comment; // notes from CDDB
+ wchar_t **composers;
+ wchar_t **conductors;
+ wchar_t **gracenoteFileIDs;
+ wchar_t **gracenoteExtData;
+ int total_length_bytes;
+
+ int ntracks; // total number of tracks
+ wchar_t **tracks; // set these to NULL to not rip em
+ wchar_t **trackArtists;
+
+ int *lengths; // lengths, in seconds
+
+ wchar_t **filenames; // can be used internally to override output filenames
+ // (should always allocate, but leave NULL ptrs in the array)
+ wchar_t **tempFilenames; //where we are ripping to, we'll move at the end
+} cdrip_params;
+
+void cdrip_extractFiles(cdrip_params *parms);
+
+int cdrip_isextracting(char drive);
+void cdrip_stop_all_extracts();
+
+//gracenote.cpp
+void gracenoteInit();
+int gracenoteQueryFile(const char *filename);
+void gracenoteCancelRequest();
+int gracenoteDoTimerStuff();
+void gracenoteSetValues(char *artist, char *album, char *title);
+char *gracenoteGetTuid();
+int gracenoteIsWorking();
+
+//view_ripburn.cpp
+INT_PTR CALLBACK view_ripburnDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
+
+// view_cdrom.cpp
+void saveCDToItemRecordList(CHAR cLetter, itemRecordList *obj, char *album);
+int cdrom_contextMenu(HWND parent, CHAR cLetter, HNAVITEM hItem);
+void cdburn_appendItemRecord(itemRecordList *obj, char driveletter);
+
+HWND CreateContainerWindow(HWND hwndParent, CHAR cLetter, BOOL bQueryInfo);
+HWND CreateWaitWindow(HWND hwndParent, CHAR cLetter);
+HWND CreateInfoWindow(HWND hwndParent, CHAR cLetter);
+HWND CreateCDViewWindow(HWND hwndParent, DM_NOTIFY_PARAM *phdr);
+HWND CreateCDBurnWindow(HWND hwndParent, CHAR cLetter);
+HWND CreateCDRipWindow(HWND hwndParent, CHAR cLetter);
+HWND CreateCdDataViewWindow(HWND hwndParent, CHAR cLetter);
+
+BOOL CALLBACK browseEnumProc(HWND hwnd, LPARAM lParam);
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/medium.cpp b/Src/Plugins/Library/ml_disc/medium.cpp
new file mode 100644
index 00000000..38bfd259
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/medium.cpp
@@ -0,0 +1,136 @@
+#include "./main.h"
+#include "./medium.h"
+#include "./drive.h"
+#include "./resource.h"
+//#include <primosdk.h>
+
+static int pType[] =
+{
+ IDS_STAMPED_DISC_OR_RECORDABLE_THAT_HAS_BEEN_RECORDED,
+ IDS_REWRITEABLE_DISC_HAS_DATA_BUT_KEPT_OPEN_FOR_APPEND,
+ IDS_REWRITEABLE_DISC_NOT_POSSIBLE_TO_APPEND_DATA,
+ IDS_BLANK_REWRITEABLE_DISC,
+};
+
+static int pFormat[] =
+{
+ IDS_MEDIA_BLANK_DISC,
+ IDS_MEDIA_DATA_MODE_1_DAO,
+ IDS_MEDIA_KODAK_PHOTO_CD,
+ IDS_MEDIA_DATA_MULTISESSION_MODE_1_CLOSED,
+ IDS_MEDIA_DATA_MULTISESSION_MODE_2_CLOSED,
+ IDS_MEDIA_DATA_MODE_2_DAO,
+ IDS_MEDIA_CDRFS,
+ IDS_MEDIA_PACKET_WRITING,
+ IDS_MEDIA_DATA_MULTISESSION_MODE_1_OPEN,
+ IDS_MEDIA_DATA_MULTISESSION_MODE_2_OPEN,
+ IDS_MEDIA_AUDIO_DAO_SAO_TAO,
+ IDS_MEDIA_AUDIO_REWRITEABLE_DISC_WITH_SESSION_NOT_CLOSED,
+ IDS_MEDIA_FIRST_TYPE_OF_ENHANCED_CD_ABORTED,
+ IDS_MEDIA_CD_EXTRA,
+ IDS_MEDIA_AUDIO_TAO_WITH_SESSION_NOT_WRITTEN,
+ IDS_MEDIA_FIRST_TRACK_DATA_OTHERS_AUDIO,
+ IDS_MEDIA_MIXED_MODE_MADE_TAO,
+ IDS_MEDIA_KODAK_PORTFOLIO,
+ IDS_MEDIA_VIDEO_CD,
+ IDS_MEDIA_CDi,
+ IDS_MEDIA_PLAYSTATION_SONY_GAMES,
+ IDS_MEDIA_OBSOLETE,
+ IDS_MEDIA_OBSOLETE_FOR_RESTRICTED_OVERWRITE_DVD,
+ IDS_MEDIA_DVDROM_OR_CLOSED_RECORDABLE,
+ IDS_MEDIA_INCREMENTAL_DVD_WITH_APPENDABLE_ZONE,
+ IDS_MEDIA_APPENDABLE_DVD_OF_ANY_TYPE,
+ IDS_MEDIA_DVDRAM_CARTRIDGE,
+ IDS_MEDIA_CD_OTHER_TYPE,
+};
+
+static wchar_t buffer[256];
+
+LPCWSTR Medium_GetTypeString(DWORD nType)
+{
+ int index = -1;
+#if 0
+ switch(nType)
+ {
+ case PRIMOSDK_SILVER: index = 0; break;
+ case PRIMOSDK_COMPLIANTGOLD: index = 1; break;
+ case PRIMOSDK_OTHERGOLD: index = 2; break;
+ case PRIMOSDK_BLANK: index = 3; break;
+ }
+#endif
+ return WASABI_API_LNGSTRINGW_BUF((-1 != index) ? pType[index] : IDS_UNKNOWN, buffer,
+ sizeof(buffer)/sizeof(wchar_t));
+}
+
+LPCWSTR Medium_GetPhysicalTypeString(DWORD nType)
+{
+ return Drive_GetTypeString(nType);
+}
+
+LPCWSTR Medium_GetFormatString(DWORD nFormat)
+{
+ int index = -1;
+#if 0
+ switch(nFormat)
+ {
+ case PRIMOSDK_B1: index = 0; break;
+ case PRIMOSDK_D1: index = 1; break;
+ case PRIMOSDK_D2: index = 2; break;
+ case PRIMOSDK_D3: index = 3; break;
+ case PRIMOSDK_D4: index = 4; break;
+ case PRIMOSDK_D5: index = 5; break;
+ case PRIMOSDK_D6: index = 6; break;
+ case PRIMOSDK_D7: index = 7; break;
+ case PRIMOSDK_D8: index = 8; break;
+ case PRIMOSDK_D9: index = 9; break;
+ case PRIMOSDK_A1: index = 10; break;
+ case PRIMOSDK_A2: index = 11; break;
+ case PRIMOSDK_A3: index = 12; break;
+ case PRIMOSDK_A4: index = 13; break;
+ case PRIMOSDK_A5: index = 14; break;
+ case PRIMOSDK_M1: index = 15; break;
+ case PRIMOSDK_M2: index = 16; break;
+ case PRIMOSDK_M3: index = 17; break;
+ case PRIMOSDK_M4: index = 18; break;
+ case PRIMOSDK_M5: index = 19; break;
+ case PRIMOSDK_M6: index = 20; break;
+ case PRIMOSDK_F1: index = 21; break;
+ case PRIMOSDK_F2: index = 22; break;
+ case PRIMOSDK_F3: index = 23; break;
+ case PRIMOSDK_F4: index = 24; break;
+ case PRIMOSDK_F8: index = 25; break;
+ case PRIMOSDK_FA: index = 26; break;
+ case PRIMOSDK_GENERICCD: index = 27; break;
+ }
+#endif
+ return WASABI_API_LNGSTRINGW_BUF((-1 != index) ? pFormat[index] : IDS_UNKNOWN, buffer,
+ sizeof(buffer)/sizeof(wchar_t));
+}
+
+BOOL Medium_IsRecordableType(DWORD nType)
+{
+ #if 0
+ return (PRIMOSDK_COMPLIANTGOLD == nType || PRIMOSDK_BLANK == nType);
+#else
+ return FALSE;
+#endif
+}
+
+BOOL Medium_IsRecordable(CHAR cLetter)
+{
+ wchar_t info[128] = {0};
+ wchar_t name[] = L"cda://X.cda";
+ DWORD result;
+ BOOL reloaded = FALSE;
+
+ name[6] = cLetter;
+
+ for(;;)
+ {
+ result = getFileInfoW(name, L"cdtype", info, sizeof(info)/sizeof(wchar_t));
+ if (result || reloaded || !getFileInfoW(name, L"reloadsonic", NULL, 0)) break;
+ reloaded = TRUE;
+ }
+
+ return (result) ? (!lstrcmpW(info, L"CDR") || !lstrcmpW(info, L"CDRW")) : FALSE;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/medium.h b/Src/Plugins/Library/ml_disc/medium.h
new file mode 100644
index 00000000..fc0f5764
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/medium.h
@@ -0,0 +1,17 @@
+#ifndef NULLSOFT_MLDISC_MEDIUM_HEADER
+#define NULLSOFT_MLDISC_MEDIUM_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <windows.h>
+
+LPCWSTR Medium_GetTypeString(DWORD nType);
+LPCWSTR Medium_GetPhysicalTypeString(DWORD nType);
+LPCWSTR Medium_GetFormatString(DWORD nFormat);
+BOOL Medium_IsRecordableType(DWORD nType);
+BOOL Medium_IsRecordable(CHAR cLetter);
+
+
+#endif // NULLSOFT_MLDISC_MEDIUM_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/menu.cpp b/Src/Plugins/Library/ml_disc/menu.cpp
new file mode 100644
index 00000000..e01503f4
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/menu.cpp
@@ -0,0 +1,25 @@
+#include "main.h"
+#include "./menu.h"
+#include "./resource.h"
+#include "../gen_ml/ml_ipc_0313.h"
+
+INT Menu_TrackPopup(HMENU hMenu, UINT fuFlags, INT x, INT y, HWND hwnd, LPTPMPARAMS lptpm)
+{
+ if (NULL == hMenu)
+ return NULL;
+
+ MLSKINNEDPOPUP popup;
+ ZeroMemory(&popup, sizeof(MLSKINNEDPOPUP));
+ popup.cbSize = sizeof(MLSKINNEDPOPUP);
+ popup.hmenu = hMenu;
+ popup.fuFlags = fuFlags;
+ popup.x = x;
+ popup.y = y;
+ popup.hwnd = hwnd;
+ popup.lptpm = lptpm;
+ popup.skinStyle = SMS_USESKINFONT/*SMS_SYSCOLORS*/;
+ popup.customProc = NULL;
+ popup.customParam = 0;
+
+ return (INT)SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_TRACKSKINNEDPOPUPEX, &popup);
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/menu.h b/Src/Plugins/Library/ml_disc/menu.h
new file mode 100644
index 00000000..d776685b
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/menu.h
@@ -0,0 +1,13 @@
+#ifndef NULLOSFT_MLDISC_PLUGIN_MENU_HEADER
+#define NULLOSFT_MLDISC_PLUGIN_MENU_HEADER
+
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+INT Menu_TrackPopup(HMENU hMenu, UINT fuFlags, INT x, INT y, HWND hwnd, LPTPMPARAMS lptpm);
+
+#endif //NULLOSFT_MLDISC_PLUGIN_MENU_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/ml_disc.rc b/Src/Plugins/Library/ml_disc/ml_disc.rc
new file mode 100644
index 00000000..a1dc5bc4
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/ml_disc.rc
@@ -0,0 +1,939 @@
+// 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_VIEW_WAIT DIALOGEX 0, 0, 291, 242
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ CONTROL "Show Info",IDC_BTN_SHOWINFO,"Button",BS_OWNERDRAW | WS_TABSTOP,251,231,40,11
+ CTEXT "",IDC_LBL_TEXT,0,19,290,209,SS_CENTERIMAGE
+END
+
+IDD_PREFSCDRIPFR DIALOGEX 0, 0, 272, 246
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "Tab1",IDC_TAB1,"SysTabControl32",WS_TABSTOP,0,0,271,246
+END
+
+IDD_PREFS_CDRIP1 DIALOGEX 0, 0, 260, 226
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Output Filenames",IDC_STATIC,4,3,256,108
+ LTEXT "Specify the destination folder for ripped tracks:",IDC_STATIC,10,15,204,8
+ EDITTEXT IDC_DESTPATH,10,26,198,13,ES_AUTOHSCROLL
+ PUSHBUTTON "Browse...",IDC_BUTTON1,208,26,44,13
+ LTEXT "Specify the naming convention for ripped tracks:",IDC_STATIC,10,44,203,8
+ EDITTEXT IDC_FILENAMEFMT,10,55,198,13,ES_AUTOHSCROLL
+ PUSHBUTTON "Format Help",IDC_BUTTON2,208,55,44,13
+ CONTROL "Use uppercase file extensions (i.e. MP3 instead of mp3)",IDC_UPPERCASEEXT,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,73,192,10
+ LTEXT "",IDC_FMTOUT,10,87,244,18
+ GROUPBOX "Tagging Settings",IDC_STATIC,4,115,256,110
+ CONTROL "Automatically add ripped files to Library database",IDC_CHECK_ML,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,128,193,10
+ CONTROL "Automatically add tags with metadata to ripped files",IDC_TAGFILES,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,141,183,10
+ CONTROL "Automatically calculate replay gain",IDC_AUTO_RG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,154,126,10
+ CONTROL "Write track numbers as track/total (e.g. 12/15)",IDC_TOTAL_TRACKS,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,167,167,10
+ LTEXT "When tagging ripped CDs, make track numbers start with:",IDC_STATIC,10,181,186,8
+ EDITTEXT IDC_EDIT2,198,179,25,12,ES_AUTOHSCROLL | ES_NUMBER
+ LTEXT "Add the following to the comment field of ripped files:",IDC_STATIC,10,195,174,8
+ EDITTEXT IDC_EDIT1,10,206,243,13,ES_AUTOHSCROLL
+END
+
+IDD_PREFS_CDRIP2 DIALOGEX 0, 0, 260, 226
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Encoding Format",IDC_STATIC,4,3,256,44
+ LTEXT "Select the format you wish to encode to:",IDC_STATIC,12,15,240,11
+ COMBOBOX IDC_ENCFORMAT,20,27,233,116,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "",IDC_ENC_CONFIG,"Static",SS_BLACKRECT | NOT WS_VISIBLE,4,51,255,167
+END
+
+IDD_VIEW_CDROM_EXTRACT DIALOGEX 0, 0, 269, 156
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU
+CAPTION "Ripping from CD..."
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ GROUPBOX "Current Track",IDC_CTFRAME,4,7,261,70
+ EDITTEXT IDC_CURTRACK,16,18,240,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP
+ CONTROL "",IDC_STATUS,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,16,31,240,18
+ CONTROL "Progress1",IDC_PROGRESS1,"msctls_progress32",WS_BORDER,16,52,240,14
+ GROUPBOX "All Tracks",IDC_STATIC,4,72,261,62
+ LTEXT "",IDC_STATUS2,16,88,240,22
+ CONTROL "Progress1",IDC_PROGRESS2,"msctls_progress32",WS_BORDER,16,114,240,14
+ PUSHBUTTON "Hide Window",IDCANCEL,76,139,57,13
+ PUSHBUTTON "Cancel",IDC_BUTTON1,137,139,57,13
+END
+
+IDD_PREFS_CDRIP4 DIALOGEX 0, 0, 260, 226
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Playlist Creation Settings",IDC_STATIC,4,3,256,152
+ LTEXT "When Winamp completes ripping an entire CD, it can automatically generate a playlist of the ripped tracks.",IDC_STATIC,10,15,243,18
+ LTEXT "Create the following playlists:",IDC_STATIC,10,37,230,10
+ CONTROL "Library playlist",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,18,49,80,10
+ CONTROL ".M3U playlist",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,18,62,57,10
+ CONTROL "Use extended M3U playlist format",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,29,74,121,10
+ CONTROL ".PLS playlist",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,18,86,53,10
+ LTEXT "Specify the naming convention for M3U and PLS playlists:",IDC_STATIC,10,102,183,8
+ EDITTEXT IDC_FILENAMEFMT,10,114,196,13,ES_AUTOHSCROLL
+ PUSHBUTTON "Format Help",IDC_BUTTON2,207,114,45,13
+ LTEXT "",IDC_FMTOUT,13,131,242,18
+END
+
+IDD_VIEW_CDROM_EX2 DIALOGEX 0, 0, 291, 242
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_NOFAILCREATE | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LTEXT "",IDC_CDINFO,1,0,288,18,SS_NOPREFIX
+ CONTROL "List4",IDC_LIST2,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_TABSTOP,0,19,290,209
+ CONTROL "Rip Options",IDC_RIPOPTS,"Button",BS_OWNERDRAW | WS_TABSTOP,0,231,48,11
+ CONTROL "Cancel Rip",IDC_CANCEL_RIP,"Button",BS_OWNERDRAW | WS_TABSTOP,52,231,44,11
+ CONTROL "Show Info",IDC_BTN_SHOWINFO,"Button",BS_OWNERDRAW | WS_TABSTOP,251,231,40,11
+END
+
+IDD_VIEW_CDROM_BURN DIALOGEX 0, 0, 291, 242
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_NOFAILCREATE | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN
+EXSTYLE WS_EX_ACCEPTFILES | WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "",IDC_CDINFO,"Static",SS_LEFTNOWORDWRAP | SS_NOPREFIX | WS_GROUP,1,0,255,17
+ CONTROL 1101,IDC_LOGO,"Static",SS_BITMAP | SS_NOTIFY | SS_REALSIZEIMAGE,260,2,31,15
+ CONTROL "List4",IDC_LIST2,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_OWNERDATA | LVS_NOSORTHEADER | WS_TABSTOP,0,19,290,209
+ CONTROL "Add...",IDC_ADD,"Button",BS_OWNERDRAW | WS_TABSTOP,0,231,40,11
+ CONTROL "Burn Options",IDC_BURN_OPTS,"Button",BS_OWNERDRAW | NOT WS_VISIBLE | WS_TABSTOP,0,231,46,11
+ CONTROL "Clear",IDC_CLEAR,"Button",BS_OWNERDRAW | WS_TABSTOP,42,231,40,11
+ CONTROL "Cancel Burn",IDC_CANCEL_BURN,"Button",BS_OWNERDRAW | NOT WS_VISIBLE | WS_TABSTOP,51,231,46,11
+ CONTROL "Burn",IDC_BURN,"Button",BS_OWNERDRAW | WS_TABSTOP,84,231,40,11
+ CONTROL "Show Info",IDC_BTN_SHOWINFO,"Button",BS_OWNERDRAW | WS_TABSTOP,251,231,40,11
+END
+
+IDD_BURN_ADD_STATUS DIALOGEX 0, 0, 160, 42
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "CD Burner"
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LTEXT "Adding tracks to burner...",IDC_STAT,7,7,146,8
+ CONTROL "Progress1",IDC_PROGRESS1,"msctls_progress32",WS_BORDER,7,21,146,14
+END
+
+IDD_BURN DIALOGEX 0, 0, 251, 143
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Dialog"
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LTEXT "Burn speed:",IDC_STATIC,7,9,39,8
+ COMBOBOX IDC_COMBO1,50,7,71,154,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Want to burn at your computer's maximum speed?",IDC_PRO1,7,23,168,8
+ CONTROL "Click here",IDC_PRO2,"Button",BS_OWNERDRAW | WS_TABSTOP,176,23,35,10
+ CONTROL "Enable Burn-proof mode (when available)",IDC_CHECK2,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,35,145,10
+ LTEXT "Burn-proof mode is supported on newer CD burners and helps ensure that the burn does not fail. The downside of burn-proof mode is higher memory use. Burn-proof mode is highly recommended.",IDC_STATIC,20,47,224,26
+ CONTROL "Test mode (do not actually write to media)",IDC_CHECK1,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,77,147,10
+ LTEXT "Test mode lets you go through the burning process without actually writing to the physical CD. This can be useful to test system performance without risking making unusable media.",IDC_STATIC,20,91,222,26
+ DEFPUSHBUTTON "Burn",IDOK,71,123,50,13
+ PUSHBUTTON "Cancel",IDCANCEL,124,123,50,13
+END
+
+IDD_WAITFORCDR DIALOGEX 0, 0, 186, 63
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Waiting for blank CD..."
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LTEXT "",IDC_TEXT,7,7,172,8
+ PUSHBUTTON "Cancel",IDCANCEL,64,42,50,14
+END
+
+/* IDD_UPSELL_RIPPING DIALOGEX 0, 0, 223, 93
+STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Rip CDs faster in MP3"
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LTEXT "With Winamp Pro you can:\n\t- Rip in MP3\n\t- Burn and Rip Faster\n\t- Support Winamp",IDC_STATIC,7,7,209,33
+ LTEXT "The free version of Winamp will only rip CDs at 8x speed in AAC.",IDC_STATIC,7,43,209,10
+ CONTROL "Never show me this again",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,57,97,10
+ PUSHBUTTON "More Info",IDC_BUTTON1,7,73,60,13
+ PUSHBUTTON "Rip at 8x in AAC",IDOK,70,73,60,13
+ PUSHBUTTON "Cancel Rip",IDCANCEL,166,73,50,13
+END */
+
+IDD_NOBURN DIALOGEX 0, 0, 300, 58
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Cannot burn track"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "Skip File",IDOK,192,41,50,13
+ PUSHBUTTON "Cancel",IDCANCEL,246,41,50,13
+ EDITTEXT IDC_MESSAGE2,4,4,292,32,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
+END
+
+IDD_VIEW_RIPBURN DIALOGEX 0, 0, 294, 236
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_NOFAILCREATE | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN
+EXSTYLE WS_EX_NOPARENTNOTIFY | WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ CTEXT "Available drives:",IDC_LBL_DRIVES,0,2,66,12,SS_CENTERIMAGE
+ LTEXT "Drive Info:",IDC_LBL_INFO_DRIVE,70,2,222,69
+ LISTBOX IDC_LIST_DRIVES,0,18,66,205,LBS_SORT | LBS_OWNERDRAWVARIABLE | LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | NOT WS_BORDER | WS_TABSTOP
+ LTEXT "Drive Letter:",IDC_LBL_DRIVE_LETTER,80,18,58,8,NOT WS_VISIBLE
+ CONTROL "",IDC_LBL_DRIVE_LETTER_VAL,"Static",SS_LEFTNOWORDWRAP | SS_ENDELLIPSIS | NOT WS_VISIBLE | WS_GROUP,140,18,150,8
+ LTEXT "Description:",IDC_LBL_DRIVE_DESCRIPTION,80,28,58,8,NOT WS_VISIBLE
+ CONTROL "",IDC_LBL_DRIVE_DESCRIPTION_VAL,"Static",SS_LEFTNOWORDWRAP | SS_ENDELLIPSIS | NOT WS_VISIBLE | WS_GROUP,140,28,150,8
+ LTEXT "Bus type:",IDC_LBL_DRIVE_BUS,80,38,58,8,NOT WS_VISIBLE
+ CONTROL "",IDC_LBL_DRIVE_BUS_VAL,"Static",SS_LEFTNOWORDWRAP | SS_ENDELLIPSIS | NOT WS_VISIBLE | WS_GROUP,140,38,150,8
+ LTEXT "Supported Types:",IDC_LBL_DRIVE_TYPES,80,48,58,8,NOT WS_VISIBLE
+ LTEXT "",IDC_LBL_DRIVE_TYPES_VAL,140,48,150,16,SS_ENDELLIPSIS | NOT WS_VISIBLE
+ LTEXT "Medium Info:",IDC_LBL_INFO_MEDIUM,70,77,222,146
+// LTEXT "Medium information is not available due to the Sonic Burning engine not being installed.\n\nPlease install the Sonic Burning engine if you want to see medium information.",IDC_LBL_MEDIUM_UPDATE,110,112,150,51
+ LTEXT "This feature is not available in this Winamp build.\n\nDrive info and CD Burning will return for the next release.",IDC_LBL_MEDIUM_UPDATE,110,112,150,51
+ LTEXT "Type:",IDC_LBL_MEDIUM_TYPE,80,91,50,8,NOT WS_VISIBLE
+ CONTROL "",IDC_LBL_MEDIUM_DISC_VAL,"Static",SS_LEFTNOWORDWRAP | SS_ENDELLIPSIS | NOT WS_VISIBLE | WS_GROUP,140,91,150,8
+ LTEXT "Format:",IDC_LBL_MEDIUM_FORMAT,80,101,50,8,NOT WS_VISIBLE
+ CONTROL "",IDC_LBL_MEDIUM_FORMAT_VAL,"Static",SS_LEFTNOWORDWRAP | SS_ENDELLIPSIS | NOT WS_VISIBLE | WS_GROUP,140,101,150,8
+ LTEXT "Tracks #:",IDC_LBL_MEDIUM_TRACKN,80,111,50,8,NOT WS_VISIBLE
+ CONTROL "",IDC_LBL_MEDIUM_TRACKN_VAL,"Static",SS_LEFTNOWORDWRAP | SS_ENDELLIPSIS | NOT WS_VISIBLE | WS_GROUP,140,111,150,8
+ LTEXT "Capacity:",IDC_LBL_MEDIUM_CAPACITY,80,121,50,8,NOT WS_VISIBLE
+ CONTROL "",IDC_LBL_MEDIUM_CAPACITY_VAL,"Static",SS_LEFTNOWORDWRAP | SS_ENDELLIPSIS | NOT WS_VISIBLE | WS_GROUP,140,121,150,8
+ LTEXT "Recordable:",IDC_LBL_MEDIUM_RECORDABLE,80,135,50,8,NOT WS_VISIBLE
+ CONTROL "",IDC_LBL_MEDIUM_RECORDABLE_VAL,"Static",SS_LEFTNOWORDWRAP | SS_ENDELLIPSIS | NOT WS_VISIBLE | WS_GROUP,140,135,150,8
+ LTEXT "Erasable:",IDC_LBL_MEDIUM_ERASEABLE,80,145,50,8,NOT WS_VISIBLE
+ CONTROL "",IDC_LBL_MEDIUM_ERASEABLE_VAL,"Static",SS_LEFTNOWORDWRAP | SS_ENDELLIPSIS | NOT WS_VISIBLE | WS_GROUP,140,145,150,8
+ LTEXT "Additional Info:",IDC_LBL_MEDIUM_ADDINFO,80,159,50,8,NOT WS_VISIBLE
+ LTEXT "",IDC_LBL_MEDIUM_ADDINFO_VAL,140,159,149,8,SS_ENDELLIPSIS | NOT WS_VISIBLE
+ CONTROL "Refresh List",IDC_BTN_REFRESH,"Button",BS_OWNERDRAW | WS_TABSTOP,0,225,67,11
+END
+
+IDD_VIEW_CONTAINER DIALOGEX 0, 0, 276, 166
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+END
+
+IDD_VIEW_CDROM DIALOGEX 0, 0, 291, 242
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LTEXT "",IDC_CDINFO,1,0,173,18,SS_NOPREFIX
+ LTEXT "",IDC_CDINFO2,174,0,116,18
+ CONTROL "List4",IDC_LIST2,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_TABSTOP,0,19,290,209
+ CONTROL "Play",IDC_BUTTON_PLAY,"Button",BS_OWNERDRAW | WS_TABSTOP,0,231,35,11
+ CONTROL "Enqueue",IDC_BUTTON_ENQUEUE,"Button",BS_OWNERDRAW | WS_TABSTOP,39,231,38,11
+ CONTROL "Rip",IDC_BUTTON_EXTRACT,"Button",BS_OWNERDRAW | WS_TABSTOP,81,231,32,11
+ CONTROL "Eject CD",IDC_BUTTON_EJECT,"Button",BS_OWNERDRAW | WS_TABSTOP,117,231,42,11
+ CONTROL "Show Info",IDC_BTN_SHOWINFO,"Button",BS_OWNERDRAW | WS_TABSTOP,251,231,40,11
+END
+
+IDD_VIEW_INFO DIALOGEX 0, 0, 291, 242
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ CONTROL "Show Info",IDC_BTN_SHOWINFO,"Button",BS_OWNERDRAW | WS_TABSTOP,251,231,40,11
+ CTEXT "\n\n\nAnother instance of Winamp has locked this drive.\nYou will need to wait or cancel the operation.",IDC_LBL_TEXT,0,19,290,209
+END
+
+IDD_VIEW_CDROM_DATA DIALOGEX 0, 0, 478, 474
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_NOFAILCREATE | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+END
+
+IDD_COMMANDBAR_DATA DIALOGEX 0, 0, 291, 14
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "",IDC_BTN_PLAYEX,0,0,75,14,WS_GROUP
+ PUSHBUTTON "Copy...",IDC_BTN_COPY,83,0,50,14
+ PUSHBUTTON "",IDC_BTN_EJECT,265,0,24,14
+ CONTROL "",IDC_LBL_STATUS,"Static",SS_LEFTNOWORDWRAP | SS_NOPREFIX | SS_CENTERIMAGE | SS_WORDELLIPSIS | WS_GROUP,137,0,108,14
+END
+
+IDD_FILECOPY_PREPARE DIALOGEX 0, 0, 279, 206
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CLIPSIBLINGS | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "Copy Files"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ PUSHBUTTON "",IDC_BTN_OPTIONS,4,43,59,14
+ DEFPUSHBUTTON "OK",IDOK,169,43,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,225,43,50,14
+ EDITTEXT IDC_EDT_PATH,10,83,259,13,ES_AUTOHSCROLL
+ PUSHBUTTON "Browse...",IDC_BTN_BROWSE,219,98,50,14
+ CONTROL "Add files to Library database",IDC_CHK_ADDTOMLDB,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,123,259,10
+ CONTROL "Use custom naming",IDC_CHK_CUSTOMNAME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,135,259,10
+ EDITTEXT IDC_EDT_NAMEFORMAT,20,147,249,13,ES_AUTOHSCROLL
+ PUSHBUTTON "Help",IDC_BTN_HELP,219,179,50,14
+ LTEXT "",IDC_LBL_MESSAGE,68,6,207,28
+ CONTROL "Destination folder:",IDC_STATIC,"Static",SS_SIMPLE | WS_GROUP,10,73,259,8
+ GROUPBOX "",IDC_GRP_OPTIONS,4,65,271,135
+ CONTROL "",IDC_PIC_LOGO,"Static",SS_BITMAP | SS_REALSIZEIMAGE,10,6,15,13
+ CONTROL "",IDC_LBL_EXAMPLE,"Static",SS_LEFTNOWORDWRAP | SS_ENDELLIPSIS | WS_GROUP,56,162,211,8
+ CONTROL "Example:",IDC_LBL_EXAMPLE_TITLE,"Static",SS_SIMPLE | WS_GROUP,22,162,30,8
+ CONTROL "Free:",IDC_LBL_FREE_TITLE,"Static",SS_SIMPLE | WS_GROUP,12,97,18,8
+ CONTROL "Required:",IDC_LBL_REQUIRED_TITLE,"Static",SS_SIMPLE | WS_GROUP,12,106,32,8
+ LTEXT "",IDC_LBL_FREE,49,97,113,8
+ LTEXT "",IDC_LBL_REQUIRED,49,106,113,8
+END
+
+IDD_FILECOPY_PROGRESS DIALOGEX 0, 0, 279, 63
+STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU
+CAPTION "Copy Files"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ PUSHBUTTON "Cancel",IDCANCEL,225,43,50,14
+ CONTROL "",IDC_PIC_LOGO,"Static",SS_BITMAP | SS_REALSIZEIMAGE,10,6,15,13
+ LTEXT "",IDC_LBL_TASK,68,6,207,9
+ CONTROL "",IDC_PRG_TOTAL,"msctls_progress32",WS_BORDER,68,27,207,10
+ LTEXT "",IDC_LBL_OPERATION,68,16,207,9
+END
+
+IDD_SIMPLEHELP DIALOGEX 0, 0, 148, 123
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_NOFAILCREATE | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU
+CAPTION "Dialog"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "Close",IDCANCEL,90,102,50,14
+ EDITTEXT IDC_EDT_TEXT,7,39,133,50,ES_MULTILINE | ES_READONLY | ES_WANTRETURN | NOT WS_BORDER | WS_VSCROLL,WS_EX_STATICEDGE
+ LTEXT "",IDC_LBL_CAPTION,7,7,133,24
+END
+
+IDD_FILECOPY_QUESTION DIALOGEX 0, 0, 248, 66
+STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,136,46,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,192,46,50,14
+ LTEXT "Label",IDC_LBL_MESSAGE,53,6,189,23
+ CONTROL "",IDC_CHECKBOX1,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,6,35,236,10
+ PUSHBUTTON "Extra1",IDC_BTN_EXTRA1,80,46,50,14,NOT WS_VISIBLE
+ ICON 32514,IDC_PIC_ICON,6,6,21,20,SS_REALSIZEIMAGE
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+IDB_LISTITEM_CDDRIVE BITMAP "resources\\cdrom_32x32_24.bmp"
+IDB_LISTBOX_BACK BITMAP "resources\\listbox_back_2x68x24.bmp"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_CONTEXTMENUS MENU
+BEGIN
+ POPUP "ExtractMenu"
+ BEGIN
+ MENUITEM "CD ripping preferences...", 40001
+ MENUITEM SEPARATOR
+ MENUITEM "Rip all tracks", 40002
+ MENUITEM "Rip selected tracks", 40003
+ END
+ POPUP "CdromMenu"
+ BEGIN
+ MENUITEM "Play selection\tEnter", 40004
+ MENUITEM "Enqueue selection\tShift+Enter", 40005
+ MENUITEM SEPARATOR
+ MENUITEM "Select All\tCtrl+A", ID_CDROMMENU_SELECTALL
+ MENUITEM SEPARATOR
+ MENUITEM "Play all tracks", 40006
+ MENUITEM "Enqueue all tracks", 40007
+ MENUITEM SEPARATOR
+ MENUITEM "Edit &CD Info...\tAlt+3", 40008
+ POPUP "Rip"
+ BEGIN
+ MENUITEM "Rip selected track(s)", 40009
+ MENUITEM "Rip all tracks", 40010
+ MENUITEM SEPARATOR
+ MENUITEM "CD ripping preferences...", 40011
+ END
+ END
+ POPUP "CdromMenu2"
+ BEGIN
+ MENUITEM "Play audio CD", 40006
+ MENUITEM "Enqueue audio CD", 40007
+ MENUITEM SEPARATOR
+ MENUITEM "Rip audio CD", 40010
+ MENUITEM "CD ripping preferences...", 40011
+ MENUITEM SEPARATOR
+ MENUITEM "Eject CD", 40012
+ MENUITEM SEPARATOR
+ MENUITEM "Help", ID_CDROMMENU_EXTRACT_HELP
+ END
+ POPUP "BurnAddMenu"
+ BEGIN
+ MENUITEM "Files...", 40013
+ MENUITEM "Folder...", 40014
+ MENUITEM SEPARATOR
+ MENUITEM "Current playlist", 40015
+ END
+ POPUP "BurnContextMenu"
+ BEGIN
+ MENUITEM "Play selection\tEnter", 40016
+ MENUITEM "Enqueue selection\tShift+Enter", 40017
+ MENUITEM SEPARATOR
+ MENUITEM "Select all\tCtrl+A", ID_BURNCONTEXTMENU_SELECTALL
+ MENUITEM SEPARATOR
+ MENUITEM "Move selected items up\tAlt+Up", 40018
+ MENUITEM "Move selected items down\tAlt+Down", 40019
+ MENUITEM SEPARATOR
+ MENUITEM "Remove selected items\tDel", 40020
+ MENUITEM SEPARATOR
+ MENUITEM "Burn...", 40021
+ END
+ POPUP "RipOptions"
+ BEGIN
+ POPUP "Priority"
+ BEGIN
+ MENUITEM "Highest", 40022
+ MENUITEM "Above normal", 40023
+ MENUITEM "Normal", 40024
+ MENUITEM "Below normal", 40025
+ MENUITEM "Lowest", 40026
+ MENUITEM "Idle", 40027
+ END
+ MENUITEM SEPARATOR
+ MENUITEM "&Ripping Status Window", 40028
+ MENUITEM SEPARATOR
+ MENUITEM "&Eject CD when complete", 40029
+ MENUITEM "&Play tracks when complete", 40030
+ MENUITEM "&Close view when complete", 40031
+ END
+ POPUP "BurnOptions"
+ BEGIN
+ MENUITEM "&Burning Status Window", 40028
+ MENUITEM SEPARATOR
+ MENUITEM "&Eject CD when complete", 40029
+ MENUITEM "&Close view when complete", 40031
+ MENUITEM "&Add CD titles to local DB when complete", 40032
+ END
+ POPUP "Navigation"
+ BEGIN
+ MENUITEM "&Preferences", ID_NAVIGATION_PREFERENCES
+ MENUITEM SEPARATOR
+ MENUITEM "Help", ID_NAVIGATION_HELP
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_VIEW_CDROM_EXTRACT, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ RIGHTMARGIN, 265
+ BOTTOMMARGIN, 152
+ END
+
+ IDD_NOBURN, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ RIGHTMARGIN, 296
+ TOPMARGIN, 4
+ BOTTOMMARGIN, 54
+ END
+
+ IDD_VIEW_CONTAINER, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 269
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 159
+ END
+
+ IDD_VIEW_CDROM_DATA, DIALOG
+ BEGIN
+ RIGHTMARGIN, 477
+ BOTTOMMARGIN, 472
+ END
+
+ IDD_FILECOPY_PREPARE, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ RIGHTMARGIN, 275
+ VERTGUIDE, 10
+ VERTGUIDE, 20
+ VERTGUIDE, 49
+ VERTGUIDE, 68
+ VERTGUIDE, 269
+ TOPMARGIN, 6
+ BOTTOMMARGIN, 200
+ HORZGUIDE, 57
+ HORZGUIDE, 97
+ HORZGUIDE, 106
+ END
+
+ IDD_FILECOPY_PROGRESS, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ RIGHTMARGIN, 275
+ VERTGUIDE, 68
+ TOPMARGIN, 6
+ BOTTOMMARGIN, 57
+ END
+
+ IDD_SIMPLEHELP, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 140
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 116
+ END
+
+ IDD_FILECOPY_QUESTION, DIALOG
+ BEGIN
+ LEFTMARGIN, 6
+ RIGHTMARGIN, 242
+ TOPMARGIN, 6
+ BOTTOMMARGIN, 60
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accelerator
+//
+
+IDR_ACCELERATOR_VIEW ACCELERATORS
+BEGIN
+ VK_RETURN, ID_COPY_SELECTION, VIRTKEY, CONTROL, NOINVERT
+ "E", ID_EJECT_DISC, VIRTKEY, SHIFT, CONTROL, NOINVERT
+ "I", ID_MINIINFO_SHOW, VIRTKEY, CONTROL, NOINVERT
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.K.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_RIP_AND_BURN "Nullsoft Rip & Burn v%d.%02d"
+ 65535 "{2C913A2F-CD49-40a1-8F1A-8EF7C2A22229}"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_ERROR_WRITING_TEMP_BURN_LIST "Error writing temporary burning list."
+ IDS_ERROR "Error"
+ IDS_BURN_X_TRACKX_ON_X "Burn %d %s on %c:"
+ IDS_X_OF_X_SECTORS_FREE "%d of %d sectors free"
+ IDS_GET_PRO_FOR_BURN_HIGHER_THAN_2X
+ "In order to use more than 2x burning, you must purchase Winamp Pro.\nWould you like information on purchasing Winamp Pro?"
+// IDS_WINAMP_PRO_FEATURE "Winamp Pro Feature"
+ IDS_NEED_TO_ADD_SOME_TRACKS_TO_BURN
+ "You need to add some tracks to burn first!"
+ IDS_TOTAL_LENGTH_IS_BIGGER_THAN_MEDIA_CAPACITY
+ "The total length of the tracks in the list is bigger than the capacity of the blank CD in the drive!\n\nYou need to remove %01d:%02ds from the list."
+ IDS_BURNING "Burning..."
+ IDS_CANCEL_BURN "Cancel Burn"
+ IDS_NO_BLANK_CDR_IN_DRIVE "No blank CD-R in drive.\n"
+ IDS_X_CAPACITY_DETAILS "%s - Capacity: %01d:%02d.\n"
+ IDS_USED_X_X_TRACKS "Used: %01d:%02d (%d %s)"
+ IDS_AVAILABLE_X_X ", Available: %01d:%02d."
+ IDS_X_OVER_CAPACITY_REMOVE_X_TRACKS
+ ", %01d:%02d over capacity. Please remove %d tracks."
+END
+
+STRINGTABLE
+BEGIN
+ IDS_FILE_X_CANNOT_BE_BURNED_REASON_NOT_FOUND
+ "The file\n%s\ncannot be burned.\nReason: File not found."
+ IDS_FILE_X_CANNOT_BE_BURNED_REASON_FILETYPE_NOT_REGISTERED
+ "The file\n%s\ncannot be burned.\nReason: The file type (extension %s) is not registered with Winamp"
+ IDS_FILE_X_CANNOT_BE_BURNED_REASON_X
+ "The file\n%s\ncannot be burned.\nReason: %s"
+ IDS_VIDEO_FILES_CANNOT_BE_BURNED "Video files cannot be burned"
+ IDS_NOT_AN_AUDIO_FILE "Not an audio file"
+ IDS_FILE_CANNOT_BE_BURNED "The file\n%s\ncannot be burned."
+ IDS_TRACK_NUMBER "Track #"
+ IDS_TITLE "Title"
+ IDS_LENGTH "Length"
+ IDS_STATUS "Status"
+ IDS_CANCELLING "Cancelling..."
+ IDS_BURNING_AUDIO_CANCELLING
+ "Burning Audio CD...\nCurrent Operation: Canceling..."
+ IDS_BURNING_AUDIO_FINISHING
+ "Burning Audio CD...\nCurrent Operation: Finishing..."
+ IDS_BURNING_AUDIO_DATA_PREP_FINISHED
+ "Burning Audio CD...\nCurrent Operation: Data preparation finished."
+ IDS_BURNING_AUDIO_VERIFYING_FILES
+ "Burning Audio CD...\nCurrent Operation: Verifying files..."
+ IDS_BURNING_AUDIO_VERIFICATION_COMPLETED
+ "Burning Audio CD...\nCurrent Operation: Verification completed."
+END
+
+STRINGTABLE
+BEGIN
+ IDS_OPENING_DISC_WRITING_LEAD_IN "Opening disc / Writing Lead-In..."
+ IDS_CLOSING_DISC_WRITING_LEAD_OUT "Closing disc / Writing Lead-Out..."
+ IDS_BURNING_AUDIO_CURRENT_OPERATION
+ "Burning Audio CD... ( %d%% completed)\nCurrent Operation: %s"
+ IDS_BURNING_AUDIO_CD_PREP_DATA
+ "Burning Audio CD... ( %d%% completed)\nCurrent Operation: Preparing data..."
+ IDS_BURNING_AUDIO_BURNING_DATA
+ "Burning Audio CD... ( %d%% completed)\nCurrent Operation: Burning data..."
+ IDS_CLOSE "Close"
+ IDS_AUDIO_CD_BURNED_SUCCESSFULLY "Audio CD burned successfully."
+ IDS_BURN_ABORTED_BY_USER "Burning aborted by user."
+ IDS_BURNING_FAILED "Burning failed."
+ IDS_BURNING_COMPLETED_STATUS_X "Burning completed.\nStatus: %s"
+ IDS_ALL_FILES "All files"
+ IDS_ADD_FILES_TO_BURNING_LIST "Add file(s) to burning list"
+ IDS_CHOOSE_A_FOLDER_TO_ADD_TO_BURNING_LIST
+ "Choose a folder to add to the burning list"
+ IDS_SURE_YOU_WANT_TO_CLEAR_BURNING_LIST
+ "Are you sure you want to clear the burning list?"
+ IDS_CONFIRMATION "Confirmation"
+ IDS_BURNING_ "Burning"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_PREPARING "Preparing"
+ IDS_FINISHED "Finished"
+ IDS_PREPARED "Prepared"
+ IDS_SKIPPED "Skipped"
+ IDS_SCHEDULED "Scheduled"
+ IDS_CHECKING_LICENSE "Checking License..."
+ IDS_LICENSED "Licensed"
+ IDS_CANCELLED "Cancelled"
+ IDS_FAILED "Failed"
+ IDS_BAD_FILENAME "Bad filename"
+ IDS_UNABLE_TO_OPEN_FILE "Unable to open file"
+ IDS_CACHE_WRITE_FAILED "Cache write failed"
+ IDS_UNABLE_TO_FIND_DECODER "Unable to find decoder"
+ IDS_CANNOT_ADD_TO_THE_DISC "Cannot add to the disc"
+ IDS_CACHE_READ_FAILED "Cache read failed"
+ IDS_UNKNOWN_ERROR "Unknown Error"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_ADDING_TRACKS_TO_BURNER_TOTAL_LENGTH_X
+ "Adding tracks to burner. Total length: %01d:%02d"
+ IDS_PLEASE_INSERT_BLANK_RECORDABLE_CD
+ "Please insert a blank recordable CD in drive %c:"
+ IDS_COMPLETED "Completed"
+ IDS_QUEUED "Queued"
+ IDS_RIPPING "Ripping"
+ IDS_CANCEL_RIP "Cancel rip?"
+ IDS_CD_RIP_QUESTION "CD Rip Question"
+ IDS_INITIALIZING "Initializing..."
+ IDS_CALCULATING_REPLAY_GAIN "Calculating Replay Gain"
+ IDS_UNKNOWN_ARTIST "Unknown Artist"
+ IDS_UNKNOWN_ALBUM "Unknown Album"
+ IDS_UNKNOWN "Unknown"
+ IDS_RIP_COMPLETE "Rip Complete"
+ IDS_RIP_FAILED "Rip Failed"
+ IDS_X_TRACKS_RIPPED_IN_X
+ "%d track%s ripped in %d:%02d (%.1fx realtime)\nOutput bitrate: %dkbps, Output size: %.1fMB"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_DONE "Done"
+ IDS_ELAPSED_X_REMAINING_X_TOTAL_X
+ "Elapsed: %d:%02d, Remaining: %d:%02d, Total: %d:%02d (%.1fx realtime)\nOutput bitrate: %dkbps, Output size (estimated): %.1fMB"
+ IDS_X_KBPS_AT_X_REALTIME "%02d%%: %dkbps at %.1fx realtime"
+ IDS_X_OF_X_ELAPSED_X_REMAINING_X
+ "%d/%d: Elapsed: %d:%02d, Remaining: %d:%02d, Total: %d:%02d (%.1fx realtime)\nOutput bitrate: %dkbps, Output size (estimated): %.1fMB"
+ IDS_X_PERCENT_RIPPING_FROM_CD "[%d%%] Ripping from CD..."
+ IDS_WAITING "Waiting..."
+ IDS_STAMPED_DISC_OR_RECORDABLE_THAT_HAS_BEEN_RECORDED
+ "Stamped disc or a recordable disc that has been recorded Disc-At-Once."
+ IDS_REWRITEABLE_DISC_HAS_DATA_BUT_KEPT_OPEN_FOR_APPEND
+ "Rewritable disc that contains data but remains open, allowing the appending of additional data."
+ IDS_REWRITEABLE_DISC_NOT_POSSIBLE_TO_APPEND_DATA
+ "Rewritable disc not possible to append additional data."
+ IDS_BLANK_REWRITEABLE_DISC "Blank rewritable disc."
+ IDS_MEDIA_BLANK_DISC "Blank disc"
+ IDS_MEDIA_DATA_MODE_1_DAO "Data Mode 1 DAO"
+ IDS_MEDIA_KODAK_PHOTO_CD "Kodak Photo CD"
+ IDS_MEDIA_DATA_MULTISESSION_MODE_1_CLOSED
+ "Data multisession Mode 1 (closed)"
+ IDS_MEDIA_DATA_MULTISESSION_MODE_2_CLOSED
+ "Data multisession Mode 2 (closed)"
+ IDS_MEDIA_DATA_MODE_2_DAO "Data Mode 2 DAO"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_MEDIA_CDRFS "CDRFS"
+ IDS_MEDIA_PACKET_WRITING "Packet writing"
+ IDS_MEDIA_DATA_MULTISESSION_MODE_1_OPEN "Data multisession Mode 1 (open)"
+ IDS_MEDIA_DATA_MULTISESSION_MODE_2_OPEN "Data multisession Mode 2 (open)"
+ IDS_MEDIA_AUDIO_DAO_SAO_TAO "Audio DAO/SAO/TAO"
+ IDS_MEDIA_AUDIO_REWRITEABLE_DISC_WITH_SESSION_NOT_CLOSED
+ "Audio Rewritable disc with session not closed (TAO or SAO)"
+ IDS_MEDIA_FIRST_TYPE_OF_ENHANCED_CD_ABORTED
+ "First type of Enhanced CD (aborted)"
+ IDS_MEDIA_CD_EXTRA "CD Extra"
+ IDS_MEDIA_AUDIO_TAO_WITH_SESSION_NOT_WRITTEN
+ "Audio TAO with session not written (in-progress compilation)"
+ IDS_MEDIA_FIRST_TRACK_DATA_OTHERS_AUDIO "First track data, others audio"
+ IDS_MEDIA_MIXED_MODE_MADE_TAO "Mixed-mode made TAO"
+ IDS_MEDIA_KODAK_PORTFOLIO "Kodak Portfolio (as per the Kodak standard)"
+ IDS_MEDIA_VIDEO_CD "Video CD"
+ IDS_MEDIA_CDi "CD-i"
+ IDS_MEDIA_PLAYSTATION_SONY_GAMES "PlayStation (Sony games)"
+ IDS_MEDIA_OBSOLETE "Obsolete"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_MEDIA_OBSOLETE_FOR_RESTRICTED_OVERWRITE_DVD
+ "Obsolete for restricted overwrite DVD (DLA DVD-RW)"
+ IDS_MEDIA_DVDROM_OR_CLOSED_RECORDABLE "DVD-ROM or closed recordable"
+ IDS_MEDIA_INCREMENTAL_DVD_WITH_APPENDABLE_ZONE
+ "Incremental DVD with appendable zone (DLA DVD-R and DVD+RW)"
+ IDS_MEDIA_APPENDABLE_DVD_OF_ANY_TYPE
+ "Appendable DVD of any type (single border or multiborder)"
+ IDS_MEDIA_DVDRAM_CARTRIDGE "DVD-RAM cartridge"
+ IDS_MEDIA_CD_OTHER_TYPE "CD (other type)"
+ IDS_X_DRIVE_X "%s Drive %c:"
+ IDS_X_DRIVE_BRACKET_X "%s Drive (%c:)"
+ IDS_YOU_ARE_CURRENTLY_BURNING_AUDIO_CD_MUST_CANCEL_TO_CLOSE_WINAMP
+ "You are currently burning Audio CD in drive %c:\nYou must first cancel burning in order to close Winamp."
+ IDS_NOTIFICATION "Notification"
+ IDS_RIP_AND_BURN "Rip & Burn"
+ IDS_CD_RIPPING "CD Ripping"
+ IDS_CD_BURNER_ON_X "CD Burner on %c:"
+ IDS_EXAMPLE_RIPPED_FILE_FILENAME "Example ripped file filename:\n"
+ IDS_EXAMPLE_PLAYLIST_FILENAME "Example playlist filename:\n"
+ IDS_RIPPED_FILENAME_FORMAT_HELP "Ripped Filename Format Help"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_RIPPPED_PLAYLIST_FORMAT_HELP "Ripped Playlist Filename Format Help"
+ IDS_RIPPED_PLAYLIST_FORMAT
+ "<Album> - inserts the album (default capitalization)\r\n<ALBUM> - inserts the album (all uppercase)\r\n<album> - inserts the album (all lowercase)\r\n<Artist> - inserts the album artist (default capitalization)\r\n<ARTIST> - inserts the album artist (all uppercase)\r\n<artist> - inserts the album artist (all lowercase)\r\n<Genre> - inserts the album genre (default capitalization)\r\n<GENRE> - inserts the album genre (all uppercase)\r\n<genre> - inserts the album genre (all lowercase)\r\n<year> - inserts the album year\r\n<disc> - inserts the disc number\r\n<discs> - inserts the total discs number (e.g. CD<disc> of <discs>)"
+ IDS_RIPPED_FILENAME_FORMAT
+ "<Album> - inserts the album (default capitalization)\r\n<ALBUM> - inserts the album (all uppercase)\r\n<album> - inserts the album (all lowercase)\r\n<Artist> - inserts the album artist (default capitalization)\r\n<ARTIST> - inserts the album artist (all uppercase)\r\n<artist> - inserts the album artist (all lowercase)\r\n<Genre> - inserts the album genre (default capitalization)\r\n<GENRE> - inserts the album genre (all uppercase)\r\n<genre> - inserts the album genre (all lowercase)\r\n<Title> - inserts the track title (default capitalization)\r\n<TITLE> - inserts the track title (all uppercase)\r\n<title> - inserts the track title (all lowercase)\r\n<Trackartist> - inserts the track artist (default capitalization)\r\n<TRACKARTIST> - inserts the track artist (all uppercase)\r\n<trackartist> - inserts the track artist (all lowercase)\r\n<year> - inserts the album year\r\n#, ##, or ### - inserts the track number, with leading 0s if ## or ###\r\n<disc> - inserts the disc number\r\n<discs> - inserts the total discs number (e.g. CD<disc> of <discs>)"
+ IDS_CHOOSE_A_FOLDER "Choose a folder"
+ IDS_TO_EXTRACT_TO_MP3_NEED_TO_PURCHASE_WINAMP_PRO
+ "In order to extract to MP3, you must purchase Winamp Pro.\nWould you like information on purchasing Winamp Pro?"
+ IDS_ENCODER "Encoder"
+ IDS_OUTPUT_FILE_SETTINGS "Output File Settings"
+ IDS_PLAYLIST_GENERATION "Playlist Generation"
+ IDS_ERROR_CD_RIP_IN_PROGRESS "Error - CD Rip in progress"
+ IDS_CD_RIP "CD Rip"
+ IDS_ERROR_CD_BURN_IN_PROGRESS "Error - CD Burn in progress"
+ IDS_ERROR_CANNOT_EXTRACT_DATA_CDS "Error - Cannot extract Data CDs"
+ IDS_NO_TRACKS_TO_RIP "No tracks to rip"
+ IDS_CD_PLAYBACK_ERROR "CD Playback Error"
+ IDS_CD_EJECT "CD Eject"
+ IDS_NO_CD "No CD"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_DATA_TRACK "Data track"
+ IDS_AUDIO_TRACK "Audio track"
+ IDS_DISC_READ_ERROR "Unable to read disc info."
+ IDS_DATA_CD "Data CD"
+ IDS_DRIVE "drive"
+ IDS_ARTIST "Artist"
+ IDS_TRACK "track"
+ IDS_TRACKS "tracks"
+ IDS_YES "Yes"
+ IDS_NO "No"
+ IDS_SHOW_INFO "Show Info"
+ IDS_HIDE_INFO "Hide Info"
+ IDS_READINGDISC "Reading Disc..."
+ IDS_INFO_RIPPING "Sorry, Information unavailable while ripping cd."
+ IDS_DVD_DRIVE "DVD Drive"
+ IDS_CD_DRIVE "CD Drive"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_CD_RECORDER "CD Recorder"
+ IDS_DATA_DISC "Data Disc"
+ IDS_BLANK_DISC "Blank Disc"
+ IDS_DRIVE_CAP "Drive"
+ IDS_RECORDER_CAP "Recorder"
+ IDS_CD_AUDIO "Audio CD"
+ IDS_DISC_BLANK "Blank Disc"
+ IDS_DISC_DATA "Data Disc"
+ IDS_CD "CD"
+ IDS_DVD "DVD"
+ IDS_CALCULATING "Calculating..."
+ IDS_ML_VIEW_ARTIST_ALBUM "Artist: %s\nAlbum: %s"
+ IDS_ML_VIEW_YEAR_GENRE "Year: %s\nGenre: %s"
+ IDS_RIPPED_PLAYLIST_FORMAT_CAPTION
+ "You may enter a format string for the playlist of your ripped files.\nIt can contain \\ or / to delimit a path, and the following keywords:"
+ IDS_RIPPED_FILENAME_FORMAT_CAPTION
+ "You may enter a filename format string for your ripped files.\nIt can contain \\ or / to delimit a path, and the following keywords:"
+ IDS_COPY_FILENAME_FORMAT_TITLE "Filename Format Help"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_COPY_FILENAME_FORMAT_CAPTION
+ "You may enter a format string for your copied files.\nIt can contain \\ or / to delimit a path, and the following keywords:"
+ IDS_COPY_FILENAME_FORMAT
+ "<Album> - inserts the album (default capitalization)\r\n<ALBUM> - inserts the album (all uppercase)\r\n<album> - inserts the album (all lowercase)\r\n<Artist> - inserts the album artist (default capitalization)\r\n<ARTIST> - inserts the album artist (all uppercase)\r\n<artist> - inserts the album artist (all lowercase)\r\n<Extension> - inserts file extension (default capitalization)\r\n<EXTENSION> - inserts file extension (all uppercase)\r\n<extension> - inserts file extension (all lowercase)\r\n<Filename> - inserts file name (default capitalization)\r\n<FILENAME> - inserts file name (all uppercase)\r\n<filename> - inserts file name (all lowercase)\r\n<Genre> - inserts the album genre (default capitalization)\r\n<GENRE> - inserts the album genre (all uppercase)\r\n<genre> - inserts the album genre (all lowercase)\r\n<Title> - inserts the track title (default capitalization)\r\n<TITLE> - inserts the track title (all uppercase)\r\n<title> - inserts the track title (all lowercase)\r\n<Trackartist> - inserts the track artist (default capitalization)\r\n<TRACKARTIST> - inserts the track artist (all uppercase)\r\n<trackartist> - inserts the track artist (all lowercase)\r\n<year> - inserts the album year\r\n#, ##, or ### - inserts the track number, with leading 0s if ## or ###"
+ IDS_OPTIONS_SHOW "Show Options"
+ IDS_OPTIONS_HIDE "Hide Options"
+ IDS_COPY_PREP_MESSAGE_SINGLE_FILE
+ "Are you sure you want to copy '%s' to '%s'?"
+ IDS_COPY_PREP_MESSAGE_MULTIPLE_FILES
+ "Are you sure you want to copy these %d files to '%s'?"
+ IDS_COPY_TASK_PREPARE "Initializing..."
+ IDS_COPY_TASK_COPY "Copying..."
+ IDS_COPY_OP_CALCULATESIZE "Calculating total size..."
+ IDS_COPY_OP_CHECKDESTINATION "Checking destination..."
+ IDS_COPY_TASK_FINISHED "Completed"
+ IDS_COPY_ERROR_CAPTION "File Copy Error"
+ IDS_COPY_ERROR_MESSAGE "Unable to copy files to '%s'.\n\nReason:\t %s.\nCode:\t %d\nDetails:\t %s"
+ IDS_COPY_ERRMSG_INITIALIZATION_FAILED "Initialization failed"
+ IDS_COPY_ERRMSG_DIRECTORYCREATE_FAILED "Unable to create directory"
+ IDS_COPY_ERRMSG_COPYFILE_FAILED "Unable to copy file"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_COPY_ERRMSG_TITLEFORMAT_FAILED "File title formatting failed"
+ IDS_COPY_ERRMSG_ADDTOMLDB_FAILED
+ "Unable to add file to local media database"
+ IDS_COPY_ERRMSG_SETATTRIBUTES_FAILED "Unable to set file attributes"
+ IDS_COPY_ERRMSG_COPYFILE_USERABORT "User abort"
+ IDS_COPY_ERRMSG_DELETEFILE_FAILED "Unable to delete file"
+ IDS_CONFIRM_CREATE_DESTINATION "Confirm Create Destination"
+ IDS_DESTINATION_NOT_EXIST_FORMAT
+ "Destination path '%s' does not exist.\nDo you want to create it?"
+ IDS_CANCEL "Cancel"
+ IDS_SKIP "Skip"
+ IDS_OVERWRITE "Overwrite"
+ IDS_APPLY_TO_ALL_FILES "Apply to all files"
+ IDS_CONFIRM_FILE_REPLACE "Confirm File Replace"
+ IDS_FILE_REPLACE_FORMAT "Destination folder '%s' already contains file named '%s'.\n\nWould you like to replace existing file %s with this one? %s"
+ IDS_CONFIRM_FILE_DELETE "Confirm File Delete"
+ IDS_READONLY_FILE_DELETE_FORMAT
+ "The file '%s' is a read-only file.\nAre you sure you want to delete it? "
+ IDS_SIZE "Size"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_CREATED "Created"
+ IDS_MODIFIED "Modified"
+ IDS_UNKNOWN_GENRE "Unknown Genre"
+ IDS_YOU_ARE_CURRENTLY_COPYING_DATA_CD_MUST_CANCEL_TO_CLOSE_WINAMP
+ "You are currently copying files from drive %c:\nYou must first cancel copying in order to close Winamp."
+ IDS_YOU_ARE_CURRENTLY_RIPPING_AUDIO_CD_MUST_CANCEL_TO_CLOSE_WINAMP
+ "You are currently ripping Audio CD in drive %c:\nYou must first cancel ripping in order to close Winamp."
+ IDS_ERROR_RIPPING_TRACK "Error ripping track #%d: %s\n\nAborting."
+ IDS_NO_INFO_AVAILABLE "No information available"
+END
+
+#endif // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc2"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/Plugins/Library/ml_disc/ml_disc.sln b/Src/Plugins/Library/ml_disc/ml_disc.sln
new file mode 100644
index 00000000..6fa5a23a
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/ml_disc.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}") = "ml_disc", "ml_disc.vcxproj", "{CAEC36CE-5A74-4C31-9956-E2FF8713D26F}"
+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
+ {CAEC36CE-5A74-4C31-9956-E2FF8713D26F}.Debug|Win32.ActiveCfg = Debug|Win32
+ {CAEC36CE-5A74-4C31-9956-E2FF8713D26F}.Debug|Win32.Build.0 = Debug|Win32
+ {CAEC36CE-5A74-4C31-9956-E2FF8713D26F}.Debug|x64.ActiveCfg = Debug|x64
+ {CAEC36CE-5A74-4C31-9956-E2FF8713D26F}.Debug|x64.Build.0 = Debug|x64
+ {CAEC36CE-5A74-4C31-9956-E2FF8713D26F}.Release|Win32.ActiveCfg = Release|Win32
+ {CAEC36CE-5A74-4C31-9956-E2FF8713D26F}.Release|Win32.Build.0 = Release|Win32
+ {CAEC36CE-5A74-4C31-9956-E2FF8713D26F}.Release|x64.ActiveCfg = Release|x64
+ {CAEC36CE-5A74-4C31-9956-E2FF8713D26F}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {4B5DC2A4-73F3-41B4-A1E4-5F556BDE113D}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/Plugins/Library/ml_disc/ml_disc.vcxproj b/Src/Plugins/Library/ml_disc/ml_disc.vcxproj
new file mode 100644
index 00000000..a20771ca
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/ml_disc.vcxproj
@@ -0,0 +1,382 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{CAEC36CE-5A74-4C31-9956-E2FF8713D26F}</ProjectGuid>
+ <RootNamespace>ml_disc</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>
+ <EmbedManifest>true</EmbedManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <EmbedManifest>true</EmbedManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ <EmbedManifest>true</EmbedManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <EmbedManifest>true</EmbedManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnableManifest>false</VcpkgEnableManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <InlineFunctionExpansion>Default</InlineFunctionExpansion>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_DEBUG;_WINDOWS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <SmallerTypeCheck>false</SmallerTypeCheck>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4838;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;comctl32.lib;winmm.lib;setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ </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>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Manifest>
+ <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile>
+ </Manifest>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <InlineFunctionExpansion>Default</InlineFunctionExpansion>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;_DEBUG;_WINDOWS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <SmallerTypeCheck>false</SmallerTypeCheck>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4244;4838;4090;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;comctl32.lib;winmm.lib;setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ </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>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Manifest>
+ <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile>
+ </Manifest>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;NDEBUG;_WINDOWS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4838;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ShowIncludes>false</ShowIncludes>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;comctl32.lib;winmm.lib;setupapi.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>
+ <ModuleDefinitionFile>
+ </ModuleDefinitionFile>
+ </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>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Manifest>
+ <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile>
+ </Manifest>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;NDEBUG;_WINDOWS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4244;4838;4090;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ShowIncludes>false</ShowIncludes>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;comctl32.lib;winmm.lib;setupapi.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>
+ <ModuleDefinitionFile>
+ </ModuleDefinitionFile>
+ </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>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Manifest>
+ <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile>
+ </Manifest>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\General\gen_ml\gaystring.cpp" />
+ <ClCompile Include="..\..\General\gen_ml\graphics.cpp" />
+ <ClCompile Include="..\..\General\gen_ml\menu.cpp" />
+ <ClCompile Include="..\..\General\gen_ml\ml_lib.cpp" />
+ <ClCompile Include="..\..\..\nu\ChildSizer.cpp" />
+ <ClCompile Include="..\..\..\nu\DialogSkinner.cpp" />
+ <ClCompile Include="..\..\..\nu\listview.cpp" />
+ <ClCompile Include="..\..\..\nu\MediaLibraryInterface.cpp" />
+ <ClCompile Include="..\..\..\nu\menushortcuts.cpp" />
+ <ClCompile Include="..\..\..\nu\trace.cpp" />
+ <ClCompile Include="..\..\..\Winamp\strutil.cpp" />
+ <ClCompile Include="cdburn.cpp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="cdrip.cpp" />
+ <ClCompile Include="cmdbar_data.cpp" />
+ <ClCompile Include="commandbar.cpp" />
+ <ClCompile Include="config.cpp" />
+ <ClCompile Include="copyfiles.cpp" />
+ <ClCompile Include="copyfiles_post.cpp" />
+ <ClCompile Include="copyprep.cpp" />
+ <ClCompile Include="copyprogress.cpp" />
+ <ClCompile Include="drive.cpp" />
+ <ClCompile Include="driveListBox.cpp" />
+ <ClCompile Include="drivemngr.cpp" />
+ <ClCompile Include="formatfilename.cpp" />
+ <ClCompile Include="helpwnd.cpp" />
+ <ClCompile Include="infoBox.cpp" />
+ <ClCompile Include="M3UWriter.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="medium.cpp" />
+ <ClCompile Include="PLSWriter.cpp" />
+ <ClCompile Include="prefs.cpp" />
+ <ClCompile Include="primosdk_helper.cpp" />
+ <ClCompile Include="questionwnd.cpp" />
+ <ClCompile Include="ReplayGain.cpp" />
+ <ClCompile Include="settings.cpp" />
+ <ClCompile Include="spti.cpp" />
+ <ClCompile Include="view_cdrom.cpp" />
+ <ClCompile Include="view_container.cpp" />
+ <ClCompile Include="view_data.cpp" />
+ <ClCompile Include="view_info.cpp" />
+ <ClCompile Include="view_ripburn.cpp" />
+ <ClCompile Include="view_wait.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\General\gen_ml\gaystring.h" />
+ <ClInclude Include="..\..\General\gen_ml\menu.h" />
+ <ClInclude Include="..\..\..\nu\listview.h" />
+ <ClInclude Include="..\..\..\Winamp\strutil.h" />
+ <ClInclude Include="api__ml_disc.h" />
+ <ClInclude Include="commandbar.h" />
+ <ClInclude Include="config.h" />
+ <ClInclude Include="copyfiles.h" />
+ <ClInclude Include="copyinternal.h" />
+ <ClInclude Include="drive.h" />
+ <ClInclude Include="driveListBox.h" />
+ <ClInclude Include="drivemngr.h" />
+ <ClInclude Include="infoBox.h" />
+ <ClInclude Include="M3UWriter.h" />
+ <ClInclude Include="main.h" />
+ <ClInclude Include="medium.h" />
+ <ClInclude Include="PLSWriter.h" />
+ <ClInclude Include="primosdk_helper.h" />
+ <ClInclude Include="ReplayGain.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="settings.h" />
+ <ClInclude Include="spti.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <Image Include="resources\cdrom_32x32_24.bmp" />
+ <Image Include="resources\filecopy.bmp" />
+ <Image Include="resources\listbox_back_2x68x24.bmp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="ml_disc.rc" />
+ <ResourceCompile Include="png.rc" />
+ </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/Library/ml_disc/ml_disc.vcxproj.filters b/Src/Plugins/Library/ml_disc/ml_disc.vcxproj.filters
new file mode 100644
index 00000000..0f828c34
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/ml_disc.vcxproj.filters
@@ -0,0 +1,250 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="cdrip.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="cdburn.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="cmdbar_data.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="commandbar.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="config.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="copyfiles.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="copyfiles_post.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="copyprep.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="copyprogress.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="drive.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="driveListBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="drivemngr.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="formatfilename.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="helpwnd.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="infoBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="M3UWriter.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="medium.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="PLSWriter.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="prefs.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="primosdk_helper.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="questionwnd.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ReplayGain.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="settings.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="spti.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="view_cdrom.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="view_container.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="view_data.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="view_info.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="view_ripburn.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="view_wait.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\General\gen_ml\graphics.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\ChildSizer.cpp">
+ <Filter>Source Files\nu</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\DialogSkinner.cpp">
+ <Filter>Source Files\nu</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\General\gen_ml\gaystring.cpp">
+ <Filter>Source Files\gen_ml</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\listview.cpp">
+ <Filter>Source Files\nu</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\MediaLibraryInterface.cpp">
+ <Filter>Source Files\nu</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\General\gen_ml\menu.cpp">
+ <Filter>Source Files\gen_ml</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\menushortcuts.cpp">
+ <Filter>Source Files\nu</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\General\gen_ml\ml_lib.cpp">
+ <Filter>Source Files\gen_ml</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Winamp\strutil.cpp">
+ <Filter>Source Files\Winamp</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\trace.cpp">
+ <Filter>Source Files\nu</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api__ml_disc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="commandbar.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="config.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="copyfiles.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="copyinternal.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="drive.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="driveListBox.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="drivemngr.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="infoBox.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="M3UWriter.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="medium.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="PLSWriter.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="primosdk_helper.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ReplayGain.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="settings.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="spti.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\General\gen_ml\gaystring.h">
+ <Filter>Header Files\gen_ml</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nu\listview.h">
+ <Filter>Header Files\nu</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\General\gen_ml\menu.h">
+ <Filter>Header Files\gen_ml</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Winamp\strutil.h">
+ <Filter>Header Files\Winamp</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="png.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ <ResourceCompile Include="ml_disc.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <Image Include="resources\cdrom_32x32_24.bmp">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\filecopy.bmp">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\listbox_back_2x68x24.bmp">
+ <Filter>Image Files</Filter>
+ </Image>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{1b8ee540-fd62-43d4-8cca-be075c8c8d67}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{e018c8c5-3a48-41af-bf92-d63db4e43326}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{0b7fb4d3-0bfd-4ef3-90f2-d969de7e4400}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Image Files">
+ <UniqueIdentifier>{191771a6-3744-4d06-a476-64f3d85cdd56}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\gen_ml">
+ <UniqueIdentifier>{12bdb53c-e56b-4403-bb0c-7fbe8226593c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\nu">
+ <UniqueIdentifier>{64766d69-e8b1-4432-a8f3-d082202e7362}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Winamp">
+ <UniqueIdentifier>{bcf8a930-ff77-437f-a229-e985d9675be1}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\nu">
+ <UniqueIdentifier>{91f09635-9292-4055-b23d-613bb045199b}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\gen_ml">
+ <UniqueIdentifier>{615eddd8-7e7c-4526-a8bf-1afe093a8149}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Winamp">
+ <UniqueIdentifier>{ac350456-8c89-462f-b8c1-8868c38dede8}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/png.rc b/Src/Plugins/Library/ml_disc/png.rc
new file mode 100644
index 00000000..3ae6cdb0
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/png.rc
@@ -0,0 +1,45 @@
+#include "resource.h"
+/////////////////////////////////////////////////////////////////////////////
+//
+// Data
+//
+IDB_NAVITEM_CDROM RCDATA
+".\\resources\\cdrom.png"
+IDB_EJECT_NORMAL RCDATA
+".\\resources\\eject3.png"
+IDB_EJECT_HILITED RCDATA
+".\\resources\\eject1.png"
+IDB_EJECT_PRESSED RCDATA
+".\\resources\\eject2.png"
+IDB_EJECT_DISABLED RCDATA
+".\\resources\\eject4.png"
+IDB_PLAY_NORMAL RCDATA
+".\\resources\\play_n.png"
+IDB_PLAY_HIGHLIGHTED RCDATA
+".\\resources\\play_p.png"
+IDB_PLAY_DISABLED RCDATA
+".\\resources\\play_d.png"
+IDB_PLAY_PRESSED RCDATA
+".\\resources\\play_p.png"
+IDB_ENQUEUE_NORMAL RCDATA
+".\\resources\\enqueue_n.png"
+IDB_ENQUEUE_HIGHLIGHTED RCDATA
+".\\resources\\enqueue_p.png"
+IDB_ENQUEUE_DISABLED RCDATA
+".\\resources\\enqueue_d.png"
+IDB_ENQUEUE_PRESSED RCDATA
+".\\resources\\enqueue_p.png"
+IDB_EJECT2_NORMAL RCDATA
+".\\resources\\eject_n.png"
+IDB_EJECT2_HIGHLIGHTED RCDATA
+".\\resources\\eject_p.png"
+IDB_EJECT2_DISABLED RCDATA
+".\\resources\\eject_d.png"
+IDB_EJECT2_PRESSED RCDATA
+".\\resources\\eject_p.png"
+IDB_FILECOPY RCDATA
+".\\resources\\filecopy.png"
+IDB_PLAY_MENU RCDATA
+".\\resources\\play_menu_16x16.png"
+IDB_ENQUEUE_MENU RCDATA
+".\\resources\\enqueue_menu_16x16.png"
diff --git a/Src/Plugins/Library/ml_disc/prefs.cpp b/Src/Plugins/Library/ml_disc/prefs.cpp
new file mode 100644
index 00000000..f145f984
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/prefs.cpp
@@ -0,0 +1,351 @@
+#include "main.h"
+#include "../nu/AutoWide.h"
+#include "./resource.h"
+#include "./settings.h"
+#include "../Winamp/wa_ipc.h"
+#include <strsafe.h>
+
+static convertConfigStruct m_ccs;
+static int m_has_seled;
+
+static void myEnumProc(intptr_t user_data, const char *desc, int fourcc)
+{
+ HWND hwndDlg = (HWND) user_data;
+ if (fourcc == OLD_AAC_CODEC)
+ return ;
+
+ int a = (INT)SendDlgItemMessage(hwndDlg, IDC_ENCFORMAT, CB_ADDSTRING, 0, (LPARAM)(const wchar_t *)AutoWide(desc));
+
+ SendDlgItemMessage(hwndDlg, IDC_ENCFORMAT, CB_SETITEMDATA, (WPARAM)a, fourcc);
+
+ if ( m_ccs.format == fourcc )
+ {
+ m_has_seled = 1;
+ SendDlgItemMessage( hwndDlg, IDC_ENCFORMAT, CB_SETCURSEL, (WPARAM)a, 0 );
+ }
+}
+
+static void doConfigResizeChild(HWND parent, HWND child)
+{
+ if (child)
+ {
+ RECT r;
+ GetWindowRect(GetDlgItem(parent, IDC_ENC_CONFIG), &r);
+ ScreenToClient(parent, (LPPOINT)&r);
+ SetWindowPos(child, 0, r.left, r.top, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
+ ShowWindow(child, SW_SHOWNA);
+ }
+}
+
+static HWND subWnd;
+
+static void DisplayFormatExample(HWND hdlg, INT nItemId, BOOL bFile)
+{
+ BOOL bUpper;
+ TCHAR szBuffer[MAX_PATH*2] = {0};
+ TCHAR szFormat[MAX_PATH] = {0};
+
+ Settings_ReadString(C_EXTRACT, (bFile) ? EF_TITLEFMT : EF_PLAYLISTFMT, szFormat, ARRAYSIZE(szFormat));
+
+ WASABI_API_LNGSTRINGW_BUF(((bFile) ? IDS_EXAMPLE_RIPPED_FILE_FILENAME : IDS_EXAMPLE_PLAYLIST_FILENAME),
+ szBuffer, ARRAYSIZE(szBuffer));
+
+ FormatFileName(szBuffer, ARRAYSIZE(szBuffer), szFormat,
+ (bFile) ? 10 : 0xdeadbeef,
+ TEXT("U2"), TEXT("The Joshua Tree"),
+ (bFile) ? TEXT("Exit") : NULL,
+ TEXT("Rock"),
+ TEXT("1987"),
+ TEXT("U2"),
+ NULL,
+ TEXT(""));
+
+ wchar_t szExtension[32] = {0};
+ if (bFile)
+ {
+ int c;
+ Settings_GetInt(C_EXTRACT, EF_FOURCC, &c);
+ if (c == OLD_AAC_CODEC) Settings_GetDefault(C_EXTRACT, EF_FOURCC, &c);
+ GetExtensionString(szExtension, ARRAYSIZE(szExtension), c);
+
+ Settings_GetBool(C_EXTRACT, EF_UPPEREXTENSION, &bUpper);
+ if (bUpper) CharUpper(szExtension);
+ else CharLower(szExtension);
+ }
+ else StringCchCopy(szExtension, ARRAYSIZE(szExtension), TEXT("m3u"));
+
+ StringCchCat(szBuffer, ARRAYSIZE(szBuffer), TEXT("."));
+ StringCchCat(szBuffer, ARRAYSIZE(szBuffer), szExtension);
+ SetDlgItemText(hdlg, nItemId, szBuffer);
+}
+
+static INT_PTR CALLBACK CDPrefs1Proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ static HWND hActiveHelp = NULL;
+
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ SendDlgItemMessage(hwndDlg, IDC_DESTPATH, EM_SETLIMITTEXT, MAX_PATH, 0);
+ Settings_SetCheckBox(C_EXTRACT, EF_UPPEREXTENSION, hwndDlg, IDC_UPPERCASEEXT);
+ Settings_SetDirectoryCtrl(C_EXTRACT, EF_PATH, hwndDlg, IDC_DESTPATH);
+ Settings_SetDlgItemText(C_EXTRACT, EF_TITLEFMT, hwndDlg, IDC_FILENAMEFMT);
+ Settings_SetCheckBox(C_EXTRACT, EF_ADDMETADATA, hwndDlg, IDC_TAGFILES);
+ Settings_SetCheckBox(C_EXTRACT, EF_CALCULATERG, hwndDlg, IDC_AUTO_RG);
+ Settings_SetCheckBox(C_EXTRACT, EF_USETOTALTRACKS, hwndDlg, IDC_TOTAL_TRACKS);
+ Settings_SetCheckBox(C_EXTRACT, EF_ADDTOMLDB, hwndDlg, IDC_CHECK_ML);
+ Settings_SetDlgItemInt(C_EXTRACT, EF_TRACKOFFSET, hwndDlg, IDC_EDIT2);
+ Settings_SetDlgItemText(C_EXTRACT, EF_COMMENTTEXT, hwndDlg, IDC_EDIT1);
+
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_DESTPATH:
+ if (HIWORD(wParam) == EN_CHANGE) Settings_FromDirectoryCtrl(C_EXTRACT, EF_PATH, hwndDlg,IDC_DESTPATH);
+ break;
+ case IDC_UPPERCASEEXT:
+ Settings_FromCheckBox(C_EXTRACT, EF_UPPEREXTENSION, hwndDlg, IDC_UPPERCASEEXT);
+ DisplayFormatExample(hwndDlg, IDC_FMTOUT, TRUE);
+ break;
+ case IDC_FILENAMEFMT:
+ if (HIWORD(wParam) == EN_CHANGE)
+ {
+ Settings_FromDlgItemText(C_EXTRACT, EF_TITLEFMT, hwndDlg, IDC_FILENAMEFMT);
+ DisplayFormatExample(hwndDlg, IDC_FMTOUT, TRUE);
+ }
+ break;
+ case IDC_BUTTON1:
+ Settings_BrowseForFolder(C_EXTRACT, EF_PATH, hwndDlg, IDC_DESTPATH);
+ break;
+ case IDC_BUTTON2:
+ if (hActiveHelp) SetWindowPos(hActiveHelp, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
+ else hActiveHelp = MLDisc_ShowHelp(hwndDlg, MAKEINTRESOURCE(IDS_RIPPED_FILENAME_FORMAT_HELP),
+ MAKEINTRESOURCE(IDS_RIPPED_FILENAME_FORMAT_CAPTION), MAKEINTRESOURCE(IDS_RIPPED_FILENAME_FORMAT), HF_ALLOWRESIZE);
+ break;
+ case IDC_EDIT2: if (EN_CHANGE == HIWORD(wParam)) Settings_FromDlgItemText(C_EXTRACT, EF_TRACKOFFSET, hwndDlg, IDC_EDIT2);break;
+ case IDC_EDIT1: if (EN_CHANGE == HIWORD(wParam)) Settings_FromDlgItemText(C_EXTRACT, EF_COMMENTTEXT, hwndDlg, IDC_EDIT1); break;
+ case IDC_AUTO_RG: if (BN_CLICKED == HIWORD(wParam)) Settings_FromCheckBox(C_EXTRACT, EF_CALCULATERG, hwndDlg, IDC_AUTO_RG); break;
+ case IDC_TOTAL_TRACKS: if (BN_CLICKED == HIWORD(wParam)) Settings_FromCheckBox(C_EXTRACT, EF_USETOTALTRACKS, hwndDlg, IDC_TOTAL_TRACKS); break;
+ case IDC_TAGFILES: if (BN_CLICKED == HIWORD(wParam)) Settings_FromCheckBox(C_EXTRACT, EF_ADDMETADATA, hwndDlg, IDC_TAGFILES); break;
+ case IDC_CHECK_ML: if (BN_CLICKED == HIWORD(wParam)) Settings_FromCheckBox(C_EXTRACT, EF_ADDTOMLDB, hwndDlg, IDC_CHECK_ML); break;
+
+ }
+ break;
+ case WM_DESTROY:
+ if (hActiveHelp) DestroyWindow(hActiveHelp);
+ break;
+ case WM_PARENTNOTIFY:
+ if (hActiveHelp && LOWORD(wParam) == WM_DESTROY && hActiveHelp == (HWND)lParam)
+ hActiveHelp = NULL;
+ break;
+ }
+ return 0;
+}
+
+int getRegVer();
+
+static INT_PTR CALLBACK CDPrefs2Proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ m_ccs.hwndParent = hwndDlg;
+ Settings_GetInt(C_EXTRACT, EF_FOURCC, &m_ccs.format);
+ if (m_ccs.format == OLD_AAC_CODEC) Settings_GetDefault(C_EXTRACT, EF_FOURCC, &m_ccs.format);
+
+ converterEnumFmtStruct enumf = { myEnumProc, (INT)(INT_PTR)hwndDlg };
+ m_has_seled = 0;
+ SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&enumf, IPC_CONVERT_CONFIG_ENUMFMTS);
+ if (!m_has_seled)
+ {
+ SendDlgItemMessage(hwndDlg, IDC_ENCFORMAT, CB_SETCURSEL, 0, 0);
+ m_ccs.format = mmioFOURCC('W', 'A', 'V', ' ');
+ }
+
+ HWND h = (HWND)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM) & m_ccs, IPC_CONVERT_CONFIG);
+ doConfigResizeChild(hwndDlg, h);
+ }
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_ENCFORMAT:
+ if (HIWORD(wParam) != CBN_SELCHANGE) return 0;
+ {
+ int sel = (INT)(INT_PTR)SendDlgItemMessage(hwndDlg, IDC_ENCFORMAT, CB_GETCURSEL, 0, 0);
+ if (sel != CB_ERR)
+ {
+ SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&m_ccs, IPC_CONVERT_CONFIG_END);
+ int last = m_ccs.format;
+ if (RegisteredEncoder(last) || last == OLD_AAC_CODEC) Settings_GetDefault(C_EXTRACT, EF_FOURCC, &last);
+
+ m_ccs.format = (int)SendDlgItemMessage(hwndDlg, IDC_ENCFORMAT, CB_GETITEMDATA, sel, 0);
+ Settings_SetInt(C_EXTRACT, EF_FOURCC, m_ccs.format);
+
+ HWND h = (HWND)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM) & m_ccs, IPC_CONVERT_CONFIG);
+ doConfigResizeChild(hwndDlg, h);
+ }
+ }
+ break;
+ }
+ break;
+ case WM_DESTROY:
+ SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&m_ccs, IPC_CONVERT_CONFIG_END);
+ break;
+ }
+ return 0;
+}
+
+
+static INT_PTR CALLBACK CDPrefs4Proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ static HWND hActiveHelp = NULL;
+
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ Settings_SetDlgItemText(C_EXTRACT, EF_PLAYLISTFMT, hwndDlg, IDC_FILENAMEFMT);
+ Settings_SetCheckBox(C_EXTRACT, EF_CREATEM3U, hwndDlg, IDC_CHECK1);
+ Settings_SetCheckBox(C_EXTRACT, EF_USEM3UEXT, hwndDlg, IDC_CHECK3);
+ Settings_SetCheckBox(C_EXTRACT, EF_CREATEPLS, hwndDlg, IDC_CHECK2);
+ Settings_SetCheckBox(C_EXTRACT, EF_CREATEMLPL, hwndDlg, IDC_CHECK4);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK3), IsDlgButtonChecked(hwndDlg, IDC_CHECK1));
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_FILENAMEFMT:
+ if (LOWORD(wParam) != IDC_FILENAMEFMT || HIWORD(wParam) == EN_CHANGE)
+ {
+ Settings_FromDlgItemText(C_EXTRACT, EF_PLAYLISTFMT, hwndDlg, IDC_FILENAMEFMT);
+ DisplayFormatExample(hwndDlg, IDC_FMTOUT, FALSE);
+ }
+ return 0;
+ case IDC_BUTTON2:
+ if (hActiveHelp) SetWindowPos(hActiveHelp, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
+ else hActiveHelp = MLDisc_ShowHelp(hwndDlg, MAKEINTRESOURCE(IDS_RIPPPED_PLAYLIST_FORMAT_HELP),
+ MAKEINTRESOURCE(IDS_RIPPED_PLAYLIST_FORMAT_CAPTION), MAKEINTRESOURCE(IDS_RIPPED_PLAYLIST_FORMAT), HF_ALLOWRESIZE);
+ break;
+ case IDC_CHECK1:
+ if (BN_CLICKED == HIWORD(wParam)) Settings_FromCheckBox(C_EXTRACT, EF_CREATEM3U, hwndDlg, IDC_CHECK1);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK3), IsDlgButtonChecked(hwndDlg, IDC_CHECK1));
+ break;
+
+ case IDC_CHECK3: if (BN_CLICKED == HIWORD(wParam)) Settings_FromCheckBox(C_EXTRACT, EF_USEM3UEXT, hwndDlg, IDC_CHECK3); break;
+ case IDC_CHECK2: if (BN_CLICKED == HIWORD(wParam)) Settings_FromCheckBox(C_EXTRACT, EF_CREATEPLS, hwndDlg, IDC_CHECK2); break;
+ case IDC_CHECK4: if (BN_CLICKED == HIWORD(wParam)) Settings_FromCheckBox(C_EXTRACT, EF_CREATEMLPL, hwndDlg, IDC_CHECK4); break;
+ }
+ break;
+
+ case WM_DESTROY:
+ if (hActiveHelp) DestroyWindow(hActiveHelp);
+ break;
+ case WM_PARENTNOTIFY:
+ if (hActiveHelp && LOWORD(wParam) == WM_DESTROY && hActiveHelp == (HWND)lParam)
+ hActiveHelp = NULL;
+ break;
+ }
+ return 0;
+}
+
+
+static int has_extract;
+
+static void _dosetsel(HWND hwndDlg)
+{
+ HWND tabwnd = GetDlgItem(hwndDlg, IDC_TAB1);
+ int sel = TabCtrl_GetCurSel(tabwnd);
+
+ if (sel >= 0 && (sel != g_config->ReadInt(L"lastcdprefp", 0) || !subWnd))
+ {
+ g_config->WriteInt(L"lastcdprefp", sel);
+ if (subWnd) DestroyWindow(subWnd);
+ subWnd = 0;
+
+ UINT t = 0;
+ DLGPROC p = NULL;
+ if (!has_extract && sel) sel++;
+ switch (sel)
+ {
+ case 2: t = IDD_PREFS_CDRIP1; p = CDPrefs1Proc; break;
+ case 0: t = IDD_PREFS_CDRIP2; p = CDPrefs2Proc; break;
+ case 3: t = IDD_PREFS_CDRIP4; p = CDPrefs4Proc; break;
+ case 1:
+ {
+ t = 0;
+ char buf2[512] = {0};
+ char buf3[512] = {0};
+ StringCchPrintfA(buf3, 512, "cdda_cf_%d", (INT)(INT_PTR)hwndDlg);
+ getFileInfo("cda://", buf3, buf2, sizeof(buf2));
+ subWnd = (HWND)(INT_PTR)atoi(buf2);
+ }
+ break;
+ default: subWnd = 0; t = 0; break;
+ }
+ if (t) subWnd = WASABI_API_CREATEDIALOGW(t, hwndDlg, p);
+
+ if (subWnd)
+ {
+ RECT r;
+ GetClientRect(tabwnd, &r);
+ TabCtrl_AdjustRect(tabwnd, FALSE, &r);
+ SetWindowPos(subWnd, HWND_TOP, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOACTIVATE);
+ ShowWindow(subWnd, SW_SHOWNA);
+ }
+
+ if(!SendMessage(plugin.hwndWinampParent,WM_WA_IPC,IPC_ISWINTHEMEPRESENT,IPC_USE_UXTHEME_FUNC))
+ {
+ SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)tabwnd,IPC_USE_UXTHEME_FUNC);
+ SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)subWnd,IPC_USE_UXTHEME_FUNC);
+ }
+ }
+}
+
+
+BOOL CALLBACK CDRipPrefsProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ TCITEM item;
+ HWND tabwnd = GetDlgItem(hwndDlg, IDC_TAB1);
+ item.mask = TCIF_TEXT;
+ item.pszText = WASABI_API_LNGSTRINGW(IDS_ENCODER);
+ TabCtrl_InsertItem(tabwnd, 0, &item);
+
+ wchar_t buf2[512] = {0};
+ getFileInfoW(L"cda://", L"cdda_config_text", buf2, 512);
+
+ if (buf2[0])
+ {
+ item.pszText = buf2;
+ TabCtrl_InsertItem(tabwnd, 3, &item);
+ has_extract = 1;
+ }
+ else has_extract = 0;
+
+ item.pszText = WASABI_API_LNGSTRINGW(IDS_OUTPUT_FILE_SETTINGS);
+ TabCtrl_InsertItem(tabwnd, 1 + has_extract, &item);
+ item.pszText = WASABI_API_LNGSTRINGW(IDS_PLAYLIST_GENERATION);
+ TabCtrl_InsertItem(tabwnd, 2 + has_extract, &item);
+
+ TabCtrl_SetCurSel(tabwnd, g_config->ReadInt(L"lastcdprefp", 0));
+ _dosetsel(hwndDlg);
+ }
+ return 0;
+ case WM_NOTIFY:
+ {
+ LPNMHDR p = (LPNMHDR) lParam;
+ if (p->idFrom == IDC_TAB1 && p->code == TCN_SELCHANGE) _dosetsel(hwndDlg);
+ }
+ return 0;
+ case WM_DESTROY:
+ subWnd = NULL;
+ return 0;
+ }
+ return 0;
+}
+
diff --git a/Src/Plugins/Library/ml_disc/primosdk_helper.cpp b/Src/Plugins/Library/ml_disc/primosdk_helper.cpp
new file mode 100644
index 00000000..60b32ff2
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/primosdk_helper.cpp
@@ -0,0 +1,109 @@
+#include "./primosdk_helper.h"
+//#include "../primo/obj_primo.h"
+#include "api__ml_disc.h"
+#include <api/service/waservicefactory.h>
+
+typedef struct _PRIMOSDK_INSTANCE
+{
+// obj_primo *primo;
+ BOOL bLoadFailed;
+ LONG uRef;
+} PRIMOSDK_INSTANCE;
+
+static PRIMOSDK_INSTANCE sdk = {/*NULL,*/ FALSE, 0, };
+
+BOOL PrimoSDKHelper_IsInitialized(void)
+{
+ return (0 != sdk.uRef);
+}
+
+BOOL PrimoSDKHelper_IsLoaded(void)
+{
+ /*char t[64] = {0};
+ wsprintfA(t,"%d %d %d\n%d",sdk.bLoadFailed, sdk.primo, sdk.uRef, !(!sdk.uRef || !sdk.primo));
+ MessageBoxA(0,t,0,0);*/
+ return !(!sdk.uRef /*|| !sdk.primo*/);//(sdk.bLoadFailed==FALSE);
+}
+
+LONG PrimoSDKHelper_Initialize(void)
+{
+ return 0;
+#if 0
+ if (sdk.bLoadFailed) return 0;
+
+ if (!sdk.uRef)
+ {
+ BOOL bFailed = TRUE;
+ waServiceFactory *sf = plugin.service->service_getServiceByGuid(obj_primo::getServiceGuid());
+ if (sf) sdk.primo = reinterpret_cast<obj_primo *>(sf->getInterface());
+ if (sdk.primo)
+ bFailed = FALSE;
+
+ sdk.bLoadFailed += bFailed;
+ }
+ InterlockedIncrement(&sdk.uRef);
+
+ return sdk.uRef;
+#endif
+}
+
+LONG PrimoSDKHelper_Uninitialize(void)
+{
+ return 0;
+#if 0
+ if (sdk.uRef && 0 == InterlockedDecrement(&sdk.uRef))
+ {
+ if (sdk.primo)
+ {
+ waServiceFactory *sf = plugin.service->service_getServiceByGuid(obj_primo::getServiceGuid());
+ if (sf) sf->releaseInterface(sdk.primo);
+ sdk.primo = 0;
+ }
+ }
+ return sdk.uRef;
+#endif
+}
+
+DWORD PrimoSDKHelper_UnitInfo(PDWORD pdwUnit, PDWORD pdwType, PBYTE szDescr, PDWORD pdwReady)
+{
+ return PRIMOSDK_CMDSEQUENCE;
+#if 0
+ if (!sdk.uRef) return PRIMOSDK_CMDSEQUENCE;
+ if (!sdk.primo) return PRIMOSDK_CMDSEQUENCE;
+
+ return sdk.primo->UnitInfo(pdwUnit, pdwType, szDescr, pdwReady);
+#endif
+}
+
+DWORD PrimoSDKHelper_UnitInfo2(PDWORD pdwUnit, PDWORD pdwTypes, PDWORD pdwClass, PDWORD pdwBusType, PDWORD pdwRFU)
+{
+return PRIMOSDK_CMDSEQUENCE;
+#if 0
+ if (!sdk.uRef) return PRIMOSDK_CMDSEQUENCE;
+ if (!sdk.primo) return PRIMOSDK_CMDSEQUENCE;
+
+ return sdk.primo->UnitInfo2(pdwUnit, pdwTypes, pdwClass, pdwBusType, pdwRFU);
+#endif
+}
+
+DWORD PrimoSDKHelper_DiscInfoEx(PDWORD pdwUnit, DWORD dwFlags, PDWORD pdwMediumType, PDWORD pdwMediumFormat, PDWORD pdwErasable, PDWORD pdwTracks, PDWORD pdwUsed, PDWORD pdwFree)
+{
+#if 0
+ if (!sdk.uRef) return PRIMOSDK_CMDSEQUENCE;
+ if (!sdk.primo) return PRIMOSDK_CMDSEQUENCE;
+
+ return sdk.primo->DiscInfoEx(pdwUnit, dwFlags, pdwMediumType, pdwMediumFormat, pdwErasable, pdwTracks, pdwUsed, pdwFree);
+#endif
+ return PRIMOSDK_CMDSEQUENCE;
+}
+
+DWORD PrimoSDKHelper_DiscInfo2(PDWORD pdwUnit, PDWORD pdwMedium, PDWORD pdwProtectedDVD, PDWORD pdwFlags, PDWORD pdwMediumEx, PDWORD pdwRFU3)
+{
+ return PRIMOSDK_CMDSEQUENCE;
+#if 0
+ if (!sdk.uRef) return PRIMOSDK_CMDSEQUENCE;
+ if (!sdk.primo) return PRIMOSDK_CMDSEQUENCE;
+
+ return sdk.primo->DiscInfo2(pdwUnit, pdwMedium, pdwProtectedDVD, pdwFlags, pdwMediumEx, pdwRFU3);
+#endif
+}
diff --git a/Src/Plugins/Library/ml_disc/primosdk_helper.h b/Src/Plugins/Library/ml_disc/primosdk_helper.h
new file mode 100644
index 00000000..04078bfd
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/primosdk_helper.h
@@ -0,0 +1,31 @@
+#ifndef NULLOSFT_MLDISC_PRIMOSDK_HELPER_HEADER
+#define NULLOSFT_MLDISC_PRIMOSDK_HELPER_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <windows.h>
+//#include <primosdk.h>
+#define PRIMOSDK_CMDSEQUENCE ((DWORD)0xFFFFFFFF)
+#define PRIMOSDK_OK 0
+#define PRIMOSDK_PACKETWRITTEN 0
+// !!!! Not thread safe !!!!
+
+#define DEFAULT_HANDLE ((DWORD)0xFFFFFFFF)
+
+// Initialization
+LONG PrimoSDKHelper_Initialize(void);
+LONG PrimoSDKHelper_Uninitialize(void);
+BOOL PrimoSDKHelper_IsInitialized(void);
+BOOL PrimoSDKHelper_IsLoaded(void);
+
+// Drive Info (You can use DEFAULT_HANDLE)
+DWORD PrimoSDKHelper_UnitInfo(PDWORD pdwUnit, PDWORD pdwType, PBYTE szDescr, PDWORD pdwReady);
+DWORD PrimoSDKHelper_UnitInfo2(PDWORD pdwUnit, PDWORD pdwTypes, PDWORD pdwClass, PDWORD pdwBusType, PDWORD pdwRFU);
+
+// Medium Info (You can use DEFAULT_HANDLE)
+DWORD PrimoSDKHelper_DiscInfoEx(PDWORD pdwUnit, DWORD dwFlags, PDWORD pdwMediumType, PDWORD pdwMediumFormat, PDWORD pdwErasable, PDWORD pdwTracks, PDWORD pdwUsed, PDWORD pdwFree);
+DWORD PrimoSDKHelper_DiscInfo2(PDWORD pdwUnit, PDWORD pdwMedium, PDWORD pdwProtectedDVD, PDWORD pdwFlags, PDWORD pdwMediumEx, PDWORD pdwRFU3);
+
+#endif // NULLOSFT_MLDISC_PRIMOSDK_HELPER_HEADER
diff --git a/Src/Plugins/Library/ml_disc/questionwnd.cpp b/Src/Plugins/Library/ml_disc/questionwnd.cpp
new file mode 100644
index 00000000..66071eea
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/questionwnd.cpp
@@ -0,0 +1,279 @@
+#include "main.h"
+#include "./copyfiles.h"
+#include "./copyinternal.h"
+#include "./resource.h"
+#include "../nu/trace.h"
+#include <shlwapi.h>
+#include <strsafe.h>
+
+
+#define QUESTIONBOX_PROP TEXT("QUESTIONBOX")
+#define GetQuestionBox(__hdlg) ((QUESTIONBOX*)GetProp((__hdlg), QUESTIONBOX_PROP))
+
+
+#define GetResolvedString(__pszText, __pszBuffer, __chhBufferMax)\
+ (IS_INTRESOURCE(__pszText) ? WASABI_API_LNGSTRINGW_BUF((UINT)(UINT_PTR)(__pszText), (__pszBuffer), (__chhBufferMax)) : (__pszText))
+
+static INT_PTR CALLBACK CopyQuestion_DialogProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+INT_PTR MLDisc_ShowQuestionBox(QUESTIONBOX *pQuestionBox)
+{
+ if (!pQuestionBox) return IDCANCEL;
+ return WASABI_API_DIALOGBOXPARAMW(IDD_FILECOPY_QUESTION, pQuestionBox->hParent, CopyQuestion_DialogProc, (LPARAM)pQuestionBox);
+}
+
+static BOOL FindPrefferedSizeEx(HDC hdc, LPCTSTR pszText, LPCTSTR pszNewLine, SIZE *pSize)
+{
+ if (!pSize) return FALSE;
+ pSize->cx = 0; pSize->cy = 0;
+ if (!hdc || !pszText || !pszNewLine) return FALSE;
+ LPCTSTR pszBlock = pszText;
+ LPCTSTR pszCursor = pszBlock;
+ INT cchSep = lstrlenW(pszNewLine);
+ INT matched = 0;
+ for(;;)
+ {
+ if (*pszCursor)
+ {
+ if (*pszCursor == pszNewLine[matched]) matched++;
+ else matched = 0;
+ pszCursor++;
+ }
+ if (matched == cchSep || TEXT('\0') == *pszCursor)
+ {
+ SIZE sz;
+
+ INT l = (INT)(size_t)((pszCursor - pszBlock) - matched);
+ if (l > 0)
+ {
+ if (!GetTextExtentPoint32(hdc, pszBlock, l, &sz)) return FALSE;
+ }
+ else
+ {
+ if (!GetTextExtentPoint32(hdc, TEXT("\n"), 1, &sz)) return FALSE;
+ sz.cx = 0;
+ }
+
+ if (pSize->cx < sz.cx) pSize->cx= sz.cx;
+ pSize->cy += sz.cy;
+
+ if (TEXT('\0') == *pszCursor) break;
+ else
+ {
+ matched = 0;
+ pszBlock = pszCursor;
+ }
+ }
+ }
+ return TRUE;
+}
+
+static BOOL FindPrefferedSize(HWND hwnd, LPCTSTR pszText, LPCTSTR pszNewLine, SIZE *pSize)
+{
+ HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_PARENTCLIP);
+ if (!hdc) return FALSE;
+ HFONT hf, hfo;
+ hf = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0L);
+ if (NULL == hf) hf = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+ hfo = (NULL != hf) ? (HFONT)SelectObject(hdc, hf) : NULL;
+
+ BOOL br = FindPrefferedSizeEx(hdc, pszText, pszNewLine, pSize);
+
+ if (hfo) SelectObject(hdc, hfo);
+ ReleaseDC(hwnd, hdc);
+
+ return br;
+}
+
+static INT_PTR CopyQuestion_OnInitDialog(HWND hdlg, HWND hFocus, QUESTIONBOX *pqb)
+{
+ if (!pqb) return FALSE;
+ SetProp(hdlg, QUESTIONBOX_PROP, pqb);
+
+ HWND hctrl;
+ TCHAR szBuffer[2048] = {0};
+ LONG messageLeft = 0;
+
+ if (NULL != pqb->pszTitle) SetWindowText(hdlg, GetResolvedString(pqb->pszTitle, szBuffer, ARRAYSIZE(szBuffer)));
+
+ if (NULL != pqb->pszBtnOkText) SetDlgItemText(hdlg, IDOK, GetResolvedString(pqb->pszBtnOkText, szBuffer, ARRAYSIZE(szBuffer)));
+ if (NULL != pqb->pszBtnCancelText) SetDlgItemText(hdlg, IDCANCEL, GetResolvedString(pqb->pszBtnCancelText, szBuffer, ARRAYSIZE(szBuffer)));
+
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDC_BTN_EXTRA1)))
+ {
+ ShowWindow(hctrl, (QBF_SHOW_EXTRA_BUTTON & pqb->uFlags) ? SW_SHOWNA : SW_HIDE);
+ if (NULL != pqb->pszBtnExtraText) SetWindowText(hctrl, GetResolvedString(pqb->pszBtnExtraText, szBuffer, ARRAYSIZE(szBuffer)));
+ }
+
+
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDC_PIC_ICON)))
+ {
+ HICON hIcon = NULL;
+ if (NULL != pqb->pszIcon)
+ {
+ hIcon = LoadIcon(WASABI_API_LNG_HINST, pqb->pszIcon);
+ if (NULL == hIcon) hIcon = LoadIcon(WASABI_API_ORIG_HINST, pqb->pszIcon);
+ if (NULL == hIcon) hIcon = LoadIcon(NULL, pqb->pszIcon);
+ }
+ SendMessage(hctrl, STM_SETICON, (WPARAM)hIcon, 0L);
+ ShowWindow(hctrl, (hIcon) ? SW_SHOWNA : SW_HIDE);
+ RECT rw;
+ GetWindowRect(hctrl, &rw);
+ MapWindowPoints(HWND_DESKTOP, hdlg, (POINT*)&rw, 2);
+ messageLeft = (hIcon) ? (rw.right + 24) : rw.left;
+ }
+
+ INT shiftRight = 0, shiftBottom = 0;
+
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDC_LBL_MESSAGE)))
+ {
+ RECT rw;
+ SIZE textSize = { 0, 0 };
+ LPCTSTR pszText = (NULL != pqb->pszMessage) ? GetResolvedString(pqb->pszMessage, szBuffer, ARRAYSIZE(szBuffer)) : NULL;
+ if (pszText)
+ {
+ FindPrefferedSize(hctrl, pszText, TEXT("\n"), &textSize);
+ textSize.cx += 8; textSize.cy += 4;
+ }
+ SetWindowText(hctrl, pszText);
+ GetWindowRect(hctrl, &rw);
+ MapWindowPoints(HWND_DESKTOP, hdlg, (POINT*)&rw, 2);
+ rw.left = messageLeft;
+ shiftRight = (rw.left + textSize.cx) - rw.right;
+ if (shiftRight < 0) shiftRight = 0;
+ shiftBottom = textSize.cy - (rw.bottom - rw.top);
+ if (shiftBottom < 0) shiftBottom = 0;
+ SetWindowPos(hctrl, NULL, rw.left, rw.top, (rw.right - rw.left) + shiftRight, (rw.bottom - rw.top) + shiftBottom, SWP_NOACTIVATE | SWP_NOZORDER);
+ }
+
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDC_CHECKBOX1)))
+ {
+
+ if (NULL != pqb->pszCheckboxText) SetWindowText(hctrl, GetResolvedString(pqb->pszCheckboxText, szBuffer, ARRAYSIZE(szBuffer)));
+ SendMessage(hctrl, BM_SETCHECK, (pqb->checkboxChecked) ? BST_CHECKED : BST_UNCHECKED, 0L);
+
+ RECT rw;
+ GetWindowRect(hctrl, &rw);
+
+ if (0 == (QBF_SHOW_CHECKBOX & pqb->uFlags))
+ {
+ shiftBottom -= (rw.bottom - rw.top);
+ ShowWindow(hctrl, SW_HIDE);
+ }
+ else if (shiftRight || shiftBottom)
+ {
+ MapWindowPoints(HWND_DESKTOP, hdlg, (POINT*)&rw, 2);
+ SetWindowPos(hctrl, NULL, rw.left, rw.top + shiftBottom,
+ (rw.right - rw.bottom) + shiftRight, (rw.bottom - rw.top), SWP_NOACTIVATE | SWP_NOZORDER);
+ ShowWindow(hctrl, SW_SHOWNA);
+ }
+ }
+
+ if (shiftRight || shiftBottom)
+ {
+ RECT rw;
+ INT idList[] = {IDC_BTN_EXTRA1, IDOK, IDCANCEL, };
+ for (int i = 0; i < ARRAYSIZE(idList); i++)
+ {
+ if (NULL != (hctrl = GetDlgItem(hdlg, idList[i])))
+ {
+ GetWindowRect(hctrl, &rw);
+ MapWindowPoints(HWND_DESKTOP, hdlg, (POINT*)&rw, 2);
+ SetWindowPos(hctrl, NULL, rw.left + shiftRight, rw.top + shiftBottom, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
+ }
+ }
+
+ }
+
+ HWND hParent = GetParent(hdlg);
+ if (hParent)
+ {
+ RECT rw, rc;
+ GetClientRect(hParent, &rc);
+ GetWindowRect(hdlg, &rw);
+ rw.right += shiftRight;
+ rw.bottom += shiftBottom;
+
+ SetWindowPos(hdlg, NULL,
+ rw.left + ((rc.right - rc.left) - (rw.right - rw.left))/2,
+ rw.top + ((rc.bottom - rc.top) - (rw.bottom - rw.top))/2,
+ rw.right - rw.left, rw.bottom - rw.top, SWP_NOACTIVATE | SWP_NOZORDER);
+ }
+
+ SendMessage(hdlg, DM_REPOSITION, 0, 0L);
+ return FALSE;
+}
+
+static void CopyQuestion_OnDestroy(HWND hdlg)
+{
+ QUESTIONBOX *pqb = GetQuestionBox(hdlg);
+ if (pqb)
+ {
+ pqb->checkboxChecked = (BST_CHECKED == IsDlgButtonChecked(hdlg, IDC_CHECKBOX1));
+ }
+ RemoveProp(hdlg, QUESTIONBOX_PROP);
+}
+
+static void CopyQuestion_OnCommand(HWND hdlg, INT ctrlId, INT eventId, HWND hctrl)
+{
+ switch(ctrlId)
+ {
+ case IDOK:
+ case IDCANCEL:
+ EndDialog(hdlg, ctrlId);
+ break;
+ case IDC_BTN_EXTRA1:
+ if (BN_CLICKED == eventId) EndDialog(hdlg, ctrlId);
+ break;
+ }
+}
+#define IDT_POSTSHOW 1985
+#define DELAY_POSTSHOW 0
+
+static void CALLBACK CopyQuestion_OnPostShowElapsed(HWND hdlg, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ QUESTIONBOX *pqb = GetQuestionBox(hdlg);
+ KillTimer(hdlg, idEvent);
+ if (!pqb) return;
+
+ if (QBF_FLASH & pqb->uFlags)
+ {
+ FLASHWINFO flash = { sizeof(FLASHWINFO), };
+ flash.hwnd = hdlg;
+ flash.dwFlags = FLASHW_ALL;
+ flash.uCount = 2;
+ flash.dwTimeout = 300;
+ FlashWindowEx(&flash);
+ }
+
+ if (QBF_BEEP & pqb->uFlags) MessageBeep(pqb->uBeepType);
+
+ if ((QBF_SETFOREGROUND | QBF_TOPMOST) & pqb->uFlags)
+ {
+ SetForegroundWindow(hdlg);
+ SetWindowPos(hdlg, (QBF_SETFOREGROUND & pqb->uFlags) ? HWND_TOP : HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
+ }
+
+
+
+
+
+}
+static void CopyQuestion_OnShowWindow(HWND hdlg, BOOL bShow, UINT nStatus)
+{
+ if (bShow)
+ {
+ SetTimer(hdlg, IDT_POSTSHOW, DELAY_POSTSHOW, CopyQuestion_OnPostShowElapsed);
+ }
+}
+
+static INT_PTR CALLBACK CopyQuestion_DialogProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG: return CopyQuestion_OnInitDialog(hdlg, (HWND)wParam, (QUESTIONBOX*)lParam);
+ case WM_DESTROY: CopyQuestion_OnDestroy(hdlg); break;
+ case WM_COMMAND: CopyQuestion_OnCommand(hdlg, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); break;
+ case WM_SHOWWINDOW: CopyQuestion_OnShowWindow(hdlg, (BOOL)wParam, (UINT)lParam); break;
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/resource.h b/Src/Plugins/Library/ml_disc/resource.h
new file mode 100644
index 00000000..60dec117
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resource.h
@@ -0,0 +1,455 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by ml_disc.rc
+//
+#define IDS_ERROR_WRITING_TEMP_BURN_LIST 1
+#define IDS_ERROR 2
+#define IDS_BURN_X_TRACKX_ON_X 3
+#define IDS_X_OF_X_SECTORS_FREE 4
+#define IDS_GET_PRO_FOR_BURN_HIGHER_THAN_2X 5
+// #define IDS_WINAMP_PRO_FEATURE 6
+#define IDS_NEED_TO_ADD_SOME_TRACKS_TO_BURN 7
+#define IDS_TOTAL_LENGTH_IS_BIGGER_THAN_MEDIA_CAPACITY 8
+#define IDS_BURNING 9
+#define IDS_CANCEL_BURN 10
+#define IDS_NO_BLANK_CDR_IN_DRIVE 11
+#define IDS_X_CAPACITY_DETAILS 12
+#define IDS_USED_X_X_TRACKS 13
+#define IDS_AVAILABLE_X_X 14
+#define IDS_X_OVER_CAPACITY_REMOVE_X_TRACKS 15
+#define IDS_FILE_X_CANNOT_BE_BURNED_REASON_NOT_FOUND 16
+#define IDS_FILE_X_CANNOT_BE_BURNED_REASON_FILETYPE_NOT_REGISTERED 17
+#define IDS_FILE_X_CANNOT_BE_BURNED_REASON_X 18
+#define IDS_VIDEO_FILES_CANNOT_BE_BURNED 19
+#define IDS_NOT_AN_AUDIO_FILE 20
+#define IDS_FILE_CANNOT_BE_BURNED 21
+#define IDS_TRACK_NUMBER 22
+#define IDS_TITLE 23
+#define IDS_LENGTH 24
+#define IDS_STATUS 25
+#define IDS_CANCELLING 26
+#define IDS_BURNING_AUDIO_CANCELLING 27
+#define IDS_BURNING_AUDIO_FINISHING 28
+#define IDS_BURNING_AUDIO_DATA_PREP_FINISHED 29
+#define IDS_BURNING_AUDIO_VERIFYING_FILES 30
+#define IDS_BURNING_AUDIO_VERIFICATION_COMPLETED 31
+#define IDS_OPENING_DISC_WRITING_LEAD_IN 32
+#define IDS_CLOSING_DISC_WRITING_LEAD_OUT 33
+#define IDS_BURNING_AUDIO_CURRENT_OPERATION 34
+#define IDS_BURNING_AUDIO_CD_PREP_DATA 35
+#define IDS_BURNING_AUDIO_BURNING_DATA 36
+#define IDS_CLOSE 37
+#define IDS_AUDIO_CD_BURNED_SUCCESSFULLY 38
+#define IDS_BURN_ABORTED_BY_USER 39
+#define IDS_BURNING_FAILED 40
+#define IDS_BURNING_COMPLETED_STATUS_X 41
+#define IDS_ALL_FILES 42
+#define IDS_ADD_FILES_TO_BURNING_LIST 43
+#define IDS_CHOOSE_A_FOLDER_TO_ADD_TO_BURNING_LIST 44
+#define IDS_SURE_YOU_WANT_TO_CLEAR_BURNING_LIST 45
+#define IDS_CONFIRMATION 46
+#define IDS_BURNING_ 47
+#define IDS_PREPARING 48
+#define IDS_FINISHED 49
+#define IDS_PREPARED 50
+#define IDS_SKIPPED 51
+#define IDS_SCHEDULED 52
+#define IDS_CHECKING_LICENSE 53
+#define IDS_LICENSED 54
+#define IDS_CANCELLED 55
+#define IDS_FAILED 56
+#define IDS_BAD_FILENAME 57
+#define IDS_UNABLE_TO_OPEN_FILE 58
+#define IDS_CACHE_WRITE_FAILED 59
+#define IDS_UNABLE_TO_FIND_DECODER 60
+#define IDS_CANNOT_ADD_TO_THE_DISC 61
+#define IDS_CACHE_READ_FAILED 62
+#define IDS_UNKNOWN_ERROR 63
+#define IDS_ADDING_TRACKS_TO_BURNER_TOTAL_LENGTH_X 64
+#define IDS_PLEASE_INSERT_BLANK_RECORDABLE_CD 65
+#define IDS_COMPLETED 66
+#define IDS_QUEUED 67
+#define IDS_RIPPING 68
+#define IDS_CANCEL_RIP 69
+#define IDS_CD_RIP_QUESTION 70
+#define IDS_INITIALIZING 71
+#define IDS_CALCULATING_REPLAY_GAIN 72
+#define IDS_UNKNOWN_ARTIST 73
+#define IDS_UNKNOWN_ALBUM 74
+#define IDS_UNKNOWN 75
+#define IDS_RIP_COMPLETE 77
+#define IDS_RIP_FAILED 78
+#define IDS_X_TRACKS_RIPPED_IN_X 79
+#define IDS_DONE 80
+#define IDS_ELAPSED_X_REMAINING_X_TOTAL_X 81
+#define IDS_X_KBPS_AT_X_REALTIME 82
+#define IDS_X_OF_X_ELAPSED_X_REMAINING_X 83
+#define IDS_X_PERCENT_RIPPING_FROM_CD 84
+#define IDS_WAITING 85
+#define IDS_STAMPED_DISC_OR_RECORDABLE_THAT_HAS_BEEN_RECORDED 86
+#define IDS_REWRITEABLE_DISC_HAS_DATA_BUT_KEPT_OPEN_FOR_APPEND 87
+#define IDS_REWRITEABLE_DISC_NOT_POSSIBLE_TO_APPEND_DATA 88
+#define IDS_BLANK_REWRITEABLE_DISC 89
+#define IDS_MEDIA_BLANK_DISC 90
+#define IDS_MEDIA_DATA_MODE_1_DAO 91
+#define IDS_MEDIA_KODAK_PHOTO_CD 92
+#define IDS_MEDIA_DATA_MULTISESSION_MODE_1_CLOSED 93
+#define IDS_MEDIA_DATA_MULTISESSION_MODE_2_CLOSED 94
+#define IDS_MEDIA_DATA_MODE_2_DAO 95
+#define IDS_MEDIA_CDRFS 96
+#define IDS_MEDIA_PACKET_WRITING 97
+#define IDS_MEDIA_DATA_MULTISESSION_MODE_1_OPEN 98
+#define IDS_MEDIA_DATA_MULTISESSION_MODE_2_OPEN 99
+#define IDS_MEDIA_AUDIO_DAO_SAO_TAO 100
+#define IDS_MEDIA_AUDIO_REWRITEABLE_DISC_WITH_SESSION_NOT_CLOSED 101
+#define IDS_MEDIA_FIRST_TYPE_OF_ENHANCED_CD_ABORTED 102
+#define IDS_MEDIA_CD_EXTRA 103
+#define IDS_MEDIA_AUDIO_TAO_WITH_SESSION_NOT_WRITTEN 104
+#define IDS_MEDIA_FIRST_TRACK_DATA_OTHERS_AUDIO 105
+#define IDS_MEDIA_MIXED_MODE_MADE_TAO 106
+#define IDR_CONTEXTMENUS 107
+#define IDS_MEDIA_KODAK_PORTFOLIO 107
+#define IDS_MEDIA_VIDEO_CD 108
+#define IDS_MEDIA_CDi 109
+#define IDD_VIEW_CONTAINER 109
+#define IDD_VIEW_CDROM 110
+#define IDS_MEDIA_PLAYSTATION_SONY_GAMES 110
+#define IDD_VIEW_CDROM1 110
+#define IDD_PREFSCDRIPFR 111
+#define IDS_MEDIA_OBSOLETE 111
+#define IDD_PREFS_CDRIP1 112
+#define IDS_MEDIA_OBSOLETE_FOR_RESTRICTED_OVERWRITE_DVD 112
+#define IDD_PREFS_CDRIP2 113
+#define IDS_MEDIA_DVDROM_OR_CLOSED_RECORDABLE 113
+#define IDD_PREFS_CDRIP4 114
+#define IDS_MEDIA_INCREMENTAL_DVD_WITH_APPENDABLE_ZONE 114
+#define IDD_VIEW_CDROM_EXTRACT 115
+#define IDS_MEDIA_APPENDABLE_DVD_OF_ANY_TYPE 115
+#define IDD_VIEW_CDROM_EX2 116
+#define IDS_MEDIA_DVDRAM_CARTRIDGE 116
+#define IDD_VIEW_CDROM_BURN 117
+#define IDS_MEDIA_CD_OTHER_TYPE 117
+#define IDD_BURN_ADD_STATUS 118
+#define IDS_X_DRIVE_X 118
+#define IDD_BURN 119
+#define IDS_X_DRIVE_BRACKET_X 119
+#define IDD_WAITFORCDR 120
+#define IDS_YOU_ARE_CURRENTLY_BURNING_AUDIO_CD_MUST_CANCEL_TO_CLOSE_WINAMP 120
+#define IDD_UPSELL_RIPPING 121
+#define IDS_NOTIFICATION 121
+#define IDD_NOBURN 122
+#define IDS_RIP_AND_BURN 122
+#define IDD_VIEW_RIPBURN 123
+#define IDS_CD_RIPPING 123
+#define IDS_CD_BURNER_ON_X 124
+#define IDD_VIEW_WAIT 124
+#define IDS_EXAMPLE_RIPPED_FILE_FILENAME 125
+#define IDD_VIEW_INFO 125
+#define IDS_EXAMPLE_PLAYLIST_FILENAME 126
+#define IDD_DIALOG1 126
+#define IDD_VIEW_CDROM_DATA 126
+#define IDS_RIPPED_FILENAME_FORMAT_HELP 127
+#define IDS_RIPPPED_PLAYLIST_FORMAT_HELP 128
+#define IDD_COMMANDBAR_DATA 128
+#define IDS_RIPPED_PLAYLIST_FORMAT 129
+#define IDS_RIPPED_FILENAME_FORMAT 130
+#define IDS_CHOOSE_A_FOLDER 131
+#define IDR_DATAVIEW_ACCELERATOR 131
+#define IDR_ACCELERATOR_VIEW 131
+#define IDS_TO_EXTRACT_TO_MP3_NEED_TO_PURCHASE_WINAMP_PRO 132
+#define IDD_FILECOPY_PREPARE 132
+#define IDS_ENCODER 133
+#define IDD_FILECOPY_PROGRESS 133
+#define IDS_OUTPUT_FILE_SETTINGS 134
+#define IDS_PLAYLIST_GENERATION 135
+#define IDS_ERROR_CD_RIP_IN_PROGRESS 136
+#define IDB_BITMAP1 136
+#define IDB_FILECOPY 136
+#define IDS_CD_RIP 137
+#define IDD_DLG_SIMPLEHELP 137
+#define IDD_SIMPLEHELP 137
+#define IDS_ERROR_CD_BURN_IN_PROGRESS 138
+#define IDD_FILECOPY_QUESTION 138
+#define IDS_ERROR_CANNOT_EXTRACT_DATA_CDS 139
+#define IDS_NO_TRACKS_TO_RIP 140
+#define IDS_CD_PLAYBACK_ERROR 141
+#define IDS_CD_EJECT 142
+#define IDS_NO_CD 143
+#define IDS_DATA_TRACK 144
+#define IDS_AUDIO_TRACK 145
+#define IDS_DISC_READ_ERROR 146
+#define IDS_DATA_CD 147
+#define IDS_DRIVE 148
+#define IDS_ARTIST 149
+#define IDS_TRACK 150
+#define IDS_TRACKS 151
+#define IDS_YES 152
+#define IDS_NO 153
+#define IDS_SHOW_INFO 154
+#define IDS_HIDE_INFO 155
+#define IDS_READINGDISC 156
+#define IDS_INFO_RIPPING 157
+#define IDS_DVD_DRIVE 158
+#define IDS_CD_DRIVE 159
+#define IDS_CD_RECORDER 160
+#define IDS_DATA_DISC 161
+#define IDS_STRING162 162
+#define IDS_BLANK_DISC 162
+#define IDS_DRIVE_CAP 163
+#define IDS_RECORDER_CAP 164
+#define IDS_CD_AUDIO 165
+#define IDS_DISC_BLANK 166
+#define IDS_DISC_DATA 167
+#define IDS_CD 168
+#define IDS_DVD 169
+#define IDS_CALCULATING 170
+#define IDS_ML_VIEW_ARTIST_ALBUM 171
+#define IDS_ML_VIEW_YEAR_GENRE 172
+#define IDS_RIPPED_PLAYLIST_FORMAT_CAPTION 173
+#define IDS_RIPPED_FILENAME_FORMAT_CAPTION 174
+#define IDS_COPY_FILENAME_FORMAT_TITLE 175
+#define IDS_COPY_FILENAME_FORMAT_CAPTION 176
+#define IDS_COPY_FILENAME_FORMAT 177
+#define IDS_OPTIONS_SHOW 178
+#define IDS_OPTIONS_HIDE 179
+#define IDS_EXAMPLE 180
+#define IDS_COPY_PREP_MESSAGE_SINGLE_FILE 180
+#define IDS_COPY_PREP_MESSAGE_MULTIPLE_FILES 181
+#define IDS_COPY_TASK_PREPARE 182
+#define IDS_COPY_TASK_COPY 183
+#define IDS_COPY_OP_CALCULATESIZE 184
+#define IDS_COPY_OP_CHECKDESTINATION 185
+#define IDS_COPY_TASK_FINISHED 186
+#define IDS_COPY_ERROR_CAPTION 187
+#define IDS_COPY_ERROR_MESSAGE 188
+#define IDS_COPY_ERRMSG_INITIALIZATION_FAILED 189
+#define IDS_COPY_ERRMSG_DIRECTORYCREATE_FAILED 190
+#define IDS_COPY_ERRMSG_COPYFILE_FAILED 191
+#define IDS_COPY_ERRMSG_TITLEFORMAT_FAILED 192
+#define IDS_COPY_ERRMSG_ADDTOMLDB_FAILED 193
+#define IDS_COPY_ERRMSG_SETATTRIBUTES_FAILED 194
+#define IDS_COPY_ERRMSG_COPYFILE_USERABORT 195
+#define IDS_COPY_ERRMSG_DELETEFILE_FAILED 196
+#define IDS_CONFIRM_CREATE_DESTINATION 197
+#define IDS_DESTINATION_NOT_EXIST_FORMAT 198
+#define IDS_CANCEL 199
+#define IDS_SKIP 200
+#define IDS_OVERWRITE 201
+#define IDS_APPLY_TO_ALL_FILES 202
+#define IDS_CONFIRM_FILE_REPLACE 203
+#define IDS_FILE_REPLACE_FORMAT 204
+#define IDS_CONFIRM_FILE_DELETE 205
+#define IDS_READONLY_FILE_DELETE_FORMAT 206
+#define IDS_SIZE 207
+#define IDS_CREATED 208
+#define IDS_MODIFIED 209
+#define IDS_UNKNOWN_GENRE 210
+#define IDS_YOU_ARE_CURRENTLY_COPYING_DATA_CD_MUST_CANCEL_TO_CLOSE_WINAMP 211
+#define IDS_YOU_ARE_CURRENTLY_RIPPING_AUDIO_CD_MUST_CANCEL_TO_CLOSE_WINAMP 212
+#define IDS_ERROR_RIPPING_TRACK 213
+#define IDS_NO_INFO_AVAILABLE 214
+#define IDC_LIST2 1001
+#define IDC_BUTTON_PLAY 1002
+#define IDC_BUTTON_ENQUEUE 1003
+#define IDC_BUTTON_EXTRACT 1004
+#define IDC_BUTTON_EJECT 1005
+#define IDC_CDINFO 1006
+#define IDC_CDINFO2 1007
+#define IDC_TAB1 1008
+#define IDC_TOTAL_TRACKS 1008
+#define IDC_DESTPATH 1009
+#define IDC_BUTTON1 1010
+#define IDC_FILENAMEFMT 1011
+#define IDC_BTN_BROWSE 1011
+#define IDC_BUTTON2 1012
+#define IDC_UPPERCASEEXT 1013
+#define IDC_CHECK_ML 1014
+#define IDC_EDT_NAMEFORMAT 1014
+#define IDC_TAGFILES 1015
+#define IDC_BTN_HELP 1015
+#define IDC_AUTO_RG 1016
+#define IDC_CHK_EXTUPPERCASE 1016
+#define IDC_EDIT2 1017
+#define IDC_CHK_CUSTOMNAME 1017
+#define IDC_EDIT1 1018
+#define IDC_MESSAGE2 1018
+#define IDC_CHK_ADDTOMLDB 1018
+#define IDC_FMTOUT 1019
+#define IDC_BTN_SHOWINFO 1019
+#define IDC_CHK_REPLAYGAIN 1019
+#define IDC_ENCFORMAT 1020
+#define IDC_LBL_TEXT 1020
+#define IDC_EDIT3 1020
+#define IDC_ENC_CONFIG 1021
+#define IDC_LV_DETAILS 1022
+#define IDC_STATUS 1023
+#define IDC_CTFRAME 1024
+#define IDC_PROGRESS1 1025
+#define IDC_BTN_PLAY 1025
+#define IDC_BTN_PLAYEX 1025
+#define IDC_PROGRESS2 1026
+#define IDC_BTN_ENQUEUE 1026
+#define IDC_STATUS2 1027
+#define IDC_BTN_EJECT 1027
+#define IDC_CHECK1 1028
+#define IDC_LBL_TEST 1028
+#define IDC_CHECK2 1029
+#define IDC_LIST1 1029
+#define IDC_CHECK3 1030
+#define IDC_COMBO2 1030
+#define IDC_CHECK4 1031
+#define IDC_COMBO3 1031
+#define IDC_CANCEL_RIP 1032
+#define IDC_CUSTOM1 1032
+#define IDC_CURTRACK 1033
+#define IDC_RIPOPTS 1034
+#define IDC_ADD 1035
+#define IDC_VDELIM 1035
+#define IDC_HDELIM 1035
+#define IDC_CLEAR 1036
+#define IDC_BURN 1037
+#define IDC_LOGO 1039
+#define IDC_BURN_OPTS 1040
+#define IDC_CANCEL_BURN 1042
+#define IDC_STAT 1043
+#define IDC_COMBO1 1044
+#define IDC_PRO1 1045
+#define IDC_PRO2 1046
+#define IDC_TEXT 1047
+#define IDC_MESSAGE 1048
+#define IDC_LBL 1048
+#define IDC_LBL_STATUS 1048
+#define IDC_LBL_BANNER 1049
+#define IDC_BTN_COPY 1049
+#define IDC_LBL_DRIVES 1050
+#define IDC_LBL_MESSAGE 1050
+#define IDC_LIST_DRIVES 1051
+#define IDC_BTN_OPTIONS 1051
+#define IDC_LBL_INFO_DRIVE 1052
+#define IDC_EDT_PATH 1052
+#define IDC_LBL_INFO_MEDIUM 1053
+#define IDC_GRP_OPTIONS 1053
+#define IDC_LBL_DRIVE_LETTER 1054
+#define IDC_EDT_TEXT 1054
+#define IDC_LBL_DRIVE_DESCRIPTION 1055
+#define IDC_LBL_CAPTION 1055
+#define IDC_LBL_DRIVE_BUS 1056
+#define IDC_LBL_EXAMPLE 1056
+#define IDC_LBL_DRIVE_TYPES 1057
+#define IDC_LBL_EXAMPLE_TITLE 1057
+#define IDC_LBL_MEDIUM_TYPE 1058
+#define IDC_LBL_FREE_TITLE 1058
+#define IDC_LBL_MEDIUM_FORMAT 1059
+#define IDC_LBL_REQUIRED_TITLE 1059
+#define IDC_LBL_MEDIUM_TRACKN 1060
+#define IDC_LBL_FREE 1060
+#define IDC_LBL_MEDIUM_CAPACITY 1061
+#define IDC_LBL_REQUIRED 1061
+#define IDC_LBL_MEDIUM_RECORDABLE 1062
+#define IDC_PRG_TOTAL 1062
+#define IDC_LBL_MEDIUM_ERASEABLE 1063
+#define IDC_LBL_TASK 1063
+#define IDC_LBL_MEDIUM_ADDINFO 1064
+#define IDC_LBL_OPERATION 1064
+#define IDC_LBL_DRIVE_LETTER_VAL 1065
+#define IDC_RB_GAIN_MODE_ALBUM 1065
+#define IDC_CHECKBOX1 1065
+#define IDC_LBL_DRIVE_DESCRIPTION_VAL 1066
+#define IDC_BTN_EXTRA1 1066
+#define IDC_LBL_DRIVE_BUS_VAL 1067
+#define IDC_PIC_ICON 1067
+#define IDC_LBL_DRIVE_TYPES_VAL 1068
+#define IDC_PIC_LOGO 1068
+#define IDC_LBL_MEDIUM_DISC_VAL 1069
+#define IDC_LBL_MEDIUM_FORMAT_VAL 1070
+#define IDC_LBL_MEDIUM_TRACKN_VAL 1071
+#define IDC_LBL_MEDIUM_CAPACITY_VAL 1072
+#define IDC_LBL_MEDIUM_RECORDABLE_VAL 1073
+#define IDC_LBL_MEDIUM_ERASEABLE_VAL 1074
+#define IDC_LBL_MEDIUM_ADDINFO_VAL 1075
+#define IDC_BTN_REFRESH 1076
+#define IDC_LBL_MEDIUM_UPDATE 1077
+#define IDC_LBL_MEDIUM_ADDINFO_VAL2 1078
+#define IDC_LBL_MEDIUM_NOINFO 1078
+// #define IDB_LOGO_SONIC 1101
+#define IDB_LISTITEM_CDDRIVE 1102
+#define IDB_LISTBOX_BACK 1104
+#define IDB_NAVITEM_CDROM 1106
+#define IDB_EJECT_NORMAL 1107
+#define IDB_EJECT_HILITED 1108
+#define IDB_EJECT_PRESSED 1109
+#define IDB_EJECT_DISABLED 1110
+#define IDB_PLAY_NORMAL 1111
+#define IDB_PLAY_HIGHLIGHTED 1112
+#define IDB_PLAY_DISABLED 1113
+#define IDB_PLAY_PRESSED 1114
+#define IDB_ENQUEUE_PRESSED 1115
+#define IDB_EJECT2_PRESSED 1116
+#define IDB_ENQUEUE_NORMAL 1117
+#define IDB_ENQUEUE_HIGHLIGHTED 1118
+#define IDB_ENQUEUE_DISABLED 1119
+#define IDB_EJECT2_NORMAL 1123
+#define IDB_EJECT2_HIGHLIGHTED 1124
+#define IDB_EJECT2_DISABLED 1125
+#define IDB_PLAY_MENU 1126
+#define IDB_ENQUEUE_MENU 1127
+#define ID_EXTRACTMENU_CONFIGURE 40001
+#define ID_EXTRACTMENU_EXTRACTALLTRACKS 40002
+#define ID_EXTRACTMENU_EXTRACTSELECTEDTRACKS 40003
+#define ID_CDROMMENU_PLAYSELECTEDITEMS 40004
+#define ID_CDROMMENU_ENQUEUESELECTEDITEMS 40005
+#define ID_CDROMMENU_PLAYALL 40006
+#define ID_CDROMMENU_ENQUEUEALL 40007
+#define ID_PE_ID3 40008
+#define ID_CDROMMENU_EXTRACT_EXTRACTSELECTEDITEMS 40009
+#define ID_CDROMMENU_EXTRACT_EXTRACTALL 40010
+#define ID_CDROMMENU_EXTRACT_CONFIGURE 40011
+#define ID_CDROMMENU_EJECTCD 40012
+#define ID_BURNADDMENU_FILES 40013
+#define ID_BURNADDMENU_FOLDER 40014
+#define ID_BURNADDMENU_CURRENTPLAYLIST 40015
+#define ID_BURNCONTEXTMENU_PLAYSELECTEDITEMS 40016
+#define ID_BURNCONTEXTMENU_ENQUEUESELECTEDITEMS 40017
+#define ID_BURNCONTEXTMENU_MOVESELECTEDITEMSUP 40018
+#define ID_BURNCONTEXTMENU_MOVESELECTEDITEMSDOWN 40019
+#define ID_BURNCONTEXTMENU_REMOVESELECTEDITEMS 40020
+#define ID_BURNCONTEXTMENU_BURN 40021
+#define ID_RIPOPTIONS_PRIORITY_HIGH 40022
+#define ID_RIPOPTIONS_PRIORITY_ABOVENORMAL 40023
+#define ID_RIPOPTIONS_PRIORITY_NORMAL 40024
+#define ID_RIPOPTIONS_PRIORITY_BELOWNORMAL 40025
+#define ID_RIPOPTIONS_PRIORITY_LOWEST 40026
+#define ID_RIPOPTIONS_PRIORITY_IDLE 40027
+#define ID_RIPOPTIONS_RIPPINGSTATUSWINDOW 40028
+#define ID_RIPOPTIONS_EJECTCDWHENCOMPLETED 40029
+#define ID_RIPOPTIONS_PLAYTRACKSWHENCOMPLETED 40030
+#define ID_RIPOPTIONS_CLOSEVIEWWHENCOMPLETE 40031
+#define ID_BURNOPTIONS_ADDCDTITLESTOLOCALCDDBCACHE 40032
+#define ID_BURNCONTEXTMENU_SELECTALL 40033
+#define ID_CDROMMENU2_SELECTALL 40034
+#define ID_CDROMMENU_SELECTALL 40035
+#define ID_Menu 40036
+#define ID_DATAVIEWOPTIONS_HIDEEXTENSIONS 40045
+#define ID_DATAVIEWOPTIONS_SHOWHIDDENFILES 40046
+#define ID_DATAVIEWOPTIONS_SHOW 40047
+#define ID_EJECT_DISC 40083
+#define ID_COPY_SELECTION 40085
+#define ID_DRIVE_MODE_CHANGED 40086
+#define ID_NAVIGATION_PREFERENCES 40087
+#define ID_NAVIGATION_HELP 40088
+#define ID_CDROMMENU2_HELP 40089
+#define ID_CDROMMENU_EXTRACT_HELP 40090
+#define ID_MINIINFO_SHOW 40104
+#define IDS_NULLSOFT_RIP_AND_BURN 65534
+#define IDS_COPY_ERROR_MESSSAGE 65535
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 216
+#define _APS_NEXT_COMMAND_VALUE 40091
+#define _APS_NEXT_CONTROL_VALUE 1079
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/Plugins/Library/ml_disc/resources/cdrom.png b/Src/Plugins/Library/ml_disc/resources/cdrom.png
new file mode 100644
index 00000000..3c5d6856
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/cdrom.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/cdrom_32x32_24.bmp b/Src/Plugins/Library/ml_disc/resources/cdrom_32x32_24.bmp
new file mode 100644
index 00000000..f05dc379
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/cdrom_32x32_24.bmp
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/eject1.png b/Src/Plugins/Library/ml_disc/resources/eject1.png
new file mode 100644
index 00000000..7db2d8ad
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/eject1.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/eject2.png b/Src/Plugins/Library/ml_disc/resources/eject2.png
new file mode 100644
index 00000000..72bdd140
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/eject2.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/eject3.png b/Src/Plugins/Library/ml_disc/resources/eject3.png
new file mode 100644
index 00000000..54f37222
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/eject3.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/eject4.png b/Src/Plugins/Library/ml_disc/resources/eject4.png
new file mode 100644
index 00000000..b929fbac
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/eject4.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/eject_d.png b/Src/Plugins/Library/ml_disc/resources/eject_d.png
new file mode 100644
index 00000000..6b7c9b45
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/eject_d.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/eject_n.png b/Src/Plugins/Library/ml_disc/resources/eject_n.png
new file mode 100644
index 00000000..dd198f2b
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/eject_n.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/eject_p.png b/Src/Plugins/Library/ml_disc/resources/eject_p.png
new file mode 100644
index 00000000..68aa0c16
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/eject_p.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/enqueue_d.png b/Src/Plugins/Library/ml_disc/resources/enqueue_d.png
new file mode 100644
index 00000000..fe755949
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/enqueue_d.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/enqueue_menu_16x16.png b/Src/Plugins/Library/ml_disc/resources/enqueue_menu_16x16.png
new file mode 100644
index 00000000..d3dd357a
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/enqueue_menu_16x16.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/enqueue_n.png b/Src/Plugins/Library/ml_disc/resources/enqueue_n.png
new file mode 100644
index 00000000..7b7ad057
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/enqueue_n.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/enqueue_p.png b/Src/Plugins/Library/ml_disc/resources/enqueue_p.png
new file mode 100644
index 00000000..12704e8f
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/enqueue_p.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/enqueuem_d.png b/Src/Plugins/Library/ml_disc/resources/enqueuem_d.png
new file mode 100644
index 00000000..0bd93761
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/enqueuem_d.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/enqueuem_n.png b/Src/Plugins/Library/ml_disc/resources/enqueuem_n.png
new file mode 100644
index 00000000..f76d4027
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/enqueuem_n.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/enqueuem_p.png b/Src/Plugins/Library/ml_disc/resources/enqueuem_p.png
new file mode 100644
index 00000000..11f1b4d8
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/enqueuem_p.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/filecopy.png b/Src/Plugins/Library/ml_disc/resources/filecopy.png
new file mode 100644
index 00000000..85d7f746
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/filecopy.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/listbox_back_2x68x24.bmp b/Src/Plugins/Library/ml_disc/resources/listbox_back_2x68x24.bmp
new file mode 100644
index 00000000..6786dbbe
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/listbox_back_2x68x24.bmp
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/play_d.png b/Src/Plugins/Library/ml_disc/resources/play_d.png
new file mode 100644
index 00000000..f68d02ad
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/play_d.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/play_menu_16x16.png b/Src/Plugins/Library/ml_disc/resources/play_menu_16x16.png
new file mode 100644
index 00000000..20f265dd
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/play_menu_16x16.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/play_n.png b/Src/Plugins/Library/ml_disc/resources/play_n.png
new file mode 100644
index 00000000..b457b984
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/play_n.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/play_p.png b/Src/Plugins/Library/ml_disc/resources/play_p.png
new file mode 100644
index 00000000..e6a33c91
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/play_p.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/playm_d.png b/Src/Plugins/Library/ml_disc/resources/playm_d.png
new file mode 100644
index 00000000..6c279fb7
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/playm_d.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/playm_n.png b/Src/Plugins/Library/ml_disc/resources/playm_n.png
new file mode 100644
index 00000000..a3b219c3
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/playm_n.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/playm_p.png b/Src/Plugins/Library/ml_disc/resources/playm_p.png
new file mode 100644
index 00000000..ece39e30
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/playm_p.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/rip&burn_logo_228x25x16.bmp b/Src/Plugins/Library/ml_disc/resources/rip&burn_logo_228x25x16.bmp
new file mode 100644
index 00000000..ee89767a
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/rip&burn_logo_228x25x16.bmp
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/resources/sonic_powered_44x22.bmp b/Src/Plugins/Library/ml_disc/resources/sonic_powered_44x22.bmp
new file mode 100644
index 00000000..5ead745e
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/resources/sonic_powered_44x22.bmp
Binary files differ
diff --git a/Src/Plugins/Library/ml_disc/settings.cpp b/Src/Plugins/Library/ml_disc/settings.cpp
new file mode 100644
index 00000000..e7fcb40c
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/settings.cpp
@@ -0,0 +1,573 @@
+#include "main.h"
+#include "./settings.h"
+#include "./resource.h"
+#include <shlwapi.h>
+#include <strsafe.h>
+
+#define SECTION_DEFAULT TEXT("gen_ml_config")
+#define SECTION_DATAVIEW TEXT("data_view")
+// Keys
+#define KEY_EF_PATH TEXT("extractpath")
+#define KEY_EF_TITLEFMT TEXT("extractfmt2")
+#define KEY_EF_PLAYLISTFMT TEXT("extractplfmt2")
+#define KEY_EF_COMMENTTEXT TEXT("tagcomment")
+#define KEY_EF_UPPEREXTENSION TEXT("extractucext")
+#define KEY_EF_ADDMETADATA TEXT("extracttag")
+#define KEY_EF_CALCULATERG TEXT("auto_rg")
+#define KEY_EF_USETOTALTRACKS TEXT("total_tracks")
+#define KEY_EF_ADDTOMLDB TEXT("extractaddml")
+#define KEY_EF_TRACKOFFSET TEXT("trackoffs")
+#define KEY_EF_CREATEM3U TEXT("extractm3u")
+#define KEY_EF_CREATEPLS TEXT("extractpls")
+#define KEY_EF_CREATEMLPL TEXT("extractplml")
+#define KEY_EF_USEM3UEXT TEXT("extractm3uext")
+#define KEY_EF_FOURCC TEXT("extract4cc")
+
+// Defeault values
+#define DEF_EF_PATH GetDefaultExtractPath()
+#define DEF_EF_TITLEFMT TEXT("<Artist> - <Album>\\## - <Trackartist> - <Title>")
+#define DEF_EF_PLAYLISTFMT TEXT("<Artist> - <Album>\\<Artist> - <Album>")
+#define DEF_EF_COMMENTTEXT TEXT("Ripped by Winamp")
+#define DEF_EF_UPPEREXTENSION FALSE
+#define DEF_EF_ADDMETADATA TRUE
+#define DEF_EF_CALCULATERG FALSE
+#define DEF_EF_USETOTALTRACKS FALSE
+#define DEF_EF_ADDTOMLDB TRUE
+#define DEF_EF_TRACKOFFSET 1
+#define DEF_EF_CREATEM3U TRUE
+#define DEF_EF_CREATEPLS FALSE
+#define DEF_EF_CREATEMLPL TRUE
+#define DEF_EF_USEM3UEXT TRUE
+#define DEF_EF_FOURCC mmioFOURCC('A','A','C','f')
+
+
+#define KEY_CF_PATH KEY_EF_PATH
+#define KEY_CF_USETITLEFMT TEXT("copy_use_title_fmt")
+#define KEY_CF_TITLEFMT TEXT("copy_title_fmt")
+#define KEY_CF_ADDTOMLDB TEXT("copy_add_to_mldb")
+#define KEY_CF_CALCULATERG TEXT("copy_calc_gain")
+#define KEY_CF_GAINMODE TEXT("copy_gain_mode")
+
+#define DEF_CF_PATH DEF_EF_PATH
+#define DEF_CF_USETITLEFMT TRUE
+#define DEF_CF_TITLEFMT TEXT("<Artist>\\<Album>\\<Filename><extension>")
+#define DEF_CF_ADDTOMLDB DEF_EF_ADDTOMLDB
+#define DEF_CF_CALCULATERG DEF_EF_CALCULATERG
+#define DEF_CF_GAINMODE 0
+
+
+#define KEY_GF_SHOWINFO TEXT("showinfo")
+#define KEY_GF_SHOWPARENT TEXT("showparent")
+#define KEY_GF_ENQUEUEBYDEFAULT TEXT("enqueuedef")
+
+#define DEF_GF_SHOWINFO FALSE
+#define DEF_GF_SHOWPARENT FALSE
+#define DEF_GF_ENQUEUEBYDEFAULT FALSE
+
+#define KEY_DVF_COLUMNLIST TEXT("column_list")
+#define KEY_DVF_LASTFOLDER TEXT("last_folder")
+#define KEY_DVF_ORDERBY TEXT("column_order_by")
+#define KEY_DVF_ORDERASC TEXT("column_order_asc")
+#define KEY_DVF_VIEWMODE TEXT("view_mode")
+#define KEY_DVF_SHOWAUDIO TEXT("show_audio")
+#define KEY_DVF_SHOWVIDEO TEXT("show_video")
+#define KEY_DVF_SHOWPLAYLIST TEXT("show_playlist")
+#define KEY_DVF_SHOWUNKNOWN TEXT("show_unknown")
+#define KEY_DVF_HIDEEXTENSION TEXT("hide_extension")
+#define KEY_DVF_IGNOREHIDDEN TEXT("ignore_hidden")
+#define KEY_DVF_DIVIDERPOS TEXT("horz_divider_pos")
+
+
+#define DEF_DVF_COLUMNLIST NULL
+#define DEF_DVF_LASTFOLDER NULL
+#define DEF_DVF_ORDERBY 0
+#define DEF_DVF_ORDERASC TRUE
+#define DEF_DVF_VIEWMODE -1
+#define DEF_DVF_SHOWAUDIO TRUE
+#define DEF_DVF_SHOWVIDEO TRUE
+#define DEF_DVF_SHOWPLAYLIST TRUE
+#define DEF_DVF_SHOWUNKNOWN FALSE
+#define DEF_DVF_HIDEEXTENSION TRUE
+#define DEF_DVF_IGNOREHIDDEN TRUE
+#define DEF_DVF_DIVIDERPOS 240
+
+
+// configs
+#define GLOBAL g_config
+#define VIEW g_view_metaconf
+
+// readers
+#define READ_LPTSTR(config) (config)->ReadCbStringEx
+#define READ_QUOTED_LPTSTR(config) (config)->ReadCbQuotedString
+#define GET_INT(config) (config)->ReadIntEx
+#define GET_BOOL(config) (config)->ReadBoolEx
+
+// writers
+#define WRITE_LPCTSTR(config) (config)->WriteStringEx
+#define WRITE_QUOTED_LPCTSTR(config) (config)->WriteQuotedString
+#define WRITE_INT(config) (config)->WriteIntEx
+#define WRITE_BOOL(config) (config)->WriteBoolEx
+
+
+#define CHECK_EXREAD(type, config, section, field, buffer, cb) case field##: READ_##type##(config)(((##type##)buffer), cb, section, KEY_##field, DEF_##field); return S_OK;
+#define CHECK_EXREAD_QUOTED(type, config, section, field, buffer, cb) case field##: READ_QUOTED_##type##(config)(((##type##)buffer), cb, section, KEY_##field, DEF_##field); return S_OK;
+#define CHECK_EXGET(type, config, section, field, result) case field##: *((##type##*)result) = GET_##type##(config)(section, KEY_##field, DEF_##field); return S_OK;
+#define CHECK_EXWRITE(type, config, section, field, value) case field##: WRITE_##type##(config)(section, KEY_##field, value); return S_OK;
+#define CHECK_EXWRITE_QUOTED(type, config, section, field, value) case field##: WRITE_QUOTED_##type##(config)(section, KEY_##field, value); return S_OK;
+
+#define CHECK_DEFAULT(type, field, result) case field##: *((##type##*)result) = DEF_##field; return S_OK;
+#define CHECK_READ(type, config, field, buffer, cb) CHECK_EXREAD(type, config, SECTION_DEFAULT, field, buffer, cb)
+#define CHECK_READ_QUOTED(type, config, field, buffer, cb) CHECK_EXREAD_QUOTED(type, config, SECTION_DEFAULT, field, buffer, cb)
+#define CHECK_GET(type, config, field, result) CHECK_EXGET(type, config, SECTION_DEFAULT, field, result)
+#define CHECK_WRITE(type, config, field, value) CHECK_EXWRITE(type, config, SECTION_DEFAULT, field, value)
+#define CHECK_WRITE_QUOTED(type, config, field, value) CHECK_EXWRITE_QUOTED(type, config, SECTION_DEFAULT, field, value)
+
+#define CHECK_EXREAD_VIEW(type, section, field, buffer, cb) CHECK_EXREAD(type, VIEW, section, field, buffer, cb)
+#define CHECK_EXGET_VIEW(type, section, field, result) CHECK_EXGET(type, VIEW, section, field, result)
+#define CHECK_EXWRITE_VIEW(type, section, field, value) CHECK_EXWRITE(type, VIEW, section, field, value)
+
+#define CHECK_READ_GLOBAL(type, field, buffer, cb) CHECK_READ(type, GLOBAL, field, buffer, cb)
+#define CHECK_READ_QUOTED_GLOBAL(type, field, buffer, cb) CHECK_READ_QUOTED(type, GLOBAL, field, buffer, cb)
+#define CHECK_GET_GLOBAL(type, field, result) CHECK_GET(type, GLOBAL, field, result)
+#define CHECK_READ_VIEW(type, field, buffer, cb) CHECK_READ(type, VIEW, field, buffer, cb)
+#define CHECK_GET_VIEW(type, field, result) CHECK_GET(type, VIEW, field, result)
+#define CHECK_WRITE_GLOBAL(type, field, value) CHECK_WRITE(type, GLOBAL, field, value)
+#define CHECK_WRITE_QUOTED_GLOBAL(type, field, value) CHECK_WRITE_QUOTED(type, GLOBAL, field, value)
+#define CHECK_WRITE_VIEW(type, field, value) CHECK_WRITE(type, VIEW, field, value)
+
+#define STR_CHECK_DEFAULT(field, result) CHECK_DEFAULT(LPCTSTR, field, result)
+#define INT_CHECK_DEFAULT(field, result) CHECK_DEFAULT(INT, field, result)
+#define BOOL_CHECK_DEFAULT(field, result) CHECK_DEFAULT(BOOL, field, result)
+
+#define STR_CHECK_READ_GLOBAL(field, buffer, cb) CHECK_READ_GLOBAL(LPTSTR, field, buffer, cb)
+#define STR_CHECK_READ_QUOTED_GLOBAL(field, buffer, cb) CHECK_READ_QUOTED_GLOBAL(LPTSTR, field, buffer, cb)
+#define INT_CHECK_GET_GLOBAL(field, result) CHECK_GET_GLOBAL(INT, field, result)
+#define BOOL_CHECK_GET_GLOBAL(field, result) CHECK_GET_GLOBAL(BOOL, field, result)
+
+#define STR_CHECK_WRITE_GLOBAL(field, value) CHECK_WRITE_GLOBAL(LPCTSTR, field, value)
+#define STR_CHECK_WRITE_QUOTED_GLOBAL(field, value) CHECK_WRITE_QUOTED_GLOBAL(LPCTSTR, field, value)
+#define INT_CHECK_WRITE_GLOBAL(field, value) CHECK_WRITE_GLOBAL(INT, field, value)
+#define BOOL_CHECK_WRITE_GLOBAL(field, value) CHECK_WRITE_GLOBAL(BOOL, field, value)
+
+#define STR_CHECK_EXREAD_VIEW(section, field, buffer, cb) CHECK_EXREAD_VIEW(LPTSTR, section, field, buffer, cb)
+#define INT_CHECK_EXGET_VIEW(section, field, result) CHECK_EXGET_VIEW(INT, section, field, result)
+#define BOOL_CHECK_EXGET_VIEW(section, field, result) CHECK_EXGET_VIEW(BOOL, section, field, result)
+
+#define STR_CHECK_EXWRITE_VIEW(section, field, value) CHECK_EXWRITE_VIEW(LPCTSTR, section, field, value)
+#define INT_CHECK_EXWRITE_VIEW(section, field, value) CHECK_EXWRITE_VIEW(INT, section, field, value)
+#define BOOL_CHECK_EXWRITE_VIEW(section, field, value) CHECK_EXWRITE_VIEW(BOOL, section, field, value)
+
+#define STR_CHECK_READ_VIEW(field, buffer, cb) CHECK_READ_VIEW(LPTSTR, field, buffer, cb)
+#define INT_CHECK_GET_VIEW(field, result) CHECK_GET_VIEW(INT, field, result)
+#define BOOL_CHECK_GET_VIEW(field, result) CHECK_GET_VIEW(BOOL, field, result)
+
+#define STR_CHECK_WRITE_VIEW(field, value) CHECK_WRITE_VIEW(LPCTSTR, field, value)
+#define INT_CHECK_WRITE_VIEW(field, value) CHECK_WRITE_VIEW(INT, field, value)
+#define BOOL_CHECK_WRITE_VIEW(field, value) CHECK_WRITE_VIEW(BOOL, field, value)
+
+static LPCWSTR GetDefaultExtractPath()
+{
+ static TCHAR m_def_extract_path[MAX_PATH] = { TEXT('\0'), };
+
+ if (L'\0' == m_def_extract_path[0])
+ {
+ if(FAILED(SHGetFolderPath(NULL, CSIDL_MYMUSIC, NULL, SHGFP_TYPE_CURRENT, m_def_extract_path)))
+ {
+ if(FAILED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, m_def_extract_path)))
+ {
+ // and if that all fails then do a reasonable default
+ StringCchCopyW(m_def_extract_path, ARRAYSIZE(m_def_extract_path), L"C:\\My Music");
+ }
+ // if there's no valid My Music folder (typically win2k) then default to %my_documents%\my music
+ else PathCombineW(m_def_extract_path, m_def_extract_path, L"My Music");
+ }
+ }
+ return m_def_extract_path;
+}
+
+HRESULT Settings_GetDefault(INT categoryId, INT fieldId, VOID *pValue)
+{
+ switch(categoryId)
+ {
+ case C_EXTRACT:
+ switch(fieldId)
+ {
+ STR_CHECK_DEFAULT(EF_PATH, pValue);
+ STR_CHECK_DEFAULT(EF_TITLEFMT, pValue);
+ STR_CHECK_DEFAULT(EF_PLAYLISTFMT, pValue);
+ STR_CHECK_DEFAULT(EF_COMMENTTEXT, pValue);
+ BOOL_CHECK_DEFAULT(EF_UPPEREXTENSION, pValue);
+ BOOL_CHECK_DEFAULT(EF_ADDMETADATA, pValue);
+ BOOL_CHECK_DEFAULT(EF_CALCULATERG, pValue);
+ BOOL_CHECK_DEFAULT(EF_USETOTALTRACKS, pValue);
+ BOOL_CHECK_DEFAULT(EF_ADDTOMLDB, pValue);
+ INT_CHECK_DEFAULT(EF_TRACKOFFSET, pValue);
+ BOOL_CHECK_DEFAULT(EF_CREATEM3U, pValue);
+ BOOL_CHECK_DEFAULT(EF_CREATEPLS, pValue);
+ BOOL_CHECK_DEFAULT(EF_CREATEMLPL, pValue);
+ BOOL_CHECK_DEFAULT(EF_USEM3UEXT, pValue);
+ INT_CHECK_DEFAULT(EF_FOURCC, pValue);
+ }
+ break;
+ case C_COPY:
+ switch(fieldId)
+ {
+ STR_CHECK_DEFAULT(CF_PATH, pValue);
+ STR_CHECK_DEFAULT(CF_TITLEFMT, pValue);
+ BOOL_CHECK_DEFAULT(CF_USETITLEFMT, pValue);
+ BOOL_CHECK_DEFAULT(CF_ADDTOMLDB, pValue);
+ BOOL_CHECK_DEFAULT(CF_CALCULATERG, pValue);
+ INT_CHECK_DEFAULT(CF_GAINMODE, pValue);
+ }
+ break;
+ case C_GLOBAL:
+ switch(fieldId)
+ {
+ BOOL_CHECK_DEFAULT(GF_SHOWINFO, pValue);
+ BOOL_CHECK_DEFAULT(GF_SHOWPARENT, pValue);
+ BOOL_CHECK_DEFAULT(GF_ENQUEUEBYDEFAULT, pValue);
+ }
+ break;
+ case C_DATAVIEW:
+ switch(fieldId)
+ {
+ STR_CHECK_DEFAULT(DVF_COLUMNLIST, pValue);
+ STR_CHECK_DEFAULT(DVF_LASTFOLDER, pValue);
+ INT_CHECK_DEFAULT(DVF_ORDERBY, pValue);
+ BOOL_CHECK_DEFAULT(DVF_ORDERASC, pValue);
+ INT_CHECK_DEFAULT(DVF_VIEWMODE, pValue);
+ BOOL_CHECK_DEFAULT(DVF_SHOWAUDIO, pValue);
+ BOOL_CHECK_DEFAULT(DVF_SHOWVIDEO, pValue);
+ BOOL_CHECK_DEFAULT(DVF_SHOWPLAYLIST, pValue);
+ BOOL_CHECK_DEFAULT(DVF_SHOWUNKNOWN, pValue);
+ BOOL_CHECK_DEFAULT(DVF_HIDEEXTENSION, pValue);
+ BOOL_CHECK_DEFAULT(DVF_IGNOREHIDDEN, pValue);
+ INT_CHECK_DEFAULT(DVF_DIVIDERPOS, pValue);
+ }
+ break;
+ }
+ return E_INVALIDARG;
+}
+
+
+HRESULT Settings_ReadValue(INT categoryId, INT fieldId, VOID *pValue, INT cbSize)
+{
+ LPCTSTR pszSection;
+ switch(categoryId)
+ {
+ case C_EXTRACT:
+ switch(fieldId)
+ {
+ STR_CHECK_READ_GLOBAL(EF_PATH, pValue, cbSize);
+ STR_CHECK_READ_QUOTED_GLOBAL(EF_TITLEFMT, pValue, cbSize);
+ STR_CHECK_READ_QUOTED_GLOBAL(EF_PLAYLISTFMT, pValue, cbSize);
+ STR_CHECK_READ_QUOTED_GLOBAL(EF_COMMENTTEXT, pValue, cbSize);
+ BOOL_CHECK_GET_GLOBAL(EF_UPPEREXTENSION, pValue);
+ BOOL_CHECK_GET_GLOBAL(EF_ADDMETADATA, pValue);
+ BOOL_CHECK_GET_GLOBAL(EF_CALCULATERG, pValue);
+ BOOL_CHECK_GET_GLOBAL(EF_USETOTALTRACKS, pValue);
+ BOOL_CHECK_GET_GLOBAL(EF_ADDTOMLDB, pValue);
+ INT_CHECK_GET_GLOBAL(EF_TRACKOFFSET, pValue);
+ BOOL_CHECK_GET_GLOBAL(EF_CREATEM3U, pValue);
+ BOOL_CHECK_GET_GLOBAL(EF_CREATEPLS, pValue);
+ BOOL_CHECK_GET_GLOBAL(EF_CREATEMLPL, pValue);
+ BOOL_CHECK_GET_GLOBAL(EF_USEM3UEXT, pValue);
+ INT_CHECK_GET_GLOBAL(EF_FOURCC, pValue);
+ }
+ break;
+ case C_COPY:
+ switch(fieldId)
+ {
+ STR_CHECK_READ_GLOBAL(CF_PATH, pValue, cbSize);
+ STR_CHECK_READ_QUOTED_GLOBAL(CF_TITLEFMT, pValue, cbSize);
+ BOOL_CHECK_GET_GLOBAL(CF_USETITLEFMT, pValue);
+ BOOL_CHECK_GET_GLOBAL(CF_ADDTOMLDB, pValue);
+ BOOL_CHECK_GET_GLOBAL(CF_CALCULATERG, pValue);
+ INT_CHECK_GET_GLOBAL(CF_GAINMODE, pValue);
+ }
+ break;
+ case C_GLOBAL:
+ switch(fieldId)
+ {
+ BOOL_CHECK_GET_VIEW(GF_SHOWINFO, pValue);
+ BOOL_CHECK_GET_VIEW(GF_SHOWPARENT, pValue);
+ BOOL_CHECK_GET_GLOBAL(GF_ENQUEUEBYDEFAULT, pValue);
+ }
+ break;
+ case C_DATAVIEW:
+ pszSection = SECTION_DATAVIEW;
+ switch(fieldId)
+ {
+ STR_CHECK_EXREAD_VIEW(pszSection, DVF_COLUMNLIST, pValue, cbSize);
+ STR_CHECK_EXREAD_VIEW(pszSection, DVF_LASTFOLDER, pValue, cbSize);
+ INT_CHECK_EXGET_VIEW(pszSection, DVF_ORDERBY, pValue);
+ BOOL_CHECK_EXGET_VIEW(pszSection, DVF_ORDERASC, pValue);
+ INT_CHECK_EXGET_VIEW(pszSection, DVF_VIEWMODE, pValue);
+ BOOL_CHECK_EXGET_VIEW(pszSection, DVF_SHOWAUDIO, pValue);
+ BOOL_CHECK_EXGET_VIEW(pszSection, DVF_SHOWVIDEO, pValue);
+ BOOL_CHECK_EXGET_VIEW(pszSection, DVF_SHOWPLAYLIST, pValue);
+ BOOL_CHECK_EXGET_VIEW(pszSection, DVF_SHOWUNKNOWN, pValue);
+ BOOL_CHECK_EXGET_VIEW(pszSection, DVF_HIDEEXTENSION, pValue);
+ BOOL_CHECK_EXGET_VIEW(pszSection, DVF_IGNOREHIDDEN, pValue);
+ INT_CHECK_EXGET_VIEW(pszSection, DVF_DIVIDERPOS, pValue);
+ }
+ break;
+ }
+ return E_INVALIDARG;
+}
+
+HRESULT Settings_SetString(INT categoryId, INT fieldId, LPCWSTR pszBuffer)
+{
+ LPCTSTR pszSection;
+ switch(categoryId)
+ {
+ case C_EXTRACT:
+ switch(fieldId)
+ {
+ STR_CHECK_WRITE_QUOTED_GLOBAL(EF_PATH, pszBuffer);
+ STR_CHECK_WRITE_QUOTED_GLOBAL(EF_TITLEFMT, pszBuffer);
+ STR_CHECK_WRITE_QUOTED_GLOBAL(EF_PLAYLISTFMT, pszBuffer);
+ STR_CHECK_WRITE_QUOTED_GLOBAL(EF_COMMENTTEXT, pszBuffer);
+ STR_CHECK_WRITE_GLOBAL(EF_UPPEREXTENSION, pszBuffer);
+ STR_CHECK_WRITE_GLOBAL(EF_ADDMETADATA, pszBuffer);
+ STR_CHECK_WRITE_GLOBAL(EF_CALCULATERG, pszBuffer);
+ STR_CHECK_WRITE_GLOBAL(EF_USETOTALTRACKS, pszBuffer);
+ STR_CHECK_WRITE_GLOBAL(EF_ADDTOMLDB, pszBuffer);
+ STR_CHECK_WRITE_GLOBAL(EF_TRACKOFFSET, pszBuffer);
+ STR_CHECK_WRITE_GLOBAL(EF_CREATEM3U, pszBuffer);
+ STR_CHECK_WRITE_GLOBAL(EF_CREATEPLS, pszBuffer);
+ STR_CHECK_WRITE_GLOBAL(EF_CREATEMLPL, pszBuffer);
+ STR_CHECK_WRITE_GLOBAL(EF_USEM3UEXT, pszBuffer);
+ STR_CHECK_WRITE_GLOBAL(EF_FOURCC, pszBuffer);
+ }
+ break;
+ case C_COPY:
+ switch(fieldId)
+ {
+ STR_CHECK_WRITE_QUOTED_GLOBAL(CF_PATH, pszBuffer);
+ STR_CHECK_WRITE_QUOTED_GLOBAL(CF_TITLEFMT, pszBuffer);
+ STR_CHECK_WRITE_GLOBAL(CF_USETITLEFMT, pszBuffer);
+ STR_CHECK_WRITE_GLOBAL(CF_ADDTOMLDB, pszBuffer);
+ STR_CHECK_WRITE_GLOBAL(CF_CALCULATERG, pszBuffer);
+ STR_CHECK_WRITE_GLOBAL(CF_GAINMODE, pszBuffer);
+ }
+ break;
+ case C_GLOBAL:
+ switch(fieldId)
+ {
+ STR_CHECK_WRITE_VIEW(GF_SHOWINFO, pszBuffer);
+ STR_CHECK_WRITE_VIEW(GF_SHOWPARENT, pszBuffer);
+ STR_CHECK_WRITE_VIEW(GF_ENQUEUEBYDEFAULT, pszBuffer);
+ }
+ break;
+ case C_DATAVIEW:
+ pszSection = SECTION_DATAVIEW;
+ switch(fieldId)
+ {
+ STR_CHECK_EXWRITE_VIEW(pszSection, DVF_COLUMNLIST, pszBuffer);
+ STR_CHECK_EXWRITE_VIEW(pszSection, DVF_LASTFOLDER, pszBuffer);
+ STR_CHECK_EXWRITE_VIEW(pszSection, DVF_ORDERBY, pszBuffer);
+ STR_CHECK_EXWRITE_VIEW(pszSection, DVF_ORDERASC, pszBuffer);
+ STR_CHECK_EXWRITE_VIEW(pszSection, DVF_VIEWMODE, pszBuffer);
+ STR_CHECK_EXWRITE_VIEW(pszSection, DVF_SHOWAUDIO, pszBuffer);
+ STR_CHECK_EXWRITE_VIEW(pszSection, DVF_SHOWVIDEO, pszBuffer);
+ STR_CHECK_EXWRITE_VIEW(pszSection, DVF_SHOWPLAYLIST, pszBuffer);
+ STR_CHECK_EXWRITE_VIEW(pszSection, DVF_SHOWUNKNOWN, pszBuffer);
+ STR_CHECK_EXWRITE_VIEW(pszSection, DVF_HIDEEXTENSION, pszBuffer);
+ STR_CHECK_EXWRITE_VIEW(pszSection, DVF_IGNOREHIDDEN, pszBuffer);
+ STR_CHECK_EXWRITE_VIEW(pszSection, DVF_DIVIDERPOS, pszBuffer);
+ }
+ break;
+ }
+ return E_INVALIDARG;
+}
+
+
+HRESULT Settings_GetInt(INT categoryId, INT fieldId, INT *pnVal)
+{
+ return Settings_ReadValue(categoryId, fieldId, pnVal, sizeof(INT));
+}
+HRESULT Settings_GetBool(INT categoryId, INT fieldId, BOOL *pbVal)
+{
+ return Settings_ReadValue(categoryId, fieldId, pbVal, sizeof(BOOL));
+}
+
+HRESULT Settings_ReadString(INT categoryId, INT fieldId, LPTSTR pszBuffer, INT cchBufferMax)
+{
+ return Settings_ReadValue(categoryId, fieldId, pszBuffer, cchBufferMax * sizeof(TCHAR));
+}
+
+HRESULT Settings_SetWindowText(INT categoryId, INT fieldId, HWND hwnd)
+{
+ TCHAR szBuffer[8192] = {0};
+ HRESULT hr = Settings_ReadValue(categoryId, fieldId, szBuffer, sizeof(szBuffer));
+ if (S_OK == hr) SetWindowText(hwnd, szBuffer);
+ return hr;
+}
+
+HRESULT Settings_SetWindowInt(INT categoryId, INT fieldId, HWND hwnd)
+{
+ INT nVal;
+ HRESULT hr = Settings_ReadValue(categoryId, fieldId, &nVal, sizeof(nVal));
+
+ if (S_OK == hr)
+ {
+ TCHAR szBuffer[256] = {0};
+ hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("%d"), nVal);
+ if (S_OK == hr) SetWindowText(hwnd, szBuffer);
+ }
+ return hr;
+}
+HRESULT Settings_SetDlgItemText(INT categoryId, INT fieldId, HWND hdlg, INT nItemId)
+{
+ HWND hctrl = GetDlgItem(hdlg, nItemId);
+ return (hctrl) ? Settings_SetWindowText(categoryId, fieldId, hctrl) : E_HANDLE;
+}
+
+HRESULT Settings_SetDlgItemInt(INT categoryId, INT fieldId, HWND hdlg, INT nItemId)
+{
+ HWND hctrl = GetDlgItem(hdlg, nItemId);
+ return (hctrl) ? Settings_SetWindowInt(categoryId, fieldId, hctrl) : E_HANDLE;
+}
+
+HRESULT Settings_SetCheckBox(INT categoryId, INT fieldId, HWND hdlg, INT nItemId)
+{
+ INT nVal;
+ HRESULT hr = Settings_GetInt(categoryId, fieldId, &nVal);
+ if (S_OK == hr) hr = (CheckDlgButton(hdlg, nItemId, (0 != nVal) ? BST_CHECKED : BST_UNCHECKED)) ? S_OK : E_HANDLE;
+ return hr;
+}
+
+HRESULT Settings_SetInt(INT categoryId, INT fieldId, INT nValue)
+{
+ TCHAR szBuffer[64] = {0};
+ HRESULT hr = StringCchPrintfW(szBuffer, ARRAYSIZE(szBuffer), TEXT("%d"), nValue);
+ if (S_OK == hr) hr = Settings_SetString(categoryId, fieldId, szBuffer);
+ return hr;
+}
+HRESULT Settings_SetBool(INT categoryId, INT fieldId, BOOL bValue)
+{
+ return Settings_SetInt(categoryId, fieldId, (0 != bValue));
+}
+HRESULT Settings_FromWindowText(INT categoryId, INT fieldId, HWND hwnd)
+{
+ if (!hwnd) return E_HANDLE;
+ INT l = GetWindowTextLength(hwnd);
+ if (l < 1024)
+ {
+ TCHAR szBuffer[1024] = {0};
+ GetWindowText(hwnd, szBuffer, ARRAYSIZE(szBuffer));
+ return Settings_SetString(categoryId, fieldId, szBuffer);
+ }
+ else
+ {
+ LPTSTR pszBuffer = (LPTSTR)calloc((l + 1), sizeof(TCHAR));
+ if (!pszBuffer) return E_OUTOFMEMORY;
+ GetWindowText(hwnd, pszBuffer, sizeof(TCHAR)*(l + 1));
+ HRESULT hr = Settings_SetString(categoryId, fieldId, pszBuffer);
+ free(pszBuffer);
+ return hr;
+ }
+}
+HRESULT Settings_FromDlgItemText(INT categoryId, INT fieldId, HWND hdlg, INT nItemId)
+{
+ HWND hctrl = GetDlgItem(hdlg, nItemId);
+ return (hctrl) ? Settings_FromWindowText(categoryId, fieldId, hctrl) : E_HANDLE;
+}
+
+HRESULT Settings_FromCheckBox(INT categoryId, INT fieldId, HWND hdlg, INT nItemId)
+{
+ UINT v = IsDlgButtonChecked(hdlg, nItemId);
+ return Settings_SetInt(categoryId, fieldId, v);
+}
+
+typedef struct _FBROWSER
+{
+ LPCWSTR pszSelection;
+} FBROWSER;
+
+BOOL CALLBACK browseEnumProc(HWND hwnd, LPARAM lParam)
+{
+ wchar_t cl[32] = {0};
+ GetClassNameW(hwnd, cl, ARRAYSIZE(cl));
+ if (!lstrcmpiW(cl, WC_TREEVIEW))
+ {
+ PostMessage(hwnd, TVM_ENSUREVISIBLE, 0, (LPARAM)TreeView_GetSelection(hwnd));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+int CALLBACK WINAPI BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM pData)
+{
+ FBROWSER *pfb = (FBROWSER*)pData;
+ switch(uMsg)
+ {
+ case BFFM_INITIALIZED:
+ if (pfb && pfb->pszSelection) SendMessageW(hwnd, BFFM_SETSELECTIONW, TRUE, (LPARAM)pfb->pszSelection);
+
+ // this is not nice but it fixes the selection not working correctly on all OSes
+ EnumChildWindows(hwnd, browseEnumProc, 0);
+ break;
+ }
+ return 0;
+}
+
+HRESULT Settings_BrowseForFolder(INT categoryId, INT fieldId, HWND hDlg, INT nEditId)
+{
+ BROWSEINFO bi = {0};
+ FBROWSER fb;
+ wchar_t szPath[MAX_PATH] = {0};
+
+ HRESULT hr = Settings_ReadString(categoryId,fieldId, szPath, ARRAYSIZE(szPath));
+ if (S_OK != hr) return hr;
+
+ fb.pszSelection = szPath;
+
+ bi.hwndOwner = hDlg;
+ bi.pidlRoot = NULL;
+ bi.pszDisplayName = szPath;
+ bi.lpszTitle = WASABI_API_LNGSTRINGW(IDS_CHOOSE_A_FOLDER);
+ bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_RETURNFSANCESTORS | BIF_NEWDIALOGSTYLE;
+ bi.lpfn = BrowseCallbackProc;
+ bi.lParam = (LPARAM)&fb;
+ HRESULT hrCom = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+ if (SUCCEEDED(hrCom))
+ {
+ ITEMIDLIST *idlist = SHBrowseForFolder(&bi);
+ if (idlist)
+ {
+ if (SHGetPathFromIDList(idlist, szPath))
+ {
+ hr = Settings_SetString(categoryId,fieldId, szPath);
+ if (S_OK == hr) SetDlgItemText(hDlg, nEditId, szPath);
+ }
+ else hr = S_FALSE;
+ CoTaskMemFree(idlist);
+ }
+ }
+ else hr = hrCom;
+ if (S_OK == hrCom) CoUninitialize();
+ return hr;
+}
+
+HRESULT Settings_SetDirectoryCtrl(INT categoryId, INT fieldId, HWND hdlg, INT nItemId)
+{
+ TCHAR szBuffer[MAX_PATH] = {0};
+ HRESULT hr;
+ hr = Settings_ReadString(categoryId, fieldId, szBuffer, ARRAYSIZE(szBuffer));
+ if (S_OK != hr) *szBuffer = TEXT('\0');
+ else CleanupDirectoryString(szBuffer);
+ SetDlgItemText(hdlg, nItemId, szBuffer);
+ return hr;
+}
+HRESULT Settings_FromDirectoryCtrl(INT categoryId, INT fieldId, HWND hdlg, INT nItemId)
+{
+ TCHAR szBuffer[MAX_PATH] = {0};
+ GetDlgItemText(hdlg, nItemId, szBuffer, ARRAYSIZE(szBuffer));
+ CleanupDirectoryString(szBuffer);
+ return Settings_SetString(categoryId, fieldId, szBuffer);
+}
+
+
diff --git a/Src/Plugins/Library/ml_disc/settings.h b/Src/Plugins/Library/ml_disc/settings.h
new file mode 100644
index 00000000..86aafa7a
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/settings.h
@@ -0,0 +1,108 @@
+#ifndef NULLOSFT_MEDIALIBRARY_MLDISC_SETTINGS_HEADER
+#define NULLOSFT_MEDIALIBRARY_MLDISC_SETTINGS_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <windows.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ // Groups
+enum
+{
+ C_EXTRACT = 0,
+ C_COPY,
+ C_GLOBAL,
+ C_DATAVIEW,
+};
+
+// Extract Group Fields
+enum
+{
+ EF_PATH = 0,
+ EF_TITLEFMT,
+ EF_PLAYLISTFMT,
+ EF_UPPEREXTENSION,
+ EF_ADDMETADATA,
+ EF_CALCULATERG,
+ EF_USETOTALTRACKS,
+ EF_ADDTOMLDB,
+ EF_TRACKOFFSET,
+ EF_COMMENTTEXT,
+ EF_CREATEM3U,
+ EF_CREATEPLS,
+ EF_CREATEMLPL,
+ EF_USEM3UEXT,
+ EF_FOURCC,
+};
+
+// Copy Gorup fields
+enum
+{
+ CF_PATH = 0,
+ CF_USETITLEFMT,
+ CF_TITLEFMT,
+ CF_ADDTOMLDB,
+ CF_CALCULATERG,
+ CF_GAINMODE,
+};
+
+// Global group
+enum
+{
+ GF_SHOWINFO = 0,
+ GF_SHOWPARENT,
+ GF_ENQUEUEBYDEFAULT,
+};
+// Data View flags
+enum
+{
+ DVF_COLUMNLIST = 0,
+ DVF_ORDERBY,
+ DVF_ORDERASC,
+ DVF_VIEWMODE,
+ DVF_SHOWAUDIO,
+ DVF_SHOWVIDEO,
+ DVF_SHOWPLAYLIST,
+ DVF_SHOWUNKNOWN,
+ DVF_HIDEEXTENSION,
+ DVF_IGNOREHIDDEN,
+ DVF_LASTFOLDER,
+ DVF_DIVIDERPOS,
+};
+
+HRESULT Settings_ReadValue(INT categoryId, INT fieldId, VOID *pValue, INT cbSize);
+HRESULT Settings_GetDefault(INT categoryId, INT fieldId, VOID *pValue);
+HRESULT Settings_ReadString(INT categoryId, INT fieldId, LPTSTR pszBuffer, INT cchBufferMax);
+HRESULT Settings_GetInt(INT categoryId, INT fieldId, INT *pnVal);
+HRESULT Settings_GetBool(INT categoryId, INT fieldId, BOOL *pbVal);
+
+HRESULT Settings_SetWindowText(INT categoryId, INT fieldId, HWND hwnd);
+HRESULT Settings_SetWindowInt(INT categoryId, INT fieldId, HWND hwnd);
+HRESULT Settings_SetDlgItemText(INT categoryId, INT fieldId, HWND hdlg, INT nItemId);
+HRESULT Settings_SetDlgItemInt(INT categoryId, INT fieldId, HWND hdlg, INT nItemId);
+HRESULT Settings_SetCheckBox(INT categoryId, INT fieldId, HWND hdlg, INT nItemId);
+
+HRESULT Settings_SetString(INT categoryId, INT fieldId, LPCWSTR pszBuffer);
+HRESULT Settings_SetInt(INT categoryId, INT fieldId, INT nValue);
+HRESULT Settings_SetBool(INT categoryId, INT fieldId, BOOL bValue);
+HRESULT Settings_FromWindowText(INT categoryId, INT fieldId, HWND hwnd);
+HRESULT Settings_FromDlgItemText(INT categoryId, INT fieldId, HWND hdlg, INT nItemId);
+HRESULT Settings_FromCheckBox(INT categoryId, INT fieldId, HWND hdlg, INT nItemId);
+HRESULT Settings_BrowseForFolder(INT categoryId, INT fieldId, HWND hDlg, INT nEditId);
+
+HRESULT Settings_SetDirectoryCtrl(INT categoryId, INT fieldId, HWND hdlg, INT nItemId);
+HRESULT Settings_FromDirectoryCtrl(INT categoryId, INT fieldId, HWND hdlg, INT nItemId);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+#endif // NULLOSFT_MEDIALIBRARY_MLDISC_SETTINGS_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/spti.cpp b/Src/Plugins/Library/ml_disc/spti.cpp
new file mode 100644
index 00000000..8a34320a
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/spti.cpp
@@ -0,0 +1,239 @@
+#include "./spti.h"
+#include <ntddscsi.h>
+
+
+#define CDB6GENERIC_LENGTH 6
+#define CDB10GENERIC_LENGTH 10
+
+#define SETBITON 1
+#define SETBITOFF 0
+
+//
+// Mode Sense/Select page constants.
+//
+
+#define MODE_PAGE_ERROR_RECOVERY 0x01
+#define MODE_PAGE_DISCONNECT 0x02
+#define MODE_PAGE_FORMAT_DEVICE 0x03
+#define MODE_PAGE_RIGID_GEOMETRY 0x04
+#define MODE_PAGE_FLEXIBILE 0x05
+#define MODE_PAGE_VERIFY_ERROR 0x07
+#define MODE_PAGE_CACHING 0x08
+#define MODE_PAGE_PERIPHERAL 0x09
+#define MODE_PAGE_CONTROL 0x0A
+#define MODE_PAGE_MEDIUM_TYPES 0x0B
+#define MODE_PAGE_NOTCH_PARTITION 0x0C
+#define MODE_SENSE_RETURN_ALL 0x3f
+#define MODE_SENSE_CURRENT_VALUES 0x00
+#define MODE_SENSE_CHANGEABLE_VALUES 0x40
+#define MODE_SENSE_DEFAULT_VAULES 0x80
+#define MODE_SENSE_SAVED_VALUES 0xc0
+#define MODE_PAGE_DEVICE_CONFIG 0x10
+#define MODE_PAGE_MEDIUM_PARTITION 0x11
+#define MODE_PAGE_DATA_COMPRESS 0x0f
+#define MODE_PAGE_CAPABILITIES 0x2A
+
+//
+// SCSI CDB operation codes
+//
+
+#define SCSIOP_TEST_UNIT_READY 0x00
+#define SCSIOP_REZERO_UNIT 0x01
+#define SCSIOP_REWIND 0x01
+#define SCSIOP_REQUEST_BLOCK_ADDR 0x02
+#define SCSIOP_REQUEST_SENSE 0x03
+#define SCSIOP_FORMAT_UNIT 0x04
+#define SCSIOP_READ_BLOCK_LIMITS 0x05
+#define SCSIOP_REASSIGN_BLOCKS 0x07
+#define SCSIOP_READ6 0x08
+#define SCSIOP_RECEIVE 0x08
+#define SCSIOP_WRITE6 0x0A
+#define SCSIOP_PRINT 0x0A
+#define SCSIOP_SEND 0x0A
+#define SCSIOP_SEEK6 0x0B
+#define SCSIOP_TRACK_SELECT 0x0B
+#define SCSIOP_SLEW_PRINT 0x0B
+#define SCSIOP_SEEK_BLOCK 0x0C
+#define SCSIOP_PARTITION 0x0D
+#define SCSIOP_READ_REVERSE 0x0F
+#define SCSIOP_WRITE_FILEMARKS 0x10
+#define SCSIOP_FLUSH_BUFFER 0x10
+#define SCSIOP_SPACE 0x11
+#define SCSIOP_INQUIRY 0x12
+#define SCSIOP_VERIFY6 0x13
+#define SCSIOP_RECOVER_BUF_DATA 0x14
+#define SCSIOP_MODE_SELECT 0x15
+#define SCSIOP_RESERVE_UNIT 0x16
+#define SCSIOP_RELEASE_UNIT 0x17
+#define SCSIOP_COPY 0x18
+#define SCSIOP_ERASE 0x19
+#define SCSIOP_MODE_SENSE 0x1A
+#define SCSIOP_START_STOP_UNIT 0x1B
+#define SCSIOP_STOP_PRINT 0x1B
+#define SCSIOP_LOAD_UNLOAD 0x1B
+#define SCSIOP_RECEIVE_DIAGNOSTIC 0x1C
+#define SCSIOP_SEND_DIAGNOSTIC 0x1D
+#define SCSIOP_MEDIUM_REMOVAL 0x1E
+#define SCSIOP_READ_CAPACITY 0x25
+#define SCSIOP_READ 0x28
+#define SCSIOP_WRITE 0x2A
+#define SCSIOP_SEEK 0x2B
+#define SCSIOP_LOCATE 0x2B
+#define SCSIOP_WRITE_VERIFY 0x2E
+#define SCSIOP_VERIFY 0x2F
+#define SCSIOP_SEARCH_DATA_HIGH 0x30
+#define SCSIOP_SEARCH_DATA_EQUAL 0x31
+#define SCSIOP_SEARCH_DATA_LOW 0x32
+#define SCSIOP_SET_LIMITS 0x33
+#define SCSIOP_READ_POSITION 0x34
+#define SCSIOP_SYNCHRONIZE_CACHE 0x35
+#define SCSIOP_COMPARE 0x39
+#define SCSIOP_COPY_COMPARE 0x3A
+#define SCSIOP_WRITE_DATA_BUFF 0x3B
+#define SCSIOP_READ_DATA_BUFF 0x3C
+#define SCSIOP_CHANGE_DEFINITION 0x40
+#define SCSIOP_READ_SUB_CHANNEL 0x42
+#define SCSIOP_READ_TOC 0x43
+#define SCSIOP_READ_HEADER 0x44
+#define SCSIOP_PLAY_AUDIO 0x45
+#define SCSIOP_PLAY_AUDIO_MSF 0x47
+#define SCSIOP_PLAY_TRACK_INDEX 0x48
+#define SCSIOP_PLAY_TRACK_RELATIVE 0x49
+#define SCSIOP_PAUSE_RESUME 0x4B
+#define SCSIOP_LOG_SELECT 0x4C
+#define SCSIOP_LOG_SENSE 0x4D
+#define SCSIOP_READ_DISC_INFORMATION 0x51
+
+typedef struct _SCSI_PASS_THROUGH_WITH_BUFFERS
+{
+ SCSI_PASS_THROUGH spt;
+ DWORD filler;
+ UCHAR ucSenseBuf[24];
+ UCHAR ucDataBuf[256];
+}SCSI_PASS_THROUGH_WITH_BUFFERS;
+
+#pragma pack(1)
+
+typedef struct _CDB_START_STOP_UNIT
+{
+ UCHAR OperationCode; // 0x1B - SCSIOP_START_STOP_UNIT
+ UCHAR Immediate : 1;
+ UCHAR Reserved1 : 4;
+ UCHAR Lun : 3;
+ UCHAR Reserved2[2];
+ UCHAR Start : 1;
+ UCHAR LoadEject : 1;
+ UCHAR Reserved3 : 2;
+ UCHAR PowerCondition : 4;
+ UCHAR Control;
+} CDB_START_STOP_UNIT;
+
+
+typedef struct _READ_DISC_INFORMATION
+{
+ UCHAR OperationCode; // 0x51 - SCSIOP_READ_DISC_INFORMATION
+ UCHAR Reserved1 : 5;
+ UCHAR Lun : 3;
+ UCHAR Reserved2[5];
+ UCHAR AllocationLength[2];
+ UCHAR Control;
+} READ_DISC_INFORMATION;
+
+#pragma pack()
+
+
+BOOL SPTI_TestUnitReady(HANDLE hDevice, BYTE *pbSC, BYTE *pbASC, BYTE *pbASCQ, INT timeOutSec)
+{
+ INT length;
+ DWORD returned;
+ SCSI_PASS_THROUGH_WITH_BUFFERS sptwb;
+
+ if (INVALID_HANDLE_VALUE == hDevice) return FALSE;
+
+ ZeroMemory(&sptwb, sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS));
+ sptwb.spt.Length = sizeof(SCSI_PASS_THROUGH);
+ sptwb.spt.CdbLength = CDB6GENERIC_LENGTH;
+ sptwb.spt.SenseInfoLength = 24;
+ sptwb.spt.DataIn = SCSI_IOCTL_DATA_IN;
+ sptwb.spt.DataTransferLength = 0;
+ sptwb.spt.TimeOutValue = timeOutSec;
+ sptwb.spt.DataBufferOffset = ((DWORD)(DWORD_PTR)&sptwb.ucDataBuf) - ((DWORD)(DWORD_PTR)&sptwb);
+ sptwb.spt.SenseInfoOffset = ((DWORD)(DWORD_PTR)&sptwb.ucSenseBuf) - ((DWORD)(DWORD_PTR)&sptwb);
+ sptwb.spt.Cdb[0] = SCSIOP_TEST_UNIT_READY;
+ length = ((DWORD)(DWORD_PTR)&sptwb.ucDataBuf) - ((DWORD)(DWORD_PTR)&sptwb);
+ DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH, &sptwb, sizeof(SCSI_PASS_THROUGH), &sptwb, length, &returned, FALSE);
+
+ if (pbSC) *pbSC = sptwb.ucSenseBuf[2];
+ if (pbASC) *pbASC = sptwb.ucSenseBuf[12];
+ if (pbASCQ) *pbASCQ = sptwb.ucSenseBuf[13];
+
+ return TRUE;
+}
+BOOL SPTI_StartStopUnit(HANDLE hDevice, BOOL bImmediate, BOOL bLoadEject, BOOL bStart, INT timeOutSec, SENSEINFO *pSense)
+{
+ INT length;
+ BOOL status;
+ DWORD returned;
+ CDB_START_STOP_UNIT cmd;
+ SCSI_PASS_THROUGH_WITH_BUFFERS sptwb;
+
+ UNREFERENCED_PARAMETER(pSense);
+
+ if (INVALID_HANDLE_VALUE == hDevice) return FALSE;
+
+ ZeroMemory(&sptwb, sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS));
+ ZeroMemory(&cmd, sizeof(CDB_START_STOP_UNIT));
+
+ sptwb.spt.Length = sizeof(SCSI_PASS_THROUGH);
+ sptwb.spt.CdbLength = CDB6GENERIC_LENGTH;
+ sptwb.spt.SenseInfoLength = 24;
+ sptwb.spt.DataIn = SCSI_IOCTL_DATA_IN;
+ sptwb.spt.DataTransferLength = 0;
+ sptwb.spt.TimeOutValue = timeOutSec;
+ sptwb.spt.DataBufferOffset = ((DWORD)(DWORD_PTR)&sptwb.ucDataBuf) - ((DWORD)(DWORD_PTR)&sptwb);
+ sptwb.spt.SenseInfoOffset = ((DWORD)(DWORD_PTR)&sptwb.ucSenseBuf) - ((DWORD)(DWORD_PTR)&sptwb);
+ cmd.OperationCode = SCSIOP_START_STOP_UNIT;
+ cmd.Immediate = (bImmediate) ? 1 : 0;
+ cmd.LoadEject= (bLoadEject) ? 1 : 0;
+ cmd.Start = (bStart) ? 1 : 0;
+ CopyMemory(sptwb.spt.Cdb, &cmd, sizeof(CDB_START_STOP_UNIT));
+
+ length = ((DWORD)(DWORD_PTR)&sptwb.ucDataBuf) - ((DWORD)(DWORD_PTR)&sptwb);
+ status = DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH, &sptwb, sizeof(SCSI_PASS_THROUGH), &sptwb, length, &returned, FALSE);
+
+ return status;
+}
+BOOL SPTI_GetCapabilities(HANDLE hDevice, DWORD *pCap)
+{
+ INT length = 0;
+ BOOL status;
+ DWORD returned = 0;
+ SCSI_PASS_THROUGH_WITH_BUFFERS sptwb;
+
+ if (INVALID_HANDLE_VALUE == hDevice || !pCap) return FALSE;
+
+ *pCap = 0;
+
+ ZeroMemory(&sptwb, sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS));
+ sptwb.spt.Length = sizeof(SCSI_PASS_THROUGH);
+ sptwb.spt.CdbLength = CDB6GENERIC_LENGTH;
+ sptwb.spt.SenseInfoLength = 24;
+ sptwb.spt.DataIn = SCSI_IOCTL_DATA_IN;
+ sptwb.spt.DataTransferLength = 192;
+ sptwb.spt.TimeOutValue = 10; //2 sec
+ sptwb.spt.DataBufferOffset = ((DWORD)(DWORD_PTR)&sptwb.ucDataBuf) - ((DWORD)(DWORD_PTR)&sptwb);
+ sptwb.spt.SenseInfoOffset = ((DWORD)(DWORD_PTR)&sptwb.ucSenseBuf) - ((DWORD)(DWORD_PTR)&sptwb);
+ sptwb.spt.Cdb[0] = SCSIOP_MODE_SENSE;
+ sptwb.spt.Cdb[1] = 0x08; // target shall not return any block descriptors
+ sptwb.spt.Cdb[2] = MODE_PAGE_CAPABILITIES;
+ sptwb.spt.Cdb[4] = 192;
+
+ length = ((DWORD)(DWORD_PTR)&sptwb.ucDataBuf) - ((DWORD)(DWORD_PTR)&sptwb) + sptwb.spt.DataTransferLength;;
+
+ status = DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH, &sptwb, sizeof(SCSI_PASS_THROUGH), &sptwb, length, &returned, FALSE);
+ if (!status) return FALSE;
+
+ *pCap = *((DWORD*)&sptwb.ucDataBuf[6]) ;
+
+ return TRUE;
+}
diff --git a/Src/Plugins/Library/ml_disc/spti.h b/Src/Plugins/Library/ml_disc/spti.h
new file mode 100644
index 00000000..8c14e8e1
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/spti.h
@@ -0,0 +1,35 @@
+#ifndef NULLOSFT_MLDISC_STPI_HEADER
+#define NULLOSFT_MLDISC_STPI_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <windows.h>
+
+
+// 02:04:01 - LOGICAL UNIT IS IN PROCESS OF BECOMING READY
+// 02:3A:00 - MEDIUM NOT PRESENT
+// 02:3A:01 - MEDIUM NOT PRESENT - TRAY CLOSED
+// 02:3A:02 - MEDIUM NOT PRESENT - TRAY OPEN
+// 06:28:00 - MEDIUM NOT PRESENT - NOT READY TO READY CHANGE, MEDIUM MAY HAVE CHANGED
+
+typedef struct _SENSEINFO
+{
+ BYTE bSense;
+ BYTE bASC;
+ BYTE bASCQ;
+} SENSEINFO;
+BOOL SPTI_TestUnitReady(HANDLE hDevice, BYTE *pbSC, BYTE *pbASC, BYTE *pbASCQ, INT timeOutSec);
+BOOL SPTI_StartStopUnit(HANDLE hDevice, BOOL bImmediate, BOOL bLoadEject, BOOL bStart, INT timeOutSec, SENSEINFO *pSense);
+BOOL SPTI_GetCapabilities(HANDLE hDevice, DWORD *pCap);
+
+// links to read:
+// http://www.t10.org/ftp/t10/drafts/mmc3/mmc3r10g.pdf
+// http://www.reactos.org/generated/doxygen/da/db8/scsi_8h-source.html
+// http://www.vbarchiv.net/workshop/workshop78s4.html
+// http://files.codes-sources.com/fichier.aspx?id=26141&f=Graver.h
+// http://files.codes-sources.com/fichier.aspx?id=26141&f=Graver.cpp
+// http://www.hackingtruths.org/files/cd_cracking/SRC/etc/DDK.SPTI/spti.c
+
+#endif //NULLOSFT_MLDISC_STPI_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/version.rc2 b/Src/Plugins/Library/ml_disc/version.rc2
new file mode 100644
index 00000000..efe64048
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../../../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 2,0,0,1
+ 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 Media Library Plug-in"
+ VALUE "FileVersion", "2,0,0,1"
+ VALUE "InternalName", "Nullsoft Rip & Burn"
+ VALUE "LegalCopyright", "Copyright © 2003-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "ml_disc.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/Library/ml_disc/view_cdrom.cpp b/Src/Plugins/Library/ml_disc/view_cdrom.cpp
new file mode 100644
index 00000000..a980aef0
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/view_cdrom.cpp
@@ -0,0 +1,1014 @@
+#include <shlwapi.h>
+
+#include "main.h"
+#include <windowsx.h>
+#include "resource.h"
+#include "../nu/DialogSkinner.h"
+#include "../nu/ChildSizer.h"
+#include "../winamp/wa_ipc.h"
+#include "../Winamp/strutil.h"
+#include "../nu/AutoChar.h"
+#include "../nu/AutoWide.h"
+#include "../nu/listview.h"
+#include <strsafe.h>
+
+#ifndef LVS_EX_DOUBLEBUFFER
+#define LVS_EX_DOUBLEBUFFER 0x00010000
+#endif
+
+static INT_PTR WINAPI DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+#define TIMER_NOTIFYINFO_ID 1985
+#define TIMER_NOTIFYINFO_DELAY 200
+
+HWND CreateCDViewWindow(HWND hwndParent, DM_NOTIFY_PARAM *phdr)
+{
+ return WASABI_API_CREATEDIALOGPARAMW(IDD_VIEW_CDROM, hwndParent, DlgProc, (LPARAM)phdr);
+}
+
+void TAG_FMT(void *f, void *ff, void *p, char *out, int out_len)
+{
+ waFormatTitle fmt;
+ fmt.out = out;
+ fmt.out_len = out_len;
+ fmt.p = p;
+ fmt.spec = 0;
+ *(void **)&fmt.TAGFUNC = f;
+ *(void **)&fmt.TAGFREEFUNC = ff;
+ *out = 0;
+ SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&fmt, IPC_FORMAT_TITLE);
+}
+
+
+static wchar_t m_artist[128], m_album[128], m_genre[128], m_year[32];
+static int m_start_immediate_extract = 0;
+static int m_atracks, m_dtracks;
+
+
+void CopyComment(wchar_t *&dest, wchar_t *comments)
+{
+ if (comments)
+ {
+ int numCarriageReturns = 0;
+ wchar_t *commentCursor = comments;
+ while (commentCursor && *commentCursor)
+ {
+ if (*commentCursor == '\r')
+ {
+ commentCursor++;
+ if (*commentCursor == '\n')
+ commentCursor++;
+ }
+ if (*commentCursor == '\n')
+ numCarriageReturns++;
+
+ commentCursor = CharNext(commentCursor);
+ }
+ size_t size = commentCursor - comments;
+ dest = (wchar_t *)calloc((size + numCarriageReturns + 1), sizeof(wchar_t));
+ wchar_t *destCursor = dest;
+ commentCursor = comments;
+ while (commentCursor && *commentCursor)
+ {
+ if (*commentCursor == '\r')
+ {
+ *destCursor++ = *commentCursor++;
+ if (*commentCursor == '\n')
+ *destCursor++ = *commentCursor++;
+ }
+ if (*commentCursor == '\n')
+ *destCursor++ = '\r';
+
+ wchar_t *next = CharNextW(commentCursor);
+ while (commentCursor != next)
+ {
+ *destCursor++ = *commentCursor++;
+ }
+ }
+ *destCursor = 0;
+ }
+ else
+ dest = 0;
+}
+
+static void extractFiles(HWND hwndDlg, CHAR cLetter, int all)
+{
+ HWND hwndList;
+ hwndList = GetDlgItem(hwndDlg, IDC_LIST2);
+
+ char cMode;
+ wchar_t title[32] = {0}, buf[512] = {0};
+ INT msgTextId, l, i, cnt = 0;
+ cdrip_params *p;
+ wchar_t info[65536] = {0};
+
+ LVITEMW lvitem = {0};
+
+ cMode = DriveManager_GetDriveMode(cLetter);
+
+ switch (cMode)
+ {
+ case DM_MODE_BURNING: msgTextId = IDS_ERROR_CD_RIP_IN_PROGRESS; break;
+ case DM_MODE_RIPPING: msgTextId = IDS_ERROR_CD_BURN_IN_PROGRESS; break;
+ default: msgTextId = 0; break;
+ }
+ if (msgTextId)
+ {
+ MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(msgTextId), WASABI_API_LNGSTRINGW_BUF(IDS_CD_RIP,title,32), 0);
+ return ;
+ }
+
+ if (m_dtracks && !m_atracks)
+ {
+ MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(IDS_ERROR_CANNOT_EXTRACT_DATA_CDS),
+ WASABI_API_LNGSTRINGW_BUF(IDS_CD_RIP,title,32), 0);
+ return ;
+ }
+
+ l = (hwndList) ? ListView_GetItemCount(hwndList) : 0;
+ if (l)
+ {
+ p = (cdrip_params *)calloc(1, sizeof(cdrip_params));
+ if (!p) return;
+ p->ntracks = l;
+ p->tracks = (wchar_t **)calloc(sizeof(wchar_t*), p->ntracks);
+ p->trackArtists = (wchar_t **)calloc(sizeof(wchar_t*), p->ntracks);
+ p->composers = (wchar_t **)calloc(sizeof(wchar_t*), p->ntracks);
+ p->gracenoteFileIDs = (wchar_t **)calloc(sizeof(wchar_t*), p->ntracks);
+ p->gracenoteExtData = (wchar_t **)calloc(sizeof(wchar_t*), p->ntracks);
+ p->conductors = (wchar_t **)calloc(sizeof(wchar_t*), p->ntracks);
+ p->lengths = (int *)calloc(sizeof(int), p->ntracks);
+ }
+ else p = NULL;
+
+ lvitem.mask = LVIF_TEXT;
+ lvitem.iSubItem = 3;
+ lvitem.cchTextMax = sizeof(buf)/sizeof(char);
+
+ for (i = 0;i < l;i++)
+ {
+ if (all || (LVIS_SELECTED & ListView_GetItemState(hwndList, i, LVIS_SELECTED)))
+ {
+ wchar_t cdFilename[MAX_PATH] = {0};
+ StringCchPrintfW(cdFilename, MAX_PATH, L"cda://%c,%d.cda", cLetter, i + 1);
+ //check if track is Data track
+ {
+ wchar_t buf2[512] = L"";
+ getFileInfoW(cdFilename, L"tracktype", buf2, sizeof(buf2)/sizeof(*buf2));
+ if (lstrcmpiW(buf2, L"audio")) continue; //skip it
+ }
+
+ lvitem.iItem = i;
+ lvitem.pszText = buf;
+ SendMessageW(hwndList, LVM_GETITEMTEXTW, i, (LPARAM)&lvitem);
+ int len = _wtoi(lvitem.pszText) * 60 + _wtoi(wcsstr(lvitem.pszText, L":") + 1); //such hackish :)
+
+ p->total_length_bytes += len * 44100 * 4;
+
+ getFileInfoW(cdFilename, L"title", info, sizeof(info)/sizeof(wchar_t));
+ p->tracks[i] = _wcsdup(info);
+
+ getFileInfoW(cdFilename, L"artist", info, sizeof(info)/sizeof(wchar_t));
+ p->trackArtists[i] = _wcsdup(info);
+
+ getFileInfoW(cdFilename, L"composer", info, sizeof(info)/sizeof(wchar_t));
+ p->composers[i] = _wcsdup(info);
+
+ getFileInfoW(cdFilename, L"conductor", info, sizeof(info)/sizeof(wchar_t));
+ p->conductors[i] = _wcsdup(info);
+
+ getFileInfoW(cdFilename, L"GracenoteFileID", info, sizeof(info)/sizeof(wchar_t));
+ p->gracenoteFileIDs[i] = _wcsdup(info);
+
+ getFileInfoW(cdFilename, L"GracenoteExtData", info, sizeof(info)/sizeof(wchar_t));
+ p->gracenoteExtData[i] = _wcsdup(info);
+
+ p->lengths[i] = len;
+ cnt++;
+ }
+ }
+ if (!cnt)
+ {
+ if (p)
+ {
+ for (int i = 0 ; i < l; i ++) free(p->tracks[i]);
+ free(p->tracks);
+ free(p->trackArtists);
+ free(p->composers);
+ free(p->gracenoteFileIDs);
+ free(p->gracenoteExtData);
+ free(p->conductors);
+ free(p->lengths);
+ free(p);
+ }
+ MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(IDS_NO_TRACKS_TO_RIP),
+ WASABI_API_LNGSTRINGW_BUF(IDS_CD_RIP,title,32), MB_OK);
+ return ;
+ }
+
+ p->filenames = (wchar_t **)calloc(sizeof(wchar_t *), p->ntracks); // allocate for cdrip to use :)
+ p->tempFilenames = (wchar_t **)calloc(sizeof(wchar_t *), p->ntracks); // allocate for cdrip to use :)
+ p->artist = _wcsdup(m_artist);
+ p->album = _wcsdup(m_album);
+ p->genre = _wcsdup(m_genre);
+ p->year = _wcsdup(m_year);
+
+ wchar_t name[] = L"cda://X.cda";
+ name[6] = cLetter;
+
+ info[0] = 0;
+ getFileInfoW(name, L"publisher", info, sizeof(info)/sizeof(wchar_t));
+ p->publisher = _wcsdup(info);
+
+ info[0] = 0;
+ getFileInfoW(name, L"comment", info, sizeof(info)/sizeof(wchar_t));
+ CopyComment(p->comment, info);
+
+ info[0] = 0;
+ getFileInfoW(name, L"disc", info, sizeof(info)/sizeof(wchar_t));
+ p->disc = _wcsdup(info);
+
+ p->drive_letter = cLetter;
+
+ cdrip_extractFiles(p); // will free p when done with it
+}
+
+static void playFiles(HWND hwndDlg, CHAR cLetter, int enqueue, int all)
+{
+ HWND hwndList;
+ hwndList = GetDlgItem(hwndDlg, IDC_LIST2);
+ CHAR cMode;
+ INT msgTextId;
+
+ cMode = DriveManager_GetDriveMode(cLetter);
+
+ switch (cMode)
+ {
+ case DM_MODE_BURNING: msgTextId = IDS_ERROR_CD_RIP_IN_PROGRESS; break;
+ case DM_MODE_RIPPING: msgTextId = IDS_ERROR_CD_BURN_IN_PROGRESS; break;
+ default: msgTextId = 0; break;
+ }
+ if (msgTextId)
+ {
+ wchar_t title[64] = {0};
+ MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(msgTextId),
+ WASABI_API_LNGSTRINGW_BUF(IDS_CD_PLAYBACK_ERROR, title, 64), 0);
+ return ;
+ }
+
+ int cnt = 0;
+ int l = (hwndList) ? ListView_GetItemCount(hwndList) : 0;
+ if (enqueue && all == 1024) all = 0;
+
+ int firstsel = -1;
+ char buf[64] = {0}, titlebuf[2048] = {0};
+ enqueueFileWithMetaStruct s;
+ LVITEMA lvitem = {0};
+
+ lvitem.cchTextMax = sizeof(buf)/sizeof(char);
+
+ for (int i = 0;i < l;i++)
+ {
+ if (all == 1024 && firstsel < 0 && (LVIS_SELECTED & ListView_GetItemState(hwndList, i, LVIS_SELECTED))) firstsel = i;
+
+ if (all || (LVIS_SELECTED & ListView_GetItemState(hwndList, i, LVIS_SELECTED)))
+ {
+ lvitem.iItem = i;
+
+ lvitem.mask = LVIF_PARAM;
+ lvitem.iSubItem = 0;
+ SendMessageW(hwndList, LVM_GETITEMA, 0, (LPARAM)&lvitem);
+ int a = (INT)(INT_PTR)lvitem.lParam;
+ if (a > 0)
+ {
+ if (!cnt)
+ {
+ if (!enqueue) SendMessageW(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_DELETE);
+ cnt++;
+ }
+
+ lvitem.mask = LVIF_TEXT;
+ lvitem.iSubItem = 3;
+ lvitem.pszText = buf;
+ SendMessageW(hwndList, LVM_GETITEMTEXTA, i, (LPARAM)&lvitem);
+
+ s.length = atoi(lvitem.pszText) * 60 + atoi(strstr(lvitem.pszText, ":") + 1); //such hackish :)
+
+ StringCchPrintfA(buf, 64, "cda://%c,%d.cda", cLetter, i + 1);
+ TAG_FMT(0, 0, buf, titlebuf, sizeof(titlebuf)/sizeof(*titlebuf));
+ s.filename = buf;
+ s.title = titlebuf;
+ s.ext = NULL;
+ SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_PLAYFILE);
+ }
+ }
+ }
+ if (cnt && !enqueue)
+ {
+ if (firstsel >= 0)
+ {
+ SendMessage(plugin.hwndWinampParent, WM_WA_IPC, firstsel, IPC_SETPLAYLISTPOS);
+ SendMessage(plugin.hwndWinampParent, WM_COMMAND, 40047, 0); // stop button, literally
+ SendMessage(plugin.hwndWinampParent, WM_COMMAND, 40045, 0); // play button, literally
+ }
+ else SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_STARTPLAY);
+ }
+}
+
+void saveCDToItemRecordList(CHAR cLetter, itemRecordList *obj, char *albumname)
+{
+ char fname[64] = {0}, buf2[64] = {0};
+ StringCchPrintfA(fname, 64, "cda://%c.cda", cLetter);
+
+ getFileInfo(fname, "<begin>", buf2, sizeof(buf2)/sizeof(char));
+ getFileInfo(fname, "ntracks", buf2, sizeof(buf2)/sizeof(char));
+
+ int ntracks = atoi(buf2);
+
+ if (ntracks > 0 && ntracks < 256)
+ {
+ obj->Items = 0; obj->Alloc = 0; obj->Size = 0;
+ allocRecordList(obj, ntracks, 0);
+
+ int x;
+ for (x = 0; x < ntracks; x ++)
+ {
+ StringCchPrintfA(fname, 64, "cda://%c,%d.cda", cLetter, x + 1);
+ getFileInfo(fname, "tracktype", buf2, sizeof(buf2)/sizeof(char));
+ if (!lstrcmpiA(buf2, "audio"))
+ {
+ int len = -1;
+ char titlebuf[FILETITLE_SIZE] = {0};
+ mediaLibrary.GetFileInfo(fname, titlebuf, FILETITLE_SIZE, &len);
+ itemRecord *pRec = &obj->Items[obj->Size];
+ ZeroMemory(pRec, sizeof(itemRecord));
+ if (titlebuf) pRec->title = _strdup(titlebuf);
+ pRec->length = len;
+ if (fname) pRec->filename = _strdup(fname);
+ obj->Size++;
+ }
+ }
+ }
+ getFileInfo(fname, "<end>", buf2, sizeof(buf2)/sizeof(char));
+}
+
+
+
+
+static ChildWndResizeItem cdromwnd_rlist[] =
+{
+ {IDC_LIST2, 0x0011},
+ {IDC_CDINFO, 0x0000},
+ {IDC_CDINFO2, 0x0000},
+ {IDC_BUTTON_PLAY, 0x0101},
+ {IDC_BUTTON_ENQUEUE, 0x0101},
+ {IDC_BUTTON_EXTRACT, 0x0101},
+ {IDC_BUTTON_EJECT, 0x0101},
+ {IDC_BTN_SHOWINFO, 0x1111},
+};
+
+typedef struct _VIEWCOLUMN
+{
+ INT stringId;
+ LPTSTR pszConfig;
+ INT defaultWidth;
+} VIEWCOLUMN;
+
+static VIEWCOLUMN viewColumns[] =
+{
+ { IDS_TRACK_NUMBER, TEXT("col_track"), 60 },
+ { IDS_ARTIST, TEXT("col_artist"), 150 },
+ { IDS_TITLE, TEXT("col_title"), 200 },
+ { IDS_LENGTH, TEXT("col_len"), 80 },
+};
+
+static char m_cdrom;
+
+typedef struct _APCPARAM
+{
+ HWND hwndDlg;
+ CHAR cLetter;
+ INT_PTR user;
+}
+APCPARAM;
+
+static void CALLBACK APC_GetCracenoteInfo(ULONG_PTR param);
+
+static void GetGracenoteInfo(HWND hwndDlg, CHAR cLetter, HANDLE hThread = NULL)
+{
+ HWND hwndList;
+ int l, x;
+ LVITEMW lvitem = {0};
+ wchar_t buf[32] = {0}, titlebuf[256] = {0};
+
+ if (hThread)
+ {
+ APCPARAM *pParam = (APCPARAM*)calloc(1, sizeof(APCPARAM));
+ if (pParam)
+ {
+ pParam->cLetter = cLetter;
+ pParam->hwndDlg = hwndDlg;
+ if (!QueueUserAPC(APC_GetCracenoteInfo, hThread, (ULONG_PTR)pParam)) free(pParam);
+ }
+ return;
+ }
+
+ hwndList = GetDlgItem(hwndDlg, IDC_LIST2);
+ if (!hwndList) return;
+
+ l = (INT)SendMessageW(hwndList, LVM_GETITEMCOUNT, 0,0);
+
+ // first, let's try to get artist/album info
+ {
+ StringCchPrintfW(buf, 32, L"cda://%c.cda", cLetter);
+ wchar_t artistbuf[256] = {0}, albumbuf[256] = {0}, yearbuf[256] = {0}, genrebuf[256] = {0};
+ getFileInfoW(buf, L"albumartist", artistbuf, sizeof(artistbuf) / sizeof(artistbuf[0]));
+ getFileInfoW(buf, L"album", albumbuf, sizeof(albumbuf) / sizeof(albumbuf[0]));
+ getFileInfoW(buf, L"genre", genrebuf, sizeof(genrebuf) / sizeof(genrebuf[0]));
+ getFileInfoW(buf, L"year", yearbuf, sizeof(yearbuf) / sizeof(yearbuf[0]));
+
+ wchar_t newbuf[1024] = {0};
+ lstrcpynW(m_artist, artistbuf, sizeof(m_artist)/sizeof(*m_artist));
+ lstrcpynW(m_album, albumbuf, sizeof(m_album)/sizeof(*m_album));
+ lstrcpynW(m_year, yearbuf, sizeof(m_year)/sizeof(*m_year));
+ lstrcpynW(m_genre, genrebuf, sizeof(m_genre)/sizeof(*m_genre));
+ StringCchPrintfW(newbuf, 1024, WASABI_API_LNGSTRINGW(IDS_ML_VIEW_ARTIST_ALBUM), artistbuf, albumbuf);
+ SetDlgItemText(hwndDlg, IDC_CDINFO, newbuf);
+ StringCchPrintfW(newbuf, 1024, WASABI_API_LNGSTRINGW(IDS_ML_VIEW_YEAR_GENRE), yearbuf, genrebuf);
+ SetDlgItemText(hwndDlg, IDC_CDINFO2, newbuf);
+ }
+
+ for (x = 0; x < l; x ++)
+ {
+ lvitem.iItem = x;
+ lvitem.mask = LVIF_PARAM;
+ lvitem.iSubItem = 0;
+ SendMessageW(hwndList, LVM_GETITEMW, 0, (LPARAM)&lvitem);
+
+ int wt = (INT)(INT_PTR)lvitem.lParam;
+
+ if (wt > 0)
+ {
+ StringCchPrintfW(buf, 32, L"cda://%c,%d.cda", cLetter, x + 1);
+
+ lvitem.mask = LVIF_TEXT;
+
+ titlebuf[0] = 0;
+ getFileInfoW(buf, L"title", titlebuf, sizeof(titlebuf) / sizeof(titlebuf[0]));
+ if (titlebuf[0])
+ {
+ lvitem.iSubItem = 2;
+ lvitem.pszText = titlebuf;
+ SendMessageW(hwndList, LVM_SETITEMW, 0, (LPARAM)&lvitem);
+ }
+
+ titlebuf[0] = 0;
+ getFileInfoW(buf, L"artist", titlebuf, sizeof(titlebuf) / sizeof(titlebuf[0]));
+ if (titlebuf[0])
+ {
+ lvitem.iSubItem = 1;
+ lvitem.pszText = titlebuf;
+ SendMessageW(hwndList, LVM_SETITEMW, 0, (LPARAM)&lvitem);
+ }
+ }
+ }
+
+ if (l && hwndList)
+ UpdateWindow(hwndList);
+
+ if ( Plugin_IsExtractScheduled( cLetter ) )
+ extractFiles( hwndDlg, cLetter, 1 );
+}
+
+static void UpdateCDView(HWND hwndDlg, DM_NOTIFY_PARAM *phdr)
+{
+ HWND hwndList;
+
+ m_cdrom = phdr->cLetter;
+ m_atracks = m_dtracks = 0;
+
+ hwndList = GetDlgItem(hwndDlg, IDC_LIST2);
+ if (!hwndList) return;
+
+ ListView_DeleteAllItems(hwndList);
+
+ SetDlgItemText(hwndDlg, IDC_CDINFO2, L"");
+
+ SendMessageW(hwndList, WM_SETREDRAW, FALSE, 0L);
+
+ if (DMOP_MCIINFO == phdr->opCode)
+ {
+ DM_MCI_PARAM *pmci;
+ LVITEMW lvitem = {0};
+ wchar_t buffer[512] = {0};
+ INT strid, param;
+ pmci = (DM_MCI_PARAM*)phdr;
+
+ for (int i = 0; i < pmci->nTracks; i++)
+ {
+ INT time = (0x7FFFFFFF & pmci->pTracks[i])/1000;
+
+ if (0x80000000 & pmci->pTracks[i])
+ {
+ param = i + 1; strid = IDS_AUDIO_TRACK; m_atracks++;
+ }
+ else
+ {
+ param = -1; strid = IDS_DATA_TRACK; m_dtracks++;
+ }
+
+ StringCchPrintfW(buffer, sizeof(buffer)/sizeof(wchar_t), L"%d", i + 1);
+
+ lvitem.mask = LVIF_TEXT | LVIF_PARAM;
+ lvitem.iItem = i;
+ lvitem.iSubItem = 0;
+ lvitem.pszText = buffer;
+ lvitem.lParam = param;
+ INT index = (INT)SendMessageW(hwndList, LVM_INSERTITEMW, 0, (LPARAM)&lvitem);
+
+ if (-1 != index)
+ {
+ lvitem.iItem = index;
+ lvitem.mask = LVIF_TEXT;
+
+ lvitem.iSubItem = 2;
+ lvitem.pszText = WASABI_API_LNGSTRINGW(strid);
+ SendMessageW(hwndList, LVM_SETITEMW, 0, (LPARAM)&lvitem);
+
+ if (time < 0) StringCchCopyW(buffer, sizeof(buffer)/sizeof(wchar_t), L"???");
+ else StringCchPrintfW(buffer, sizeof(buffer)/sizeof(wchar_t), L"%d:%02d", time / 60, time % 60);
+
+ lvitem.iSubItem = 3;
+ lvitem.pszText = buffer;
+ SendMessageW(hwndList, LVM_SETITEMW, 0, (LPARAM)&lvitem);
+ }
+ }
+
+ SetDlgItemText(hwndDlg, IDC_CDINFO, WASABI_API_LNGSTRINGW((m_atracks) ? IDS_CD_AUDIO : ((m_dtracks) ? IDS_DATA_CD : IDS_NO_CD)));
+
+ if (m_atracks) GetGracenoteInfo(hwndDlg, phdr->cLetter, GetCurrentThread());
+ }
+ else SetDlgItemText(hwndDlg, IDC_CDINFO, WASABI_API_LNGSTRINGW(IDS_NO_CD));
+
+ SendMessageW(hwndList, WM_SETREDRAW, TRUE, 0L);
+ UpdateWindow(hwndList);
+}
+
+static LRESULT editCDInfo(HWND hwndDlg, CHAR cLetter, int trackNum)
+{
+ wchar_t name[MAX_PATH] = {0};
+ if (trackNum)
+ StringCchPrintfW(name, MAX_PATH, L"cda://%c,%d", cLetter, trackNum);
+ else
+ StringCchPrintfW(name, MAX_PATH, L"cda://%c.cda", cLetter);
+ infoBoxParamW p;
+ p.filename = name;
+ p.parent = hwndDlg;
+ return SendMessageW(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&p, IPC_INFOBOXW);
+}
+
+static void NotifyInfoWindow(HWND hwnd, CHAR cLetter, INT nTrack, BOOL bForceRefresh)
+{
+ HWND hwndParent;
+ hwndParent = GetParent(hwnd);
+
+ if (hwndParent)
+ {
+ wchar_t szFileName[MAX_PATH], *p;
+ if (nTrack && S_OK == StringCchPrintfW(szFileName, sizeof(szFileName)/sizeof(wchar_t), L"cda://%c,%d.cda", cLetter, nTrack)) p = szFileName;
+ else p = L"";
+ SendMessageW(hwndParent, WM_SHOWFILEINFO, (WPARAM)((bForceRefresh) ? WISF_FORCE : WISF_NORMAL), (LPARAM)p);
+ }
+}
+
+static BOOL Window_OnInitDialog(HWND hwndDlg, HWND hwndFocus, LPARAM lParam)
+{
+ HWND hwndList;
+
+ if (!lParam)
+ {
+ DestroyWindow(hwndDlg);
+ return 0;
+ }
+
+ SendMessageW(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(IDC_BTN_SHOWINFO, BN_EX_GETTEXT), (LPARAM)GetDlgItem(hwndDlg, IDC_BTN_SHOWINFO));
+
+ childSizer.Init(hwndDlg, cdromwnd_rlist, sizeof(cdromwnd_rlist) / sizeof(cdromwnd_rlist[0]));
+
+ hwndList = GetDlgItem(hwndDlg, IDC_LIST2);
+
+ if (hwndList)
+ {
+ MLSKINWINDOW sw;
+ LVCOLUMNW column;
+
+ sw.hwndToSkin = hwndList;
+ sw.skinType = SKINNEDWND_TYPE_LISTVIEW;
+ sw.style = SWLVS_FULLROWSELECT | SWLVS_DOUBLEBUFFER | SWLVS_ALTERNATEITEMS | SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS;
+ MLSkinWindow(plugin.hwndLibraryParent, &sw);
+
+ column.mask = LVCF_WIDTH | LVCF_TEXT;
+ for (int i = 0; i < sizeof(viewColumns)/sizeof(VIEWCOLUMN); i++)
+ {
+ column.cx = g_view_metaconf->ReadInt(viewColumns[i].pszConfig, viewColumns[i].defaultWidth);
+ column.pszText = WASABI_API_LNGSTRINGW(viewColumns[i].stringId);
+ SendMessageW(hwndList, LVM_INSERTCOLUMNW, (WPARAM)0xEFFF, (LPARAM)&column);
+ }
+ }
+
+
+ SetDlgItemText(hwndDlg, IDC_CDINFO, L"");
+ SetDlgItemText(hwndDlg, IDC_CDINFO2, L"");
+
+ NotifyInfoWindow(hwndDlg, ((DM_NOTIFY_PARAM*)lParam)->cLetter, NULL, TRUE); // ignore cache
+
+ UpdateCDView(hwndDlg, (DM_NOTIFY_PARAM*)lParam);
+
+ return FALSE;
+}
+static void Window_OnDestroy(HWND hwndDlg)
+{
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_LIST2);
+
+ if (hwndList)
+ {
+ for (int i = 0; i < sizeof(viewColumns)/sizeof(VIEWCOLUMN); i++)
+ {
+ g_view_metaconf->WriteInt(viewColumns[i].pszConfig,
+ (INT)SendMessageW(hwndList, LVM_GETCOLUMNWIDTH, i, 0L));
+ }
+ }
+
+ if (m_cdrom) NotifyInfoWindow(hwndDlg, m_cdrom, NULL, FALSE);
+
+}
+
+
+static void Window_OnSize(HWND hwndDlg, UINT nType, INT cx, INT cy)
+{
+ if (nType != SIZE_MINIMIZED)
+ {
+ childSizer.Resize(hwndDlg, cdromwnd_rlist, sizeof(cdromwnd_rlist) / sizeof(cdromwnd_rlist[0]));
+ InvalidateRect(hwndDlg, NULL, TRUE);
+ }
+}
+
+static void CALLBACK Window_TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ HWND hwndList;
+
+ switch (idEvent)
+ {
+ case TIMER_NOTIFYINFO_ID:
+ KillTimer(hwnd, TIMER_NOTIFYINFO_ID);
+ hwndList = GetDlgItem(hwnd, IDC_LIST2);
+ NotifyInfoWindow(hwnd, m_cdrom,
+ (hwndList) ? (INT)SendMessage(hwndList, LVM_GETNEXTITEM, (WPARAM)-1, (LPARAM)LVNI_FOCUSED) + 1: 0,
+ FALSE);
+ break;
+ }
+}
+
+static void Window_OnCommand(HWND hwndDlg, INT eventId, INT ctrlId, HWND hwndCtrl)
+{
+ switch (ctrlId)
+ {
+ case IDC_BUTTON_ENQUEUE:
+ case IDC_BUTTON_PLAY:
+ {
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_LIST2);
+ if (hwndList)
+ {
+ int selcnt = (INT)SendMessageW(hwndList, LVM_GETSELECTEDCOUNT, 0, 0L);
+ playFiles(hwndDlg, m_cdrom, (IDC_BUTTON_ENQUEUE == ctrlId), (selcnt) ? ((selcnt == 1) ? 1024 : 0) : 1);
+ }
+ }
+ break;
+ case IDC_BUTTON_EXTRACT:
+ {
+ RECT r;
+ GetWindowRect(hwndCtrl, &r);
+ int x = Menu_TrackPopup(plugin.hwndLibraryParent, GetSubMenu(g_context_menus, 0),
+ TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_BOTTOMALIGN |
+ TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RETURNCMD,
+ r.left, r.top, hwndDlg, NULL);
+ switch (x)
+ {
+ case ID_EXTRACTMENU_EXTRACTSELECTEDTRACKS: extractFiles(hwndDlg, m_cdrom, 0); break;
+ case ID_EXTRACTMENU_EXTRACTALLTRACKS: extractFiles(hwndDlg, m_cdrom, 1); break;
+ case ID_EXTRACTMENU_CONFIGURE: Plugin_ShowRippingPreferences(); break;
+ }
+ UpdateWindow(hwndDlg);
+ Sleep(100);
+ MSG msg;
+ while (PeekMessageW(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)); //eat return
+ }
+ break;
+ case IDC_BUTTON_EJECT:
+ {
+ wchar_t result[32] = {0};
+ wchar_t name[] = L"cda://X.cda";
+
+ name[6] = m_cdrom;
+ getFileInfoW(name, L"<eject>", result, sizeof(result)/sizeof(wchar_t));
+ }
+ break;
+ case IDC_BTN_SHOWINFO:
+ switch (eventId)
+ {
+ case BN_CLICKED:
+ SendMessageW(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(ctrlId, eventId), (LPARAM)hwndCtrl);
+ break;
+ }
+ break;
+ }
+}
+static void ListView_OnItemChanged(HWND hwndDlg, NMLISTVIEW *pnmv)
+{
+ if (LVIF_STATE & pnmv->uChanged)
+ {
+ if ((LVIS_FOCUSED & pnmv->uOldState) != (LVIS_FOCUSED & pnmv->uNewState))
+ {
+ KillTimer(hwndDlg, TIMER_NOTIFYINFO_ID);
+ SetTimer(hwndDlg, TIMER_NOTIFYINFO_ID, TIMER_NOTIFYINFO_DELAY, Window_TimerProc);
+
+ }
+ }
+}
+static INT_PTR Window_OnNotify(HWND hwndDlg, INT ctrlId, LPNMHDR phdr)
+{
+ switch (phdr->idFrom)
+ {
+ case IDC_LIST2:
+ switch (phdr->code)
+ {
+ case LVN_ITEMCHANGED: ListView_OnItemChanged(hwndDlg, (NMLISTVIEW*)phdr); break;
+ case LVN_KEYDOWN:
+ {
+ LPNMLVKEYDOWN pnkd = (LPNMLVKEYDOWN)phdr;
+ switch (pnkd->wVKey)
+ {
+ case '3':
+ if (GetAsyncKeyState(VK_MENU)&0x8000)
+ {
+ W_ListView view(GetDlgItem(hwndDlg, IDC_LIST2));
+ if (view.GetSelectedCount() == 0 || view.GetSelectedCount() == view.GetCount())
+ editCDInfo(hwndDlg, m_cdrom, 0);
+
+ int sel =-1;
+ while ((sel = view.GetNextSelected(sel)) != -1)
+ {
+ if (editCDInfo(hwndDlg, m_cdrom, sel+1) == 1)
+ break;
+ }
+
+
+ PostMessageW(hwndDlg, WM_NEXTDLGCTL, (WPARAM)phdr->hwndFrom, TRUE);
+ }
+ break;
+ case 'A':
+ if (GetAsyncKeyState(VK_CONTROL)) ListView_SetItemState(phdr->hwndFrom, -1, LVIS_SELECTED, LVIS_SELECTED);
+ break;
+ }
+ }
+ break;
+ case NM_DBLCLK:
+ playFiles(hwndDlg, m_cdrom, (!!g_config->ReadInt(L"enqueuedef", 0)) ^(!!(GetAsyncKeyState(VK_SHIFT)&0x8000)), 1024);
+ break;
+ case LVN_BEGINDRAG:
+ SetCapture(hwndDlg);
+ break;
+ case NM_RETURN:
+ SendMessageW(hwndDlg, WM_COMMAND, ((!!(GetAsyncKeyState(VK_SHIFT)&0x8000)) ^(!!g_config->ReadInt(L"enqueuedef", 0)))
+ ? IDC_BUTTON_ENQUEUE : IDC_BUTTON_PLAY, 0);
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+static void Window_OnMouseMove(HWND hwndDlg, INT vKey, POINTS pts)
+{
+ mlDropItemStruct m = {0};
+ if (GetCapture() != hwndDlg) return;
+
+ POINTSTOPOINT(m.p, pts);
+ MapWindowPoints(hwndDlg, HWND_DESKTOP, &m.p, 1);
+
+ m.type = ML_TYPE_CDTRACKS;
+
+ SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_HANDLEDRAG, (WPARAM)&m);
+
+}
+
+static void Window_OnLButtonUp(HWND hwndDlg, INT vKey, POINTS pts)
+{
+ mlDropItemStruct m = {0};
+
+ if (GetCapture() != hwndDlg) return;
+
+ ReleaseCapture();
+
+ m.type = ML_TYPE_CDTRACKS;
+ m.flags = ML_HANDLEDRAG_FLAG_NOCURSOR;
+
+ POINTSTOPOINT(m.p, pts);
+ MapWindowPoints(hwndDlg, HWND_DESKTOP, &m.p, 1);
+
+ SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_HANDLEDRAG, (WPARAM)&m);
+
+ if (m.result > 0)
+ {
+ HWND hwndList;
+ LVITEMW lvitem = {0};
+ int i, l, len;
+ itemRecordList myObj = {0};
+ char trackname[] = "cda://X,%d.cda";
+ char name[32] = {0}, total[512] = {0};
+
+ hwndList = GetDlgItem(hwndDlg, IDC_LIST2);
+
+ l = (hwndList) ? (INT)SendMessageW(hwndList, LVM_GETITEMCOUNT, 0,0) : 0;
+ if (l > 256) l = 256;
+
+
+ allocRecordList(&myObj, l, 0);
+
+ lvitem.mask = LVIF_PARAM;
+ lvitem.iSubItem = 0;
+
+ trackname[6] = m_cdrom;
+
+ for (i = 0; i < l; i++)
+ {
+ lvitem.iItem = i;
+ SendMessageW(hwndList, LVM_GETITEMW, 0, (LPARAM)&lvitem);
+ int p = (INT)(INT_PTR)lvitem.lParam;
+
+ if ((LVIS_SELECTED & SendMessageW(hwndList, LVM_GETITEMSTATE, i, LVIS_SELECTED)) && p > 0)
+ {
+
+ StringCchPrintfA(name, sizeof(name)/sizeof(char), trackname, p);
+
+ total[0] = 0;
+ mediaLibrary.GetFileInfo(name, total, sizeof(total)/sizeof(char), &len);
+ memset(myObj.Items + myObj.Size, 0, sizeof(itemRecord));
+ myObj.Items[myObj.Size].filename = _strdup(name);
+ myObj.Items[myObj.Size].length = len;
+ myObj.Items[myObj.Size++].title = _strdup(total);
+ }
+ }
+ if (myObj.Size)
+ {
+ m.flags = 0;
+ m.result = 0;
+ m.data = (void*)&myObj;
+ SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_HANDLEDROP, (WPARAM)&m);
+ }
+
+ freeRecordList(&myObj);
+ }
+}
+
+
+
+static void Window_OnQueryInfo(HWND hwnd)
+{
+ KillTimer(hwnd, TIMER_NOTIFYINFO_ID);
+ NotifyInfoWindow(hwnd, m_cdrom, NULL, TRUE);
+ SetTimer(hwnd, TIMER_NOTIFYINFO_ID, TIMER_NOTIFYINFO_DELAY, Window_TimerProc);
+}
+
+
+static void Window_OnFileTagUpdated(HWND hwnd, CHAR cLetter, LPCWSTR pszFileName)
+{
+ INT len, lcid;
+
+ len = (pszFileName) ? lstrlenW(pszFileName) : 0;
+ if (len < 7) return;
+ lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+ if (CSTR_EQUAL == CompareStringW(lcid, 0, L"cda://", 6, pszFileName, 6) && pszFileName[6] == cLetter)
+ {
+ GetGracenoteInfo(hwnd, cLetter, GetCurrentThread());
+ }
+}
+
+static void Window_OnContextMenu(HWND hwndDlg, HWND hwndFrom, int x, int y)
+{
+ POINT pt = {x,y};
+ W_ListView view(GetDlgItem(hwndDlg, IDC_LIST2));
+
+ if(view.GetCount() == 0) return;
+
+ if (x == -1 || y == -1) // x and y are -1 if the user invoked a shift-f10 popup menu
+ {
+ RECT itemRect = {0};
+ int selected = view.GetNextSelected();
+ if (selected != -1) // if something is selected we'll drop the menu from there
+ {
+ view.GetItemRect(selected, &itemRect);
+ ClientToScreen(view.getwnd(), (POINT *)&itemRect);
+ }
+ else // otherwise we'll drop it from the top-left corner of the listview, adjusting for the header location
+ {
+ GetWindowRect(view.getwnd(), &itemRect);
+
+ HWND hHeader = (HWND)SNDMSG(hwndFrom, LVM_GETHEADER, 0, 0L);
+ RECT headerRect;
+ if ((WS_VISIBLE & GetWindowLongPtr(hHeader, GWL_STYLE)) && GetWindowRect(hHeader, &headerRect))
+ {
+ itemRect.top += (headerRect.bottom - headerRect.top);
+ }
+ }
+ x = itemRect.left;
+ y = itemRect.top;
+ }
+
+ HWND hHeader = (HWND)SNDMSG(hwndFrom, LVM_GETHEADER, 0, 0L);
+ RECT headerRect;
+ if (0 == (WS_VISIBLE & GetWindowLongPtr(hHeader, GWL_STYLE)) || FALSE == GetWindowRect(hHeader, &headerRect))
+ {
+ SetRectEmpty(&headerRect);
+ }
+
+ if (FALSE != PtInRect(&headerRect, pt))
+ {
+ return;
+ }
+
+ int r = Menu_TrackPopup(plugin.hwndLibraryParent, GetSubMenu(g_context_menus, 1),
+ TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_NONOTIFY,
+ x, y, hwndDlg, NULL);
+ switch (r)
+ {
+ case ID_PE_ID3:
+ {
+ if (view.GetSelectedCount() == 0 || view.GetSelectedCount() == view.GetCount())
+ editCDInfo(hwndDlg, m_cdrom, 0);
+
+ int sel =-1;
+ while ((sel = view.GetNextSelected(sel)) != -1)
+ {
+ if (editCDInfo(hwndDlg, m_cdrom, sel+1) == 1)
+ break;
+ }
+ }
+ PostMessageW(hwndDlg, WM_NEXTDLGCTL, (WPARAM)hwndFrom, TRUE);
+ break;
+ case ID_CDROMMENU_PLAYSELECTEDITEMS: playFiles(hwndDlg, m_cdrom, 0, 0); break;
+ case ID_CDROMMENU_ENQUEUESELECTEDITEMS: playFiles(hwndDlg, m_cdrom, 1, 0); break;
+ case ID_CDROMMENU_SELECTALL: ListView_SetItemState(hwndFrom, -1, LVIS_SELECTED, LVIS_SELECTED); break;
+ case ID_CDROMMENU_PLAYALL: playFiles(hwndDlg, m_cdrom, 0, 1); break;
+ case ID_CDROMMENU_ENQUEUEALL: playFiles(hwndDlg, m_cdrom, 1, 1); break;
+ case ID_CDROMMENU_EXTRACT_EXTRACTSELECTEDITEMS: extractFiles(hwndDlg, m_cdrom, 0); break;
+ case ID_CDROMMENU_EXTRACT_EXTRACTALL: extractFiles(hwndDlg, m_cdrom, 1); break;
+ case ID_CDROMMENU_EXTRACT_CONFIGURE: Plugin_ShowRippingPreferences(); break;
+ }
+ UpdateWindow(hwndDlg);
+ Sleep(100);
+ MSG msg;
+ while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)); //eat return
+}
+
+static INT_PTR WINAPI DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ INT_PTR a;
+ a = dialogSkinner.Handle(hwndDlg, uMsg, wParam, lParam);
+ if (a) return a;
+
+ switch (uMsg)
+ {
+ case WM_INITDIALOG: return Window_OnInitDialog(hwndDlg, (HWND)wParam, lParam);
+ case WM_DESTROY: Window_OnDestroy(hwndDlg); break;
+ case WM_SIZE: Window_OnSize(hwndDlg, (UINT)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(wParam)); break;
+ case WM_COMMAND: Window_OnCommand(hwndDlg, HIWORD(wParam), LOWORD(wParam), (HWND)lParam); break;
+ case WM_NOTIFY: return Window_OnNotify(hwndDlg, (INT)wParam, (LPNMHDR) lParam);
+ case WM_MOUSEMOVE: Window_OnMouseMove(hwndDlg, (INT)wParam, MAKEPOINTS(lParam)); break;
+ case WM_LBUTTONUP: Window_OnLButtonUp(hwndDlg, (INT)wParam, MAKEPOINTS(lParam)); break;
+ case WM_CONTEXTMENU: Window_OnContextMenu(hwndDlg, (HWND)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); return 1;
+ case WM_ERASEBKGND: return 1;
+ case WM_TAGUPDATED: Window_OnFileTagUpdated(hwndDlg, m_cdrom, (LPCWSTR)lParam); break;
+ case WM_QUERYFILEINFO: Window_OnQueryInfo(hwndDlg); break;
+ case WM_PAINT:
+ {
+ int tab[] = { IDC_LIST2 | DCW_SUNKENBORDER};
+ dialogSkinner.Draw(hwndDlg, tab, 1);
+ }
+ return 0;
+
+ case WM_EXTRACTDISC:
+ if ((CHAR)wParam == m_cdrom) extractFiles(hwndDlg, m_cdrom, TRUE);
+ SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, ((CHAR)wParam == m_cdrom));
+ return TRUE;
+
+ }
+
+ return 0;
+}
+
+static void CALLBACK APC_GetCracenoteInfo(ULONG_PTR param)
+{
+ GetGracenoteInfo(((APCPARAM*)param)->hwndDlg, ((APCPARAM*)param)->cLetter, NULL);
+ free((APCPARAM*)param);
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/view_container.cpp b/Src/Plugins/Library/ml_disc/view_container.cpp
new file mode 100644
index 00000000..d5dad335
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/view_container.cpp
@@ -0,0 +1,573 @@
+#include "main.h"
+#include "./resource.h"
+#include "../nu/DialogSkinner.h"
+#include "../nu/AutoWideFn.h"
+#include "./commandbar.h"
+#include "./settings.h"
+#include "../nu/trace.h"
+
+//#include <primosdk.h>
+#include <windowsx.h>
+#include <strsafe.h>
+
+#define MINIINFO_HEIGHT 100
+
+#define IDC_CHILDVIEW 0x1000
+#define IDC_MINIINFO 0x1001
+#define IDC_COMMAND_BAR 0x1002
+
+#define WINDOWS_SPACING 3
+
+#define CONTAINER_PROPW L"CONTAINER"
+
+typedef struct _CONTAINER
+{
+ CHAR cLetter;
+ INT typeChild;
+ BOOL bInfoVisible;
+ HANDLE hLastQuery;
+} CONTAINER;
+
+static UINT msgNotify = 0;
+#define GetContainer(__hwnd) ((CONTAINER*)GetPropW(__hwnd, CONTAINER_PROPW))
+static INT_PTR WINAPI DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+// public function
+typedef struct _CONTAINER_PARAM
+{
+ CHAR cLetter;
+ BOOL bQueryInfo;
+}CONTAINER_PARAM;
+
+HWND CreateContainerWindow(HWND hwndParent, CHAR cLetter, BOOL bQueryInfo)
+{
+ CONTAINER_PARAM param;
+ param.cLetter = cLetter;
+ param.bQueryInfo = bQueryInfo;
+ return WASABI_API_CREATEDIALOGPARAMW(IDD_VIEW_CONTAINER, hwndParent, DlgProc, (LPARAM)&param);
+}
+
+static void ViewContainer_LayoutChildren(HWND hdlg, BOOL bRedraw)
+{
+ RECT rc;
+ HWND hctrl;
+ HDWP hdwp;
+ DWORD flags;
+
+ CONTAINER *pc = GetContainer(hdlg);
+ if (!pc) return;
+
+ if (!GetClientRect(hdlg, &rc)) return;
+ rc.right -= 2;
+
+ flags = SWP_NOACTIVATE | ((bRedraw) ? 0 : SWP_NOREDRAW);
+
+ hdwp = BeginDeferWindowPos(3);
+ if (!hdwp) return;
+
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDC_COMMAND_BAR)))
+ {
+ INT height = CommandBar_GetBestHeight(hctrl);
+ hdwp = DeferWindowPos(hdwp, hctrl, HWND_BOTTOM, rc.left, rc.bottom - (height), rc.right - rc.left, height, flags);
+ rc.bottom -= (height + WINDOWS_SPACING);
+ }
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDC_MINIINFO)) && pc->bInfoVisible)
+ {
+ INT height = MINIINFO_HEIGHT;
+ hdwp = DeferWindowPos(hdwp, hctrl, HWND_TOP, rc.left, rc.bottom - height, rc.right - rc.left, height, flags);
+ rc.bottom -= (height + WINDOWS_SPACING);
+ }
+
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDC_CHILDVIEW)))
+ {
+ hdwp = DeferWindowPos(hdwp, hctrl, HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, flags);
+ }
+
+ EndDeferWindowPos(hdwp);
+}
+
+static void CreateChild(HWND hParent, INT typeId, LPARAM param, BOOL fCreateAlways = TRUE)
+{
+ DM_NOTIFY_PARAM hdr;
+ CONTAINER *pc = GetContainer(hParent);
+ if (!pc) return;
+
+ HWND hChild = GetDlgItem(hParent, IDC_CHILDVIEW);
+ if (hChild)
+ {
+ if (pc->typeChild == typeId && !fCreateAlways) return;
+ DestroyWindow(hChild);
+ }
+
+ hChild = NULL;
+ pc->typeChild = 0;
+
+ switch(typeId)
+ {
+ case IDD_VIEW_INFO:
+ hChild = CreateInfoWindow(hParent, (CHAR)(0xFF & param));
+ break;
+ case IDD_VIEW_WAIT:
+ hChild = CreateWaitWindow(hParent, (CHAR)(0xFF & param));
+ break;
+ case IDD_VIEW_CDROM:
+ if (param <= 0xFF)
+ {
+ ZeroMemory(&hdr, sizeof(DM_NOTIFY_PARAM));
+ hdr.cLetter = (CHAR)(0xFF & (INT)param);
+ hdr.opCode = DMOP_GENERAL;
+ param = (INT_PTR)&hdr;
+ }
+ hChild = CreateCDViewWindow(hParent, (DM_NOTIFY_PARAM*)param);
+ break;
+ case IDD_VIEW_CDROM_EX2:
+ hChild = CreateCDRipWindow(hParent, (CHAR)(0xFF & param));
+ break;
+ case IDD_VIEW_CDROM_DATA:
+ hChild = CreateCdDataViewWindow(hParent, (CHAR)(0xFF & param));
+ break;
+ }
+ TRACE_FMT(L"Creating cd view (%d)\n", typeId);
+ if (hChild)
+ {
+ pc->typeChild = typeId;
+ SetWindowLongPtrW(hChild, GWLP_ID, IDC_CHILDVIEW);
+ ViewContainer_LayoutChildren(hParent, TRUE);
+ ShowWindow(hChild, SW_SHOWNA);
+ }
+}
+
+static HWND CreateMiniInfoWindow(HWND hwndParent)
+{
+ RECT rw;
+ HWND hwnd;
+ WEBINFOCREATE wic;
+
+ GetWindowRect(hwndParent, &rw);
+
+ wic.hwndParent = hwndParent;
+ wic.uMsgQuery = WM_QUERYFILEINFO;
+ wic.ctrlId = IDC_MINIINFO;
+ wic.cx = rw.right - rw.left - 3;
+ wic.cy = MINIINFO_HEIGHT;
+ wic.x = 0;
+ wic.y = (rw.bottom - rw.top) - MINIINFO_HEIGHT - 1;
+
+ hwnd = (HWND)SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_CREATEWEBINFO, (WPARAM)&wic);
+ if (hwnd)
+ {
+ SetWindowLongPtrW(hwnd, GWL_EXSTYLE, GetWindowLongPtrW(hwnd, GWL_EXSTYLE) | WS_EX_CLIENTEDGE);
+ MLSkinWindow2(plugin.hwndLibraryParent, hwnd, SKINNEDWND_TYPE_WINDOW, SWS_USESKINCOLORS);
+ }
+
+ return hwnd;
+}
+
+static void CALLBACK FreeAsyncParam(DM_NOTIFY_PARAM *phdr)
+{
+ DM_MCI_PARAM *pmci;
+ switch(phdr->opCode)
+ {
+ case DMOP_MCIINFO:
+ pmci = (DM_MCI_PARAM*)phdr;
+ if (pmci->pTracks) free(pmci->pTracks);
+ break;
+ case DMOP_IMAPIINFO:
+ break;
+ }
+ free(phdr);
+}
+
+static void QueryDriveRecordable(HWND hwnd, CHAR cLetter)
+{
+ CONTAINER *pc = GetContainer(hwnd);
+ if (!pc) return;
+
+ DWORD dwType;
+ dwType = DriveManager_GetDriveType(cLetter);
+ if (0 == (DRIVE_CAP_UNKNOWN & dwType))
+ {
+ CreateChild(hwnd, ((DRIVE_CAP_R | DRIVE_CAP_RW) & dwType) ? IDD_VIEW_CDROM_BURN : IDD_VIEW_CDROM, (LPARAM)cLetter);
+ return;
+ }
+ else
+ {
+ DM_UNITINFO_PARAM *pui;
+
+ pui = (DM_UNITINFO_PARAM*)calloc(1, sizeof(DM_UNITINFO_PARAM));
+ if (pui)
+ {
+ pui->header.callback = (INT_PTR)hwnd;
+ pui->header.uMsg = msgNotify;
+ pui->header.cLetter = cLetter;
+ pui->header.fnFree = FreeAsyncParam;
+ pc->hLastQuery = pui;
+ if (!DriveManager_GetUnitInfo(pui)) CreateChild(hwnd, IDD_VIEW_CDROM, (LPARAM)cLetter);
+ }
+ }
+}
+
+static void QueryDiscInfo(HWND hwnd, CHAR cLetter)
+{
+ DM_MCI_PARAM *pmci;
+ CHAR cMode;
+
+ CONTAINER *pc = GetContainer(hwnd);
+ if (!pc) return;
+
+ cMode = DriveManager_GetDriveMode(cLetter);
+
+ switch(cMode)
+ {
+ case DM_MODE_RIPPING:
+ if (cdrip_isextracting(cLetter)) CreateChild(hwnd, IDD_VIEW_CDROM_EX2, cLetter);
+ else CreateChild(hwnd, IDD_VIEW_INFO, cLetter);
+ return;
+
+ case DM_MODE_COPYING:
+ if (IDD_VIEW_CDROM_DATA == pc->typeChild) return;
+ break;
+ case DM_MODE_READY:
+ if (IDD_VIEW_CDROM_DATA == pc->typeChild) return;
+ break;
+ }
+
+ CreateChild(hwnd, IDD_VIEW_WAIT, cLetter);
+
+ pmci = (DM_MCI_PARAM*)calloc(1, sizeof(DM_MCI_PARAM));
+ if (pmci)
+ {
+ pmci->header.callback = (INT_PTR)hwnd;
+ pmci->header.uMsg = msgNotify;
+ pmci->header.cLetter = cLetter;
+ pmci->header.fnFree = FreeAsyncParam;
+ pmci->header.fFlags = DMF_READY | DMF_MEDIUMPRESENT | DMF_MODE | DMF_TRACKCOUNT | DMF_TRACKSINFO;
+ pmci->nTracks = 256;
+ pmci->pTracks = (DWORD*)calloc(pmci->nTracks, sizeof(DWORD));
+
+ pc->hLastQuery = pmci;
+
+ if (!DriveManager_GetMCIInfo(pmci))
+ {
+ pc->hLastQuery = NULL;
+ // display error dialog
+ }
+ }
+}
+
+static void Medium_OnArrived(HWND hwnd, CHAR cLetter)
+{
+ QueryDiscInfo(hwnd, cLetter);
+}
+
+static void Medium_OnRemoved(HWND hwnd, CHAR cLetter)
+{
+ QueryDriveRecordable(hwnd, cLetter);
+}
+
+static void Drive_OnModeChanged(HWND hwnd, CHAR cLetter, CHAR cMode)
+{
+ QueryDiscInfo(hwnd, cLetter);
+ HWND hctrl = GetDlgItem(hwnd, IDC_CHILDVIEW);
+ if (hctrl) SendMessage(hctrl, WM_COMMAND, MAKEWPARAM(ID_DRIVE_MODE_CHANGED, 0), (LPARAM)hwnd);
+}
+
+static void GetInfo_Completed(HWND hwnd, DM_NOTIFY_PARAM *phdr)
+{
+ DM_MCI_PARAM *pmci;
+ CONTAINER *pc = GetContainer(hwnd);
+ if (!pc || phdr != pc->hLastQuery) return;
+
+ switch(phdr->opCode)
+ {
+ case DMOP_MCIINFO:
+ pmci = (DM_MCI_PARAM*)phdr;
+ if (0 == phdr->result)
+ {
+ if (pmci->bMediumPresent && pmci->nTracks > 0)
+ {
+ int i;
+ for (i = 0; i < pmci->nTracks; i++) if (0x80000000 & pmci->pTracks[i]) break;
+ if (i == pmci->nTracks) CreateChild(hwnd, IDD_VIEW_CDROM_DATA, (LPARAM)phdr->cLetter);
+ else CreateChild(hwnd, IDD_VIEW_CDROM, (LPARAM)phdr);
+ }
+ else QueryDriveRecordable(hwnd, phdr->cLetter);
+ }
+ else {/*go to error view*/ }
+ break;
+ case DMOP_UNITINFO:
+ CreateChild(hwnd, ( 0 == phdr->result && Drive_IsRecorderType(((DM_UNITINFO_PARAM*)phdr)->dwType)) ? IDD_VIEW_CDROM_BURN : IDD_VIEW_CDROM, (LPARAM)phdr->cLetter);
+ break;
+ }
+}
+
+static BOOL ViewContainer_OnGetMiniInfoEnabled(HWND hdlg)
+{
+ return g_config->ReadInt(TEXT("useminiinfo2"), 0);
+}
+
+static INT_PTR Window_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
+{
+ CONTAINER_PARAM *param = (CONTAINER_PARAM*)lParam;
+ CONTAINER *pc = (CONTAINER*)calloc(1, sizeof(CONTAINER));
+ if (pc)
+ {
+ pc->cLetter = param->cLetter;
+ SetPropW(hwnd, CONTAINER_PROPW, pc);
+ }
+
+ MLSkinWindow2(plugin.hwndLibraryParent, hwnd, SKINNEDWND_TYPE_DIALOG, SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS);
+
+ BOOL bVal = FALSE;
+ if (S_OK == Settings_GetBool(C_GLOBAL, GF_SHOWINFO, &bVal) && bVal)
+ SendMessageW(hwnd, WM_COMMAND, MAKEWPARAM(IDC_BTN_SHOWINFO, BN_CLICKED), (LPARAM)NULL);
+
+ if (!msgNotify) msgNotify = RegisterWindowMessageW(L"cdrom_notify_msg");
+ Plugin_RegisterListener(hwnd, msgNotify, pc->cLetter);
+
+ if (param->bQueryInfo) QueryDiscInfo(hwnd, pc->cLetter);
+
+ if (WASABI_API_APP)
+ {
+ HACCEL hAccel = WASABI_API_LOADACCELERATORSW(IDR_ACCELERATOR_VIEW);
+ if (hAccel) WASABI_API_APP->app_addAccelerators(hwnd, &hAccel, 1, TRANSLATE_MODE_CHILD);
+ WASABI_API_APP->ActiveDialog_Register(hwnd);
+ }
+
+ return 0;
+}
+
+static void Window_OnDestroy(HWND hdlg)
+{
+ Plugin_UnregisterListener(hdlg);
+ if (WASABI_API_APP) WASABI_API_APP->app_removeAccelerators(hdlg);
+
+ HWND hctrl = GetDlgItem(hdlg, IDC_MINIINFO);
+ if (hctrl && IsWindow(hctrl)) SENDMLIPC(hctrl, ML_IPC_WEBINFO_RELEASE, 0);
+
+ CONTAINER *pc = GetContainer(hdlg);
+ if (pc)
+ {
+ Settings_SetBool(C_GLOBAL, GF_SHOWINFO, pc->bInfoVisible);
+ RemovePropW(hdlg, CONTAINER_PROPW);
+ free(pc);
+ }
+}
+
+static void Window_OnDisplayChange(HWND hdlg, INT dpi, INT resX, INT resY)
+{
+ SendDlgItemMessageW(hdlg, IDC_CHILDVIEW, WM_DISPLAYCHANGE, dpi, MAKELPARAM(resX, resY));
+ SendDlgItemMessageW(hdlg, IDC_MINIINFO, WM_DISPLAYCHANGE, dpi, MAKELPARAM(resX, resY));
+ SendDlgItemMessageW(hdlg, IDC_COMMAND_BAR, WM_DISPLAYCHANGE, dpi, MAKELPARAM(resX, resY));
+}
+
+static void ViewContainer_OnWindowPosChanged(HWND hdlg, WINDOWPOS *pwp)
+{
+ RECT rc, rw;
+ HWND hctrl;
+ HDWP hdwp;
+ DWORD flags;
+
+ CONTAINER *pc = GetContainer(hdlg);
+ if (!pc) return;
+
+ if (SWP_FRAMECHANGED & pwp->flags)
+ {
+ ViewContainer_LayoutChildren(hdlg, (SWP_NOREDRAW & pwp->flags));
+ return;
+ }
+
+ if (0 != (SWP_NOSIZE & pwp->flags)) return;
+
+ if (!GetClientRect(hdlg, &rc)) return;
+
+ rc.right -= 2;
+
+ flags = SWP_NOACTIVATE | SWP_NOZORDER | ((SWP_NOREDRAW | SWP_NOCOPYBITS) & pwp->flags);
+
+ hdwp = BeginDeferWindowPos(3);
+
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDC_COMMAND_BAR)) && GetWindowRect(hctrl, &rw))
+ {
+ hdwp = DeferWindowPos(hdwp, hctrl, HWND_BOTTOM, rc.left, rc.bottom - (rw.bottom - rw.top), rc.right - rc.left, (rw.bottom - rw.top), flags);
+ rc.bottom -= (rw.bottom - rw.top + WINDOWS_SPACING);
+ }
+
+ if (pc->bInfoVisible && NULL != (hctrl = GetDlgItem(hdlg, IDC_MINIINFO)) && GetWindowRect(hctrl, &rw))
+ {
+ hdwp = DeferWindowPos(hdwp, hctrl, HWND_BOTTOM, rc.left, rc.bottom - (rw.bottom - rw.top), rc.right - rc.left, (rw.bottom - rw.top), flags);
+ rc.bottom -= (rw.bottom - rw.top + WINDOWS_SPACING);
+ }
+
+ if (NULL != (hctrl = GetDlgItem(hdlg, IDC_CHILDVIEW)))
+ {
+ hdwp = DeferWindowPos(hdwp, hctrl, HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, flags | SWP_NOMOVE);
+ }
+
+ EndDeferWindowPos(hdwp);
+}
+
+static void ViewContainer_ShowMiniInfo(HWND hdlg, BOOL bShow)
+{
+ CONTAINER *pc = GetContainer(hdlg);
+ if (!pc) return;
+
+ pc->bInfoVisible = FALSE;
+
+ HWND hInfo = GetDlgItem(hdlg, IDC_MINIINFO);
+ if (bShow && !hInfo) hInfo = CreateMiniInfoWindow(hdlg);
+
+ if (hInfo)
+ {
+ if (bShow)
+ {
+ pc->bInfoVisible = TRUE;
+ ViewContainer_LayoutChildren(hdlg, TRUE);
+ ShowWindow(hInfo, SW_SHOWNA);
+ UpdateWindow(hInfo);
+ }
+ else
+ {
+ ShowWindow(hInfo, SW_HIDE);
+ ViewContainer_LayoutChildren(hdlg, TRUE);
+ }
+ }
+
+ HWND hctrl = GetDlgItem(hdlg, IDC_CHILDVIEW);
+ if (hctrl && NULL != (hctrl = GetDlgItem(hctrl, IDC_BTN_SHOWINFO)))
+ SetWindowTextW(hctrl, WASABI_API_LNGSTRINGW((bShow) ? IDS_HIDE_INFO : IDS_SHOW_INFO));
+}
+
+static void Window_OnCommand(HWND hdlg, INT eventId, INT ctrlId, HWND hctrl)
+{
+ CONTAINER *pc = GetContainer(hdlg);
+ if (!pc) return;
+
+ switch(ctrlId)
+ {
+ case IDC_BTN_SHOWINFO:
+ switch(eventId)
+ {
+ case BN_CLICKED:
+ ViewContainer_ShowMiniInfo(hdlg, !pc->bInfoVisible);
+ break;
+ case BN_EX_GETTEXT:
+ {
+ if (IsWindow(hctrl))
+ {
+ // to ensure we're able to remove this for all views, we check this here
+ if (!ViewContainer_OnGetMiniInfoEnabled(hdlg))
+ DestroyWindow(hctrl);
+ else
+ SetWindowTextW(hctrl, WASABI_API_LNGSTRINGW((pc->bInfoVisible) ? IDS_HIDE_INFO : IDS_SHOW_INFO));
+ }
+ break;
+ }
+ }
+ break;
+ case ID_MINIINFO_SHOW: ViewContainer_ShowMiniInfo(hdlg, !pc->bInfoVisible); break;
+ case ID_EJECT_DISC: DriveManager_Eject(pc->cLetter, DM_EJECT_CHANGE); break;
+ case ID_COPY_SELECTION: SendDlgItemMessageW(hdlg, IDC_CHILDVIEW, WM_COMMAND, MAKEWPARAM(ctrlId, eventId), (LPARAM)hctrl); break;
+ }
+}
+
+static void Window_OnFileTagUpdated(HWND hdlg, WORD wCode, INT_PTR param)
+{
+ HWND hChild = GetDlgItem(hdlg, IDC_CHILDVIEW);
+ if (hChild && IsWindow(hChild))
+ {
+ SendMessageW(hChild, WM_TAGUPDATED, 0,
+ (LPARAM)((IPC_FILE_TAG_MAY_HAVE_UPDATED == wCode) ? AutoWideFn((LPCSTR)param) : (LPCWSTR)param));
+ }
+}
+
+static void Window_OnPluginNotify(HWND hdlg, WORD wCode, INT_PTR param)
+{
+ switch(wCode)
+ {
+ case DMW_MEDIUMARRIVED: Medium_OnArrived(hdlg, (CHAR)param); break;
+ case DMW_MEDIUMREMOVED: Medium_OnRemoved(hdlg, (CHAR)param); break;
+ case DMW_OPCOMPLETED: GetInfo_Completed(hdlg, (DM_NOTIFY_PARAM*)param); break;
+ case DMW_MODECHANGED: Drive_OnModeChanged(hdlg, (CHAR)LOWORD(param), (CHAR)(LOWORD(param) >> 8)); break;
+ case IPC_FILE_TAG_MAY_HAVE_UPDATED:
+ case IPC_FILE_TAG_MAY_HAVE_UPDATEDW: Window_OnFileTagUpdated(hdlg, wCode, param); break;
+ }
+}
+
+static INT_PTR Window_OnSendFileInfo(HWND hdlg, LPCWSTR pszFileName, UINT fFlags)
+{
+ WEBINFOSHOW wis;
+ HWND hInfo = GetDlgItem(hdlg, IDC_MINIINFO);
+ if (!hInfo) return 0;
+
+ wis.pszFileName = pszFileName;;
+ wis.fFlags = fFlags;
+ return (INT_PTR)SENDMLIPC(hInfo, ML_IPC_WEBINFO_SHOWINFO, (WPARAM)&wis);
+}
+
+static void Window_OnQueryFileInfo(HWND hdlg)
+{
+ HWND hChild = GetDlgItem(hdlg, IDC_CHILDVIEW);
+ if(!hChild || !PostMessageW(hChild, WM_QUERYFILEINFO, 0, 0L)) Window_OnSendFileInfo(hdlg, L"", WISF_FORCE);
+}
+
+INT_PTR CALLBACK CommandBar_DialogProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+static HWND ViewContainer_OnCreateCommandBar(HWND hdlg, CMDBARCREATESTRUCT *pcbcs)
+{
+ if (!pcbcs || !pcbcs->fnDialogProc) return NULL;
+ HWND hwndBar = WASABI_API_CREATEDIALOGPARAMW(pcbcs->resourceId, hdlg, CommandBar_DialogProc, (LPARAM)pcbcs);
+ if (hwndBar)
+ {
+ SetWindowLongPtrW(hwndBar, GWLP_ID, IDC_COMMAND_BAR);
+ ViewContainer_LayoutChildren(hdlg, TRUE);
+ ShowWindow(hwndBar, SW_SHOWNA);
+ }
+ return hwndBar;
+}
+
+static BOOL ViewContainer_OnDestroyCommandBar(HWND hdlg)
+{
+ HWND hwndBar = GetDlgItem(hdlg, IDC_COMMAND_BAR);
+ if (!hwndBar) return FALSE;
+
+ DestroyWindow(hwndBar);
+ ViewContainer_LayoutChildren(hdlg, TRUE);
+ return TRUE;
+}
+
+static BOOL ViewContainer_OnGetMiniInfoVisible(HWND hdlg)
+{
+ HWND hctrl = GetDlgItem(hdlg, IDC_MINIINFO);
+ return (hctrl && IsWindowVisible(hctrl));
+}
+
+static BOOL ViewContainer_OnExtractDisc(HWND hdlg, WPARAM wParam, LPARAM lParam)
+{
+ HWND hChild = GetDlgItem(hdlg, IDC_CHILDVIEW);
+ return (hChild) ? (BOOL)SendMessageW(hChild, WM_EXTRACTDISC, wParam, lParam) : FALSE;
+}
+
+static INT_PTR WINAPI DlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG: return (INT_PTR)Window_OnInitDialog(hdlg, (HWND)wParam, lParam);
+ case WM_DESTROY: Window_OnDestroy(hdlg); break;
+ case WM_DISPLAYCHANGE: Window_OnDisplayChange(hdlg, (INT)wParam, LOWORD(lParam), HIWORD(lParam)); break;
+ case WM_WINDOWPOSCHANGED: ViewContainer_OnWindowPosChanged(hdlg, (WINDOWPOS*)lParam); return TRUE;
+ case WM_COMMAND: Window_OnCommand(hdlg, HIWORD(wParam), LOWORD(wParam), (HWND)lParam); break;
+ case WM_QUERYFILEINFO: Window_OnQueryFileInfo(hdlg); return 1;
+ case WM_SHOWFILEINFO: MSGRESULT(hdlg, Window_OnSendFileInfo(hdlg, (LPCWSTR)lParam, (UINT)wParam));
+ case WM_EXTRACTDISC: MSGRESULT(hdlg, ViewContainer_OnExtractDisc(hdlg, wParam, lParam));
+ case VCM_CREATECOMMANDBAR: MSGRESULT(hdlg, ViewContainer_OnCreateCommandBar(hdlg, (CMDBARCREATESTRUCT*)lParam));
+ case VCM_DESTROYCOMMANDBAR: MSGRESULT(hdlg, ViewContainer_OnDestroyCommandBar(hdlg));
+ case VCM_GETCOMMANDBAR: MSGRESULT(hdlg, GetDlgItem(hdlg, IDC_COMMAND_BAR));
+ case VCM_GETMININFOENABLED: MSGRESULT(hdlg, ViewContainer_OnGetMiniInfoEnabled(hdlg));
+ case VCM_GETMININFOVISIBLE: MSGRESULT(hdlg, ViewContainer_OnGetMiniInfoVisible(hdlg));
+ case WM_USER + 0x200: MSGRESULT(hdlg, TRUE);
+ }
+
+ if (msgNotify == uMsg) Window_OnPluginNotify(hdlg, (WORD)wParam, (INT_PTR)lParam);
+ return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/view_data.cpp b/Src/Plugins/Library/ml_disc/view_data.cpp
new file mode 100644
index 00000000..d21def96
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/view_data.cpp
@@ -0,0 +1,675 @@
+#include "main.h"
+#include <windowsx.h>
+#include "resource.h"
+#include "../winamp/wa_ipc.h"
+#include "../nu/DialogSkinner.h"
+#include "../nu/menushortcuts.h"
+#include "./commandbar.h"
+#include "./copyfiles.h"
+#include "./settings.h"
+#include <shlwapi.h>
+#include <strsafe.h>
+
+
+#define IDT_NOTIFYINFO 1985
+#define IDT_UPDATESTATUS 1986
+#define DELAY_NOTIFYINFO 200
+#define DELAY_UPDATESTATUS 5
+
+#define IDC_FILEVIEW 10000
+
+#define DATAVIEW_PROPW L"DATAVIEW"
+
+#define STRCOMP_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
+
+typedef struct _DATAVIEW
+{
+ CHAR cLetter;
+ WCHAR szSongInfoCache[MAX_PATH];
+} DATAVIEW;
+
+
+static INT_PTR WINAPI DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+INT_PTR WINAPI DataCmdBar_DialogProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+static void CALLBACK DataViewTimer_OnNotifyInfoElapsed(HWND hdlg, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
+static void CALLBACK DataViewTimer_OnStatusUpdateElapsed(HWND hdlg, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
+
+HWND CreateCdDataViewWindow(HWND hwndParent, CHAR cLetter)
+{
+ return WASABI_API_CREATEDIALOGPARAMW(IDD_VIEW_CDROM_DATA, hwndParent, DlgProc, (LPARAM)cLetter);
+}
+
+#define GetDataView(__hwnd) ((DATAVIEW*)GetPropW(__hwnd, DATAVIEW_PROPW))
+
+static void DataView_NotifyInfoWindow(HWND hdlg, INT iFile, BOOL bForceRefresh)
+{
+ DATAVIEW *pdv = GetDataView(hdlg);
+
+ HWND hParent = GetParent(hdlg);
+ if (hParent)
+ {
+ wchar_t szPath[MAX_PATH], *pszPath;
+ szPath[0] = L'\0';
+ if (-1 != iFile)
+ {
+ HWND hfv = GetDlgItem(hdlg, IDC_FILEVIEW);
+ if (hfv)
+ {
+ if (FileView_GetCurrentPath(hfv, szPath, ARRAYSIZE(szPath)) &&
+ S_OK == StringCchCatW(szPath, ARRAYSIZE(szPath), L"\\"))
+ {
+ FVITEM item;
+ item.mask = FVIF_TEXT | FVIF_TYPE;
+ INT l = lstrlenW(szPath);
+ pszPath = szPath + l;
+ item.cchTextMax = l;
+ item.pszText = pszPath;
+ item.cchTextMax = ARRAYSIZE(szPath) - item.cchTextMax;
+ if (!FileView_GetFile(hfv, iFile, &item) || FVFT_AUDIO != item.wType) szPath[0] = L'\0';
+ else if (item.pszText != szPath) StringCchCopy(szPath, ARRAYSIZE(szPath), item.pszText);
+ }
+ else szPath[0] = L'\0';
+ }
+ }
+
+ if (pdv && !bForceRefresh &&
+ CSTR_EQUAL == CompareString(STRCOMP_INVARIANT, NORM_IGNORECASE, pdv->szSongInfoCache, -1, szPath, -1))
+ {
+ return;
+ }
+ SendMessageW(hParent, WM_SHOWFILEINFO, (WPARAM)((bForceRefresh) ? WISF_FORCE : WISF_NORMAL), (LPARAM)szPath);
+ if (pdv) StringCchPrintfW(pdv->szSongInfoCache, sizeof(pdv->szSongInfoCache)/sizeof(pdv->szSongInfoCache[0]), szPath);
+ }
+}
+
+static void DataViewStatus_UpdateText(HWND hdlg)
+{
+ WCHAR szStatus[256] = {0};
+
+ HWND hFileView = GetDlgItem(hdlg, IDC_FILEVIEW);
+
+ HWND hbar = ViewContainer_GetCmdBar(GetParent(hdlg));
+ if (!hbar) return;
+
+ if (hFileView)
+ FileView_GetStatusText(hFileView, szStatus, sizeof(szStatus)/sizeof(szStatus[0]));
+ DataView_NotifyInfoWindow(hdlg, -1, FALSE);
+ SetWindowTextW(hbar, szStatus);
+}
+typedef struct _COLUMN
+{
+ INT id;
+ INT width;
+} COLUMN;
+
+static COLUMN defaultColumns[] =
+{
+ { FVCOLUMN_NAME, 200 },
+ { FVCOLUMN_SIZE, 82 },
+ { FVCOLUMN_TYPE, 64 },
+ { FVCOLUMN_MODIFIED, 140 },
+ { FVCOLUMN_EXTENSION, 64 },
+};
+
+static LPTSTR DataView_ColumnsToStr(LPTSTR pszText, size_t cchTextMax, COLUMN *pColumns, INT count)
+{
+ if (!pszText || cchTextMax < 1) return NULL;
+ pszText[0] = TEXT('\0');
+
+ if (!pColumns) return pszText;
+ LPTSTR pc = pszText;
+ for(int i = 0; i < count; i++)
+ {
+ HRESULT hr = StringCchPrintfEx(pc, cchTextMax, &pc, &cchTextMax, STRSAFE_IGNORE_NULLS,
+ TEXT("%c(%d, %d)"), ((0 == i) ? TEXT(' ') : TEXT(',')), pColumns[i].id, pColumns[i].width);
+ if (S_OK != hr) return NULL;
+ }
+ return pszText;
+}
+
+static void DataView_LoadFileViewColumns(HWND hdlg)
+{
+ UINT szColumns[256] = {0};
+ HWND hctrl = GetDlgItem(hdlg, IDC_FILEVIEW);
+ if (!hctrl) return;
+
+ INT count = FileView_GetColumnArray(hctrl, ARRAYSIZE(szColumns), szColumns);
+ for (int i = count-1; i >= 0; i--) FileView_DeleteColumn(hctrl, szColumns[i]);
+ TCHAR szColumnList[4096] = {0};
+
+ if (S_OK != Settings_ReadValue(C_DATAVIEW, DVF_COLUMNLIST, szColumnList, sizeof(szColumnList)) ||
+ TEXT('\0') == *szColumnList)
+ {
+ DataView_ColumnsToStr(szColumnList, ARRAYSIZE(szColumnList), defaultColumns, ARRAYSIZE(defaultColumns));
+ }
+
+ for (LPCTSTR pc = szColumnList, pBlock = NULL; TEXT('\0') != *pc; pc++)
+ {
+ if (TEXT('(') == *pc) pBlock = (pc + 1);
+ else if (TEXT(')') == *pc)
+ {
+ if (pBlock && pBlock != pc)
+ {
+ FVCOLUMN fvc;
+ fvc.mask = 0;
+ while (TEXT(' ') == *pBlock && pBlock != pc) pBlock++;
+ if (pBlock != pc)
+ {
+ fvc.id = StrToInt(pBlock);
+ while (TEXT(',') != *pBlock && pBlock != pc) pBlock++;
+ if (pBlock != pc)
+ {
+ while ((TEXT(',') == *pBlock || TEXT(' ') == *pBlock) && pBlock != pc) pBlock++;
+ if (pBlock != pc)
+ {
+ fvc.width = StrToInt(pBlock);
+ if (fvc.width > 0) fvc.mask |= FVCF_WIDTH;
+ }
+ }
+ FileView_InsertColumn(hctrl, &fvc);
+ }
+ }
+ pBlock = NULL;
+ }
+ }
+
+ INT orderBy, orderAsc;
+ Settings_GetInt(C_DATAVIEW, DVF_ORDERBY, &orderBy);
+ Settings_GetBool(C_DATAVIEW, DVF_ORDERASC, &orderAsc);
+ FileView_SetSort(hctrl, orderBy, orderAsc);
+}
+
+static void DataView_SaveFileViewColumns(HWND hdlg)
+{
+ UINT szOrder[256] = {0};
+ COLUMN szColumns[256] = {0};
+ TCHAR szBuffer[1024] = {0};
+
+ HWND hctrl = GetDlgItem(hdlg, IDC_FILEVIEW);
+ if (!hctrl) return;
+
+ INT count = FileView_GetColumnArray(hctrl, ARRAYSIZE(szOrder), szOrder);
+ for (int i = 0; i < count; i++)
+ {
+ szColumns[i].id = szOrder[i];
+ szColumns[i].width = FileView_GetColumnWidth(hctrl, szOrder[i]);
+ }
+ DataView_ColumnsToStr(szBuffer, ARRAYSIZE(szBuffer), szColumns, count);
+ Settings_SetString(C_DATAVIEW, DVF_COLUMNLIST, szBuffer);
+
+ DWORD sort = FileView_GetSort(hctrl);
+ Settings_SetInt(C_DATAVIEW, DVF_ORDERBY, LOWORD(sort));
+ Settings_SetBool(C_DATAVIEW, DVF_ORDERASC, HIWORD(sort));
+
+
+}
+static UINT DataView_ReadFileViewStyle()
+{
+ UINT style = 0;
+ INT nVal;
+
+ style = FVS_DETAILVIEW;
+ if (S_OK == Settings_GetInt(C_DATAVIEW, DVF_VIEWMODE, &nVal))
+ {
+ switch(nVal)
+ {
+ case FVS_ICONVIEW:
+ case FVS_LISTVIEW:
+ style = nVal;
+ break;
+ }
+ }
+
+ if (S_OK == Settings_GetInt(C_DATAVIEW, DVF_SHOWAUDIO, &nVal) && nVal) style |= FVS_SHOWAUDIO;
+ if (S_OK == Settings_GetInt(C_DATAVIEW, DVF_SHOWVIDEO, &nVal) && nVal) style |= FVS_SHOWVIDEO;
+ if (S_OK == Settings_GetInt(C_DATAVIEW, DVF_SHOWPLAYLIST, &nVal) && nVal) style |= FVS_SHOWPLAYLIST;
+ if (S_OK == Settings_GetInt(C_DATAVIEW, DVF_SHOWUNKNOWN, &nVal) && nVal) style |= FVS_SHOWUNKNOWN;
+ if (S_OK == Settings_GetInt(C_DATAVIEW, DVF_HIDEEXTENSION, &nVal) && nVal) style |= FVS_HIDEEXTENSION;
+ if (S_OK == Settings_GetInt(C_DATAVIEW, DVF_IGNOREHIDDEN, &nVal) && nVal) style |= FVS_IGNOREHIDDEN;
+ if (S_OK == Settings_GetInt(C_GLOBAL, GF_ENQUEUEBYDEFAULT, &nVal) && nVal)
+ style |= FVS_ENQUEUE;
+
+ return style;
+}
+
+static void DataView_SaveFileViewStyle(UINT uStyle)
+{
+ Settings_SetInt(C_DATAVIEW, DVF_VIEWMODE, (FVS_VIEWMASK & uStyle));
+ Settings_SetBool(C_DATAVIEW, DVF_SHOWAUDIO, (FVS_SHOWAUDIO & uStyle));
+ Settings_SetBool(C_DATAVIEW, DVF_SHOWVIDEO, (FVS_SHOWVIDEO & uStyle));
+ Settings_SetBool(C_DATAVIEW, DVF_SHOWPLAYLIST, (FVS_SHOWPLAYLIST & uStyle));
+ Settings_SetBool(C_DATAVIEW, DVF_SHOWUNKNOWN, (FVS_SHOWUNKNOWN & uStyle));
+ Settings_SetBool(C_DATAVIEW, DVF_HIDEEXTENSION, (FVS_HIDEEXTENSION & uStyle));
+ Settings_SetBool(C_DATAVIEW, DVF_IGNOREHIDDEN, (FVS_IGNOREHIDDEN & uStyle));
+}
+
+static BOOL DataView_OnInitDialog(HWND hdlg, HWND hwndFocus, LPARAM lParam)
+{
+ RECT rc;
+ HWND hctrl;
+
+ DATAVIEW *pdv = (DATAVIEW*)calloc(1, sizeof(DATAVIEW));
+ if (pdv)
+ {
+ pdv->cLetter = (CHAR)lParam;
+ pdv->szSongInfoCache[0] = L'\0';
+ SetPropW(hdlg, DATAVIEW_PROPW, pdv);
+ }
+
+ GetClientRect(hdlg, &rc);
+
+ MLSkinWindow2(plugin.hwndLibraryParent, hdlg, SKINNEDWND_TYPE_AUTO, SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS);
+
+ MLFILEVIEWCREATESTRUCT fvcs;
+ fvcs.hwndParent = hdlg;
+ fvcs.hwndInsertAfter = hdlg;
+ fvcs.fStyle = DataView_ReadFileViewStyle();
+ fvcs.x = 0;
+ fvcs.y = 0;
+ fvcs.cx = rc.right - rc.left;
+ fvcs.cy = rc.bottom - rc.top - 4;
+ hctrl = (HWND)SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_CREATEFILEVIEW, (WPARAM)&fvcs);
+
+ if (hctrl)
+ {
+ wchar_t szRoot[] = L"x:";
+ if (pdv) szRoot[0] = pdv->cLetter;
+ //szRoot[0] = L'C';
+
+ FileView_SetRoot(hctrl, szRoot);
+ SetWindowLongPtrW(hctrl, GWLP_ID, IDC_FILEVIEW);
+
+ DataView_LoadFileViewColumns(hdlg);
+ SetWindowLongPtrW(hctrl, GWL_STYLE, GetWindowLongPtrW(hctrl, GWL_STYLE) | WS_VISIBLE);
+
+ INT nPos;
+ if (S_OK == Settings_GetInt(C_DATAVIEW, DVF_DIVIDERPOS, &nPos))
+ FileView_SetDividerPos(hctrl, nPos, FVRF_NOREDRAW);
+ }
+
+ ViewContainer_CreateCmdBar(GetParent(hdlg), hdlg, IDD_COMMANDBAR_DATA, DataCmdBar_DialogProc, (ULONG_PTR)hctrl);
+
+ DataView_NotifyInfoWindow(hdlg, -1, TRUE); // ignore cache
+ SendMessage(hdlg, WM_COMMAND, MAKEWPARAM(ID_DRIVE_MODE_CHANGED, 0), 0L);
+
+ TCHAR szRoot[MAX_PATH] = {0};
+ if (hctrl && S_OK == Settings_ReadString(C_DATAVIEW, DVF_LASTFOLDER, szRoot, ARRAYSIZE(szRoot)))
+ FileView_SetCurrentPath(hctrl, szRoot, TRUE);
+
+ return FALSE;
+}
+
+static void DataView_OnDestroy(HWND hdlg)
+{
+ KillTimer(hdlg, IDT_NOTIFYINFO);
+ DataView_NotifyInfoWindow(hdlg, -1, FALSE);
+
+ HWND hView = GetDlgItem(hdlg, IDC_FILEVIEW);
+ if (hView)
+ {
+ WCHAR szRoot[MAX_PATH] = {0};
+ FileView_GetCurrentPath(hView, szRoot, ARRAYSIZE(szRoot));
+ Settings_SetString(C_DATAVIEW, DVF_LASTFOLDER, szRoot);
+
+ DataView_SaveFileViewColumns(hdlg);
+ DataView_SaveFileViewStyle(FileView_GetStyle(hView));
+
+ Settings_SetInt(C_DATAVIEW, DVF_DIVIDERPOS, FileView_GetDividerPos(hView));
+
+ }
+ DATAVIEW *pdv = GetDataView(hdlg);
+ if (pdv)
+ {
+ RemovePropW(hdlg, DATAVIEW_PROPW);
+ free(pdv);
+ }
+ ViewContainer_DestroyCmdBar(GetParent(hdlg));
+}
+
+static void CALLBACK DataViewTimer_OnStatusUpdateElapsed(HWND hdlg, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ KillTimer(hdlg, idEvent);
+ DataViewStatus_UpdateText(hdlg);
+
+ BOOL enablePlay = FALSE;
+ HWND hctrl = GetDlgItem(hdlg, IDC_FILEVIEW);
+
+ if (hctrl && FileView_GetSelectedCount(hctrl) > 0)
+ {
+ enablePlay = (-1 != FileView_GetNextFile(hctrl, -1, FVNF_PLAYABLE | FVNF_SELECTED));
+ }
+
+ HWND hbar = ViewContainer_GetCmdBar(GetParent(hdlg));
+ if (hbar)
+ {
+ if (NULL != (hctrl = GetDlgItem(hbar, IDC_BTN_PLAYEX))) EnableWindow(hctrl, enablePlay);
+ if (NULL != (hctrl = GetDlgItem(hbar, IDC_BTN_COPY))) EnableWindow(hctrl, enablePlay);
+ }
+
+}
+
+static void CALLBACK DataViewTimer_OnNotifyInfoElapsed(HWND hdlg, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ KillTimer(hdlg, IDT_NOTIFYINFO);
+ HWND hFileView = GetDlgItem(hdlg, IDC_FILEVIEW);
+
+ INT iFile = -1;
+ if (hFileView)
+ {
+ iFile = FileView_GetSelectedCount(hFileView);
+ if (1 != iFile) iFile = -1;
+ else iFile = FileView_GetNextFile(hFileView, -1, FVNF_FOCUSED);
+ }
+
+ DataView_NotifyInfoWindow(hdlg, iFile, FALSE);
+}
+
+static void DataView_OnWindowPosChanged(HWND hdlg, WINDOWPOS *pwp)
+{
+ if (0 == (SWP_NOSIZE & pwp->flags))
+ {
+ HWND hctrl;
+ RECT rc, rw;
+ GetClientRect(hdlg, &rc);
+
+ hctrl = GetDlgItem(hdlg, IDC_FILEVIEW);
+ if (hctrl && GetWindowRect(hctrl, &rw))
+ {
+ MapWindowPoints(HWND_DESKTOP, hdlg, (POINT*)&rw, 2);
+ SetWindowPos(hctrl, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top,
+ SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE | ((SWP_NOREDRAW | SWP_NOCOPYBITS) & pwp->flags));
+ }
+ }
+}
+
+
+
+static void DataView_OnCopySelection(HWND hdlg)
+{
+ LPWSTR *ppszFiles = NULL;
+ ULONGLONG *pFSizes;
+ INT count, iFile;
+ size_t cchPath, cchFile;
+ WCHAR szPath[MAX_PATH] = {0};
+ FVITEM file = {0};
+
+ HWND hctrl = GetDlgItem(hdlg, IDC_FILEVIEW);
+ if (!hctrl) return;
+
+
+ count = FileView_GetSelectedCount(hctrl);
+ if (count < 1) return;
+
+ if (!FileView_GetCurrentPath(hctrl, szPath, sizeof(szPath)/sizeof(szPath[0])) ||
+ S_OK != StringCchLengthW(szPath, sizeof(szPath)/sizeof(szPath[0]), &cchPath)) return;
+
+ ppszFiles = (LPWSTR*)CoTaskMemAlloc(sizeof(LPWSTR)*count);
+ pFSizes = (ULONGLONG*)CoTaskMemAlloc(sizeof(ULONGLONG)*count);
+ if (!ppszFiles || !pFSizes)
+ {
+ if (ppszFiles) CoTaskMemFree(ppszFiles);
+ if (pFSizes) CoTaskMemFree(pFSizes);
+ return;
+ }
+
+ iFile = -1;
+ count = 0;
+ file.mask = FVIF_TEXT | FVIF_SIZE;
+ TCHAR szBuffer[MAX_PATH] = {0};
+ while (-1 != (iFile = FileView_GetNextFile(hctrl, iFile, FVNF_SELECTED | FVNF_PLAYABLE)))
+ {
+ file.pszText = szBuffer;
+ file.cchTextMax = ARRAYSIZE(szBuffer);
+ if (FileView_GetFile(hctrl, iFile, &file))
+ {
+ cchFile = (file.pszText) ? lstrlenW(file.pszText) : 0;
+ if (cchFile)
+ {
+ pFSizes[count] = (ULONGLONG)(((__int64)file.dwSizeHigh << 32) | file.dwSizeLow);
+ ppszFiles[count] = (LPWSTR)CoTaskMemAlloc(sizeof(WCHAR)*(cchPath + cchFile + 4));
+ if (ppszFiles[count])
+ {
+ PathCombineW(ppszFiles[count], szPath, file.pszText);
+ count++;
+ }
+ }
+ }
+ }
+
+ if (!MLDisc_CopyFiles(hdlg, ppszFiles, pFSizes, count))
+ {
+ if (ppszFiles)
+ {
+ for (int i = 0; i < count; i++) CoTaskMemFree(ppszFiles[i]);
+ CoTaskMemFree(ppszFiles);
+ }
+ if (pFSizes) CoTaskMemFree(pFSizes);
+ }
+}
+
+static void DataView_OnDriveModeChanged(HWND hdlg)
+{
+ DATAVIEW *pdv = GetDataView(hdlg);
+ if (!pdv) return;
+
+ HWND hbar;
+ if (NULL != (hbar = ViewContainer_GetCmdBar(GetParent(hdlg))))
+ {
+ HWND hctrl;
+ if (NULL != (hctrl = GetDlgItem(hbar, IDC_BTN_EJECT)))
+ {
+ UINT cMode = DriveManager_GetDriveMode(pdv->cLetter);
+ EnableWindow(hctrl, (DM_MODE_READY == cMode));
+ }
+ }
+
+}
+
+static void DataView_OnCommand(HWND hdlg, INT eventId, INT ctrlId, HWND hwndCtrl)
+{
+ switch(ctrlId)
+ {
+ case ID_COPY_SELECTION:
+ DataView_OnCopySelection(hdlg);
+ break;
+ case ID_DRIVE_MODE_CHANGED:
+ DataView_OnDriveModeChanged(hdlg);
+ break;
+ }
+}
+
+static void FileView_OnFolderChanged(HWND hdlg, NMHDR *phdr)
+{
+ DataView_NotifyInfoWindow(hdlg, -1, FALSE);
+ SetTimer(hdlg, IDT_UPDATESTATUS, DELAY_UPDATESTATUS, DataViewTimer_OnStatusUpdateElapsed);
+}
+
+static void FileView_OnStatusChanged(HWND hdlg, NMHDR *phdr)
+{
+ SetTimer(hdlg, IDT_UPDATESTATUS, DELAY_UPDATESTATUS, DataViewTimer_OnStatusUpdateElapsed);
+}
+
+static void FileView_OnFileStateChanged(HWND hdlg, NMFVSTATECHANGED *pnmsc)
+{
+ SetTimer(hdlg, IDT_UPDATESTATUS, DELAY_UPDATESTATUS, DataViewTimer_OnStatusUpdateElapsed);
+ SetTimer(hdlg, IDT_NOTIFYINFO, DELAY_NOTIFYINFO, DataViewTimer_OnNotifyInfoElapsed);
+}
+
+static void FileView_OnDoubleClick(HWND hdlg, NMFVFILEACTIVATE *pnma)
+{
+ if (-1 == pnma->iFile) return;
+
+ HWND hctrl = GetDlgItem(hdlg, IDC_FILEVIEW);
+ if (!hctrl) return;
+
+ SendMessageW(hctrl, WM_COMMAND,
+ MAKEWPARAM(FileView_GetActionCommand(hctrl, (FVS_ENQUEUE & (FileView_GetStyle(hctrl)) ? FVA_ENQUEUE : FVA_PLAY)), 0), 0L);
+}
+
+static void FileView_OnUninitOptionsMenu(HWND hdlg, HMENU hMenu, UINT uCommand)
+{
+ HWND hParent = GetParent(hdlg);
+ if (!hMenu || !hParent || !ViewContainer_GetMiniInfoEnabled(hParent)) return;
+ INT index = GetMenuItemCount(hMenu);
+
+ MENUITEMINFOW mii = { sizeof(MENUITEMINFOW), };
+ mii.fMask = MIIM_ID;
+ while (index--)
+ {
+ if (GetMenuItemInfoW(hMenu, index, TRUE, &mii) && ID_MINIINFO_SHOW == mii.wID)
+ {
+ if (DeleteMenu(hMenu, index, MF_BYPOSITION))
+ DeleteMenu(hMenu, --index, MF_BYPOSITION);
+ break;
+ }
+ }
+}
+
+static void FileView_OnInitOptionsMenu(HWND hdlg, HMENU hMenu)
+{
+ INT index;
+ HWND hParent;
+
+ hParent = GetParent(hdlg);
+ if (!hMenu || !hParent || !ViewContainer_GetMiniInfoEnabled(hParent)) return;
+
+ MENUITEMINFOW mii = { sizeof(MENUITEMINFOW), };
+
+ index = GetMenuItemCount(hMenu);
+
+ mii.fMask = MIIM_TYPE;
+ mii.fType = MFT_SEPARATOR;
+
+ if (InsertMenuItemW(hMenu, index, TRUE, &mii))
+ {
+ wchar_t szText[1024] = {0};
+ WASABI_API_LNGSTRINGW_BUF(IDS_SHOW_INFO, szText, sizeof(szText)/sizeof(szText[0]));
+
+ if (WASABI_API_APP)
+ {
+ HACCEL szAccel[24] = {0};
+ INT c = WASABI_API_APP->app_getAccelerators(GetParent(hdlg), szAccel, sizeof(szAccel)/sizeof(szAccel[0]), FALSE);
+ AppendShortcutText(szText, sizeof(szText)/sizeof(szText[0]), ID_MINIINFO_SHOW, szAccel, c, MSF_REPLACE);
+ }
+
+ index++;
+ mii.fMask = MIIM_STRING | MIIM_STATE | MIIM_ID;
+ mii.dwTypeData = szText;
+ mii.wID = ID_MINIINFO_SHOW;
+ mii.fState = (ViewContainer_GetMiniInfoVisible(hParent)) ? MFS_CHECKED : MFS_UNCHECKED;
+
+ InsertMenuItemW(hMenu, index, TRUE, &mii);
+ }
+}
+
+static void FileView_OnInitFileContextMenu(HWND hdlg, HMENU hMenu, HWND hView)
+{
+ HWND hbar = ViewContainer_GetCmdBar(GetParent(hdlg));
+ HWND hButton = (hbar) ? GetDlgItem(hbar, IDC_BTN_COPY) : NULL;
+ if (!hButton) return;
+
+ wchar_t szText[1024] = {0};
+ GetWindowTextW(hButton, szText, sizeof(szText)/sizeof(szText[0]));
+
+ if (WASABI_API_APP)
+ {
+ HACCEL szAccel[24] = {0};
+ INT c = WASABI_API_APP->app_getAccelerators(GetParent(hdlg), szAccel, sizeof(szAccel)/sizeof(szAccel[0]), FALSE);
+ AppendShortcutText(szText, sizeof(szText)/sizeof(szText[0]), ID_COPY_SELECTION, szAccel, c, MSF_REPLACE);
+ }
+
+ MENUITEMINFOW mii = { sizeof(MENUITEMINFOW), };
+ mii.fMask = MIIM_STRING | MIIM_STATE | MIIM_ID;
+ mii.dwTypeData = szText;
+ mii.wID = ID_COPY_SELECTION; // TODO: make id uniqueue
+ mii.fState = (IsWindowEnabled(hButton)) ? MFS_ENABLED : MFS_DISABLED;
+
+ InsertMenuItemW(hMenu, 2, TRUE, &mii);
+}
+
+static BOOL FileView_OnInitMenu(HWND hdlg, NMFVMENU *pnm)
+{
+ switch(pnm->uMenuType)
+ {
+ case FVMENU_OPTIONS: FileView_OnInitOptionsMenu(hdlg, pnm->hMenu); break;
+ case FVMENU_FILEOPCONTEXT: FileView_OnInitFileContextMenu(hdlg, pnm->hMenu, pnm->hdr.hwndFrom); break;
+ }
+ return FALSE;
+}
+
+static void FileView_OnUninitMenu(HWND hdlg, NMFVMENU *pnm)
+{
+ switch(pnm->uMenuType)
+ {
+ case FVMENU_OPTIONS: FileView_OnUninitOptionsMenu(hdlg, pnm->hMenu, pnm->uCommand); break;
+ case FVMENU_FILEOPCONTEXT: DeleteMenu(pnm->hMenu, ID_COPY_SELECTION, MF_BYCOMMAND); break;
+ }
+}
+
+static BOOL FileView_OnMenuCommand(HWND hdlg, NMFVMENU *pnm)
+{
+ switch(pnm->uCommand)
+ {
+ case ID_MINIINFO_SHOW:
+ SendMessageW(GetParent(hdlg), WM_COMMAND, MAKEWPARAM(ID_MINIINFO_SHOW, 0), 0L);
+ return TRUE;
+ case ID_COPY_SELECTION:
+ SendMessageW(hdlg, WM_COMMAND, MAKEWPARAM(ID_COPY_SELECTION, 0), 0L);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static INT_PTR DataView_OnNotify(HWND hdlg, INT ctrlId, NMHDR *phdr)
+{
+ switch(phdr->idFrom)
+ {
+ case IDC_FILEVIEW:
+ switch(phdr->code)
+ {
+ case FVN_FOLDERCHANGED: FileView_OnFolderChanged(hdlg, phdr); break;
+ case FVN_STATECHANGED: FileView_OnFileStateChanged(hdlg, (NMFVSTATECHANGED*)phdr); break;
+ case FVN_STATUSCHANGED: FileView_OnStatusChanged(hdlg, phdr); break;
+ case NM_DBLCLK: FileView_OnDoubleClick(hdlg, (NMFVFILEACTIVATE*)phdr); break;
+ case FVN_INITMENU: return FileView_OnInitMenu(hdlg, (NMFVMENU*)phdr);
+ case FVN_UNINITMENU: FileView_OnUninitMenu(hdlg, (NMFVMENU*)phdr); break;
+ case FVN_MENUCOMMAND: return FileView_OnMenuCommand(hdlg, (NMFVMENU*)phdr);
+ }
+ break;
+ }
+ return 0;
+}
+
+
+static void DataView_OnQueryInfo(HWND hdlg)
+{
+ KillTimer(hdlg, IDT_NOTIFYINFO);
+ DataView_NotifyInfoWindow(hdlg, -1, TRUE);
+ SetTimer(hdlg, IDT_NOTIFYINFO, DELAY_NOTIFYINFO, DataViewTimer_OnNotifyInfoElapsed);
+}
+
+static void DataView_OnDisplayChange(HWND hdlg)
+{
+ HWND hctrl = GetDlgItem(hdlg, IDC_FILEVIEW);
+ if (!hctrl) return;
+ BOOL bVal;
+ Settings_GetBool(C_GLOBAL, GF_ENQUEUEBYDEFAULT, &bVal);
+ FileView_SetStyle(hctrl, (bVal) ? FVS_ENQUEUE : 0L, FVS_ENQUEUE);
+}
+
+static INT_PTR WINAPI DlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG: return DataView_OnInitDialog(hdlg, (HWND)wParam, lParam);
+ case WM_DESTROY: DataView_OnDestroy(hdlg); break;
+ case WM_WINDOWPOSCHANGED: DataView_OnWindowPosChanged(hdlg, (WINDOWPOS*)lParam); return TRUE;
+ case WM_COMMAND: DataView_OnCommand(hdlg, HIWORD(wParam), LOWORD(wParam), (HWND)lParam); break;
+ case WM_NOTIFY: MSGRESULT(hdlg, DataView_OnNotify(hdlg, (INT)wParam, (LPNMHDR) lParam));
+ case WM_QUERYFILEINFO: DataView_OnQueryInfo(hdlg); break;
+ case WM_DISPLAYCHANGE: DataView_OnDisplayChange(hdlg); break;
+
+ }
+
+ return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/view_info.cpp b/Src/Plugins/Library/ml_disc/view_info.cpp
new file mode 100644
index 00000000..609405f9
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/view_info.cpp
@@ -0,0 +1,127 @@
+#include "main.h"
+#include "./resource.h"
+#include "../nu/DialogSkinner.h"
+
+#include <windowsx.h>
+
+static HBRUSH hbBack = NULL;
+
+static INT_PTR WINAPI DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+// public function
+HWND CreateInfoWindow(HWND hwndParent, CHAR cLetter)
+{
+ return WASABI_API_CREATEDIALOGPARAMW(IDD_VIEW_INFO, hwndParent, DlgProc, (LPARAM)cLetter);
+}
+
+static INT_PTR Window_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
+{
+ SendMessageW(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(IDC_BTN_SHOWINFO, BN_EX_GETTEXT), (LPARAM)GetDlgItem(hwnd, IDC_BTN_SHOWINFO));
+ return 0;
+}
+
+static void Window_OnDestroy(HWND hwnd)
+{
+ if (hbBack)
+ {
+ DeleteObject(hbBack);
+ hbBack = NULL;
+ }
+}
+
+static void Window_OnDisplayChange(HWND hwnd, INT dpi, INT resX, INT resY)
+{
+ if (hbBack)
+ {
+ DeleteObject(hbBack);
+ hbBack = NULL;
+ }
+ RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
+}
+
+static void Window_OnWindowPosChanged(HWND hwnd, WINDOWPOS *pwp)
+{
+ HWND hwndCtrl;
+ RECT rw;
+
+ hwndCtrl = GetDlgItem(hwnd, IDC_BTN_SHOWINFO);
+ if(hwndCtrl)
+ {
+ GetWindowRect(hwndCtrl, &rw);
+ OffsetRect(&rw, -rw.left, -rw.top);
+ SetWindowPos(hwndCtrl, NULL, pwp->cx - rw.right, pwp->cy - rw.bottom, 0, 0,
+ SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS);
+ }
+
+ hwndCtrl = GetDlgItem(hwnd, IDC_LBL_TEXT);
+ if(hwndCtrl)
+ {
+ GetWindowRect(hwndCtrl, &rw);
+ MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rw, 2);
+ SetWindowPos(hwndCtrl, NULL, 0, 0, pwp->cx - rw.left - 2, pwp->cy - 22 - rw.top,
+ SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS);
+ }
+ if (0 == (SWP_NOREDRAW & pwp->flags))
+ {
+ InvalidateRect(hwnd, NULL, TRUE);
+ RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
+ }
+}
+
+static void Window_OnCommand(HWND hwnd, INT eventId, INT ctrlId, HWND hwndCtrl)
+{
+ switch(ctrlId)
+ {
+ case IDC_BTN_SHOWINFO:
+ switch(eventId)
+ {
+ case BN_CLICKED:
+ SendMessageW(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(ctrlId, eventId),(LPARAM)hwndCtrl);
+ break;
+ }
+ break;
+ }
+}
+
+static HBRUSH Window_OnStaticColor(HWND hwnd, HDC hdc)
+{
+ if (!hbBack) hbBack = CreateSolidBrush(dialogSkinner.Color(WADLG_ITEMBG));
+ SetTextColor(hdc, dialogSkinner.Color(WADLG_ITEMFG));
+ SetBkColor(hdc, dialogSkinner.Color(WADLG_ITEMBG));
+ return hbBack;
+}
+
+static void Window_OnPaint(HWND hwnd)
+{
+ int tab[] = { IDC_LBL_TEXT | DCW_SUNKENBORDER};
+ dialogSkinner.Draw(hwnd, tab, 1);
+}
+
+static void Window_OnQueryInfo(HWND hwnd)
+{
+ HWND hwndParent;
+ hwndParent = GetParent(hwnd);
+ if (hwndParent) SendMessageW(hwndParent, WM_SHOWFILEINFO, (WPARAM)WISF_NORMAL, (LPARAM)L"");
+}
+
+static INT_PTR WINAPI DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ INT_PTR result;
+
+ if (WM_CTLCOLORSTATIC == uMsg) return (INT_PTR)Window_OnStaticColor(hwnd, (HDC)wParam);
+
+ result = dialogSkinner.Handle(hwnd, uMsg, wParam, lParam);
+ if (result) return result;
+
+ switch(uMsg)
+ {
+ case WM_INITDIALOG: return (INT_PTR)Window_OnInitDialog(hwnd, (HWND)wParam, lParam);
+ case WM_DESTROY: Window_OnDestroy(hwnd); break;
+ case WM_DISPLAYCHANGE: Window_OnDisplayChange(hwnd, (INT)wParam, LOWORD(lParam), HIWORD(lParam)); break;
+ case WM_WINDOWPOSCHANGED: Window_OnWindowPosChanged(hwnd, (WINDOWPOS*)lParam); break;
+ case WM_COMMAND: Window_OnCommand(hwnd, HIWORD(wParam), LOWORD(wParam), (HWND)lParam); break;
+ case WM_PAINT: Window_OnPaint(hwnd); break;
+ case WM_QUERYFILEINFO: Window_OnQueryInfo(hwnd); break;
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/view_ripburn.cpp b/Src/Plugins/Library/ml_disc/view_ripburn.cpp
new file mode 100644
index 00000000..b8e55e9c
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/view_ripburn.cpp
@@ -0,0 +1,567 @@
+#include "main.h"
+#include "resource.h"
+#include "../nu/DialogSkinner.h"
+#include "../nu/ChildSizer.h"
+#include "config.h"
+#include ".\driveListBox.h"
+#include ".\infoBox.h"
+#include ".\primosdk_helper.h"
+#include <strsafe.h>
+
+static ChildWndResizeItem ripburn_rlist[]=
+{
+ {IDC_LBL_DRIVES, 0x0000},
+ {IDC_LIST_DRIVES, 0x0001},
+ {IDC_LBL_INFO_DRIVE, 0x0010},
+ {IDC_LBL_INFO_MEDIUM, 0x0011},
+ {IDC_LBL_DRIVE_LETTER_VAL, 0x0010},
+ {IDC_LBL_DRIVE_DESCRIPTION_VAL,0x0010},
+ {IDC_LBL_DRIVE_BUS_VAL, 0x0010},
+ {IDC_LBL_DRIVE_TYPES_VAL, 0x0010},
+ {IDC_LBL_MEDIUM_UPDATE, 0x0010},
+ {IDC_LBL_MEDIUM_CAPACITY_VAL, 0x0010},
+ {IDC_LBL_MEDIUM_TRACKN_VAL, 0x0010},
+ {IDC_LBL_MEDIUM_ERASEABLE_VAL, 0x0010},
+ {IDC_LBL_MEDIUM_RECORDABLE_VAL,0x0010},
+ {IDC_LBL_MEDIUM_FORMAT_VAL, 0x0010},
+ {IDC_LBL_MEDIUM_ADDINFO_VAL, 0x0010},
+ {IDC_LBL_MEDIUM_DISC_VAL, 0x0010},
+ {IDC_BTN_REFRESH, 0x0101},
+};
+
+static DriveListBox *driveListBox = NULL;
+static MLInfoBox *driveInfo = NULL;
+static MLInfoBox *mediumInfo = NULL;
+
+static HBRUSH lblHeaderBrush = NULL;
+static HBRUSH lblValueBrush = NULL;
+
+static UINT msgNotify = 0;
+static CHAR activeDrive = 0x00;
+
+static void CALLBACK FreeAsyncParam(DM_NOTIFY_PARAM *phdr)
+{
+ DM_UNITINFO_PARAM *pui = NULL;
+ DM_UNITINFO2_PARAM *pui2 = NULL;
+ if(!phdr) return;
+
+ switch(phdr->opCode)
+ {
+ case DMOP_UNITINFO:
+ pui = (DM_UNITINFO_PARAM*)phdr;
+ if (pui->pszDesc) free(pui->pszDesc);
+ break;
+ case DMOP_UNITINFO2:
+ pui2 = (DM_UNITINFO2_PARAM*)phdr;
+ if (pui2->pdwTypes) free(pui2->pdwTypes);
+ break;
+ }
+ free(phdr);
+}
+
+static void UpdateDriveInfo(HWND hwndDlg, CHAR cLetter)
+{
+ DM_NOTIFY_PARAM header = {0};
+ DM_UNITINFO_PARAM *pui = NULL;
+ DM_UNITINFO2_PARAM *pui2 = NULL;
+ DM_DISCINFOEX_PARAM *pdi = NULL;
+ DM_DISCINFO2_PARAM *pdi2 = NULL;
+ wchar_t message[128] = {0};
+
+ activeDrive = cLetter;
+
+ if(!PrimoSDKHelper_IsLoaded())
+ {
+ WASABI_API_LNGSTRINGW_BUF(IDS_NO_INFO_AVAILABLE,message,128);
+ }
+
+ SetDlgItemTextA(hwndDlg, IDC_LBL_DRIVE_LETTER_VAL, &cLetter);
+ SetDlgItemText(hwndDlg, IDC_LBL_DRIVE_DESCRIPTION_VAL, message);
+ SetDlgItemText(hwndDlg, IDC_LBL_DRIVE_BUS_VAL, message);
+ SetDlgItemText(hwndDlg, IDC_LBL_DRIVE_TYPES_VAL, message);
+ SetDlgItemTextA(hwndDlg, IDC_LBL_MEDIUM_DISC_VAL, NULL);
+ SetDlgItemTextA(hwndDlg, IDC_LBL_MEDIUM_CAPACITY_VAL, NULL);
+ SetDlgItemTextA(hwndDlg, IDC_LBL_MEDIUM_FORMAT_VAL, NULL);
+ SetDlgItemTextA(hwndDlg, IDC_LBL_MEDIUM_ERASEABLE_VAL, NULL);
+ SetDlgItemTextA(hwndDlg, IDC_LBL_MEDIUM_RECORDABLE_VAL, NULL);
+ SetDlgItemTextA(hwndDlg, IDC_LBL_MEDIUM_TRACKN_VAL, NULL);
+ SetDlgItemTextA(hwndDlg, IDC_LBL_MEDIUM_ADDINFO_VAL, NULL);
+
+ if (0 == activeDrive) return;
+
+ ZeroMemory(&header, sizeof(DM_NOTIFY_PARAM));
+
+ header.callback = (INT_PTR)hwndDlg;
+ header.uMsg = msgNotify;
+ header.cLetter = cLetter;
+ header.fnFree = FreeAsyncParam;
+
+ // request unitinfo
+ pui = (DM_UNITINFO_PARAM*)calloc(1, sizeof(DM_UNITINFO_PARAM));
+ if (pui)
+ {
+ CopyMemory(&pui->header, &header, sizeof(DM_NOTIFY_PARAM));
+ pui->header.fFlags = DMF_DESCRIPTION;
+ pui->cchDesc = 128;
+ pui->pszDesc = (CHAR*)calloc(pui->cchDesc, sizeof(CHAR));
+ DriveManager_GetUnitInfo(pui);
+ }
+
+ // request unitinfo2
+ pui2 = (DM_UNITINFO2_PARAM*)calloc(1, sizeof(DM_UNITINFO2_PARAM));
+ if (pui2)
+ {
+ CopyMemory(&pui2->header, &header, sizeof(DM_NOTIFY_PARAM));
+ pui2->header.fFlags = DMF_TYPES;
+ pui2->nTypes = 32;
+ pui2->pdwTypes = (DWORD*)calloc(pui2->nTypes, sizeof(DWORD));
+ DriveManager_GetUnitInfo2(pui2);
+ }
+
+ // request discinfoex
+ pdi = (DM_DISCINFOEX_PARAM*)calloc(1, sizeof(DM_DISCINFOEX_PARAM));
+ if (pdi)
+ {
+ CopyMemory(&pdi->header, &header, sizeof(DM_NOTIFY_PARAM));
+ pdi->header.fFlags = DMF_DRIVEMODE_DAO | DMF_MEDIUMTYPE | DMF_MEDIUMFORMAT | DMF_TRACKS | DMF_USED | DMF_FREE;
+ DriveManager_GetDiscInfoEx(pdi);
+ }
+
+ // request discinfo2
+ pdi2 = (DM_DISCINFO2_PARAM*)calloc(1, sizeof(DM_DISCINFO2_PARAM));
+ if (pdi2)
+ {
+ CopyMemory(&pdi2->header, &header, sizeof(DM_NOTIFY_PARAM));
+ pdi2->header.fFlags = DMF_MEDIUM | DMF_MEDIUMEX;
+ DriveManager_GetDiscInfo2(pdi2);
+ }
+}
+
+static BOOL CALLBACK EnumerateNavItemsCB(HNAVITEM hItem, DRIVE *pDrive, LPARAM param)
+{
+ if (!param) return FALSE;
+ if (pDrive) PostMessageW((HWND)param, msgNotify, (WPARAM)DMW_DRIVEADDED, (LPARAM)pDrive->cLetter);
+ return TRUE;
+}
+
+static void SwitchControlVisible(HWND hwndDlg, INT ctrlId, RECT *prcParent, BOOL hide, BOOL bInvalidate = FALSE)
+{
+ HWND hwndCtrl = GetDlgItem(hwndDlg, ctrlId);
+
+ if (hwndCtrl)
+ {
+ if (hide) ShowWindow(hwndCtrl, SW_HIDE);
+ else
+ {
+ RECT rc;
+ GetWindowRect(hwndCtrl, &rc);
+
+ BOOL bVisible = ((prcParent->right > rc.right) && (prcParent->bottom > rc.bottom));
+ if (bVisible != IsWindowVisible(hwndCtrl)) ShowWindow(hwndCtrl, (bVisible) ? SW_SHOWNORMAL : SW_HIDE);
+ if (bVisible && bInvalidate) InvalidateRect(hwndCtrl, NULL, TRUE);
+ }
+ }
+}
+
+static void ripburn_OnDisplayChanges(HWND hwndDlg)
+{
+ driveListBox->SetColors(dialogSkinner.Color(WADLG_ITEMBG),
+ dialogSkinner.Color(WADLG_ITEMBG),
+ dialogSkinner.Color(WADLG_ITEMFG),
+ dialogSkinner.Color(WADLG_ITEMFG),
+ dialogSkinner.Color(WADLG_WNDFG));
+ driveInfo->SetColors( dialogSkinner.Color(WADLG_ITEMBG),
+ dialogSkinner.Color(WADLG_LISTHEADER_FONTCOLOR),
+ dialogSkinner.Color(WADLG_LISTHEADER_BGCOLOR));
+ mediumInfo->SetColors( dialogSkinner.Color(WADLG_ITEMBG),
+ dialogSkinner.Color(WADLG_LISTHEADER_FONTCOLOR),
+ dialogSkinner.Color(WADLG_LISTHEADER_BGCOLOR));
+
+ if (lblHeaderBrush) DeleteObject(lblHeaderBrush);
+ lblHeaderBrush = NULL;
+
+ if (lblValueBrush) DeleteObject(lblValueBrush);
+ lblValueBrush = NULL;
+
+ // fixes the view not updating correctly on colour theme changes, etc
+ // NOTE: ideal would be using a LayoutWindows(..) method which would
+ // help to resolve this as things can be offloaded to gen_ml...
+ RECT rc;
+ GetClientRect(hwndDlg, &rc);
+ RedrawWindow(hwndDlg, &rc, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW | RDW_UPDATENOW);
+}
+
+static void ripburn_OnInitDialog(HWND hwndDlg)
+{
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_LIST_DRIVES);
+
+ driveListBox = new DriveListBox(IDC_LIST_DRIVES);
+ driveListBox->SetImages(plugin.hDllInstance, IDB_LISTBOX_BACK, IDB_LISTITEM_CDDRIVE);
+ driveListBox->Init(hwndList);
+
+ driveInfo = new MLInfoBox();
+ driveInfo->Init(GetDlgItem(hwndDlg, IDC_LBL_INFO_DRIVE));
+
+ mediumInfo = new MLInfoBox();
+ mediumInfo->Init(GetDlgItem(hwndDlg, IDC_LBL_INFO_MEDIUM));
+
+ UpdateDriveInfo(hwndDlg, 0);
+
+ childSizer.Init(hwndDlg,ripburn_rlist,sizeof(ripburn_rlist)/sizeof(ripburn_rlist[0]));
+ ripburn_OnDisplayChanges(hwndDlg);
+
+ if (!msgNotify) msgNotify = RegisterWindowMessageW(L"ripburn_notify_msg");
+
+ Plugin_EnumerateNavItems(EnumerateNavItemsCB, (LPARAM)hwndDlg);
+ Plugin_RegisterListener(hwndDlg, msgNotify, 0);
+}
+
+static void ripburn_OnDestroy(HWND hwndDlg)
+{
+ Plugin_UnregisterListener(hwndDlg);
+
+ HWND hwndLB = GetDlgItem(hwndDlg, IDC_LIST_DRIVES);
+ if (hwndLB)
+ {
+ INT index = (int)(INT_PTR)SendMessageW(hwndLB, LB_GETCURSEL, 0,0);
+ DWORD data = (LB_ERR != index) ? (DWORD)SendMessageW(hwndLB, LB_GETITEMDATA, index, 0) : 0;
+ if (data) g_config->WriteInt(L"last_drive", (CHAR)(0xFF & data));
+ }
+
+ if (lblHeaderBrush) DeleteObject(lblHeaderBrush);
+ lblHeaderBrush = NULL;
+ if (lblValueBrush) DeleteObject(lblValueBrush);
+ lblValueBrush = NULL;
+
+ if (driveListBox) delete(driveListBox);
+ driveListBox = NULL;
+ if (driveInfo) delete(driveInfo);
+ driveInfo = NULL;
+ if (mediumInfo) delete(mediumInfo);
+ mediumInfo = NULL;
+}
+
+static void ripburn_OnSize(HWND hwndDlg, int cx, int cy)
+{
+ RECT box;
+
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_LBL_INFO_DRIVE), &box);
+ BOOL hide = FALSE;
+
+ SwitchControlVisible(hwndDlg, IDC_LBL_DRIVE_LETTER, &box, FALSE);
+ SwitchControlVisible(hwndDlg, IDC_LBL_DRIVE_DESCRIPTION, &box, FALSE);
+ SwitchControlVisible(hwndDlg, IDC_LBL_DRIVE_BUS, &box, FALSE);
+ SwitchControlVisible(hwndDlg, IDC_LBL_DRIVE_TYPES,&box, FALSE);
+ SwitchControlVisible(hwndDlg, IDC_LBL_DRIVE_LETTER_VAL, &box, hide, TRUE);
+ SwitchControlVisible(hwndDlg, IDC_LBL_DRIVE_DESCRIPTION_VAL, &box, hide, TRUE);
+ SwitchControlVisible(hwndDlg, IDC_LBL_DRIVE_BUS_VAL, &box, hide, TRUE);
+ SwitchControlVisible(hwndDlg, IDC_LBL_DRIVE_TYPES_VAL, &box, hide, TRUE);
+
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_LBL_INFO_MEDIUM), &box);
+ hide = IsWindowVisible(GetDlgItem(hwndDlg, IDC_LBL_MEDIUM_UPDATE));
+ if (hide) InvalidateRect(GetDlgItem(hwndDlg, IDC_LBL_MEDIUM_UPDATE), NULL, TRUE);
+
+ if(PrimoSDKHelper_IsLoaded())
+ /*{
+ ShowWindow(GetDlgItem(hwndDlg, IDC_LBL_MEDIUM_UPDATE), SW_SHOW);
+ }
+ else*/
+ {
+ ShowWindow(GetDlgItem(hwndDlg, IDC_LBL_MEDIUM_UPDATE), SW_HIDE);
+ SwitchControlVisible(hwndDlg, IDC_LBL_MEDIUM_CAPACITY_VAL, &box, hide, TRUE);
+ SwitchControlVisible(hwndDlg, IDC_LBL_MEDIUM_FORMAT_VAL, &box, hide, TRUE);
+ SwitchControlVisible(hwndDlg, IDC_LBL_MEDIUM_ERASEABLE_VAL, &box, hide, TRUE);
+ SwitchControlVisible(hwndDlg, IDC_LBL_MEDIUM_RECORDABLE_VAL,&box, hide, TRUE);
+ SwitchControlVisible(hwndDlg, IDC_LBL_MEDIUM_TRACKN_VAL, &box, hide, TRUE);
+ SwitchControlVisible(hwndDlg, IDC_LBL_MEDIUM_DISC_VAL, &box, hide, TRUE);
+ SwitchControlVisible(hwndDlg, IDC_LBL_MEDIUM_ADDINFO_VAL, &box, hide, TRUE);
+
+ SwitchControlVisible(hwndDlg, IDC_LBL_MEDIUM_TYPE, &box, hide);
+ SwitchControlVisible(hwndDlg, IDC_LBL_MEDIUM_CAPACITY, &box, hide);
+ SwitchControlVisible(hwndDlg, IDC_LBL_MEDIUM_FORMAT, &box, hide);
+ SwitchControlVisible(hwndDlg, IDC_LBL_MEDIUM_ERASEABLE, &box, hide);
+ SwitchControlVisible(hwndDlg, IDC_LBL_MEDIUM_RECORDABLE, &box, hide);
+ SwitchControlVisible(hwndDlg, IDC_LBL_MEDIUM_TRACKN, &box, hide);
+ SwitchControlVisible(hwndDlg, IDC_LBL_MEDIUM_ADDINFO, &box, hide);
+ }
+}
+
+static int LabelColoring(HDC hdc, HWND hwndCtrl)
+{
+ switch(GetDlgCtrlID(hwndCtrl))
+ {
+ case IDC_LBL_DRIVES:
+ if(!lblHeaderBrush) lblHeaderBrush = CreateSolidBrush(dialogSkinner.Color(WADLG_LISTHEADER_BGCOLOR));
+ SetBkMode(hdc, TRANSPARENT);
+ SetTextColor(hdc, dialogSkinner.Color(WADLG_LISTHEADER_FONTCOLOR));
+ return (BOOL)(INT_PTR)lblHeaderBrush;
+ case IDC_LBL_MEDIUM_NOINFO:
+ case IDC_LBL_MEDIUM_CAPACITY_VAL:
+ case IDC_LBL_MEDIUM_TRACKN_VAL:
+ case IDC_LBL_MEDIUM_ERASEABLE_VAL:
+ case IDC_LBL_MEDIUM_RECORDABLE_VAL:
+ case IDC_LBL_MEDIUM_FORMAT_VAL:
+ case IDC_LBL_MEDIUM_DISC_VAL:
+ case IDC_LBL_MEDIUM_ADDINFO_VAL:
+ case IDC_LBL_DRIVE_LETTER_VAL:
+ case IDC_LBL_DRIVE_DESCRIPTION_VAL:
+ case IDC_LBL_DRIVE_BUS_VAL:
+ case IDC_LBL_DRIVE_TYPES_VAL:
+ case IDC_LBL_MEDIUM_UPDATE:
+ if(!lblValueBrush) lblValueBrush = CreateSolidBrush(dialogSkinner.Color(WADLG_ITEMBG));
+ SetBkColor(hdc, dialogSkinner.Color(WADLG_ITEMBG));
+ SetTextColor(hdc, dialogSkinner.Color(WADLG_ITEMFG));
+ return (BOOL)(INT_PTR)lblValueBrush;
+ case IDC_LBL_MEDIUM_CAPACITY:
+ case IDC_LBL_MEDIUM_TRACKN:
+ case IDC_LBL_MEDIUM_ERASEABLE:
+ case IDC_LBL_MEDIUM_RECORDABLE:
+ case IDC_LBL_MEDIUM_FORMAT:
+ case IDC_LBL_MEDIUM_ADDINFO:
+ case IDC_LBL_DRIVE_LETTER:
+ case IDC_LBL_DRIVE_DESCRIPTION:
+ case IDC_LBL_DRIVE_BUS:
+ case IDC_LBL_DRIVE_TYPES:
+ case IDC_LBL_MEDIUM_TYPE:
+ if(!lblValueBrush) lblValueBrush = CreateSolidBrush(dialogSkinner.Color(WADLG_ITEMBG));
+ SetBkMode(hdc, TRANSPARENT);
+ // SetBkColor(hdc, dialogSkinner.Color(WADLG_ITEMBG));
+ SetTextColor(hdc, dialogSkinner.Color(WADLG_ITEMFG));
+ return (BOOL)(INT_PTR)lblValueBrush;
+ }
+ return FALSE;
+}
+
+static void Drive_OnAdded(HWND hwndDlg, CHAR cLetter)
+{
+ HWND hwndLB = GetDlgItem(hwndDlg, IDC_LIST_DRIVES);
+ if (IsWindow(hwndLB))
+ {
+ wchar_t str[] = {cLetter, 0x00};
+ INT index = (INT)SendMessageW(hwndLB, LB_ADDSTRING, 0, (LPARAM)str);
+ if (LB_ERR != index)
+ {
+ SendMessageW(hwndLB, LB_SETITEMDATA, index, (LPARAM)cLetter);
+
+ INT idxSelection = (int)(INT_PTR)SendMessageW(hwndLB, LB_GETCURSEL, 0,0);
+ if (LB_ERR == idxSelection && cLetter == g_config->ReadInt(L"last_drive", cLetter))
+ {
+ if (LB_ERR != SendMessageW(hwndLB, LB_SETCURSEL, index, 0L))
+ {
+ UpdateDriveInfo(hwndDlg, cLetter);
+ }
+ }
+
+ // request unitinfo
+ DM_UNITINFO_PARAM *pui = (DM_UNITINFO_PARAM*)calloc(1, sizeof(DM_UNITINFO_PARAM));
+ if (pui)
+ {
+ pui->header.callback = (INT_PTR)hwndDlg;
+ pui->header.uMsg = msgNotify;
+ pui->header.cLetter = cLetter;
+ pui->header.fnFree = FreeAsyncParam;
+ DriveManager_GetUnitInfo(pui);
+ }
+ }
+ }
+}
+
+static INT GetListBoxIndex(HWND hwndLB, CHAR cLetter)
+{
+ wchar_t str[] = {cLetter, 0x00};
+ return (cLetter && hwndLB) ? (INT)SendMessageW(hwndLB, LB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)str) : LB_ERR;
+}
+
+static void Drive_OnRemoved(HWND hwndDlg, CHAR cLetter)
+{
+ HWND hwndLB = GetDlgItem(hwndDlg, IDC_LIST_DRIVES);
+
+ if (IsWindow(hwndLB))
+ {
+ INT index = GetListBoxIndex(hwndLB, cLetter);
+ if (LB_ERR != index) SendMessageW(hwndLB, LB_DELETESTRING, (WPARAM)index, 0L);
+ }
+}
+
+static void GetInfo_Completed(HWND hwndDlg, DM_NOTIFY_PARAM *phdr)
+{
+ wchar_t szBuffer[256] = {0};
+
+ DM_UNITINFO_PARAM *pui = NULL;
+ DM_UNITINFO2_PARAM *pui2 = NULL;
+ DM_DISCINFOEX_PARAM *pdi = NULL;
+ DM_DISCINFO2_PARAM *pdi2 = NULL;
+
+ switch(phdr->opCode)
+ {
+ case DMOP_UNITINFO:
+ pui = (DM_UNITINFO_PARAM*)phdr;
+ if (0 == phdr->result)
+ {
+ HWND hwndLB = GetDlgItem(hwndDlg, IDC_LIST_DRIVES);
+ if (hwndLB)
+ {
+ INT idxLB = GetListBoxIndex(hwndLB, phdr->cLetter);
+ if (LB_ERR != idxLB)
+ {
+ DWORD data = MAKELONG(phdr->cLetter, pui->dwType);
+ if (data != (DWORD)SendMessage(hwndLB ,LB_GETITEMDATA, idxLB, 0))
+ {
+ if (LB_ERR != SendMessageW(hwndLB, LB_SETITEMDATA, idxLB, (LPARAM)data))
+ {
+ RECT rc;
+ SendMessageW(hwndLB, LB_GETITEMRECT, idxLB, (LPARAM)&rc);
+ InvalidateRect(hwndLB, &rc, FALSE);
+ UpdateWindow(hwndLB);
+ }
+ }
+ }
+ }
+ if (activeDrive == phdr->cLetter && pui->pszDesc) SetDlgItemTextA(hwndDlg, IDC_LBL_DRIVE_DESCRIPTION_VAL, (pui->cchDesc > 0) ? pui->pszDesc : "");
+ }
+ break;
+ case DMOP_UNITINFO2:
+ pui2 = (DM_UNITINFO2_PARAM*)phdr;
+ if (0 == phdr->result && activeDrive == phdr->cLetter)
+ {
+ SetDlgItemTextW(hwndDlg, IDC_LBL_DRIVE_BUS_VAL, Drive_GetBusTypeString(pui2->dwBusType));
+ szBuffer[0] = 0x00;
+ for (int i = 0; i < pui2->nTypes; i++)
+ {
+ if (0 != i) StringCchCatW(szBuffer, sizeof(szBuffer)/sizeof(wchar_t), L", ");
+ StringCchCatW(szBuffer, sizeof(szBuffer)/sizeof(wchar_t), Drive_GetTypeString(pui2->pdwTypes[i]));
+ }
+ SetDlgItemTextW(hwndDlg, IDC_LBL_DRIVE_TYPES_VAL, szBuffer);
+ }
+ break;
+ case DMOP_DISCINFO:
+ pdi = (DM_DISCINFOEX_PARAM*)phdr;
+ if (0 == phdr->result && activeDrive == phdr->cLetter)
+ {
+ StringCchPrintfW(szBuffer, sizeof(szBuffer)/sizeof(wchar_t),
+ WASABI_API_LNGSTRINGW(IDS_X_OF_X_SECTORS_FREE),
+ pdi->dwFree, pdi->dwUsed + pdi->dwFree);
+ SetDlgItemTextW(hwndDlg, IDC_LBL_MEDIUM_CAPACITY_VAL, szBuffer);
+ SetDlgItemInt(hwndDlg, IDC_LBL_MEDIUM_TRACKN_VAL, pdi->dwTracks, FALSE);
+ SetDlgItemText(hwndDlg, IDC_LBL_MEDIUM_ERASEABLE_VAL, WASABI_API_LNGSTRINGW((pdi->bErasable) ? IDS_YES : IDS_NO));
+ SetDlgItemText(hwndDlg, IDC_LBL_MEDIUM_RECORDABLE_VAL, WASABI_API_LNGSTRINGW((Medium_IsRecordableType(pdi->dwMediumType)) ? IDS_YES : IDS_NO));
+ SetDlgItemText(hwndDlg, IDC_LBL_MEDIUM_ADDINFO_VAL, Medium_GetTypeString(pdi->dwMediumType));
+ SetDlgItemText(hwndDlg, IDC_LBL_MEDIUM_FORMAT_VAL, Medium_GetFormatString(pdi->dwMediumFormat));
+ }
+ break;
+ case DMOP_DISCINFO2:
+ pdi2 = (DM_DISCINFO2_PARAM*)phdr;
+ if (0 == phdr->result && activeDrive == phdr->cLetter)
+ {
+ SetDlgItemTextW(hwndDlg, IDC_LBL_MEDIUM_DISC_VAL, Medium_GetPhysicalTypeString(pdi2->dwMediumEx));
+ }
+ break;
+ }
+}
+
+static void View_OnPluginNotify(HWND hwndDlg, WORD wCode, INT_PTR param)
+{
+ switch(wCode)
+ {
+ case DMW_DRIVEADDED: Drive_OnAdded(hwndDlg, (CHAR)param); break;
+ case DMW_DRIVEREMOVED: Drive_OnRemoved(hwndDlg, (CHAR)param); break;
+ case DMW_MEDIUMARRIVED:
+ case DMW_MEDIUMREMOVED: if ((CHAR)param == activeDrive) UpdateDriveInfo(hwndDlg, activeDrive); break;
+ case DMW_OPCOMPLETED:
+ SendMessage(hwndDlg, WM_SIZE, 0, 0);
+ GetInfo_Completed(hwndDlg, (DM_NOTIFY_PARAM*)param);
+ break;
+ }
+}
+
+static INT_PTR ListBox_OnKeyPressed(HWND hwndDlg, HWND hwndLB, WORD wKey, INT iCurret)
+{
+ switch(wKey)
+ {
+ case VK_F5: DriveManager_Update(TRUE); return -2;
+ case VK_SPACE:
+ PostMessageW(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_LIST_DRIVES,LBN_DBLCLK), (LPARAM)hwndLB);
+ return -2;
+ }
+ if (wKey >= 'A' && wKey <= 'Z')
+ {
+ INT index = GetListBoxIndex(hwndLB, (CHAR)wKey);
+ return (LB_ERR != index) ? index : -2;
+ }
+
+ return -1; // do default
+}
+
+INT_PTR CALLBACK view_ripburnDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
+{
+ INT_PTR a;
+
+ if (uMsg == WM_CTLCOLORSTATIC )
+ {
+ a = LabelColoring((HDC)wParam, (HWND) lParam);
+ if (a) return a;
+ }
+
+ a = driveListBox->HandleMsgProc(uMsg,wParam,lParam); if (a) return a;
+ a = dialogSkinner.Handle(hwndDlg,uMsg,wParam,lParam); if (a) return a;
+
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ ripburn_OnInitDialog(hwndDlg);
+ break;
+ case WM_DISPLAYCHANGE:
+ ripburn_OnDisplayChanges(hwndDlg);
+ break;
+ case WM_SIZE:
+ if (wParam != SIZE_MINIMIZED)
+ {
+ childSizer.Resize(hwndDlg,ripburn_rlist,sizeof(ripburn_rlist)/sizeof(ripburn_rlist[0]));
+ ripburn_OnSize(hwndDlg, LOWORD(lParam), HIWORD(lParam));
+ }
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_LIST_DRIVES:
+ if (HIWORD(wParam) == LBN_SELCHANGE)
+ {
+ INT index = (int)(INT_PTR)SendMessage((HWND)lParam, LB_GETCURSEL, 0,0);
+ DWORD data = (LB_ERR != index) ? (DWORD)SendMessage((HWND)lParam ,LB_GETITEMDATA, index, 0) : 0;
+ if (data) UpdateDriveInfo(hwndDlg, (CHAR)(0xFF & data));
+ }
+ else if (HIWORD(wParam) == LBN_DBLCLK)
+ {
+ INT index = (int)(INT_PTR)SendMessage((HWND)lParam, LB_GETCURSEL, 0,0);
+ DWORD data = (LB_ERR != index) ? (DWORD)SendMessage((HWND)lParam ,LB_GETITEMDATA, index, 0) : 0;
+ HNAVITEM hItem = (data) ? Plugin_GetNavItemFromLetter((CHAR)(0xFF & data)) : NULL;
+ if (hItem) MLNavItem_Select(plugin.hwndLibraryParent, hItem);
+ }
+ break;
+ case IDC_BTN_REFRESH:
+ if (HIWORD(wParam) == BN_CLICKED) DriveManager_Update(TRUE);
+ break;
+ }
+ break;
+ case WM_PAINT:
+ {
+ int tab[] = { IDC_LIST_DRIVES | DCW_SUNKENBORDER,
+ IDC_LBL_DRIVES | DCW_SUNKENBORDER,
+ IDC_LBL_INFO_DRIVE | DCW_SUNKENBORDER,
+ IDC_LBL_INFO_MEDIUM | DCW_SUNKENBORDER};
+ dialogSkinner.Draw(hwndDlg, tab, 4);
+ }
+ return 0;
+ case WM_DESTROY:
+ ripburn_OnDestroy(hwndDlg);
+ break;
+ case WM_ERASEBKGND:
+ return 0;
+ case WM_VKEYTOITEM:
+ return ListBox_OnKeyPressed(hwndDlg, (HWND)lParam, LOWORD(wParam), HIWORD(wParam));
+ }
+ if (msgNotify == uMsg)
+ View_OnPluginNotify(hwndDlg, (WORD)wParam, (INT_PTR)lParam);
+
+ return FALSE;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_disc/view_wait.cpp b/Src/Plugins/Library/ml_disc/view_wait.cpp
new file mode 100644
index 00000000..17a8fa6f
--- /dev/null
+++ b/Src/Plugins/Library/ml_disc/view_wait.cpp
@@ -0,0 +1,140 @@
+#include "main.h"
+#include "./resource.h"
+#include "../nu/DialogSkinner.h"
+
+#include <windowsx.h>
+
+static HBRUSH hbBack = NULL;
+#define TIMER_SHOWTEXT_ID 1985
+#define TIMER_SHOWTEXT_DELAY 1000
+
+static INT_PTR WINAPI DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+// public function
+HWND CreateWaitWindow(HWND hwndParent, CHAR cLetter)
+{
+ return WASABI_API_CREATEDIALOGPARAMW(IDD_VIEW_WAIT, hwndParent, DlgProc, (LPARAM)cLetter);
+}
+
+static void CALLBACK Window_TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ switch(idEvent)
+ {
+ case TIMER_SHOWTEXT_ID:
+ KillTimer(hwnd, TIMER_SHOWTEXT_ID);
+ SetDlgItemTextW(hwnd, IDC_LBL_TEXT, WASABI_API_LNGSTRINGW(IDS_READINGDISC));
+ break;
+ }
+}
+
+static INT_PTR Window_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
+{
+ SendMessageW(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(IDC_BTN_SHOWINFO, BN_EX_GETTEXT), (LPARAM)GetDlgItem(hwnd, IDC_BTN_SHOWINFO));
+ SetTimer(hwnd, TIMER_SHOWTEXT_ID, TIMER_SHOWTEXT_DELAY, Window_TimerProc);
+ return 0;
+}
+
+static void Window_OnDestroy(HWND hwnd)
+{
+ if (hbBack)
+ {
+ DeleteObject(hbBack);
+ hbBack = NULL;
+ }
+}
+
+static void Window_OnDisplayChange(HWND hwnd, INT dpi, INT resX, INT resY)
+{
+ if (hbBack)
+ {
+ DeleteObject(hbBack);
+ hbBack = NULL;
+ }
+ RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
+}
+
+static void Window_OnWindowPosChanged(HWND hwnd, WINDOWPOS *pwp)
+{
+ HWND hwndCtrl;
+ RECT rw;
+
+ hwndCtrl = GetDlgItem(hwnd, IDC_BTN_SHOWINFO);
+ if(hwndCtrl)
+ {
+ GetWindowRect(hwndCtrl, &rw);
+ OffsetRect(&rw, -rw.left, -rw.top);
+ SetWindowPos(hwndCtrl, NULL, pwp->cx - rw.right, pwp->cy - rw.bottom, 0, 0,
+ SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS);
+ }
+
+ hwndCtrl = GetDlgItem(hwnd, IDC_LBL_TEXT);
+ if(hwndCtrl)
+ {
+ GetWindowRect(hwndCtrl, &rw);
+ MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rw, 2);
+ SetWindowPos(hwndCtrl, NULL, 0, 0, pwp->cx - rw.left - 2, pwp->cy - 22 - rw.top,
+ SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS);
+ }
+ if (0 == (SWP_NOREDRAW & pwp->flags))
+ {
+ InvalidateRect(hwnd, NULL, TRUE);
+ RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
+ }
+}
+
+static void Window_OnCommand(HWND hwnd, INT eventId, INT ctrlId, HWND hwndCtrl)
+{
+ switch(ctrlId)
+ {
+ case IDC_BTN_SHOWINFO:
+ switch(eventId)
+ {
+ case BN_CLICKED:
+ SendMessageW(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(ctrlId, eventId),(LPARAM)hwndCtrl);
+ break;
+ }
+ break;
+ }
+}
+
+static HBRUSH Window_OnStaticColor(HWND hwnd, HDC hdc)
+{
+ if (!hbBack) hbBack = CreateSolidBrush(dialogSkinner.Color(WADLG_ITEMBG));
+ SetTextColor(hdc, dialogSkinner.Color(WADLG_ITEMFG));
+ SetBkColor(hdc, dialogSkinner.Color(WADLG_ITEMBG));
+ return hbBack;
+}
+
+static void Window_OnPaint(HWND hwnd)
+{
+ int tab[] = { IDC_LBL_TEXT | DCW_SUNKENBORDER};
+ dialogSkinner.Draw(hwnd, tab, 1);
+}
+
+static void Window_OnQueryInfo(HWND hwnd)
+{
+ HWND hwndParent;
+ hwndParent = GetParent(hwnd);
+ if (hwndParent) SendMessageW(hwndParent, WM_SHOWFILEINFO, (WPARAM)WISF_NORMAL, (LPARAM)L"");
+}
+
+static INT_PTR WINAPI DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ INT_PTR result;
+
+ if (WM_CTLCOLORSTATIC == uMsg) return (INT_PTR)Window_OnStaticColor(hwnd, (HDC)wParam);
+ result = dialogSkinner.Handle(hwnd, uMsg, wParam, lParam);
+ if (result) return result;
+
+ switch(uMsg)
+ {
+ case WM_INITDIALOG: return (INT_PTR)Window_OnInitDialog(hwnd, (HWND)wParam, lParam);
+ case WM_DESTROY: Window_OnDestroy(hwnd); break;
+ case WM_DISPLAYCHANGE: Window_OnDisplayChange(hwnd, (INT)wParam, LOWORD(lParam), HIWORD(lParam)); break;
+ case WM_WINDOWPOSCHANGED: Window_OnWindowPosChanged(hwnd, (WINDOWPOS*)lParam); break;
+ case WM_COMMAND: Window_OnCommand(hwnd, HIWORD(wParam), LOWORD(wParam), (HWND)lParam); break;
+ case WM_PAINT: Window_OnPaint(hwnd); break;
+ case WM_QUERYFILEINFO: Window_OnQueryInfo(hwnd); break;
+ }
+ return 0;
+} \ No newline at end of file