#include "main.h" #include "Metadata.h" #include "../Winamp/wa_ipc.h" // ID3v2 stuff #include "../id3v2/id3_tag.h" #include "FactoryHelper.h" #include "id3.h" #include "../nu/AutoWide.h" #include "../nu/AutoChar.h" #include "AACFrame.h" #include "LAMEinfo.h" #include #include "../nu/ns_wc.h" #include "../nu/ListView.h" #include "resource.h" #include "Stopper.h" #include "config.h" #include // TODO: benski> CUT!!! char g_stream_title[256] = {0}; int fixAACCBRbitrate(int br) { static short brs[] = { 8, 12, 16, 20, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }; int x; for (x = 0; x < sizeof(brs) / sizeof(brs[0]); x ++) { int delta = (brs[x] * 8) / 128; if (delta < 2) delta = 2; if (br < brs[x] - delta) break; if (br < brs[x] + delta) return brs[x]; } return br; } void ConvertTryUTF8(const char *in, wchar_t *out, size_t outlen) { out[0]=0; int x = MultiByteToWideCharSZ(CP_UTF8, MB_ERR_INVALID_CHARS, in, -1, out, (int)outlen); if (!x) { if (GetLastError() == ERROR_NO_UNICODE_TRANSLATION) MultiByteToWideCharSZ(CP_ACP, 0, in, -1, out, (int)outlen); else MultiByteToWideCharSZ(CP_UTF8, 0, in, -1, out, (int)outlen); } } void getfileinfo(const wchar_t *filename, wchar_t *title, int *length_in_ms) { const wchar_t *fn; if (length_in_ms) *length_in_ms = -1000; if (filename && filename[0]) fn = filename; else fn = lastfn; if (!_wcsnicmp(fn, L"file://", 7)) fn += 7; if (PathIsURL(fn)) { if (title) { if (fn != filename || !_wcsicmp(fn, lastfn)) { EnterCriticalSection(&g_lfnscs); if (lastfn_status[0]) { char buf[4096] = {0}; StringCchPrintfA(buf, 4096, "[%s] %s", lastfn_status, lastfn_data_ready ? g_stream_title : (char *)AutoChar(fn)); ConvertTryUTF8(buf, title, 256); } else { if (!lastfn_data_ready) lstrcpynW(title, fn, 256); else { ConvertTryUTF8(g_stream_title, title, 256); } } LeaveCriticalSection(&g_lfnscs); if (length_in_ms) *length_in_ms = getlength(); } else { lstrcpynW(title, fn, 256); } } return ; } else { Metadata info; if (info.Open(fn) == METADATA_SUCCESS) { if (title) { wchar_t mp3artist[256] = L"", mp3title[256] = L""; info.GetExtendedData("artist", mp3artist, 256); info.GetExtendedData("title", mp3title, 256); if (mp3artist[0] && mp3title[0]) StringCchPrintfW(title, 256, L"%s - %s", mp3artist, mp3title); else if (mp3title[0]) lstrcpynW(title, mp3title, 256); else { lstrcpynW(title, fn, MAX_PATH); PathStripPathW(title); PathRemoveExtensionW(title); } } if (fn == filename) { wchar_t ln[128]=L""; info.GetExtendedData("length", ln, 128); *length_in_ms = _wtoi(ln); } else *length_in_ms = getlength(); } else if (fn != filename) *length_in_ms = getlength(); } } int id3Dlg(const wchar_t *fn, HWND hwnd) { return 1; } extern const wchar_t *id3v1_genres[]; extern size_t numGenres; static int our_change=0; #define GetMeta(hwnd) (Metadata *)GetPropW(GetParent(hwnd),L"mp3info") #define SetMeta(hwnd,meta) SetPropW(GetParent(hwnd),L"mp3info",(HANDLE)meta) static INT_PTR CALLBACK id3v1_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static int my_change_v1=0; static const int ctrls[] = { IDC_ID3V11_TRACK, IDC_ID3_TITLE, IDC_ID3_ARTIST, IDC_ID3_ALBUM, IDC_ID3_YEAR, IDC_ID3_COMMENT, IDC_ID3_GENRE, }; static const int strs_lim[] = { 3, 30, 30, 30, 4, 28, 1, }; static const char * strs[] = { "track", "title", "artist", "album", "year", "comment", "genre", }; switch (uMsg) { case WM_INITDIALOG: { Metadata *meta = GetMeta(hwndDlg); if (meta) meta->AddRef(); else { meta = new Metadata(); meta->Open((wchar_t*)lParam); SetMeta(hwndDlg,meta); } wchar_t genre_buf[32] = {0}; meta->id3v1.GetString("genre",genre_buf,32); our_change=1; SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_RESETCONTENT, 0, 0); SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_SETCURSEL, -1, 0); for (size_t x = 0; x != numGenres; x ++) { int y = (int)SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_ADDSTRING, 0, (LPARAM)id3v1_genres[x]); SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_SETITEMDATA, y, x); if (_wcsicmp(genre_buf,id3v1_genres[x])==0) SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_SETCURSEL, y, 0); } for (int i=0; iid3v1.GetString(strs[i],buf,32); SetDlgItemTextW(hwndDlg,ctrls[i],buf); } if (meta->id3v1.HasData()) { CheckDlgButton(hwndDlg,IDC_ID3V1,TRUE); if (!config_write_id3v1) { // if we have id3v1 writing turned off, disable controls for (int i=0; iid3v1.HasData()) break; wchar_t *key = (wchar_t*)wParam; wchar_t *value = (wchar_t*)lParam; AutoChar keyA(key); for (int i=0; i i don't think this is what we want? meta->SetExtendedData(strs[i],value); meta->id3v1.SetString(strs[i], value); wchar_t buf[2048]=L""; meta->id3v1.GetString(strs[i],buf,2048); if (!IsDlgButtonChecked(hwndDlg,IDC_ID3V1)) { // re-enable stuff CheckDlgButton(hwndDlg,IDC_ID3V1,TRUE); for (int j=0; jfilename)) stopper.Stop(); int ret = meta->Save(); stopper.Play(); wchar_t boxtitle[256] = {0}; switch(ret) { case SAVE_SUCCESS: { // cheap way to trigger a metadata reset in a thread-safe manner wchar_t d[10] = {0}; extendedFileInfoStructW reset_info = {0}; reset_info.filename=L".mp3"; reset_info.metadata=L"artist"; reset_info.ret = d; reset_info.retlen=10; SendMessage(mod.hMainWindow, WM_WA_IPC, (WPARAM)&reset_info, IPC_GET_EXTENDED_FILE_INFOW); } break; case SAVE_ERROR_READONLY: MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_READONLY), WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256), MB_OK); break; case SAVE_ERROR_OPENING_FILE: MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_OPENING_FILE), WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256), MB_OK); break; case SAVE_APEV2_WRITE_ERROR: MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_APEV2), WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256), MB_OK); break; case SAVE_LYRICS3_WRITE_ERROR: MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_LYRICS3), WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256), MB_OK); break; case SAVE_ID3V1_WRITE_ERROR: MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_ID3V1), WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256), MB_OK); break; case SAVE_ID3V2_WRITE_ERROR: MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_ID3V2), WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256), MB_OK); break; default: MessageBox(hwndDlg, WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_UNSPECIFIED), WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256), MB_OK); break; } } break; case IDC_ID3V1_TO_V2: { my_change_v1=1; Metadata *meta = GetMeta(hwndDlg); if (!meta) break; for (int i=0; iid3v2.SetString(strs[i],buf); SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf); } my_change_v1=0; } break; case IDC_ID3V1: { our_change=1; BOOL checked = IsDlgButtonChecked(hwndDlg,IDC_ID3V1); Metadata *meta = GetMeta(hwndDlg); if (!meta) break; if (!checked) meta->id3v1.Clear(); for (int i=0; iid3v1.SetString(strs[i],buf); } SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf); } our_change=0; } break; default: if (!our_change && !my_change_v1 && (HIWORD(wParam) == EN_CHANGE || HIWORD(wParam) == CBN_SELCHANGE)) { our_change=1; for (int i=0; i=0 && n<(LRESULT)numGenres) lstrcpyn(buf,id3v1_genres[n],2048); } Metadata *meta = GetMeta(hwndDlg); if (!meta) break; meta->id3v1.SetString(strs[i],buf); if (!meta->id3v2.HasData()) SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf); } } our_change=0; } } break; case WM_DESTROY: { Metadata *meta = GetMeta(hwndDlg); if (meta) meta->Release(); } break; } return 0; } static INT_PTR CALLBACK id3v2_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static int my_change_v2=0; static const int ctrls[] = { IDC_ID3V2_TRACK, IDC_ID3V2_TITLE, IDC_ID3V2_ARTIST, IDC_ID3V2_ALBUM, IDC_ID3V2_YEAR, IDC_ID3V2_COMMENT, IDC_ID3V2_GENRE, IDC_ID3V2_COMPOSER, IDC_ID3V2_PUBLISHER, IDC_ID3V2_MARTIST, IDC_ID3V2_RECORD, IDC_ID3V2_URL, IDC_ID3V2_ENCODER, IDC_ID3V2_ALBUM_ARTIST, IDC_ID3V2_DISC, IDC_TRACK_GAIN, IDC_ALBUM_GAIN, IDC_ID3V2_BPM, }; static const char * strs[] = { "track", "title", "artist", "album", "year", "comment", "genre", "composer", "publisher", "originalartist", "copyright", "url", "tool", "albumartist", "disc", "replaygain_track_gain", // 15 "replaygain_album_gain", // 16 "bpm", }; switch (uMsg) { case WM_INITDIALOG: { our_change=1; SendDlgItemMessage(hwndDlg, IDC_ID3V2_GENRE, CB_RESETCONTENT, 0, 0); for (size_t x = 0; x != numGenres; x ++) { int y = (int)SendDlgItemMessage(hwndDlg, IDC_ID3V2_GENRE, CB_ADDSTRING, 0, (LPARAM)id3v1_genres[x]); SendDlgItemMessage(hwndDlg, IDC_ID3V2_GENRE, CB_SETITEMDATA, y, x); } Metadata *meta = GetMeta(hwndDlg); if (meta) meta->AddRef(); else { meta = new Metadata(); meta->Open((wchar_t*)lParam); SetMeta(hwndDlg,meta); } for (int i=0; iid3v2.GetString(strs[i],buf,32768); if((i == 15 || i == 16) && buf[0]) { SetDlgItemTextW(hwndDlg,ctrls[i],L"buf"); // this isn't nice but it localises the RG values // for display as they're saved in the "C" locale double value = _wtof_l(buf,WASABI_API_LNG->Get_C_NumericLocale()); StringCchPrintfW(buf,64,L"%-+.2f dB", value); } SetDlgItemTextW(hwndDlg,ctrls[i],buf); } if (meta->id3v2.HasData()) CheckDlgButton(hwndDlg,IDC_ID3V2,TRUE); else for (int i=0; i cut? i don't think this is what we want: meta->SetExtendedData(strs[i],value); meta->id3v2.SetString(strs[i], value); wchar_t buf[32768]=L""; meta->id3v2.GetString(strs[i],buf,32768); if (!IsDlgButtonChecked(hwndDlg,IDC_ID3V2)) { // re-enable items CheckDlgButton(hwndDlg,IDC_ID3V2,TRUE); for (int j=0; jid3v1.SetString(strs[i],buf); meta->GetExtendedData(strs[i], buf, 32768); SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf); } my_change_v2=0; } break; case IDC_ID3V2: { our_change=1; BOOL checked = IsDlgButtonChecked(hwndDlg,IDC_ID3V2); Metadata *meta = GetMeta(hwndDlg); if (!meta) break; if (!checked) meta->id3v2.Clear(); for (int i=0; iid3v2.SetString(strs[i],buf); } meta->GetExtendedData(strs[i],buf,32768); SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf); } our_change=0; } break; case IDOK: { extern Metadata *m_ext_get_mp3info; if (m_ext_get_mp3info) m_ext_get_mp3info->Release(); m_ext_get_mp3info=0; } break; default: if (!our_change && !my_change_v2 && (HIWORD(wParam) == EN_CHANGE || HIWORD(wParam) == CBN_SELCHANGE || HIWORD(wParam) == CBN_EDITCHANGE || HIWORD(wParam) == CBN_EDITUPDATE)) { our_change=1; for (int i=0; i=0 && n<(LRESULT)numGenres) lstrcpyn(buf,id3v1_genres[n],32768); else{ GetDlgItemTextW(hwndDlg,ctrls[i],buf,32768); } } Metadata *meta = GetMeta(hwndDlg); if (!meta) break; meta->id3v2.SetString(strs[i],buf); meta->GetExtendedData(strs[i],buf,32768); SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf); } } our_change=0; } } break; case WM_DESTROY: { Metadata *meta = GetMeta(hwndDlg); if (meta) meta->Release(); } break; } return 0; } static INT_PTR CALLBACK lyrics3_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static const int ctrls[] = { IDC_LYRICS3_TITLE, IDC_LYRICS3_ARTIST, IDC_LYRICS3_ALBUM, }; static const char * strs[] = { "title", "artist", "album", }; switch (uMsg) { case WM_INITDIALOG: { Metadata *meta = GetMeta(hwndDlg); if (meta) meta->AddRef(); else { meta = new Metadata(); meta->Open((wchar_t*)lParam); SetMeta(hwndDlg,meta); } for (int i=0; ilyrics3.GetString(strs[i],buf,250); SetDlgItemTextW(hwndDlg,ctrls[i],buf); } if (meta->lyrics3.HasData()) CheckDlgButton(hwndDlg,IDC_LYRICS3,TRUE); } break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_LYRICS3: { BOOL checked = IsDlgButtonChecked(hwndDlg,IDC_LYRICS3); Metadata *meta = GetMeta(hwndDlg); if (!meta) break; if (!checked) meta->lyrics3.Clear(); else // clear the dirty state if re-enabled so we don't lose the lyrics3 tag meta->lyrics3.ResetDirty(); for (int i=0; ilyrics3.SetString(strs[i],buf); } meta->GetExtendedData(strs[i],buf,2048); // if we don't flag this then we can lose info in the id3v1 and v2 tags which is definitely bad our_change++; SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf); our_change--; } } break; } break; case WM_DESTROY: { Metadata *meta = GetMeta(hwndDlg); if (meta) meta->Release(); } break; } return 0; } /* ================ APEv2 Editor Tab ================ */ static INT_PTR CALLBACK apev2_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static W_ListView listview; static int my_change_ape=0; switch (uMsg) { case WM_NOTIFYFORMAT: return NFR_UNICODE; case WM_INITDIALOG: { our_change++; Metadata *meta = GetMeta(hwndDlg); if (meta) { meta->AddRef(); } else { meta = new Metadata(); meta->Open((wchar_t*)lParam); SetMeta(hwndDlg,meta); } if (meta->apev2.HasData()) CheckDlgButton(hwndDlg,IDC_APEV2,TRUE); listview.setwnd(GetDlgItem(hwndDlg, IDC_APE_LIST)); listview.SetDoubleBuffered(true); listview.AddCol(WASABI_API_LNGSTRINGW(IDS_NAME), 82); listview.AddCol(WASABI_API_LNGSTRINGW(IDS_VALUE), 160); listview.SetVirtualCount((int)meta->apev2.GetNumItems()); listview.AutoSizeColumn(0); listview.AutoSizeColumn(1); SetDlgItemTextW(hwndDlg,IDC_APE_KEY,L""); SetDlgItemTextW(hwndDlg,IDC_APE_VALUE,L""); EnableWindow(GetDlgItem(hwndDlg,IDC_APE_KEY),FALSE); EnableWindow(GetDlgItem(hwndDlg,IDC_APE_VALUE),FALSE); EnableWindow(GetDlgItem(hwndDlg,IDC_APE_DELETE),FALSE); our_change--; return 1; } break; case WM_COMMAND: switch(LOWORD(wParam)) { case IDC_APE_KEY: case IDC_APE_VALUE: if(HIWORD(wParam) == EN_CHANGE) { int selected = listview.GetNextSelected(); if (selected != LB_ERR) { listview.RefreshItem(selected); } } else if(HIWORD(wParam) == EN_KILLFOCUS) { Metadata *meta = GetMeta(hwndDlg); if (!meta) break; my_change_ape++; char key[1024] = {0}; wchar_t value[32768] = {0}; GetDlgItemTextA(hwndDlg, IDC_APE_KEY, key, 1024); GetDlgItemText(hwndDlg, IDC_APE_VALUE, value, 32768); int selected = listview.GetNextSelected(); if (selected != LB_ERR) { meta->apev2.SetKeyValueByIndex(selected, key, value); } const wchar_t *winamp_key = APE::MapApeKeyToWinampKeyW(key); if (winamp_key) { our_change++; SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)winamp_key,(WPARAM)value); our_change--; } my_change_ape--; } break; case IDC_APEV2: { BOOL checked = IsDlgButtonChecked(hwndDlg,IDC_APEV2); Metadata *meta = GetMeta(hwndDlg); if (!meta) break; if (!checked) meta->apev2.MarkClear(); else // clear the dirty state if re-enabled so we don't lose the apev2 tag meta->apev2.ResetDirty(); } break; case IDC_DELETE_ALL: { Metadata *meta = GetMeta(hwndDlg); if (!meta) break; my_change_ape++; listview.UnselectAll(); meta->apev2.Clear(); listview.SetVirtualCount((int)meta->apev2.GetNumItems()); my_change_ape--; } break; case IDC_APE_ADD: { Metadata *meta = GetMeta(hwndDlg); if (!meta) break; int index = listview.GetCount(); if (meta->apev2.AddItem() == APEv2::APEV2_SUCCESS) { listview.SetVirtualCount((int)meta->apev2.GetNumItems()); listview.SetSelected(index); listview.ScrollTo(index); SetFocus(GetDlgItem(hwndDlg, IDC_APE_KEY)); } } break; case IDC_APE_DELETE: { Metadata *meta = GetMeta(hwndDlg); if (!meta) break; int selected = listview.GetNextSelected(); if (selected != LB_ERR) { listview.UnselectAll(); meta->apev2.RemoveItem(selected); listview.SetVirtualCount((int)meta->apev2.GetNumItems()); } } break; } break; case WM_NOTIFY: { LPNMHDR l=(LPNMHDR)lParam; if (l->idFrom==IDC_APE_LIST) switch (l->code) { case LVN_GETDISPINFO: { Metadata *meta = GetMeta(hwndDlg); if (meta) { NMLVDISPINFO *lpdi = (NMLVDISPINFO*) l; if (lpdi->item.mask & LVIF_TEXT) { int selected = listview.GetNextSelected(); switch (lpdi->item.iSubItem) { case 0: { if (lpdi->item.iItem == selected) { GetDlgItemText(hwndDlg, IDC_APE_KEY, lpdi->item.pszText, lpdi->item.cchTextMax); } else { const char *key=0; meta->apev2.EnumValue(lpdi->item.iItem, &key, 0, 0); MultiByteToWideCharSZ(CP_ACP, 0, key?key:"", -1, lpdi->item.pszText, lpdi->item.cchTextMax); } } return 0; case 1: { if (lpdi->item.iItem == selected) { GetDlgItemText(hwndDlg, IDC_APE_VALUE, lpdi->item.pszText, lpdi->item.cchTextMax); } else { const char *key=0; meta->apev2.EnumValue(lpdi->item.iItem, &key, lpdi->item.pszText, lpdi->item.cchTextMax); } } return 0; } } } } break; case LVN_KEYDOWN: break; case LVN_ITEMCHANGED: { my_change_ape++; LPNMLISTVIEW lv=(LPNMLISTVIEW)lParam; if (lv->uNewState & LVIS_SELECTED) { Metadata *meta = GetMeta(hwndDlg); if (meta) { const char *key=0; wchar_t value[32768] = {0}; meta->apev2.EnumValue(lv->iItem, &key, value, 32768); SetDlgItemTextA(hwndDlg,IDC_APE_KEY,key); SetDlgItemText(hwndDlg,IDC_APE_VALUE,value); BOOL editable = meta->apev2.IsItemReadOnly(lv->iItem)?FALSE:TRUE; EnableWindow(GetDlgItem(hwndDlg,IDC_APE_KEY),editable); EnableWindow(GetDlgItem(hwndDlg,IDC_APE_VALUE),editable); EnableWindow(GetDlgItem(hwndDlg,IDC_APE_DELETE),TRUE); listview.RefreshItem(lv->iItem); } } if (lv->uOldState & LVIS_SELECTED) { SetDlgItemTextW(hwndDlg,IDC_APE_KEY,L""); SetDlgItemTextW(hwndDlg,IDC_APE_VALUE,L""); EnableWindow(GetDlgItem(hwndDlg,IDC_APE_KEY),FALSE); EnableWindow(GetDlgItem(hwndDlg,IDC_APE_VALUE),FALSE); EnableWindow(GetDlgItem(hwndDlg,IDC_APE_DELETE),FALSE); } my_change_ape--; } } } break; case WM_USER: if (wParam && lParam && !our_change && !my_change_ape) { Metadata *meta = GetMeta(hwndDlg); if (meta) { const wchar_t *keyW = (const wchar_t *)wParam; const wchar_t *value = (const wchar_t *)lParam; AutoChar key(keyW); my_change_ape++; meta->apev2.SetString(key, value); listview.UnselectAll(); listview.SetVirtualCount((int)meta->apev2.GetNumItems()); listview.RefreshAll(); my_change_ape--; } } break; case WM_DESTROY: { Metadata *meta = GetMeta(hwndDlg); if (meta) meta->Release(); } break; } return 0; } extern "C" { // return 1 if you want winamp to show it's own file info dialogue, 0 if you want to show your own (via In_Module.InfoBox) // if returning 1, remember to implement winampGetExtendedFileInfo("formatinformation")! __declspec(dllexport) int winampUseUnifiedFileInfoDlg(const wchar_t * fn) { if (!_wcsnicmp(fn, L"file://", 7)) fn += 7; if (PathIsURLW(fn)) return 2; return 1; } // should return a child window of 513x271 pixels (341x164 in msvc dlg units), or return NULL for no tab. // Fill in name (a buffer of namelen characters), this is the title of the tab (defaults to "Advanced"). // filename will be valid for the life of your window. n is the tab number. This function will first be // called with n == 0, then n == 1 and so on until you return NULL (so you can add as many tabs as you like). // The window you return will recieve WM_COMMAND, IDOK/IDCANCEL messages when the user clicks OK or Cancel. // when the user edits a field which is duplicated in another pane, do a SendMessage(GetParent(hwnd),WM_USER,(WPARAM)L"fieldname",(LPARAM)L"newvalue"); // this will be broadcast to all panes (including yours) as a WM_USER. __declspec(dllexport) HWND winampAddUnifiedFileInfoPane(int n, const wchar_t * filename, HWND parent, wchar_t *name, size_t namelen) { if (n == 0) { SetPropW(parent,L"INBUILT_NOWRITEINFO", (HANDLE)1); lstrcpyn(name,L"ID3v1", (int)namelen); return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO_ID3V1, parent, id3v1_dlgproc, (LPARAM)filename); } if (n == 1) { lstrcpyn(name,L"ID3v2", (int)namelen); return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO_ID3V2, parent, id3v2_dlgproc, (LPARAM)filename); } if (n == 2) { Metadata *meta = (Metadata *)GetPropW(parent, L"mp3info"); if (meta->lyrics3.HasData()) { lstrcpyn(name,L"Lyrics3", (int)namelen); return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO_LYRICS3, parent, lyrics3_dlgproc, (LPARAM)filename); } else if (meta->apev2.HasData()) { lstrcpyn(name,L"APEv2", (int)namelen); return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO_APEV2, parent, apev2_dlgproc, (LPARAM)filename); } } if (n == 3) { Metadata *meta = (Metadata *)GetPropW(parent, L"mp3info"); if (meta->lyrics3.HasData() && meta->apev2.HasData()) { lstrcpyn(name,L"APEv2", (int)namelen); return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO_APEV2, parent, apev2_dlgproc, (LPARAM)filename); } } return NULL; } }