aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Output/out_disk/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Output/out_disk/main.cpp')
-rw-r--r--Src/Plugins/Output/out_disk/main.cpp1203
1 files changed, 1203 insertions, 0 deletions
diff --git a/Src/Plugins/Output/out_disk/main.cpp b/Src/Plugins/Output/out_disk/main.cpp
new file mode 100644
index 00000000..ca0ed8ce
--- /dev/null
+++ b/Src/Plugins/Output/out_disk/main.cpp
@@ -0,0 +1,1203 @@
+#define STRICT
+#include <windows.h>
+#include <windowsx.h>
+#include <commctrl.h>
+#include <shlobj.h>
+#include <mmreg.h>
+#include <msacm.h>
+#include "out_disk.h"
+#include "../winamp/wa_ipc.h"
+#include <shlwapi.h>
+#include <strsafe.h>
+
+// wasabi based services for localisation support
+api_service *WASABI_API_SVC = 0;
+api_language *WASABI_API_LNG = 0;
+HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
+
+class CriticalSection : public CRITICAL_SECTION
+{
+public:
+ inline void Enter() {EnterCriticalSection(this);}
+ inline void Leave() {LeaveCriticalSection(this);}
+ CriticalSection() {InitializeCriticalSection(this);}
+ ~CriticalSection() {DeleteCriticalSection(this);}
+ //BOOL TryEnter() {return TryEnterCriticalSection(this);}
+};
+
+class __T_SYNC
+{
+private:
+ CriticalSection *p;
+public:
+ inline __T_SYNC(CriticalSection& s) {p=&s;p->Enter();}
+ inline void Leave() {if (p) p->Leave();}
+ inline void Enter() {if (p) p->Enter();}
+ inline void Abort() {p=0;}
+ inline ~__T_SYNC() {Leave();}
+};
+
+static CriticalSection g_sync;
+
+#define SYNCFUNC __T_SYNC __sync(g_sync);
+
+#define tabsize(X) (sizeof(X)/sizeof(*X))
+
+enum
+{
+ MODE_AUTO=0,
+ MODE_WAV=1,
+ MODE_RAW=2
+};
+
+int mode_names_idx[] = {IDS_AUTO_RECOMMENDED,IDS_FORCE_WAV_FILE,IDS_FORCE_RAW_DATA};
+static const wchar_t* format_names[]={L"%title%",L"%filename%",L"%title%_%extension%",L"%filename%_%extension%"};
+static wchar_t szDescription[256];
+int index_name_idx[] = {IDS_DISABLED,IDS_1_DIGIT,IDS_2_DIGITS,IDS_3_DIGITS,IDS_4_DIGITS};
+
+#define rev32(X) ((((DWORD)(X)&0xFF)<<24)|(((DWORD)(X)&0xFF00)<<8)|(((DWORD)(X)&0xFF0000)>>8)|(((DWORD)(X)&0xFF000000)>>24))
+
+static char cfg_output_dir[MAX_PATH]="c:\\";
+static char cfg_singlefile_output[MAX_PATH]="c:\\output.wav";
+static bool cfg_singlefile_enabled = 0;
+static bool cfg_convert_enabled=0;
+static bool cfg_thread_override = 0;
+static bool cfg_output_source_dir = 0;
+static int cfg_output_mode=0;
+static bool cfg_show_saveas=0;
+static int cfg_format_mode=0;
+static int cfg_format_index=2;
+
+static bool use_convert;
+
+
+static bool GetCheck(HWND wnd,int id) {return !!SendDlgItemMessage(wnd,id,BM_GETCHECK,0,0);}
+
+static void SetCheck(HWND wnd,int id,bool b) {SendDlgItemMessage(wnd,id,BM_SETCHECK,b ? BST_CHECKED : BST_UNCHECKED,0);}
+
+void SetPathChoiceButtonText(HWND hwndDlg, char* path, UINT id)
+{
+ HWND control = GetDlgItem(hwndDlg, id);
+ HDC hdc = GetDC(control);
+ RECT r = {0};
+ char temp[MAX_PATH] = {0};
+
+ lstrcpynA(temp, path, MAX_PATH);
+ SelectObject(hdc, (HFONT)SendMessage(control, WM_GETFONT, 0, 0));
+ GetClientRect(control, &r);
+ r.left += 5;
+ r.right -= 5;
+ DrawTextA(hdc, temp, -1, &r, DT_PATH_ELLIPSIS|DT_WORD_ELLIPSIS|DT_MODIFYSTRING);
+ SetWindowTextA(control, temp);
+ ReleaseDC(control, hdc);
+}
+
+BOOL CALLBACK browseEnumProc(HWND hwnd, LPARAM lParam)
+{
+ char cl[32] = {0};
+ GetClassNameA(hwnd, cl, ARRAYSIZE(cl));
+ if (!lstrcmpiA(cl, WC_TREEVIEW))
+ {
+ PostMessage(hwnd, TVM_ENSUREVISIBLE, 0, (LPARAM)TreeView_GetSelection(hwnd));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int _stdcall browzaproc(HWND hwnd, UINT msg, LPARAM lp, LPARAM dat)
+{
+ if (msg == BFFM_INITIALIZED)
+ {
+ SendMessage(hwnd, BFFM_SETSELECTION, 1, dat);
+
+ // this is not nice but it fixes the selection not working correctly on all OSes
+ EnumChildWindows(hwnd, browseEnumProc, 0);
+ }
+ return 0;
+}
+
+char g_tmp[MAX_PATH] = {0}, g_tmp_sf[MAX_PATH] = {0};
+static void d_browza(HWND wnd,HWND bt,wchar_t* tx)
+{
+ IMalloc* pMalloc=0;
+ SHGetMalloc(&pMalloc);
+ if (!pMalloc) return;
+
+ BROWSEINFOW bi=
+ {
+ wnd,
+ 0,
+ 0,
+ tx,
+ BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE,
+ browzaproc,
+#ifdef WIN64
+ (long long)g_tmp,
+#else
+ (long)g_tmp,
+#endif
+ 0
+ };
+ LPITEMIDLIST li=SHBrowseForFolderW(&bi);
+ if (li)
+ {
+ SHGetPathFromIDListA(li,g_tmp);
+ SetPathChoiceButtonText(wnd, g_tmp, IDC_OUTPUT_DIRECTORY);
+ pMalloc->Free(li);
+ }
+ pMalloc->Release();
+}
+
+static WAVEFORMATEX singlefile_wfx,singlefile_wfx_temp;
+
+#define WFSIZ 0x800
+typedef struct
+{
+ WAVEFORMATEX wfx;
+ BYTE crap[WFSIZ];
+} EXT_WFX;
+
+EXT_WFX convert_wfx,convert_wfx_temp;
+static const WAVEFORMATEX wfx_default =
+{
+ WAVE_FORMAT_PCM,
+ 2,
+ 44100,
+ 44100*4,
+ 4,
+ 16,
+ 0
+};
+
+void _inline ACM_gettext1(char* tx, int txCch)
+{
+ ACMFORMATDETAILS afd;
+ ZeroMemory(&afd, sizeof(afd));
+ afd.cbStruct = sizeof(afd);
+ afd.dwFormatTag = WAVE_FORMAT_PCM;
+ afd.pwfx = &singlefile_wfx_temp;
+ afd.cbwfx = sizeof(singlefile_wfx_temp);
+ if (!acmFormatDetails(0, &afd, ACM_FORMATDETAILSF_FORMAT))
+ {
+ lstrcpyn(tx, afd.szFormat, txCch);
+ }
+}
+
+void _inline ACM_gettext(char* tx)
+{
+ ACMFORMATTAGDETAILS aftd;
+ ZeroMemory(&aftd,sizeof(aftd));
+ aftd.cbStruct=sizeof(aftd);
+ aftd.dwFormatTag=convert_wfx_temp.wfx.wFormatTag;
+ if (!acmFormatTagDetails(0,&aftd,ACM_FORMATTAGDETAILSF_FORMATTAG))
+ {
+ char* p=aftd.szFormatTag;
+ while(p && *p) *(tx++)=*(p++);
+ *(tx++)=13;
+ *(tx++)=10;
+ }
+ ACMFORMATDETAILS afd;
+ ZeroMemory(&afd,sizeof(afd));
+ afd.cbStruct=sizeof(afd);
+ afd.dwFormatTag=convert_wfx_temp.wfx.wFormatTag;
+ afd.pwfx=&convert_wfx_temp.wfx;
+ afd.cbwfx=sizeof(convert_wfx_temp);
+ if (!acmFormatDetails(0,&afd,ACM_FORMATDETAILSF_FORMAT))
+ {
+ char* p=afd.szFormat;
+ while(p && *p) *(tx++)=*(p++);
+ }
+ *tx=0;
+}
+
+void ACM_choose(HWND w,bool pcm)
+{
+ ACMFORMATCHOOSE afc;
+ memset(&afc,0,sizeof(afc));
+ afc.cbStruct=sizeof(afc);
+ afc.fdwStyle=ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT;
+ if (pcm)
+ {
+ singlefile_wfx_temp.wFormatTag=WAVE_FORMAT_PCM;
+ afc.pwfxEnum=&singlefile_wfx_temp;
+ afc.fdwEnum=ACM_FORMATENUMF_WFORMATTAG;
+ afc.pwfx = &singlefile_wfx_temp;
+ afc.cbwfx = sizeof(singlefile_wfx_temp);
+ }
+ else
+ {
+ afc.pwfx = &convert_wfx_temp.wfx;
+ afc.cbwfx = sizeof(convert_wfx_temp);
+ }
+
+ afc.hwndOwner=w;
+
+ if (!acmFormatChoose(&afc))
+ {
+ if (pcm)
+ {
+ SetDlgItemText(w,IDC_SINGLEFILE_FORMAT_BUTTON,afc.szFormat);
+ }
+ else
+ {
+ char tmp[512] = {0};
+ StringCchPrintf(tmp, 512, "%s\x0d\x0a%s",afc.szFormatTag,afc.szFormat);
+ SetDlgItemText(w,IDC_CONVERT_BUTTON,tmp);
+ }
+ }
+}
+
+void _inline do_acm_text(HWND wnd)
+{
+ char tmp[256] = {0};
+ ACM_gettext(tmp);
+ SetDlgItemText(wnd,IDC_CONVERT_BUTTON,tmp);
+}
+
+void _inline do_acm_text1(HWND wnd)
+{
+ char tmp[256] = {0};
+ ACM_gettext1(tmp, 256);
+ SetDlgItemText(wnd,IDC_SINGLEFILE_FORMAT_BUTTON,tmp);
+}
+
+void wav1_set(HWND w,bool b)
+{
+ static struct
+ {
+ WORD id;
+ bool t;
+ } wav1_w_c[]=
+ {
+ {IDC_OUTPUT_STATIC,1},
+ {IDC_OUTPUT_DIRECTORY_STATIC,1},
+ {IDC_OUTPUT_DIRECTORY,1},
+ {IDC_OUTPUT_SRCDIR,1},
+ {IDC_FILENAME_SAVEAS,1},
+ {IDC_FILENAME_INDEX_STATIC,1},
+ {IDC_FILENAME_INDEX,1},
+ {IDC_OUTMODE_STATIC,1},
+ {IDC_OUTMODE,1},
+ {IDC_CONVERT_STATIC,1},
+ {IDC_CONVERT_CHECK,1},
+ {IDC_CONVERT_BUTTON,1},
+ {IDC_CONVERT_NOTE,1},
+ {IDC_FILENAME_FORMAT,1},
+ {IDC_FILENAME_FORMAT_STATIC,1},
+ {IDC_SINGLEFILE_FILE_STATIC,0},
+ {IDC_SINGLEFILE_FILE_BUTTON,0},
+ {IDC_SINGLEFILE_FORMAT_STATIC,0},
+ {IDC_SINGLEFILE_FORMAT_BUTTON,0},
+ };
+
+ UINT n;
+ for(n=0;n<tabsize(wav1_w_c);n++)
+ {
+ EnableWindow(GetDlgItem(w,wav1_w_c[n].id),wav1_w_c[n].t^b);
+ }
+}
+
+BOOL CALLBACK DlgProc(HWND wnd,UINT msg,WPARAM wp,LPARAM lp)
+{
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ wchar_t title[128] = {0}, temp[128] = {0};
+ StringCchPrintfW(title,128,WASABI_API_LNGSTRINGW(IDS_SETTINGS_TITLE),WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_DISK_WRITER_OLD, temp, 128));
+ SetWindowTextW(wnd,title);
+ SetDlgItemText(wnd,IDC_OUTPUT_DIRECTORY,cfg_output_dir);
+ SetPathChoiceButtonText(wnd,cfg_output_dir,IDC_OUTPUT_DIRECTORY);
+ lstrcpyn(g_tmp,cfg_output_dir,MAX_PATH);
+ SetCheck(wnd,IDC_CONVERT,cfg_convert_enabled);
+ memcpy(&convert_wfx_temp,&convert_wfx,sizeof(convert_wfx));
+ memcpy(&singlefile_wfx_temp,&singlefile_wfx,sizeof(WAVEFORMATEX));
+ SetCheck(wnd,IDC_SINGLEFILE_CHECK,cfg_singlefile_enabled);
+ SetCheck(wnd,IDC_THREAD_HACK,cfg_thread_override);
+ SetPathChoiceButtonText(wnd,cfg_singlefile_output,IDC_SINGLEFILE_FILE_BUTTON);
+ lstrcpyn(g_tmp_sf,cfg_singlefile_output,MAX_PATH);
+ do_acm_text(wnd);
+ do_acm_text1(wnd);
+
+ {
+ HWND w=GetDlgItem(wnd,IDC_MODE);
+ int n;
+ for(n=0;n<tabsize(mode_names_idx);n++)
+ SendMessageW(w,CB_ADDSTRING,0,
+#ifdef WIN64
+ (long long)WASABI_API_LNGSTRINGW(mode_names_idx[n])
+#else
+ (long)WASABI_API_LNGSTRINGW(mode_names_idx[n])
+#endif
+ );
+ SendMessage(w,CB_SETCURSEL,cfg_output_mode,0);
+
+ w=GetDlgItem(wnd,IDC_FILENAME_FORMAT);
+ for(n=0;n<tabsize(format_names);n++)
+ SendMessageW(w,CB_ADDSTRING,0,
+#ifdef WIN64
+ (long long)format_names[n]
+#else
+ (long)format_names[n]
+#endif
+ );
+ SendMessage(w,CB_SETCURSEL,cfg_format_mode,0);
+
+ w=GetDlgItem(wnd,IDC_FILENAME_INDEX);
+ for(n=0;n<tabsize(index_name_idx);n++)
+ SendMessageW(w,CB_ADDSTRING,0,
+#ifdef WIN64
+ (long long)WASABI_API_LNGSTRINGW(index_name_idx[n])
+#else
+ (long)WASABI_API_LNGSTRINGW(index_name_idx[n])
+#endif
+ );
+ SendMessage(w,CB_SETCURSEL,cfg_format_index,0);
+ }
+
+ wav1_set(wnd,cfg_singlefile_enabled);
+
+ SetCheck(wnd,IDC_OUTPUT_SRCDIR,cfg_output_source_dir);
+ SetCheck(wnd,IDC_FILENAME_SAVEAS,cfg_show_saveas);
+
+ return 1;
+ }
+ case WM_COMMAND:
+ if (wp==IDC_CONVERT_BUTTON)
+ {
+ ACM_choose(wnd,0);
+ }
+ else if (wp==IDC_OUTPUT_DIRECTORY)
+ {
+ d_browza(wnd,(HWND)lp,WASABI_API_LNGSTRINGW(IDS_SELECT_OUTPUT_DIRECTORY));
+ }
+ else if (wp==IDCANCEL) EndDialog(wnd,0);
+ else if (wp==IDOK)
+ {
+ cfg_convert_enabled=GetCheck(wnd,IDC_CONVERT);
+ lstrcpyn(cfg_output_dir,g_tmp,MAX_PATH);
+ memcpy(&convert_wfx,&convert_wfx_temp,sizeof(convert_wfx));
+ memcpy(&singlefile_wfx,&singlefile_wfx_temp,sizeof(WAVEFORMATEX));
+ cfg_singlefile_enabled=GetCheck(wnd,IDC_SINGLEFILE_CHECK);
+ cfg_thread_override=GetCheck(wnd,IDC_THREAD_HACK);
+ cfg_output_mode=(int)SendDlgItemMessage(wnd,IDC_MODE,CB_GETCURSEL,0,0);
+ lstrcpyn(cfg_singlefile_output,g_tmp_sf,MAX_PATH);
+ cfg_output_source_dir=GetCheck(wnd,IDC_OUTPUT_SRCDIR);
+ cfg_show_saveas=GetCheck(wnd,IDC_FILENAME_SAVEAS);
+
+ cfg_format_mode=(int)SendDlgItemMessage(wnd,IDC_FILENAME_FORMAT,CB_GETCURSEL,0,0);
+ cfg_format_index=(int)SendDlgItemMessage(wnd,IDC_FILENAME_INDEX,CB_GETCURSEL,0,0);
+
+ EndDialog(wnd,0);
+ }
+ else if (wp==IDC_SINGLEFILE_FILE_BUTTON)
+ {
+ char tmp[MAX_PATH] = {0}, filter[64] = {0};
+
+ StringCchPrintf(filter,64, WASABI_API_LNGSTRING(IDS_X_FILES_DOT_X),"WAV",".wav");
+ char * ptr=filter;
+ while(ptr && *ptr)
+ {
+ if (*ptr=='|') *ptr=0;
+ ptr++;
+ }
+
+ GetDlgItemText(wnd,IDC_SINGLEFILE_FILE_BUTTON,tmp,MAX_PATH);
+ lstrcpyn(tmp,g_tmp_sf,MAX_PATH);
+ OPENFILENAME ofn = {0};
+ ofn.lStructSize=sizeof(ofn);
+ ofn.hwndOwner=wnd;
+ ofn.lpstrFilter=filter;
+ ofn.lpstrFile=tmp;
+ ofn.nMaxFile=MAX_PATH;
+ ofn.Flags=OFN_HIDEREADONLY|OFN_PATHMUSTEXIST;
+ ofn.lpstrDefExt="wav";
+ if (GetSaveFileName(&ofn))
+ {
+ SetPathChoiceButtonText(wnd,tmp,IDC_SINGLEFILE_FILE_BUTTON);
+ lstrcpyn(g_tmp_sf, tmp, MAX_PATH);
+ }
+ }
+ else if (wp==IDC_SINGLEFILE_FORMAT_BUTTON) ACM_choose(wnd,1);
+ else if (wp==IDC_SINGLEFILE_CHECK) wav1_set(wnd,GetCheck(wnd,IDC_SINGLEFILE_CHECK));
+
+ break;
+ }
+ return 0;
+}
+
+void Config(HWND hWnd)
+{
+ WASABI_API_DIALOGBOXW(IDD_DIALOG1,hWnd,DlgProc);
+}
+
+static int nsam;
+static int g_freq,g_nch,g_bps;
+static int paused;
+
+const static char badchars[]={'.','\\','/',':','*','?','\"','<','>','|'};
+
+char* GetDefaultSaveToFolder(char* path_to_store)
+{
+ if(FAILED(SHGetFolderPathA(NULL, CSIDL_MYMUSIC, NULL, SHGFP_TYPE_CURRENT, path_to_store)))
+ {
+ if(FAILED(SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, path_to_store)))
+ {
+ lstrcpynA(path_to_store, "C:\\", MAX_PATH);
+ }
+ }
+ return path_to_store;
+}
+
+static void GetName(char* out, int outCch, const char* format_ext)
+{
+ int index = (int)SendMessage(mod.hMainWindow,WM_USER,0,IPC_GETLISTPOS);
+
+ if (cfg_output_source_dir)
+ {
+ const char * dir=(const char *)SendMessage(mod.hMainWindow,WM_USER,index,211);
+ const char * last=strrchr(dir,'\\');
+ while(dir<=last) {*(out++)=*(dir++);}
+ }
+ else
+ {
+ char * dir=cfg_output_dir;
+ if (!*dir)
+ {
+ char tmp[MAX_PATH] = {0};
+ dir=GetDefaultSaveToFolder(tmp);
+ }
+ while(dir && *dir) {*(out++)=*(dir++);}
+ if (out[-1]!='\\') *(out++)='\\';
+ }
+
+ char * badchar_test = out;
+
+ if (cfg_format_index>0)
+ {
+ char temp[16] = {0};
+ StringCchPrintf(temp, 16, "%09u_",index+1);
+ lstrcpyn(out, temp+9-cfg_format_index, outCch);
+ out+=cfg_format_index+1;
+ }
+
+ if (cfg_format_mode&1)//filename
+ {
+ const char * source_full = (const char*)SendMessage(mod.hMainWindow,WM_USER,index,211);
+ const char * source = strrchr(source_full,'\\');
+ if (!source) source = source_full;
+ else source++;
+
+ const char * dot = strrchr(source,'.');
+ if (dot) while(source<dot) *(out++)=*(source++);
+ else while(source && *source) *(out++)=*(source++);
+ }
+ else //title
+ {
+ const char * source = (const char*)SendMessage(mod.hMainWindow,WM_USER,index,212);
+ if (!source) source = "(unknown title)";
+ while(source && *source) *(out++)=*(source++);
+ }
+
+ if (cfg_format_mode&2)
+ {//add extension
+ const char * extension = strrchr((const char*)SendMessage(mod.hMainWindow,WM_USER,index,211),'.');
+ while(extension && *extension) *(out++)=*(extension++);
+ }
+
+
+ while(badchar_test<out)
+ {
+ BYTE c = *badchar_test;
+ int n;
+ for(n=0;n<tabsize(badchars);n++)
+ {
+ if (c==badchars[n]) {*badchar_test='_';break;}
+ }
+
+ badchar_test++;
+ }
+
+ while(format_ext && *format_ext) *(out++)=*(format_ext++);
+ *out=0;
+}
+
+static HANDLE hOut=INVALID_HANDLE_VALUE;
+
+static HACMSTREAM hStream;
+
+static DWORD fact_ofs,data_ofs;
+static bool riff;
+
+static DWORD FileTell(HANDLE hFile) {return SetFilePointer(hFile,0,0,FILE_CURRENT);}
+static void FileAlign(HANDLE hFile) {if (FileTell(hFile)&1) SetFilePointer(hFile,1,0,FILE_CURRENT);}
+
+#define BUFSIZE 0x20000
+
+static BYTE *acm_outbuf;
+static UINT acm_outbufsize;
+static BYTE *acm_buf;
+static BYTE *acm_buf1;
+static UINT inbuf,inbuf1;
+
+static ACMSTREAMHEADER ahd,ahd1;
+static bool active;
+
+static void file_err(char* f, wchar_t* t)
+{
+ wchar_t tmp[512+128] = {0};
+ StringCchPrintfW(tmp, 512+128, L"%s: \"%hs\"", t, f);
+ MessageBoxW(mod.hMainWindow, tmp, szDescription, MB_ICONERROR);
+}
+
+void acm_close();
+
+static int pos_delta;
+
+int Open(int sr,int nch,int bps,int xx,int xxx)
+{
+ // if someone didn't call Close(), close the file for them
+ if (hOut!=INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(hOut);
+ hOut=INVALID_HANDLE_VALUE;
+ }
+ int failFlags = 0;
+ SYNCFUNC;
+ char fn[512] = {0};
+ use_convert=cfg_convert_enabled;
+ DWORD bw = 0, t = 0;
+ WAVEFORMATEX wfx=
+ {
+ WAVE_FORMAT_PCM,
+ (WORD)nch,
+ (DWORD)sr,
+ (DWORD)(sr*nch*(bps>>3)),
+ (WORD)(nch*(bps>>3)),
+ (WORD)bps,
+ 0
+ };
+ bool append=0;
+
+ WAVEFORMATEX *pDst=&convert_wfx.wfx;
+ if (!cfg_convert_enabled) pDst=&wfx;
+ if (cfg_singlefile_enabled)
+ {
+ pDst=&singlefile_wfx;
+ use_convert=1;
+ lstrcpyn(fn, cfg_singlefile_output,512);
+ riff=1;
+ }
+ else
+ {
+ const char* ext=".wav";
+ riff=1;
+ if (cfg_output_mode==MODE_RAW)
+ {
+ riff=0;
+ ext=".raw";
+ }
+ else if (cfg_output_mode==MODE_AUTO)
+ {
+ if (pDst->wFormatTag==WAVE_FORMAT_MPEGLAYER3)
+ {
+ riff=0;
+ ext=".mp3";
+ }
+ }
+ GetName(fn, 512, ext);
+ if (cfg_show_saveas)
+ {
+ char filter[64] = {0}, title[128] = {0}, title2[128] = {0};
+ StringCchPrintf(filter,64, WASABI_API_LNGSTRING(IDS_X_FILES_DOT_X),ext,ext);
+ char * ptr=filter;
+ while(ptr && *ptr)
+ {
+ if (*ptr=='|') *ptr=0;
+ ptr++;
+ }
+
+ OPENFILENAME ofn= {0};
+ ofn.lStructSize=sizeof(ofn);
+ ofn.hwndOwner=mod.hMainWindow;
+ ofn.lpstrFilter = filter;
+ ofn.lpstrFile = fn;
+ StringCchPrintf(title,128,WASABI_API_LNGSTRING(IDS_CHOOSE_FILE_NAME),
+ WASABI_API_LNGSTRING_BUF(IDS_NULLSOFT_DISK_WRITER,title2,128));
+ ofn.lpstrTitle = title;
+ ofn.nMaxFile = tabsize(fn);
+ ofn.Flags = OFN_HIDEREADONLY|OFN_PATHMUSTEXIST|OFN_OVERWRITEPROMPT;
+ ofn.lpstrDefExt = ext;
+ if (!GetOpenFileName(&ofn)) return -1;
+ }
+ }
+
+ if (memcmp(&wfx,pDst,sizeof(wfx))==0) use_convert=0;
+
+ nsam=0;
+ g_freq=sr;
+ g_nch=nch;
+ g_bps=bps;
+ paused=0;
+
+ SetLastError(0);
+ hOut=CreateFile(fn,GENERIC_WRITE,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,0);
+
+ if (hOut==INVALID_HANDLE_VALUE)
+ {
+ DWORD e=GetLastError();
+
+ if (e==ERROR_ALREADY_EXISTS || e==0x50)
+ {
+ if (cfg_singlefile_enabled)
+ {
+ append=1;
+ goto _ap;
+ }
+ wchar_t tmp[512+128] = {0};
+ StringCchPrintfW(tmp, 512+128, WASABI_API_LNGSTRINGW(IDS_FILE_ALREADY_EXISTS_OVERWRITE),fn);
+ if (MessageBoxW(mod.hMainWindow,tmp,szDescription,MB_ICONQUESTION|MB_OKCANCEL)==IDOK)
+ {
+ hOut=CreateFile(fn,GENERIC_WRITE,0,0,CREATE_ALWAYS,0,0);
+ }
+ else return -1;
+ }
+ if (hOut==INVALID_HANDLE_VALUE)
+ {
+ file_err(fn, WASABI_API_LNGSTRINGW(IDS_CANNOT_CREATE_OUTPUT_FILE));
+ return -1;
+ }
+ }
+_ap:
+ fact_ofs=data_ofs=0;
+ if (append)
+ {
+
+ hOut=CreateFile(fn,GENERIC_WRITE|GENERIC_READ,0,0,OPEN_EXISTING,0,0);
+ if (hOut==INVALID_HANDLE_VALUE)
+ {
+ file_err(fn, WASABI_API_LNGSTRINGW(IDS_CANNOT_OPEN_FILE));
+ return -1;
+ }
+ {
+ DWORD br=0;
+ DWORD rf=0,wfs=0;
+ ReadFile(hOut,&rf,4,&br,0);
+ if (rf!='FFIR') goto ap_f;
+ SetFilePointer(hOut,4,0,FILE_CURRENT);
+ br=0; ReadFile(hOut,&rf,4,&br,0);
+ if (rf!='EVAW') goto ap_f;
+ br=0; ReadFile(hOut,&rf,4,&br,0);
+ if (rf!=' tmf') goto ap_f;
+ static WAVEFORMATEX ap_wfx;
+ br=0; ReadFile(hOut,&wfs,4,&br,0);
+ if (wfs<sizeof(ap_wfx)-2 || wfs>sizeof(ap_wfx)) goto ap_f;
+ br=0; ReadFile(hOut,&ap_wfx,wfs,&br,0);
+ if (ap_wfx.wFormatTag!=WAVE_FORMAT_PCM) goto ap_f;
+ br=0; ReadFile(hOut,&rf,4,&br,0);
+ pDst=&ap_wfx;
+ if (rf!='atad') goto ap_f;
+ data_ofs=8+4+8+wfs+4;
+ DWORD data_size=0;
+ br=0; ReadFile(hOut,&data_size,4,&br,0);
+ SetFilePointer(hOut,data_size,0,FILE_CURRENT);
+ SetEndOfFile(hOut);
+ use_convert = !!memcmp(&wfx,&ap_wfx,sizeof(wfx));
+ }
+ goto ap_ok;
+ap_f:
+ file_err(fn, WASABI_API_LNGSTRINGW(IDS_NOT_A_PCM_WAV_FILE));
+ if (hOut && hOut!=INVALID_HANDLE_VALUE) {CloseHandle(hOut);hOut=INVALID_HANDLE_VALUE;}
+ return -1;
+ap_ok:;
+ }
+
+ if (riff && !append)
+ {
+ t=rev32('RIFF');
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ SetFilePointer(hOut,4,0,FILE_CURRENT);
+ t=rev32('WAVE');
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ t=rev32('fmt ');
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ }
+
+ if (use_convert)
+ {
+#ifdef SSRC
+ if (pDst->wFormatTag==WAVE_FORMAT_PCM)
+ {
+ if ((wfx.nChannels!=1 && wfx.nChannels!=2) || (pDst->nChannels!=1 && pDst->nChannels!=2)) goto fail;
+
+ res = SSRC_create(wfx.nSamplesPerSec,pDst->nSamplesPerSec,wfx.wBitsPerSample,pDst->wBitsPerSample,wfx.nChannels,2,1,0,0,wfx.nChannels!=pDst->nChannels);
+
+ acm_outbuf=0;
+ acm_outbufsize=0;
+ }
+ else
+#endif
+ {
+ MMRESULT rs=acmStreamOpen(&hStream,0,&wfx,pDst,0,0,0,ACM_STREAMOPENF_NONREALTIME);
+ if (rs)
+ {
+ WAVEFORMATEX wfx1;
+ ZeroMemory(&wfx1,sizeof(wfx1));
+ wfx1.wFormatTag=WAVE_FORMAT_PCM;
+ if (acmFormatSuggest(0,pDst,&wfx1,sizeof(WAVEFORMATEX),ACM_FORMATSUGGESTF_WFORMATTAG)) goto fail;
+ if (acmStreamOpen(&hStream,0,&wfx1,pDst,0,0,0,ACM_STREAMOPENF_NONREALTIME)) goto fail;
+
+ if ((wfx.nChannels!=1 && wfx.nChannels!=2) || (wfx1.nChannels!=1 && wfx1.nChannels!=2)) goto fail;
+#ifdef SSRC
+ res = SSRC_create(wfx.nSamplesPerSec,wfx1.nSamplesPerSec,wfx.wBitsPerSample,wfx1.wBitsPerSample,wfx.nChannels,2,1,0,0,wfx.nChannels!=wfx1.nChannels);
+ //TODO fix different channel setups
+#endif
+ acm_buf1=(BYTE*)malloc(BUFSIZE);
+
+ if (!acm_buf1
+#ifdef SSRC
+ || !res
+#endif
+ )
+ goto fail;
+ }
+ acm_buf=(BYTE*)malloc(BUFSIZE);
+ acm_outbuf=(BYTE*)malloc(BUFSIZE);
+ if (!acm_buf || !acm_outbuf) goto fail;
+ ZeroMemory(&ahd,sizeof(ahd));
+ ahd.cbStruct=sizeof(ahd);
+ ahd.pbSrc=acm_buf;
+ ahd.cbSrcLength=BUFSIZE;
+ ahd.pbDst=acm_outbuf;
+ ahd.cbDstLength=BUFSIZE;
+ if (acmStreamPrepareHeader(hStream,&ahd,0)) goto fail;
+ }
+
+ if (riff && !append)
+ {
+ if (pDst->wFormatTag==WAVE_FORMAT_PCM) t=0x10;
+ else t=sizeof(WAVEFORMATEX)+pDst->cbSize;
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ bw = 0; WriteFile(hOut,pDst,t,&bw,0);
+ FileAlign(hOut);
+
+ if (pDst->wFormatTag!=WAVE_FORMAT_PCM)
+ {
+ t=rev32('fact');
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ t=4;
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ fact_ofs=FileTell(hOut);
+ SetFilePointer(hOut,4,0,FILE_CURRENT);
+ }
+
+ t=rev32('data');
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ data_ofs=FileTell(hOut);
+ SetFilePointer(hOut,4,0,FILE_CURRENT);
+ }
+ }
+ else if (riff && !append)
+ {
+ t=0x10;
+ //t=sizeof(WAVEFORMATEX)+pDst->cbSize;
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ bw = 0; WriteFile(hOut,&wfx,t,&bw,0);
+ t=rev32('data');
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ data_ofs=FileTell(hOut);
+ SetFilePointer(hOut,4,0,FILE_CURRENT);
+ }
+ inbuf1=inbuf=0;
+
+ active=1;
+
+ pos_delta=0;
+
+ return 0;
+
+fail:
+ if (hOut && hOut!=INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(hOut);
+ hOut=INVALID_HANDLE_VALUE;
+ DeleteFile(fn);
+ }
+ hOut=0;
+ acm_close();
+ MessageBoxW(mod.hMainWindow,WASABI_API_LNGSTRINGW(IDS_ERROR_INITIALIZING_OUTPUT),
+ szDescription,MB_ICONERROR);
+ return -1;
+}
+
+void acm_close()
+{
+#ifdef SSRC
+ if (res)
+ {
+ if (acm_buf1)
+ {
+ free(acm_buf1);
+ acm_buf1=0;
+ }
+ delete res;
+ res=0;
+ }
+#endif
+ if (hStream)
+ {
+ if (ahd.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) acmStreamUnprepareHeader(hStream,&ahd,0);
+ ZeroMemory(&ahd,sizeof(ahd));
+ acmStreamClose(hStream,0);
+ hStream=0;
+ if (acm_buf)
+ {
+ free(acm_buf);
+ acm_buf=0;
+ }
+ if (acm_outbuf)
+ {
+ free(acm_outbuf);
+ acm_outbuf=0;
+ }
+ }
+}
+
+void do_cvt(BYTE* data,UINT size);
+
+void Close()
+{
+ SYNCFUNC;
+ active=0;
+ if (use_convert)
+ {
+ do_cvt(0,0);
+ acm_close();
+ }
+ if (hOut!=INVALID_HANDLE_VALUE)
+ {
+ if (riff)
+ {
+ FileAlign(hOut);
+
+ DWORD t,bw = 0;
+ SetFilePointer(hOut,4,0,FILE_BEGIN);
+ t=GetFileSize(hOut,0)-8;
+ WriteFile(hOut,&t,4,&bw,0);
+ if (data_ofs)
+ {
+ DWORD data_size=GetFileSize(hOut,0)-(data_ofs+4);
+ SetFilePointer(hOut,data_ofs,0,FILE_BEGIN);
+ bw = 0; WriteFile(hOut,&data_size,4,&bw,0);
+ }
+ if (fact_ofs)
+ {
+ SetFilePointer(hOut,fact_ofs,0,FILE_BEGIN);
+ t=nsam;
+ bw = 0; WriteFile(hOut,&t,4,&bw,0);
+ }
+ }
+ CloseHandle(hOut);
+ hOut=INVALID_HANDLE_VALUE;
+ }
+}
+
+void do_cvt(BYTE* data,UINT size)
+{
+#ifdef SSRC
+ if (res && !hStream)
+ {
+ if (!data || !size) res->Finish();
+ else res->Write(data,size);
+
+ UINT out_size;
+ void *out = res->GetBuffer(&out_size);
+ DWORD bw = 0;
+ WriteFile(hOut,out,out_size,&bw,0);
+ res->Read(out_size);
+
+ }
+ else
+#endif
+ {
+ DWORD flags=0;
+ if (nsam==0) flags|=ACM_STREAMCONVERTF_START;
+ if (data) flags|=ACM_STREAMCONVERTF_BLOCKALIGN;
+#ifdef SSRC
+ if (res)
+ {
+ if (inbuf1+size>BUFSIZE) return;
+ if (data)
+ {
+ memcpy(acm_buf1+inbuf1,data,size);
+ inbuf1+=size;
+ }
+ res->Write(acm_buf1,inbuf1);
+ memcpy(acm_buf1,acm_buf1+inbuf1,inbuf1);
+ inbuf1=0;
+ if (!data || !size) res->Finish();
+ data = (BYTE*)res->GetBuffer(&size);
+ if (inbuf+size>BUFSIZE) return;
+ memcpy(acm_buf+inbuf,data,size);
+ inbuf+=size;
+ res->Read(size);
+ }
+ else
+#endif
+ if (data)
+ {
+ if (inbuf+size>BUFSIZE) return;
+ memcpy(acm_buf+inbuf,data,size);
+ inbuf+=size;
+ }
+
+ ahd.cbSrcLength=inbuf;
+ acmStreamConvert(hStream,&ahd,flags);
+ inbuf-=ahd.cbSrcLengthUsed;
+ memmove(acm_buf,acm_buf+ahd.cbSrcLengthUsed,inbuf);//memmove
+ DWORD bw = 0;
+ WriteFile(hOut,acm_outbuf,ahd.cbDstLengthUsed,&bw,0);
+ }
+}
+
+int Write(char *data, int len)
+{
+ SYNCFUNC;
+ if (!active) return 0;
+ if (cfg_thread_override) SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_NORMAL);
+ else Sleep(0);
+
+ len-=len%((g_bps>>3)*g_nch);
+
+ nsam+=len/((g_bps>>3)*g_nch);
+
+ if (use_convert)
+ {
+ do_cvt((BYTE*)data,len);
+ }
+ else
+ {
+ DWORD bw = 0;
+ WriteFile(hOut,data,len,&bw,0);
+ }
+
+
+ return 0;
+}
+
+int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message)
+{
+ MSGBOXPARAMSW msgbx = {sizeof(MSGBOXPARAMSW),0};
+ msgbx.lpszText = message;
+ msgbx.lpszCaption = title;
+ msgbx.lpszIcon = MAKEINTRESOURCEW(102);
+ msgbx.hInstance = GetModuleHandle(0);
+ msgbx.dwStyle = MB_USERICON;
+ msgbx.hwndOwner = parent;
+ return MessageBoxIndirectW(&msgbx);
+}
+
+void About(HWND hwndParent)
+{
+ wchar_t message[1024] = {0}, text[1024] = {0};
+ WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_DISK_WRITER_OLD,text,1024);
+ StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),
+ szDescription, __DATE__);
+ DoAboutMessageBox(hwndParent,text,message);
+}
+
+static bool _cfg_store;
+static const char *inifile;
+
+#define CFG_NAME "out_disk"//fixme
+
+static void wppi(char* nam,UINT val)
+{
+ char t[16] = {0};
+ StringCchPrintf(t,16, "%u",val);
+ WritePrivateProfileString(CFG_NAME,nam,t,inifile);
+}
+
+#define WritePrivateProfileInt(A,B,C,D) wppi(B,C)
+
+static int _do_int(int x,char* nam)
+{
+ if (_cfg_store)
+ {
+ WritePrivateProfileInt(CFG_NAME,nam,x,inifile);
+ return x;
+ }
+ else
+ {
+ return GetPrivateProfileInt(CFG_NAME,nam,x,inifile);
+ }
+}
+
+#define do_int(x) x=_do_int(x,#x)
+#define do_bool(x) x=!!_do_int(x,#x)
+
+static void _do_string(char* x,char* nam)
+{
+ if (_cfg_store)
+ {
+ WritePrivateProfileString(CFG_NAME,nam,x,inifile);
+ }
+ else
+ {
+ GetPrivateProfileString(CFG_NAME,nam,x,x,MAX_PATH,inifile);
+ }
+}
+
+#define do_string(x) _do_string(x,#x)
+
+void do_cfg()
+{
+ if (!_cfg_store)
+ {
+ GetDefaultSaveToFolder(cfg_output_dir);
+ PathCombine(cfg_singlefile_output, cfg_output_dir, "output.wav");
+ }
+
+ do_string(cfg_output_dir);
+ do_string(cfg_singlefile_output);
+ do_bool(cfg_singlefile_enabled);
+ do_bool(cfg_convert_enabled);
+ do_bool(cfg_thread_override);
+ do_int(cfg_output_mode);
+ do_bool(cfg_output_source_dir);
+ do_bool(cfg_show_saveas);
+ do_int(cfg_format_mode);
+ do_int(cfg_format_index);
+
+
+ if (_cfg_store)
+ {
+ UINT _s=sizeof(WAVEFORMATEX)+convert_wfx.wfx.cbSize;
+ WritePrivateProfileInt(CFG_NAME,"cfg_wfx_s",_s,inifile);
+ WritePrivateProfileStruct(CFG_NAME,"cfg_wfx",&convert_wfx,_s,inifile);
+ WritePrivateProfileStruct(CFG_NAME,"cfg_wfx1",&singlefile_wfx,sizeof(singlefile_wfx),inifile);
+ }
+ else
+ {
+ UINT _s=GetPrivateProfileInt(CFG_NAME,"cfg_wfx_s",0,inifile);
+ if (_s && _s<=sizeof(convert_wfx))
+ {
+ GetPrivateProfileStruct(CFG_NAME,"cfg_wfx",&convert_wfx,_s,inifile);
+ }
+ GetPrivateProfileStruct(CFG_NAME,"cfg_wfx1",&singlefile_wfx,sizeof(singlefile_wfx),inifile);
+ }
+}
+
+void Init()
+{
+ SYNCFUNC;
+ if (!mod.hMainWindow)
+ return;
+ inifile = (const char *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIFILE);
+ singlefile_wfx = wfx_default;
+ convert_wfx.wfx = wfx_default;
+ memset(convert_wfx.crap, 0, sizeof(convert_wfx.crap));
+
+ // loader so that we can get the localisation service api for use
+ WASABI_API_SVC = (api_service*)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_API_SERVICE);
+ if (WASABI_API_SVC == (api_service*)1) WASABI_API_SVC = NULL;
+
+ waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID);
+ if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
+
+ // need to have this initialised before we try to do anything with localisation features
+ WASABI_API_START_LANG(mod.hDllInstance,OutDiskLangGUID);
+
+ StringCchPrintfW(szDescription, 256, WASABI_API_LNGSTRINGW(IDS_NULLSOFT_DISK_WRITER), PLUGIN_VERSION);
+ mod.description = (char*)szDescription;
+
+ _cfg_store=0;
+ do_cfg();
+}
+
+void Quit()
+{
+ SYNCFUNC;
+ if (active) Close();
+
+ _cfg_store=1;
+ do_cfg();
+}
+
+int CanWrite()
+{
+ return paused ? 0 : 666666666;
+}
+
+int IsPlaying()
+{
+ return 0;
+}
+
+int Pause(int p)
+{
+ int _p=paused;
+ paused=p;
+ return _p;
+}
+
+void SetVolume(int volume)
+{
+}
+
+void SetPan(int pan)
+{
+}
+
+void Flush(int t)
+{
+ nsam=0;
+ pos_delta=t;
+}
+
+int GetOutputTime()
+{
+ return pos_delta+MulDiv(nsam,1000,g_freq);
+}
+
+Out_Module mod=
+{
+ OUT_VER_U,
+ 0,
+ 426119909,
+ 0,
+ 0,
+ Config,
+ About,
+ Init,
+ Quit,
+ Open,
+ Close,
+ Write,
+ CanWrite,
+ IsPlaying,
+ Pause,
+ SetVolume,
+ SetVolume,
+ Flush,
+ GetOutputTime,
+ GetOutputTime
+};
+
+extern "C"
+{
+ __declspec( dllexport ) Out_Module * winampGetOutModule()
+ {
+ return &mod;
+ }
+}
+
+BOOL APIENTRY DllMain(HANDLE hMod,DWORD r,void*)
+{
+ if (r==DLL_PROCESS_ATTACH)
+ {
+ DisableThreadLibraryCalls((HMODULE)hMod);
+ }
+ return TRUE;
+} \ No newline at end of file