diff options
Diffstat (limited to 'Src/Plugins/Output/out_disk/main.cpp')
-rw-r--r-- | Src/Plugins/Output/out_disk/main.cpp | 1203 |
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 |